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.
 
 
 

181 lines
5.9 KiB

  1. // TODO figure out how to refactor left sidebar with menu widget
  2. import * as React from 'react';
  3. import {SpanValues as Span} from '@tesseract-design/viewfinder-base';
  4. import clsx from 'clsx';
  5. type BaseMenuItem = {
  6. label: React.ReactNode,
  7. icon: React.ReactNode,
  8. url: unknown,
  9. }
  10. export type MenuItem = BaseMenuItem & {
  11. id: string,
  12. secondary?: boolean,
  13. }
  14. export type LeftSidebarWithMenuBaseProps = Omit<React.HTMLProps<HTMLDivElement>, 'span'> & {
  15. span?: Span,
  16. items?: MenuItem[],
  17. linkComponent?: React.ElementType,
  18. moreLinkComponent?: React.ElementType,
  19. moreLinkItem?: BaseMenuItem,
  20. moreItemsOpen?: boolean,
  21. open?: boolean,
  22. }
  23. export const LeftSidebarWithMenuBase = React.forwardRef<HTMLDivElement, LeftSidebarWithMenuBaseProps>(({
  24. children,
  25. span = 'wide',
  26. open = false,
  27. items: sidebarMenuItems = [],
  28. linkComponent: LinkComponent = 'a',
  29. moreLinkComponent: MoreLinkComponent = LinkComponent,
  30. moreLinkItem,
  31. moreItemsOpen = false,
  32. ...etcProps
  33. }, ref) => {
  34. const primarySidebarMenuItems = sidebarMenuItems.filter(s => !s.secondary)
  35. const secondarySidebarMenuItems = sidebarMenuItems.filter(s => s.secondary)
  36. const visibleSecondarySidebarMenuItems = secondarySidebarMenuItems.slice(0, 1)
  37. const moreSecondarySidebarMenuItems = secondarySidebarMenuItems.slice(1)
  38. const visiblePrimarySidebarMenuItems = (
  39. visibleSecondarySidebarMenuItems.length === 1
  40. ? primarySidebarMenuItems.slice(0, 3)
  41. : primarySidebarMenuItems.slice(0, 4)
  42. )
  43. const morePrimarySidebarMenuItems = (
  44. visibleSecondarySidebarMenuItems.length === 1
  45. ? primarySidebarMenuItems.slice(3)
  46. : primarySidebarMenuItems.slice(4)
  47. )
  48. const hasMoreSidebarMenuItems = (
  49. morePrimarySidebarMenuItems.length > 0
  50. || moreSecondarySidebarMenuItems.length > 0
  51. )
  52. return (
  53. <div
  54. className="left-sidebar-with-menu-base box-border overflow-hidden contents left-[calc(var(--base-width)*-1)] md:fixed md:top-0 md:left-0 md:w-[calc(50%-var(--base-width)*0.5)] md:h-full md:block"
  55. {...etcProps}
  56. ref={ref}
  57. >
  58. <div
  59. data-viewfinder="menu"
  60. className="box-border scrollbar-hidden overflow-auto fixed bottom-0 left-0 w-full h-menu z-[3] bg-bg md:top-0 md:ml-auto md:absolute md:h-full md:pt-inherit md:overflow-auto md:z-auto"
  61. >
  62. <div className="w-full h-full bg-bg">
  63. <div
  64. className="flex w-full h-full max-w-[calc(var(--base-width)*2)] mx-auto relative z-[1] md:max-w-none md:mr-0 md:flex-col md:justify-between md:items-end"
  65. >
  66. <div
  67. className="contents md:w-full md:block"
  68. >
  69. {visiblePrimarySidebarMenuItems.map(({ id, ...item }) => (
  70. <span
  71. key={id}
  72. className="w-0 flex-auto h-menu md:flex-shrink-0 md:flex-grow"
  73. data-viewfinder="menu-item"
  74. >
  75. <LinkComponent
  76. {...item}
  77. />
  78. </span>
  79. ))}
  80. </div>
  81. <div
  82. className={clsx(
  83. 'fixed top-0 w-full h-full pt-topbar pb-menu -z-[1] box-border md:contents',
  84. moreItemsOpen ? 'left-0': '-left-full',
  85. )}
  86. >
  87. <div
  88. className="w-full h-full overflow-auto bg-bg md:contents"
  89. >
  90. <div
  91. className="contents md:w-full md:block md:flex-auto"
  92. >
  93. {morePrimarySidebarMenuItems.map(({ id, ...item }) => (
  94. <span
  95. key={id}
  96. data-viewfinder="menu-item"
  97. className="h-menu block md:flex-shrink-0 md:flex-grow md:flex-auto"
  98. >
  99. <MoreLinkComponent
  100. {...item}
  101. />
  102. </span>
  103. ))}
  104. </div>
  105. <div
  106. className="contents md:w-full md:block md:order-4"
  107. >
  108. {moreSecondarySidebarMenuItems.map(({ id, ...item }) => (
  109. <span
  110. key={id}
  111. data-viewfinder="menu-item"
  112. className="h-menu block md:flex-shrink-0 md:flex-grow md:flex-auto"
  113. >
  114. <MoreLinkComponent
  115. {...item}
  116. />
  117. </span>
  118. ))}
  119. </div>
  120. </div>
  121. </div>
  122. {hasMoreSidebarMenuItems && (
  123. <span
  124. className="contents md:hidden"
  125. >
  126. <span
  127. className="w-0 flex-auto h-menu md:flex-shrink-0 md:flex-grow md:flex-auto"
  128. data-viewfinder="menu-item"
  129. >
  130. <LinkComponent
  131. {...moreLinkItem}
  132. />
  133. </span>
  134. </span>
  135. )}
  136. {visibleSecondarySidebarMenuItems.length > 0 && (
  137. <div
  138. className="contents md:w-full md:block"
  139. >
  140. {visibleSecondarySidebarMenuItems.map(({ id, ...item }) => (
  141. <span
  142. key={id}
  143. className="w-0 flex-auto h-menu md:flex-shrink-0 md:flex-grow md:flex-auto"
  144. data-viewfinder="menu-item"
  145. >
  146. <LinkComponent
  147. {...item}
  148. />
  149. </span>
  150. ))}
  151. </div>
  152. )}
  153. </div>
  154. </div>
  155. </div>
  156. {children && (
  157. <div
  158. className={clsx(
  159. 'box-border fixed top-0 w-full h-full pt-inherit pb-menu z-[2] bg-bg',
  160. open ? 'right-0': 'right-full',
  161. 'md:absolute md:right-0 md:ml-0 md:pb-0 md:w-[calc(var(--base-width)-var(--size-menu,4rem))]'
  162. )}
  163. >
  164. <div
  165. className="bg-bg w-full h-full overflow-auto scrollbar-hidden relative z-[1]"
  166. >
  167. {children}
  168. </div>
  169. </div>
  170. )}
  171. </div>
  172. )
  173. });