@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,777 @@
1
+ import {
2
+ useEffect,
3
+ useId,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ type DragEvent,
8
+ type KeyboardEvent,
9
+ type PointerEvent,
10
+ type ReactElement,
11
+ } from "react";
12
+ import { Direction } from "radix-ui";
13
+ import { cx } from "../lib/cx.ts";
14
+ import { useFormat, useT } from "../lib/context.tsx";
15
+ import { StateIcon } from "../StateIcon/StateIcon.tsx";
16
+ import { ScrollRegion } from "../Layout/Layout.tsx";
17
+ import { Button } from "../Button/Button.tsx";
18
+ import { Icon } from "../Icon/Icon.tsx";
19
+ import { Tag } from "../Chips/Tag.tsx";
20
+ import { type TimelineChange, type TimelineProps } from "./Charts.types.ts";
21
+ import { categoryText, TL_CATEGORY } from "./categories.ts";
22
+ import { planChange, sameMoves, sameRows, withMoves, type TimelinePlan } from "./timeline-plan.ts";
23
+
24
+ const TL_BAR: Record<string, string> = {
25
+ active: cx("bg-accent"),
26
+ complete: cx("bg-success opacity-85"),
27
+ blocked: cx("bg-bg-hover ring-(length:--ui-hairline-half) ring-inset ring-danger"),
28
+ };
29
+
30
+ /** What a pointer drag changes: the whole bar, or one of its ends. */
31
+ type DragMode = "move" | "start" | "end";
32
+
33
+ interface Drag {
34
+ readonly row: number;
35
+ readonly mode: DragMode;
36
+ readonly x: number;
37
+ readonly from: TimelineChange;
38
+ readonly pointer: number;
39
+ }
40
+
41
+ /** Pointer travel, in pixels, before a press on a bar becomes a drag. */
42
+ const DRAG_SLOP = 4;
43
+
44
+ /** The drag payload type of a record dropped on the day grid (`onDayDrop`). */
45
+ export const RECORD_MIME = "application/x-xos-record";
46
+
47
+ /**
48
+ * The day index under a pointer on a day grid `count` days wide, counted from the inline start: from
49
+ * the left edge in left-to-right layouts and from the right edge in right-to-left ones.
50
+ */
51
+ export function dayAtPointer(
52
+ clientX: number,
53
+ rect: { readonly left: number; readonly right: number },
54
+ dayWidth: number,
55
+ count: number,
56
+ rtl: boolean,
57
+ ): number {
58
+ const offset = rtl ? rect.right - clientX : clientX - rect.left;
59
+ return Math.max(0, Math.min(count - 1, Math.floor(offset / dayWidth)));
60
+ }
61
+
62
+ /**
63
+ * The Gantt view: bars by state, baselines, dependencies, gate markers, today and the critical path.
64
+ * The drawing is hidden from assistive technology; a table beside it carries every row.
65
+ *
66
+ * With `onReschedule`, bars move and resize (Section 11.11). Dragging a bar moves it and dragging an
67
+ * end resizes it; with a bar focused, arrow keys move it a day, Shift with an arrow moves its end,
68
+ * Enter applies and Escape cancels. While a change is pending, the successors it pushes along
69
+ * `links` show where they would land, a preview line says what moves and whether the critical path
70
+ * changes, and a live region reads the same line. Apply takes the second interaction. With
71
+ * `awaitPreview`, Apply and Enter wait for the page's own preview of the exact change.
72
+ *
73
+ * With `onDayDrop`, the day grid accepts a dragged record (MIME type application/x-xos-record) and
74
+ * reports the day under the pointer, mirrored in right-to-left layouts (P8, the schedule's
75
+ * Unscheduled list). The page offers a keyboard path of its own beside it.
76
+ *
77
+ * Day-type tags (`days[].tags`, B4) draw as a band under the dates and are named with every day the table and the
78
+ * announcements read; row `segments` draw inside their row and are named in its table cell. Each carries text and an
79
+ * icon besides its colour. With `anchorDay`, the table gains an Offset column read against the anchor.
80
+ */
81
+ export function Timeline({
82
+ label,
83
+ days,
84
+ rows,
85
+ gates = [],
86
+ today,
87
+ dayWidth = 32,
88
+ links = [],
89
+ onReschedule,
90
+ onPendingChange,
91
+ preview = null,
92
+ awaitPreview = false,
93
+ onDayDrop,
94
+ anchorDay,
95
+ }: TimelineProps): ReactElement {
96
+ const t = useT();
97
+ const format = useFormat();
98
+ const dir = Direction.useDirection();
99
+ const uid = useId().replace(/:/g, "");
100
+ const markerId = `${uid}-arrow`;
101
+ const rowH = 36;
102
+ const width = days.length * dayWidth;
103
+ // A grid with no rows still takes a drop: two rows high.
104
+ const height = Math.max(rows.length, onDayDrop ? 2 : 0) * rowH;
105
+ const editable = onReschedule !== undefined;
106
+ const [draft, setDraft] = useState<TimelineChange | null>(null);
107
+ const [applying, setApplying] = useState(false);
108
+ // What the live region last said about a change that is no longer pending (applied, canceled).
109
+ const [notice, setNotice] = useState("");
110
+ const drag = useRef<Drag | null>(null);
111
+ const dragged = useRef(false);
112
+ const pending = useRef(onPendingChange);
113
+ useEffect(() => {
114
+ pending.current = onPendingChange;
115
+ });
116
+ const controls = useRef<HTMLDivElement>(null);
117
+ const previewBar = useRef<HTMLDivElement>(null);
118
+ // The bar that takes focus back once the preview's Apply or Cancel, which held it, unmounts.
119
+ const refocus = useRef<string | null>(null);
120
+
121
+ const dayLabel = (i: number) => {
122
+ const d = days[i];
123
+ if (!d) return "";
124
+ const name = d.label ?? `${d.dow} ${d.date}`;
125
+ const tags = categoryText(d.tags);
126
+ return tags ? t("timeline.dayWithTypes", { day: name, types: tags }) : name;
127
+ };
128
+ const tagged = days.some((d) => (d.tags?.length ?? 0) > 0);
129
+ const offsetText = (start: number) => {
130
+ if (anchorDay === undefined) return "";
131
+ const n = start - anchorDay;
132
+ if (n === 0) return t("timeline.offsetOn");
133
+ const key = n < 0 ? "timeline.offsetBefore" : "timeline.offsetAfter";
134
+ return Math.abs(n) === 1 ? t(`${key}One`) : t(key, { n: Math.abs(n) });
135
+ };
136
+ const offsetShort = (start: number) => {
137
+ if (anchorDay === undefined) return "";
138
+ const n = start - anchorDay;
139
+ return n === 0 ? "D0" : n < 0 ? `D\u2212${-n}` : `D+${n}`;
140
+ };
141
+ const spans = useMemo(
142
+ () =>
143
+ rows.map((r) => ({ start: r.start, end: r.end, ...(r.milestone ? { point: true } : {}) })),
144
+ [rows],
145
+ );
146
+ const page =
147
+ draft &&
148
+ preview &&
149
+ preview.change.row === draft.row &&
150
+ preview.change.start === draft.start &&
151
+ preview.change.end === draft.end
152
+ ? preview
153
+ : null;
154
+ const isLocked = useMemo(() => (i: number) => rows[i]?.locked === true, [rows]);
155
+ // The estimate from `links`; the page's own preview replaces it once it matches the change.
156
+ const estimate: TimelinePlan | null = useMemo(
157
+ () => (draft ? planChange(spans, links, draft, isLocked) : null),
158
+ [draft, spans, links, isLocked],
159
+ );
160
+ const plan: TimelinePlan | null = useMemo(
161
+ () => (draft && page ? withMoves(spans, links, draft, page.moves, isLocked) : estimate),
162
+ [draft, page, spans, links, isLocked, estimate],
163
+ );
164
+ const cycle = draft !== null && plan === null;
165
+ const refusal = page?.refusal ?? null;
166
+ // The page's answer for this exact change has not arrived yet, so the estimate stands in for it.
167
+ const waiting = awaitPreview && draft !== null && page === null && !cycle;
168
+ const blocked = cycle || waiting || refusal !== null || (plan?.locked.length ?? 0) > 0;
169
+
170
+ useEffect(() => {
171
+ pending.current?.(draft);
172
+ const key = refocus.current;
173
+ if (draft || key === null) return;
174
+ refocus.current = null;
175
+ const bars = controls.current?.querySelectorAll<HTMLButtonElement>("button[data-row]") ?? [];
176
+ for (const bar of bars) if (bar.dataset["row"] === key) bar.focus();
177
+ }, [draft]);
178
+
179
+ /** Where each row sits now: the pending change and what it pushes, else where it is. */
180
+ const at = (i: number): { start: number; end: number } => {
181
+ if (draft && draft.row === i) return draft;
182
+ const move = plan?.moves.find((m) => m.row === i);
183
+ return move ?? rows[i] ?? { start: 0, end: 0 };
184
+ };
185
+ const moved = (i: number) => {
186
+ const r = rows[i];
187
+ const now = at(i);
188
+ return !!r && (now.start !== r.start || now.end !== r.end);
189
+ };
190
+ const criticalNow = (i: number) =>
191
+ plan ? plan.criticalAfter.includes(i) : rows[i]?.critical === true;
192
+
193
+ const summary = (() => {
194
+ if (!draft) return "";
195
+ const row = rows[draft.row];
196
+ if (!row) return "";
197
+ const keys = (list: readonly number[]) =>
198
+ format.list(list.flatMap((i) => (rows[i] ? [rows[i].key] : [])));
199
+ const parts = [
200
+ t("timeline.preview.change", {
201
+ key: row.key,
202
+ start: dayLabel(draft.start),
203
+ end: dayLabel(draft.end),
204
+ }),
205
+ ];
206
+ if (cycle) return [...parts, t("timeline.preview.cycle")].join(" ");
207
+ if (plan) {
208
+ const count = plan.moves.length;
209
+ parts.push(
210
+ count === 0
211
+ ? t("timeline.preview.movesNone")
212
+ : t(count === 1 ? "timeline.preview.movesOne" : "timeline.preview.movesMany", {
213
+ count,
214
+ keys: keys(plan.moves.map((m) => m.row)),
215
+ }),
216
+ );
217
+ parts.push(
218
+ t(
219
+ sameRows(plan.criticalBefore, plan.criticalAfter)
220
+ ? "timeline.preview.criticalSame"
221
+ : "timeline.preview.criticalChanges",
222
+ ),
223
+ );
224
+ const shift = plan.finishShift;
225
+ if (shift !== 0) {
226
+ const n = Math.abs(shift);
227
+ const later = shift > 0 ? "Later" : "Earlier";
228
+ parts.push(t(`timeline.preview.finish${later}${n === 1 ? "One" : "Many"}`, { count: n }));
229
+ }
230
+ if (plan.locked.length > 0) {
231
+ parts.push(t("timeline.preview.locked", { keys: keys(plan.locked) }));
232
+ }
233
+ }
234
+ if (page?.note) parts.push(page.note);
235
+ if (refusal) parts.push(refusal);
236
+ if (waiting) parts.push(t("timeline.preview.checking"));
237
+ return parts.join(" ");
238
+ })();
239
+
240
+ const lastDay = Math.max(0, days.length - 1);
241
+ const shifted = (from: TimelineChange, mode: DragMode, by: number): TimelineChange => {
242
+ if (mode === "move") {
243
+ const d = Math.max(-from.start, Math.min(lastDay - from.end, by));
244
+ return { row: from.row, start: from.start + d, end: from.end + d };
245
+ }
246
+ if (mode === "end") {
247
+ return { ...from, end: Math.max(from.start, Math.min(lastDay, from.end + by)) };
248
+ }
249
+ return { ...from, start: Math.min(from.end, Math.max(0, from.start + by)) };
250
+ };
251
+ const base = (i: number): TimelineChange => {
252
+ if (draft && draft.row === i) return draft;
253
+ const r = rows[i];
254
+ return { row: i, start: r?.start ?? 0, end: r?.end ?? 0 };
255
+ };
256
+ const propose = (next: TimelineChange) => {
257
+ const r = rows[next.row];
258
+ setDraft(r && r.start === next.start && r.end === next.end ? null : next);
259
+ };
260
+ /** Before the preview closes: if focus is on its buttons, hand it back to the row's bar. */
261
+ const keepFocus = (row: number) => {
262
+ if (previewBar.current?.contains(document.activeElement))
263
+ refocus.current = rows[row]?.key ?? null;
264
+ };
265
+ const cancel = () => {
266
+ if (!draft) return;
267
+ keepFocus(draft.row);
268
+ setDraft(null);
269
+ setNotice(t("timeline.preview.canceled"));
270
+ };
271
+ // Apply or Enter pressed while the page's preview is on its way: applied when it arrives, if it
272
+ // moves the same rows the estimate showed; otherwise its answer stays up to be applied again.
273
+ const [queued, setQueued] = useState<TimelineChange | null>(null);
274
+ const apply = async () => {
275
+ if (!draft || !onReschedule || applying) return;
276
+ if (waiting) {
277
+ setQueued(draft);
278
+ return;
279
+ }
280
+ if (blocked) return;
281
+ const change = draft;
282
+ const key = rows[change.row]?.key ?? "";
283
+ setApplying(true);
284
+ try {
285
+ const result = await onReschedule(change);
286
+ if (result !== false) {
287
+ keepFocus(change.row);
288
+ setDraft((d) => (d === change ? null : d));
289
+ setNotice(t("timeline.preview.applied", { key }));
290
+ }
291
+ } finally {
292
+ setApplying(false);
293
+ }
294
+ };
295
+
296
+ useEffect(() => {
297
+ if (!queued || waiting) return;
298
+ setQueued(null);
299
+ if (queued !== draft || blocked || !page || page.note || !estimate) return;
300
+ if (sameMoves(estimate.moves, page.moves)) void apply();
301
+ // apply reads this render's draft and preview, which are the ones checked here.
302
+ }, [queued, waiting, draft, blocked, page, estimate]);
303
+
304
+ const sign = dir === "rtl" ? -1 : 1;
305
+ const onKey = (event: KeyboardEvent<HTMLButtonElement>, i: number) => {
306
+ const step = event.key === "ArrowRight" ? sign : event.key === "ArrowLeft" ? -sign : 0;
307
+ if (step !== 0) {
308
+ event.preventDefault();
309
+ propose(shifted(base(i), event.shiftKey && !rows[i]?.milestone ? "end" : "move", step));
310
+ event.currentTarget.scrollIntoView?.({ block: "nearest", inline: "nearest" });
311
+ } else if (event.key === "Enter" && draft?.row === i) {
312
+ event.preventDefault();
313
+ void apply();
314
+ } else if (event.key === "Escape" && draft) {
315
+ event.preventDefault();
316
+ event.stopPropagation();
317
+ cancel();
318
+ }
319
+ };
320
+ const onPointerDown = (event: PointerEvent<HTMLElement>, i: number, mode: DragMode) => {
321
+ if (event.button > 0) return;
322
+ event.stopPropagation();
323
+ if (draft && draft.row !== i) setDraft(null);
324
+ drag.current = {
325
+ row: i,
326
+ mode,
327
+ x: event.clientX,
328
+ from: draft && draft.row === i ? draft : base(i),
329
+ pointer: event.pointerId,
330
+ };
331
+ dragged.current = false;
332
+ event.currentTarget.setPointerCapture?.(event.pointerId);
333
+ };
334
+ const onPointerMove = (event: PointerEvent<HTMLElement>) => {
335
+ const d = drag.current;
336
+ if (!d || d.pointer !== event.pointerId) return;
337
+ const dx = event.clientX - d.x;
338
+ if (!dragged.current && Math.abs(dx) < DRAG_SLOP) return;
339
+ dragged.current = true;
340
+ propose(shifted(d.from, d.mode, Math.round((dx * sign) / dayWidth)));
341
+ };
342
+ const onPointerEnd = (event: PointerEvent<HTMLElement>) => {
343
+ if (drag.current?.pointer !== event.pointerId) return;
344
+ drag.current = null;
345
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
346
+ };
347
+
348
+ const barBox = (i: number) => {
349
+ const p = at(i);
350
+ return {
351
+ insetInlineStart: p.start * dayWidth + 2,
352
+ inlineSize: (p.end - p.start + 1) * dayWidth - 4,
353
+ insetBlockStart: i * rowH + 8,
354
+ };
355
+ };
356
+
357
+ return (
358
+ <figure aria-label={label} className="relative m-0">
359
+ <table className="sr-only">
360
+ <caption>{label}</caption>
361
+ <thead>
362
+ <tr>
363
+ {[
364
+ "timeline.key",
365
+ "timeline.record",
366
+ "timeline.state",
367
+ "timeline.start",
368
+ "timeline.end",
369
+ "timeline.dependsOn",
370
+ ...(anchorDay !== undefined ? ["timeline.offset"] : []),
371
+ ].map((k) => (
372
+ <th key={k} scope="col">
373
+ {t(k)}
374
+ </th>
375
+ ))}
376
+ </tr>
377
+ </thead>
378
+ <tbody>
379
+ {rows.map((r) => (
380
+ <tr key={r.key}>
381
+ <td>{r.key}</td>
382
+ <td>
383
+ {r.title}
384
+ {r.milestone ? ` (${t("timeline.milestone")})` : ""}
385
+ {r.critical ? ` (${t("timeline.critical")})` : ""}
386
+ {editable && r.locked ? ` (${t("timeline.locked")})` : ""}
387
+ {r.segments?.length
388
+ ? ` (${r.segments
389
+ .map((s) =>
390
+ t("timeline.segment", {
391
+ label: s.label,
392
+ start: dayLabel(s.start),
393
+ end: dayLabel(s.end),
394
+ }),
395
+ )
396
+ .join("; ")})`
397
+ : ""}
398
+ {r.badge ? ` (${r.badge.label})` : ""}
399
+ </td>
400
+ <td>{t(`recordStates.${r.state === "in-review" ? "inReview" : r.state}`)}</td>
401
+ <td>{dayLabel(r.start)}</td>
402
+ <td>{dayLabel(r.end)}</td>
403
+ <td>{r.dependsOn !== undefined ? (rows[r.dependsOn]?.key ?? "") : ""}</td>
404
+ {anchorDay !== undefined ? (
405
+ <td>
406
+ <abbr title={offsetText(r.start)}>{offsetShort(r.start)}</abbr>
407
+ <span className="sr-only"> {offsetText(r.start)}</span>
408
+ </td>
409
+ ) : null}
410
+ </tr>
411
+ ))}
412
+ </tbody>
413
+ </table>
414
+ <div className="flex overflow-hidden rounded-card border border-border-subtle bg-bg-canvas">
415
+ <div
416
+ aria-hidden="true"
417
+ className="flex-none w-(--ui-tree) max-sm:w-(--ui-skeleton-inline) bg-bg-surface border-e border-border-subtle"
418
+ >
419
+ <div className="flex items-center h-(--ui-shell-head) px-12 font-semibold border-b border-border-subtle">
420
+ {label}
421
+ </div>
422
+ {tagged ? (
423
+ <div className="flex items-center h-(--space-20) px-12 text-text-11 text-text-secondary border-b border-border-subtle">
424
+ {t("timeline.dayTypes")}
425
+ </div>
426
+ ) : null}
427
+ {rows.map((r) => (
428
+ <div
429
+ key={r.key}
430
+ className="flex items-center gap-6 h-row-default px-12 whitespace-nowrap overflow-hidden"
431
+ >
432
+ <StateIcon state={r.state} />
433
+ <span className="font-mono text-text-12 text-text-secondary">{r.key}</span>
434
+ <span className="truncate" title={r.title}>
435
+ {r.title}
436
+ </span>
437
+ {r.badge ? (
438
+ <span className="ms-auto flex-none">
439
+ <Tag tone={r.badge.tone ?? "neutral"}>{r.badge.label}</Tag>
440
+ </span>
441
+ ) : null}
442
+ </div>
443
+ ))}
444
+ </div>
445
+ {/* The day grid scrolls sideways; the region takes keyboard focus when it overflows, so
446
+ arrow keys reach the later dates (2.1.1). Its drawing stays hidden: the table above
447
+ carries the data, and the bar controls laid over it carry rescheduling. */}
448
+ <ScrollRegion label={t("timeline.scroll", { label })} frameClassName="flex-1">
449
+ {/* Without rescheduling the whole content is the hidden drawing; with it, the drawing is
450
+ hidden and the bar controls beside it are not. */}
451
+ <div
452
+ className="relative"
453
+ style={{ inlineSize: width }}
454
+ {...(editable ? {} : { "aria-hidden": true })}
455
+ {...(onDayDrop
456
+ ? {
457
+ "data-day-drop": "",
458
+ onDragOver: (e: DragEvent<HTMLDivElement>) => {
459
+ if (e.dataTransfer.types.includes(RECORD_MIME)) {
460
+ e.preventDefault();
461
+ e.dataTransfer.dropEffect = "move";
462
+ }
463
+ },
464
+ onDrop: (e: DragEvent<HTMLDivElement>) => {
465
+ const payload = e.dataTransfer.getData(RECORD_MIME);
466
+ if (!payload) return;
467
+ e.preventDefault();
468
+ const rect = e.currentTarget.getBoundingClientRect();
469
+ onDayDrop(
470
+ dayAtPointer(e.clientX, rect, dayWidth, days.length, dir === "rtl"),
471
+ payload,
472
+ );
473
+ },
474
+ }
475
+ : {})}
476
+ >
477
+ <div {...(editable ? { "aria-hidden": true } : {})}>
478
+ <div
479
+ className="flex h-(--ui-shell-head) border-b border-border-subtle"
480
+ style={{ inlineSize: width }}
481
+ >
482
+ {days.map((d, i) => (
483
+ <div
484
+ key={i}
485
+ style={{ inlineSize: dayWidth }}
486
+ className={cx(
487
+ "flex flex-none flex-col items-center justify-center text-text-11 text-text-tertiary",
488
+ d.weekend && "bg-bg-surface",
489
+ )}
490
+ >
491
+ <span>{d.dow}</span>
492
+ <strong className="text-text-12 font-medium text-text-secondary tabular-nums">
493
+ {d.date}
494
+ </strong>
495
+ </div>
496
+ ))}
497
+ </div>
498
+ {tagged ? (
499
+ <div
500
+ className="flex h-(--space-20) border-b border-border-subtle"
501
+ style={{ inlineSize: width }}
502
+ >
503
+ {days.map((d, i) => (
504
+ <div
505
+ key={i}
506
+ style={{ inlineSize: dayWidth }}
507
+ title={categoryText(d.tags)}
508
+ className="flex flex-none items-center justify-center gap-1 overflow-hidden"
509
+ >
510
+ {(d.tags ?? []).slice(0, 2).map((tag) => (
511
+ <span
512
+ key={tag.label}
513
+ className={cx(
514
+ "inline-flex size-(--space-16) items-center justify-center rounded-mark text-viz-ink forced-colors:border",
515
+ TL_CATEGORY[tag.category],
516
+ )}
517
+ >
518
+ {tag.icon ? <Icon name={tag.icon} size={12} /> : tag.label.slice(0, 1)}
519
+ </span>
520
+ ))}
521
+ </div>
522
+ ))}
523
+ </div>
524
+ ) : null}
525
+ <div className="relative" style={{ inlineSize: width, blockSize: height }}>
526
+ {days.map((d, i) =>
527
+ d.weekend ? (
528
+ <div
529
+ key={i}
530
+ className="absolute inset-y-0 bg-bg-surface"
531
+ style={{ insetInlineStart: i * dayWidth, inlineSize: dayWidth }}
532
+ />
533
+ ) : null,
534
+ )}
535
+ {rows.map((r, i) => {
536
+ const top = i * rowH;
537
+ const p = at(i);
538
+ const isMoved = moved(i);
539
+ return (
540
+ <div key={r.key}>
541
+ {(r.segments ?? []).map((seg) => (
542
+ <div
543
+ key={`${seg.label}-${seg.start}`}
544
+ title={seg.label}
545
+ className={cx(
546
+ "absolute flex h-(--space-6) items-center rounded-pill opacity-70 forced-colors:border",
547
+ TL_CATEGORY[seg.category],
548
+ )}
549
+ style={{
550
+ insetInlineStart: seg.start * dayWidth,
551
+ inlineSize: (seg.end - seg.start + 1) * dayWidth,
552
+ insetBlockStart: top + 30,
553
+ }}
554
+ />
555
+ ))}
556
+ {r.baselineStart !== undefined && r.baselineEnd !== undefined ? (
557
+ <div
558
+ className="absolute h-(--space-4) rounded-pill bg-text-tertiary opacity-60"
559
+ style={{
560
+ insetInlineStart: r.baselineStart * dayWidth,
561
+ inlineSize: (r.baselineEnd - r.baselineStart + 1) * dayWidth,
562
+ insetBlockStart: top + 26,
563
+ }}
564
+ />
565
+ ) : null}
566
+ {isMoved && !r.milestone ? (
567
+ // Where the bar was before the pending change.
568
+ <div
569
+ className="absolute h-(--space-20) rounded-chip border border-dashed border-border-strong"
570
+ style={{
571
+ insetInlineStart: r.start * dayWidth + 2,
572
+ inlineSize: (r.end - r.start + 1) * dayWidth - 4,
573
+ insetBlockStart: top + 8,
574
+ }}
575
+ />
576
+ ) : null}
577
+ {r.milestone ? (
578
+ <div
579
+ className={cx(
580
+ "absolute size-(--space-12) rotate-45 rounded-mark bg-warning",
581
+ isMoved && "ring-(length:--stroke-width-focus) ring-accent",
582
+ )}
583
+ style={{
584
+ insetInlineStart: p.start * dayWidth + dayWidth / 2 - 6,
585
+ insetBlockStart: top + 12,
586
+ }}
587
+ />
588
+ ) : (
589
+ <div
590
+ className={cx(
591
+ "absolute h-(--space-20) overflow-hidden rounded-chip",
592
+ TL_BAR[r.state] ??
593
+ "bg-bg-hover ring-(length:--stroke-width-hairline) ring-inset ring-border-strong",
594
+ criticalNow(i) &&
595
+ "ring-(length:--stroke-width-focus) ring-inset ring-danger",
596
+ isMoved &&
597
+ (r.locked
598
+ ? "outline-(length:--stroke-width-focus) outline-dashed outline-danger"
599
+ : "outline-(length:--stroke-width-focus) outline-dashed outline-accent"),
600
+ "forced-colors:bg-[Highlight]",
601
+ )}
602
+ style={barBox(i)}
603
+ >
604
+ {r.progress !== undefined ? (
605
+ <span
606
+ className="block h-full bg-viz-ink opacity-20"
607
+ style={{ inlineSize: `${r.progress}%` }}
608
+ />
609
+ ) : null}
610
+ </div>
611
+ )}
612
+ </div>
613
+ );
614
+ })}
615
+ {gates.map((g) => (
616
+ <div
617
+ key={g.label}
618
+ className="absolute inset-y-0 border-s border-dashed border-state-in-review"
619
+ style={{ insetInlineStart: g.day * dayWidth }}
620
+ >
621
+ <span className="absolute top-2 start-4 px-2 whitespace-nowrap text-text-11 text-text-secondary bg-bg-canvas">
622
+ {g.label}
623
+ </span>
624
+ </div>
625
+ ))}
626
+ {today !== undefined ? (
627
+ <div
628
+ className="absolute inset-y-0 w-(--stroke-width-focus) bg-danger"
629
+ style={{ insetInlineStart: today * dayWidth + dayWidth / 2 }}
630
+ />
631
+ ) : null}
632
+ <svg
633
+ className="absolute start-0 top-0 overflow-visible pointer-events-none rtl:-scale-x-100"
634
+ width={width}
635
+ height={height}
636
+ >
637
+ <defs>
638
+ <marker
639
+ id={markerId}
640
+ viewBox="0 0 8 8"
641
+ refX={7}
642
+ refY={4}
643
+ markerWidth={6}
644
+ markerHeight={6}
645
+ orient="auto"
646
+ >
647
+ <path d="M0 0 L8 4 L0 8 z" className="fill-text-secondary" />
648
+ </marker>
649
+ </defs>
650
+ {rows.map((r, i) => {
651
+ if (r.dependsOn === undefined) return null;
652
+ if (!rows[r.dependsOn]) return null;
653
+ const x1 = (at(r.dependsOn).end + 1) * dayWidth;
654
+ const y1 = r.dependsOn * rowH + rowH / 2;
655
+ const x2 = at(i).start * dayWidth;
656
+ const y2 = i * rowH + rowH / 2;
657
+ const sx = Math.min(x1 - 8, x2 - 4);
658
+ return (
659
+ <path
660
+ key={r.key}
661
+ d={`M${sx} ${y1 + 10} V${y2} H${x2 - 2}`}
662
+ className="fill-none stroke-text-secondary"
663
+ strokeWidth={1.25}
664
+ markerEnd={`url(#${markerId})`}
665
+ />
666
+ );
667
+ })}
668
+ </svg>
669
+ </div>
670
+ </div>
671
+ {editable ? (
672
+ <div
673
+ ref={controls}
674
+ role="group"
675
+ aria-label={t("timeline.controls", { label })}
676
+ className="absolute inset-x-0 bottom-0"
677
+ style={{ blockSize: height }}
678
+ >
679
+ <span id={`${uid}-hint`} className="sr-only">
680
+ {t("timeline.moveHint")}
681
+ </span>
682
+ {rows.map((r, i) =>
683
+ r.locked ? null : (
684
+ <button
685
+ key={r.key}
686
+ type="button"
687
+ data-row={r.key}
688
+ aria-label={t("timeline.bar", {
689
+ key: r.key,
690
+ title: r.title,
691
+ start: dayLabel(at(i).start),
692
+ end: dayLabel(at(i).end),
693
+ })}
694
+ aria-describedby={`${uid}-hint`}
695
+ className={cx(
696
+ "absolute h-(--space-20) p-0 rounded-chip border-0 bg-transparent cursor-grab touch-none",
697
+ "active:cursor-grabbing",
698
+ )}
699
+ style={
700
+ r.milestone
701
+ ? {
702
+ insetInlineStart: at(i).start * dayWidth + dayWidth / 2 - 10,
703
+ inlineSize: 20,
704
+ insetBlockStart: i * rowH + 8,
705
+ }
706
+ : barBox(i)
707
+ }
708
+ onKeyDown={(e) => onKey(e, i)}
709
+ onPointerDown={(e) => onPointerDown(e, i, "move")}
710
+ onPointerMove={onPointerMove}
711
+ onPointerUp={onPointerEnd}
712
+ onPointerCancel={onPointerEnd}
713
+ >
714
+ {r.milestone ? null : (
715
+ <>
716
+ <span
717
+ aria-hidden="true"
718
+ className="absolute inset-y-0 start-0 w-(--space-6) cursor-ew-resize"
719
+ onPointerDown={(e) => onPointerDown(e, i, "start")}
720
+ onPointerMove={onPointerMove}
721
+ onPointerUp={onPointerEnd}
722
+ onPointerCancel={onPointerEnd}
723
+ />
724
+ <span
725
+ aria-hidden="true"
726
+ className="absolute inset-y-0 end-0 w-(--space-6) cursor-ew-resize"
727
+ onPointerDown={(e) => onPointerDown(e, i, "end")}
728
+ onPointerMove={onPointerMove}
729
+ onPointerUp={onPointerEnd}
730
+ onPointerCancel={onPointerEnd}
731
+ />
732
+ </>
733
+ )}
734
+ </button>
735
+ ),
736
+ )}
737
+ </div>
738
+ ) : null}
739
+ </div>
740
+ </ScrollRegion>
741
+ </div>
742
+ {editable && draft ? (
743
+ <div
744
+ ref={previewBar}
745
+ role="group"
746
+ aria-label={t("timeline.preview.label")}
747
+ onKeyDown={(e) => {
748
+ if (e.key === "Escape") {
749
+ e.stopPropagation();
750
+ cancel();
751
+ }
752
+ }}
753
+ className="flex flex-wrap items-center gap-8 mt-8 px-12 py-8 rounded-card border border-border-subtle bg-bg-surface"
754
+ >
755
+ <p className="m-0 flex-1 min-w-0 text-text-13">{summary}</p>
756
+ <Button
757
+ size="sm"
758
+ variant="primary"
759
+ disabled={blocked && !waiting}
760
+ loading={applying || (queued !== null && queued === draft)}
761
+ onClick={() => void apply()}
762
+ >
763
+ {t("timeline.preview.apply")}
764
+ </Button>
765
+ <Button size="sm" variant="ghost" onClick={cancel}>
766
+ {t("timeline.preview.cancel")}
767
+ </Button>
768
+ </div>
769
+ ) : null}
770
+ {editable ? (
771
+ <p aria-live="polite" className="sr-only">
772
+ {draft ? summary : notice}
773
+ </p>
774
+ ) : null}
775
+ </figure>
776
+ );
777
+ }