@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,170 @@
1
+ "use client";
2
+
3
+ // src/Button/Button.tsx
4
+ import { Slot } from "@radix-ui/react-slot";
5
+ import { cva } from "class-variance-authority";
6
+ import {
7
+ Children,
8
+ Fragment,
9
+ cloneElement,
10
+ forwardRef,
11
+ isValidElement
12
+ } from "react";
13
+ import clsx from "clsx";
14
+ import { useMotion, composeMotionHandlers } from "../_internal/motion.js";
15
+ import { useFocusableWhenUnavailable } from "../_internal/unavailable.js";
16
+ import { ButtonManifest } from "./Button.manifest.js";
17
+ import { devValidate } from "../_internal/devValidate.js";
18
+ import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
19
+ var buttonStyles = cva(
20
+ [
21
+ "oprt-cut border border-transparent",
22
+ // The label is centered, one line or several (design call DC-8). `:where(&)` gives that default
23
+ // no specificity, as the browser's own button style has, so a caller's `text-left` still wins.
24
+ "inline-flex items-center justify-center gap-1u [:where(&)]:text-center whitespace-normal",
25
+ // The height is a minimum (sizes, below), so a row that stretches its items would grow a
26
+ // one-line button to its tallest neighbor. `fit-content` is not `auto`, so stretch leaves it
27
+ // alone, and the minimum height still applies.
28
+ "h-fit",
29
+ // Tracking is NOT set here — it belongs to the size variant, because the
30
+ // correct em value depends on the font size the variant selects.
31
+ "font-sans font-semibold uppercase [&_code]:normal-case [&_code]:tracking-normal [&_code]:oprt-wrap-anywhere [&_kbd]:normal-case [&_kbd]:tracking-normal",
32
+ "oprt-motion oprt-motion-press",
33
+ // Keyboard focus is the system's ring around the element's own box, with the edge of the shape
34
+ // lit inside it.
35
+ "focus-visible:outline-hidden focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus focus-visible:[--oprt-cut-edge:var(--oprt-border-focus)]",
36
+ "disabled:text-oprt-text-dim disabled:cursor-not-allowed",
37
+ "data-[disabled]:text-oprt-text-dim data-[disabled]:cursor-not-allowed data-[disabled]:focus-visible:border-oprt-border-focus data-[disabled]:focus-visible:[--oprt-cut-edge:var(--oprt-border-focus)]"
38
+ ].join(" "),
39
+ {
40
+ variants: {
41
+ variant: {
42
+ // The main action is the one button cut at rest, and a disabled one keeps that cut.
43
+ primary: "oprt-cut-standing text-oprt-bg [--oprt-cut-fill:var(--oprt-accent)] [--oprt-cut-edge:var(--oprt-accent-active)] hover:[--oprt-cut-fill:var(--oprt-accent-hover)] active:[--oprt-cut-fill:var(--oprt-accent-active)] disabled:[--oprt-cut-fill:var(--oprt-surface-3)] disabled:[--oprt-cut-edge:var(--oprt-border-control)] data-[disabled]:[--oprt-cut-fill:var(--oprt-surface-3)] data-[disabled]:[--oprt-cut-edge:var(--oprt-border-control)]",
44
+ secondary: "text-oprt-accent [--oprt-cut-fill:transparent] [--oprt-cut-edge:var(--oprt-accent)] hover:[--oprt-cut-fill:var(--oprt-accent-ghost)] disabled:[--oprt-cut-fill:transparent] disabled:[--oprt-cut-edge:var(--oprt-border-control)] data-[disabled]:[--oprt-cut-fill:transparent] data-[disabled]:[--oprt-cut-edge:var(--oprt-border-control)]",
45
+ // Outline variants draw their boundary in the control outline (design call DC-3).
46
+ tertiary: "text-oprt-text [--oprt-cut-fill:transparent] [--oprt-cut-edge:var(--oprt-border-control)] hover:[--oprt-cut-edge:var(--oprt-accent)] disabled:[--oprt-cut-fill:transparent] disabled:[--oprt-cut-edge:var(--oprt-border-control)] data-[disabled]:[--oprt-cut-fill:transparent] data-[disabled]:[--oprt-cut-edge:var(--oprt-border-control)]",
47
+ // Danger keeps its red at full strength; only a disabled one drops to the gray outline
48
+ // (DC-9). Armed (hovered, focused, pressed), it fills with hazard stripes in place of a flat
49
+ // tint: `oprt-cut-hazard`.
50
+ danger: "oprt-cut-hazard text-oprt-error [--oprt-cut-fill:transparent] [--oprt-cut-edge:var(--oprt-error)] disabled:[--oprt-cut-fill:transparent] disabled:[--oprt-cut-edge:var(--oprt-border-control)] data-[disabled]:[--oprt-cut-fill:transparent] data-[disabled]:[--oprt-cut-edge:var(--oprt-border-control)]",
51
+ // Ghost: no container stroke — text-only until hover, and still none when disabled.
52
+ // Ideal for asChild links.
53
+ ghost: "text-oprt-text [--oprt-cut-fill:transparent] [--oprt-cut-edge:transparent] hover:[--oprt-cut-fill:var(--oprt-accent-ghost)] disabled:[--oprt-cut-fill:transparent] disabled:[--oprt-cut-edge:transparent] data-[disabled]:[--oprt-cut-fill:transparent] data-[disabled]:[--oprt-cut-edge:transparent]"
54
+ },
55
+ size: {
56
+ // Sizes carry size AND tracking; weight is set once on the base
57
+ // (font-semibold, 600). text-oprt-base (11px) is the MIL-STD-1472H floor.
58
+ // The height is a minimum, so a label that wraps grows the button instead of spilling out
59
+ // of it. The vertical padding is what is left of the height around one line and the 2 px
60
+ // of border, so a one-line button is exactly its token height (24, 32 and 40 px).
61
+ sm: "oprt-cut-sm min-h-oprt-sm px-2u py-[calc((var(--oprt-height-sm)-2px-var(--oprt-line-base))/2)] text-oprt-base tracking-oprt-caps",
62
+ md: "oprt-cut-md min-h-oprt-md px-3u py-[calc((var(--oprt-height-md)-2px-var(--oprt-line-base))/2)] text-oprt-base tracking-oprt-caps",
63
+ lg: "oprt-cut-lg min-h-oprt-lg px-4u py-[calc((var(--oprt-height-lg)-2px-var(--oprt-line-md))/2)] text-oprt-md tracking-oprt-caps-lg"
64
+ }
65
+ },
66
+ defaultVariants: { variant: "primary", size: "md" }
67
+ }
68
+ );
69
+ function flatten(children, prefix = "") {
70
+ return Children.toArray(children).flatMap((child) => {
71
+ if (!isValidElement(child)) return [child];
72
+ if (child.type === Fragment) return flatten(child.props.children, `${prefix}${child.key}/`);
73
+ return [prefix ? cloneElement(child, { key: `${prefix}${child.key}` }) : child];
74
+ });
75
+ }
76
+ function isIcon(node) {
77
+ if (!isValidElement(node)) return false;
78
+ const hidden = node.props["aria-hidden"];
79
+ return node.type === "svg" || hidden === true || hidden === "true" || typeof node.type !== "string" && node.props.children == null;
80
+ }
81
+ function splitLabel(children) {
82
+ const parts = flatten(children);
83
+ const edge = (node) => isIcon(node) || typeof node === "string" && node.trim() === "";
84
+ let start = 0;
85
+ let end = parts.length;
86
+ while (start < end && edge(parts[start])) start += 1;
87
+ while (end > start && edge(parts[end - 1])) end -= 1;
88
+ const label = parts.slice(start, end);
89
+ const composite = label.some((part) => typeof part !== "string" && typeof part !== "number");
90
+ return composite ? { before: parts.slice(0, start), label, after: parts.slice(end) } : null;
91
+ }
92
+ var reportedUnavailableLink = false;
93
+ function reportUnavailableLink() {
94
+ if (process.env.NODE_ENV === "production" || reportedUnavailableLink) return;
95
+ reportedUnavailableLink = true;
96
+ console.error(
97
+ JSON.stringify(
98
+ {
99
+ component: "Button",
100
+ error: "UNAVAILABLE_LINK",
101
+ prop: "disabled",
102
+ received: "asChild with disabled",
103
+ suggestion: "A disabled asChild link is aria-disabled and cancels clicks and middle clicks, but a browser still offers its address (open in a new tab, copy link), and a child that sets its own props can override the block. Show navigation that is not available yet as plain text, and render the link when it is."
104
+ },
105
+ null,
106
+ 2
107
+ )
108
+ );
109
+ }
110
+ function stopActivation(event) {
111
+ event.preventDefault();
112
+ event.stopPropagation();
113
+ }
114
+ var Button = forwardRef((allProps, ref) => {
115
+ const {
116
+ className,
117
+ variant,
118
+ size,
119
+ asChild = false,
120
+ motion = "auto",
121
+ disabled,
122
+ children,
123
+ ...props
124
+ } = allProps;
125
+ const motionState = useMotion(motion, "Button");
126
+ const keepFocusable = useFocusableWhenUnavailable();
127
+ const Comp = asChild ? Slot : "button";
128
+ if (process.env.NODE_ENV !== "production") {
129
+ devValidate("Button", ButtonManifest.Button.props, { ...allProps });
130
+ }
131
+ const unavailable = disabled === true;
132
+ const native = unavailable && !asChild && !keepFocusable;
133
+ const blocked = unavailable && !native;
134
+ if (blocked && asChild) reportUnavailableLink();
135
+ const own = blocked ? {
136
+ ...props,
137
+ onClick: void 0,
138
+ onClickCapture: stopActivation,
139
+ onAuxClickCapture: stopActivation
140
+ } : props;
141
+ const split = asChild ? null : splitLabel(children);
142
+ return /* @__PURE__ */ jsx(
143
+ Comp,
144
+ {
145
+ ref,
146
+ type: asChild ? void 0 : "button",
147
+ className: clsx(
148
+ buttonStyles({ variant: variant ?? void 0, size: size ?? void 0 }),
149
+ className
150
+ ),
151
+ ...own,
152
+ ...composeMotionHandlers(motionState.props, own),
153
+ ...native ? { disabled: true } : {},
154
+ ...blocked ? {
155
+ "aria-disabled": true,
156
+ "data-disabled": "true",
157
+ ...asChild && !keepFocusable ? { tabIndex: -1 } : {}
158
+ } : {},
159
+ children: split ? /* @__PURE__ */ jsxs(Fragment2, { children: [
160
+ split.before,
161
+ /* @__PURE__ */ jsx("span", { "data-oprt-button-label": "", className: "min-w-0 [&_svg]:inline-block", children: split.label }),
162
+ split.after
163
+ ] }) : children
164
+ }
165
+ );
166
+ });
167
+ Button.displayName = "Button";
168
+ export {
169
+ Button
170
+ };
@@ -0,0 +1,36 @@
1
+ // src/Button/Button.manifest.ts
2
+ var ButtonManifest = {
3
+ Button: {
4
+ description: "Primary interactive element for triggering actions. Short action labels display uppercase. A label that does not fit wraps and centers, and the button grows with it; a one-line button keeps its exact height, also in a row that stretches its items. A label made of text and an element (an identifier in code, a case-sensitive name in a span) wraps as one block, and an icon at either end stays beside it, drawn at 12, 16 and 20 px for sm, md and lg so it fits the one-line height. Code and kbd retain their casing, and code breaks anywhere; wrap other case-sensitive names or IDs in a child with normal-case tracking-normal oprt-wrap-anywhere. Shape: the primary variant carries a cut top-right corner at rest, and every enabled button takes a second cut at the opposite corner on hover, press and keyboard focus; an armed danger button fills with hazard stripes; segments of an attached ButtonGroup stay square. The fill and edge are drawn by a layer clipped to that shape, so bg-* and border-* classes do not change them: set --oprt-cut-fill and --oprt-cut-edge instead. The oprt-cut-none class squares off one button, or every button inside the element that carries it.",
5
+ props: {
6
+ motion: {
7
+ type: "enum",
8
+ values: ["auto", "none"],
9
+ default: "auto",
10
+ description: "Tactile pointer/touch feedback. None disables motion; keyboard and system reduced motion are always immediate."
11
+ },
12
+ variant: {
13
+ type: "enum",
14
+ values: ["primary", "secondary", "tertiary", "danger", "ghost"],
15
+ default: "primary"
16
+ },
17
+ size: { type: "enum", values: ["sm", "md", "lg"], default: "md" },
18
+ disabled: {
19
+ type: "boolean",
20
+ default: false,
21
+ description: "Unavailable: dim text on the control outline, no hover, and it never activates. A Button that is a Tooltip or Popover trigger with an explanation stays in the Tab order (aria-disabled) so the explanation can be reached."
22
+ },
23
+ asChild: {
24
+ type: "boolean",
25
+ default: false,
26
+ description: "Renders the child element in place of the button, for example a link; the child's own content is not laid out for you, so put a label made of text and an element in one span. A disabled asChild Button is aria-disabled, leaves the Tab order and blocks the click and the middle click, but a browser still offers the link's address (open in a new tab, copy link) and a child can override the block: show unavailable navigation as plain text."
27
+ }
28
+ },
29
+ composable_with: ["ButtonGroup", "Toolbar", "CardFooter"],
30
+ never_inside: ["Button", "Badge"],
31
+ example: '<Button variant="primary" size="md">DEPLOY</Button>'
32
+ }
33
+ };
34
+ export {
35
+ ButtonManifest
36
+ };
@@ -0,0 +1,7 @@
1
+ // src/Button/index.ts
2
+ import { Button } from "./Button.js";
3
+ import { ButtonManifest } from "./Button.manifest.js";
4
+ export {
5
+ Button,
6
+ ButtonManifest
7
+ };
@@ -0,0 +1,60 @@
1
+ "use client";
2
+
3
+ // src/ButtonGroup/ButtonGroup.tsx
4
+ import { cva } from "class-variance-authority";
5
+ import { forwardRef } from "react";
6
+ import clsx from "clsx";
7
+ import { ButtonGroupManifest } from "./ButtonGroup.manifest.js";
8
+ import { devValidate } from "../_internal/devValidate.js";
9
+ import { jsx } from "react/jsx-runtime";
10
+ var groupStyles = cva("inline-flex max-w-full", {
11
+ variants: {
12
+ orientation: {
13
+ horizontal: "flex-wrap",
14
+ vertical: "flex-col"
15
+ },
16
+ attached: {
17
+ // Negative 1px margins collapse adjacent 1px borders into a single shared edge (the
18
+ // compound variants below). hover/focus-visible get relative+z-10 so the active
19
+ // border/ring is painted on top and never clipped by the neighboring segment, on any row.
20
+ // A Button has a height of its own so that a stretching row cannot grow a one-line button;
21
+ // the segments of an attached group are the exception, and take the height of the tallest
22
+ // segment in their row so the shared edges run straight.
23
+ // Joined segments are one object, and a cut corner inside the group would notch it, so the
24
+ // group turns the cut off for everything in it (`oprt-cut-none`, see the cut recipe).
25
+ true: "oprt-cut-none [&>*]:h-auto [&>*:hover]:relative [&>*:focus-visible]:relative [&>*:hover]:z-10 [&>*:focus-visible]:z-10",
26
+ false: "gap-1u"
27
+ }
28
+ },
29
+ compoundVariants: [
30
+ // Every segment, not only the non-first ones, takes the negative margin: the first of each row
31
+ // then shares its top edge with the row above, and the 1px of padding keeps the first row and
32
+ // the first column flush with the group's own edge instead of one pixel outside it.
33
+ { orientation: "horizontal", attached: true, class: "pl-px pt-px [&>*]:-ml-px [&>*]:-mt-px" },
34
+ { orientation: "vertical", attached: true, class: "pt-px [&>*]:-mt-px" }
35
+ ],
36
+ defaultVariants: { orientation: "horizontal", attached: true }
37
+ });
38
+ var ButtonGroup = forwardRef((allProps, ref) => {
39
+ const { className, orientation = "horizontal", attached = true, ...props } = allProps;
40
+ if (process.env.NODE_ENV !== "production") {
41
+ devValidate("ButtonGroup", ButtonGroupManifest.ButtonGroup.props, { ...allProps });
42
+ }
43
+ return /* @__PURE__ */ jsx(
44
+ "div",
45
+ {
46
+ ref,
47
+ role: "group",
48
+ className: clsx(
49
+ // A null enum value would mean "no variant", which is no layout at all.
50
+ groupStyles({ orientation: orientation ?? void 0, attached: attached ?? void 0 }),
51
+ className
52
+ ),
53
+ ...props
54
+ }
55
+ );
56
+ });
57
+ ButtonGroup.displayName = "ButtonGroup";
58
+ export {
59
+ ButtonGroup
60
+ };
@@ -0,0 +1,22 @@
1
+ // src/ButtonGroup/ButtonGroup.manifest.ts
2
+ var ButtonGroupManifest = {
3
+ ButtonGroup: {
4
+ description: "Segmented row or column of Buttons with collapsed shared borders. A row that does not fit wraps onto more rows and grows taller, so no action is hidden; attached segments keep their shared edges across rows and take the height of the tallest segment in their row, so the edges run straight when one label wrapped; in a detached group each segment keeps its own height. A column stacks. Attached segments stay square: the group turns off the cut a Button otherwise carries.",
5
+ props: {
6
+ orientation: {
7
+ type: "enum",
8
+ values: ["horizontal", "vertical"],
9
+ default: "horizontal"
10
+ },
11
+ attached: { type: "boolean", default: true }
12
+ },
13
+ slots: {
14
+ children: { type: "Button", required: true }
15
+ },
16
+ composable_with: ["Toolbar", "CardFooter"],
17
+ example: "<ButtonGroup>\n <Button>COPY</Button>\n <Button>PASTE</Button>\n</ButtonGroup>"
18
+ }
19
+ };
20
+ export {
21
+ ButtonGroupManifest
22
+ };
@@ -0,0 +1,7 @@
1
+ // src/ButtonGroup/index.ts
2
+ import { ButtonGroup } from "./ButtonGroup.js";
3
+ import { ButtonGroupManifest } from "./ButtonGroup.manifest.js";
4
+ export {
5
+ ButtonGroup,
6
+ ButtonGroupManifest
7
+ };
@@ -0,0 +1,106 @@
1
+ "use client";
2
+
3
+ // src/Calendar/Calendar.tsx
4
+ import { forwardRef, useId, useMemo } from "react";
5
+ import { GregorianCalendar, toCalendar } from "@internationalized/date";
6
+ import {
7
+ Button,
8
+ Calendar as AriaCalendar,
9
+ CalendarCell,
10
+ CalendarGrid,
11
+ CalendarGridBody,
12
+ CalendarGridHeader,
13
+ CalendarHeaderCell,
14
+ Heading
15
+ } from "react-aria-components";
16
+ import { cva } from "class-variance-authority";
17
+ import clsx from "clsx";
18
+ import { ChevronLeft, ChevronRight } from "@oprt/icons";
19
+ import { dateConfiguration, dateWithinBounds } from "../_internal/date.js";
20
+ import { definedOnly } from "../_internal/aria.js";
21
+ import { useHydrationRoot } from "../_internal/hydration.js";
22
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
23
+ var calendarStyles = cva(
24
+ "flex w-fit min-w-[calc(7*var(--oprt-space-3))] max-w-full flex-col gap-2u font-sans text-oprt-base text-oprt-text"
25
+ );
26
+ var CALENDAR_ROOT = "flex w-[calc(7*var(--oprt-space-4))] min-w-[calc(7*var(--oprt-space-3))] max-w-full flex-col gap-2u";
27
+ var FOCUS_OUTLINE = "focus-visible:outline-hidden forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-2 forced-colors:data-[focus-visible]:outline-[CanvasText]";
28
+ var navigationStyles = `flex min-h-3u min-w-3u shrink-0 items-center justify-center border border-oprt-border-control bg-oprt-surface-2 text-oprt-text ${FOCUS_OUTLINE} forced-colors:data-[focus-visible]:outline-offset-2 data-[hovered]:border-oprt-accent data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus data-[disabled]:cursor-not-allowed data-[disabled]:text-oprt-text-dim`;
29
+ var dayStyles = `flex aspect-square w-full min-h-3u min-w-3u items-center justify-center border border-transparent font-sans text-oprt-base font-medium tabular-nums ${FOCUS_OUTLINE} forced-colors:data-[focus-visible]:-outline-offset-2 data-[outside-month]:text-oprt-text-dim data-[selected]:border-oprt-accent data-[selected]:bg-oprt-surface-3 data-[selected]:font-semibold data-[hovered]:border-oprt-border data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 forced-colors:data-[selected]:border-[Highlight]`;
30
+ function CalendarContent() {
31
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
32
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2u", children: [
33
+ /* @__PURE__ */ jsx(Button, { slot: "previous", className: navigationStyles, children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 }) }),
34
+ /* @__PURE__ */ jsx(Heading, { className: "m-0 text-center font-sans text-oprt-base font-semibold uppercase tracking-oprt-caps" }),
35
+ /* @__PURE__ */ jsx(Button, { slot: "next", className: navigationStyles, children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 }) })
36
+ ] }),
37
+ /* @__PURE__ */ jsxs(CalendarGrid, { className: "w-full table-fixed border-collapse", weekdayStyle: "short", children: [
38
+ /* @__PURE__ */ jsx(CalendarGridHeader, { children: (day) => /* @__PURE__ */ jsx(CalendarHeaderCell, { className: "p-0 font-medium text-oprt-text-dim", children: day }) }),
39
+ /* @__PURE__ */ jsx(CalendarGridBody, { children: (date) => /* @__PURE__ */ jsx(CalendarCell, { date, className: dayStyles }) })
40
+ ] })
41
+ ] });
42
+ }
43
+ var Calendar = forwardRef(
44
+ ({
45
+ label,
46
+ value,
47
+ onValueChange,
48
+ min,
49
+ max,
50
+ disabled = false,
51
+ readOnly = false,
52
+ required = false,
53
+ id,
54
+ className
55
+ }, ref) => {
56
+ const generated = useId();
57
+ const errorId = `${id ?? generated}-error`;
58
+ const hydration = useHydrationRoot(id);
59
+ const config = useMemo(() => dateConfiguration(value, min, max), [value, min, max]);
60
+ const error = config.error ?? (required && value === null ? "A date is required." : null);
61
+ return (
62
+ // The key remounts the error with the calendar, so both take the client's ids when hydration
63
+ // disagrees with the server markup and the calendar's description still names the error
64
+ // (Calendar-R1).
65
+ /* @__PURE__ */ jsxs(
66
+ "div",
67
+ {
68
+ ...hydration.probeProps,
69
+ className: clsx(calendarStyles(), className),
70
+ children: [
71
+ /* @__PURE__ */ jsx(
72
+ AriaCalendar,
73
+ {
74
+ ...definedOnly({ id, "aria-describedby": error ? errorId : void 0 }),
75
+ ref,
76
+ "aria-label": label,
77
+ value: config.date,
78
+ minValue: config.min,
79
+ maxValue: config.max,
80
+ isDisabled: disabled || config.invalidBounds,
81
+ isReadOnly: readOnly,
82
+ isInvalid: !!error,
83
+ createCalendar: () => new GregorianCalendar(),
84
+ className: CALENDAR_ROOT,
85
+ onChange: (next) => {
86
+ const date = toCalendar(next, new GregorianCalendar());
87
+ if (!disabled && !readOnly && !config.invalidBounds && dateWithinBounds(date, config.min, config.max))
88
+ onValueChange(date.toString());
89
+ },
90
+ children: /* @__PURE__ */ jsx(CalendarContent, {})
91
+ }
92
+ ),
93
+ error && /* @__PURE__ */ jsx("p", { id: errorId, className: "m-0 max-w-full text-oprt-error", children: error })
94
+ ]
95
+ },
96
+ hydration.key
97
+ )
98
+ );
99
+ }
100
+ );
101
+ Calendar.displayName = "Calendar";
102
+ export {
103
+ CALENDAR_ROOT,
104
+ Calendar,
105
+ CalendarContent
106
+ };
@@ -0,0 +1,32 @@
1
+ // src/Calendar/Calendar.manifest.ts
2
+ var CalendarManifest = {
3
+ Calendar: {
4
+ description: "Controlled Gregorian calendar with date-only strings, keyboard day/month navigation, strict date bounds and visible configuration errors.",
5
+ props: {
6
+ label: { type: "string", required: true },
7
+ value: {
8
+ type: "string",
9
+ required: true,
10
+ description: "YYYY-MM-DD in years 0001\u20139999, or null for no selection. Never an instant or a local Date."
11
+ },
12
+ onValueChange: {
13
+ type: "function",
14
+ required: true,
15
+ description: "Requests the chosen date-only string. The parent accepts it through value."
16
+ },
17
+ min: { type: "string", description: "Inclusive YYYY-MM-DD lower bound." },
18
+ max: { type: "string", description: "Inclusive YYYY-MM-DD upper bound." },
19
+ disabled: { type: "boolean", default: false },
20
+ readOnly: { type: "boolean", default: false },
21
+ required: { type: "boolean", default: false },
22
+ id: { type: "string" },
23
+ className: { type: "string" }
24
+ },
25
+ composable_with: ["FormSection", "DatePicker"],
26
+ never_inside: ["Button"],
27
+ example: '<Calendar label="Inspection date" value={date} onValueChange={setDate} min="2026-01-01" max="2026-12-31" />'
28
+ }
29
+ };
30
+ export {
31
+ CalendarManifest
32
+ };
@@ -0,0 +1,7 @@
1
+ // src/Calendar/index.ts
2
+ import { Calendar } from "./Calendar.js";
3
+ import { CalendarManifest } from "./Calendar.manifest.js";
4
+ export {
5
+ Calendar,
6
+ CalendarManifest
7
+ };
@@ -0,0 +1,70 @@
1
+ "use client";
2
+
3
+ // src/Card/Card.tsx
4
+ import { forwardRef } from "react";
5
+ import clsx from "clsx";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var Card = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
8
+ "div",
9
+ {
10
+ ref,
11
+ className: clsx(
12
+ "flex min-w-0 flex-col bg-oprt-surface-1 border border-oprt-border font-sans",
13
+ className
14
+ ),
15
+ ...props
16
+ }
17
+ ));
18
+ Card.displayName = "Card";
19
+ var CardHeader = forwardRef(
20
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21
+ "div",
22
+ {
23
+ ref,
24
+ className: clsx(
25
+ // A long title takes its own row and wraps; a badge or button that does not fit beside it
26
+ // drops to the next one. The gap keeps them apart in either case.
27
+ "flex min-w-0 flex-wrap items-center justify-between gap-x-2u gap-y-1u px-3u py-2u border-b border-oprt-border oprt-wrap-anywhere text-oprt-sm uppercase tracking-oprt-sm text-oprt-text [&_code]:normal-case [&_code]:tracking-normal [&_kbd]:normal-case [&_kbd]:tracking-normal",
28
+ className
29
+ ),
30
+ ...props
31
+ }
32
+ )
33
+ );
34
+ CardHeader.displayName = "CardHeader";
35
+ var CardBody = forwardRef(
36
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
37
+ "div",
38
+ {
39
+ ref,
40
+ className: clsx(
41
+ "min-w-0 break-words px-3u py-3u text-oprt-md normal-case text-oprt-text",
42
+ className
43
+ ),
44
+ ...props
45
+ }
46
+ )
47
+ );
48
+ CardBody.displayName = "CardBody";
49
+ var CardFooter = forwardRef(
50
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
51
+ "div",
52
+ {
53
+ ref,
54
+ className: clsx(
55
+ // Actions that do not fit side by side wrap onto further rows, right-aligned: a plain row
56
+ // ran off its start edge, where nothing can scroll to it.
57
+ "flex flex-wrap items-center justify-end gap-2u px-3u py-2u border-t border-oprt-border",
58
+ className
59
+ ),
60
+ ...props
61
+ }
62
+ )
63
+ );
64
+ CardFooter.displayName = "CardFooter";
65
+ export {
66
+ Card,
67
+ CardBody,
68
+ CardFooter,
69
+ CardHeader
70
+ };
@@ -0,0 +1,57 @@
1
+ // src/Card/Card.manifest.ts
2
+ var CardManifest = {
3
+ Card: {
4
+ description: "Bordered surface container. Use with CardHeader, CardBody, CardFooter.",
5
+ props: {},
6
+ slots: {
7
+ children: { type: "CardHeader | CardBody | CardFooter", required: true }
8
+ },
9
+ composable_with: ["DataTable", "Sidebar"],
10
+ never_inside: ["Card"],
11
+ example: "<Card>\n <CardHeader>STATUS</CardHeader>\n <CardBody>All systems nominal.</CardBody>\n</Card>"
12
+ },
13
+ CardHeader: {
14
+ description: "Uppercase card title row, allows text + Badge + small buttons. Code and kbd retain their casing; wrap other names, IDs, or supporting text in a child with normal-case tracking-normal.",
15
+ props: {},
16
+ slots: {
17
+ children: { type: "text | Badge | Button(size=sm)", required: false }
18
+ },
19
+ composable_with: ["Card"],
20
+ never_inside: ["CardHeader", "CardBody", "CardFooter"],
21
+ example: "<CardHeader>STATUS</CardHeader>"
22
+ },
23
+ CardBody: {
24
+ description: "Card body slot \u2014 arbitrary content, preserving original casing.",
25
+ props: {},
26
+ slots: {
27
+ children: { type: "any", required: false }
28
+ },
29
+ composable_with: ["Card"],
30
+ never_inside: ["CardHeader", "CardBody", "CardFooter"],
31
+ example: "<CardBody>All systems nominal.</CardBody>"
32
+ },
33
+ CardFooter: {
34
+ description: "Card footer slot \u2014 action row, allows Button and ButtonGroup.",
35
+ props: {},
36
+ slots: {
37
+ children: { type: "Button | ButtonGroup", required: false }
38
+ },
39
+ composable_with: ["Card"],
40
+ never_inside: ["CardHeader", "CardBody", "CardFooter"],
41
+ example: '<CardFooter><Button variant="primary">DEPLOY</Button></CardFooter>'
42
+ }
43
+ };
44
+ var CardCompositionRules = {
45
+ Card: {
46
+ allowed_children: ["CardHeader", "CardBody", "CardFooter"],
47
+ children: {
48
+ CardHeader: { allowed_children: ["text", "Badge", "Button"] },
49
+ CardBody: { allowed_children: ["any"] },
50
+ CardFooter: { allowed_children: ["Button", "ButtonGroup"] }
51
+ }
52
+ }
53
+ };
54
+ export {
55
+ CardCompositionRules,
56
+ CardManifest
57
+ };
@@ -0,0 +1,16 @@
1
+ // src/Card/index.ts
2
+ import {
3
+ Card,
4
+ CardHeader,
5
+ CardBody,
6
+ CardFooter
7
+ } from "./Card.js";
8
+ import { CardManifest, CardCompositionRules } from "./Card.manifest.js";
9
+ export {
10
+ Card,
11
+ CardBody,
12
+ CardCompositionRules,
13
+ CardFooter,
14
+ CardHeader,
15
+ CardManifest
16
+ };