@ecopages/radiant-ui 0.1.0-beta.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 (254) hide show
  1. package/README.md +70 -0
  2. package/dist/components/ui/alert/alert.css +27 -0
  3. package/dist/components/ui/alert/alert.d.ts +4 -0
  4. package/dist/components/ui/alert/alert.script.d.ts +25 -0
  5. package/dist/components/ui/alert/index.d.ts +2 -0
  6. package/dist/components/ui/alert/index.js +2 -0
  7. package/dist/components/ui/alert/index.js.map +7 -0
  8. package/dist/components/ui/breadcrumb/breadcrumb.css +41 -0
  9. package/dist/components/ui/breadcrumb/breadcrumb.d.ts +59 -0
  10. package/dist/components/ui/breadcrumb/breadcrumb.script.d.ts +40 -0
  11. package/dist/components/ui/breadcrumb/index.d.ts +2 -0
  12. package/dist/components/ui/breadcrumb/index.js +2 -0
  13. package/dist/components/ui/breadcrumb/index.js.map +7 -0
  14. package/dist/components/ui/button/button.d.ts +37 -0
  15. package/dist/components/ui/button/index.d.ts +1 -0
  16. package/dist/components/ui/button/index.js +2 -0
  17. package/dist/components/ui/button/index.js.map +7 -0
  18. package/dist/components/ui/carousel/carousel.css +170 -0
  19. package/dist/components/ui/carousel/carousel.d.ts +39 -0
  20. package/dist/components/ui/carousel/carousel.script.d.ts +113 -0
  21. package/dist/components/ui/carousel/index.d.ts +2 -0
  22. package/dist/components/ui/carousel/index.js +2 -0
  23. package/dist/components/ui/carousel/index.js.map +7 -0
  24. package/dist/components/ui/checkbox/checkbox.css +55 -0
  25. package/dist/components/ui/checkbox/checkbox.d.ts +4 -0
  26. package/dist/components/ui/checkbox/checkbox.script.d.ts +58 -0
  27. package/dist/components/ui/checkbox/index.d.ts +2 -0
  28. package/dist/components/ui/checkbox/index.js +2 -0
  29. package/dist/components/ui/checkbox/index.js.map +7 -0
  30. package/dist/components/ui/combobox/combobox.css +65 -0
  31. package/dist/components/ui/combobox/combobox.d.ts +53 -0
  32. package/dist/components/ui/combobox/combobox.script.d.ts +89 -0
  33. package/dist/components/ui/combobox/index.d.ts +2 -0
  34. package/dist/components/ui/combobox/index.js +2 -0
  35. package/dist/components/ui/combobox/index.js.map +7 -0
  36. package/dist/components/ui/dialog/dialog-registry.d.ts +34 -0
  37. package/dist/components/ui/dialog/dialog-registry.test.d.ts +1 -0
  38. package/dist/components/ui/dialog/dialog-types.d.ts +55 -0
  39. package/dist/components/ui/dialog/dialog.css +53 -0
  40. package/dist/components/ui/dialog/dialog.d.ts +36 -0
  41. package/dist/components/ui/dialog/dialog.script.d.ts +61 -0
  42. package/dist/components/ui/dialog/index.d.ts +4 -0
  43. package/dist/components/ui/dialog/index.js +2 -0
  44. package/dist/components/ui/dialog/index.js.map +7 -0
  45. package/dist/components/ui/disclosure/disclosure-group.script.d.ts +31 -0
  46. package/dist/components/ui/disclosure/disclosure.css +105 -0
  47. package/dist/components/ui/disclosure/disclosure.d.ts +35 -0
  48. package/dist/components/ui/disclosure/disclosure.script.d.ts +38 -0
  49. package/dist/components/ui/disclosure/index.d.ts +3 -0
  50. package/dist/components/ui/disclosure/index.js +2 -0
  51. package/dist/components/ui/disclosure/index.js.map +7 -0
  52. package/dist/components/ui/feed/feed.css +14 -0
  53. package/dist/components/ui/feed/feed.d.ts +12 -0
  54. package/dist/components/ui/feed/feed.script.d.ts +22 -0
  55. package/dist/components/ui/feed/index.d.ts +2 -0
  56. package/dist/components/ui/feed/index.js +2 -0
  57. package/dist/components/ui/feed/index.js.map +7 -0
  58. package/dist/components/ui/field/field-context.d.ts +20 -0
  59. package/dist/components/ui/field/field-description.d.ts +8 -0
  60. package/dist/components/ui/field/field-error.d.ts +9 -0
  61. package/dist/components/ui/field/field-ssr-hydration.test.d.ts +1 -0
  62. package/dist/components/ui/field/field.css +26 -0
  63. package/dist/components/ui/field/field.d.ts +4 -0
  64. package/dist/components/ui/field/field.script.d.ts +68 -0
  65. package/dist/components/ui/field/index.d.ts +5 -0
  66. package/dist/components/ui/field/index.js +2 -0
  67. package/dist/components/ui/field/index.js.map +7 -0
  68. package/dist/components/ui/form/control-protocol.d.ts +19 -0
  69. package/dist/components/ui/form/form-context.d.ts +27 -0
  70. package/dist/components/ui/form/form-field.test.d.ts +3 -0
  71. package/dist/components/ui/form/form-store.d.ts +53 -0
  72. package/dist/components/ui/form/form-store.test.d.ts +1 -0
  73. package/dist/components/ui/form/form.css +12 -0
  74. package/dist/components/ui/form/form.d.ts +5 -0
  75. package/dist/components/ui/form/form.script.d.ts +67 -0
  76. package/dist/components/ui/form/index.d.ts +7 -0
  77. package/dist/components/ui/form/index.js +2 -0
  78. package/dist/components/ui/form/index.js.map +7 -0
  79. package/dist/components/ui/form/resolvers.d.ts +3 -0
  80. package/dist/components/ui/form/types.d.ts +72 -0
  81. package/dist/components/ui/grid/grid.css +47 -0
  82. package/dist/components/ui/grid/grid.d.ts +7 -0
  83. package/dist/components/ui/grid/grid.script.d.ts +26 -0
  84. package/dist/components/ui/grid/index.d.ts +2 -0
  85. package/dist/components/ui/grid/index.js +2 -0
  86. package/dist/components/ui/grid/index.js.map +7 -0
  87. package/dist/components/ui/input/index.d.ts +1 -0
  88. package/dist/components/ui/input/index.js +2 -0
  89. package/dist/components/ui/input/index.js.map +7 -0
  90. package/dist/components/ui/input/input.css +32 -0
  91. package/dist/components/ui/input/input.d.ts +23 -0
  92. package/dist/components/ui/label/index.d.ts +1 -0
  93. package/dist/components/ui/label/index.js +2 -0
  94. package/dist/components/ui/label/index.js.map +7 -0
  95. package/dist/components/ui/label/label.css +8 -0
  96. package/dist/components/ui/label/label.d.ts +9 -0
  97. package/dist/components/ui/listbox/index.d.ts +2 -0
  98. package/dist/components/ui/listbox/index.js +2 -0
  99. package/dist/components/ui/listbox/index.js.map +7 -0
  100. package/dist/components/ui/listbox/listbox.css +23 -0
  101. package/dist/components/ui/listbox/listbox.d.ts +12 -0
  102. package/dist/components/ui/listbox/listbox.script.d.ts +40 -0
  103. package/dist/components/ui/menu-button/index.d.ts +2 -0
  104. package/dist/components/ui/menu-button/index.js +2 -0
  105. package/dist/components/ui/menu-button/index.js.map +7 -0
  106. package/dist/components/ui/menu-button/menu-button.css +43 -0
  107. package/dist/components/ui/menu-button/menu-button.d.ts +13 -0
  108. package/dist/components/ui/menu-button/menu-button.script.d.ts +64 -0
  109. package/dist/components/ui/menubar/index.d.ts +2 -0
  110. package/dist/components/ui/menubar/index.js +2 -0
  111. package/dist/components/ui/menubar/index.js.map +7 -0
  112. package/dist/components/ui/menubar/menubar.css +45 -0
  113. package/dist/components/ui/menubar/menubar.d.ts +16 -0
  114. package/dist/components/ui/menubar/menubar.script.d.ts +52 -0
  115. package/dist/components/ui/meter/index.d.ts +2 -0
  116. package/dist/components/ui/meter/index.js +2 -0
  117. package/dist/components/ui/meter/index.js.map +7 -0
  118. package/dist/components/ui/meter/meter.css +17 -0
  119. package/dist/components/ui/meter/meter.d.ts +4 -0
  120. package/dist/components/ui/meter/meter.script.d.ts +33 -0
  121. package/dist/components/ui/navigation-menu/index.d.ts +2 -0
  122. package/dist/components/ui/navigation-menu/index.js +2 -0
  123. package/dist/components/ui/navigation-menu/index.js.map +7 -0
  124. package/dist/components/ui/navigation-menu/navigation-menu.css +90 -0
  125. package/dist/components/ui/navigation-menu/navigation-menu.d.ts +28 -0
  126. package/dist/components/ui/navigation-menu/navigation-menu.script.d.ts +49 -0
  127. package/dist/components/ui/radio-group/index.d.ts +2 -0
  128. package/dist/components/ui/radio-group/index.js +2 -0
  129. package/dist/components/ui/radio-group/index.js.map +7 -0
  130. package/dist/components/ui/radio-group/radio-group.css +44 -0
  131. package/dist/components/ui/radio-group/radio-group.d.ts +12 -0
  132. package/dist/components/ui/radio-group/radio-group.script.d.ts +52 -0
  133. package/dist/components/ui/sidebar/index.d.ts +3 -0
  134. package/dist/components/ui/sidebar/index.js +2 -0
  135. package/dist/components/ui/sidebar/index.js.map +7 -0
  136. package/dist/components/ui/sidebar/sidebar-trigger.script.d.ts +53 -0
  137. package/dist/components/ui/sidebar/sidebar.css +362 -0
  138. package/dist/components/ui/sidebar/sidebar.d.ts +141 -0
  139. package/dist/components/ui/sidebar/sidebar.script.d.ts +140 -0
  140. package/dist/components/ui/sidebar/sidebar.test.d.ts +1 -0
  141. package/dist/components/ui/slider/index.d.ts +2 -0
  142. package/dist/components/ui/slider/index.js +2 -0
  143. package/dist/components/ui/slider/index.js.map +7 -0
  144. package/dist/components/ui/slider/slider.css +51 -0
  145. package/dist/components/ui/slider/slider.d.ts +7 -0
  146. package/dist/components/ui/slider/slider.script.d.ts +102 -0
  147. package/dist/components/ui/spinbutton/index.d.ts +2 -0
  148. package/dist/components/ui/spinbutton/index.js +2 -0
  149. package/dist/components/ui/spinbutton/index.js.map +7 -0
  150. package/dist/components/ui/spinbutton/spinbutton.css +19 -0
  151. package/dist/components/ui/spinbutton/spinbutton.d.ts +4 -0
  152. package/dist/components/ui/spinbutton/spinbutton.script.d.ts +64 -0
  153. package/dist/components/ui/switch/index.d.ts +2 -0
  154. package/dist/components/ui/switch/index.js +2 -0
  155. package/dist/components/ui/switch/index.js.map +7 -0
  156. package/dist/components/ui/switch/switch.css +47 -0
  157. package/dist/components/ui/switch/switch.d.ts +4 -0
  158. package/dist/components/ui/switch/switch.script.d.ts +51 -0
  159. package/dist/components/ui/tabs/index.d.ts +2 -0
  160. package/dist/components/ui/tabs/index.js +2 -0
  161. package/dist/components/ui/tabs/index.js.map +7 -0
  162. package/dist/components/ui/tabs/tabs.css +28 -0
  163. package/dist/components/ui/tabs/tabs.d.ts +12 -0
  164. package/dist/components/ui/tabs/tabs.script.d.ts +54 -0
  165. package/dist/components/ui/textarea/index.d.ts +1 -0
  166. package/dist/components/ui/textarea/index.js +2 -0
  167. package/dist/components/ui/textarea/index.js.map +7 -0
  168. package/dist/components/ui/textarea/textarea.css +33 -0
  169. package/dist/components/ui/textarea/textarea.d.ts +23 -0
  170. package/dist/components/ui/toast/index.d.ts +5 -0
  171. package/dist/components/ui/toast/index.js +2 -0
  172. package/dist/components/ui/toast/index.js.map +7 -0
  173. package/dist/components/ui/toast/toast-api.d.ts +29 -0
  174. package/dist/components/ui/toast/toast-context.d.ts +59 -0
  175. package/dist/components/ui/toast/toast-state.d.ts +25 -0
  176. package/dist/components/ui/toast/toast.css +315 -0
  177. package/dist/components/ui/toast/toast.d.ts +8 -0
  178. package/dist/components/ui/toast/toast.script.d.ts +91 -0
  179. package/dist/components/ui/toast/toast.test.d.ts +2 -0
  180. package/dist/components/ui/toast/toaster.script.d.ts +79 -0
  181. package/dist/components/ui/toolbar/index.d.ts +2 -0
  182. package/dist/components/ui/toolbar/index.js +2 -0
  183. package/dist/components/ui/toolbar/index.js.map +7 -0
  184. package/dist/components/ui/toolbar/toolbar.css +16 -0
  185. package/dist/components/ui/toolbar/toolbar.d.ts +4 -0
  186. package/dist/components/ui/toolbar/toolbar.script.d.ts +30 -0
  187. package/dist/components/ui/tooltip/index.d.ts +2 -0
  188. package/dist/components/ui/tooltip/index.js +2 -0
  189. package/dist/components/ui/tooltip/index.js.map +7 -0
  190. package/dist/components/ui/tooltip/tooltip.css +25 -0
  191. package/dist/components/ui/tooltip/tooltip.d.ts +4 -0
  192. package/dist/components/ui/tooltip/tooltip.script.d.ts +70 -0
  193. package/dist/components/ui/tree/index.d.ts +2 -0
  194. package/dist/components/ui/tree/index.js +2 -0
  195. package/dist/components/ui/tree/index.js.map +7 -0
  196. package/dist/components/ui/tree/tree.css +54 -0
  197. package/dist/components/ui/tree/tree.d.ts +13 -0
  198. package/dist/components/ui/tree/tree.script.d.ts +45 -0
  199. package/dist/components/ui/treegrid/index.d.ts +2 -0
  200. package/dist/components/ui/treegrid/index.js +2 -0
  201. package/dist/components/ui/treegrid/index.js.map +7 -0
  202. package/dist/components/ui/treegrid/treegrid.css +77 -0
  203. package/dist/components/ui/treegrid/treegrid.d.ts +14 -0
  204. package/dist/components/ui/treegrid/treegrid.script.d.ts +49 -0
  205. package/dist/components/ui/window-splitter/index.d.ts +2 -0
  206. package/dist/components/ui/window-splitter/index.js +2 -0
  207. package/dist/components/ui/window-splitter/index.js.map +7 -0
  208. package/dist/components/ui/window-splitter/window-splitter.css +35 -0
  209. package/dist/components/ui/window-splitter/window-splitter.d.ts +8 -0
  210. package/dist/components/ui/window-splitter/window-splitter.script.d.ts +49 -0
  211. package/dist/index.d.ts +36 -0
  212. package/dist/index.js +2 -0
  213. package/dist/index.js.map +7 -0
  214. package/dist/lib/focusable-elements.d.ts +2 -0
  215. package/dist/lib/radiant-view.d.ts +17 -0
  216. package/dist/lib/roving-tabindex.d.ts +27 -0
  217. package/dist/styles/components/button.css +115 -0
  218. package/dist/styles/radiant-ui-core.css +7 -0
  219. package/dist/styles/radiant-ui.css +10 -0
  220. package/dist/styles/styles.css +36 -0
  221. package/dist/styles/tailwind.css +41 -0
  222. package/dist/styles/themes/aurora.css +19 -0
  223. package/dist/styles/themes/default-compact-soft.css +19 -0
  224. package/dist/styles/themes/default.css +2 -0
  225. package/dist/styles/themes/glacier.css +19 -0
  226. package/dist/styles/tokens/colors/aurora.css +73 -0
  227. package/dist/styles/tokens/colors/basalt.css +36 -0
  228. package/dist/styles/tokens/colors/glacier.css +45 -0
  229. package/dist/styles/tokens/colors/palettes.css +3 -0
  230. package/dist/styles/tokens/elevation/default.css +22 -0
  231. package/dist/styles/tokens/elevation/flat.css +22 -0
  232. package/dist/styles/tokens/motion/default.css +19 -0
  233. package/dist/styles/tokens/motion/snappy.css +19 -0
  234. package/dist/styles/tokens/presets/colors/aurora.css +126 -0
  235. package/dist/styles/tokens/presets/colors/basalt.css +126 -0
  236. package/dist/styles/tokens/presets/colors/ember.css +134 -0
  237. package/dist/styles/tokens/presets/colors/glacier.css +130 -0
  238. package/dist/styles/tokens/presets/colors/index.css +4 -0
  239. package/dist/styles/tokens/presets/radius.css +45 -0
  240. package/dist/styles/tokens/presets/spacing.css +57 -0
  241. package/dist/styles/tokens/radius/default.css +34 -0
  242. package/dist/styles/tokens/radius/edgy.css +35 -0
  243. package/dist/styles/tokens/radius/sharp.css +2 -0
  244. package/dist/styles/tokens/radius/soft.css +34 -0
  245. package/dist/styles/tokens/semantic.css +80 -0
  246. package/dist/styles/tokens/spacing/compact.css +23 -0
  247. package/dist/styles/tokens/spacing/default.css +23 -0
  248. package/dist/styles/tokens/spacing/scale-bridge.css +17 -0
  249. package/dist/styles/tokens/spacing/wide.css +23 -0
  250. package/dist/styles/tokens/system.css +36 -0
  251. package/dist/styles/tokens/typography/default.css +37 -0
  252. package/dist/styles/utilities/sheen.css +7 -0
  253. package/dist/types.d.ts +14 -0
  254. package/package.json +371 -0
@@ -0,0 +1,91 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ import { type ToastId, type ToastPosition, type ToastVariant } from './toast-context';
3
+ export type RuiToastProps = {
4
+ /** JSX list reconciliation key (not reflected to the DOM). */
5
+ key?: string | number;
6
+ toastId?: ToastId;
7
+ title?: string;
8
+ description?: string;
9
+ variant?: ToastVariant;
10
+ duration?: number;
11
+ dismissible?: boolean;
12
+ closeButton?: boolean;
13
+ actionLabel?: string;
14
+ position?: ToastPosition;
15
+ /** When true, plays the exit animation then removes the toast. */
16
+ markedDelete?: boolean;
17
+ };
18
+ type RuiToastBindings = {
19
+ title: string;
20
+ description: string;
21
+ variant: ToastVariant;
22
+ actionLabel: string;
23
+ closeButton: boolean;
24
+ };
25
+ /**
26
+ * `<rui-toast>` — a single transient notification in a toaster stack.
27
+ *
28
+ * Prefer the imperative {@link toast} API with `<rui-toaster>`; this element is
29
+ * rendered by the toaster and is not typically authored by hand.
30
+ *
31
+ * @element rui-toast
32
+ */
33
+ export declare class RuiToast extends RadiantElement<RuiToastBindings> {
34
+ toastId: string;
35
+ title: string;
36
+ description: string;
37
+ variant: ToastVariant;
38
+ duration: number;
39
+ dismissible: boolean;
40
+ closeButton: boolean;
41
+ actionLabel: string;
42
+ position: ToastPosition;
43
+ markedDelete: boolean;
44
+ toastTarget: HTMLElement;
45
+ private mounted;
46
+ private removed;
47
+ private swiping;
48
+ private swipeOut;
49
+ private swipeAxis;
50
+ private swipeOutDirection;
51
+ private held;
52
+ private timerReady;
53
+ private timeoutId;
54
+ private pointerStart;
55
+ private dragStartTime;
56
+ private exitTimer;
57
+ /**
58
+ * Wire pointer listeners, then announce mount after paint so enter CSS can run.
59
+ * On reconnect, re-announce without restarting the enter sequence.
60
+ */
61
+ connectedCallback(): void;
62
+ /**
63
+ * Clear timers on unmount. Only purge shared state when this instance was exiting —
64
+ * overflow toasts are intentionally unmounted while still queued.
65
+ */
66
+ disconnectedCallback(): void;
67
+ onContentUpdated(): void;
68
+ onPositionUpdated(): void;
69
+ /**
70
+ * Hold or release auto-dismiss (toaster-controlled on stack hover).
71
+ * Leaving hover resets lifetime to a full `duration` (does not resume leftover time).
72
+ * A no-op `setPaused(false)` still starts the timer on first paint if it never ran.
73
+ */
74
+ setPaused(paused: boolean): void;
75
+ onDurationUpdated(): void;
76
+ onMarkedDeleteUpdated(): void;
77
+ private get resolvedId();
78
+ private clearTimer;
79
+ /** Start a fresh full-duration countdown (never resumes leftover time). */
80
+ private startTimer;
81
+ private beginRemove;
82
+ private onPointerDown;
83
+ private onPointerMove;
84
+ private onPointerUp;
85
+ onCloseClick(event: Event): void;
86
+ onActionClick(event: MouseEvent): void;
87
+ private syncDomState;
88
+ private iconForVariant;
89
+ render(): import("@ecopages/jsx").JsxRenderable;
90
+ }
91
+ export {};
@@ -0,0 +1,2 @@
1
+ import './toast.script';
2
+ import './toaster.script';
@@ -0,0 +1,79 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ import { type ToastPosition, type ToastRecord } from './toast-context';
3
+ import './toast.script';
4
+ export type RuiToasterProps = {
5
+ /** Corner / edge placement. Default: `bottom-end`. */
6
+ position?: ToastPosition;
7
+ /** Default lifetime in ms. Default: `4000`. */
8
+ duration?: number;
9
+ /**
10
+ * Max toasts shown in the stack at once (older ones wait in queue).
11
+ * Default: `3`.
12
+ */
13
+ visibleToasts?: number;
14
+ /** Show a close button on every toast. */
15
+ closeButton?: boolean;
16
+ /**
17
+ * Always show the stack expanded with full gaps.
18
+ * Default `false`: toasts collapse behind the front one (Sonner-style peek)
19
+ * and expand on hover.
20
+ */
21
+ expand?: boolean;
22
+ /** Gap between toasts when expanded, in px. Default: `14`. */
23
+ gap?: number;
24
+ /** Viewport inset in px. Default: `24`. */
25
+ offset?: number;
26
+ };
27
+ type RuiToasterBindings = {
28
+ position: ToastPosition;
29
+ toasts: ToastRecord[];
30
+ };
31
+ /**
32
+ * `<rui-toaster>` — fixed viewport that renders the toast stack.
33
+ *
34
+ * @element rui-toaster
35
+ */
36
+ export declare class RuiToaster extends RadiantElement<RuiToasterBindings> {
37
+ position: ToastPosition;
38
+ duration: number;
39
+ visibleToasts: number;
40
+ closeButton: boolean;
41
+ expand: boolean;
42
+ gap: number;
43
+ offset: number;
44
+ toasts: ToastRecord[];
45
+ private expanded;
46
+ private interacting;
47
+ private unsubscribe;
48
+ private visibilityHandler;
49
+ private readonly heights;
50
+ private readonly observers;
51
+ private layoutFrame;
52
+ connectedCallback(): void;
53
+ disconnectedCallback(): void;
54
+ onConfigUpdated(): void;
55
+ onDocumentShow(event: Event): void;
56
+ onDocumentDismiss(event: Event): void;
57
+ onToastMounted(): void;
58
+ private filteredToasts;
59
+ private syncHostPosition;
60
+ private isStackExpanded;
61
+ private isPaused;
62
+ private resetLayoutState;
63
+ private syncPauseState;
64
+ /**
65
+ * Measure + layout on the next frame. Re-reads `:hover` because emptying the
66
+ * stack under the cursor can skip `pointerout`, leaving hold/expand stale.
67
+ */
68
+ private scheduleLayoutSync;
69
+ private measureToast;
70
+ private observeToasts;
71
+ private patchStackLayout;
72
+ private onPointerOver;
73
+ private onPointerOut;
74
+ private onPointerDown;
75
+ /** End interaction; collapse only when the pointer is no longer over the toaster. */
76
+ private onPointerUp;
77
+ render(): import("@ecopages/jsx").JsxRenderable;
78
+ }
79
+ export {};
@@ -0,0 +1,2 @@
1
+ export { RuiToolbar as RuiToolbarElement, type RuiToolbarProps } from './toolbar.script';
2
+ export { RuiToolbar } from './toolbar';
@@ -0,0 +1,2 @@
1
+ import{RadiantElement as E,customElement as x,onEvent as f,prop as T}from"@ecopages/radiant";function u(t,e){t.forEach((r,o)=>{r.tabIndex=o===e?0:-1})}function R(t,e){if(!t.length)return null;let r=Math.max(0,Math.min(t.length-1,e));return u(t,r),t[r]?.focus(),t[r]??null}function A(t,e){return t==="Home"?"first":t==="End"?"last":e==="horizontal"?t==="ArrowRight"?"next":t==="ArrowLeft"?"prev":null:t==="ArrowDown"?"next":t==="ArrowUp"?"prev":null}function g(t){let{items:e,current:r,key:o,orientation:s="horizontal",wrap:d=!0}=t;if(!e.length||!r)return{handled:!1};let n=e.indexOf(r);if(n<0)return{handled:!1};let l=A(o,s);if(!l)return{handled:!1};let i=n;l==="first"?i=0:l==="last"?i=e.length-1:l==="next"?i=d?(n+1)%e.length:Math.min(e.length-1,n+1):i=d?(n-1+e.length)%e.length:Math.max(0,n-1);let p=R(e,i);return p?{handled:!0,index:i,item:p}:{handled:!1}}var a=@x("rui-toolbar")class extends E{@T({type:String,defaultValue:""})label;@T({type:Boolean,reflect:!0,defaultValue:!1})exclusiveToggles;resolvedAriaLabel=this.$.label.map(e=>e||void 0);getItems(){return Array.from(this.querySelectorAll('button, a[href], input, select, [tabindex]:not([tabindex="-1"])')).filter(e=>!e.hasAttribute("disabled"))}connectedCallback(){super.connectedCallback(),queueMicrotask(()=>u(this.getItems(),0))}@f({type:"keydown",selector:"button, a[href], input, select, [tabindex]"})onKeydown(e){let r=this.getItems();g({items:r,current:e.target,key:e.key,orientation:"horizontal"}).handled&&e.preventDefault()}getToggleButtons(){return Array.from(this.querySelectorAll("button[data-toggle][aria-pressed]")).filter(e=>!e.disabled)}@f({type:"click",selector:"button[data-toggle][aria-pressed]",options:{capture:!0}})onToggleButtonClick(e){let r=e.target.closest("button[data-toggle][aria-pressed]");if(!r||r.disabled)return;e.stopImmediatePropagation();let o=r.getAttribute("aria-pressed")==="true";if(this.exclusiveToggles){for(let s of this.getToggleButtons())s.setAttribute("aria-pressed","false");o||r.setAttribute("aria-pressed","true");return}r.setAttribute("aria-pressed",String(!o))}render(){return React.createElement("div",{class:"rui-toolbar",role:"toolbar","aria-label":this.resolvedAriaLabel},React.createElement("slot",null))}};var h=Symbol.for("@ecopages/storybook-radiant.viewElement"),c=Symbol.for("@ecopages/storybook-radiant.scriptModule"),m=Symbol.for("@ecopages/storybook-radiant.scriptExport");function v(t,e){t[h]=e;let r=e[c];r&&(t[c]=r,t[m]=e[m]??e.name)}function b(t,e){let r=(o=>e(o));return v(r,t),r}import"./toolbar.css";var y=b(a,({slot:t,label:e,exclusiveToggles:r,children:o})=>React.createElement("rui-toolbar",{slot:t,label:e,exclusiveToggles:r},o));export{y as RuiToolbar,a as RuiToolbarElement};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/components/ui/toolbar/toolbar.script.tsx", "../../../../src/lib/roving-tabindex.ts", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/toolbar/toolbar.tsx"],
4
+ "sourcesContent": ["import { RadiantElement, customElement, onEvent, prop } from '@ecopages/radiant';\nimport { applyRovingTabindex, navigateRovingTabindex } from '../../../lib/roving-tabindex';\n\nexport type RuiToolbarProps = {\n\tlabel?: string;\n\t/** When true, only one toggle button in the toolbar can stay pressed at a time. */\n\texclusiveToggles?: boolean;\n};\n\ntype RuiToolbarBindings = {\n\tlabel: string;\n};\n\n/**\n * `<rui-toolbar>` \u2014 a container for grouping controls.\n *\n * Implements the APG Toolbar pattern with left/right arrow navigation among\n * focusable descendants.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/\n * @element rui-toolbar\n */\n@customElement('rui-toolbar')\nexport class RuiToolbar extends RadiantElement<RuiToolbarBindings> {\n\t@prop({ type: String, defaultValue: '' }) label: string;\n\t@prop({ type: Boolean, reflect: true, defaultValue: false }) exclusiveToggles: boolean;\n\n\tprivate readonly resolvedAriaLabel = this.$.label.map((label) => label || undefined);\n\n\tprivate getItems(): HTMLElement[] {\n\t\treturn Array.from(\n\t\t\tthis.querySelectorAll<HTMLElement>('button, a[href], input, select, [tabindex]:not([tabindex=\"-1\"])'),\n\t\t).filter((el) => !el.hasAttribute('disabled'));\n\t}\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => applyRovingTabindex(this.getItems(), 0));\n\t}\n\n\t@onEvent({ type: 'keydown', selector: 'button, a[href], input, select, [tabindex]' })\n\tonKeydown(event: KeyboardEvent): void {\n\t\tconst items = this.getItems();\n\t\tconst result = navigateRovingTabindex({\n\t\t\titems,\n\t\t\tcurrent: event.target as HTMLElement,\n\t\t\tkey: event.key,\n\t\t\torientation: 'horizontal',\n\t\t});\n\t\tif (!result.handled) return;\n\t\tevent.preventDefault();\n\t}\n\n\tprivate getToggleButtons(): HTMLButtonElement[] {\n\t\treturn Array.from(this.querySelectorAll<HTMLButtonElement>('button[data-toggle][aria-pressed]')).filter(\n\t\t\t(button) => !button.disabled,\n\t\t);\n\t}\n\n\t// Captures ahead of the button's own inline toggle handler (see `RuiButton`'s\n\t// `on:click`) so this reads pre-click state and stays the single source of truth\n\t// for `aria-pressed` when a toggle button lives in a toolbar.\n\t@onEvent({ type: 'click', selector: 'button[data-toggle][aria-pressed]', options: { capture: true } })\n\tonToggleButtonClick(event: Event): void {\n\t\tconst button = (event.target as HTMLElement).closest(\n\t\t\t'button[data-toggle][aria-pressed]',\n\t\t) as HTMLButtonElement | null;\n\t\tif (!button || button.disabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tevent.stopImmediatePropagation();\n\n\t\tconst wasPressed = button.getAttribute('aria-pressed') === 'true';\n\n\t\tif (this.exclusiveToggles) {\n\t\t\tfor (const toggle of this.getToggleButtons()) {\n\t\t\t\ttoggle.setAttribute('aria-pressed', 'false');\n\t\t\t}\n\n\t\t\tif (!wasPressed) {\n\t\t\t\tbutton.setAttribute('aria-pressed', 'true');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tbutton.setAttribute('aria-pressed', String(!wasPressed));\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<div class=\"rui-toolbar\" role=\"toolbar\" aria-label={this.resolvedAriaLabel}>\n\t\t\t\t<slot></slot>\n\t\t\t</div>\n\t\t);\n\t}\n}\n", "export type RovingOrientation = 'horizontal' | 'vertical';\n\nexport type RovingNavResult = { handled: false } | { handled: true; index: number; item: HTMLElement };\n\n/**\n * Apply the APG roving-tabindex pattern: one item is tabbable, the rest are `-1`.\n */\nexport function applyRovingTabindex(items: HTMLElement[], activeIndex: number): void {\n\titems.forEach((item, index) => {\n\t\titem.tabIndex = index === activeIndex ? 0 : -1;\n\t});\n}\n\n/**\n * Focus `items[index]` and update tabindexes. Returns the focused item, or null.\n */\nexport function focusRovingItem(items: HTMLElement[], index: number): HTMLElement | null {\n\tif (!items.length) return null;\n\tconst next = Math.max(0, Math.min(items.length - 1, index));\n\tapplyRovingTabindex(items, next);\n\titems[next]?.focus();\n\treturn items[next] ?? null;\n}\n\nfunction deltaForKey(key: string, orientation: RovingOrientation): 'prev' | 'next' | 'first' | 'last' | null {\n\tif (key === 'Home') return 'first';\n\tif (key === 'End') return 'last';\n\n\tif (orientation === 'horizontal') {\n\t\tif (key === 'ArrowRight') return 'next';\n\t\tif (key === 'ArrowLeft') return 'prev';\n\t\treturn null;\n\t}\n\n\tif (key === 'ArrowDown') return 'next';\n\tif (key === 'ArrowUp') return 'prev';\n\treturn null;\n}\n\n/**\n * Handle arrow / Home / End navigation for a flat roving-tabindex collection.\n * Does not activate the item \u2014 callers decide whether to select on move.\n */\nexport function navigateRovingTabindex(options: {\n\titems: HTMLElement[];\n\tcurrent: HTMLElement | null;\n\tkey: string;\n\torientation?: RovingOrientation;\n\twrap?: boolean;\n}): RovingNavResult {\n\tconst { items, current, key, orientation = 'horizontal', wrap = true } = options;\n\tif (!items.length || !current) return { handled: false };\n\n\tconst index = items.indexOf(current);\n\tif (index < 0) return { handled: false };\n\n\tconst delta = deltaForKey(key, orientation);\n\tif (!delta) return { handled: false };\n\n\tlet next = index;\n\tif (delta === 'first') next = 0;\n\telse if (delta === 'last') next = items.length - 1;\n\telse if (delta === 'next') next = wrap ? (index + 1) % items.length : Math.min(items.length - 1, index + 1);\n\telse next = wrap ? (index - 1 + items.length) % items.length : Math.max(0, index - 1);\n\n\tconst item = focusRovingItem(items, next);\n\tif (!item) return { handled: false };\n\treturn { handled: true, index: next, item };\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { WithChildren, RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiToolbarProps } from './toolbar.script';\nimport { RuiToolbar as RuiToolbarElement } from './toolbar.script';\nimport './toolbar.css';\n\nexport const RuiToolbar = defineRadiantView(\n\tRuiToolbarElement,\n\t({ slot, label, exclusiveToggles, children }: WithChildren<RuiToolbarProps & RadiantSlotProps>) => (\n\t\t<rui-toolbar slot={slot} label={label} exclusiveToggles={exclusiveToggles}>\n\t\t\t{children}\n\t\t</rui-toolbar>\n\t),\n);\n"],
5
+ "mappings": "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",
6
+ "names": ["RadiantElement", "customElement", "onEvent", "prop", "applyRovingTabindex", "items", "activeIndex", "item", "index", "focusRovingItem", "next", "deltaForKey", "key", "orientation", "navigateRovingTabindex", "options", "current", "wrap", "delta", "RuiToolbar", "customElement", "RadiantElement", "prop", "label", "el", "applyRovingTabindex", "onEvent", "event", "items", "navigateRovingTabindex", "button", "wasPressed", "toggle", "RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "RuiToolbar", "defineRadiantView", "slot", "label", "exclusiveToggles", "children"]
7
+ }
@@ -0,0 +1,16 @@
1
+ @reference '../../../styles/radiant-ui.css';
2
+
3
+ rui-toolbar {
4
+ @apply block;
5
+ }
6
+
7
+ @layer components {
8
+ .rui-toolbar {
9
+ @apply inline-flex items-center gap-1 rounded-container border border-border bg-surface p-1;
10
+ }
11
+
12
+ .rui-toolbar .rui-button--ghost[aria-pressed='true'],
13
+ .rui-toolbar .rui-button--outline[aria-pressed='true'] {
14
+ @apply bg-background font-medium text-on-background shadow-sm;
15
+ }
16
+ }
@@ -0,0 +1,4 @@
1
+ import type { WithChildren, RadiantSlotProps } from '../../../types';
2
+ import type { RuiToolbarProps } from './toolbar.script';
3
+ import './toolbar.css';
4
+ export declare const RuiToolbar: import("../../../lib/radiant-view").RadiantViewComponent<WithChildren<RuiToolbarProps & RadiantSlotProps>>;
@@ -0,0 +1,30 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ export type RuiToolbarProps = {
3
+ label?: string;
4
+ /** When true, only one toggle button in the toolbar can stay pressed at a time. */
5
+ exclusiveToggles?: boolean;
6
+ };
7
+ type RuiToolbarBindings = {
8
+ label: string;
9
+ };
10
+ /**
11
+ * `<rui-toolbar>` — a container for grouping controls.
12
+ *
13
+ * Implements the APG Toolbar pattern with left/right arrow navigation among
14
+ * focusable descendants.
15
+ *
16
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/
17
+ * @element rui-toolbar
18
+ */
19
+ export declare class RuiToolbar extends RadiantElement<RuiToolbarBindings> {
20
+ label: string;
21
+ exclusiveToggles: boolean;
22
+ private readonly resolvedAriaLabel;
23
+ private getItems;
24
+ connectedCallback(): void;
25
+ onKeydown(event: KeyboardEvent): void;
26
+ private getToggleButtons;
27
+ onToggleButtonClick(event: Event): void;
28
+ render(): import("@ecopages/jsx").JsxRenderable;
29
+ }
30
+ export {};
@@ -0,0 +1,2 @@
1
+ export { RuiTooltip as RuiTooltipElement, type RuiTooltipProps } from './tooltip.script';
2
+ export { RuiTooltip } from './tooltip';
@@ -0,0 +1,2 @@
1
+ import{RadiantElement as c,bound as n,customElement as h,onEvent as s,onUpdated as T,prop as a,query as m}from"@ecopages/radiant";import{autoUpdate as g,computePosition as R,flip as v,offset as f,shift as E}from"@floating-ui/dom";var r=@h("rui-tooltip")class extends c{@a({type:String,defaultValue:""})content;@a({type:String,defaultValue:"top"})placement;@a({type:Number,defaultValue:200})delay;@m({ref:"tooltip"})tooltipTarget;open=!1;showTimer=null;hideTimer=null;cleanup=null;tooltipId=`rui-tooltip-${Math.random().toString(36).slice(2,9)}`;describedEl=null;connectedCallback(){super.connectedCallback(),this.addEventListener("pointerenter",this.onPointerEnter),this.addEventListener("pointerleave",this.onPointerLeave),queueMicrotask(()=>this.wireTrigger())}disconnectedCallback(){this.clearTimers(),this.teardownFloating(),this.removeEventListener("pointerenter",this.onPointerEnter),this.removeEventListener("pointerleave",this.onPointerLeave),this.describedEl?.removeAttribute("aria-describedby"),super.disconnectedCallback()}clearTimers(){this.showTimer&&clearTimeout(this.showTimer),this.hideTimer&&clearTimeout(this.hideTimer),this.showTimer=null,this.hideTimer=null}teardownFloating(){this.cleanup?.(),this.cleanup=null}getAnchor(){return this.querySelector('button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])')??this}wireTrigger(){this.describedEl?.removeAttribute("aria-describedby"),this.describedEl=this.getAnchor(),this.describedEl.setAttribute("aria-describedby",this.tooltipId)}@n updatePosition(){if(!this.tooltipTarget||!this.open)return;let t=this.getAnchor();R(t,this.tooltipTarget,{placement:this.placement,middleware:[f(8),v(),E({padding:8})]}).then(({x:e,y:o})=>{Object.assign(this.tooltipTarget.style,{left:`${e}px`,top:`${o}px`})})}@T(["content","placement"])onDescribedPropsUpdated(){this.wireTrigger(),this.open&&this.updatePosition()}setOpen(t){if(this.clearTimers(),this.open=t,!!this.tooltipTarget)if(this.tooltipTarget.hidden=!t,t){this.teardownFloating();let e=this.getAnchor();this.cleanup=g(e,this.tooltipTarget,this.updatePosition),this.updatePosition()}else this.teardownFloating()}scheduleShow(){if(this.clearTimers(),this.delay<=0){this.setOpen(!0);return}this.showTimer=setTimeout(()=>this.setOpen(!0),this.delay)}scheduleHide(){this.clearTimers(),this.hideTimer=setTimeout(()=>this.setOpen(!1),80)}@s({document:!0,type:"keydown"})onDocumentKeydown(t){!this.open||t.key!=="Escape"||this.setOpen(!1)}@s({type:"focusin",selector:".rui-tooltip__trigger"})onFocusIn(){this.scheduleShow()}@s({type:"focusout",selector:".rui-tooltip__trigger"})onFocusOut(t){let e=t.relatedTarget;e&&this.contains(e)||this.scheduleHide()}@n onPointerEnter(){this.scheduleShow()}@n onPointerLeave(){this.scheduleHide()}render(){return React.createElement("span",{class:"rui-tooltip"},React.createElement("span",{class:"rui-tooltip__trigger"},React.createElement("slot",null)),React.createElement("span",{"data-ref":"tooltip",id:this.tooltipId,class:"rui-tooltip__content",role:"tooltip",hidden:!0},this.$.content))}};var y=Symbol.for("@ecopages/storybook-radiant.viewElement"),l=Symbol.for("@ecopages/storybook-radiant.scriptModule"),p=Symbol.for("@ecopages/storybook-radiant.scriptExport");function b(i,t){i[y]=t;let e=t[l];e&&(i[l]=e,i[p]=t[p]??t.name)}function d(i,t){let e=(o=>t(o));return b(e,i),e}import"./tooltip.css";var A=d(r,({slot:i,content:t,placement:e,delay:o,children:u})=>React.createElement("rui-tooltip",{slot:i,content:t,placement:e,delay:o},u));export{A as RuiTooltip,r as RuiTooltipElement};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/components/ui/tooltip/tooltip.script.tsx", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/tooltip/tooltip.tsx"],
4
+ "sourcesContent": ["import { RadiantElement, bound, customElement, onEvent, onUpdated, prop, query } from '@ecopages/radiant';\nimport { type Placement, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';\n\nexport type RuiTooltipProps = {\n\t/** Accessible description shown in the tooltip. */\n\tcontent?: string;\n\t/** Floating-ui placement. Default: `top`. */\n\tplacement?: Placement;\n\t/** Delay in ms before showing on hover/focus. Default: `200`. */\n\tdelay?: number;\n};\n\ntype RuiTooltipBindings = {\n\tcontent: string;\n};\n\n/**\n * `<rui-tooltip>` \u2014 a popup that describes a trigger on hover or focus.\n *\n * Implements the WAI-ARIA APG Tooltip pattern: the popup has `role=\"tooltip\"`\n * and the focusable trigger references it with `aria-describedby`. The tooltip\n * itself never receives focus.\n *\n * Event wiring:\n * - Bubbling events (`focusin` / `focusout`, document `keydown`) use `@onEvent`.\n * - `pointerenter` / `pointerleave` are bound directly on the host \u2014 they do not\n * bubble, so `@onEvent` delegation cannot see them on the slotted trigger.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/\n *\n * @element rui-tooltip\n * @slot - The trigger element (typically a button or focusable control).\n */\n@customElement('rui-tooltip')\nexport class RuiTooltip extends RadiantElement<RuiTooltipBindings> {\n\t@prop({ type: String, defaultValue: '' }) content: string;\n\t@prop({ type: String, defaultValue: 'top' }) placement: Placement;\n\t@prop({ type: Number, defaultValue: 200 }) delay: number;\n\n\t@query({ ref: 'tooltip' }) tooltipTarget: HTMLElement;\n\n\t/**\n\t * Plain (non-reactive) field, not a JSX binding target: `hidden` is toggled\n\t * imperatively in `setOpen()` rather than bound, since it flips on every\n\t * hover/focus in/out \u2014 high-frequency enough that a plain synchronous DOM\n\t * write is preferable to routing it through the reactive binding system.\n\t */\n\tprivate open = false;\n\n\tprivate showTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate hideTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate cleanup: ReturnType<typeof autoUpdate> | null = null;\n\tprivate tooltipId = `rui-tooltip-${Math.random().toString(36).slice(2, 9)}`;\n\tprivate describedEl: HTMLElement | null = null;\n\n\t/**\n\t * Pointer enter/leave must be host listeners: those events do not bubble, and\n\t * `@onEvent` only supports delegated (bubbling) listeners.\n\t */\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tthis.addEventListener('pointerenter', this.onPointerEnter);\n\t\tthis.addEventListener('pointerleave', this.onPointerLeave);\n\t\tqueueMicrotask(() => this.wireTrigger());\n\t}\n\n\toverride disconnectedCallback(): void {\n\t\tthis.clearTimers();\n\t\tthis.teardownFloating();\n\t\tthis.removeEventListener('pointerenter', this.onPointerEnter);\n\t\tthis.removeEventListener('pointerleave', this.onPointerLeave);\n\t\tthis.describedEl?.removeAttribute('aria-describedby');\n\t\tsuper.disconnectedCallback();\n\t}\n\n\tprivate clearTimers(): void {\n\t\tif (this.showTimer) clearTimeout(this.showTimer);\n\t\tif (this.hideTimer) clearTimeout(this.hideTimer);\n\t\tthis.showTimer = null;\n\t\tthis.hideTimer = null;\n\t}\n\n\tprivate teardownFloating(): void {\n\t\tthis.cleanup?.();\n\t\tthis.cleanup = null;\n\t}\n\n\tprivate getAnchor(): HTMLElement {\n\t\treturn (\n\t\t\t(this.querySelector(\n\t\t\t\t'button, a[href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n\t\t\t) as HTMLElement | null) ?? this\n\t\t);\n\t}\n\n\tprivate wireTrigger(): void {\n\t\tthis.describedEl?.removeAttribute('aria-describedby');\n\t\tthis.describedEl = this.getAnchor();\n\t\tthis.describedEl.setAttribute('aria-describedby', this.tooltipId);\n\t}\n\n\t@bound\n\tupdatePosition(): void {\n\t\tif (!this.tooltipTarget || !this.open) return;\n\t\tconst anchor = this.getAnchor();\n\t\tcomputePosition(anchor, this.tooltipTarget, {\n\t\t\tplacement: this.placement,\n\t\t\tmiddleware: [offset(8), flip(), shift({ padding: 8 })],\n\t\t}).then(({ x, y }) => {\n\t\t\tObject.assign(this.tooltipTarget.style, {\n\t\t\t\tleft: `${x}px`,\n\t\t\t\ttop: `${y}px`,\n\t\t\t});\n\t\t});\n\t}\n\n\t@onUpdated(['content', 'placement'])\n\tonDescribedPropsUpdated(): void {\n\t\tthis.wireTrigger();\n\t\tif (this.open) this.updatePosition();\n\t}\n\n\tprivate setOpen(next: boolean): void {\n\t\tthis.clearTimers();\n\t\tthis.open = next;\n\t\tif (!this.tooltipTarget) return;\n\t\tthis.tooltipTarget.hidden = !next;\n\n\t\tif (next) {\n\t\t\tthis.teardownFloating();\n\t\t\tconst anchor = this.getAnchor();\n\t\t\tthis.cleanup = autoUpdate(anchor, this.tooltipTarget, this.updatePosition);\n\t\t\tthis.updatePosition();\n\t\t} else {\n\t\t\tthis.teardownFloating();\n\t\t}\n\t}\n\n\tprivate scheduleShow(): void {\n\t\tthis.clearTimers();\n\t\tif (this.delay <= 0) {\n\t\t\tthis.setOpen(true);\n\t\t\treturn;\n\t\t}\n\t\tthis.showTimer = setTimeout(() => this.setOpen(true), this.delay);\n\t}\n\n\tprivate scheduleHide(): void {\n\t\tthis.clearTimers();\n\t\tthis.hideTimer = setTimeout(() => this.setOpen(false), 80);\n\t}\n\n\t@onEvent({ document: true, type: 'keydown' })\n\tonDocumentKeydown(event: KeyboardEvent): void {\n\t\tif (!this.open || event.key !== 'Escape') return;\n\t\tthis.setOpen(false);\n\t}\n\n\t@onEvent({ type: 'focusin', selector: '.rui-tooltip__trigger' })\n\tonFocusIn(): void {\n\t\tthis.scheduleShow();\n\t}\n\n\t@onEvent({ type: 'focusout', selector: '.rui-tooltip__trigger' })\n\tonFocusOut(event: FocusEvent): void {\n\t\tconst next = event.relatedTarget as Node | null;\n\t\tif (next && this.contains(next)) return;\n\t\tthis.scheduleHide();\n\t}\n\n\t@bound\n\tonPointerEnter(): void {\n\t\tthis.scheduleShow();\n\t}\n\n\t@bound\n\tonPointerLeave(): void {\n\t\tthis.scheduleHide();\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<span class=\"rui-tooltip\">\n\t\t\t\t<span class=\"rui-tooltip__trigger\">\n\t\t\t\t\t<slot></slot>\n\t\t\t\t</span>\n\t\t\t\t<span data-ref=\"tooltip\" id={this.tooltipId} class=\"rui-tooltip__content\" role=\"tooltip\" hidden>\n\t\t\t\t\t{this.$.content}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { WithChildren, RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiTooltipProps } from './tooltip.script';\nimport { RuiTooltip as RuiTooltipElement } from './tooltip.script';\nimport './tooltip.css';\n\nexport const RuiTooltip = defineRadiantView(\n\tRuiTooltipElement,\n\t({ slot, content, placement, delay, children }: WithChildren<RuiTooltipProps & RadiantSlotProps>) => (\n\t\t<rui-tooltip slot={slot} content={content} placement={placement} delay={delay}>\n\t\t\t{children}\n\t\t</rui-tooltip>\n\t),\n);\n"],
5
+ "mappings": "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",
6
+ "names": ["RadiantElement", "bound", "customElement", "onEvent", "onUpdated", "prop", "query", "autoUpdate", "computePosition", "flip", "offset", "shift", "RuiTooltip", "anchor", "x", "y", "next", "event", "RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "RuiTooltip", "defineRadiantView", "slot", "content", "placement", "delay", "children"]
7
+ }
@@ -0,0 +1,25 @@
1
+ @reference '../../../styles/radiant-ui.css';
2
+
3
+ rui-tooltip {
4
+ @apply inline-flex;
5
+ }
6
+
7
+ @layer components {
8
+ .rui-tooltip {
9
+ @apply relative inline-flex;
10
+ }
11
+
12
+ .rui-tooltip__trigger {
13
+ @apply inline-flex;
14
+ }
15
+
16
+ .rui-tooltip__content {
17
+ @apply absolute z-overlay max-w-xs rounded-container bg-on-background px-2 py-1 text-xs text-background shadow-md;
18
+ width: max-content;
19
+ pointer-events: none;
20
+ }
21
+
22
+ .rui-tooltip__content[hidden] {
23
+ display: none;
24
+ }
25
+ }
@@ -0,0 +1,4 @@
1
+ import type { WithChildren, RadiantSlotProps } from '../../../types';
2
+ import type { RuiTooltipProps } from './tooltip.script';
3
+ import './tooltip.css';
4
+ export declare const RuiTooltip: import("../../../lib/radiant-view").RadiantViewComponent<WithChildren<RuiTooltipProps & RadiantSlotProps>>;
@@ -0,0 +1,70 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ import { type Placement } from '@floating-ui/dom';
3
+ export type RuiTooltipProps = {
4
+ /** Accessible description shown in the tooltip. */
5
+ content?: string;
6
+ /** Floating-ui placement. Default: `top`. */
7
+ placement?: Placement;
8
+ /** Delay in ms before showing on hover/focus. Default: `200`. */
9
+ delay?: number;
10
+ };
11
+ type RuiTooltipBindings = {
12
+ content: string;
13
+ };
14
+ /**
15
+ * `<rui-tooltip>` — a popup that describes a trigger on hover or focus.
16
+ *
17
+ * Implements the WAI-ARIA APG Tooltip pattern: the popup has `role="tooltip"`
18
+ * and the focusable trigger references it with `aria-describedby`. The tooltip
19
+ * itself never receives focus.
20
+ *
21
+ * Event wiring:
22
+ * - Bubbling events (`focusin` / `focusout`, document `keydown`) use `@onEvent`.
23
+ * - `pointerenter` / `pointerleave` are bound directly on the host — they do not
24
+ * bubble, so `@onEvent` delegation cannot see them on the slotted trigger.
25
+ *
26
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/
27
+ *
28
+ * @element rui-tooltip
29
+ * @slot - The trigger element (typically a button or focusable control).
30
+ */
31
+ export declare class RuiTooltip extends RadiantElement<RuiTooltipBindings> {
32
+ content: string;
33
+ placement: Placement;
34
+ delay: number;
35
+ tooltipTarget: HTMLElement;
36
+ /**
37
+ * Plain (non-reactive) field, not a JSX binding target: `hidden` is toggled
38
+ * imperatively in `setOpen()` rather than bound, since it flips on every
39
+ * hover/focus in/out — high-frequency enough that a plain synchronous DOM
40
+ * write is preferable to routing it through the reactive binding system.
41
+ */
42
+ private open;
43
+ private showTimer;
44
+ private hideTimer;
45
+ private cleanup;
46
+ private tooltipId;
47
+ private describedEl;
48
+ /**
49
+ * Pointer enter/leave must be host listeners: those events do not bubble, and
50
+ * `@onEvent` only supports delegated (bubbling) listeners.
51
+ */
52
+ connectedCallback(): void;
53
+ disconnectedCallback(): void;
54
+ private clearTimers;
55
+ private teardownFloating;
56
+ private getAnchor;
57
+ private wireTrigger;
58
+ updatePosition(): void;
59
+ onDescribedPropsUpdated(): void;
60
+ private setOpen;
61
+ private scheduleShow;
62
+ private scheduleHide;
63
+ onDocumentKeydown(event: KeyboardEvent): void;
64
+ onFocusIn(): void;
65
+ onFocusOut(event: FocusEvent): void;
66
+ onPointerEnter(): void;
67
+ onPointerLeave(): void;
68
+ render(): import("@ecopages/jsx").JsxRenderable;
69
+ }
70
+ export {};
@@ -0,0 +1,2 @@
1
+ export { RuiTree as RuiTreeElement, type RuiTreeProps, type RuiTreeChangeDetail } from './tree.script';
2
+ export { RuiTree, type RuiTreeNode } from './tree';
@@ -0,0 +1,2 @@
1
+ import{RadiantElement as R,customElement as v,event as A,onEvent as f,onUpdated as y,prop as g}from"@ecopages/radiant";function p(n,t){n.forEach((e,r)=>{e.tabIndex=r===t?0:-1})}function d(n,t){if(!n.length)return null;let e=Math.max(0,Math.min(n.length-1,t));return p(n,e),n[e]?.focus(),n[e]??null}function x(n,t){return n==="Home"?"first":n==="End"?"last":t==="horizontal"?n==="ArrowRight"?"next":n==="ArrowLeft"?"prev":null:n==="ArrowDown"?"next":n==="ArrowUp"?"prev":null}function m(n){let{items:t,current:e,key:r,orientation:i="horizontal",wrap:o=!0}=n;if(!t.length||!e)return{handled:!1};let l=t.indexOf(e);if(l<0)return{handled:!1};let s=x(r,i);if(!s)return{handled:!1};let a=l;s==="first"?a=0:s==="last"?a=t.length-1:s==="next"?a=o?(l+1)%t.length:Math.min(t.length-1,l+1):a=o?(l-1+t.length)%t.length:Math.max(0,l-1);let c=d(t,a);return c?{handled:!0,index:a,item:c}:{handled:!1}}var u=@v("rui-tree")class extends R{@g({type:String,defaultValue:""})label;@g({type:String,reflect:!0,defaultValue:""})value;@A({name:"rui-change",bubbles:!0,composed:!0})changeEvent;resolvedAriaLabel=this.$.label.map(t=>t||void 0);getVisibleItems(){return Array.from(this.querySelectorAll('[role="treeitem"]')).filter(t=>{let e=t.parentElement;for(;e&&e!==this;){if(e.getAttribute("role")==="group"&&e.hidden)return!1;e=e.parentElement}return!0})}getParentItem(t){let e=t.closest('[role="group"]');if(!e)return null;let r=e.previousElementSibling;return r?.getAttribute("role")==="treeitem"?r:null}getSiblingItems(t){let e=this.getParentItem(t),r=e?e.nextElementSibling?.getAttribute("role")==="group"?e.nextElementSibling:null:this.querySelector('[role="tree"]')??this;return r?Array.from(r.querySelectorAll(':scope > li > [role="treeitem"], :scope > [role="treeitem"]')):[]}syncSelection(){let t=Array.from(this.querySelectorAll('[role="treeitem"]')),e=this.getVisibleItems(),r=0;t.forEach((i,o)=>{let l=i.getAttribute("data-value")||i.id||"",s=!!this.value&&l===this.value;if(i.setAttribute("aria-selected",String(s)),s){let a=e.indexOf(i);a>=0&&(r=a)}else!this.value&&o===0&&(r=0)}),p(e,r)}syncExpanded(){let t=Array.from(this.querySelectorAll('[role="treeitem"]'));for(let e of t){if(!e.hasAttribute("aria-expanded"))continue;let r=e.nextElementSibling;r?.getAttribute("role")==="group"&&(r.hidden=e.getAttribute("aria-expanded")==="false")}}connectedCallback(){super.connectedCallback(),queueMicrotask(()=>{this.syncExpanded(),this.syncSelection()})}@y("value")onValueUpdated(){this.syncSelection()}select(t){let e=t.getAttribute("data-value")||t.id||t.textContent?.trim()||"";this.value=e,this.syncSelection(),this.changeEvent.emit({value:e}),t.focus()}setExpanded(t,e){t.setAttribute("aria-expanded",String(e));let r=t.nextElementSibling;r?.getAttribute("role")==="group"&&(r.hidden=!e),this.syncSelection()}@f({selector:'[role="treeitem"]',type:"click"})onClick(t){let e=t.target.closest('[role="treeitem"]');!e||!this.contains(e)||(e.hasAttribute("aria-expanded")&&this.setExpanded(e,e.getAttribute("aria-expanded")!=="true"),this.select(e))}@f({selector:'[role="treeitem"]',type:"keydown"})onKeydown(t){let e=t.target.closest('[role="treeitem"]');if(e)switch(t.key){case"ArrowDown":case"ArrowUp":case"Home":case"End":{if(!m({items:this.getVisibleItems(),current:e,key:t.key,orientation:"vertical",wrap:!1}).handled)return;t.preventDefault();break}case"ArrowRight":{if(t.preventDefault(),e.getAttribute("aria-expanded")==="false"){this.setExpanded(e,!0);return}if(e.getAttribute("aria-expanded")==="true"){let r=this.getVisibleItems(),i=r.indexOf(e);r[i+1]&&d(r,i+1)}break}case"ArrowLeft":{if(t.preventDefault(),e.getAttribute("aria-expanded")==="true"){this.setExpanded(e,!1);return}let r=this.getParentItem(e);if(r){let i=this.getVisibleItems(),o=i.indexOf(r);o>=0&&d(i,o)}break}case"*":{t.preventDefault();for(let r of this.getSiblingItems(e))r.hasAttribute("aria-expanded")&&this.setExpanded(r,!0);break}case"Enter":{t.preventDefault(),e.hasAttribute("aria-expanded")&&this.setExpanded(e,e.getAttribute("aria-expanded")!=="true"),this.select(e);break}case" ":{t.preventDefault(),this.select(e);break}default:break}}render(){return React.createElement("ul",{class:"rui-tree",role:"tree","aria-label":this.resolvedAriaLabel,"aria-multiselectable":"false"},React.createElement("slot",null))}};var M=Symbol.for("@ecopages/storybook-radiant.viewElement"),b=Symbol.for("@ecopages/storybook-radiant.scriptModule"),T=Symbol.for("@ecopages/storybook-radiant.scriptExport");function L(n,t){n[M]=t;let e=t[b];e&&(n[b]=e,n[T]=t[T]??t.name)}function E(n,t){let e=(r=>t(r));return L(e,n),e}import"./tree.css";var h=({nodes:n})=>React.createElement(React.Fragment,null,n.map(t=>React.createElement("li",{class:"rui-tree__item"},t.children?.length?React.createElement(React.Fragment,null,React.createElement("button",{type:"button",class:"rui-tree__node",role:"treeitem","data-value":t.id,"aria-expanded":t.expanded??!1,"aria-selected":"false",tabindex:-1},t.label),React.createElement("ul",{role:"group",hidden:!(t.expanded??!1)},React.createElement(h,{nodes:t.children}))):React.createElement("button",{type:"button",class:"rui-tree__node",role:"treeitem","data-value":t.id,"aria-selected":"false",tabindex:-1},t.label)))),I=E(u,({slot:n,label:t,value:e,nodes:r})=>React.createElement("rui-tree",{slot:n,label:t,value:e},React.createElement(h,{nodes:r})));export{I as RuiTree,u as RuiTreeElement};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/components/ui/tree/tree.script.tsx", "../../../../src/lib/roving-tabindex.ts", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/tree/tree.tsx"],
4
+ "sourcesContent": ["import { RadiantElement, customElement, event, onEvent, onUpdated, prop } from '@ecopages/radiant';\nimport type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';\nimport { applyRovingTabindex, focusRovingItem, navigateRovingTabindex } from '../../../lib/roving-tabindex';\n\nexport type RuiTreeProps = {\n\tlabel?: string;\n\t/** Selected tree item `data-value` / id. */\n\tvalue?: string;\n};\n\nexport type RuiTreeChangeDetail = { value: string };\n\ntype RuiTreeBindings = {\n\tlabel: string;\n};\n\n/**\n * `<rui-tree>` \u2014 a hierarchical list of tree items.\n *\n * Implements a single-select APG Tree View with Arrow key navigation,\n * Home/End, parent navigation on ArrowLeft, and Enter/Space to select.\n * Expand/collapse is handled with ArrowRight/ArrowLeft (and `*` for siblings);\n * click or Enter on a parent also toggles expansion.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/treeview/\n * @element rui-tree\n * @fires rui-change\n */\n@customElement('rui-tree')\nexport class RuiTree extends RadiantElement<RuiTreeBindings> {\n\t@prop({ type: String, defaultValue: '' }) label: string;\n\t@prop({ type: String, reflect: true, defaultValue: '' }) value: string;\n\n\t@event({ name: 'rui-change', bubbles: true, composed: true })\n\tchangeEvent: EventEmitter<RuiTreeChangeDetail>;\n\n\tprivate readonly resolvedAriaLabel = this.$.label.map((label) => label || undefined);\n\n\tprivate getVisibleItems(): HTMLElement[] {\n\t\treturn Array.from(this.querySelectorAll<HTMLElement>('[role=\"treeitem\"]')).filter((item) => {\n\t\t\tlet node: HTMLElement | null = item.parentElement;\n\t\t\twhile (node && node !== this) {\n\t\t\t\tif (node.getAttribute('role') === 'group' && node.hidden) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tnode = node.parentElement;\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tprivate getParentItem(item: HTMLElement): HTMLElement | null {\n\t\tconst group = item.closest('[role=\"group\"]');\n\t\tif (!group) return null;\n\t\tconst parent = group.previousElementSibling as HTMLElement | null;\n\t\treturn parent?.getAttribute('role') === 'treeitem' ? parent : null;\n\t}\n\n\t/** Same-level treeitems as `item` (for `*` expand-siblings). */\n\tprivate getSiblingItems(item: HTMLElement): HTMLElement[] {\n\t\tconst parent = this.getParentItem(item);\n\t\tconst container = parent\n\t\t\t? parent.nextElementSibling?.getAttribute('role') === 'group'\n\t\t\t\t? (parent.nextElementSibling as HTMLElement)\n\t\t\t\t: null\n\t\t\t: (this.querySelector<HTMLElement>('[role=\"tree\"]') ?? this);\n\n\t\tif (!container) return [];\n\n\t\treturn Array.from(\n\t\t\tcontainer.querySelectorAll<HTMLElement>(':scope > li > [role=\"treeitem\"], :scope > [role=\"treeitem\"]'),\n\t\t);\n\t}\n\n\tprivate syncSelection(): void {\n\t\tconst items = Array.from(this.querySelectorAll<HTMLElement>('[role=\"treeitem\"]'));\n\t\tconst visible = this.getVisibleItems();\n\t\tlet activeIndex = 0;\n\n\t\titems.forEach((item, i) => {\n\t\t\tconst id = item.getAttribute('data-value') || item.id || '';\n\t\t\tconst selected = Boolean(this.value) && id === this.value;\n\t\t\titem.setAttribute('aria-selected', String(selected));\n\t\t\tif (selected) {\n\t\t\t\tconst vis = visible.indexOf(item);\n\t\t\t\tif (vis >= 0) activeIndex = vis;\n\t\t\t} else if (!this.value && i === 0) {\n\t\t\t\tactiveIndex = 0;\n\t\t\t}\n\t\t});\n\n\t\tapplyRovingTabindex(visible, activeIndex);\n\t}\n\n\tprivate syncExpanded(): void {\n\t\tconst items = Array.from(this.querySelectorAll<HTMLElement>('[role=\"treeitem\"]'));\n\t\tfor (const item of items) {\n\t\t\tif (!item.hasAttribute('aria-expanded')) continue;\n\t\t\tconst group = item.nextElementSibling as HTMLElement | null;\n\t\t\tif (group?.getAttribute('role') === 'group') {\n\t\t\t\tgroup.hidden = item.getAttribute('aria-expanded') === 'false';\n\t\t\t}\n\t\t}\n\t}\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => {\n\t\t\tthis.syncExpanded();\n\t\t\tthis.syncSelection();\n\t\t});\n\t}\n\n\t@onUpdated('value')\n\tonValueUpdated(): void {\n\t\tthis.syncSelection();\n\t}\n\n\tprivate select(item: HTMLElement): void {\n\t\tconst value = item.getAttribute('data-value') || item.id || item.textContent?.trim() || '';\n\t\tthis.value = value;\n\t\tthis.syncSelection();\n\t\tthis.changeEvent.emit({ value });\n\t\titem.focus();\n\t}\n\n\tprivate setExpanded(item: HTMLElement, expanded: boolean): void {\n\t\titem.setAttribute('aria-expanded', String(expanded));\n\t\tconst group = item.nextElementSibling as HTMLElement | null;\n\t\tif (group?.getAttribute('role') === 'group') group.hidden = !expanded;\n\t\tthis.syncSelection();\n\t}\n\n\t@onEvent({ selector: '[role=\"treeitem\"]', type: 'click' })\n\tonClick(event: Event): void {\n\t\tconst item = (event.target as HTMLElement).closest('[role=\"treeitem\"]') as HTMLElement | null;\n\t\tif (!item || !this.contains(item)) return;\n\t\tif (item.hasAttribute('aria-expanded')) {\n\t\t\tthis.setExpanded(item, item.getAttribute('aria-expanded') !== 'true');\n\t\t}\n\t\tthis.select(item);\n\t}\n\n\t@onEvent({ selector: '[role=\"treeitem\"]', type: 'keydown' })\n\tonKeydown(event: KeyboardEvent): void {\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"treeitem\"]') as HTMLElement | null;\n\t\tif (!current) return;\n\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowDown':\n\t\t\tcase 'ArrowUp':\n\t\t\tcase 'Home':\n\t\t\tcase 'End': {\n\t\t\t\tconst result = navigateRovingTabindex({\n\t\t\t\t\titems: this.getVisibleItems(),\n\t\t\t\t\tcurrent,\n\t\t\t\t\tkey: event.key,\n\t\t\t\t\torientation: 'vertical',\n\t\t\t\t\twrap: false,\n\t\t\t\t});\n\t\t\t\tif (!result.handled) return;\n\t\t\t\tevent.preventDefault();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'ArrowRight': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (current.getAttribute('aria-expanded') === 'false') {\n\t\t\t\t\tthis.setExpanded(current, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (current.getAttribute('aria-expanded') === 'true') {\n\t\t\t\t\tconst items = this.getVisibleItems();\n\t\t\t\t\tconst index = items.indexOf(current);\n\t\t\t\t\tif (items[index + 1]) focusRovingItem(items, index + 1);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'ArrowLeft': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (current.getAttribute('aria-expanded') === 'true') {\n\t\t\t\t\tthis.setExpanded(current, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst parent = this.getParentItem(current);\n\t\t\t\tif (parent) {\n\t\t\t\t\tconst nextItems = this.getVisibleItems();\n\t\t\t\t\tconst parentIndex = nextItems.indexOf(parent);\n\t\t\t\t\tif (parentIndex >= 0) focusRovingItem(nextItems, parentIndex);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase '*': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfor (const sibling of this.getSiblingItems(current)) {\n\t\t\t\t\tif (sibling.hasAttribute('aria-expanded')) {\n\t\t\t\t\t\tthis.setExpanded(sibling, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'Enter': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (current.hasAttribute('aria-expanded')) {\n\t\t\t\t\tthis.setExpanded(current, current.getAttribute('aria-expanded') !== 'true');\n\t\t\t\t}\n\t\t\t\tthis.select(current);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase ' ': {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.select(current);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<ul class=\"rui-tree\" role=\"tree\" aria-label={this.resolvedAriaLabel} aria-multiselectable=\"false\">\n\t\t\t\t<slot></slot>\n\t\t\t</ul>\n\t\t);\n\t}\n}\n", "export type RovingOrientation = 'horizontal' | 'vertical';\n\nexport type RovingNavResult = { handled: false } | { handled: true; index: number; item: HTMLElement };\n\n/**\n * Apply the APG roving-tabindex pattern: one item is tabbable, the rest are `-1`.\n */\nexport function applyRovingTabindex(items: HTMLElement[], activeIndex: number): void {\n\titems.forEach((item, index) => {\n\t\titem.tabIndex = index === activeIndex ? 0 : -1;\n\t});\n}\n\n/**\n * Focus `items[index]` and update tabindexes. Returns the focused item, or null.\n */\nexport function focusRovingItem(items: HTMLElement[], index: number): HTMLElement | null {\n\tif (!items.length) return null;\n\tconst next = Math.max(0, Math.min(items.length - 1, index));\n\tapplyRovingTabindex(items, next);\n\titems[next]?.focus();\n\treturn items[next] ?? null;\n}\n\nfunction deltaForKey(key: string, orientation: RovingOrientation): 'prev' | 'next' | 'first' | 'last' | null {\n\tif (key === 'Home') return 'first';\n\tif (key === 'End') return 'last';\n\n\tif (orientation === 'horizontal') {\n\t\tif (key === 'ArrowRight') return 'next';\n\t\tif (key === 'ArrowLeft') return 'prev';\n\t\treturn null;\n\t}\n\n\tif (key === 'ArrowDown') return 'next';\n\tif (key === 'ArrowUp') return 'prev';\n\treturn null;\n}\n\n/**\n * Handle arrow / Home / End navigation for a flat roving-tabindex collection.\n * Does not activate the item \u2014 callers decide whether to select on move.\n */\nexport function navigateRovingTabindex(options: {\n\titems: HTMLElement[];\n\tcurrent: HTMLElement | null;\n\tkey: string;\n\torientation?: RovingOrientation;\n\twrap?: boolean;\n}): RovingNavResult {\n\tconst { items, current, key, orientation = 'horizontal', wrap = true } = options;\n\tif (!items.length || !current) return { handled: false };\n\n\tconst index = items.indexOf(current);\n\tif (index < 0) return { handled: false };\n\n\tconst delta = deltaForKey(key, orientation);\n\tif (!delta) return { handled: false };\n\n\tlet next = index;\n\tif (delta === 'first') next = 0;\n\telse if (delta === 'last') next = items.length - 1;\n\telse if (delta === 'next') next = wrap ? (index + 1) % items.length : Math.min(items.length - 1, index + 1);\n\telse next = wrap ? (index - 1 + items.length) % items.length : Math.max(0, index - 1);\n\n\tconst item = focusRovingItem(items, next);\n\tif (!item) return { handled: false };\n\treturn { handled: true, index: next, item };\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\n\nconst RADIANT_VIEW_ELEMENT = Symbol.for('@ecopages/storybook-radiant.viewElement');\nconst RADIANT_SCRIPT_MODULE = Symbol.for('@ecopages/storybook-radiant.scriptModule');\nconst RADIANT_SCRIPT_EXPORT = Symbol.for('@ecopages/storybook-radiant.scriptExport');\n\nexport type RadiantViewComponent<TArgs = unknown> = ((args: TArgs) => JsxRenderable) & {\n\t[RADIANT_VIEW_ELEMENT]?: CustomElementConstructor;\n\t[RADIANT_SCRIPT_MODULE]?: string;\n\t[RADIANT_SCRIPT_EXPORT]?: string;\n};\n\nfunction linkViewToElement<TArgs>(\n\tview: RadiantViewComponent<TArgs>,\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n): void {\n\tview[RADIANT_VIEW_ELEMENT] = element;\n\n\tconst ssrModule = element[RADIANT_SCRIPT_MODULE];\n\tif (ssrModule) {\n\t\tview[RADIANT_SCRIPT_MODULE] = ssrModule;\n\t\tview[RADIANT_SCRIPT_EXPORT] = element[RADIANT_SCRIPT_EXPORT] ?? element.name;\n\t}\n}\n\n/** Link a JSX view export to its RadiantElement class (same symbols as the Storybook framework). */\nexport function defineRadiantView<TArgs>(\n\telement: CustomElementConstructor & {\n\t\t[RADIANT_SCRIPT_MODULE]?: string;\n\t\t[RADIANT_SCRIPT_EXPORT]?: string;\n\t},\n\trender: (args: TArgs) => JsxRenderable,\n): RadiantViewComponent<TArgs> {\n\tconst view = ((args: TArgs) => render(args)) as RadiantViewComponent<TArgs>;\n\tlinkViewToElement(view, element);\n\treturn view;\n}\n\n/** Re-copy SSR metadata from the linked element (e.g. after HMR). */\nexport function syncRadiantViewMetadata(view: RadiantViewComponent): void {\n\tconst element = view[RADIANT_VIEW_ELEMENT];\n\tif (element) {\n\t\tlinkViewToElement(view, element);\n\t}\n}\n", "import type { JsxRenderable } from '@ecopages/jsx';\nimport type { RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiTreeProps } from './tree.script';\nimport { RuiTree as RuiTreeElement } from './tree.script';\nimport './tree.css';\n\nexport type RuiTreeNode = {\n\tid: string;\n\tlabel: JsxRenderable;\n\tchildren?: RuiTreeNode[];\n\texpanded?: boolean;\n};\n\nconst TreeNodes = ({ nodes }: { nodes: RuiTreeNode[] }) => (\n\t<>\n\t\t{nodes.map((node) => (\n\t\t\t<li class=\"rui-tree__item\">\n\t\t\t\t{node.children?.length ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"rui-tree__node\"\n\t\t\t\t\t\t\trole=\"treeitem\"\n\t\t\t\t\t\t\tdata-value={node.id}\n\t\t\t\t\t\t\taria-expanded={node.expanded ?? false}\n\t\t\t\t\t\t\taria-selected=\"false\"\n\t\t\t\t\t\t\ttabindex={-1}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{node.label}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t<ul role=\"group\" hidden={!(node.expanded ?? false)}>\n\t\t\t\t\t\t\t<TreeNodes nodes={node.children} />\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"rui-tree__node\"\n\t\t\t\t\t\trole=\"treeitem\"\n\t\t\t\t\t\tdata-value={node.id}\n\t\t\t\t\t\taria-selected=\"false\"\n\t\t\t\t\t\ttabindex={-1}\n\t\t\t\t\t>\n\t\t\t\t\t\t{node.label}\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t</li>\n\t\t))}\n\t</>\n);\n\nexport const RuiTree = defineRadiantView(\n\tRuiTreeElement,\n\t({ slot, label, value, nodes }: RuiTreeProps & RadiantSlotProps & { nodes: RuiTreeNode[] }) => (\n\t\t<rui-tree slot={slot} label={label} value={value}>\n\t\t\t<TreeNodes nodes={nodes} />\n\t\t</rui-tree>\n\t),\n);\n"],
5
+ "mappings": "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",
6
+ "names": ["RadiantElement", "customElement", "event", "onEvent", "onUpdated", "prop", "applyRovingTabindex", "items", "activeIndex", "item", "index", "focusRovingItem", "next", "deltaForKey", "key", "orientation", "navigateRovingTabindex", "options", "current", "wrap", "delta", "RuiTree", "customElement", "RadiantElement", "prop", "event", "label", "item", "node", "group", "parent", "container", "items", "visible", "activeIndex", "i", "id", "selected", "vis", "applyRovingTabindex", "onUpdated", "value", "expanded", "onEvent", "current", "navigateRovingTabindex", "index", "focusRovingItem", "nextItems", "parentIndex", "sibling", "RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "TreeNodes", "nodes", "node", "RuiTree", "defineRadiantView", "slot", "label", "value"]
7
+ }
@@ -0,0 +1,54 @@
1
+ @reference '../../../styles/radiant-ui.css';
2
+
3
+ rui-tree {
4
+ @apply block;
5
+ }
6
+
7
+ @layer components {
8
+ .rui-tree {
9
+ @apply m-0 list-none rounded-container border border-border bg-background p-2 text-sm shadow-sm;
10
+ }
11
+
12
+ .rui-tree__item {
13
+ @apply list-none;
14
+ }
15
+
16
+ .rui-tree ul {
17
+ @apply m-0 mt-0.5 list-none border-l border-border pl-3;
18
+ }
19
+
20
+ .rui-tree__node {
21
+ @apply relative w-full cursor-pointer rounded-container border-0 bg-transparent px-2 py-1.5 text-left text-on-background;
22
+ @apply transition-colors duration-fast;
23
+ }
24
+
25
+ .rui-tree__node:hover {
26
+ @apply bg-surface;
27
+ }
28
+
29
+ .rui-tree__node[aria-selected='true'] {
30
+ @apply bg-primary-container font-medium text-on-primary-container;
31
+ }
32
+
33
+ .rui-tree__node[aria-expanded='false']::before,
34
+ .rui-tree__node[aria-expanded='true']::before {
35
+ content: '';
36
+ display: inline-block;
37
+ width: 0;
38
+ height: 0;
39
+ margin-right: 0.5rem;
40
+ border-top: 4px solid transparent;
41
+ border-bottom: 4px solid transparent;
42
+ border-left: 6px solid currentColor;
43
+ vertical-align: middle;
44
+ transition: transform 150ms ease;
45
+ }
46
+
47
+ .rui-tree__node[aria-expanded='true']::before {
48
+ transform: rotate(90deg);
49
+ }
50
+
51
+ .rui-tree__node:focus-visible {
52
+ @apply bg-surface outline-none ring-2 ring-focus-ring;
53
+ }
54
+ }
@@ -0,0 +1,13 @@
1
+ import type { JsxRenderable } from '@ecopages/jsx';
2
+ import type { RadiantSlotProps } from '../../../types';
3
+ import type { RuiTreeProps } from './tree.script';
4
+ import './tree.css';
5
+ export type RuiTreeNode = {
6
+ id: string;
7
+ label: JsxRenderable;
8
+ children?: RuiTreeNode[];
9
+ expanded?: boolean;
10
+ };
11
+ export declare const RuiTree: import("../../../lib/radiant-view").RadiantViewComponent<RuiTreeProps & RadiantSlotProps & {
12
+ nodes: RuiTreeNode[];
13
+ }>;
@@ -0,0 +1,45 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ import type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';
3
+ export type RuiTreeProps = {
4
+ label?: string;
5
+ /** Selected tree item `data-value` / id. */
6
+ value?: string;
7
+ };
8
+ export type RuiTreeChangeDetail = {
9
+ value: string;
10
+ };
11
+ type RuiTreeBindings = {
12
+ label: string;
13
+ };
14
+ /**
15
+ * `<rui-tree>` — a hierarchical list of tree items.
16
+ *
17
+ * Implements a single-select APG Tree View with Arrow key navigation,
18
+ * Home/End, parent navigation on ArrowLeft, and Enter/Space to select.
19
+ * Expand/collapse is handled with ArrowRight/ArrowLeft (and `*` for siblings);
20
+ * click or Enter on a parent also toggles expansion.
21
+ *
22
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
23
+ * @element rui-tree
24
+ * @fires rui-change
25
+ */
26
+ export declare class RuiTree extends RadiantElement<RuiTreeBindings> {
27
+ label: string;
28
+ value: string;
29
+ changeEvent: EventEmitter<RuiTreeChangeDetail>;
30
+ private readonly resolvedAriaLabel;
31
+ private getVisibleItems;
32
+ private getParentItem;
33
+ /** Same-level treeitems as `item` (for `*` expand-siblings). */
34
+ private getSiblingItems;
35
+ private syncSelection;
36
+ private syncExpanded;
37
+ connectedCallback(): void;
38
+ onValueUpdated(): void;
39
+ private select;
40
+ private setExpanded;
41
+ onClick(event: Event): void;
42
+ onKeydown(event: KeyboardEvent): void;
43
+ render(): import("@ecopages/jsx").JsxRenderable;
44
+ }
45
+ export {};
@@ -0,0 +1,2 @@
1
+ export { RuiTreegrid as RuiTreegridElement, type RuiTreegridProps, type RuiTreegridChangeDetail, } from './treegrid.script';
2
+ export { RuiTreegrid, type RuiTreegridRow } from './treegrid';