@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,145 @@
1
+ "use client";
2
+
3
+ // src/BulkActionBar/BulkActionBar.tsx
4
+ import { forwardRef, useId } from "react";
5
+ import { cva } from "class-variance-authority";
6
+ import clsx from "clsx";
7
+ import { Button } from "../Button/Button.js";
8
+ import { DescriptionList } from "../DescriptionList/DescriptionList.js";
9
+ import { formatUtcTimestamp } from "../_internal/timestamp.js";
10
+ import { useActionFocus } from "../_internal/useActionFocus.js";
11
+ import { copySelection, idsError, scopeLabels, selectionError } from "./selection.js";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ var barStyles = cva(
14
+ "flex min-w-0 flex-col gap-2u border border-oprt-border bg-oprt-surface-1 p-2u font-sans text-oprt-base text-oprt-text outline-hidden focus-visible:shadow-oprt-focus"
15
+ );
16
+ var BulkActionBar = forwardRef(
17
+ ({
18
+ label,
19
+ selection,
20
+ visibleIds,
21
+ actions,
22
+ onAction,
23
+ onClearSelection,
24
+ disabled = false,
25
+ headingLevel = 3,
26
+ className
27
+ }, ref) => {
28
+ const id = useId();
29
+ const focus = useActionFocus(ref);
30
+ const Heading = `h${headingLevel}`;
31
+ const invalidSelection = selectionError(selection);
32
+ const invalidVisible = idsError(visibleIds);
33
+ const invalidActions = idsError(actions.map((action) => action.id)) || (actions.some((action) => typeof action.label !== "string" || !action.label.trim()) ? "Action labels are required." : null);
34
+ const error = invalidSelection ? `Invalid selection: ${invalidSelection}` : invalidVisible ? `Invalid visible IDs: ${invalidVisible}` : invalidActions ? `Invalid actions: ${invalidActions}` : null;
35
+ const visible = new Set(visibleIds);
36
+ const outside = invalidSelection || invalidVisible ? 0 : selection.ids.filter((item) => !visible.has(item)).length;
37
+ return /* @__PURE__ */ jsxs(
38
+ "div",
39
+ {
40
+ ...focus,
41
+ role: "region",
42
+ "aria-label": label,
43
+ tabIndex: -1,
44
+ className: clsx(barStyles(), className),
45
+ children: [
46
+ /* @__PURE__ */ jsx(Heading, { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-base font-medium uppercase tracking-oprt-sm", children: label }),
47
+ error && /* @__PURE__ */ jsx("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-error", children: error }),
48
+ !invalidSelection && /* @__PURE__ */ jsxs(Fragment, { children: [
49
+ /* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere", children: [
50
+ selection.ids.length,
51
+ " equipment \xB7 ",
52
+ scopeLabels[selection.scope]
53
+ ] }),
54
+ !invalidVisible && /* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-text-dim", children: [
55
+ outside,
56
+ " selected ",
57
+ outside === 1 ? "ID" : "IDs",
58
+ " outside the visible page."
59
+ ] }),
60
+ /* @__PURE__ */ jsx(
61
+ DescriptionList,
62
+ {
63
+ layout: "details",
64
+ items: [
65
+ {
66
+ id: "source",
67
+ label: "Source",
68
+ value: `${selection.sourceId} \xB7 ${selection.sourceRevision}`,
69
+ mono: true
70
+ },
71
+ {
72
+ id: "filters",
73
+ label: "Filters",
74
+ value: selection.filterDescription || "No filters"
75
+ },
76
+ {
77
+ // DC-4: the capture time in UTC to the second; the exact instant stays in dateTime.
78
+ id: "captured",
79
+ label: "Captured",
80
+ value: /* @__PURE__ */ jsx("time", { dateTime: new Date(selection.capturedAt).toISOString(), children: formatUtcTimestamp(selection.capturedAt) }),
81
+ mono: true
82
+ }
83
+ ]
84
+ }
85
+ )
86
+ ] }),
87
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-2u", children: [
88
+ actions.map((action, index) => {
89
+ const reason = action.disabled && action.disabledReason ? action.disabledReason : null;
90
+ return /* @__PURE__ */ jsxs(
91
+ "div",
92
+ {
93
+ className: "flex min-w-0 flex-col gap-1u",
94
+ children: [
95
+ /* @__PURE__ */ jsx(
96
+ Button,
97
+ {
98
+ className: "max-w-full oprt-wrap-anywhere",
99
+ size: "sm",
100
+ variant: action.danger ? "danger" : "secondary",
101
+ disabled: disabled || !!error || selection.ids.length === 0 || action.disabled,
102
+ "aria-describedby": reason ? `${id}-reason-${index}` : void 0,
103
+ onClick: () => {
104
+ if (!disabled && !error && selection.ids.length > 0 && !action.disabled)
105
+ onAction(action.id, copySelection(selection));
106
+ },
107
+ children: action.label
108
+ }
109
+ ),
110
+ reason && /* @__PURE__ */ jsx(
111
+ "p",
112
+ {
113
+ id: `${id}-reason-${index}`,
114
+ className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-text-dim",
115
+ children: reason
116
+ }
117
+ )
118
+ ]
119
+ },
120
+ invalidActions ? index : action.id
121
+ );
122
+ }),
123
+ onClearSelection && /* @__PURE__ */ jsx(
124
+ Button,
125
+ {
126
+ className: "max-w-full oprt-wrap-anywhere",
127
+ size: "sm",
128
+ variant: "tertiary",
129
+ disabled,
130
+ onClick: () => {
131
+ if (!disabled) onClearSelection();
132
+ },
133
+ children: "Clear selection"
134
+ }
135
+ )
136
+ ] })
137
+ ]
138
+ }
139
+ );
140
+ }
141
+ );
142
+ BulkActionBar.displayName = "BulkActionBar";
143
+ export {
144
+ BulkActionBar
145
+ };
@@ -0,0 +1,46 @@
1
+ // src/BulkActionBar/BulkActionBar.manifest.ts
2
+ var BulkActionBarManifest = {
3
+ BulkActionBar: {
4
+ description: "Displays an explicit equipment selection snapshot, off-page count, source and filter context, with controlled bulk action requests.",
5
+ props: {
6
+ label: { type: "string", required: true },
7
+ selection: {
8
+ type: "node",
9
+ required: true,
10
+ description: "BulkSelectionSnapshot: {id,scope:'visible'|'selected'|'all-matching',ids:readonly string[],sourceId,sourceRevision,filterDescription,capturedAt:number}. IDs must be unique and nonempty. The application resolves all matching IDs before supplying them."
11
+ },
12
+ visibleIds: {
13
+ type: "array",
14
+ required: true,
15
+ description: "Unique current visible-page IDs, used to explain retained selection outside that page."
16
+ },
17
+ actions: {
18
+ type: "array",
19
+ required: true,
20
+ description: "BulkAction[]: unique id, label, optional disabled, disabledReason and danger. A disabledReason shows under its action, as the action's description, only while the action is disabled."
21
+ },
22
+ onAction: {
23
+ type: "function",
24
+ required: true,
25
+ description: "(actionId, snapshot) => void. Receives an independent frozen copy, including its frozen IDs. Confirmation and authority checks belong to the application."
26
+ },
27
+ onClearSelection: {
28
+ type: "function",
29
+ description: "Requests clearing selection without changing it locally. Available to recover from malformed snapshots."
30
+ },
31
+ disabled: { type: "boolean", default: false },
32
+ headingLevel: {
33
+ type: "number",
34
+ default: 3,
35
+ description: "Label heading level: 2, 3, 4, 5, or 6. Choose it for the hosting surface's heading hierarchy."
36
+ },
37
+ className: { type: "string" }
38
+ },
39
+ composable_with: ["DataGrid", "ConfirmDialog", "BulkResults", "JobStatus", "DescriptionList"],
40
+ never_inside: ["Button"],
41
+ example: '<BulkActionBar label="Equipment actions" selection={snapshot} visibleIds={pageIds} actions={[{id:"flag",label:"Flag for inspection"}]} onAction={reviewAction} />'
42
+ }
43
+ };
44
+ export {
45
+ BulkActionBarManifest
46
+ };
@@ -0,0 +1,9 @@
1
+ // src/BulkActionBar/index.ts
2
+ import {
3
+ BulkActionBar
4
+ } from "./BulkActionBar.js";
5
+ import { BulkActionBarManifest } from "./BulkActionBar.manifest.js";
6
+ export {
7
+ BulkActionBar,
8
+ BulkActionBarManifest
9
+ };
@@ -0,0 +1,44 @@
1
+ // src/BulkActionBar/selection.ts
2
+ import { isValidTimestamp } from "../_internal/timestamp.js";
3
+ function idsError(ids) {
4
+ if (!Array.isArray(ids) || ids.some((id) => typeof id !== "string" || !id.trim()))
5
+ return "IDs must be nonempty strings.";
6
+ if (new Set(ids).size !== ids.length) return "IDs must be unique.";
7
+ return null;
8
+ }
9
+ function selectionError(selection) {
10
+ if (!selection || !["visible", "selected", "all-matching"].includes(selection.scope))
11
+ return "Invalid selection scope.";
12
+ const invalidIds = idsError(selection.ids);
13
+ if (invalidIds) return invalidIds;
14
+ if ([selection.id, selection.sourceId, selection.sourceRevision].some(
15
+ (value) => typeof value !== "string" || !value.trim()
16
+ ))
17
+ return "A snapshot ID, source ID, and source revision are required.";
18
+ if (typeof selection.filterDescription !== "string") return "A filter description is required.";
19
+ if (!isValidTimestamp(selection.capturedAt, { integer: true }))
20
+ return "Snapshot capture time must be valid epoch milliseconds.";
21
+ return null;
22
+ }
23
+ var scopeLabels = {
24
+ visible: "Visible rows",
25
+ selected: "Selected IDs",
26
+ "all-matching": "All matching results"
27
+ };
28
+ function copySelection(selection) {
29
+ return Object.freeze({
30
+ id: selection.id,
31
+ scope: selection.scope,
32
+ ids: Object.freeze([...selection.ids]),
33
+ sourceId: selection.sourceId,
34
+ sourceRevision: selection.sourceRevision,
35
+ filterDescription: selection.filterDescription,
36
+ capturedAt: selection.capturedAt
37
+ });
38
+ }
39
+ export {
40
+ copySelection,
41
+ idsError,
42
+ scopeLabels,
43
+ selectionError
44
+ };
@@ -0,0 +1,269 @@
1
+ "use client";
2
+
3
+ // src/BulkResults/BulkResults.tsx
4
+ import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
5
+ import { cva } from "class-variance-authority";
6
+ import clsx from "clsx";
7
+ import { Badge } from "../Badge/Badge.js";
8
+ import { Button } from "../Button/Button.js";
9
+ import { DescriptionList } from "../DescriptionList/DescriptionList.js";
10
+ import { idsError, scopeLabels, selectionError } from "../BulkActionBar/selection.js";
11
+ import { PageControls } from "../DataGrid/PageControls.js";
12
+ import { useScrollsSideways } from "../DataTable/useScrollsSideways.js";
13
+ import { useIsomorphicLayoutEffect } from "../_internal/useIsomorphicLayoutEffect.js";
14
+ import { useRestoreFocusOnRemoval } from "../_internal/useRestoreFocusOnRemoval.js";
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
+ var resultStyles = cva(
17
+ "flex min-w-0 flex-col gap-2u font-sans text-oprt-base text-oprt-text outline-hidden focus-visible:shadow-oprt-focus"
18
+ );
19
+ var variants = {
20
+ queued: "info",
21
+ running: "info",
22
+ succeeded: "success",
23
+ failed: "error",
24
+ canceled: "neutral",
25
+ skipped: "neutral"
26
+ };
27
+ var NO_IDS = [];
28
+ var DEFAULT_PAGE_SIZE = 100;
29
+ var COLUMNS = [
30
+ { heading: "Equipment", width: "min-w-16u" },
31
+ { heading: "Outcome", width: "min-w-12u" },
32
+ { heading: "Details", width: "min-w-16u" },
33
+ { heading: "Actions", width: "" }
34
+ ];
35
+ var BulkResults = forwardRef(
36
+ ({
37
+ label,
38
+ selection,
39
+ items,
40
+ pendingRetryIds = NO_IDS,
41
+ onRetryRequest,
42
+ disabled = false,
43
+ pageSize = DEFAULT_PAGE_SIZE,
44
+ headingLevel = 3,
45
+ className
46
+ }, ref) => {
47
+ const Heading = `h${headingLevel}`;
48
+ const root = useRef(null);
49
+ useImperativeHandle(ref, () => root.current, []);
50
+ const [box, scrolls] = useScrollsSideways();
51
+ const rows = useRef(/* @__PURE__ */ new Map());
52
+ const invalidSelection = selectionError(selection);
53
+ const selected = new Set(invalidSelection ? [] : selection.ids);
54
+ const invalidResults = idsError(items.map((item) => item.id)) || (items.some((item) => !selected.has(item.id)) ? "A result ID is outside the selection snapshot." : null) || (items.some(
55
+ (item) => !Object.hasOwn(variants, item.state) || typeof item.label !== "string" || !item.label.trim() || item.message !== void 0 && typeof item.message !== "string"
56
+ ) ? "Every result needs a valid state, label, and message." : null);
57
+ const invalidRetries = idsError(pendingRetryIds) || (pendingRetryIds.some((item) => !selected.has(item)) ? "A pending retry ID is outside the selection snapshot." : null);
58
+ const resultMap = new Map((invalidResults ? [] : items).map((item) => [item.id, item]));
59
+ const pending = useMemo(() => new Set(pendingRetryIds), [pendingRetryIds]);
60
+ const missing = invalidSelection ? 0 : selection.ids.filter((item) => !resultMap.has(item)).length;
61
+ const succeeded = invalidResults ? 0 : items.filter((item) => item.state === "succeeded").length;
62
+ const ids = invalidSelection ? NO_IDS : selection.ids;
63
+ const size = Number.isFinite(pageSize) && pageSize >= 1 ? Math.floor(pageSize) : DEFAULT_PAGE_SIZE;
64
+ const pageCount = Math.max(1, Math.ceil(ids.length / size));
65
+ const [paging, setPaging] = useState({ snapshot: selection.id, page: 1 });
66
+ const requestedPage = paging.snapshot === selection.id ? paging.page : 1;
67
+ const page = Math.min(pageCount, Math.max(1, requestedPage));
68
+ const pageIds = ids.length > size ? ids.slice((page - 1) * size, page * size) : ids;
69
+ const restoreFocus = useRestoreFocusOnRemoval(root, {
70
+ track: (element) => {
71
+ for (const [itemId, row] of rows.current)
72
+ if (row.contains(element)) return { itemId, index: pageIds.indexOf(itemId) };
73
+ return { itemId: null, index: -1 };
74
+ },
75
+ findTarget: ({ itemId, index }) => {
76
+ const own = itemId === null ? void 0 : rows.current.get(itemId);
77
+ if (own) return own.querySelector("th");
78
+ const nextId = index >= 0 ? pageIds[Math.min(index, pageIds.length - 1)] : void 0;
79
+ const next = nextId === void 0 ? void 0 : rows.current.get(nextId);
80
+ return next?.querySelector("button:not(:disabled)") ?? next?.querySelector("th") ?? root.current;
81
+ }
82
+ });
83
+ const [spoken, setSpoken] = useState("");
84
+ const announced = useRef(new Set(invalidRetries ? [] : pendingRetryIds));
85
+ const latest = useRef({ results: resultMap, pending: pendingRetryIds });
86
+ useIsomorphicLayoutEffect(() => {
87
+ latest.current = { results: resultMap, pending: pendingRetryIds };
88
+ });
89
+ const pendingKey = JSON.stringify(pendingRetryIds);
90
+ useEffect(() => {
91
+ const current = invalidRetries ? [] : latest.current.pending;
92
+ for (const itemId of [...announced.current])
93
+ if (!current.includes(itemId)) announced.current.delete(itemId);
94
+ const added = current.filter((itemId) => !announced.current.has(itemId));
95
+ const label2 = (itemId) => latest.current.results.get(itemId)?.label ?? itemId;
96
+ const text = added.length === 0 ? "" : added.length === 1 ? `Retry request pending for ${label2(added[0])}. Awaiting an application update.` : `Retry requests pending for ${added.length} items. Awaiting an application update.`;
97
+ let second = 0;
98
+ const first = requestAnimationFrame(() => {
99
+ setSpoken("");
100
+ second = requestAnimationFrame(() => {
101
+ setSpoken(text);
102
+ for (const itemId of added) announced.current.add(itemId);
103
+ });
104
+ });
105
+ return () => {
106
+ cancelAnimationFrame(first);
107
+ cancelAnimationFrame(second);
108
+ };
109
+ }, [pendingKey, invalidRetries]);
110
+ return /* @__PURE__ */ jsxs(
111
+ "div",
112
+ {
113
+ ref: root,
114
+ role: "region",
115
+ "aria-label": label,
116
+ tabIndex: -1,
117
+ className: clsx(resultStyles(), className),
118
+ onFocusCapture: restoreFocus.onFocusCapture,
119
+ onBlurCapture: restoreFocus.onBlurCapture,
120
+ children: [
121
+ /* @__PURE__ */ jsx(Heading, { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-base font-medium uppercase tracking-oprt-sm", children: label }),
122
+ /* @__PURE__ */ jsx("p", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only m-0", children: spoken }),
123
+ invalidSelection ? /* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-error", children: [
124
+ "Invalid selection: ",
125
+ invalidSelection
126
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
127
+ /* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere", children: [
128
+ scopeLabels[selection.scope],
129
+ " \xB7 ",
130
+ selection.ids.length,
131
+ " requested \xB7 ",
132
+ succeeded,
133
+ " ",
134
+ "succeeded \xB7 ",
135
+ missing,
136
+ " not reported"
137
+ ] }),
138
+ /* @__PURE__ */ jsx(
139
+ DescriptionList,
140
+ {
141
+ layout: "details",
142
+ items: [
143
+ {
144
+ id: "source",
145
+ label: "Source",
146
+ value: `${selection.sourceId} \xB7 ${selection.sourceRevision}`,
147
+ mono: true
148
+ },
149
+ {
150
+ id: "filters",
151
+ label: "Filters",
152
+ value: selection.filterDescription || "No filters"
153
+ }
154
+ ]
155
+ }
156
+ ),
157
+ invalidResults && /* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-error", children: [
158
+ "Invalid results: ",
159
+ invalidResults,
160
+ " Outcomes are withheld until corrected."
161
+ ] }),
162
+ invalidRetries && /* @__PURE__ */ jsxs("p", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-error", children: [
163
+ "Invalid retry requests: ",
164
+ invalidRetries
165
+ ] }),
166
+ selection.ids.length === 0 ? /* @__PURE__ */ jsx("p", { className: "m-0", children: "No equipment was requested." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
167
+ /* @__PURE__ */ jsx(
168
+ "div",
169
+ {
170
+ ref: box,
171
+ className: clsx(
172
+ "min-w-0 max-w-full overflow-x-auto",
173
+ scrolls && "border border-transparent focus-visible:outline-hidden focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus"
174
+ ),
175
+ ...scrolls ? { role: "region", "aria-label": `${label} scroll viewport`, tabIndex: 0 } : {},
176
+ children: /* @__PURE__ */ jsxs("table", { "aria-label": label, className: "m-0 w-full border-collapse", children: [
177
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: COLUMNS.map(({ heading, width }) => /* @__PURE__ */ jsx(
178
+ "th",
179
+ {
180
+ scope: "col",
181
+ className: clsx(
182
+ "border-b border-oprt-border px-1u py-2u text-left font-medium uppercase tracking-oprt-sm",
183
+ width
184
+ ),
185
+ children: heading
186
+ },
187
+ heading
188
+ )) }) }),
189
+ /* @__PURE__ */ jsx("tbody", { children: pageIds.map((itemId) => {
190
+ const item = resultMap.get(itemId);
191
+ const isPending = pending.has(itemId);
192
+ const retryable = !invalidResults && !invalidRetries && item?.state === "failed" && item.retryable === true && !!onRetryRequest;
193
+ return /* @__PURE__ */ jsxs(
194
+ "tr",
195
+ {
196
+ ref: (node) => {
197
+ if (node) rows.current.set(itemId, node);
198
+ else rows.current.delete(itemId);
199
+ },
200
+ className: "border-b border-oprt-border",
201
+ children: [
202
+ /* @__PURE__ */ jsxs(
203
+ "th",
204
+ {
205
+ scope: "row",
206
+ tabIndex: -1,
207
+ className: "min-w-0 px-1u py-2u text-left align-top font-medium oprt-wrap-anywhere outline-hidden focus-visible:shadow-oprt-focus",
208
+ children: [
209
+ item?.label ?? itemId,
210
+ item && /* @__PURE__ */ jsx("span", { className: "block oprt-wrap-anywhere font-mono text-oprt-text-dim", children: itemId })
211
+ ]
212
+ }
213
+ ),
214
+ /* @__PURE__ */ jsx("td", { className: "px-1u py-2u align-top", children: /* @__PURE__ */ jsx(
215
+ Badge,
216
+ {
217
+ appearance: "text",
218
+ indicator: "none",
219
+ variant: item ? variants[item.state] : "neutral",
220
+ children: item?.state ?? "Not reported"
221
+ }
222
+ ) }),
223
+ /* @__PURE__ */ jsxs("td", { className: "min-w-0 px-1u py-2u align-top oprt-wrap-anywhere", children: [
224
+ item?.message ?? "\u2014",
225
+ isPending && /* @__PURE__ */ jsx("p", { className: "m-0", children: "Retry requested; awaiting an application update." })
226
+ ] }),
227
+ /* @__PURE__ */ jsx("td", { className: "px-1u py-2u align-top", children: retryable && /* @__PURE__ */ jsx(
228
+ Button,
229
+ {
230
+ size: "sm",
231
+ variant: "secondary",
232
+ className: "max-w-full",
233
+ "aria-label": `Retry ${item.label}`,
234
+ disabled: disabled || isPending,
235
+ onClick: () => {
236
+ if (!disabled && !isPending && retryable)
237
+ onRetryRequest?.(itemId);
238
+ },
239
+ children: "Retry"
240
+ }
241
+ ) })
242
+ ]
243
+ },
244
+ itemId
245
+ );
246
+ }) })
247
+ ] })
248
+ }
249
+ ),
250
+ ids.length > size && /* @__PURE__ */ jsx(
251
+ PageControls,
252
+ {
253
+ page,
254
+ pageCount,
255
+ disabled: false,
256
+ onPageChange: (next) => setPaging({ snapshot: selection.id, page: next })
257
+ }
258
+ )
259
+ ] })
260
+ ] })
261
+ ]
262
+ }
263
+ );
264
+ }
265
+ );
266
+ BulkResults.displayName = "BulkResults";
267
+ export {
268
+ BulkResults
269
+ };
@@ -0,0 +1,45 @@
1
+ // src/BulkResults/BulkResults.manifest.ts
2
+ var BulkResultsManifest = {
3
+ BulkResults: {
4
+ description: "Named per-equipment result table in selection order. Missing IDs remain Not reported; malformed result coverage is visible and never counted as success.",
5
+ props: {
6
+ label: { type: "string", required: true },
7
+ selection: {
8
+ type: "node",
9
+ required: true,
10
+ description: "BulkSelectionSnapshot for the exact original request. IDs must be unique and nonempty."
11
+ },
12
+ items: {
13
+ type: "array",
14
+ required: true,
15
+ description: "BulkItemResult[]: {id,label,state:'queued'|'running'|'succeeded'|'failed'|'canceled'|'skipped',message?,retryable?}. Only snapshot IDs are accepted, once each."
16
+ },
17
+ pendingRetryIds: {
18
+ type: "array",
19
+ description: "Known unique IDs with a supplied pending retry request. Their actions remain disabled and previous results remain visible."
20
+ },
21
+ onRetryRequest: {
22
+ type: "function",
23
+ description: "(id) => void. Offered only for explicitly retryable failed items. Calling it does not change supplied outcomes."
24
+ },
25
+ disabled: { type: "boolean", default: false },
26
+ pageSize: {
27
+ type: "number",
28
+ default: 100,
29
+ description: "Rows per page. A request with more IDs than this is shown a page at a time, in selection order, with PREVIOUS PAGE and NEXT PAGE; the summary still counts every ID. Invalid or less-than-one values use 100; fractional values round down."
30
+ },
31
+ headingLevel: {
32
+ type: "number",
33
+ default: 3,
34
+ description: "Label heading level: 2, 3, 4, 5, or 6. Choose it for the hosting surface's heading hierarchy."
35
+ },
36
+ className: { type: "string" }
37
+ },
38
+ composable_with: ["JobStatus", "BulkActionBar", "Badge", "Button", "DescriptionList"],
39
+ never_inside: ["Button"],
40
+ example: '<BulkResults label="Inspection results" selection={snapshot} items={results} pendingRetryIds={pendingIds} onRetryRequest={reviewRetry} />'
41
+ }
42
+ };
43
+ export {
44
+ BulkResultsManifest
45
+ };
@@ -0,0 +1,7 @@
1
+ // src/BulkResults/index.ts
2
+ import { BulkResults } from "./BulkResults.js";
3
+ import { BulkResultsManifest } from "./BulkResults.manifest.js";
4
+ export {
5
+ BulkResults,
6
+ BulkResultsManifest
7
+ };