@oprt/react 0.0.0-stage → 0.2.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 (266) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -2
  3. package/dist/ActionStatus/ActionStatus.js +104 -0
  4. package/dist/ActionStatus/ActionStatus.manifest.js +64 -0
  5. package/dist/ActionStatus/index.js +7 -0
  6. package/dist/Alert/Alert.js +86 -0
  7. package/dist/Alert/Alert.manifest.js +36 -0
  8. package/dist/Alert/index.js +7 -0
  9. package/dist/AppShell/AppShell.js +103 -0
  10. package/dist/AppShell/AppShell.manifest.js +66 -0
  11. package/dist/AppShell/index.js +9 -0
  12. package/dist/AttachmentPreview/AttachmentPreview.js +145 -0
  13. package/dist/AttachmentPreview/AttachmentPreview.manifest.js +42 -0
  14. package/dist/AttachmentPreview/index.js +9 -0
  15. package/dist/Avatar/Avatar.js +87 -0
  16. package/dist/Avatar/Avatar.manifest.js +25 -0
  17. package/dist/Avatar/index.js +7 -0
  18. package/dist/Badge/Badge.js +124 -0
  19. package/dist/Badge/Badge.manifest.js +41 -0
  20. package/dist/Badge/index.js +7 -0
  21. package/dist/Breadcrumb/Breadcrumb.js +64 -0
  22. package/dist/Breadcrumb/Breadcrumb.manifest.js +28 -0
  23. package/dist/Breadcrumb/index.js +7 -0
  24. package/dist/BulkActionBar/BulkActionBar.js +145 -0
  25. package/dist/BulkActionBar/BulkActionBar.manifest.js +46 -0
  26. package/dist/BulkActionBar/index.js +9 -0
  27. package/dist/BulkActionBar/selection.js +44 -0
  28. package/dist/BulkResults/BulkResults.js +269 -0
  29. package/dist/BulkResults/BulkResults.manifest.js +45 -0
  30. package/dist/BulkResults/index.js +7 -0
  31. package/dist/Button/Button.js +170 -0
  32. package/dist/Button/Button.manifest.js +36 -0
  33. package/dist/Button/index.js +7 -0
  34. package/dist/ButtonGroup/ButtonGroup.js +60 -0
  35. package/dist/ButtonGroup/ButtonGroup.manifest.js +22 -0
  36. package/dist/ButtonGroup/index.js +7 -0
  37. package/dist/Calendar/Calendar.js +106 -0
  38. package/dist/Calendar/Calendar.manifest.js +32 -0
  39. package/dist/Calendar/index.js +7 -0
  40. package/dist/Card/Card.js +70 -0
  41. package/dist/Card/Card.manifest.js +57 -0
  42. package/dist/Card/index.js +16 -0
  43. package/dist/Chart/Chart.js +1200 -0
  44. package/dist/Chart/Chart.manifest.js +110 -0
  45. package/dist/Chart/ChartControls.js +190 -0
  46. package/dist/Chart/geometry.js +217 -0
  47. package/dist/Chart/index.js +9 -0
  48. package/dist/Checkbox/Checkbox.js +166 -0
  49. package/dist/Checkbox/Checkbox.manifest.js +38 -0
  50. package/dist/Checkbox/index.js +7 -0
  51. package/dist/Collapsible/Collapsible.js +177 -0
  52. package/dist/Collapsible/Collapsible.manifest.js +54 -0
  53. package/dist/Collapsible/index.js +12 -0
  54. package/dist/CollectionState/CollectionState.js +68 -0
  55. package/dist/CollectionState/CollectionState.manifest.js +44 -0
  56. package/dist/CollectionState/index.js +7 -0
  57. package/dist/ComboBox/ComboBox.js +221 -0
  58. package/dist/ComboBox/ComboBox.manifest.js +87 -0
  59. package/dist/ComboBox/index.js +7 -0
  60. package/dist/CommandPalette/CommandPalette.js +276 -0
  61. package/dist/CommandPalette/CommandPalette.manifest.js +24 -0
  62. package/dist/CommandPalette/index.js +7 -0
  63. package/dist/ConfirmDialog/ConfirmDialog.js +84 -0
  64. package/dist/ConfirmDialog/ConfirmDialog.manifest.js +32 -0
  65. package/dist/ConfirmDialog/index.js +7 -0
  66. package/dist/ContextMenu/ContextMenu.js +192 -0
  67. package/dist/ContextMenu/ContextMenu.manifest.js +41 -0
  68. package/dist/ContextMenu/index.js +7 -0
  69. package/dist/CoordinateInput/CoordinateInput.js +141 -0
  70. package/dist/CoordinateInput/CoordinateInput.manifest.js +36 -0
  71. package/dist/CoordinateInput/index.js +9 -0
  72. package/dist/CopyButton/CopyButton.js +129 -0
  73. package/dist/CopyButton/CopyButton.manifest.js +35 -0
  74. package/dist/CopyButton/index.js +7 -0
  75. package/dist/DataGrid/DataGrid.js +459 -0
  76. package/dist/DataGrid/DataGrid.manifest.js +135 -0
  77. package/dist/DataGrid/PageControls.js +41 -0
  78. package/dist/DataGrid/index.js +9 -0
  79. package/dist/DataTable/DataTable.js +375 -0
  80. package/dist/DataTable/DataTable.manifest.js +86 -0
  81. package/dist/DataTable/index.js +9 -0
  82. package/dist/DataTable/sort.js +45 -0
  83. package/dist/DataTable/useScrollsSideways.js +61 -0
  84. package/dist/DataTable/useTableWindow.js +185 -0
  85. package/dist/DatePicker/DatePicker.js +187 -0
  86. package/dist/DatePicker/DatePicker.manifest.js +40 -0
  87. package/dist/DatePicker/index.js +7 -0
  88. package/dist/DateRangePicker/DateRangePicker.js +156 -0
  89. package/dist/DateRangePicker/DateRangePicker.manifest.js +41 -0
  90. package/dist/DateRangePicker/index.js +7 -0
  91. package/dist/DateTimePicker/DateTimePicker.js +213 -0
  92. package/dist/DateTimePicker/DateTimePicker.manifest.js +44 -0
  93. package/dist/DateTimePicker/index.js +7 -0
  94. package/dist/DescriptionList/DescriptionList.js +93 -0
  95. package/dist/DescriptionList/DescriptionList.manifest.js +40 -0
  96. package/dist/DescriptionList/index.js +9 -0
  97. package/dist/Dialog/Dialog.js +214 -0
  98. package/dist/Dialog/Dialog.manifest.js +31 -0
  99. package/dist/Dialog/index.js +7 -0
  100. package/dist/Divider/Divider.js +64 -0
  101. package/dist/Divider/Divider.manifest.js +21 -0
  102. package/dist/Divider/index.js +7 -0
  103. package/dist/DropdownMenu/DropdownMenu.js +170 -0
  104. package/dist/DropdownMenu/DropdownMenu.manifest.js +82 -0
  105. package/dist/DropdownMenu/index.js +16 -0
  106. package/dist/EntityPreview/EntityPreview.js +82 -0
  107. package/dist/EntityPreview/EntityPreview.manifest.js +43 -0
  108. package/dist/EntityPreview/index.js +7 -0
  109. package/dist/EventTimeline/EventTimeline.js +249 -0
  110. package/dist/EventTimeline/EventTimeline.manifest.js +57 -0
  111. package/dist/EventTimeline/index.js +7 -0
  112. package/dist/Field/Field.js +81 -0
  113. package/dist/Field/Field.manifest.js +32 -0
  114. package/dist/Field/index.js +9 -0
  115. package/dist/FileUpload/FileUpload.js +215 -0
  116. package/dist/FileUpload/FileUpload.manifest.js +48 -0
  117. package/dist/FileUpload/index.js +7 -0
  118. package/dist/FilterBar/FilterBar.js +173 -0
  119. package/dist/FilterBar/FilterBar.manifest.js +49 -0
  120. package/dist/FilterBar/index.js +7 -0
  121. package/dist/FormErrorSummary/FormErrorSummary.js +70 -0
  122. package/dist/FormErrorSummary/FormErrorSummary.manifest.js +29 -0
  123. package/dist/FormErrorSummary/index.js +9 -0
  124. package/dist/FormSection/FormSection.js +51 -0
  125. package/dist/FormSection/FormSection.manifest.js +30 -0
  126. package/dist/FormSection/index.js +7 -0
  127. package/dist/InlineEdit/InlineEdit.js +261 -0
  128. package/dist/InlineEdit/InlineEdit.manifest.js +40 -0
  129. package/dist/InlineEdit/index.js +7 -0
  130. package/dist/Input/Input.js +126 -0
  131. package/dist/Input/Input.manifest.js +49 -0
  132. package/dist/Input/index.js +7 -0
  133. package/dist/Inspector/Inspector.js +63 -0
  134. package/dist/Inspector/Inspector.manifest.js +35 -0
  135. package/dist/Inspector/index.js +7 -0
  136. package/dist/JobStatus/JobStatus.js +118 -0
  137. package/dist/JobStatus/JobStatus.manifest.js +53 -0
  138. package/dist/JobStatus/index.js +7 -0
  139. package/dist/LayerControl/LayerControl.js +199 -0
  140. package/dist/LayerControl/LayerControl.manifest.js +34 -0
  141. package/dist/LayerControl/index.js +7 -0
  142. package/dist/Link/Link.js +83 -0
  143. package/dist/Link/Link.manifest.js +37 -0
  144. package/dist/Link/index.js +7 -0
  145. package/dist/MapControls/MapControls.js +122 -0
  146. package/dist/MapControls/MapControls.manifest.js +31 -0
  147. package/dist/MapControls/index.js +7 -0
  148. package/dist/MapLegend/MapLegend.js +89 -0
  149. package/dist/MapLegend/MapLegend.manifest.js +29 -0
  150. package/dist/MapLegend/index.js +7 -0
  151. package/dist/MapScale/MapScale.js +96 -0
  152. package/dist/MapScale/MapScale.manifest.js +31 -0
  153. package/dist/MapScale/index.js +7 -0
  154. package/dist/MediaFrame/MediaFrame.js +113 -0
  155. package/dist/MediaFrame/MediaFrame.manifest.js +57 -0
  156. package/dist/MediaFrame/index.js +7 -0
  157. package/dist/MultiSelect/MultiSelect.js +113 -0
  158. package/dist/MultiSelect/MultiSelect.manifest.js +51 -0
  159. package/dist/MultiSelect/index.js +7 -0
  160. package/dist/NumberInput/NumberInput.js +210 -0
  161. package/dist/NumberInput/NumberInput.manifest.js +56 -0
  162. package/dist/NumberInput/index.js +7 -0
  163. package/dist/OperationalStatus/OperationalStatus.js +89 -0
  164. package/dist/OperationalStatus/OperationalStatus.manifest.js +39 -0
  165. package/dist/OperationalStatus/index.js +9 -0
  166. package/dist/PlaybackControls/PlaybackControls.js +289 -0
  167. package/dist/PlaybackControls/PlaybackControls.manifest.js +85 -0
  168. package/dist/PlaybackControls/index.js +7 -0
  169. package/dist/Popover/Popover.js +302 -0
  170. package/dist/Popover/Popover.manifest.js +50 -0
  171. package/dist/Popover/index.js +7 -0
  172. package/dist/Progress/Progress.js +132 -0
  173. package/dist/Progress/Progress.manifest.js +30 -0
  174. package/dist/Progress/index.js +7 -0
  175. package/dist/PropertyFilter/PropertyFilter.js +306 -0
  176. package/dist/PropertyFilter/PropertyFilter.manifest.js +32 -0
  177. package/dist/PropertyFilter/index.js +11 -0
  178. package/dist/PropertyFilter/tokenError.js +17 -0
  179. package/dist/RadioGroup/RadioGroup.js +143 -0
  180. package/dist/RadioGroup/RadioGroup.manifest.js +44 -0
  181. package/dist/RadioGroup/index.js +9 -0
  182. package/dist/Select/Select.js +219 -0
  183. package/dist/Select/Select.manifest.js +77 -0
  184. package/dist/Select/index.js +9 -0
  185. package/dist/Sidebar/Sidebar.js +146 -0
  186. package/dist/Sidebar/Sidebar.manifest.js +72 -0
  187. package/dist/Sidebar/index.js +18 -0
  188. package/dist/Skeleton/Skeleton.js +76 -0
  189. package/dist/Skeleton/Skeleton.manifest.js +30 -0
  190. package/dist/Skeleton/index.js +7 -0
  191. package/dist/Slider/Slider.js +246 -0
  192. package/dist/Slider/Slider.manifest.js +56 -0
  193. package/dist/Slider/index.js +7 -0
  194. package/dist/Sparkline/Sparkline.js +135 -0
  195. package/dist/Sparkline/Sparkline.manifest.js +37 -0
  196. package/dist/Sparkline/index.js +7 -0
  197. package/dist/SplitPane/SplitPane.js +316 -0
  198. package/dist/SplitPane/SplitPane.manifest.js +79 -0
  199. package/dist/SplitPane/index.js +7 -0
  200. package/dist/StatCard/StatCard.js +163 -0
  201. package/dist/StatCard/StatCard.manifest.js +45 -0
  202. package/dist/StatCard/index.js +7 -0
  203. package/dist/Tabs/Tabs.js +181 -0
  204. package/dist/Tabs/Tabs.manifest.js +72 -0
  205. package/dist/Tabs/index.js +16 -0
  206. package/dist/Tabs/useTabsIndicator.js +71 -0
  207. package/dist/Tabs/useTabsScroll.js +123 -0
  208. package/dist/TagGroup/TagGroup.js +142 -0
  209. package/dist/TagGroup/TagGroup.manifest.js +35 -0
  210. package/dist/TagGroup/index.js +7 -0
  211. package/dist/Textarea/Textarea.js +68 -0
  212. package/dist/Textarea/Textarea.manifest.js +25 -0
  213. package/dist/Textarea/index.js +7 -0
  214. package/dist/TimeField/TimeField.js +118 -0
  215. package/dist/TimeField/TimeField.manifest.js +42 -0
  216. package/dist/TimeField/index.js +7 -0
  217. package/dist/TimeRange/TimeRange.js +184 -0
  218. package/dist/TimeRange/TimeRange.manifest.js +48 -0
  219. package/dist/TimeRange/index.js +9 -0
  220. package/dist/Toast/Toast.js +188 -0
  221. package/dist/Toast/Toast.manifest.js +77 -0
  222. package/dist/Toast/index.js +9 -0
  223. package/dist/Toggle/Toggle.js +154 -0
  224. package/dist/Toggle/Toggle.manifest.js +54 -0
  225. package/dist/Toggle/index.js +7 -0
  226. package/dist/Toolbar/Toolbar.js +86 -0
  227. package/dist/Toolbar/Toolbar.manifest.js +70 -0
  228. package/dist/Toolbar/index.js +12 -0
  229. package/dist/Tooltip/Tooltip.js +123 -0
  230. package/dist/Tooltip/Tooltip.manifest.js +27 -0
  231. package/dist/Tooltip/index.js +7 -0
  232. package/dist/TreeView/TreeView.js +129 -0
  233. package/dist/TreeView/TreeView.manifest.js +56 -0
  234. package/dist/TreeView/index.js +7 -0
  235. package/dist/UploadQueue/UploadQueue.js +296 -0
  236. package/dist/UploadQueue/UploadQueue.manifest.js +34 -0
  237. package/dist/UploadQueue/index.js +7 -0
  238. package/dist/_internal/MotionPopover.js +113 -0
  239. package/dist/_internal/aria.js +40 -0
  240. package/dist/_internal/bytes.js +18 -0
  241. package/dist/_internal/controlStyles.js +26 -0
  242. package/dist/_internal/date.js +179 -0
  243. package/dist/_internal/devValidate.js +51 -0
  244. package/dist/_internal/fieldIds.js +50 -0
  245. package/dist/_internal/hydration.js +74 -0
  246. package/dist/_internal/manifest-entry.js +0 -0
  247. package/dist/_internal/mergeRefs.js +68 -0
  248. package/dist/_internal/motion.js +138 -0
  249. package/dist/_internal/navigationLandmark.js +13 -0
  250. package/dist/_internal/numberText.js +176 -0
  251. package/dist/_internal/overlay.js +53 -0
  252. package/dist/_internal/segmentField.js +125 -0
  253. package/dist/_internal/suggestions.js +20 -0
  254. package/dist/_internal/timestamp.js +25 -0
  255. package/dist/_internal/unavailable.js +13 -0
  256. package/dist/_internal/useActionFocus.js +27 -0
  257. package/dist/_internal/useElementWidth.js +37 -0
  258. package/dist/_internal/useIsMounted.js +18 -0
  259. package/dist/_internal/useIsomorphicLayoutEffect.js +8 -0
  260. package/dist/_internal/useRestoreFocusOnRemoval.js +57 -0
  261. package/dist/index.cjs +17831 -0
  262. package/dist/index.d.cts +2330 -0
  263. package/dist/index.d.ts +2330 -0
  264. package/dist/index.js +407 -0
  265. package/dist/version.js +8 -0
  266. package/package.json +80 -3
@@ -0,0 +1,249 @@
1
+ "use client";
2
+
3
+ // src/EventTimeline/EventTimeline.tsx
4
+ import { cva } from "class-variance-authority";
5
+ import clsx from "clsx";
6
+ import {
7
+ forwardRef,
8
+ memo,
9
+ useCallback,
10
+ useId,
11
+ useMemo,
12
+ useRef,
13
+ useState
14
+ } from "react";
15
+ import { Badge } from "../Badge/Badge.js";
16
+ import { Button } from "../Button/Button.js";
17
+ import { useMergedRefs } from "../_internal/mergeRefs.js";
18
+ import { formatUtcTimestamp, isValidTimestamp } from "../_internal/timestamp.js";
19
+ import { useElementWidth } from "../_internal/useElementWidth.js";
20
+ import { useIsomorphicLayoutEffect } from "../_internal/useIsomorphicLayoutEffect.js";
21
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
22
+ var timelineStyles = cva("flex min-w-0 flex-col gap-2u font-sans text-oprt-text");
23
+ var markStyles = cva("fill-current", {
24
+ variants: {
25
+ severity: {
26
+ neutral: "text-oprt-text-dim",
27
+ info: "text-oprt-info",
28
+ success: "text-oprt-success",
29
+ warning: "text-oprt-warning",
30
+ error: "text-oprt-error"
31
+ }
32
+ },
33
+ defaultVariants: { severity: "neutral" }
34
+ });
35
+ var utc = (value) => formatUtcTimestamp(value, { precision: "milliseconds" }) ?? "";
36
+ var eventValid = (event) => isValidTimestamp(event.start) && (event.end === void 0 || isValidTimestamp(event.end) && event.end >= event.start);
37
+ var laneOf = (event) => event.lane || "Events";
38
+ var DEFAULT_WIDTH = 1e3;
39
+ var MIN_WIDTH = 16;
40
+ var Marker = memo(function Marker2({ id, severity, x, width, selected }) {
41
+ const classes = clsx(
42
+ markStyles({ severity: severity ?? "neutral" }),
43
+ selected && "stroke-oprt-text"
44
+ );
45
+ return width !== null ? /* @__PURE__ */ jsx(
46
+ "rect",
47
+ {
48
+ "data-event-id": id,
49
+ x,
50
+ y: selected ? 10 : 14,
51
+ width,
52
+ height: selected ? 28 : 20,
53
+ className: classes,
54
+ vectorEffect: "non-scaling-stroke"
55
+ }
56
+ ) : /* @__PURE__ */ jsx(
57
+ "path",
58
+ {
59
+ "data-event-id": id,
60
+ d: `M ${x} 8 L ${x + 5} 24 L ${x} 40 L ${x - 5} 24 Z`,
61
+ className: classes,
62
+ vectorEffect: "non-scaling-stroke"
63
+ }
64
+ );
65
+ });
66
+ var TimelineRow = memo(function TimelineRow2({
67
+ event,
68
+ selected,
69
+ selectable,
70
+ disabled,
71
+ detailsId,
72
+ onSelect
73
+ }) {
74
+ return /* @__PURE__ */ jsxs(
75
+ "li",
76
+ {
77
+ className: clsx(
78
+ "flex min-w-0 flex-col gap-1u border p-2u",
79
+ selected ? "border-oprt-accent bg-oprt-surface-2" : "border-oprt-border"
80
+ ),
81
+ children: [
82
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2u", children: [
83
+ selectable ? /* @__PURE__ */ jsx(
84
+ Button,
85
+ {
86
+ size: "sm",
87
+ variant: "ghost",
88
+ disabled,
89
+ className: "h-auto! min-h-3u min-w-0 flex-1 justify-start! px-0! whitespace-normal text-left normal-case! tracking-normal hover:underline",
90
+ "aria-pressed": selected,
91
+ "aria-describedby": detailsId,
92
+ onClick: () => {
93
+ if (!disabled) onSelect(event.id);
94
+ },
95
+ children: /* @__PURE__ */ jsx("span", { className: "block min-w-0 oprt-wrap-anywhere", children: event.label })
96
+ }
97
+ ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 text-oprt-base font-medium normal-case oprt-wrap-anywhere", children: event.label }),
98
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1u", children: [
99
+ /* @__PURE__ */ jsx(Badge, { variant: event.severity ?? "neutral", indicator: "dot", children: event.severity ?? "neutral" }),
100
+ selected && /* @__PURE__ */ jsx("span", { className: "text-oprt-base font-medium uppercase tracking-oprt-caps", children: "Selected" })
101
+ ] })
102
+ ] }),
103
+ /* @__PURE__ */ jsxs("div", { id: detailsId, className: "flex min-w-0 flex-col gap-1u text-oprt-base normal-case", children: [
104
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2u gap-y-1u text-oprt-text-dim", children: [
105
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 font-mono oprt-wrap-anywhere", children: [
106
+ /* @__PURE__ */ jsx("time", { dateTime: new Date(event.start).toISOString(), children: utc(event.start) }),
107
+ event.end !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ " \u2013 ",
109
+ /* @__PURE__ */ jsx("time", { dateTime: new Date(event.end).toISOString(), children: utc(event.end) })
110
+ ] })
111
+ ] }),
112
+ event.lane && /* @__PURE__ */ jsx("span", { className: "min-w-0 oprt-wrap-anywhere", children: event.lane })
113
+ ] }),
114
+ event.description && /* @__PURE__ */ jsx("p", { className: "m-0 oprt-wrap-anywhere", children: event.description })
115
+ ] })
116
+ ]
117
+ }
118
+ );
119
+ });
120
+ var EventTimeline = forwardRef(
121
+ ({
122
+ events,
123
+ start,
124
+ end,
125
+ selectedId,
126
+ onSelectionChange,
127
+ disabled = false,
128
+ label = "Event timeline",
129
+ emptyMessage = "No events in this time range.",
130
+ className,
131
+ ...props
132
+ }, forwardedRef) => {
133
+ const id = useId();
134
+ const rootRef = useRef(null);
135
+ const setRef = useMergedRefs(rootRef, forwardedRef);
136
+ const observed = useElementWidth(rootRef, {
137
+ initial: null,
138
+ min: MIN_WIDTH
139
+ });
140
+ const [firstWidth, setFirstWidth] = useState(null);
141
+ useIsomorphicLayoutEffect(() => {
142
+ const width = rootRef.current?.getBoundingClientRect().width;
143
+ if (width !== void 0 && Number.isFinite(width) && width > MIN_WIDTH) setFirstWidth(width);
144
+ }, []);
145
+ const plotWidth = observed ?? firstWidth ?? DEFAULT_WIDTH;
146
+ const validRange = isValidTimestamp(start) && isValidTimestamp(end) && start < end;
147
+ const { visible, omitted, lanes } = useMemo(() => {
148
+ const validEvents = events.filter(eventValid);
149
+ const inWindow = validRange ? validEvents.filter((event) => event.start <= end && (event.end ?? event.start) >= start).sort((a, b) => a.start - b.start) : [];
150
+ const byLane = /* @__PURE__ */ new Map();
151
+ for (const event of inWindow) {
152
+ const lane = laneOf(event);
153
+ const list = byLane.get(lane);
154
+ if (list) list.push(event);
155
+ else byLane.set(lane, [event]);
156
+ }
157
+ return { visible: inWindow, omitted: events.length - validEvents.length, lanes: byLane };
158
+ }, [events, start, end, validRange]);
159
+ const selectionRef = useRef(onSelectionChange);
160
+ useIsomorphicLayoutEffect(() => {
161
+ selectionRef.current = onSelectionChange;
162
+ });
163
+ const select = useCallback((eventId) => selectionRef.current?.(eventId), []);
164
+ const extent = plotWidth - 16;
165
+ const position = (time) => 8 + Math.max(0, Math.min(extent, (time - start) / (end - start) * extent));
166
+ return /* @__PURE__ */ jsxs(
167
+ "div",
168
+ {
169
+ ...props,
170
+ ref: setRef,
171
+ role: "group",
172
+ "aria-label": label,
173
+ className: clsx(timelineStyles(), className),
174
+ children: [
175
+ !validRange ? /* @__PURE__ */ jsx("p", { role: "status", className: "m-0 text-oprt-base text-oprt-text-dim", children: "Choose a valid time range with start before end." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
176
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-between gap-1u font-mono text-oprt-base text-oprt-text-dim", children: [
177
+ /* @__PURE__ */ jsx("time", { dateTime: new Date(start).toISOString(), children: utc(start) }),
178
+ /* @__PURE__ */ jsx("time", { dateTime: new Date(end).toISOString(), children: utc(end) })
179
+ ] }),
180
+ visible.length === 0 ? /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-base text-oprt-text-dim", children: emptyMessage }) : /* @__PURE__ */ jsxs(Fragment, { children: [
181
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "flex flex-col gap-1u", children: [...lanes].map(([lane, laneEvents]) => /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
182
+ /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-base font-medium uppercase tracking-oprt-caps oprt-wrap-anywhere", children: lane }),
183
+ /* @__PURE__ */ jsxs(
184
+ "svg",
185
+ {
186
+ viewBox: `0 0 ${plotWidth} 48`,
187
+ preserveAspectRatio: "none",
188
+ className: "block h-6u w-full overflow-hidden",
189
+ focusable: "false",
190
+ children: [
191
+ /* @__PURE__ */ jsx(
192
+ "line",
193
+ {
194
+ x1: "8",
195
+ x2: plotWidth - 8,
196
+ y1: "24",
197
+ y2: "24",
198
+ className: "stroke-oprt-border",
199
+ vectorEffect: "non-scaling-stroke"
200
+ }
201
+ ),
202
+ laneEvents.map((event) => {
203
+ const x = position(event.start);
204
+ const duration = event.end !== void 0 && event.end > event.start;
205
+ return /* @__PURE__ */ jsx(
206
+ Marker,
207
+ {
208
+ id: event.id,
209
+ severity: event.severity,
210
+ x,
211
+ width: duration ? Math.max(2, position(event.end) - x) : null,
212
+ selected: selectedId === event.id
213
+ },
214
+ event.id
215
+ );
216
+ })
217
+ ]
218
+ }
219
+ )
220
+ ] }, lane)) }),
221
+ /* @__PURE__ */ jsx("ol", { "aria-label": `${label} list`, className: "m-0 flex list-none flex-col gap-1u p-0", children: visible.map((event, index) => /* @__PURE__ */ jsx(
222
+ TimelineRow,
223
+ {
224
+ event,
225
+ selected: selectedId === event.id,
226
+ selectable: !!onSelectionChange,
227
+ disabled,
228
+ detailsId: `${id}-details-${index}`,
229
+ onSelect: select
230
+ },
231
+ event.id
232
+ )) })
233
+ ] })
234
+ ] }),
235
+ omitted > 0 && /* @__PURE__ */ jsxs("p", { className: "m-0 text-oprt-base text-oprt-text-dim", children: [
236
+ omitted,
237
+ " ",
238
+ omitted === 1 ? "event omitted" : "events omitted",
239
+ " because timestamps are invalid."
240
+ ] })
241
+ ]
242
+ }
243
+ );
244
+ }
245
+ );
246
+ EventTimeline.displayName = "EventTimeline";
247
+ export {
248
+ EventTimeline
249
+ };
@@ -0,0 +1,57 @@
1
+ // src/EventTimeline/EventTimeline.manifest.ts
2
+ var EventTimelineManifest = {
3
+ EventTimeline: {
4
+ description: "UTC event graphic with lanes, clipped duration bars, point markers, and a complete accessible event list. Event titles lead each entry, severity aligns at the trailing edge, and timestamps/categories form a secondary metadata row.",
5
+ props: {
6
+ events: {
7
+ type: "array",
8
+ required: true,
9
+ description: "Readonly TimelineEvent array. Invalid timestamps and reversed intervals are omitted with an explicit count. The visible list is sorted by start time.",
10
+ items: {
11
+ id: "string (required, unique)",
12
+ label: "string (required)",
13
+ start: "number (required, epoch milliseconds)",
14
+ end: "number (optional, epoch milliseconds, inclusive)",
15
+ lane: "string (optional, defaults to Events)",
16
+ description: "string (optional)",
17
+ severity: "enum [neutral, info, success, warning, error] (default neutral)"
18
+ }
19
+ },
20
+ start: {
21
+ type: "number",
22
+ required: true,
23
+ description: "Inclusive visible window start, Unix epoch milliseconds."
24
+ },
25
+ end: {
26
+ type: "number",
27
+ required: true,
28
+ description: "Inclusive visible window end, Unix epoch milliseconds. Must follow start."
29
+ },
30
+ selectedId: {
31
+ type: "string",
32
+ description: "Controlled selected event ID, or null for no selection. Filtering the visible window never changes the supplied selection."
33
+ },
34
+ onSelectionChange: {
35
+ type: "function",
36
+ description: "(id: string) => void. Adds native selection buttons to the event list; Enter and Space select an event."
37
+ },
38
+ disabled: {
39
+ type: "boolean",
40
+ default: false,
41
+ description: "Disables selection buttons while keeping data readable."
42
+ },
43
+ label: {
44
+ type: "string",
45
+ default: "Event timeline",
46
+ description: 'Accessible name of the group. The event list is named after it with "list" added (label="Service events" names the list "Service events list").'
47
+ },
48
+ emptyMessage: { type: "string", default: "No events in this time range." }
49
+ },
50
+ composable_with: ["TimeRange", "SplitPane", "Card"],
51
+ never_inside: ["Button"],
52
+ example: '<EventTimeline events={events} start={range.start} end={range.end} selectedId={selectedId} onSelectionChange={setSelectedId} label="Service events" />'
53
+ }
54
+ };
55
+ export {
56
+ EventTimelineManifest
57
+ };
@@ -0,0 +1,7 @@
1
+ // src/EventTimeline/index.ts
2
+ import { EventTimeline } from "./EventTimeline.js";
3
+ import { EventTimelineManifest } from "./EventTimeline.manifest.js";
4
+ export {
5
+ EventTimeline,
6
+ EventTimelineManifest
7
+ };
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ // src/Field/Field.tsx
4
+ import { forwardRef, useContext, useRef } from "react";
5
+ import clsx from "clsx";
6
+ import { FieldContext, usePartIds } from "../_internal/fieldIds.js";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ function useFieldContext() {
9
+ const ctx = useContext(FieldContext);
10
+ if (!ctx) {
11
+ throw new Error("useFieldContext must be used inside a <Field>.");
12
+ }
13
+ return ctx;
14
+ }
15
+ function useOptionalFieldContext() {
16
+ return useContext(FieldContext);
17
+ }
18
+ var Field = forwardRef(
19
+ ({ controlId, label, help, error, required, children, className, ...props }, ref) => {
20
+ const labelRef = useRef(null);
21
+ const ids = usePartIds(["label", "help", "error"], {
22
+ id: controlId,
23
+ readRendered: () => labelRef.current?.htmlFor
24
+ });
25
+ const id = ids.base;
26
+ const describedBy = error ? ids.error : help ? ids.help : void 0;
27
+ const ctx = {
28
+ id,
29
+ labelId: ids.label,
30
+ describedBy,
31
+ invalid: Boolean(error),
32
+ required: Boolean(required)
33
+ };
34
+ return /* @__PURE__ */ jsx(FieldContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
35
+ "div",
36
+ {
37
+ ref,
38
+ className: clsx("flex min-w-0 flex-col gap-1u font-sans normal-case", className),
39
+ ...props,
40
+ children: [
41
+ /* @__PURE__ */ jsxs(
42
+ "label",
43
+ {
44
+ ref: labelRef,
45
+ id: ids.label,
46
+ htmlFor: id,
47
+ className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-base font-medium text-oprt-text",
48
+ children: [
49
+ label,
50
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: " *" })
51
+ ]
52
+ }
53
+ ),
54
+ children,
55
+ error ? /* @__PURE__ */ jsx(
56
+ "p",
57
+ {
58
+ id: ids.error,
59
+ className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-base text-oprt-error",
60
+ role: "alert",
61
+ children: error
62
+ }
63
+ ) : help ? /* @__PURE__ */ jsx(
64
+ "p",
65
+ {
66
+ id: ids.help,
67
+ className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-base text-oprt-text-dim",
68
+ children: help
69
+ }
70
+ ) : null
71
+ ]
72
+ }
73
+ ) });
74
+ }
75
+ );
76
+ Field.displayName = "Field";
77
+ export {
78
+ Field,
79
+ useFieldContext,
80
+ useOptionalFieldContext
81
+ };
@@ -0,0 +1,32 @@
1
+ // src/Field/Field.manifest.ts
2
+ var FieldManifest = {
3
+ Field: {
4
+ description: "Form field wrapper providing label, help text, and error display. Labels, help, errors, and entered values preserve their original casing. A required Field makes its control required. NumberInput, CoordinateInput and MultiSelect carry their own label and are not placed in a Field.",
5
+ props: {
6
+ controlId: {
7
+ type: "string",
8
+ description: "Stable child control ID used by the label and context. Native id remains the wrapper ID; generated control IDs are the default."
9
+ },
10
+ label: { type: "string", required: true },
11
+ help: { type: "string" },
12
+ error: { type: "string" },
13
+ required: {
14
+ type: "boolean",
15
+ default: false,
16
+ description: "Marks the label and makes the control inside required: announced as required, and the browser stops a native submission while it is empty. A form that validates itself sets noValidate on the form, which keeps the announcement. A control's own required={false} wins."
17
+ }
18
+ },
19
+ slots: {
20
+ children: {
21
+ type: "Input | Textarea | Select | ComboBox | Checkbox | RadioGroup | Toggle | Slider",
22
+ required: true
23
+ }
24
+ },
25
+ composable_with: ["Card", "FormSection", "FormErrorSummary"],
26
+ never_inside: ["Field", "Button"],
27
+ example: '<Field label="ENDPOINT" error="Required">\n <Input placeholder="api.system.io" />\n</Field>'
28
+ }
29
+ };
30
+ export {
31
+ FieldManifest
32
+ };
@@ -0,0 +1,9 @@
1
+ // src/Field/index.ts
2
+ import { Field, useFieldContext, useOptionalFieldContext } from "./Field.js";
3
+ import { FieldManifest } from "./Field.manifest.js";
4
+ export {
5
+ Field,
6
+ FieldManifest,
7
+ useFieldContext,
8
+ useOptionalFieldContext
9
+ };
@@ -0,0 +1,215 @@
1
+ "use client";
2
+
3
+ // src/FileUpload/FileUpload.tsx
4
+ import {
5
+ forwardRef,
6
+ useEffect,
7
+ useRef,
8
+ useState
9
+ } from "react";
10
+ import { Button as AriaButton, DropZone, FileTrigger } from "react-aria-components";
11
+ import { cva } from "class-variance-authority";
12
+ import clsx from "clsx";
13
+ import { Button } from "../Button/Button.js";
14
+ import { NUMBER_UNIT_SPACE } from "../_internal/bytes.js";
15
+ import { joinIds, usePartIds } from "../_internal/fieldIds.js";
16
+ import { useMergedRefs } from "../_internal/mergeRefs.js";
17
+ import { useIsMounted } from "../_internal/useIsMounted.js";
18
+ import { jsx, jsxs } from "react/jsx-runtime";
19
+ var uploadStyles = cva(
20
+ "flex min-w-0 flex-col gap-2u font-sans text-oprt-base normal-case text-oprt-text"
21
+ );
22
+ var dropStyles = cva(
23
+ "flex min-w-0 flex-col items-start gap-2u border border-dashed border-oprt-border bg-oprt-surface-1 p-3u data-[drop-target]:border-oprt-accent data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus"
24
+ );
25
+ var validAccept = (token) => /^\.[a-z0-9][a-z0-9._+-]*$/i.test(token) || /^(?:[a-z0-9!#$&^_.+-]+\/(?:[a-z0-9!#$&^_.+-]+|\*)|\*\/\*)$/i.test(token);
26
+ var FileUpload = forwardRef(
27
+ ({
28
+ label,
29
+ description,
30
+ accept,
31
+ multiple = false,
32
+ maxFiles,
33
+ maxFileSizeBytes,
34
+ disabled = false,
35
+ onFiles,
36
+ onReject,
37
+ className,
38
+ "aria-labelledby": labelledBy,
39
+ "aria-describedby": describedBy,
40
+ ...props
41
+ }, ref) => {
42
+ const root = useRef(null);
43
+ const mergedRef = useMergedRefs(root, ref);
44
+ const ids = usePartIds(["label", "description", "configuration"], {
45
+ id: props.id,
46
+ readRendered: () => root.current?.getAttribute("data-oprt-ids")
47
+ });
48
+ const [issues, setIssues] = useState([]);
49
+ const [reading, setReading] = useState(false);
50
+ const [feedback, setFeedback] = useState("");
51
+ const isMounted = useIsMounted();
52
+ const generation = useRef(0);
53
+ const types = accept?.map((token) => token.trim().toLowerCase()) ?? [];
54
+ const configurationError = accept?.some((token) => !validAccept(token.trim())) ? "Invalid accept configuration. Use file extensions or MIME types." : maxFiles !== void 0 && (!Number.isSafeInteger(maxFiles) || maxFiles < 1) ? "Invalid maxFiles configuration. Supply a positive integer." : maxFileSizeBytes !== void 0 && (!Number.isSafeInteger(maxFileSizeBytes) || maxFileSizeBytes < 0) ? "Invalid maxFileSizeBytes configuration. Supply a nonnegative integer." : "";
55
+ const signature = JSON.stringify([types, multiple, String(maxFiles), String(maxFileSizeBytes)]);
56
+ const latest = useRef({ disabled, configurationError, signature, onFiles, onReject });
57
+ latest.current = { disabled, configurationError, signature, onFiles, onReject };
58
+ useEffect(() => {
59
+ const requests = generation;
60
+ return () => {
61
+ requests.current++;
62
+ };
63
+ }, []);
64
+ useEffect(() => {
65
+ generation.current++;
66
+ setReading(false);
67
+ }, [signature, disabled]);
68
+ async function select(candidates) {
69
+ if (!candidates.length || latest.current.disabled || latest.current.configurationError)
70
+ return;
71
+ const request = ++generation.current;
72
+ const constraints = signature;
73
+ setIssues([]);
74
+ setFeedback("");
75
+ setReading(true);
76
+ const results = await Promise.allSettled(
77
+ candidates.map(
78
+ async (candidate) => "issue" in candidate ? candidate : { file: await candidate.read() }
79
+ )
80
+ );
81
+ if (!isMounted() || generation.current !== request || latest.current.disabled || latest.current.configurationError || latest.current.signature !== constraints)
82
+ return;
83
+ const accepted = [];
84
+ const rejected = [];
85
+ const countLimit = multiple ? maxFiles ?? Infinity : 1;
86
+ let fileCount = 0;
87
+ results.forEach((result, index) => {
88
+ const candidate = candidates[index];
89
+ if (result.status === "rejected") {
90
+ rejected.push({
91
+ name: "name" in candidate ? candidate.name : "File",
92
+ code: "read",
93
+ message: "File could not be read."
94
+ });
95
+ return;
96
+ }
97
+ if ("issue" in result.value) {
98
+ rejected.push(result.value.issue);
99
+ return;
100
+ }
101
+ const value = result.value.file;
102
+ fileCount++;
103
+ if (fileCount > countLimit)
104
+ rejected.push({
105
+ name: value.name,
106
+ code: "count",
107
+ message: `Choose at most ${countLimit} file${countLimit === 1 ? "" : "s"} per selection.`
108
+ });
109
+ else if (types.length && !types.some(
110
+ (type) => type === "*/*" || (type.startsWith(".") ? value.name.toLowerCase().endsWith(type) : type.endsWith("/*") ? value.type.toLowerCase().startsWith(type.slice(0, -1)) : value.type.toLowerCase() === type)
111
+ ))
112
+ rejected.push({ name: value.name, code: "type", message: "File type is not accepted." });
113
+ else if (maxFileSizeBytes !== void 0 && value.size > maxFileSizeBytes)
114
+ rejected.push({
115
+ name: value.name,
116
+ code: "size",
117
+ // The limit and its unit stay on one line (R4, design call DC-11).
118
+ message: `File exceeds ${maxFileSizeBytes}${NUMBER_UNIT_SPACE}bytes.`
119
+ });
120
+ else accepted.push(value);
121
+ });
122
+ setReading(false);
123
+ setIssues(rejected);
124
+ setFeedback(
125
+ `${accepted.length} file${accepted.length === 1 ? "" : "s"} selected locally${rejected.length ? `; ${rejected.length} rejected` : ""}.`
126
+ );
127
+ if (accepted.length) latest.current.onFiles(Object.freeze(accepted));
128
+ if (rejected.length) latest.current.onReject?.(Object.freeze(rejected));
129
+ }
130
+ function dropped(items) {
131
+ return select(
132
+ items.map(
133
+ (item) => item.kind === "file" ? { name: item.name, read: () => item.getFile() } : {
134
+ issue: {
135
+ name: item.kind === "directory" ? item.name : "Dropped item",
136
+ code: item.kind === "directory" ? "directory" : "unsupported",
137
+ message: item.kind === "directory" ? "Directories are not supported. Choose individual files." : "Only files are supported."
138
+ }
139
+ }
140
+ )
141
+ );
142
+ }
143
+ const blocked = disabled || !!configurationError;
144
+ const hasDescription = description != null && typeof description !== "boolean";
145
+ return /* @__PURE__ */ jsxs(
146
+ "div",
147
+ {
148
+ ...props,
149
+ ref: mergedRef,
150
+ role: "group",
151
+ "data-oprt-ids": ids.base,
152
+ "aria-labelledby": joinIds(ids.label, labelledBy),
153
+ "aria-describedby": joinIds(
154
+ hasDescription && ids.description,
155
+ configurationError && ids.configuration,
156
+ describedBy
157
+ ),
158
+ className: clsx(uploadStyles(), className),
159
+ children: [
160
+ /* @__PURE__ */ jsx("p", { id: ids.label, className: "m-0 oprt-wrap-anywhere font-medium uppercase tracking-oprt-sm", children: label }),
161
+ hasDescription && /* @__PURE__ */ jsx(
162
+ "div",
163
+ {
164
+ id: ids.description,
165
+ className: "min-w-0 oprt-wrap-anywhere text-oprt-text-dim [&_p]:m-0",
166
+ children: description
167
+ }
168
+ ),
169
+ /* @__PURE__ */ jsxs(
170
+ DropZone,
171
+ {
172
+ "aria-label": `${label} drop area`,
173
+ isDisabled: blocked,
174
+ className: dropStyles(),
175
+ onDrop: (event) => {
176
+ void dropped(event.items);
177
+ },
178
+ getDropOperation: (_types, operations) => operations.includes("copy") ? "copy" : "cancel",
179
+ children: [
180
+ /* @__PURE__ */ jsx("p", { className: clsx("m-0", blocked && "text-oprt-text-dim"), children: "Drop files here or choose files." }),
181
+ /* @__PURE__ */ jsx(
182
+ FileTrigger,
183
+ {
184
+ allowsMultiple: multiple,
185
+ ...accept ? { acceptedFileTypes: accept } : {},
186
+ onSelect: (files) => {
187
+ void select(
188
+ Array.from(files ?? []).map((file) => ({
189
+ name: file.name,
190
+ read: async () => file
191
+ }))
192
+ );
193
+ },
194
+ children: /* @__PURE__ */ jsx(Button, { asChild: true, variant: "secondary", children: /* @__PURE__ */ jsx(AriaButton, { isDisabled: blocked, children: "Choose files" }) })
195
+ }
196
+ )
197
+ ]
198
+ }
199
+ ),
200
+ configurationError && /* @__PURE__ */ jsx("p", { id: ids.configuration, role: "alert", className: "m-0 text-oprt-error", children: configurationError }),
201
+ issues.length > 0 && /* @__PURE__ */ jsx("ul", { className: "m-0 flex min-w-0 list-disc flex-col gap-1u pl-3u text-oprt-error", children: issues.map((issue, index) => /* @__PURE__ */ jsxs("li", { className: "oprt-wrap-anywhere", children: [
202
+ issue.name,
203
+ ": ",
204
+ issue.message
205
+ ] }, index)) }),
206
+ /* @__PURE__ */ jsx("p", { role: "status", "aria-atomic": "true", className: "m-0 text-oprt-text-dim", children: reading ? "Reading selected files\u2026" : feedback })
207
+ ]
208
+ }
209
+ );
210
+ }
211
+ );
212
+ FileUpload.displayName = "FileUpload";
213
+ export {
214
+ FileUpload
215
+ };