xref: /expo/docs/ui/components/Layout/Layout.tsx (revision e377ff85)
1import { css } from '@emotion/react';
2import { breakpoints, theme } from '@expo/styleguide';
3import React, { PropsWithChildren, ReactNode } from 'react';
4
5import { LayoutScroll } from './LayoutScroll';
6
7type LayoutProps = PropsWithChildren<{
8  /** The content within the top bar that spans the columns */
9  header: ReactNode;
10  /** The content within the left column */
11  navigation?: ReactNode;
12  /** The content within the right column */
13  sidebar?: ReactNode;
14}>;
15
16export function Layout({ header, navigation, sidebar, children }: LayoutProps) {
17  return (
18    <div css={layoutStyle}>
19      <div css={headerStyle}>{header}</div>
20      <div css={navigationStyle}>{navigation}</div>
21      <div css={contentStyle}>
22        <LayoutScroll>
23          <div css={innerContentStyle}>{children}</div>
24        </LayoutScroll>
25      </div>
26      <div css={sidebarStyle}>{sidebar}</div>
27    </div>
28  );
29}
30
31const layoutStyle = css({
32  display: 'grid',
33  height: '100vh',
34  gridTemplateRows: '60px calc(100vh - 60px)',
35  gridTemplateColumns: 'min-content auto min-content',
36  gridTemplateAreas: `
37    "header header header"
38    "navigation content sidebar"
39  `,
40  backgroundColor: theme.background.default,
41  '[data-expo-theme="dark"] &': {
42    backgroundColor: theme.background.screen,
43  },
44});
45
46const headerStyle = css({ gridArea: 'header' });
47
48const navigationStyle = css({
49  gridArea: 'navigation',
50  [`@media screen and (max-width: 768px)`]: {
51    display: 'none',
52  },
53});
54
55const contentStyle = css({ gridArea: 'content' });
56const innerContentStyle = css({
57  margin: '0 auto',
58  maxWidth: breakpoints.large,
59  height: '100%',
60  overflowY: 'visible',
61});
62
63const sidebarStyle = css({
64  gridArea: 'sidebar',
65  [`@media screen and (max-width: ${breakpoints.medium}px)`]: {
66    display: 'none',
67  },
68});
69