Layout scaffolding for Web apps.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

130 lines
2.5 KiB

  1. import * as React from 'react';
  2. import styled, {createGlobalStyle} from 'styled-components';
  3. import TopBar from '../../widgets/TopBar';
  4. const DisableScrolling = createGlobalStyle({
  5. 'body': {
  6. overflow: 'hidden',
  7. '@media (min-width: 1080px)': {
  8. overflow: 'auto',
  9. },
  10. },
  11. })
  12. const LayoutBase = styled('div')({
  13. '--width-base': 'var(--width-base, 360px)',
  14. '--height-topbar': 'var(--height-topbar, 4rem)',
  15. })
  16. const ContentBase = styled('main')({
  17. boxSizing: 'border-box',
  18. '@media (min-width: 1080px)': {
  19. paddingLeft: 'calc(50% - var(--width-base, 360px) * 0.5)',
  20. },
  21. })
  22. const SidebarOverflow = styled('div')({
  23. width: '100%',
  24. height: '100%',
  25. overflow: 'auto',
  26. // overflow: 'overlay',
  27. scrollbarWidth: 'none',
  28. '::-webkit-scrollbar': {
  29. display: 'none',
  30. },
  31. })
  32. const SidebarBase = styled('div')({
  33. boxSizing: 'border-box',
  34. position: 'fixed',
  35. top: 0,
  36. left: '-100%',
  37. width: '100%',
  38. height: '100%',
  39. backgroundColor: 'var(--color-bg, white)',
  40. overflow: 'hidden',
  41. '@media (prefers-color-scheme: dark)': {
  42. backgroundColor: 'var(--color-bg, black)',
  43. },
  44. '@media (min-width: 1080px)': {
  45. width: 'calc(50% - var(--width-base, 360px) * 0.5)',
  46. left: 0,
  47. },
  48. })
  49. const OpenSidebarBase = styled(SidebarBase)({
  50. left: 0,
  51. })
  52. export const SidebarContainer = styled('div')({
  53. margin: '0 auto',
  54. padding: '0 1rem',
  55. boxSizing: 'border-box',
  56. maxWidth: 'calc(var(--width-base, 360px) * 2)',
  57. '@media (min-width: 1080px)': {
  58. width: 'var(--width-base, 360px)',
  59. marginRight: 0,
  60. },
  61. })
  62. export const ContentContainer = styled('div')({
  63. padding: '0 1rem',
  64. boxSizing: 'border-box',
  65. width: '100%',
  66. maxWidth: 'calc(var(--width-base, 360px) * 2)',
  67. marginRight: 'auto',
  68. marginLeft: 'auto',
  69. '@media (min-width: 1080px)': {
  70. marginLeft: 0,
  71. },
  72. })
  73. type Props = {
  74. brand?: React.ReactNode,
  75. sidebarMain?: React.ReactChild,
  76. sidebarMainOpen?: boolean,
  77. menuLink?: React.ReactNode,
  78. userLink?: React.ReactNode,
  79. topBarCenter?: React.ReactChild,
  80. }
  81. export const Layout: React.FC<Props> = ({
  82. brand,
  83. sidebarMain,
  84. sidebarMainOpen,
  85. menuLink,
  86. userLink,
  87. topBarCenter,
  88. children,
  89. }) => {
  90. const LeftSidebarComponent = sidebarMainOpen ? OpenSidebarBase : SidebarBase
  91. return (
  92. <LayoutBase>
  93. {
  94. sidebarMainOpen
  95. && (
  96. <DisableScrolling />
  97. )
  98. }
  99. <TopBar
  100. wide
  101. brand={brand}
  102. menuLink={menuLink}
  103. userLink={userLink}
  104. >
  105. {topBarCenter}
  106. </TopBar>
  107. <LeftSidebarComponent>
  108. <SidebarOverflow>
  109. {sidebarMain}
  110. </SidebarOverflow>
  111. </LeftSidebarComponent>
  112. <ContentBase>
  113. {children}
  114. </ContentBase>
  115. </LayoutBase>
  116. )
  117. }