xref: /expo/docs/ui/components/Layout/Layout.tsx (revision d04463cb)
1import { css, Global } from '@emotion/react';
2import { breakpoints, spacing, theme } from '@expo/styleguide';
3import React, { PropsWithChildren, ReactNode } from 'react';
4
5import { Header } from '~/ui/components/Header';
6import { LayoutScroll } from '~/ui/components/Layout';
7
8type LayoutProps = PropsWithChildren<{
9  // The content within the top bar that spans the columns
10  header?: ReactNode;
11  // The content within the left column
12  navigation?: ReactNode;
13  // The content within the right column
14  sidebar?: ReactNode;
15}>;
16
17export const Layout = ({ header = <Header />, navigation, sidebar, children }: LayoutProps) => (
18  <>
19    <Global
20      styles={css({
21        // Ensure correct background for Overscroll
22        '[data-expo-theme="dark"] body': {
23          backgroundColor: theme.background.screen,
24        },
25      })}
26    />
27    <header css={headerStyle}>{header}</header>
28    <main css={layoutStyle}>
29      {navigation && <nav css={navigationStyle}>{navigation}</nav>}
30      <LayoutScroll>
31        <article css={innerContentStyle}>{children}</article>
32      </LayoutScroll>
33      {sidebar && <aside css={asideStyle}>{sidebar}</aside>}
34    </main>
35  </>
36);
37
38const HEADER_HEIGHT = 60;
39
40const layoutStyle = css({
41  display: 'flex',
42  alignItems: 'stretch',
43  maxHeight: `calc(100vh - ${HEADER_HEIGHT}px)`,
44  marginTop: HEADER_HEIGHT,
45  backgroundColor: theme.background.default,
46  '[data-expo-theme="dark"] &': {
47    backgroundColor: theme.background.screen,
48  },
49  [`@media screen and (max-width: ${breakpoints.medium}px)`]: {
50    // Ditch inner scroll on mobile, which results in weird bugs
51    maxHeight: 'none',
52  },
53});
54
55const headerStyle = css({
56  position: 'fixed',
57  top: 0,
58  width: '100%',
59  height: HEADER_HEIGHT,
60  zIndex: 100,
61});
62
63const navigationStyle = css({
64  flexBasis: 256,
65  [`@media screen and (max-width: ${breakpoints.medium}px)`]: {
66    display: 'none',
67  },
68});
69
70const innerContentStyle = css({
71  margin: '0 auto',
72  maxWidth: breakpoints.large,
73  padding: `${spacing[8]}px ${spacing[4]}px`,
74});
75
76const asideStyle = css({
77  flexBasis: 288,
78  [`@media screen and (max-width: ${breakpoints.medium}px)`]: {
79    display: 'none',
80  },
81});
82