xref: /expo/docs/ui/components/Layout/Layout.tsx (revision bf3dd357)
1import { css } from '@emotion/react';
2import { breakpoints } 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});
41
42const headerStyle = css({ gridArea: 'header' });
43
44const navigationStyle = css({
45  gridArea: 'navigation',
46  [`@media screen and (max-width: 768px)`]: {
47    display: 'none',
48  },
49});
50
51const contentStyle = css({ gridArea: 'content' });
52const innerContentStyle = css({
53  margin: '0 auto',
54  maxWidth: breakpoints.large,
55  height: '100%',
56  overflowY: 'visible',
57});
58
59const sidebarStyle = css({
60  gridArea: 'sidebar',
61  [`@media screen and (max-width: ${breakpoints.medium}px)`]: {
62    display: 'none',
63  },
64});
65