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.
 
 
 

185 lines
5.9 KiB

  1. // TODO figure out how to refactor left sidebar with menu widget
  2. import * as React from 'react';
  3. import {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. className,
  33. ...etcProps
  34. }, ref) => {
  35. const primarySidebarMenuItems = sidebarMenuItems.filter(s => !s.secondary)
  36. const secondarySidebarMenuItems = sidebarMenuItems.filter(s => s.secondary)
  37. const visibleSecondarySidebarMenuItems = secondarySidebarMenuItems.slice(0, 1)
  38. const moreSecondarySidebarMenuItems = secondarySidebarMenuItems.slice(1)
  39. const visiblePrimarySidebarMenuItems = (
  40. visibleSecondarySidebarMenuItems.length === 1
  41. ? primarySidebarMenuItems.slice(0, 3)
  42. : primarySidebarMenuItems.slice(0, 4)
  43. )
  44. const morePrimarySidebarMenuItems = (
  45. visibleSecondarySidebarMenuItems.length === 1
  46. ? primarySidebarMenuItems.slice(3)
  47. : primarySidebarMenuItems.slice(4)
  48. )
  49. const hasMoreSidebarMenuItems = (
  50. morePrimarySidebarMenuItems.length > 0
  51. || moreSecondarySidebarMenuItems.length > 0
  52. )
  53. return (
  54. <div
  55. {...etcProps}
  56. className={clsx(
  57. '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',
  58. className,
  59. )}
  60. ref={ref}
  61. >
  62. <div
  63. data-viewfinder="menu"
  64. className="box-border scrollbar-hidden overflow-auto fixed bottom-0 left-0 w-full h-menu z-[3] bg-sidebar-menu md:top-0 md:ml-auto md:absolute md:h-full md:pt-inherit md:overflow-auto md:z-auto"
  65. >
  66. <div className="w-full h-full bg-sidebar-menu">
  67. <div
  68. 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"
  69. >
  70. <div
  71. className="contents md:w-full md:block"
  72. >
  73. {visiblePrimarySidebarMenuItems.map(({ id, ...item }) => (
  74. <span
  75. key={id}
  76. className="w-0 flex-auto h-menu md:flex-shrink-0 md:flex-grow"
  77. data-viewfinder="menu-item"
  78. >
  79. <LinkComponent
  80. {...item}
  81. />
  82. </span>
  83. ))}
  84. </div>
  85. <div
  86. className={clsx(
  87. 'fixed top-0 w-full h-full pt-topbar pb-menu -z-[1] box-border md:contents',
  88. moreItemsOpen ? 'left-0': '-left-full',
  89. )}
  90. >
  91. <div
  92. className="w-full h-full overflow-auto bg-sidebar-menu md:contents"
  93. >
  94. <div
  95. className="contents md:w-full md:block md:flex-auto"
  96. >
  97. {morePrimarySidebarMenuItems.map(({ id, ...item }) => (
  98. <span
  99. key={id}
  100. data-viewfinder="menu-item"
  101. className="h-menu block md:flex-shrink-0 md:flex-grow md:flex-auto"
  102. >
  103. <MoreLinkComponent
  104. {...item}
  105. />
  106. </span>
  107. ))}
  108. </div>
  109. <div
  110. className="contents md:w-full md:block md:order-4"
  111. >
  112. {moreSecondarySidebarMenuItems.map(({ id, ...item }) => (
  113. <span
  114. key={id}
  115. data-viewfinder="menu-item"
  116. className="h-menu block md:flex-shrink-0 md:flex-grow md:flex-auto"
  117. >
  118. <MoreLinkComponent
  119. {...item}
  120. />
  121. </span>
  122. ))}
  123. </div>
  124. </div>
  125. </div>
  126. {hasMoreSidebarMenuItems && (
  127. <span
  128. className="contents md:hidden"
  129. >
  130. <span
  131. className="w-0 flex-auto h-menu md:flex-shrink-0 md:flex-grow md:flex-auto"
  132. data-viewfinder="menu-item"
  133. >
  134. <LinkComponent
  135. {...moreLinkItem}
  136. />
  137. </span>
  138. </span>
  139. )}
  140. {visibleSecondarySidebarMenuItems.length > 0 && (
  141. <div
  142. className="contents md:w-full md:block"
  143. >
  144. {visibleSecondarySidebarMenuItems.map(({ id, ...item }) => (
  145. <span
  146. key={id}
  147. className="w-0 flex-auto h-menu md:flex-shrink-0 md:flex-grow md:flex-auto"
  148. data-viewfinder="menu-item"
  149. >
  150. <LinkComponent
  151. {...item}
  152. />
  153. </span>
  154. ))}
  155. </div>
  156. )}
  157. </div>
  158. </div>
  159. </div>
  160. {children && (
  161. <div
  162. className={clsx(
  163. 'box-border fixed top-0 w-full h-full pt-inherit pb-menu z-[2] bg-sidebar',
  164. open ? 'right-0': 'right-full',
  165. 'md:absolute md:right-0 md:ml-0 md:pb-0 md:w-[calc(var(--base-width)-var(--size-menu,4rem))]'
  166. )}
  167. >
  168. <div
  169. className="w-full h-full overflow-auto scrollbar-hidden relative z-[1]"
  170. >
  171. {children}
  172. </div>
  173. </div>
  174. )}
  175. </div>
  176. )
  177. });