@ghxstship/xos-ui 0.0.0-stage → 4.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (487) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -2
  3. package/package.json +76 -4
  4. package/scripts/generate-stories.mjs +85 -0
  5. package/src/AIDisclosureBadge/README.md +7 -0
  6. package/src/AccessGridMatrix/README.md +3 -0
  7. package/src/Accordion/README.md +7 -0
  8. package/src/ActivityFeedItem/README.md +3 -0
  9. package/src/AgencySlate/README.md +3 -0
  10. package/src/AppShell/README.md +5 -0
  11. package/src/ApplicationForm/README.md +3 -0
  12. package/src/AssertionRankBadge/README.md +5 -0
  13. package/src/AvailabilityCalendar/README.md +3 -0
  14. package/src/Avatar/Avatar.tsx +56 -0
  15. package/src/Avatar/README.md +3 -0
  16. package/src/Avatar/avatar-class.ts +19 -0
  17. package/src/AvatarStack/README.md +3 -0
  18. package/src/Badge/README.md +3 -0
  19. package/src/Banner/README.md +3 -0
  20. package/src/BidSheet/README.md +3 -0
  21. package/src/Board/README.md +6 -0
  22. package/src/BrandMark/BrandMark.tsx +95 -0
  23. package/src/BrandMark/README.md +7 -0
  24. package/src/BrandSlot/README.md +6 -0
  25. package/src/Breadcrumbs/README.md +3 -0
  26. package/src/BudgetGrid/README.md +10 -0
  27. package/src/BulkActionBar/README.md +3 -0
  28. package/src/Button/Button.tsx +100 -0
  29. package/src/Button/README.md +10 -0
  30. package/src/Button/button-class.ts +54 -0
  31. package/src/ButtonLink/README.md +7 -0
  32. package/src/Calendar/README.md +3 -0
  33. package/src/Card/README.md +3 -0
  34. package/src/Chart/README.md +9 -0
  35. package/src/Charts/Calendar.tsx +228 -0
  36. package/src/Charts/Chart.tsx +547 -0
  37. package/src/Charts/Charts.tsx +20 -0
  38. package/src/Charts/Charts.types.ts +40 -0
  39. package/src/Charts/Gallery.tsx +25 -0
  40. package/src/Charts/Heatmap.tsx +116 -0
  41. package/src/Charts/OrgChart.tsx +53 -0
  42. package/src/Charts/ResourceSchedule.tsx +91 -0
  43. package/src/Charts/Sparkline.tsx +59 -0
  44. package/src/Charts/Timeline.tsx +777 -0
  45. package/src/Charts/categories.ts +21 -0
  46. package/src/Charts/timeline-plan.ts +191 -0
  47. package/src/Checkbox/README.md +6 -0
  48. package/src/ChecklistRunner/README.md +3 -0
  49. package/src/Chips/Badge.tsx +27 -0
  50. package/src/Chips/Chips.shared.tsx +34 -0
  51. package/src/Chips/Chips.tsx +21 -0
  52. package/src/Chips/Chips.types.ts +35 -0
  53. package/src/Chips/DepartmentChip.tsx +16 -0
  54. package/src/Chips/PhaseChip.tsx +16 -0
  55. package/src/Chips/PropertyChip.tsx +15 -0
  56. package/src/Chips/RecordKindChip.tsx +14 -0
  57. package/src/Chips/StateChip.tsx +19 -0
  58. package/src/Chips/Tag.tsx +47 -0
  59. package/src/Chips/URIDChip.tsx +14 -0
  60. package/src/Chips/chipClass.tsx +8 -0
  61. package/src/Chips/codeClass.tsx +6 -0
  62. package/src/ClockButton/README.md +6 -0
  63. package/src/CodeSample/README.md +7 -0
  64. package/src/ColorPicker/README.md +5 -0
  65. package/src/Combobox/README.md +7 -0
  66. package/src/CommandMenu/CommandMenu.tsx +179 -0
  67. package/src/CommandMenu/README.md +5 -0
  68. package/src/ComparisonTable/README.md +9 -0
  69. package/src/Compass/ChecklistRunner.tsx +128 -0
  70. package/src/Compass/ClockButton.tsx +57 -0
  71. package/src/Compass/Compass.shared.tsx +58 -0
  72. package/src/Compass/Compass.tsx +26 -0
  73. package/src/Compass/Compass.types.ts +79 -0
  74. package/src/Compass/DaySheetView.tsx +104 -0
  75. package/src/Compass/ForceUpdateScreen.tsx +29 -0
  76. package/src/Compass/LiveActivity.tsx +60 -0
  77. package/src/Compass/OfflineBanner.tsx +31 -0
  78. package/src/Compass/PhotoCapture.tsx +69 -0
  79. package/src/Compass/QuickIncidentSheet.tsx +46 -0
  80. package/src/Compass/ScanSheet.tsx +83 -0
  81. package/src/Compass/ShiftCard.tsx +55 -0
  82. package/src/Compass/SignaturePad.tsx +71 -0
  83. package/src/Compass/SyncConflictSheet.tsx +60 -0
  84. package/src/ConsentBanner/ConsentBanner.tsx +210 -0
  85. package/src/ConsentBanner/ConsentBannerStatic.tsx +163 -0
  86. package/src/ConsentBanner/ConsentSettings.tsx +213 -0
  87. package/src/ConsentBanner/README.md +101 -0
  88. package/src/ConsentBanner/consent.ts +238 -0
  89. package/src/ConsentBanner/index.ts +40 -0
  90. package/src/ConsentBanner/static.ts +11 -0
  91. package/src/ConsentBanner/useConsent.ts +160 -0
  92. package/src/ContextMenu/README.md +3 -0
  93. package/src/CoordinateMatrix/README.md +6 -0
  94. package/src/CredentialBadge/README.md +7 -0
  95. package/src/CurrencyInput/README.md +7 -0
  96. package/src/DataTable/DataTable.tsx +1034 -0
  97. package/src/DataTable/DataTableResizer.tsx +72 -0
  98. package/src/DataTable/DataTableVirtualizer.tsx +58 -0
  99. package/src/DataTable/README.md +9 -0
  100. package/src/DatePicker/README.md +5 -0
  101. package/src/DateRangePicker/README.md +3 -0
  102. package/src/DaySheetView/README.md +3 -0
  103. package/src/DepartmentChip/README.md +3 -0
  104. package/src/DepartmentGlyph/README.md +18 -0
  105. package/src/Dialog/README.md +3 -0
  106. package/src/DiffViewer/README.md +3 -0
  107. package/src/Divider/README.md +3 -0
  108. package/src/Drawer/README.md +3 -0
  109. package/src/DropdownMenu/README.md +3 -0
  110. package/src/EPKViewer/README.md +3 -0
  111. package/src/Editing/ColorPicker.tsx +125 -0
  112. package/src/Editing/DiffViewer.tsx +49 -0
  113. package/src/Editing/Editing.tsx +29 -0
  114. package/src/Editing/Editing.types.ts +71 -0
  115. package/src/Editing/FilterBuilder.tsx +72 -0
  116. package/src/Editing/QuickAdd.tsx +102 -0
  117. package/src/Editing/QuickAddChips.tsx +180 -0
  118. package/src/Editing/contrastRatio.tsx +11 -0
  119. package/src/Editing/parseQuickAdd.tsx +187 -0
  120. package/src/Editing/quickAddCore.ts +204 -0
  121. package/src/Editing/timeShorthand.ts +157 -0
  122. package/src/Editing/useQuickAdd.ts +201 -0
  123. package/src/EmergencyCodeCard/README.md +10 -0
  124. package/src/EmptyState/README.md +5 -0
  125. package/src/EngagementTimeline/README.md +3 -0
  126. package/src/ErrorState/README.md +3 -0
  127. package/src/FAQ/README.md +7 -0
  128. package/src/FeatureGrid/README.md +7 -0
  129. package/src/Feedback/Banner.tsx +40 -0
  130. package/src/Feedback/EmptyState.tsx +32 -0
  131. package/src/Feedback/ErrorState.tsx +32 -0
  132. package/src/Feedback/Feedback.shared.tsx +38 -0
  133. package/src/Feedback/Feedback.tsx +18 -0
  134. package/src/Feedback/Feedback.types.ts +40 -0
  135. package/src/Feedback/InlineAlert.tsx +19 -0
  136. package/src/Feedback/RefusalNotice.tsx +46 -0
  137. package/src/Feedback/RegionBoundary.tsx +102 -0
  138. package/src/Feedback/Toast.tsx +31 -0
  139. package/src/Feedback/ToastRegion.tsx +26 -0
  140. package/src/Feedback/UndoToast.tsx +97 -0
  141. package/src/FeedbackWidget/README.md +3 -0
  142. package/src/Field/Field.tsx +81 -0
  143. package/src/FileDrop/README.md +3 -0
  144. package/src/FilePreviewer/README.md +3 -0
  145. package/src/FilterBuilder/README.md +3 -0
  146. package/src/ForceUpdateScreen/README.md +3 -0
  147. package/src/Forms/Checkbox.tsx +14 -0
  148. package/src/Forms/CurrencyInput.tsx +40 -0
  149. package/src/Forms/Forms.shared.tsx +5 -0
  150. package/src/Forms/Forms.tsx +20 -0
  151. package/src/Forms/Forms.types.ts +33 -0
  152. package/src/Forms/Input.tsx +28 -0
  153. package/src/Forms/Money.tsx +15 -0
  154. package/src/Forms/NumberInput.tsx +85 -0
  155. package/src/Forms/Radio.tsx +31 -0
  156. package/src/Forms/Select.tsx +54 -0
  157. package/src/Forms/Switch.tsx +14 -0
  158. package/src/Forms/Textarea.tsx +30 -0
  159. package/src/Gallery/README.md +5 -0
  160. package/src/GateLifecycleRail/README.md +7 -0
  161. package/src/GateReadinessPanel/README.md +8 -0
  162. package/src/Gateway/AgencySlate.tsx +76 -0
  163. package/src/Gateway/ApplicationForm.tsx +48 -0
  164. package/src/Gateway/AvailabilityCalendar.tsx +82 -0
  165. package/src/Gateway/EPKViewer.tsx +77 -0
  166. package/src/Gateway/EngagementTimeline.tsx +43 -0
  167. package/src/Gateway/Gateway.shared.tsx +5 -0
  168. package/src/Gateway/Gateway.tsx +30 -0
  169. package/src/Gateway/Gateway.types.ts +69 -0
  170. package/src/Gateway/OnboardingPacket.tsx +61 -0
  171. package/src/Gateway/OpportunityCard.tsx +73 -0
  172. package/src/Gateway/OpportunityFilters.tsx +64 -0
  173. package/src/Gateway/PaymentTracker.tsx +71 -0
  174. package/src/Gateway/PayoutDetailsForm.tsx +62 -0
  175. package/src/Gateway/ProfileEditor.tsx +45 -0
  176. package/src/Gateway/ProgressBar.tsx +30 -0
  177. package/src/Gateway/RatingDialog.tsx +83 -0
  178. package/src/Gateway/UpNextCard.tsx +76 -0
  179. package/src/Glyphs/Glyphs.tsx +66 -0
  180. package/src/Grids/BidSheet.tsx +63 -0
  181. package/src/Grids/BudgetGrid.tsx +164 -0
  182. package/src/Grids/Grids.shared.tsx +5 -0
  183. package/src/Grids/Grids.tsx +11 -0
  184. package/src/Grids/Grids.types.ts +38 -0
  185. package/src/Grids/SpreadsheetGrid.tsx +445 -0
  186. package/src/Grids/columnTotal.tsx +14 -0
  187. package/src/Grids/glForCode.tsx +11 -0
  188. package/src/Grids/parseCell.tsx +16 -0
  189. package/src/Help/FeedbackWidget.tsx +89 -0
  190. package/src/Help/Help.tsx +14 -0
  191. package/src/Help/Help.types.ts +51 -0
  192. package/src/Help/HelpPanel.tsx +116 -0
  193. package/src/Help/NotificationCenter.tsx +100 -0
  194. package/src/Help/OnboardingChecklist.tsx +57 -0
  195. package/src/Help/ShortcutEditor.tsx +81 -0
  196. package/src/Help/WhatsNew.tsx +33 -0
  197. package/src/HelpPanel/README.md +5 -0
  198. package/src/HoverCard/README.md +3 -0
  199. package/src/Icon/Icon.tsx +47 -0
  200. package/src/Icon/README.md +9 -0
  201. package/src/IconButton/README.md +6 -0
  202. package/src/Identity/BrandSlot.tsx +20 -0
  203. package/src/Identity/BrandSlotFrame.tsx +38 -0
  204. package/src/Identity/CredentialBadge.tsx +92 -0
  205. package/src/Identity/Identity.tsx +14 -0
  206. package/src/Identity/Identity.types.ts +25 -0
  207. package/src/Identity/PresenceIndicator.tsx +37 -0
  208. package/src/Identity/QRCode.tsx +22 -0
  209. package/src/Identity/WhiteLabelPreview.tsx +33 -0
  210. package/src/Identity/qrPath.tsx +16 -0
  211. package/src/Identity/tokenVariable.tsx +9 -0
  212. package/src/InlineAlert/README.md +3 -0
  213. package/src/Input/README.md +8 -0
  214. package/src/Kbd/Kbd.tsx +30 -0
  215. package/src/Kbd/README.md +6 -0
  216. package/src/Layout/AppShell.tsx +151 -0
  217. package/src/Layout/Layout.tsx +14 -0
  218. package/src/Layout/Layout.types.ts +43 -0
  219. package/src/Layout/PageHeader.tsx +22 -0
  220. package/src/Layout/ResizablePanel.tsx +81 -0
  221. package/src/Layout/ScrollRegion.tsx +107 -0
  222. package/src/Layout/SplitView.tsx +78 -0
  223. package/src/Layout/Stepper.tsx +39 -0
  224. package/src/LegalText/LegalText.tsx +289 -0
  225. package/src/LegalText/README.md +10 -0
  226. package/src/LegalText/index.ts +29 -0
  227. package/src/LegalText/legal-markdown.ts +581 -0
  228. package/src/Link/Link.tsx +25 -0
  229. package/src/Link/README.md +5 -0
  230. package/src/Link/link-class.ts +10 -0
  231. package/src/LiveActivity/README.md +3 -0
  232. package/src/LogoSlotWall/README.md +7 -0
  233. package/src/MapView/README.md +5 -0
  234. package/src/Marketing/AIDisclosureBadge.tsx +31 -0
  235. package/src/Marketing/ButtonLink.tsx +32 -0
  236. package/src/Marketing/CodeSample.tsx +47 -0
  237. package/src/Marketing/ComparisonScroll.tsx +7 -0
  238. package/src/Marketing/ComparisonTable.tsx +104 -0
  239. package/src/Marketing/FAQ.tsx +26 -0
  240. package/src/Marketing/FeatureGrid.tsx +65 -0
  241. package/src/Marketing/GateLifecycleRail.tsx +52 -0
  242. package/src/Marketing/LogoSlotWall.tsx +53 -0
  243. package/src/Marketing/Marketing.tsx +49 -0
  244. package/src/Marketing/Marketing.types.ts +64 -0
  245. package/src/Marketing/MarketingFooter.tsx +88 -0
  246. package/src/Marketing/MarketingHero.tsx +45 -0
  247. package/src/Marketing/MarketingNav.tsx +76 -0
  248. package/src/Marketing/MarketingPoints.tsx +16 -0
  249. package/src/Marketing/MarketingSection.tsx +52 -0
  250. package/src/Marketing/PersonaSwitcher.tsx +137 -0
  251. package/src/Marketing/PriceDisplay.tsx +56 -0
  252. package/src/Marketing/PricingTable.tsx +122 -0
  253. package/src/Marketing/ProofStatStrip.tsx +35 -0
  254. package/src/Marketing/ScreenshotFrame.tsx +56 -0
  255. package/src/Marketing/SurfaceTriptych.tsx +43 -0
  256. package/src/Marketing/Testimonial.tsx +45 -0
  257. package/src/Marketing/marketing-copy.ts +25 -0
  258. package/src/Marketing/provided.tsx +36 -0
  259. package/src/MarketingFooter/README.md +7 -0
  260. package/src/MarketingHero/README.md +7 -0
  261. package/src/MarketingNav/README.md +7 -0
  262. package/src/MarketingSection/README.md +7 -0
  263. package/src/Media/FilePreviewer.tsx +86 -0
  264. package/src/Media/MapView.tsx +200 -0
  265. package/src/Media/Media.tsx +11 -0
  266. package/src/Media/Media.types.ts +47 -0
  267. package/src/Media/RichTextEditor.tsx +198 -0
  268. package/src/Media/toLngLat.tsx +13 -0
  269. package/src/Menus/ContextMenu.tsx +106 -0
  270. package/src/Menus/DropdownMenu.tsx +51 -0
  271. package/src/Menus/Menus.shared.tsx +67 -0
  272. package/src/Menus/Menus.tsx +13 -0
  273. package/src/Menus/Menus.types.ts +49 -0
  274. package/src/Menus/OrgSwitcher.tsx +69 -0
  275. package/src/Menus/compactTriggerClass.tsx +7 -0
  276. package/src/Menus/menuClass.tsx +4 -0
  277. package/src/Menus/menuItemClass.tsx +7 -0
  278. package/src/Menus/orgSwitcherTriggerClass.tsx +7 -0
  279. package/src/Money/README.md +6 -0
  280. package/src/MultiSelect/README.md +3 -0
  281. package/src/Navigation/Accordion.tsx +59 -0
  282. package/src/Navigation/Breadcrumbs.tsx +59 -0
  283. package/src/Navigation/MoreFields.tsx +42 -0
  284. package/src/Navigation/Navigation.shared.tsx +32 -0
  285. package/src/Navigation/Navigation.tsx +21 -0
  286. package/src/Navigation/Navigation.types.ts +63 -0
  287. package/src/Navigation/Pagination.tsx +30 -0
  288. package/src/Navigation/SegmentedControl.tsx +87 -0
  289. package/src/Navigation/Sidebar.tsx +106 -0
  290. package/src/Navigation/TabBar.tsx +61 -0
  291. package/src/Navigation/Tabs.tsx +79 -0
  292. package/src/Navigation/TopNav.tsx +86 -0
  293. package/src/Navigation/sidebar-classes.ts +18 -0
  294. package/src/NotificationCenter/README.md +3 -0
  295. package/src/NumberInput/README.md +3 -0
  296. package/src/OfflineBanner/README.md +3 -0
  297. package/src/OnboardingChecklist/README.md +3 -0
  298. package/src/OnboardingPacket/README.md +3 -0
  299. package/src/OpportunityCard/README.md +5 -0
  300. package/src/OpportunityFilters/README.md +3 -0
  301. package/src/OrgChart/README.md +5 -0
  302. package/src/OrgSwitcher/README.md +3 -0
  303. package/src/Overlays/Dialog.tsx +54 -0
  304. package/src/Overlays/Drawer.tsx +62 -0
  305. package/src/Overlays/HoverCard.tsx +59 -0
  306. package/src/Overlays/Overlays.shared.tsx +12 -0
  307. package/src/Overlays/Overlays.tsx +20 -0
  308. package/src/Overlays/Overlays.types.ts +66 -0
  309. package/src/Overlays/Popover.tsx +70 -0
  310. package/src/Overlays/PropertyList.tsx +19 -0
  311. package/src/Overlays/ShortcutSheet.tsx +62 -0
  312. package/src/Overlays/SidePeek.tsx +117 -0
  313. package/src/Overlays/Tooltip.tsx +73 -0
  314. package/src/Overlays/panelClass.tsx +6 -0
  315. package/src/Overlays/popoverPanelClass.tsx +6 -0
  316. package/src/PageHeader/README.md +3 -0
  317. package/src/Pagination/README.md +3 -0
  318. package/src/PaymentTracker/README.md +3 -0
  319. package/src/PayoutDetailsForm/README.md +3 -0
  320. package/src/PersonaSwitcher/README.md +7 -0
  321. package/src/PhaseChip/README.md +5 -0
  322. package/src/PhaseGlyph/README.md +17 -0
  323. package/src/PhotoCapture/README.md +3 -0
  324. package/src/Pickers/Combobox.tsx +91 -0
  325. package/src/Pickers/DatePicker.tsx +60 -0
  326. package/src/Pickers/DateRangePicker.tsx +78 -0
  327. package/src/Pickers/FileDrop.tsx +84 -0
  328. package/src/Pickers/MultiSelect.tsx +118 -0
  329. package/src/Pickers/Pickers.shared.tsx +337 -0
  330. package/src/Pickers/Pickers.tsx +17 -0
  331. package/src/Pickers/Pickers.types.ts +47 -0
  332. package/src/Pickers/Slider.tsx +59 -0
  333. package/src/Pickers/TimePicker.tsx +68 -0
  334. package/src/Pickers/rankOptions.tsx +12 -0
  335. package/src/Popover/README.md +3 -0
  336. package/src/PresenceIndicator/README.md +3 -0
  337. package/src/PriceDisplay/README.md +7 -0
  338. package/src/PricingTable/README.md +9 -0
  339. package/src/ProfileEditor/README.md +3 -0
  340. package/src/ProgressBar/README.md +3 -0
  341. package/src/ProofStatStrip/README.md +7 -0
  342. package/src/PropertyChip/README.md +3 -0
  343. package/src/ProvenanceBadge/README.md +3 -0
  344. package/src/QRCode/README.md +6 -0
  345. package/src/QuickAdd/README.md +5 -0
  346. package/src/QuickIncidentSheet/README.md +3 -0
  347. package/src/Radio/README.md +3 -0
  348. package/src/RadioChannelTable/README.md +7 -0
  349. package/src/RatingDialog/README.md +3 -0
  350. package/src/ReconciliationTable/README.md +3 -0
  351. package/src/RecordKindChip/README.md +3 -0
  352. package/src/RecordKindGlyph/README.md +34 -0
  353. package/src/RecordRow/README.md +7 -0
  354. package/src/RefusalNotice/README.md +5 -0
  355. package/src/ResizablePanel/README.md +3 -0
  356. package/src/ResourceSchedule/README.md +6 -0
  357. package/src/RichTextEditor/README.md +3 -0
  358. package/src/RichTextEditor/extensions.ts +217 -0
  359. package/src/RunOfShowLive/README.md +5 -0
  360. package/src/ScanSheet/README.md +3 -0
  361. package/src/ScreenshotFrame/README.md +7 -0
  362. package/src/ScrollRegion/README.md +9 -0
  363. package/src/SegmentedControl/README.md +5 -0
  364. package/src/Select/README.md +5 -0
  365. package/src/ShiftCard/README.md +3 -0
  366. package/src/ShortcutEditor/README.md +3 -0
  367. package/src/ShortcutSheet/README.md +3 -0
  368. package/src/SidePeek/README.md +6 -0
  369. package/src/Sidebar/README.md +5 -0
  370. package/src/SignaturePad/README.md +3 -0
  371. package/src/Skeleton/README.md +3 -0
  372. package/src/Slider/README.md +3 -0
  373. package/src/SplitView/README.md +3 -0
  374. package/src/SpreadsheetGrid/README.md +9 -0
  375. package/src/StalenessIndicator/README.md +3 -0
  376. package/src/StatTile/README.md +6 -0
  377. package/src/StateChip/README.md +6 -0
  378. package/src/StateIcon/README.md +6 -0
  379. package/src/StateIcon/StateIcon.tsx +127 -0
  380. package/src/StatusHeatmap/README.md +15 -0
  381. package/src/Stepper/README.md +3 -0
  382. package/src/Surface/Skeleton.tsx +21 -0
  383. package/src/Surface/Surface.tsx +63 -0
  384. package/src/SurfaceTriptych/README.md +7 -0
  385. package/src/Switch/README.md +5 -0
  386. package/src/SyncConflictSheet/README.md +3 -0
  387. package/src/TabBar/README.md +7 -0
  388. package/src/Tabs/README.md +6 -0
  389. package/src/Tag/README.md +3 -0
  390. package/src/TemplateAuth/README.md +6 -0
  391. package/src/TemplateCollection/README.md +6 -0
  392. package/src/TemplateDashboard/README.md +8 -0
  393. package/src/TemplateDocument/README.md +5 -0
  394. package/src/TemplateGatewayShell/README.md +5 -0
  395. package/src/TemplateGridEditor/README.md +3 -0
  396. package/src/TemplateRecord/README.md +3 -0
  397. package/src/TemplateSettings/README.md +5 -0
  398. package/src/TemplateSplitView/README.md +3 -0
  399. package/src/TemplateSystem/README.md +5 -0
  400. package/src/TemplateWizard/README.md +3 -0
  401. package/src/Templates/TemplateAuth.tsx +16 -0
  402. package/src/Templates/TemplateCollection.tsx +35 -0
  403. package/src/Templates/TemplateDashboard.tsx +200 -0
  404. package/src/Templates/TemplateDocument.tsx +24 -0
  405. package/src/Templates/TemplateGatewayShell.tsx +36 -0
  406. package/src/Templates/TemplateGridEditor.tsx +17 -0
  407. package/src/Templates/TemplateRecord.tsx +31 -0
  408. package/src/Templates/TemplateSettings.tsx +26 -0
  409. package/src/Templates/TemplateSplitView.tsx +27 -0
  410. package/src/Templates/TemplateSystem.tsx +19 -0
  411. package/src/Templates/TemplateSystemLink.tsx +55 -0
  412. package/src/Templates/TemplateWizard.tsx +43 -0
  413. package/src/Templates/Templates.tsx +29 -0
  414. package/src/Templates/Templates.types.ts +153 -0
  415. package/src/Testimonial/README.md +7 -0
  416. package/src/Textarea/README.md +3 -0
  417. package/src/TimePicker/README.md +5 -0
  418. package/src/Timeline/README.md +6 -0
  419. package/src/Toast/README.md +3 -0
  420. package/src/Tooltip/README.md +3 -0
  421. package/src/TopNav/README.md +5 -0
  422. package/src/TreeView/README.md +3 -0
  423. package/src/URIDChip/README.md +5 -0
  424. package/src/UndoToast/README.md +3 -0
  425. package/src/UpNextCard/README.md +6 -0
  426. package/src/Views/ActivityFeedItem.tsx +52 -0
  427. package/src/Views/Board.tsx +229 -0
  428. package/src/Views/BulkActionBar.tsx +76 -0
  429. package/src/Views/RecordList.tsx +146 -0
  430. package/src/Views/RecordRow.tsx +132 -0
  431. package/src/Views/StatTile.tsx +88 -0
  432. package/src/Views/TreeView.tsx +161 -0
  433. package/src/Views/Views.tsx +18 -0
  434. package/src/Views/Views.types.ts +99 -0
  435. package/src/WhatsNew/README.md +3 -0
  436. package/src/WhiteLabelPreview/README.md +8 -0
  437. package/src/XOS/AccessGridMatrix.tsx +111 -0
  438. package/src/XOS/AssertionRankBadge.tsx +27 -0
  439. package/src/XOS/CoordinateMatrix.tsx +123 -0
  440. package/src/XOS/EmergencyCodeCard.tsx +109 -0
  441. package/src/XOS/GateReadinessPanel.tsx +100 -0
  442. package/src/XOS/ProvenanceBadge.tsx +19 -0
  443. package/src/XOS/RadioChannelTable.tsx +50 -0
  444. package/src/XOS/ReconciliationTable.tsx +64 -0
  445. package/src/XOS/RunOfShowLive.tsx +97 -0
  446. package/src/XOS/StalenessIndicator.tsx +31 -0
  447. package/src/XOS/StatusHeatmap.tsx +200 -0
  448. package/src/XOS/XOS.shared.tsx +90 -0
  449. package/src/XOS/XOS.tsx +29 -0
  450. package/src/XOS/XOS.types.ts +99 -0
  451. package/src/XOS/resolveStep.tsx +16 -0
  452. package/src/brand/defaults/README.md +13 -0
  453. package/src/brand/defaults/app-icon.svg +6 -0
  454. package/src/brand/defaults/favicon.svg +6 -0
  455. package/src/brand/defaults/logo-dark.svg +6 -0
  456. package/src/brand/defaults/logo-light.svg +6 -0
  457. package/src/brand/defaults/splash.svg +6 -0
  458. package/src/index.ts +606 -0
  459. package/src/lib/context.tsx +156 -0
  460. package/src/lib/cx.ts +4 -0
  461. package/src/lib/format.ts +200 -0
  462. package/src/lib/hooks.ts +187 -0
  463. package/src/lib/i18n.ts +97 -0
  464. package/src/lib/icons.ts +45 -0
  465. package/src/lib/live.ts +92 -0
  466. package/src/lib/preferences.ts +242 -0
  467. package/src/lib/route-transition.tsx +22 -0
  468. package/src/lib/selection.ts +92 -0
  469. package/src/lib/shortcut-targets.ts +125 -0
  470. package/src/lib/theme.ts +50 -0
  471. package/src/lib/undo-stack.ts +125 -0
  472. package/src/marketing.ts +33 -0
  473. package/src/pwa/action-outcome.tsx +81 -0
  474. package/src/pwa/analytics.ts +236 -0
  475. package/src/pwa/browser-push.tsx +132 -0
  476. package/src/pwa/client.ts +126 -0
  477. package/src/pwa/index.ts +40 -0
  478. package/src/pwa/offline-key.ts +18 -0
  479. package/src/pwa/service-worker.ts +229 -0
  480. package/src/static.ts +10 -0
  481. package/src/styles/base.css +208 -0
  482. package/src/styles/components.css +453 -0
  483. package/src/styles/index.css +16 -0
  484. package/src/styles/marketing.css +1260 -0
  485. package/src/styles/reference.css +5792 -0
  486. package/src/types/reference.d.ts +1608 -0
  487. package/tailwind.config.cjs +3 -0
@@ -0,0 +1,38 @@
1
+ import { type CSSProperties, type ReactElement } from "react";
2
+ import { Icon } from "../Icon/Icon.tsx";
3
+ import { type BrandSlotProps } from "./Identity.types.ts";
4
+
5
+ /**
6
+ * The drawing of a BrandSlot with its size line already written: no hooks and no context, so a
7
+ * server component can place a slot (ADR 0027). BrandSlot fills `sizeLabel` from the catalog.
8
+ */
9
+ export function BrandSlotFrame({
10
+ label,
11
+ width,
12
+ height,
13
+ formats,
14
+ sizeLabel,
15
+ }: BrandSlotProps & { sizeLabel: string }): ReactElement {
16
+ const scale = Math.min(1, 160 / width, 148 / height);
17
+ const frame: CSSProperties = {
18
+ inlineSize: `calc(var(--space-4) * ${Math.round(width * scale) / 4})`,
19
+ blockSize: `calc(var(--space-4) * ${Math.round(height * scale) / 4})`,
20
+ };
21
+ return (
22
+ <figure className="flex flex-col gap-6 m-0">
23
+ <div
24
+ aria-hidden="true"
25
+ style={frame}
26
+ className="flex flex-col items-center justify-center gap-4 min-w-(--space-64) min-h-(--space-48) box-border rounded-card border border-dashed border-border-strong text-text-tertiary text-text-11"
27
+ >
28
+ <Icon name="Image" size={20} />
29
+ <span className="tabular-nums">{sizeLabel}</span>
30
+ </div>
31
+ <figcaption className="flex flex-col text-text-12 text-text-secondary">
32
+ <strong className="font-medium text-text-primary">{label}</strong>
33
+ <span className="tabular-nums">{sizeLabel}</span>
34
+ <span>{formats}</span>
35
+ </figcaption>
36
+ </figure>
37
+ );
38
+ }
@@ -0,0 +1,92 @@
1
+ import { type ReactElement } from "react";
2
+ import { cx } from "../lib/cx.ts";
3
+ import { useT } from "../lib/context.tsx";
4
+ import { Button, IconButton } from "../Button/Button.tsx";
5
+ import { Avatar } from "../Avatar/Avatar.tsx";
6
+ import { BrandMark } from "../BrandMark/BrandMark.tsx";
7
+ import { chipClass } from "../Chips/Chips.tsx";
8
+ import { type CredentialBadgeProps } from "./Identity.types.ts";
9
+ import { QRCode } from "./QRCode.tsx";
10
+
11
+ const CATEGORY: Record<CredentialBadgeProps["categoryColor"], string> = {
12
+ 1: cx("border-t-viz-cat-1"),
13
+ 2: cx("border-t-viz-cat-2"),
14
+ 3: cx("border-t-viz-cat-3"),
15
+ 4: cx("border-t-viz-cat-4"),
16
+ 5: cx("border-t-viz-cat-5"),
17
+ 6: cx("border-t-viz-cat-6"),
18
+ 7: cx("border-t-viz-cat-7"),
19
+ };
20
+
21
+ /** The badge shown at access points: category band with its label, holder, offline QR code, zones and validity. */
22
+ export function CredentialBadge({
23
+ org,
24
+ name,
25
+ role,
26
+ category,
27
+ categoryColor,
28
+ credentialKey,
29
+ payload,
30
+ zones,
31
+ validFrom,
32
+ validTo,
33
+ onWallet,
34
+ onBrightness,
35
+ }: CredentialBadgeProps): ReactElement {
36
+ const t = useT();
37
+ return (
38
+ <article
39
+ aria-label={t("badge.label", { name })}
40
+ className="flex flex-col w-(--layout-property-column) max-w-full box-border overflow-hidden rounded-dialog border border-border-subtle bg-bg-raised"
41
+ >
42
+ <div
43
+ className={cx(
44
+ "px-16 py-6 border-t-stroke-stripe bg-bg-surface text-text-primary font-semibold text-text-13 tracking-wider uppercase",
45
+ CATEGORY[categoryColor],
46
+ )}
47
+ >
48
+ {category}
49
+ </div>
50
+ <div className="flex flex-col gap-12 p-16">
51
+ <div className="flex items-center justify-between gap-8">
52
+ <BrandMark name={org} size="sm" />
53
+ <span className="font-mono text-text-12 text-text-secondary">{credentialKey}</span>
54
+ </div>
55
+ <div className="flex items-center gap-12">
56
+ <Avatar name={name} size="lg" />
57
+ <div className="flex flex-col">
58
+ <strong className="text-heading-20 font-semibold">{name}</strong>
59
+ <span className="text-text-secondary">{role}</span>
60
+ </div>
61
+ </div>
62
+ <div className="flex justify-center p-8 rounded-card bg-paper">
63
+ <QRCode value={payload} label={t("badge.qr", { key: credentialKey })} size={168} />
64
+ </div>
65
+ <div className="flex flex-col gap-4">
66
+ <span className="text-text-12 font-medium text-text-secondary">{t("badge.zones")}</span>
67
+ <ul className="flex flex-wrap gap-4 m-0 p-0 list-none">
68
+ {zones.map((z) => (
69
+ <li key={z} className={chipClass}>
70
+ {z}
71
+ </li>
72
+ ))}
73
+ </ul>
74
+ </div>
75
+ <span className="text-text-secondary tabular-nums">
76
+ {t("badge.valid", { from: validFrom, to: validTo })}
77
+ </span>
78
+ <div className="flex items-center gap-8">
79
+ <Button icon="Wallet" className="flex-1" onClick={onWallet}>
80
+ {t("badge.wallet")}
81
+ </Button>
82
+ <IconButton
83
+ icon="SunMedium"
84
+ label={t("badge.brightness")}
85
+ variant="secondary"
86
+ onClick={onBrightness}
87
+ />
88
+ </div>
89
+ </div>
90
+ </article>
91
+ );
92
+ }
@@ -0,0 +1,14 @@
1
+ export {
2
+ type PresenceIndicatorProps,
3
+ type QRCodeProps,
4
+ type CredentialBadgeProps,
5
+ type BrandSlotProps,
6
+ type WhiteLabelPreviewProps,
7
+ } from "./Identity.types.ts";
8
+ export { PresenceIndicator } from "./PresenceIndicator.tsx";
9
+ export { qrPath } from "./qrPath.tsx";
10
+ export { QRCode } from "./QRCode.tsx";
11
+ export { CredentialBadge } from "./CredentialBadge.tsx";
12
+ export { BrandSlot } from "./BrandSlot.tsx";
13
+ export { tokenVariable } from "./tokenVariable.tsx";
14
+ export { WhiteLabelPreview } from "./WhiteLabelPreview.tsx";
@@ -0,0 +1,25 @@
1
+ import type {
2
+ BrandSlotProps as ReferenceBrandSlotProps,
3
+ CredentialBadgeProps as ReferenceCredentialBadgeProps,
4
+ PresenceIndicatorProps as ReferencePresenceIndicatorProps,
5
+ QRCodeProps as ReferenceQRCodeProps,
6
+ WhiteLabelPreviewProps as ReferenceWhiteLabelPreviewProps,
7
+ } from "../types/reference";
8
+
9
+ export type PresenceIndicatorProps = ReferencePresenceIndicatorProps;
10
+
11
+ export type QRCodeProps = ReferenceQRCodeProps;
12
+
13
+ export type CredentialBadgeProps = ReferenceCredentialBadgeProps & {
14
+ onWallet?: () => void;
15
+ onBrightness?: () => void;
16
+ };
17
+
18
+ export type BrandSlotProps = ReferenceBrandSlotProps;
19
+
20
+ export type WhiteLabelPreviewProps = ReferenceWhiteLabelPreviewProps;
21
+
22
+ export interface QRModules {
23
+ getModuleCount(): number;
24
+ isDark(row: number, col: number): boolean;
25
+ }
@@ -0,0 +1,37 @@
1
+ import { type ReactElement } from "react";
2
+ import { useFormat, useT } from "../lib/context.tsx";
3
+ import { Avatar } from "../Avatar/Avatar.tsx";
4
+ import { type PresenceIndicatorProps } from "./Identity.types.ts";
5
+
6
+ /** Who is viewing or editing a record live; the count and names are announced together. */
7
+ export function PresenceIndicator({
8
+ names,
9
+ verb = "viewing",
10
+ }: PresenceIndicatorProps): ReactElement {
11
+ const t = useT();
12
+ const fmt = useFormat();
13
+ const summary = t(verb === "editing" ? "presence.editing" : "presence.viewing", {
14
+ n: names.length,
15
+ });
16
+ return (
17
+ <span
18
+ role="img"
19
+ aria-label={t(verb === "editing" ? "presence.namesEditing" : "presence.namesViewing", {
20
+ names: fmt.list(names),
21
+ })}
22
+ className="inline-flex items-center gap-8 text-text-12"
23
+ >
24
+ <span aria-hidden="true" className="inline-flex [&>*+*]:ms-(--ui-avatar-overlap)">
25
+ {names.slice(0, 3).map((name) => (
26
+ <span key={name} className="relative inline-flex">
27
+ <Avatar name={name} />
28
+ <span className="absolute -end-(--stroke-width-hairline) -bottom-(--stroke-width-hairline) size-(--space-8) rounded-full bg-success ring-(length:--stroke-width-focus) ring-bg-canvas" />
29
+ </span>
30
+ ))}
31
+ </span>
32
+ <span aria-hidden="true" className="text-text-secondary">
33
+ {summary}
34
+ </span>
35
+ </span>
36
+ );
37
+ }
@@ -0,0 +1,22 @@
1
+ import { useMemo, type ReactElement } from "react";
2
+ import { type QRCodeProps } from "./Identity.types.ts";
3
+ import { qrPath } from "./qrPath.tsx";
4
+
5
+ /** A scannable code as SVG, always dark on a light quiet zone so scanners read it in every theme. */
6
+ export function QRCode({ value, label, size = 160 }: QRCodeProps): ReactElement {
7
+ const { size: modules, path } = useMemo(() => qrPath(value), [value]);
8
+ return (
9
+ <svg
10
+ role="img"
11
+ aria-label={label}
12
+ viewBox={`0 0 ${modules} ${modules}`}
13
+ width={size}
14
+ height={size}
15
+ shapeRendering="crispEdges"
16
+ className="block rounded-control"
17
+ >
18
+ <rect width={modules} height={modules} className="fill-paper" />
19
+ <path d={path} className="fill-paper-ink" />
20
+ </svg>
21
+ );
22
+ }
@@ -0,0 +1,33 @@
1
+ import { type CSSProperties, type ReactElement } from "react";
2
+ import { BrandMark } from "../BrandMark/BrandMark.tsx";
3
+ import { type WhiteLabelPreviewProps } from "./Identity.types.ts";
4
+ import { tokenVariable } from "./tokenVariable.tsx";
5
+
6
+ /** The same screen under each tenant brand, to prove theming is token-only. */
7
+ export function WhiteLabelPreview({ sample, brands }: WhiteLabelPreviewProps): ReactElement {
8
+ return (
9
+ <div className="grid grid-cols-2 max-sm:grid-cols-1 gap-16">
10
+ {brands.map((brand) => {
11
+ const style: Record<string, string> = {};
12
+ for (const [key, value] of Object.entries(brand.tokens)) style[tokenVariable(key)] = value;
13
+ return (
14
+ <section
15
+ key={brand.name}
16
+ aria-label={brand.name}
17
+ data-theme={brand.theme}
18
+ style={style as CSSProperties}
19
+ className="overflow-hidden rounded-card border border-border-subtle bg-bg-canvas text-text-primary"
20
+ >
21
+ <div className="flex flex-wrap items-center justify-between gap-x-8 gap-y-4 px-12 py-8 bg-bg-surface border-b border-border-subtle text-text-12">
22
+ <BrandMark name={brand.name} size="sm" />
23
+ <span title={brand.domain} className="min-w-0 truncate text-text-secondary">
24
+ {brand.domain}
25
+ </span>
26
+ </div>
27
+ <div className="flex flex-col gap-12 p-16">{sample}</div>
28
+ </section>
29
+ );
30
+ })}
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,16 @@
1
+ import qrcode from "qrcode-generator";
2
+ import { type QRModules } from "./Identity.types.ts";
3
+
4
+ /** The dark modules of a QR code (error correction M) as one SVG path, with a 4-module quiet zone. */
5
+ export function qrPath(value: string): { size: number; path: string } {
6
+ const code = qrcode(0, "M");
7
+ code.addData(value);
8
+ code.make();
9
+ const modules = code as unknown as QRModules;
10
+ const n = modules.getModuleCount();
11
+ const cells: string[] = [];
12
+ for (let r = 0; r < n; r++) {
13
+ for (let c = 0; c < n; c++) if (modules.isDark(r, c)) cells.push(`M${c + 4} ${r + 4}h1v1h-1z`);
14
+ }
15
+ return { size: n + 8, path: cells.join("") };
16
+ }
@@ -0,0 +1,9 @@
1
+ import nameMap from "@xos/tokens/name-map.json";
2
+
3
+ const SPEC = nameMap as Record<string, string>;
4
+
5
+ /** The CSS variable a white-label token override sets: the spec name with dots as hyphens (`accent-default` is `--color-accent-default`). */
6
+ export function tokenVariable(key: string): string {
7
+ const spec = SPEC[key];
8
+ return `--${(spec ?? key).replace(/\./g, "-")}`;
9
+ }
@@ -0,0 +1,3 @@
1
+ # InlineAlert
2
+
3
+ InlineAlert puts a short message in context; the icon carries tone and the message stays in `text-primary` and `text-secondary`.
@@ -0,0 +1,8 @@
1
+ # Input
2
+
3
+ Input is a labeled single-line text field with help text or an inline error.
4
+
5
+ - Label above, sentence-case help below. Fields with legal or financial meaning always carry help.
6
+ - Errors name the field, the problem and the fix in one sentence, validated as the person types.
7
+ - Border is `border-control`, which meets the 3:1 non-text minimum; focus replaces it with a 2 px `focus-ring`.
8
+ - `width` sets the field width; it is 280 px by default and never wider than its container.
@@ -0,0 +1,30 @@
1
+ import type { ReactElement } from "react";
2
+ import type { KbdProps as ReferenceKbdProps } from "../types/reference";
3
+ import { cx } from "../lib/cx.ts";
4
+
5
+ export type KbdProps = ReferenceKbdProps;
6
+
7
+ const kbdClass = cx(
8
+ "inline-flex items-center justify-center min-w-(--ui-kbd) h-(--ui-kbd) px-4 rounded-chip",
9
+ "border border-border-strong bg-bg-surface text-text-secondary font-sans font-medium text-text-11 leading-none",
10
+ "forced-colors:border-[CanvasText]",
11
+ );
12
+
13
+ /** One key in a shortcut. */
14
+ export function Kbd({ children }: KbdProps): ReactElement {
15
+ return <kbd className={kbdClass}>{children}</kbd>;
16
+ }
17
+
18
+ /** A shortcut written with spaces between keys (`⌘ K`, `G then P` as `G P`) as one Kbd per key. */
19
+ export function Keys({ keys }: { keys: string }): ReactElement {
20
+ return (
21
+ <span className="inline-flex items-center gap-2">
22
+ {keys
23
+ .split(" ")
24
+ .filter(Boolean)
25
+ .map((key, i) => (
26
+ <Kbd key={`${key}-${i}`}>{key}</Kbd>
27
+ ))}
28
+ </span>
29
+ );
30
+ }
@@ -0,0 +1,6 @@
1
+ # Kbd
2
+
3
+ Kbd renders one key of a keyboard shortcut.
4
+
5
+ - One Kbd per key; sequences show each key (G then P).
6
+ - Used in Button, Tooltip, CommandMenu and the shortcut sheet.
@@ -0,0 +1,151 @@
1
+ import { useEffect, useId, useRef, type ReactElement } from "react";
2
+ import { cx } from "../lib/cx.ts";
3
+ import { useT } from "../lib/context.tsx";
4
+ import { Icon } from "../Icon/Icon.tsx";
5
+ import { useControllable, useMediaQuery } from "../lib/hooks.ts";
6
+ import { type AppShellProps } from "./Layout.types.ts";
7
+
8
+ /**
9
+ * Brand slot, sidebar, header and content for every Atlas page. Below lg the sidebar becomes a drawer
10
+ * opened from the header menu button (or by the shell through menuOpen, for the bottom bar's Menu tab)
11
+ * and closed by Esc or the scrim. While it is open the page behind is inert, as behind a dialog:
12
+ * focus moves to the drawer's first control and returns to the menu button on Esc. Below md the
13
+ * bottom bar shows.
14
+ */
15
+ export function AppShell({
16
+ brand,
17
+ sidebar,
18
+ header,
19
+ children,
20
+ mobileNav,
21
+ menuOpen,
22
+ onMenuOpenChange,
23
+ }: AppShellProps): ReactElement {
24
+ const t = useT();
25
+ const sideId = useId().replace(/:/g, "");
26
+ const [open, setOpen] = useControllable(menuOpen, false, onMenuOpenChange);
27
+ const menuButton = useRef<HTMLButtonElement>(null);
28
+ const side = useRef<HTMLDivElement>(null);
29
+ const content = useRef<HTMLDivElement>(null);
30
+ const returnFocus = useRef(false);
31
+ // The drawer only exists below lg; on a wide screen the sidebar is always there.
32
+ const drawer = useMediaQuery("(max-width: 63.999rem)");
33
+ const covered = open && drawer;
34
+ // A scrolling region keyboard users cannot reach fails WCAG 2.1.1. When the page overflows and holds
35
+ // nothing focusable (a long read-only table), the region itself takes focus so arrow keys scroll it;
36
+ // otherwise it stays out of the tab order.
37
+ useEffect(() => {
38
+ const el = content.current;
39
+ if (!el || typeof ResizeObserver === "undefined") return undefined;
40
+ const sync = () => {
41
+ const overflows = el.scrollHeight > el.clientHeight;
42
+ const focusable = el.querySelector(
43
+ "a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex='-1'])",
44
+ );
45
+ if (overflows && !focusable) el.setAttribute("tabindex", "0");
46
+ else el.removeAttribute("tabindex");
47
+ };
48
+ sync();
49
+ const observer = new ResizeObserver(sync);
50
+ observer.observe(el);
51
+ for (const child of Array.from(el.children)) observer.observe(child);
52
+ return () => observer.disconnect();
53
+ }, [children]);
54
+ useEffect(() => {
55
+ if (!open) {
56
+ // The menu button takes focus back once the page behind is no longer inert.
57
+ if (returnFocus.current) menuButton.current?.focus();
58
+ returnFocus.current = false;
59
+ return undefined;
60
+ }
61
+ if (side.current && getComputedStyle(side.current).position === "absolute") {
62
+ side.current
63
+ .querySelector<HTMLElement>("a[href], button:not([disabled]), select, input")
64
+ ?.focus();
65
+ }
66
+ const onKey = (e: KeyboardEvent) => {
67
+ if (e.key === "Escape") {
68
+ returnFocus.current = true;
69
+ setOpen(false);
70
+ }
71
+ };
72
+ document.addEventListener("keydown", onKey);
73
+ return () => document.removeEventListener("keydown", onKey);
74
+ }, [open, setOpen]);
75
+ return (
76
+ <div
77
+ data-open={open ? "true" : undefined}
78
+ className={cx(
79
+ "group/shell relative grid grid-cols-[var(--layout-sidebar)_minmax(0,1fr)] max-lg:grid-cols-[minmax(0,1fr)]",
80
+ "min-h-(--ui-shell-min) h-full overflow-hidden bg-bg-canvas print:block",
81
+ )}
82
+ >
83
+ <div
84
+ id={sideId}
85
+ ref={side}
86
+ className={cx(
87
+ "flex flex-col gap-4 min-h-0 p-8 overflow-y-auto bg-bg-surface border-e border-border-subtle [&>*]:shrink-0",
88
+ "[&_nav]:border-0 [&_nav]:p-0 [&_nav]:w-auto print:hidden",
89
+ "max-lg:hidden max-lg:group-data-[open=true]/shell:flex max-lg:group-data-[open=true]/shell:absolute",
90
+ "max-lg:inset-y-0 max-lg:start-0 max-lg:z-sidebar max-lg:w-[min(var(--layout-sidebar),85%)] max-lg:shadow-dialog",
91
+ )}
92
+ >
93
+ {brand ? <div className="px-8 pt-4 pb-8">{brand}</div> : null}
94
+ {sidebar}
95
+ </div>
96
+ {open ? (
97
+ <button
98
+ type="button"
99
+ aria-label={t("action.close")}
100
+ onClick={() => setOpen(false)}
101
+ className={cx(
102
+ "hidden max-lg:block absolute inset-0 z-[calc(var(--z-sidebar)-1)] p-0 border-0 cursor-pointer",
103
+ "bg-scrim opacity-scrim-dark in-data-[theme=light]:opacity-scrim-light in-data-[theme=sunlight]:opacity-scrim-light",
104
+ )}
105
+ />
106
+ ) : null}
107
+ <main className="flex flex-col min-w-0 min-h-0" inert={covered}>
108
+ <div
109
+ className={cx(
110
+ "flex items-center justify-between gap-8 h-(--ui-shell-head) px-16 border-b border-border-subtle",
111
+ "max-lg:justify-start",
112
+ !header && "hidden max-lg:flex",
113
+ )}
114
+ >
115
+ <span className="hidden max-lg:inline-flex">
116
+ <button
117
+ ref={menuButton}
118
+ type="button"
119
+ aria-label={t("shell.menu")}
120
+ title={t("shell.menu")}
121
+ aria-expanded={open}
122
+ aria-controls={sideId}
123
+ onClick={() => setOpen(!open)}
124
+ className="inline-flex items-center justify-center h-control-md min-w-control-md rounded-control border border-transparent bg-transparent text-text-secondary cursor-pointer hover:bg-bg-hover pointer-coarse:min-h-target-coarse pointer-coarse:min-w-target-coarse"
125
+ >
126
+ <Icon name="Menu" />
127
+ </button>
128
+ </span>
129
+ {header ? (
130
+ <div className="flex flex-1 items-center justify-between gap-8 min-w-0">{header}</div>
131
+ ) : null}
132
+ </div>
133
+ <div
134
+ ref={content}
135
+ data-xos-bottom-bar-scroll={mobileNav ? "" : undefined}
136
+ className="flex flex-col gap-16 min-h-0 px-24 py-16 max-lg:p-16 overflow-y-auto [&>*]:shrink-0"
137
+ >
138
+ {children}
139
+ </div>
140
+ {mobileNav ? (
141
+ <div
142
+ data-xos-bottom-bar=""
143
+ className="hidden max-md:block sticky bottom-0 [&_nav]:w-full print:hidden"
144
+ >
145
+ {mobileNav}
146
+ </div>
147
+ ) : null}
148
+ </main>
149
+ </div>
150
+ );
151
+ }
@@ -0,0 +1,14 @@
1
+ export {
2
+ type AppShellProps,
3
+ type PageHeaderProps,
4
+ type ResizablePanelProps,
5
+ type ScrollRegionProps,
6
+ type SplitViewProps,
7
+ type StepperProps,
8
+ } from "./Layout.types.ts";
9
+ export { AppShell } from "./AppShell.tsx";
10
+ export { PageHeader } from "./PageHeader.tsx";
11
+ export { ResizablePanel } from "./ResizablePanel.tsx";
12
+ export { ScrollRegion } from "./ScrollRegion.tsx";
13
+ export { Stepper } from "./Stepper.tsx";
14
+ export { SplitView } from "./SplitView.tsx";
@@ -0,0 +1,43 @@
1
+ import { type CSSProperties, type Ref, type ReactNode } from "react";
2
+ import type {
3
+ AppShellProps as ReferenceAppShellProps,
4
+ PageHeaderProps as ReferencePageHeaderProps,
5
+ ResizablePanelProps as ReferenceResizablePanelProps,
6
+ ScrollRegionProps as ReferenceScrollRegionProps,
7
+ SplitViewProps as ReferenceSplitViewProps,
8
+ StepperProps as ReferenceStepperProps,
9
+ } from "../types/reference";
10
+
11
+ export type AppShellProps = ReferenceAppShellProps & {
12
+ /** The phone bottom bar (TabBar), shown below md. */
13
+ mobileNav?: ReactNode;
14
+ /**
15
+ * Whether the navigation drawer is open below lg, for a shell that opens it from elsewhere too
16
+ * (the bottom bar's Menu tab). Uncontrolled when omitted.
17
+ */
18
+ menuOpen?: boolean;
19
+ onMenuOpenChange?: (open: boolean) => void;
20
+ };
21
+
22
+ export type PageHeaderProps = ReferencePageHeaderProps;
23
+
24
+ export type ResizablePanelProps = ReferenceResizablePanelProps & {
25
+ onResize?: (width: number) => void;
26
+ /** Keyboard step in pixels; 16 by default. */
27
+ step?: number;
28
+ };
29
+
30
+ export type ScrollRegionProps = ReferenceScrollRegionProps & {
31
+ /** Classes for the scrolling element itself. */
32
+ className?: string;
33
+ /** Classes for the frame around it that holds the edge fades (its place in a flex row, say). */
34
+ frameClassName?: string;
35
+ style?: CSSProperties;
36
+ ref?: Ref<HTMLDivElement>;
37
+ };
38
+
39
+ export type SplitViewProps = ReferenceSplitViewProps & {
40
+ onSelect?: (index: number) => void;
41
+ };
42
+
43
+ export type StepperProps = ReferenceStepperProps;
@@ -0,0 +1,22 @@
1
+ import { type ReactElement } from "react";
2
+ import { Icon } from "../Icon/Icon.tsx";
3
+ import { type PageHeaderProps } from "./Layout.types.ts";
4
+
5
+ /** Title, count, view switcher and the one primary action of a collection. */
6
+ export function PageHeader({ title, glyph, count, views, actions }: PageHeaderProps): ReactElement {
7
+ return (
8
+ <header className="flex flex-wrap items-center justify-between gap-12 pb-12 border-b border-border-subtle">
9
+ <div className="flex items-center gap-8 min-w-0">
10
+ {glyph ? <Icon name={glyph} color="var(--color-text-secondary)" /> : null}
11
+ <h1 className="m-0 text-heading-16 font-semibold">{title}</h1>
12
+ {count !== undefined ? (
13
+ <span className="text-text-secondary tabular-nums">{count}</span>
14
+ ) : null}
15
+ </div>
16
+ <div className="flex flex-wrap items-center gap-8 print:hidden">
17
+ {views}
18
+ {actions}
19
+ </div>
20
+ </header>
21
+ );
22
+ }
@@ -0,0 +1,81 @@
1
+ import { useState, type PointerEvent as ReactPointerEvent, type ReactElement } from "react";
2
+ import { Direction } from "radix-ui";
3
+ import { useT } from "../lib/context.tsx";
4
+ import { type ResizablePanelProps } from "./Layout.types.ts";
5
+
6
+ /**
7
+ * A panel resizable by pointer or arrow keys within its limits; the handle is a vertical separator
8
+ * with its value. Home and End jump to the limits.
9
+ */
10
+ export function ResizablePanel({
11
+ initial,
12
+ min,
13
+ max,
14
+ side = "right",
15
+ label,
16
+ children,
17
+ onResize,
18
+ step = 16,
19
+ }: ResizablePanelProps): ReactElement {
20
+ const t = useT();
21
+ const dir = Direction.useDirection();
22
+ const [width, setWidthState] = useState(initial);
23
+ const setWidth = (next: number) => {
24
+ const clamped = Math.max(min, Math.min(max, next));
25
+ setWidthState(clamped);
26
+ onResize?.(clamped);
27
+ };
28
+ // The handle sits on the inline end for side "right"; dragging toward the reading direction grows it.
29
+ const growKey = (side === "left") !== (dir === "rtl") ? "ArrowLeft" : "ArrowRight";
30
+ const shrinkKey = growKey === "ArrowRight" ? "ArrowLeft" : "ArrowRight";
31
+ const down = (e: ReactPointerEvent<HTMLDivElement>) => {
32
+ const start = e.clientX;
33
+ const startWidth = width;
34
+ const sign = growKey === "ArrowRight" ? 1 : -1;
35
+ const move = (ev: PointerEvent) => setWidth(startWidth + sign * (ev.clientX - start));
36
+ const up = () => {
37
+ window.removeEventListener("pointermove", move);
38
+ window.removeEventListener("pointerup", up);
39
+ };
40
+ window.addEventListener("pointermove", move);
41
+ window.addEventListener("pointerup", up);
42
+ };
43
+ const handle = (
44
+ <div
45
+ role="separator"
46
+ aria-orientation="vertical"
47
+ aria-valuenow={width}
48
+ aria-valuemin={min}
49
+ aria-valuemax={max}
50
+ aria-label={label ?? t("panel.resize")}
51
+ tabIndex={0}
52
+ onPointerDown={down}
53
+ onKeyDown={(e) => {
54
+ const next =
55
+ e.key === growKey
56
+ ? width + step
57
+ : e.key === shrinkKey
58
+ ? width - step
59
+ : e.key === "Home"
60
+ ? min
61
+ : e.key === "End"
62
+ ? max
63
+ : null;
64
+ if (next === null) return;
65
+ e.preventDefault();
66
+ setWidth(next);
67
+ }}
68
+ className="flex-none w-(--ui-handle) cursor-col-resize touch-none border-s border-border-subtle hover:bg-accent focus-visible:bg-accent"
69
+ />
70
+ );
71
+ return (
72
+ <div
73
+ className="relative flex min-h-(--ui-resizable-min) bg-bg-surface border border-border-subtle rounded-card max-w-full"
74
+ style={{ inlineSize: width }}
75
+ >
76
+ {side === "left" ? handle : null}
77
+ <div className="flex-1 min-w-0 p-16 overflow-hidden">{children}</div>
78
+ {side === "left" ? null : handle}
79
+ </div>
80
+ );
81
+ }