Layout scaffolding for Web apps.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

497 rindas
11 KiB

  1. import * as React from 'react';
  2. import styled, { createGlobalStyle } from 'styled-components'
  3. import TopBar from '../../widgets/TopBar'
  4. import {minWidthFactor} from '../../utilities/mixins';
  5. import {configVar, loadConfig} from '../../utilities/helpers'
  6. const Config = createGlobalStyle({
  7. ...loadConfig(),
  8. })
  9. const DisableScrolling = createGlobalStyle({
  10. 'body': {
  11. overflow: 'hidden',
  12. [minWidthFactor(3)]: {
  13. overflow: 'auto',
  14. },
  15. },
  16. })
  17. const ContentBase = styled('main')({
  18. boxSizing: 'border-box',
  19. paddingBottom: 'var(--size-menu, 4rem)',
  20. [minWidthFactor(3)]: {
  21. paddingLeft: `calc(50% - ${configVar('base-width')} * 0.5)`,
  22. paddingBottom: 0,
  23. },
  24. })
  25. const SidebarBase = styled('div')({
  26. boxSizing: 'border-box',
  27. overflow: 'hidden',
  28. display: 'contents',
  29. left: `calc(${configVar('base-width')} * -1)`,
  30. [minWidthFactor(3)]: {
  31. position: 'fixed',
  32. top: 0,
  33. left: 0,
  34. width: `calc(50% - ${configVar('base-width')} * 0.5)`,
  35. height: '100%',
  36. display: 'block',
  37. },
  38. })
  39. const SidebarMain = styled('div')({
  40. boxSizing: 'border-box',
  41. position: 'fixed',
  42. top: 0,
  43. right: '100%',
  44. width: '100%',
  45. height: '100%',
  46. paddingTop: 'inherit',
  47. paddingBottom: 'var(--size-menu, 4rem)',
  48. zIndex: 2,
  49. [minWidthFactor(3)]: {
  50. position: 'absolute',
  51. right: 0,
  52. width: `calc(${configVar('base-width')} - var(--size-menu, 4rem))`,
  53. marginLeft: 'auto',
  54. paddingBottom: 0,
  55. },
  56. '> *': {
  57. display: 'block',
  58. width: '100%',
  59. height: '100%',
  60. backgroundColor: 'white',
  61. },
  62. })
  63. const SidebarMainOverflow = styled('div')({
  64. width: '100%',
  65. height: '100%',
  66. overflow: 'auto',
  67. // overflow: 'overlay',
  68. scrollbarWidth: 'none',
  69. position: 'relative',
  70. zIndex: 1,
  71. '::-webkit-scrollbar': {
  72. display: 'none',
  73. },
  74. })
  75. const OpenSidebarMain = styled(SidebarMain)({
  76. right: 0,
  77. })
  78. const SidebarMenu = styled('div')({
  79. boxSizing: 'border-box',
  80. overflow: 'auto',
  81. // overflow: 'overlay',
  82. scrollbarWidth: 'none',
  83. '::-webkit-scrollbar': {
  84. display: 'none',
  85. },
  86. position: 'fixed',
  87. bottom: 0,
  88. left: 0,
  89. width: '100%',
  90. height: 'var(--size-menu, 4rem)',
  91. zIndex: 1,
  92. '> *': {
  93. display: 'block',
  94. width: '100%',
  95. height: '100%',
  96. backgroundColor: 'white',
  97. },
  98. [minWidthFactor(3)]: {
  99. top: 0,
  100. marginLeft: 'auto',
  101. position: 'absolute',
  102. height: '100%',
  103. paddingTop: 'inherit',
  104. overflow: 'auto',
  105. zIndex: 'auto',
  106. },
  107. })
  108. const SidebarMenuSize = styled('div')({
  109. display: 'flex',
  110. width: '100%',
  111. height: '100%',
  112. maxWidth: `calc(${configVar('base-width')} * 2)`,
  113. margin: '0 auto',
  114. position: 'relative',
  115. zIndex: 1,
  116. [minWidthFactor(3)]: {
  117. maxWidth: 'none',
  118. marginRight: 0,
  119. flexDirection: 'column',
  120. justifyContent: 'space-between',
  121. alignItems: 'flex-end',
  122. },
  123. })
  124. const SidebarMenuGroup = styled('div')({
  125. display: 'contents',
  126. [minWidthFactor(3)]: {
  127. width: '100%',
  128. display: 'block',
  129. },
  130. })
  131. const MoreItems = styled('div')({
  132. position: 'fixed',
  133. top: 0,
  134. left: '-100%',
  135. width: '100%',
  136. height: '100%',
  137. paddingTop: 'var(--height-topbar, 4rem)',
  138. paddingBottom: 'var(--size-menu, 4rem)',
  139. zIndex: -1,
  140. boxSizing: 'border-box',
  141. [minWidthFactor(3)]: {
  142. display: 'contents',
  143. },
  144. })
  145. const OpenMoreItems = styled(MoreItems)({
  146. left: 0,
  147. })
  148. const MoreItemsScroll = styled('div')({
  149. width: '100%',
  150. height: '100%',
  151. overflow: 'auto',
  152. [minWidthFactor(3)]: {
  153. display: 'contents',
  154. },
  155. })
  156. const MorePrimarySidebarMenuGroup = styled(SidebarMenuGroup)({
  157. [minWidthFactor(3)]: {
  158. flex: 'auto',
  159. },
  160. })
  161. const MoreSecondarySidebarMenuGroup = styled(SidebarMenuGroup)({
  162. [minWidthFactor(3)]: {
  163. order: 4,
  164. },
  165. })
  166. const SidebarMenuItem = styled('span')({
  167. width: 0,
  168. flex: 'auto',
  169. height: 'var(--size-menu, 4rem)',
  170. '> *': {
  171. height: '100%',
  172. display: 'flex',
  173. alignItems: 'center',
  174. textDecoration: 'none',
  175. width: '100%',
  176. },
  177. [minWidthFactor(3)]: {
  178. width: 'auto !important',
  179. flex: '0 1 auto',
  180. '> *': {
  181. height: 'auto',
  182. },
  183. },
  184. })
  185. const MoreSidebarMenuItem = styled('span')({
  186. display: 'block',
  187. height: 'var(--size-menu, 4rem)',
  188. '> *': {
  189. height: '100%',
  190. display: 'flex',
  191. alignItems: 'center',
  192. textDecoration: 'none',
  193. width: '100%',
  194. },
  195. [minWidthFactor(3)]: {
  196. width: 'auto !important',
  197. flex: '0 1 auto',
  198. },
  199. })
  200. const MoreToggleSidebarMenuItem = styled(SidebarMenuItem)({
  201. [minWidthFactor(3)]: {
  202. display: 'none',
  203. },
  204. })
  205. export const SidebarMenuItemIcon = styled('span')({
  206. display: 'block',
  207. [minWidthFactor(3)]: {
  208. width: 'var(--size-menu, 4rem)',
  209. height: 'var(--size-menu, 4rem)',
  210. display: 'grid',
  211. placeContent: 'center',
  212. },
  213. })
  214. export const MoreSidebarMenuItemIcon = styled('span')({
  215. marginRight: '1rem',
  216. display: 'block',
  217. [minWidthFactor(3)]: {
  218. width: 'var(--size-menu, 4rem)',
  219. height: 'var(--size-menu, 4rem)',
  220. display: 'grid',
  221. placeContent: 'center',
  222. marginRight: 0,
  223. },
  224. })
  225. export const SidebarMenuContainer = styled('span')({
  226. boxSizing: 'border-box',
  227. display: 'grid',
  228. placeContent: 'center',
  229. width: '100%',
  230. textAlign: 'center',
  231. [minWidthFactor(3)]: {
  232. display: 'flex',
  233. justifyContent: 'flex-start',
  234. alignItems: 'center',
  235. width: `${configVar('base-width')}`,
  236. marginLeft: 'auto',
  237. paddingRight: '1rem',
  238. textAlign: 'left',
  239. boxSizing: 'border-box',
  240. },
  241. })
  242. export const MoreSidebarMenuContainer = styled('div')({
  243. display: 'flex',
  244. justifyContent: 'flex-start',
  245. alignItems: 'center',
  246. width: `calc(${configVar('base-width')} * 2)`,
  247. margin: '0 auto',
  248. padding: '0 1rem',
  249. textAlign: 'left',
  250. boxSizing: 'border-box',
  251. [minWidthFactor(3)]: {
  252. marginRight: 0,
  253. width: `${configVar('base-width')}`,
  254. paddingLeft: 0,
  255. },
  256. })
  257. export const ContentContainer = styled('div')({
  258. padding: '0 1rem',
  259. boxSizing: 'border-box',
  260. width: '100%',
  261. maxWidth: `calc(${configVar('base-width')} * 2)`,
  262. marginRight: 'auto',
  263. marginLeft: 'auto',
  264. [minWidthFactor(3)]: {
  265. marginLeft: 0,
  266. },
  267. })
  268. export const SidebarMainContainer = styled('div')({
  269. padding: '0 1rem',
  270. boxSizing: 'border-box',
  271. width: '100%',
  272. maxWidth: `calc(${configVar('base-width')} * 2)`,
  273. margin: '0 auto',
  274. [minWidthFactor(3)]: {
  275. maxWidth: 'none',
  276. },
  277. })
  278. type BaseMenuItem = {
  279. label: React.ReactChild,
  280. icon: React.ReactChild,
  281. url: unknown,
  282. }
  283. export type MenuItem = BaseMenuItem & {
  284. id: string,
  285. secondary?: boolean,
  286. }
  287. type Props = {
  288. brand?: React.ReactNode,
  289. sidebarMain?: React.ReactChild,
  290. sidebarMainOpen?: boolean,
  291. sidebarMenuItems: MenuItem[],
  292. moreItemsOpen?: boolean,
  293. moreLinkMenuItem: BaseMenuItem,
  294. menuLink?: React.ReactNode,
  295. userLink?: React.ReactNode,
  296. moreLinkComponent: React.ElementType,
  297. linkComponent: React.ElementType,
  298. topBarCenter?: React.ReactChild,
  299. topBarComponent?: React.ElementType,
  300. sidebarMainComponent?: React.ElementType,
  301. sidebarMenuComponent?: React.ElementType,
  302. }
  303. export const Layout: React.FC<Props> = ({
  304. brand,
  305. sidebarMain,
  306. sidebarMainOpen,
  307. sidebarMenuItems,
  308. moreItemsOpen,
  309. moreLinkMenuItem,
  310. menuLink,
  311. userLink,
  312. moreLinkComponent: MoreLinkComponent,
  313. linkComponent: LinkComponent,
  314. topBarCenter,
  315. children,
  316. topBarComponent: TopBarComponent = 'div',
  317. sidebarMainComponent: SidebarMainWrapperComponent = 'div',
  318. sidebarMenuComponent: SidebarMenuComponent = 'div',
  319. }) => {
  320. const SidebarMainComponent = sidebarMainOpen ? OpenSidebarMain : SidebarMain
  321. const MoreItemsComponent = moreItemsOpen ? OpenMoreItems : MoreItems
  322. const primarySidebarMenuItems = sidebarMenuItems.filter(s => !s.secondary)
  323. const secondarySidebarMenuItems = sidebarMenuItems.filter(s => s.secondary)
  324. const visibleSecondarySidebarMenuItems = secondarySidebarMenuItems.slice(0, 1)
  325. const moreSecondarySidebarMenuItems = secondarySidebarMenuItems.slice(1)
  326. const visiblePrimarySidebarMenuItems = (
  327. visibleSecondarySidebarMenuItems.length === 1
  328. ? primarySidebarMenuItems.slice(0, 3)
  329. : primarySidebarMenuItems.slice(0, 4)
  330. )
  331. const morePrimarySidebarMenuItems = (
  332. visibleSecondarySidebarMenuItems.length === 1
  333. ? primarySidebarMenuItems.slice(3)
  334. : primarySidebarMenuItems.slice(4)
  335. )
  336. return (
  337. <>
  338. <Config />
  339. {
  340. (sidebarMainOpen || moreItemsOpen)
  341. && (
  342. <DisableScrolling />
  343. )
  344. }
  345. <>
  346. {
  347. (
  348. brand
  349. || userLink
  350. || topBarCenter
  351. || sidebarMain
  352. )
  353. && (
  354. <TopBar
  355. span="wide"
  356. brand={brand}
  357. menuLink={sidebarMain ? menuLink : undefined}
  358. userLink={userLink}
  359. baseComponent={TopBarComponent}
  360. >
  361. {topBarCenter}
  362. </TopBar>
  363. )
  364. }
  365. <SidebarBase>
  366. <SidebarMenu>
  367. <SidebarMenuComponent>
  368. <SidebarMenuSize>
  369. <SidebarMenuGroup>
  370. {visiblePrimarySidebarMenuItems.map((item) => {
  371. return (
  372. <SidebarMenuItem
  373. key={item.id}
  374. >
  375. <LinkComponent
  376. {...item}
  377. />
  378. </SidebarMenuItem>
  379. )
  380. })}
  381. </SidebarMenuGroup>
  382. <MoreItemsComponent>
  383. <MoreItemsScroll>
  384. <MorePrimarySidebarMenuGroup>
  385. {morePrimarySidebarMenuItems.map((item) => {
  386. return (
  387. <MoreSidebarMenuItem
  388. key={item.id}
  389. >
  390. <MoreLinkComponent
  391. {...item}
  392. />
  393. </MoreSidebarMenuItem>
  394. )
  395. })}
  396. </MorePrimarySidebarMenuGroup>
  397. <MoreSecondarySidebarMenuGroup>
  398. {moreSecondarySidebarMenuItems.map((item) => {
  399. return (
  400. <MoreSidebarMenuItem
  401. key={item.id}
  402. >
  403. <MoreLinkComponent
  404. {...item}
  405. />
  406. </MoreSidebarMenuItem>
  407. )
  408. })}
  409. </MoreSecondarySidebarMenuGroup>
  410. </MoreItemsScroll>
  411. </MoreItemsComponent>
  412. {
  413. (
  414. morePrimarySidebarMenuItems.length > 0
  415. || moreSecondarySidebarMenuItems.length > 0
  416. )
  417. && (
  418. <MoreToggleSidebarMenuItem>
  419. <SidebarMenuItem>
  420. <LinkComponent
  421. {...moreLinkMenuItem}
  422. />
  423. </SidebarMenuItem>
  424. </MoreToggleSidebarMenuItem>
  425. )
  426. }
  427. {
  428. visibleSecondarySidebarMenuItems.length > 0
  429. && (
  430. <SidebarMenuGroup>
  431. {visibleSecondarySidebarMenuItems.map((item) => (
  432. <SidebarMenuItem
  433. key={item.id}
  434. >
  435. <LinkComponent
  436. {...item}
  437. />
  438. </SidebarMenuItem>
  439. ))}
  440. </SidebarMenuGroup>
  441. )
  442. }
  443. </SidebarMenuSize>
  444. </SidebarMenuComponent>
  445. </SidebarMenu>
  446. {
  447. (sidebarMain as unknown)
  448. && (
  449. <SidebarMainComponent>
  450. <SidebarMainWrapperComponent>
  451. <SidebarMainOverflow>
  452. {sidebarMain}
  453. </SidebarMainOverflow>
  454. </SidebarMainWrapperComponent>
  455. </SidebarMainComponent>
  456. )
  457. }
  458. </SidebarBase>
  459. <ContentBase>
  460. {children}
  461. </ContentBase>
  462. </>
  463. </>
  464. )
  465. }