@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/AttachmentPreview/AttachmentPreview.tsx
4
+ import { forwardRef, useRef, useState } from "react";
5
+ import { cva } from "class-variance-authority";
6
+ import clsx from "clsx";
7
+ import { Button } from "../Button/Button.js";
8
+ import { ByteCountLabel } from "../_internal/bytes.js";
9
+ import { joinIds, usePartIds } from "../_internal/fieldIds.js";
10
+ import { useMergedRefs } from "../_internal/mergeRefs.js";
11
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
+ var previewStyles = cva(
13
+ "flex min-w-0 flex-col gap-2u border border-oprt-border bg-oprt-surface-1 p-2u font-sans text-oprt-base normal-case text-oprt-text"
14
+ );
15
+ var PREVIEW_HEIGHT = "max-h-[var(--oprt-preview-max-height,60vh)]";
16
+ function safeImageSource(src) {
17
+ if (!src || src.trim() !== src || Array.from(src).some(
18
+ (character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127
19
+ ))
20
+ return false;
21
+ if (src.startsWith("data:"))
22
+ return /^data:image\/(?:png|jpeg|gif|webp|avif|bmp);base64,[a-z0-9+/=]+$/i.test(src);
23
+ try {
24
+ return ["https:", "http:", "blob:"].includes(
25
+ new URL(src, "https://operator.invalid/").protocol
26
+ );
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ var AttachmentPreview = forwardRef(
32
+ ({
33
+ label,
34
+ attachment,
35
+ preview,
36
+ disabled = false,
37
+ onDownload,
38
+ onRemove,
39
+ className,
40
+ "aria-labelledby": labelledBy,
41
+ ...props
42
+ }, ref) => {
43
+ const root = useRef(null);
44
+ const mergedRef = useMergedRefs(root, ref);
45
+ const ids = usePartIds(["label"], {
46
+ id: props.id,
47
+ readRendered: () => root.current?.getAttribute("data-oprt-ids")
48
+ });
49
+ const imageKey = `${attachment.id}\0${preview.kind === "image" ? preview.src : ""}`;
50
+ const [imageState, setImageState] = useState({ key: imageKey, failed: false });
51
+ if (imageState.key !== imageKey) setImageState({ key: imageKey, failed: false });
52
+ const imageError = preview.kind === "image" ? !safeImageSource(preview.src) ? "Image preview source is not supported." : imageState.key === imageKey && imageState.failed ? "Image preview could not be loaded." : null : null;
53
+ return /* @__PURE__ */ jsxs(
54
+ "div",
55
+ {
56
+ ...props,
57
+ ref: mergedRef,
58
+ role: "region",
59
+ "data-oprt-ids": ids.base,
60
+ "aria-labelledby": joinIds(ids.label, labelledBy),
61
+ className: clsx(previewStyles(), className),
62
+ children: [
63
+ /* @__PURE__ */ jsx(
64
+ "h3",
65
+ {
66
+ id: ids.label,
67
+ className: "m-0 oprt-wrap-anywhere font-medium uppercase tracking-oprt-sm",
68
+ children: label
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx("p", { className: "m-0 oprt-wrap-anywhere", children: attachment.name }),
72
+ /* @__PURE__ */ jsxs("p", { className: "m-0 text-oprt-text-dim", children: [
73
+ /* @__PURE__ */ jsx(ByteCountLabel, { value: attachment.sizeBytes }),
74
+ attachment.mediaType && /* @__PURE__ */ jsxs(Fragment, { children: [
75
+ " ",
76
+ "\xB7 ",
77
+ /* @__PURE__ */ jsx("span", { className: "oprt-wrap-anywhere", children: attachment.mediaType })
78
+ ] })
79
+ ] }),
80
+ preview.kind === "image" && (imageError ? /* @__PURE__ */ jsx("p", { role: "status", className: "m-0 text-oprt-error", children: imageError }) : /* @__PURE__ */ jsx(
81
+ "img",
82
+ {
83
+ src: preview.src,
84
+ alt: preview.alt,
85
+ onError: () => setImageState(
86
+ (current) => current.key === imageKey ? { key: imageKey, failed: true } : current
87
+ ),
88
+ className: clsx(PREVIEW_HEIGHT, "max-w-full self-start object-contain")
89
+ },
90
+ imageKey
91
+ )),
92
+ preview.kind === "text" && /* @__PURE__ */ jsxs(Fragment, { children: [
93
+ /* @__PURE__ */ jsx(
94
+ "pre",
95
+ {
96
+ role: "region",
97
+ className: clsx(
98
+ PREVIEW_HEIGHT,
99
+ "m-0 overflow-auto whitespace-pre-wrap oprt-wrap-anywhere font-mono outline-hidden focus-visible:shadow-oprt-focus"
100
+ ),
101
+ tabIndex: 0,
102
+ "aria-label": `Text preview of ${attachment.name}`,
103
+ children: preview.text.slice(0, 16384)
104
+ }
105
+ ),
106
+ preview.text.length > 16384 && /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim", children: "Preview truncated to 16,384 characters." })
107
+ ] }),
108
+ preview.kind === "unavailable" && /* @__PURE__ */ jsx("p", { className: "m-0 oprt-wrap-anywhere text-oprt-text-dim", children: preview.message }),
109
+ (onDownload || onRemove) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1u", children: [
110
+ onDownload && /* @__PURE__ */ jsx(
111
+ Button,
112
+ {
113
+ variant: "secondary",
114
+ size: "sm",
115
+ disabled,
116
+ "aria-label": `Download ${attachment.name}`,
117
+ onClick: () => {
118
+ if (!disabled) onDownload(attachment.id);
119
+ },
120
+ children: "Download"
121
+ }
122
+ ),
123
+ onRemove && /* @__PURE__ */ jsx(
124
+ Button,
125
+ {
126
+ variant: "tertiary",
127
+ size: "sm",
128
+ disabled,
129
+ "aria-label": `Remove ${attachment.name}`,
130
+ onClick: () => {
131
+ if (!disabled) onRemove(attachment.id);
132
+ },
133
+ children: "Remove"
134
+ }
135
+ )
136
+ ] })
137
+ ]
138
+ }
139
+ );
140
+ }
141
+ );
142
+ AttachmentPreview.displayName = "AttachmentPreview";
143
+ export {
144
+ AttachmentPreview
145
+ };
@@ -0,0 +1,42 @@
1
+ // src/AttachmentPreview/AttachmentPreview.manifest.ts
2
+ var AttachmentPreviewManifest = {
3
+ AttachmentPreview: {
4
+ description: "Named attachment metadata with an escaped text preview, safe image source or readable fallback.",
5
+ props: {
6
+ label: { type: "string", required: true },
7
+ attachment: {
8
+ type: "node",
9
+ required: true,
10
+ description: "AttachmentMetadata object: stable id, name, sizeBytes, optional mediaType. No file reads or persistence are performed."
11
+ },
12
+ preview: {
13
+ type: "node",
14
+ required: true,
15
+ description: "AttachmentPreviewContent: {kind:'image',src,alt}, {kind:'text',text} or {kind:'unavailable',message}. Text is escaped and limited to 16,384 displayed characters. Images permit relative/http/https/blob sources and base64 PNG/JPEG/GIF/WebP/AVIF/BMP data URLs; decoding failures remain readable. No HTML or document iframe rendering."
16
+ },
17
+ disabled: {
18
+ type: "boolean",
19
+ default: false,
20
+ description: "Disables download/remove actions while preserving the preview."
21
+ },
22
+ onDownload: {
23
+ type: "function",
24
+ description: "Requests a download by attachment ID. Invocation does not prove a file was saved."
25
+ },
26
+ onRemove: {
27
+ type: "function",
28
+ description: "Requests removal by attachment ID. The caller owns replacement and focus if removing the entire preview."
29
+ },
30
+ className: {
31
+ type: "string",
32
+ description: "Classes for the root. Set --oprt-preview-max-height here to change the image and text preview cap from its 60vh default, for example [--oprt-preview-max-height:24rem]."
33
+ }
34
+ },
35
+ composable_with: ["FileUpload", "UploadQueue", "Dialog"],
36
+ never_inside: ["Button"],
37
+ example: '<AttachmentPreview label="Equipment file" attachment={attachment} preview={{kind:"text",text:csvPreview}} onDownload={requestDownload} />'
38
+ }
39
+ };
40
+ export {
41
+ AttachmentPreviewManifest
42
+ };
@@ -0,0 +1,9 @@
1
+ // src/AttachmentPreview/index.ts
2
+ import {
3
+ AttachmentPreview
4
+ } from "./AttachmentPreview.js";
5
+ import { AttachmentPreviewManifest } from "./AttachmentPreview.manifest.js";
6
+ export {
7
+ AttachmentPreview,
8
+ AttachmentPreviewManifest
9
+ };
@@ -0,0 +1,87 @@
1
+ "use client";
2
+
3
+ // src/Avatar/Avatar.tsx
4
+ import { cva } from "class-variance-authority";
5
+ import { forwardRef, useCallback, useEffect, useState } from "react";
6
+ import clsx from "clsx";
7
+ import { User } from "@oprt/icons";
8
+ import { AvatarManifest } from "./Avatar.manifest.js";
9
+ import { devValidate } from "../_internal/devValidate.js";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ var rootStyles = cva(
12
+ // shrink-0: the root clips its content (overflow-hidden), so as a flex item its automatic minimum
13
+ // size is zero and a long neighbor in a no-wrap row would squeeze it into a sliver.
14
+ "inline-flex shrink-0 items-center justify-center overflow-hidden bg-oprt-surface-2 border border-oprt-border align-middle",
15
+ {
16
+ variants: {
17
+ size: {
18
+ sm: "h-oprt-sm w-oprt-sm",
19
+ md: "h-oprt-md w-oprt-md",
20
+ lg: "h-oprt-lg w-oprt-lg"
21
+ }
22
+ },
23
+ defaultVariants: { size: "md" }
24
+ }
25
+ );
26
+ var glyphSize = { sm: 12, md: 16, lg: 20 };
27
+ var firstCharacter = (word) => Array.from(word)[0] ?? "";
28
+ function getInitials(name) {
29
+ return name.trim().split(/\s+/).filter(Boolean).slice(0, 2).map((word) => firstCharacter(firstCharacter(word).toUpperCase())).join("");
30
+ }
31
+ var Avatar = forwardRef((allProps, ref) => {
32
+ const { className, size, name, src, alt, delayMs, ...props } = allProps;
33
+ const initials = getInitials(name);
34
+ const [load, setLoad] = useState(null);
35
+ const status = load !== null && load.src === src ? load.status : "idle";
36
+ const [fallbackReady, setFallbackReady] = useState(delayMs === void 0);
37
+ useEffect(() => {
38
+ if (delayMs === void 0) {
39
+ setFallbackReady(true);
40
+ return;
41
+ }
42
+ setFallbackReady(false);
43
+ const t = setTimeout(() => setFallbackReady(true), delayMs);
44
+ return () => clearTimeout(t);
45
+ }, [delayMs, src]);
46
+ const imgRef = useCallback(
47
+ (node) => {
48
+ if (node?.complete && node.naturalWidth > 0) setLoad({ src, status: "loaded" });
49
+ },
50
+ [src]
51
+ );
52
+ if (process.env.NODE_ENV !== "production") {
53
+ devValidate("Avatar", AvatarManifest.Avatar.props, { ...allProps });
54
+ }
55
+ const showImage = src !== void 0 && status === "loaded";
56
+ const showFallback = !showImage && (fallbackReady || status === "error");
57
+ const label = alt ?? name;
58
+ const named = label.trim() !== "";
59
+ const resolvedSize = size ?? "md";
60
+ return /* @__PURE__ */ jsxs(
61
+ "span",
62
+ {
63
+ ref,
64
+ className: clsx(rootStyles({ size: resolvedSize }), className),
65
+ ...named ? { role: "img", "aria-label": label } : { "aria-hidden": true },
66
+ ...props,
67
+ children: [
68
+ src !== void 0 && status !== "error" && /* @__PURE__ */ jsx(
69
+ "img",
70
+ {
71
+ ref: imgRef,
72
+ className: clsx("h-full w-full object-cover", !showImage && "hidden"),
73
+ src,
74
+ alt: "",
75
+ onLoad: () => setLoad({ src, status: "loaded" }),
76
+ onError: () => setLoad({ src, status: "error" })
77
+ }
78
+ ),
79
+ showFallback && (initials !== "" ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "font-mono text-oprt-base text-oprt-text", children: initials }) : /* @__PURE__ */ jsx(User, { "aria-hidden": "true", size: glyphSize[resolvedSize], className: "text-oprt-text-dim" }))
80
+ ]
81
+ }
82
+ );
83
+ });
84
+ Avatar.displayName = "Avatar";
85
+ export {
86
+ Avatar
87
+ };
@@ -0,0 +1,25 @@
1
+ // src/Avatar/Avatar.manifest.ts
2
+ var AvatarManifest = {
3
+ Avatar: {
4
+ description: "Square, initials-based avatar. Renders monospace initials from the name, or an image when src is provided. It is named once, at its root, by alt or by name, and keeps its size in a crowded row.",
5
+ props: {
6
+ name: { type: "string", required: true },
7
+ src: { type: "string" },
8
+ alt: {
9
+ type: "string",
10
+ description: "Accessible name of the avatar. Defaults to name. An empty string hides the avatar from assistive technology, for one beside a visible name."
11
+ },
12
+ size: { type: "enum", values: ["sm", "md", "lg"], default: "md" },
13
+ delayMs: {
14
+ type: "number",
15
+ description: "Milliseconds to hold back the initials while an image loads. The initials show at once if the image fails."
16
+ }
17
+ },
18
+ composable_with: ["Card", "Sidebar", "DataTable"],
19
+ never_inside: ["Badge"],
20
+ example: '<Avatar name="Ada Lovelace" />'
21
+ }
22
+ };
23
+ export {
24
+ AvatarManifest
25
+ };
@@ -0,0 +1,7 @@
1
+ // src/Avatar/index.ts
2
+ import { Avatar } from "./Avatar.js";
3
+ import { AvatarManifest } from "./Avatar.manifest.js";
4
+ export {
5
+ Avatar,
6
+ AvatarManifest
7
+ };
@@ -0,0 +1,124 @@
1
+ "use client";
2
+
3
+ // src/Badge/Badge.tsx
4
+ import { cva } from "class-variance-authority";
5
+ import { Children, forwardRef } from "react";
6
+ import clsx from "clsx";
7
+ import { BadgeManifest } from "./Badge.manifest.js";
8
+ import { devValidate } from "../_internal/devValidate.js";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ var badgeStyles = cva(
11
+ // items-start: an indicator is aligned with the label's first line, not centered against all of it.
12
+ "inline-flex w-fit items-start align-middle gap-0.5u font-sans uppercase bg-transparent [&_svg]:shrink-0 [&_code]:normal-case [&_code]:tracking-normal [&_kbd]:normal-case [&_kbd]:tracking-normal",
13
+ {
14
+ variants: {
15
+ variant: {
16
+ success: "text-oprt-success border-oprt-success/40",
17
+ warning: "text-oprt-warning border-oprt-warning/40",
18
+ error: "text-oprt-error border-oprt-error/40",
19
+ info: "text-oprt-info border-oprt-info/40",
20
+ neutral: "text-oprt-text-dim border-oprt-border"
21
+ },
22
+ appearance: {
23
+ // A status is never cut off and never spills out of a narrow container: a label that does
24
+ // not fit wraps (long tokens break anywhere) and the badge grows with it, its lines centered
25
+ // like a one-line label. A one-line badge keeps its exact height (the compound variants).
26
+ // It never shrinks: squeezed beside a long sibling in a row that does not wrap, a short
27
+ // status such as LIVE would break mid-word. `max-w-full` keeps it inside its container.
28
+ // The centering has no specificity, as Button's has, so a caller's `text-left` wins.
29
+ outlined: "min-w-0 max-w-full shrink-0 whitespace-normal oprt-wrap-anywhere [:where(&)]:text-center border",
30
+ text: "min-w-0 max-w-full shrink whitespace-normal oprt-wrap-anywhere border-0 p-0"
31
+ },
32
+ size: {
33
+ sm: "text-oprt-sm font-medium tracking-oprt-sm",
34
+ md: "text-oprt-base font-medium tracking-oprt-caps"
35
+ }
36
+ },
37
+ compoundVariants: [
38
+ // Only outlined passive labels have a minimum height and inline padding. The vertical padding
39
+ // is what is left of the height around one 16px line and the 2px of border (1px at sm and
40
+ // 3px at md), so a one-line badge is exactly 20px or 24px and a wrapped one still has room.
41
+ {
42
+ appearance: "outlined",
43
+ size: "sm",
44
+ class: "min-h-oprt-badge-sm px-1u py-[calc((var(--oprt-badge-height-sm)-2px-var(--oprt-line-sm))/2)]"
45
+ },
46
+ {
47
+ appearance: "outlined",
48
+ size: "md",
49
+ class: "min-h-oprt-badge-md px-1u py-[calc((var(--oprt-badge-height-md)-2px-var(--oprt-line-base))/2)]"
50
+ }
51
+ ],
52
+ defaultVariants: { variant: "neutral", appearance: "outlined", size: "sm" }
53
+ }
54
+ );
55
+ var dotStyles = cva("inline-block shrink-0 self-start mt-0.5u w-1u h-1u", {
56
+ variants: {
57
+ variant: {
58
+ success: "bg-oprt-success",
59
+ warning: "bg-oprt-warning",
60
+ error: "bg-oprt-error",
61
+ info: "bg-oprt-info",
62
+ neutral: "bg-oprt-text-dim"
63
+ }
64
+ },
65
+ defaultVariants: { variant: "neutral" }
66
+ });
67
+ var iconBoxStyles = cva("flex shrink-0 items-center", {
68
+ variants: {
69
+ size: {
70
+ sm: "h-[var(--oprt-line-sm)]",
71
+ md: "h-[var(--oprt-line-base)]"
72
+ }
73
+ },
74
+ defaultVariants: { size: "sm" }
75
+ });
76
+ var Badge = forwardRef((allProps, ref) => {
77
+ const {
78
+ className,
79
+ variant = "neutral",
80
+ appearance = "outlined",
81
+ size,
82
+ indicator = "none",
83
+ icon,
84
+ children,
85
+ ...props
86
+ } = allProps;
87
+ if (process.env.NODE_ENV !== "production") {
88
+ devValidate("Badge", BadgeManifest.Badge.props, { ...allProps });
89
+ }
90
+ const resolved = {
91
+ variant: variant ?? void 0,
92
+ appearance: appearance ?? void 0,
93
+ size: size ?? void 0
94
+ };
95
+ const composite = Children.toArray(children).some(
96
+ (child) => typeof child !== "string" && typeof child !== "number"
97
+ );
98
+ return /* @__PURE__ */ jsxs(
99
+ "span",
100
+ {
101
+ ref,
102
+ "data-oprt-badge": "",
103
+ "data-appearance": appearance ?? "outlined",
104
+ className: clsx(badgeStyles(resolved), className),
105
+ ...props,
106
+ children: [
107
+ indicator === "dot" && /* @__PURE__ */ jsx(
108
+ "span",
109
+ {
110
+ "data-oprt-badge-dot": "",
111
+ "aria-hidden": "true",
112
+ className: dotStyles({ variant: resolved.variant })
113
+ }
114
+ ),
115
+ indicator === "icon" && /* @__PURE__ */ jsx("span", { "data-oprt-badge-icon": "", className: iconBoxStyles({ size: resolved.size }), children: icon }),
116
+ composite ? /* @__PURE__ */ jsx("span", { "data-oprt-badge-label": "", className: "min-w-0", children }) : children
117
+ ]
118
+ }
119
+ );
120
+ });
121
+ Badge.displayName = "Badge";
122
+ export {
123
+ Badge
124
+ };
@@ -0,0 +1,41 @@
1
+ // src/Badge/Badge.manifest.ts
2
+ var BadgeManifest = {
3
+ Badge: {
4
+ description: "Read-only uppercase status display with outlined and text appearances. Use meaningful status wording so color is not the only cue; dots and icons are optional. A status never truncates: a label that does not fit wraps, long tokens break anywhere, and the badge grows with it. An outlined badge keeps its width, so a short status is never squeezed into a broken word beside a long sibling, and is never wider than its container; a text badge can shrink with its layout. Code and kbd retain their casing; wrap other case-sensitive names or IDs in a child with normal-case tracking-normal.",
5
+ props: {
6
+ variant: {
7
+ type: "enum",
8
+ values: ["success", "warning", "error", "info", "neutral"],
9
+ default: "neutral"
10
+ },
11
+ appearance: {
12
+ type: "enum",
13
+ values: ["outlined", "text"],
14
+ default: "outlined",
15
+ description: "Outlined adds a border, padding, and a minimum label height for standalone summaries: exactly that height for one line, and it grows when the label wraps, its lines centered. Text removes the box and wraps within available width for dense tables, lists, and metadata."
16
+ },
17
+ size: {
18
+ type: "enum",
19
+ values: ["sm", "md"],
20
+ default: "sm",
21
+ description: "Typography size. One-line outlined labels are 20px (sm) or 24px (md) high with 8px inline padding. Text labels have no minimum height or padding. Explicit indicators use a 4px gap and align with the label's first line. Use Button or Toggle for actions."
22
+ },
23
+ indicator: {
24
+ type: "enum",
25
+ values: ["dot", "icon", "none"],
26
+ default: "none",
27
+ description: "Optional decorative indicator for either appearance and every variant. Defaults to none; meaningful visible status text supplies the non-color cue."
28
+ },
29
+ icon: {
30
+ type: "node",
31
+ description: "ReactNode rendered when indicator='icon'. Decorative; mark with aria-hidden."
32
+ }
33
+ },
34
+ composable_with: ["DataTable", "Card", "Toolbar", "Sidebar"],
35
+ never_inside: ["Badge", "Button"],
36
+ example: '<Badge variant="success" indicator="dot">ONLINE</Badge>'
37
+ }
38
+ };
39
+ export {
40
+ BadgeManifest
41
+ };
@@ -0,0 +1,7 @@
1
+ // src/Badge/index.ts
2
+ import { Badge } from "./Badge.js";
3
+ import { BadgeManifest } from "./Badge.manifest.js";
4
+ export {
5
+ Badge,
6
+ BadgeManifest
7
+ };
@@ -0,0 +1,64 @@
1
+ "use client";
2
+
3
+ // src/Breadcrumb/Breadcrumb.tsx
4
+ import { ChevronRight } from "@oprt/icons";
5
+ import { forwardRef } from "react";
6
+ import clsx from "clsx";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ function currentIndex(items) {
9
+ const flagged = items.findIndex((item) => item.current === true);
10
+ if (flagged !== -1) return flagged;
11
+ const last = items.length - 1;
12
+ return items[last]?.current === false ? -1 : last;
13
+ }
14
+ var Breadcrumb = forwardRef(
15
+ ({ items, separator = /* @__PURE__ */ jsx(ChevronRight, { size: 12 }), className, ...props }, ref) => {
16
+ if (items.length === 0) return null;
17
+ const current = currentIndex(items);
18
+ return /* @__PURE__ */ jsx(
19
+ "nav",
20
+ {
21
+ ref,
22
+ "aria-label": "Breadcrumb",
23
+ className: clsx("font-sans normal-case", className),
24
+ ...props,
25
+ children: /* @__PURE__ */ jsx("ol", { className: "m-0 flex min-w-0 list-none flex-wrap items-center gap-1u p-0", children: items.map((it, i) => {
26
+ const last = i === items.length - 1;
27
+ const isCurrent = i === current;
28
+ return /* @__PURE__ */ jsxs("li", { className: "flex min-w-0 items-center gap-1u", children: [
29
+ isCurrent || !it.href ? /* @__PURE__ */ jsx(
30
+ "span",
31
+ {
32
+ "aria-current": isCurrent ? "page" : void 0,
33
+ className: clsx(
34
+ "min-w-0 text-oprt-base oprt-wrap-anywhere",
35
+ isCurrent ? "text-oprt-text" : "text-oprt-text-dim"
36
+ ),
37
+ children: it.label
38
+ }
39
+ ) : /* @__PURE__ */ jsx(
40
+ "a",
41
+ {
42
+ href: it.href,
43
+ className: "inline-flex min-h-3u min-w-3u items-center border border-transparent text-oprt-base font-medium text-oprt-text-dim no-underline oprt-wrap-anywhere hover:text-oprt-text focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus focus-visible:outline-hidden",
44
+ children: it.label
45
+ }
46
+ ),
47
+ !last && /* @__PURE__ */ jsx(
48
+ "span",
49
+ {
50
+ "aria-hidden": "true",
51
+ className: "flex shrink-0 items-center text-oprt-text-muted",
52
+ children: separator
53
+ }
54
+ )
55
+ ] }, `${it.label}-${i}`);
56
+ }) })
57
+ }
58
+ );
59
+ }
60
+ );
61
+ Breadcrumb.displayName = "Breadcrumb";
62
+ export {
63
+ Breadcrumb
64
+ };
@@ -0,0 +1,28 @@
1
+ // src/Breadcrumb/Breadcrumb.manifest.ts
2
+ var BreadcrumbManifest = {
3
+ Breadcrumb: {
4
+ description: "Self-contained navigation trail rendered from a flat array of items, with the final (or flagged) item marked as the current page.",
5
+ props: {
6
+ items: {
7
+ type: "array",
8
+ required: true,
9
+ items: {
10
+ label: "string",
11
+ href: "string (optional \u2014 omit to render plain text)",
12
+ current: "boolean (optional \u2014 defaults to true on the last item)"
13
+ },
14
+ description: "Ordered trail. Each entry: { label: string; href?: string; current?: boolean }. Exactly one item is current: the first that sets current:true, otherwise the last unless it sets current:false. Items after the current one keep their links. A trail that does not fit wraps onto more lines; an empty trail renders nothing."
15
+ },
16
+ separator: {
17
+ type: "node",
18
+ description: "ReactNode rendered between items (default: a small chevron-right SVG). Decorative; rendered aria-hidden. Pass a string like '/' for a slash separator."
19
+ }
20
+ },
21
+ composable_with: ["Card"],
22
+ never_inside: ["Breadcrumb"],
23
+ example: '<Breadcrumb items={[{label:"HOME",href:"/"},{label:"OPS",href:"/ops"},{label:"TRACK 7"}]} />'
24
+ }
25
+ };
26
+ export {
27
+ BreadcrumbManifest
28
+ };
@@ -0,0 +1,7 @@
1
+ // src/Breadcrumb/index.ts
2
+ import { Breadcrumb } from "./Breadcrumb.js";
3
+ import { BreadcrumbManifest } from "./Breadcrumb.manifest.js";
4
+ export {
5
+ Breadcrumb,
6
+ BreadcrumbManifest
7
+ };