@oprt/react 0.0.0-stage → 0.1.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 +110 -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 +122 -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 +17811 -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,181 @@
1
+ "use client";
2
+
3
+ // src/Tabs/Tabs.tsx
4
+ import {
5
+ Tabs as AriaTabs,
6
+ TabList as AriaTabList,
7
+ Tab as AriaTab,
8
+ TabPanel as AriaTabPanel
9
+ } from "react-aria-components";
10
+ import {
11
+ createContext,
12
+ forwardRef,
13
+ useContext,
14
+ useRef
15
+ } from "react";
16
+ import clsx from "clsx";
17
+ import { TabsManifest } from "./Tabs.manifest.js";
18
+ import { devValidate } from "../_internal/devValidate.js";
19
+ import { useHydrationRoot } from "../_internal/hydration.js";
20
+ import { useMergedRefs } from "../_internal/mergeRefs.js";
21
+ import {
22
+ useMotion,
23
+ composeMotionHandlers,
24
+ MotionContext
25
+ } from "../_internal/motion.js";
26
+ import { useTabsIndicator } from "./useTabsIndicator.js";
27
+ import { useTabsScroll } from "./useTabsScroll.js";
28
+ import { jsx, jsxs } from "react/jsx-runtime";
29
+ var TabsOrientationContext = createContext("horizontal");
30
+ var Tabs = forwardRef(
31
+ ({
32
+ className,
33
+ motion = "auto",
34
+ orientation = "horizontal",
35
+ value,
36
+ defaultValue,
37
+ onValueChange,
38
+ children,
39
+ ...props
40
+ }, ref) => {
41
+ const motionState = useMotion(motion);
42
+ const rootProps = { orientation };
43
+ if (value !== void 0) rootProps["selectedKey"] = value;
44
+ if (defaultValue !== void 0) rootProps["defaultSelectedKey"] = defaultValue;
45
+ if (onValueChange !== void 0) {
46
+ rootProps["onSelectionChange"] = (key) => onValueChange(String(key));
47
+ }
48
+ if (process.env.NODE_ENV !== "production") {
49
+ devValidate("Tabs", TabsManifest.Tabs.props, { orientation });
50
+ }
51
+ const hydration = useHydrationRoot();
52
+ return /* @__PURE__ */ jsx(MotionContext.Provider, { value: motionState.mode, children: /* @__PURE__ */ jsx(TabsOrientationContext.Provider, { value: orientation, children: /* @__PURE__ */ jsx(
53
+ AriaTabs,
54
+ {
55
+ ...hydration.probeProps,
56
+ ref,
57
+ className: clsx("oprt-motion min-w-0 font-sans", className),
58
+ ...rootProps,
59
+ ...props,
60
+ ...composeMotionHandlers(motionState.props, props),
61
+ render: (domProps) => /* @__PURE__ */ jsx("div", { ...domProps, ...composeMotionHandlers(motionState.props, props) }),
62
+ children
63
+ },
64
+ hydration.key
65
+ ) }) });
66
+ }
67
+ );
68
+ Tabs.displayName = "Tabs";
69
+ var scrollingRow = [
70
+ "min-w-0 -mb-px overflow-x-auto overflow-y-hidden p-px scroll-px-4u",
71
+ "[scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
72
+ // A fade on each side that hides tabs. A mask uses only alpha, so it suits any background; the
73
+ // color of its opaque stop does not show.
74
+ "data-[fade=left]:[mask-image:linear-gradient(to_right,transparent,var(--oprt-text)_var(--oprt-space-4))]",
75
+ "data-[fade=right]:[mask-image:linear-gradient(to_left,transparent,var(--oprt-text)_var(--oprt-space-4))]",
76
+ "data-[fade=both]:[mask-image:linear-gradient(to_right,transparent,var(--oprt-text)_var(--oprt-space-4),var(--oprt-text)_calc(100%_-_var(--oprt-space-4)),transparent)]"
77
+ ];
78
+ var TabsList = forwardRef(
79
+ ({ className, children, ...props }, ref) => {
80
+ const orientation = useContext(TabsOrientationContext);
81
+ const list = useRef(null);
82
+ const setNode = useMergedRefs(list, ref);
83
+ const vertical = orientation === "vertical";
84
+ const geometry = useTabsIndicator(list, vertical);
85
+ const fade = useTabsScroll(list, vertical);
86
+ return /* @__PURE__ */ jsx(
87
+ AriaTabList,
88
+ {
89
+ ref: setNode,
90
+ className: clsx(
91
+ "oprt-motion-tabs flex gap-1u",
92
+ vertical ? "border-b border-oprt-border" : scrollingRow,
93
+ className
94
+ ),
95
+ ...props,
96
+ "data-orientation": orientation,
97
+ "data-indicator-ready": geometry ? "true" : void 0,
98
+ "data-fade": fade,
99
+ render: (domProps) => (
100
+ // The row: it draws the rail line under a horizontal list, outside what scrolls. The
101
+ // same element in both orientations, so changing orientation does not remount the tabs.
102
+ // A vertical list draws its own rail, so there the row takes no box (`contents`): the
103
+ // tablist stays its root's flex item, a stretching root runs the rail beside the whole
104
+ // panel, and placement classes on TabsList (sticky, self-start, order) still place it.
105
+ /* @__PURE__ */ jsx(
106
+ "div",
107
+ {
108
+ "data-oprt-tabs-row": "",
109
+ className: vertical ? "contents" : "min-w-0 border-b border-oprt-border",
110
+ children: /* @__PURE__ */ jsxs("div", { ...domProps, children: [
111
+ domProps.children,
112
+ geometry && /* @__PURE__ */ jsx(
113
+ "span",
114
+ {
115
+ "aria-hidden": "true",
116
+ className: "oprt-motion-indicator",
117
+ "data-immediate": geometry.immediate,
118
+ style: {
119
+ transform: vertical ? `translateY(${geometry.y}px) scaleY(${geometry.length})` : `translateX(${geometry.x}px) scaleX(${geometry.length})`
120
+ }
121
+ }
122
+ )
123
+ ] })
124
+ }
125
+ )
126
+ ),
127
+ children
128
+ }
129
+ );
130
+ }
131
+ );
132
+ TabsList.displayName = "TabsList";
133
+ var TabsTrigger = forwardRef(
134
+ ({ className, value, disabled, ...props }, ref) => /* @__PURE__ */ jsx(
135
+ AriaTab,
136
+ {
137
+ ref,
138
+ id: value,
139
+ isDisabled: disabled ?? false,
140
+ className: clsx(
141
+ // inline-flex/centering/select-none reproduce the UA <button> defaults (label centered,
142
+ // no text selection) that the div-based RAC Tab does not get for free.
143
+ "inline-flex items-center justify-center text-center select-none px-2u -mb-px border border-b-2 border-transparent text-oprt-base font-medium uppercase tracking-oprt-caps [&_code]:normal-case [&_code]:tracking-normal [&_kbd]:normal-case [&_kbd]:tracking-normal text-oprt-text-dim hover:text-oprt-text data-[selected]:text-oprt-text data-[selected]:border-b-oprt-accent data-[selected]:font-semibold data-[disabled]:opacity-50 data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus outline-hidden",
144
+ // A tab keeps its width in a row that scrolls (DC-14), never wider than the row. A label
145
+ // wider than that wraps, breaking a long token anywhere, and the tab grows taller (the DC-8
146
+ // rule): the height is a minimum, and the padding is what is left of it around one line
147
+ // and the 3 px of border, so a one-line tab is exactly 32 px.
148
+ "shrink-0 max-w-full oprt-wrap-anywhere min-h-oprt-md py-[calc((var(--oprt-height-md)-3px-var(--oprt-line-base))/2)]",
149
+ // Forced colors drop the focus border color and shadow, and would paint the transparent
150
+ // outline on every tab: only a focused tab draws one, in CanvasText (Tabs-R3).
151
+ "forced-colors:outline-0 forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-2 forced-colors:data-[focus-visible]:-outline-offset-2 forced-colors:data-[focus-visible]:outline-[CanvasText]",
152
+ className
153
+ ),
154
+ ...props
155
+ }
156
+ )
157
+ );
158
+ TabsTrigger.displayName = "TabsTrigger";
159
+ var TabsContent = forwardRef(
160
+ ({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(
161
+ AriaTabPanel,
162
+ {
163
+ ref,
164
+ id: value,
165
+ className: clsx(
166
+ // break-words, as CardBody: an unbroken token wraps inside the panel instead of widening it.
167
+ "min-w-0 break-words pt-3u border border-transparent text-oprt-md normal-case text-oprt-text data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus outline-hidden",
168
+ "forced-colors:outline-0 forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-2 forced-colors:data-[focus-visible]:-outline-offset-2 forced-colors:data-[focus-visible]:outline-[CanvasText]",
169
+ className
170
+ ),
171
+ ...props
172
+ }
173
+ )
174
+ );
175
+ TabsContent.displayName = "TabsContent";
176
+ export {
177
+ Tabs,
178
+ TabsContent,
179
+ TabsList,
180
+ TabsTrigger
181
+ };
@@ -0,0 +1,72 @@
1
+ // src/Tabs/Tabs.manifest.ts
2
+ var TabsManifest = {
3
+ Tabs: {
4
+ description: "Tabbed interface root. Underline-accent style. Wraps TabsList and one TabsContent per TabsTrigger.",
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
+ value: { type: "string" },
13
+ defaultValue: { type: "string" },
14
+ onValueChange: { type: "function" },
15
+ orientation: {
16
+ type: "enum",
17
+ values: ["horizontal", "vertical"],
18
+ default: "horizontal"
19
+ }
20
+ },
21
+ slots: {
22
+ children: { type: "TabsList | TabsContent", required: true }
23
+ },
24
+ composable_with: ["Card"],
25
+ never_inside: ["Button", "Badge"],
26
+ example: '<Tabs defaultValue="overview">\n <TabsList>\n <TabsTrigger value="overview">OVERVIEW</TabsTrigger>\n <TabsTrigger value="logs">LOGS</TabsTrigger>\n </TabsList>\n <TabsContent value="overview">System nominal.</TabsContent>\n <TabsContent value="logs">No entries.</TabsContent>\n</Tabs>'
27
+ },
28
+ TabsList: {
29
+ description: "Row (or column, when vertical) of tab triggers with a rail on the panel side. A row wider than its container stays one row and scrolls sideways inside itself: an edge fade marks each side with hidden tabs, and the selected tab is scrolled into view on open and on selection. Name it with aria-label.",
30
+ props: {},
31
+ slots: {
32
+ children: { type: "TabsTrigger", required: true }
33
+ },
34
+ composable_with: ["Tabs"],
35
+ never_inside: ["TabsTrigger", "TabsContent"],
36
+ example: '<TabsList>\n <TabsTrigger value="overview">OVERVIEW</TabsTrigger>\n <TabsTrigger value="logs">LOGS</TabsTrigger>\n</TabsList>'
37
+ },
38
+ TabsTrigger: {
39
+ description: "Single tab trigger. Active tab shows an accent underline and bolder weight. A label wider than the row wraps inside the tab, which grows taller. Must be a child of TabsList.",
40
+ props: {
41
+ value: { type: "string", required: true },
42
+ disabled: { type: "boolean", default: false }
43
+ },
44
+ composable_with: ["TabsList"],
45
+ never_inside: ["TabsContent", "Button", "Badge"],
46
+ example: '<TabsTrigger value="overview">OVERVIEW</TabsTrigger>'
47
+ },
48
+ TabsContent: {
49
+ description: "Content panel associated with a TabsTrigger by matching value. Must be a child of Tabs.",
50
+ props: {
51
+ value: { type: "string", required: true }
52
+ },
53
+ slots: {
54
+ children: { type: "any", required: false }
55
+ },
56
+ composable_with: ["Tabs"],
57
+ never_inside: ["TabsList", "TabsTrigger"],
58
+ example: '<TabsContent value="overview">System nominal.</TabsContent>'
59
+ }
60
+ };
61
+ var TabsCompositionRules = {
62
+ Tabs: {
63
+ allowed_children: ["TabsList", "TabsContent"],
64
+ children: {
65
+ TabsList: { allowed_children: ["TabsTrigger"] }
66
+ }
67
+ }
68
+ };
69
+ export {
70
+ TabsCompositionRules,
71
+ TabsManifest
72
+ };
@@ -0,0 +1,16 @@
1
+ // src/Tabs/index.ts
2
+ import {
3
+ Tabs,
4
+ TabsList,
5
+ TabsTrigger,
6
+ TabsContent
7
+ } from "./Tabs.js";
8
+ import { TabsManifest, TabsCompositionRules } from "./Tabs.manifest.js";
9
+ export {
10
+ Tabs,
11
+ TabsCompositionRules,
12
+ TabsContent,
13
+ TabsList,
14
+ TabsManifest,
15
+ TabsTrigger
16
+ };
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ // src/Tabs/useTabsIndicator.ts
4
+ import { useRef, useState } from "react";
5
+ import { createResizeObserver } from "../_internal/useElementWidth.js";
6
+ import { useIsomorphicLayoutEffect } from "../_internal/useIsomorphicLayoutEffect.js";
7
+ function useTabsIndicator(list, vertical) {
8
+ const [geometry, setGeometry] = useState();
9
+ const selectedId = useRef();
10
+ useIsomorphicLayoutEffect(() => {
11
+ const node = list.current;
12
+ if (!node) return;
13
+ let alive = true;
14
+ const measure = (selection) => {
15
+ if (!alive) return;
16
+ const selected = node.querySelector('[role="tab"][data-selected]');
17
+ if (!selected) {
18
+ setGeometry(void 0);
19
+ selectedId.current = void 0;
20
+ return;
21
+ }
22
+ const length = vertical ? selected.offsetHeight : selected.offsetWidth;
23
+ if (!length) return;
24
+ const x = selected.offsetLeft;
25
+ const y = selected.offsetTop;
26
+ const changedSelection = selectedId.current !== void 0 && selectedId.current !== selected.id;
27
+ selectedId.current = selected.id;
28
+ setGeometry((previous) => {
29
+ if (previous && previous.x === x && previous.y === y && previous.length === length)
30
+ return previous;
31
+ return { x, y, length, immediate: !selection || !changedSelection };
32
+ });
33
+ };
34
+ const resize = createResizeObserver(() => measure(false));
35
+ const observe = () => {
36
+ if (!resize) return;
37
+ resize.disconnect();
38
+ resize.observe(node);
39
+ for (const tab of node.querySelectorAll('[role="tab"]')) resize.observe(tab);
40
+ };
41
+ observe();
42
+ measure(false);
43
+ const mutation = new MutationObserver((records) => {
44
+ if (records.some((r) => r.type === "childList")) observe();
45
+ measure(records.some((r) => r.attributeName === "data-selected"));
46
+ });
47
+ mutation.observe(node, {
48
+ subtree: true,
49
+ childList: true,
50
+ attributes: true,
51
+ attributeFilter: ["data-selected", "dir"]
52
+ });
53
+ for (let ancestor = node.parentElement; ancestor; ancestor = ancestor.parentElement) {
54
+ mutation.observe(ancestor, { attributes: true, attributeFilter: ["dir"] });
55
+ }
56
+ const settle = () => measure(false);
57
+ document.fonts?.addEventListener("loadingdone", settle);
58
+ window.addEventListener("resize", settle);
59
+ return () => {
60
+ alive = false;
61
+ resize?.disconnect();
62
+ mutation.disconnect();
63
+ document.fonts?.removeEventListener("loadingdone", settle);
64
+ window.removeEventListener("resize", settle);
65
+ };
66
+ }, [list, vertical]);
67
+ return geometry;
68
+ }
69
+ export {
70
+ useTabsIndicator
71
+ };
@@ -0,0 +1,123 @@
1
+ "use client";
2
+
3
+ // src/Tabs/useTabsScroll.ts
4
+ import { useState } from "react";
5
+ import { createResizeObserver } from "../_internal/useElementWidth.js";
6
+ import { useIsomorphicLayoutEffect } from "../_internal/useIsomorphicLayoutEffect.js";
7
+ function hiddenSides(row) {
8
+ const range = row.scrollWidth - row.clientWidth;
9
+ if (range <= 1) return void 0;
10
+ const traveled = Math.abs(row.scrollLeft);
11
+ const start = traveled > 1;
12
+ const end = traveled < range - 1;
13
+ const rtl = getComputedStyle(row).direction === "rtl";
14
+ const left = rtl ? end : start;
15
+ const right = rtl ? start : end;
16
+ if (left && right) return "both";
17
+ if (left) return "left";
18
+ return right ? "right" : void 0;
19
+ }
20
+ function revealTab(row, tab) {
21
+ const style = getComputedStyle(row);
22
+ const rowBox = row.getBoundingClientRect();
23
+ const tabBox = tab.getBoundingClientRect();
24
+ const inner = rowBox.left + row.clientLeft;
25
+ const visibleLeft = inner + (Number.parseFloat(style.scrollPaddingLeft) || 0);
26
+ const visibleRight = inner + row.clientWidth - (Number.parseFloat(style.scrollPaddingRight) || 0);
27
+ let delta = 0;
28
+ if (tabBox.left < visibleLeft) delta = tabBox.left - visibleLeft;
29
+ else if (tabBox.right > visibleRight)
30
+ delta = Math.min(tabBox.right - visibleRight, tabBox.left - visibleLeft);
31
+ if (delta !== 0) row.scrollLeft += delta;
32
+ }
33
+ function useTabsScroll(list, vertical) {
34
+ const [fade, setFade] = useState();
35
+ useIsomorphicLayoutEffect(() => {
36
+ const row = list.current;
37
+ if (!row || vertical) {
38
+ setFade(void 0);
39
+ return;
40
+ }
41
+ let alive = true;
42
+ let revealed = null;
43
+ let pinned = true;
44
+ const update = () => {
45
+ if (alive) setFade(hiddenSides(row));
46
+ };
47
+ const selectedTab = () => row.querySelector('[role="tab"][data-selected]');
48
+ const reveal = () => {
49
+ const selected = selectedTab();
50
+ revealed = selected;
51
+ pinned = true;
52
+ if (selected) revealTab(row, selected);
53
+ update();
54
+ };
55
+ const unpin = () => {
56
+ pinned = false;
57
+ };
58
+ let frame = 0;
59
+ const entered = (event) => {
60
+ if (event.relatedTarget instanceof Node && row.contains(event.relatedTarget)) return;
61
+ cancelAnimationFrame(frame);
62
+ frame = requestAnimationFrame(() => {
63
+ const selected = selectedTab();
64
+ if (pinned && selected) revealTab(row, selected);
65
+ update();
66
+ });
67
+ };
68
+ const resized = () => {
69
+ const selected = selectedTab();
70
+ if (pinned && selected) revealTab(row, selected);
71
+ update();
72
+ };
73
+ const resize = createResizeObserver(resized);
74
+ const watched = /* @__PURE__ */ new Set();
75
+ const observe = () => {
76
+ if (!resize) return;
77
+ const current = /* @__PURE__ */ new Set([row, ...row.querySelectorAll('[role="tab"]')]);
78
+ for (const element of watched)
79
+ if (!current.has(element)) {
80
+ resize.unobserve(element);
81
+ watched.delete(element);
82
+ }
83
+ for (const element of current)
84
+ if (!watched.has(element)) {
85
+ resize.observe(element);
86
+ watched.add(element);
87
+ }
88
+ };
89
+ observe();
90
+ reveal();
91
+ row.addEventListener("scroll", update, { passive: true });
92
+ row.addEventListener("focusin", entered);
93
+ row.addEventListener("wheel", unpin, { passive: true });
94
+ row.addEventListener("touchmove", unpin, { passive: true });
95
+ const mutation = new MutationObserver((records) => {
96
+ if (records.some((record) => record.type === "childList")) observe();
97
+ if (selectedTab() !== revealed) reveal();
98
+ else update();
99
+ });
100
+ mutation.observe(row, {
101
+ subtree: true,
102
+ childList: true,
103
+ attributes: true,
104
+ attributeFilter: ["data-selected"]
105
+ });
106
+ return () => {
107
+ alive = false;
108
+ resize?.disconnect();
109
+ mutation.disconnect();
110
+ cancelAnimationFrame(frame);
111
+ row.removeEventListener("scroll", update);
112
+ row.removeEventListener("focusin", entered);
113
+ row.removeEventListener("wheel", unpin);
114
+ row.removeEventListener("touchmove", unpin);
115
+ };
116
+ }, [list, vertical]);
117
+ return fade;
118
+ }
119
+ export {
120
+ hiddenSides,
121
+ revealTab,
122
+ useTabsScroll
123
+ };
@@ -0,0 +1,142 @@
1
+ "use client";
2
+
3
+ // src/TagGroup/TagGroup.tsx
4
+ import {
5
+ forwardRef,
6
+ useEffect,
7
+ useImperativeHandle,
8
+ useRef
9
+ } from "react";
10
+ import { Button, TagGroup as AriaTagGroup, TagList, Tag } from "react-aria-components";
11
+ import { cva } from "class-variance-authority";
12
+ import clsx from "clsx";
13
+ import { X } from "@oprt/icons";
14
+ import { useRestoreFocusOnRemoval } from "../_internal/useRestoreFocusOnRemoval.js";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ var groupStyles = cva("min-w-0 font-sans normal-case text-oprt-base font-medium text-oprt-text");
17
+ var tagStyles = cva(
18
+ "flex min-h-oprt-sm min-w-0 max-w-full items-center gap-1u border border-oprt-border bg-oprt-surface-2 pl-1u outline-hidden data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus data-[disabled]:opacity-50"
19
+ );
20
+ var removeStyles = cva(
21
+ "flex h-oprt-sm w-oprt-sm shrink-0 items-center justify-center border border-transparent bg-transparent text-oprt-text outline-hidden hover:bg-oprt-surface-3 data-[focus-visible]:border-oprt-border-focus data-[focus-visible]:shadow-oprt-focus disabled:cursor-not-allowed"
22
+ );
23
+ var LIST_PROPS = ["aria-labelledby", "aria-describedby", "aria-details"];
24
+ var reportedNotApplied = /* @__PURE__ */ new Set();
25
+ function reportNotApplied(prop) {
26
+ if (process.env.NODE_ENV === "production" || reportedNotApplied.has(prop)) return;
27
+ reportedNotApplied.add(prop);
28
+ const suggestion = prop === "aria-label" ? "TagGroup's `label` names the tag list; an aria-label is not applied. Pass the name as `label`." : "TagGroup draws its own tags; dangerouslySetInnerHTML is not applied. Supply the tags as `items`.";
29
+ console.error(
30
+ JSON.stringify(
31
+ { component: "TagGroup", error: "PROP_NOT_APPLIED", prop, received: prop, suggestion },
32
+ null,
33
+ 2
34
+ )
35
+ );
36
+ }
37
+ var TagGroup = forwardRef(
38
+ ({
39
+ items,
40
+ label,
41
+ onRemove,
42
+ disabled = false,
43
+ emptyMessage,
44
+ returnFocusRef,
45
+ className,
46
+ "aria-label": ariaLabel,
47
+ dangerouslySetInnerHTML,
48
+ ...props
49
+ }, ref) => {
50
+ const rootRef = useRef(null);
51
+ useImperativeHandle(ref, () => rootRef.current, []);
52
+ const { onFocusCapture, onBlurCapture } = useRestoreFocusOnRemoval(
53
+ rootRef,
54
+ {
55
+ track: (element) => {
56
+ const id = element.closest('[role="row"][data-key]')?.dataset["key"];
57
+ return id === void 0 ? null : { id };
58
+ },
59
+ isGone: (_element, { id }) => !items.some((item) => item.id === id),
60
+ findTarget: () => items.length === 0 && !disabled && onRemove && rootRef.current?.contains(document.activeElement) ? returnFocusRef?.current ?? null : null
61
+ }
62
+ );
63
+ const notApplied = [
64
+ ariaLabel !== void 0 && "aria-label",
65
+ dangerouslySetInnerHTML !== void 0 && "dangerouslySetInnerHTML"
66
+ ].filter(Boolean).join(" ");
67
+ useEffect(() => {
68
+ if (process.env.NODE_ENV === "production") return;
69
+ for (const prop of notApplied ? notApplied.split(" ") : []) reportNotApplied(prop);
70
+ }, [notApplied]);
71
+ const rootProps = { ...props };
72
+ for (const name of LIST_PROPS) delete rootProps[name];
73
+ function remove(keys) {
74
+ if (disabled || !onRemove) return;
75
+ for (const key of keys) {
76
+ const item = items.find((candidate) => candidate.id === key);
77
+ if (!item || item.disabled) continue;
78
+ onRemove(item.id);
79
+ }
80
+ }
81
+ return /* @__PURE__ */ jsx(
82
+ AriaTagGroup,
83
+ {
84
+ ...props,
85
+ ref: rootRef,
86
+ "aria-label": label,
87
+ selectionMode: "none",
88
+ className: clsx(groupStyles(), className),
89
+ ...onRemove ? { onRemove: remove } : {},
90
+ render: (domProps) => /* @__PURE__ */ jsx(
91
+ "div",
92
+ {
93
+ ...rootProps,
94
+ ...domProps,
95
+ onFocusCapture: (event) => {
96
+ rootProps.onFocusCapture?.(event);
97
+ onFocusCapture(event);
98
+ },
99
+ onBlurCapture: (event) => {
100
+ rootProps.onBlurCapture?.(event);
101
+ onBlurCapture(event);
102
+ }
103
+ }
104
+ ),
105
+ children: /* @__PURE__ */ jsx(
106
+ TagList,
107
+ {
108
+ items,
109
+ dependencies: [disabled, !!onRemove],
110
+ className: "flex min-w-0 flex-wrap gap-1u outline-hidden data-[focus-visible]:shadow-oprt-focus",
111
+ renderEmptyState: () => emptyMessage ? /* @__PURE__ */ jsx("span", { className: "text-oprt-text-dim", children: emptyMessage }) : null,
112
+ children: (item) => /* @__PURE__ */ jsxs(
113
+ Tag,
114
+ {
115
+ id: item.id,
116
+ textValue: item.label,
117
+ isDisabled: disabled || !!item.disabled,
118
+ className: tagStyles(),
119
+ children: [
120
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 whitespace-pre-wrap oprt-wrap-anywhere py-0.5u pr-1u", children: item.label }),
121
+ onRemove && /* @__PURE__ */ jsx(
122
+ Button,
123
+ {
124
+ slot: "remove",
125
+ isDisabled: disabled || !!item.disabled,
126
+ className: removeStyles(),
127
+ children: /* @__PURE__ */ jsx(X, { size: 12, "aria-hidden": "true" })
128
+ }
129
+ )
130
+ ]
131
+ }
132
+ )
133
+ }
134
+ )
135
+ }
136
+ );
137
+ }
138
+ );
139
+ TagGroup.displayName = "TagGroup";
140
+ export {
141
+ TagGroup
142
+ };
@@ -0,0 +1,35 @@
1
+ // src/TagGroup/TagGroup.manifest.ts
2
+ var TagGroupManifest = {
3
+ TagGroup: {
4
+ description: "A labeled collection of tags with optional controlled removal, native keyboard navigation and authored label casing.",
5
+ props: {
6
+ items: {
7
+ type: "array",
8
+ required: true,
9
+ description: "Readonly TagItem[]: unique string id, label and optional disabled."
10
+ },
11
+ label: {
12
+ type: "string",
13
+ required: true,
14
+ description: "Accessible name for the tag collection."
15
+ },
16
+ onRemove: {
17
+ type: "function",
18
+ description: "Requests removal by id. The parent supplies the updated items; declined removals retain the item and focus."
19
+ },
20
+ disabled: { type: "boolean", default: false },
21
+ emptyMessage: { type: "string", description: "Optional message when items is empty." },
22
+ returnFocusRef: {
23
+ type: "node",
24
+ description: "RefObject<HTMLElement | null> focused when the application removes the last tag while focus is in the group, on the next render or after a server round trip. An operator who has moved on keeps their focus."
25
+ },
26
+ className: { type: "string" }
27
+ },
28
+ composable_with: ["PropertyFilter"],
29
+ never_inside: ["Button"],
30
+ example: '<TagGroup label="Active filters" items={filters} onRemove={(id) => setFilters(filters.filter((item) => item.id !== id))} returnFocusRef={addButtonRef} />'
31
+ }
32
+ };
33
+ export {
34
+ TagGroupManifest
35
+ };
@@ -0,0 +1,7 @@
1
+ // src/TagGroup/index.ts
2
+ import { TagGroup } from "./TagGroup.js";
3
+ import { TagGroupManifest } from "./TagGroup.manifest.js";
4
+ export {
5
+ TagGroup,
6
+ TagGroupManifest
7
+ };