@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,53 @@
1
+ import type { FieldError, FieldRegistration, FieldValues, FormState, RegisterOptions, Resolver, ValidationMode } from './types';
2
+ export type FormStoreOptions<T extends FieldValues> = {
3
+ defaultValues?: Partial<T>;
4
+ values?: Partial<T>;
5
+ resolver?: Resolver<T>;
6
+ mode?: ValidationMode;
7
+ reValidateMode?: ValidationMode;
8
+ };
9
+ export declare class FormStore<T extends FieldValues = FieldValues> {
10
+ mode: ValidationMode;
11
+ reValidateMode: ValidationMode;
12
+ private resolver;
13
+ private readonly fields;
14
+ private listeners;
15
+ private revision;
16
+ private defaultValues;
17
+ values: T;
18
+ errors: Partial<Record<keyof T, FieldError>>;
19
+ touched: Partial<Record<keyof T, boolean>>;
20
+ dirty: Partial<Record<keyof T, boolean>>;
21
+ isSubmitting: boolean;
22
+ isSubmitted: boolean;
23
+ constructor(options?: FormStoreOptions<T>);
24
+ /** Update options without dropping registered fields. */
25
+ updateOptions(options: FormStoreOptions<T>): void;
26
+ subscribe(listener: () => void): () => void;
27
+ private notify;
28
+ getRevision(): number;
29
+ getRegisteredFieldNames(): string[];
30
+ register(registration: FieldRegistration): () => void;
31
+ updateFieldOptions(name: string, options: RegisterOptions): void;
32
+ getValues(): T;
33
+ setValue(name: keyof T, value: unknown, options?: {
34
+ shouldDirty?: boolean;
35
+ shouldTouch?: boolean;
36
+ }): void;
37
+ markTouched(name: string): void;
38
+ markDirty(name: string): void;
39
+ clearErrors(name?: string): void;
40
+ getFieldError(name: string): string | undefined;
41
+ getState(): FormState<T>;
42
+ validateField(name: string): Promise<boolean>;
43
+ validateAll(): Promise<boolean>;
44
+ shouldValidate(name: string, trigger: 'change' | 'blur'): boolean;
45
+ handleFieldChange(name: string): Promise<void>;
46
+ handleFieldBlur(name: string): Promise<void>;
47
+ /** Whether a field's validation message should be shown in the UI (RHF-style). */
48
+ shouldDisplayFieldError(name: string): boolean;
49
+ handleSubmit(onValid: (values: T) => void | Promise<void>, onInvalid?: (errors: Partial<Record<keyof T, FieldError>>) => void): Promise<void>;
50
+ reset(values?: Partial<T>): void;
51
+ private collectFieldDefaults;
52
+ private buildResolverFields;
53
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ @reference '../../../styles/radiant-ui.css';
2
+
3
+ rui-form {
4
+ @apply block;
5
+ }
6
+
7
+ @layer components {
8
+ .rui-form {
9
+ @apply flex flex-col;
10
+ gap: var(--space-stack);
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { WithChildren } from '../../../types';
2
+ import type { RuiFormProps } from './form.script';
3
+ import './form.css';
4
+ export declare const RuiForm: import("../../../lib/radiant-view").RadiantViewComponent<WithChildren<RuiFormProps>>;
5
+ export type { RuiFormSubmitDetail, RuiFormInvalidDetail } from './form.script';
@@ -0,0 +1,67 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ import type { ContextProvider } from '@ecopages/radiant/context';
3
+ import type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';
4
+ import { formContext } from './form-context';
5
+ import type { FieldValues, Resolver, ValidationMode } from './types';
6
+ export type RuiFormProps<T extends FieldValues = FieldValues> = {
7
+ defaultValues?: Partial<T>;
8
+ /** JSON default values for SSR / JSX attribute channel. */
9
+ defaultValuesData?: string;
10
+ resolver?: Resolver<T>;
11
+ mode?: ValidationMode;
12
+ reValidateMode?: ValidationMode;
13
+ };
14
+ export type RuiFormSubmitDetail<T extends FieldValues = FieldValues> = {
15
+ values: T;
16
+ };
17
+ export type RuiFormInvalidDetail<T extends FieldValues = FieldValues> = {
18
+ errors: Partial<Record<keyof T, {
19
+ message?: string;
20
+ }>>;
21
+ };
22
+ /**
23
+ * `<rui-form>` — form coordinator with RHF-like validation and field registration.
24
+ *
25
+ * Owns the {@link FormStore}, publishes field presentation via {@link formContext},
26
+ * and exposes registration / validation entry points for `<rui-field>` connectors.
27
+ *
28
+ * @element rui-form
29
+ * @fires rui-submit - Emitted when validation passes; `detail.values` holds field values.
30
+ * @fires rui-invalid - Emitted when validation fails on submit.
31
+ */
32
+ export declare class RuiForm extends RadiantElement {
33
+ formProvider: ContextProvider<typeof formContext>;
34
+ submitEvent: EventEmitter<RuiFormSubmitDetail>;
35
+ invalidEvent: EventEmitter<RuiFormInvalidDetail>;
36
+ defaultValues: FieldValues;
37
+ defaultValuesData?: string;
38
+ resolver?: Resolver<FieldValues>;
39
+ mode: ValidationMode;
40
+ reValidateMode: ValidationMode;
41
+ private store;
42
+ private unsubscribeStore?;
43
+ private readonly nativeFormId;
44
+ private lastPublishedRevision;
45
+ private lastPublishedContext;
46
+ private readonly presentationListeners;
47
+ private readonly formActions;
48
+ connectedCallback(): void;
49
+ private queryNativeForm;
50
+ /** Link submit buttons left on the host (outside the inner `<form>`) after slot projection. */
51
+ private wireOrphanSubmitButtons;
52
+ private runFormSubmit;
53
+ disconnectedCallback(): void;
54
+ private resolveMode;
55
+ private resolveReValidateMode;
56
+ private resolveDefaultValues;
57
+ private ensureStore;
58
+ private buildFieldPresentations;
59
+ private publishFormContext;
60
+ syncOptions(): void;
61
+ syncResolver(): void;
62
+ /** Save clicks must run validation even when the button is not associated with the inner `<form>`. */
63
+ onSubmitButtonClick(event: Event): void;
64
+ onNativeSubmit(event: SubmitEvent): void;
65
+ onNativeReset(): void;
66
+ render(): import("@ecopages/jsx").JsxRenderable;
67
+ }
@@ -0,0 +1,7 @@
1
+ export { RuiForm, type RuiFormSubmitDetail, type RuiFormInvalidDetail } from './form';
2
+ export { RuiForm as RuiFormElement, type RuiFormProps } from './form.script';
3
+ export { formContext, type FormContextValue, type FormFieldPresentation, type FormContextActions, } from './form-context';
4
+ export { FormStore } from './form-store';
5
+ export { createRulesResolver, runRulesResolver } from './resolvers';
6
+ export type { FieldRules, FieldValues, Resolver, ValidationMode, FormState, FieldError } from './types';
7
+ export { findFieldControl, readControlValue, writeControlValue, RUI_CONTROL_ATTR, RUI_FIELD_LABEL_ATTR, RUI_FIELD_DESCRIPTION_ATTR, RUI_FIELD_ERROR_ATTR, RUI_FIELD_MANAGED_ATTR, } from './control-protocol';
@@ -0,0 +1,2 @@
1
+ var C=Symbol.for("@ecopages/storybook-radiant.viewElement"),g=Symbol.for("@ecopages/storybook-radiant.scriptModule"),b=Symbol.for("@ecopages/storybook-radiant.scriptExport");function k(i,e){i[C]=e;let t=e[g];t&&(i[g]=t,i[b]=e[b]??e.name)}function R(i,e){let t=(r=>e(r));return k(t,i),t}import{RadiantElement as $,customElement as J,event as S,onEvent as y,onUpdated as L,prop as f}from"@ecopages/radiant";import{provideContext as W}from"@ecopages/radiant/context";import{createContext as I}from"@ecopages/radiant/context";var h=I(Symbol.for("rui-form-context"));function u(i,e){if(!(e===void 0||e===!1))return typeof e=="string"?e:typeof e=="object"&&e!==null&&"message"in e&&typeof e.message=="string"?e.message:i}function a(i){return typeof i=="object"&&i!==null&&"value"in i?i.value:i}async function H(i,e,t,r){if(!t)return;if(t.required&&(e==null||e===""||Array.isArray(e)&&e.length===0||e===!1))return u("This field is required",t.required)??"This field is required";if(typeof e=="number"||typeof e=="string"&&e!==""&&!Number.isNaN(Number(e))){let s=typeof e=="number"?e:Number(e);if(t.min!==void 0&&s<a(t.min))return u(`Minimum value is ${a(t.min)}`,t.min);if(t.max!==void 0&&s>a(t.max))return u(`Maximum value is ${a(t.max)}`,t.max)}let n=e==null?"":String(e);if(t.minLength!==void 0&&n.length<a(t.minLength))return u(`Minimum length is ${a(t.minLength)}`,t.minLength);if(t.maxLength!==void 0&&n.length>a(t.maxLength))return u(`Maximum length is ${a(t.maxLength)}`,t.maxLength);if(t.pattern!==void 0&&!a(t.pattern).test(n)){let l=t.pattern;return typeof l=="object"&&"message"in l&&typeof l.message=="string"?l.message:"Invalid format"}if(t.validate){if(typeof t.validate=="function"){let s=await t.validate(e,r);return s===!0||s===void 0?void 0:s===!1?"Invalid value":String(s)}for(let s of Object.keys(t.validate)){let l=t.validate[s],o=await l(e,r);if(o!==!0&&o!==void 0)return o===!1?"Invalid value":String(o)}}}async function F(i,e,t){let r=t??Object.keys(e.fields),n={};for(let s of r){let l=e.fields[s]?.rules,o=await H(s,i[s],l,i);o&&(n[s]={type:"validation",message:o})}return Object.keys(n).length>0?{values:{},errors:n}:{values:i,errors:{}}}function p(){return(i,e,t)=>F(i,e,t.names)}function E(i){return typeof i=="function"?i:p()}var d=class{mode;reValidateMode;resolver;fields=new Map;listeners=new Set;revision=0;defaultValues;values;errors={};touched={};dirty={};isSubmitting=!1;isSubmitted=!1;constructor(e={}){this.mode=e.mode??"onSubmit",this.reValidateMode=e.reValidateMode??"onChange",this.resolver=E(e.resolver),this.defaultValues={...e.defaultValues??{}},this.values={...this.defaultValues,...e.values??{}}}updateOptions(e){e.mode!==void 0&&(this.mode=e.mode),e.reValidateMode!==void 0&&(this.reValidateMode=e.reValidateMode),e.resolver!==void 0&&(this.resolver=E(e.resolver)),e.defaultValues!==void 0&&(this.defaultValues={...e.defaultValues})}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.revision+=1;for(let e of this.listeners)e()}getRevision(){return this.revision}getRegisteredFieldNames(){return Array.from(this.fields.keys())}register(e){let t=this.fields.get(e.name),r={...e,rules:e.rules??t?.rules,defaultValue:e.defaultValue??t?.defaultValue,getRules:e.getRules??t?.getRules};return this.fields.set(e.name,r),this.values[e.name]===void 0&&r.defaultValue!==void 0&&(this.values={...this.values,[e.name]:r.defaultValue}),r.setValue(this.values[e.name]),()=>{this.fields.get(e.name)===r&&(this.fields.delete(e.name),this.notify())}}updateFieldOptions(e,t){let r=this.fields.get(e);r&&(t.rules!==void 0&&(r.rules=t.rules),t.defaultValue!==void 0&&(r.defaultValue=t.defaultValue))}getValues(){let e={...this.values};for(let[t,r]of this.fields)e[t]=r.getValue();return e}setValue(e,t,r){this.values={...this.values,[e]:t},this.fields.get(String(e))?.setValue(t),r?.shouldDirty&&(this.dirty={...this.dirty,[e]:!0}),r?.shouldTouch&&(this.touched={...this.touched,[e]:!0}),this.notify()}markTouched(e){this.touched={...this.touched,[e]:!0},this.notify()}markDirty(e){this.dirty={...this.dirty,[e]:!0},this.notify()}clearErrors(e){if(e){let t={...this.errors};delete t[e],this.errors=t,this.notify();return}this.errors={},this.notify()}getFieldError(e){return this.errors[e]?.message}getState(){let e=this.getValues(),t=Object.keys(this.errors).length;return{values:e,errors:this.errors,touched:this.touched,dirty:this.dirty,isSubmitting:this.isSubmitting,isSubmitted:this.isSubmitted,isValid:t===0}}async validateField(e){let t=this.getValues(),n=(await this.resolver(t,{fields:this.buildResolverFields()},{names:[e]})).errors[e]?.message,s={...this.errors};return n?s[e]={type:"validation",message:n}:delete s[e],this.errors=s,this.notify(),!n}async validateAll(){let e=this.getValues(),t=await this.resolver(e,{fields:this.buildResolverFields()},{});return this.errors=t.errors,this.notify(),Object.keys(this.errors).length===0}shouldValidate(e,t){switch(this.isSubmitted?this.reValidateMode:this.mode){case"all":return!0;case"onChange":return t==="change";case"onBlur":return t==="blur";case"onTouched":return t==="blur"&&!!this.touched[e];case"onSubmit":return!1;default:return!1}}async handleFieldChange(e){let t=this.fields.get(e);if(!t)return;let r=t.getValue();this.values={...this.values,[e]:r},this.dirty={...this.dirty,[e]:!0},this.shouldValidate(e,"change")?await this.validateField(e):this.notify()}async handleFieldBlur(e){this.markTouched(e),this.shouldValidate(e,"blur")?await this.validateField(e):this.notify()}shouldDisplayFieldError(e){if(!this.errors[e]?.message)return!1;if(this.isSubmitted)return!0;switch(this.mode){case"all":return!!(this.dirty[e]||this.touched[e]);case"onChange":return!!this.dirty[e];case"onBlur":case"onTouched":return!!this.touched[e];default:return!1}}async handleSubmit(e,t){this.isSubmitting=!0,this.isSubmitted=!0;for(let n of this.fields.keys())this.touched={...this.touched,[n]:!0};this.notify();let r=await this.validateAll();if(this.isSubmitting=!1,this.notify(),r){await e(this.getValues());return}t?.(this.errors)}reset(e){let t=e??{...this.defaultValues,...this.collectFieldDefaults()};this.values={...t},this.errors={},this.touched={},this.dirty={},this.isSubmitted=!1,this.isSubmitting=!1;for(let r of this.fields.values())r.setValue(this.values[r.name]);this.notify()}collectFieldDefaults(){let e={};for(let[t,r]of this.fields)r.defaultValue!==void 0&&(e[t]=r.defaultValue);return e}buildResolverFields(){let e={};for(let[t,r]of this.fields){let n=r.getRules?.()??r.rules;e[t]={rules:n}}return e}};var c="data-rui-control",P="data-rui-field-managed",_="data-rui-field-label",D="data-rui-field-description",N="data-rui-field-error";var v="data-default-values",T=new Set(["rui-combobox","rui-checkbox","rui-switch","rui-radio-group","rui-slider","rui-spinbutton","rui-listbox"]),x=`[${c}], ${Array.from(T).join(", ")}, input, textarea`;function O(i,e){let t=i,r=new Set,n=o=>{r.has(o)||(r.add(o),e(o))};if(typeof t.getSlotElements=="function")for(let o of t.getSlotElements())n(o);let s=i.querySelector(".rui-field")??i;for(let o of Array.from(s.children))n(o);for(let o of s.querySelectorAll("*"))n(o);for(let o of Array.from(i.children))n(o);let l=i.shadowRoot;if(l)for(let o of Array.from(l.children))n(o)}function w(i){if(i.length===0)return null;if(i.length===1)return i[0];let e=document.activeElement;if(e instanceof HTMLElement){for(let n of i)if(n===e||n.contains(e))return n}let t=i.filter(n=>n.isConnected&&n.getClientRects().length>0&&!n.closest('[hidden],[aria-hidden="true"]')),r=t.length>0?t:i.filter(n=>n.isConnected);return r[r.length-1]??i[i.length-1]}function q(i){let e=i.querySelector(".rui-field");return e instanceof HTMLElement?Array.from(e.querySelectorAll(x)).filter(t=>i.contains(t)):[]}function U(i){let e=w(q(i));if(e)return e;let t=null;if(O(i,n=>{if(t)return;let s=V(n);s&&(t=s)}),t)return t;let r=i.querySelector(".rui-field")??i;return V(r)}function V(i){return i instanceof HTMLElement?i.matches(`[${c}]`)||T.has(i.localName)||i instanceof HTMLInputElement||i instanceof HTMLTextAreaElement?i:i.querySelector(x):null}function B(i){if(i instanceof HTMLInputElement)return i.type==="checkbox"?i.checked:i.type==="number"||i.type==="range"?i.value===""?"":Number(i.value):i.value;if(i instanceof HTMLTextAreaElement)return i.value;let e=M(i);if(e.localName==="rui-checkbox"||e.localName==="rui-switch"){let t=e.querySelector('input[type="checkbox"]');return t?t.checked:e.hasAttribute("checked")}if(e.localName==="rui-combobox"||e.localName==="rui-radio-group"||e.localName==="rui-listbox")return e.value??e.getAttribute("value")??"";if(e.localName==="rui-slider"||e.localName==="rui-spinbutton"){let t=e.value;if(typeof t=="number")return t;let r=e.getAttribute("value");return r==null||r===""?"":Number(r)}return e.getAttribute("value")??""}function j(i,e){if(i instanceof HTMLInputElement){if(i.type==="checkbox"){i.checked=!!e;let r=i.closest("rui-checkbox, rui-switch");r&&(e?r.setAttribute("checked",""):r.removeAttribute("checked"));return}i.value=e==null?"":String(e);return}if(i instanceof HTMLTextAreaElement){i.value=e==null?"":String(e);return}let t=M(i);if(t.localName==="rui-checkbox"||t.localName==="rui-switch"){e?t.setAttribute("checked",""):t.removeAttribute("checked");return}t.setAttribute("value",e==null?"":String(e))}function M(i){return i.hasAttribute(c)?i.closest(Array.from(T).join(","))??i:i}var A=()=>{},X={register:()=>A,updateFieldOptions:()=>{},handleFieldChange:()=>{},handleFieldBlur:()=>{},subscribePresentation:()=>A},m=@J("rui-form")class extends ${@W({context:h,initialValue:{ready:!1,revision:0,fields:{},actions:X}})formProvider;@S({name:"rui-submit",bubbles:!0,composed:!0})submitEvent;@S({name:"rui-invalid",bubbles:!0,composed:!0})invalidEvent;@f({type:Object,defaultValue:{}})defaultValues;@f({type:String,attribute:v})defaultValuesData;@f({type:Object})resolver;@f({type:String,reflect:!0,defaultValue:"onSubmit"})mode;@f({type:String,reflect:!0,attribute:"revalidate-mode",defaultValue:"onChange"})reValidateMode;store;unsubscribeStore;nativeFormId=`rui-form-native-${Math.random().toString(36).slice(2,9)}`;lastPublishedRevision=-1;lastPublishedContext;presentationListeners=new Set;formActions={register:e=>{let r=this.ensureStore().register(e);return this.publishFormContext({force:!0}),()=>{r(),this.publishFormContext({force:!0})}},updateFieldOptions:(e,t)=>{this.ensureStore().updateFieldOptions(e,t)},handleFieldChange:e=>{this.ensureStore().handleFieldChange(e).then(()=>this.publishFormContext({force:!0}))},handleFieldBlur:e=>{this.ensureStore().handleFieldBlur(e).then(()=>this.publishFormContext({force:!0}))},subscribePresentation:e=>(this.presentationListeners.add(e),this.lastPublishedContext&&e(this.lastPublishedContext),()=>this.presentationListeners.delete(e))};connectedCallback(){super.connectedCallback(),this.ensureStore(),this.publishFormContext({force:!0}),queueMicrotask(()=>this.wireOrphanSubmitButtons())}queryNativeForm(){return this.getRef("form")??this.querySelector("form.rui-form")}wireOrphanSubmitButtons(){let e=this.queryNativeForm();if(e){e.id||(e.id=this.nativeFormId);for(let t of this.querySelectorAll("button[type=submit]"))e.contains(t)||t.setAttribute("form",e.id)}}runFormSubmit(){this.ensureStore().handleSubmit(t=>{this.submitEvent.emit({values:t})},t=>{this.invalidEvent.emit({errors:t})})}disconnectedCallback(){this.unsubscribeStore?.(),this.unsubscribeStore=void 0,super.disconnectedCallback()}resolveMode(){let e=this.getAttribute("mode");return e||(this.mode??"onSubmit")}resolveReValidateMode(){let e=this.getAttribute("revalidate-mode");return e||(this.reValidateMode??"onChange")}resolveDefaultValues(){if(this.defaultValues&&Object.keys(this.defaultValues).length>0)return this.defaultValues;let e=this.defaultValuesData??this.getAttribute(v);if(!e)return this.defaultValues??{};try{let t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return this.defaultValues??{}}}ensureStore(){return this.store?this.store:(this.store=new d({defaultValues:this.resolveDefaultValues(),resolver:typeof this.resolver=="function"?this.resolver:void 0,mode:this.resolveMode(),reValidateMode:this.resolveReValidateMode()}),this.unsubscribeStore=this.store.subscribe(()=>this.publishFormContext()),this.store)}buildFieldPresentations(e){let t={};for(let r of e.getRegisteredFieldNames()){let n=e.getFieldError(r),s=e.shouldDisplayFieldError(r);t[r]={error:s?n:void 0,invalid:!!(s&&n)}}return t}publishFormContext(e){let t=this.store;if(!t)return;let r=t.getRevision();if(!e?.force&&r===this.lastPublishedRevision)return;this.lastPublishedRevision=r;let n={ready:!0,revision:r,fields:this.buildFieldPresentations(t),actions:this.formActions};this.lastPublishedContext=n,this.formProvider.setContext(n);for(let s of this.presentationListeners)s(n)}@L(["defaultValues","defaultValuesData","mode","reValidateMode"])syncOptions(){this.ensureStore().updateOptions({defaultValues:this.resolveDefaultValues(),mode:this.resolveMode(),reValidateMode:this.resolveReValidateMode()})}@L(["resolver"])syncResolver(){typeof this.resolver=="function"&&this.ensureStore().updateOptions({resolver:this.resolver})}@y({selector:"button[type=submit]",type:"click"})onSubmitButtonClick(e){e.preventDefault(),this.runFormSubmit()}@y({ref:"form",type:"submit"})onNativeSubmit(e){e.preventDefault(),this.runFormSubmit()}@y({ref:"form",type:"reset"})onNativeReset(){this.store?.reset()}render(){return React.createElement("form",{class:"rui-form","data-ref":"form",noValidate:!0},React.createElement("slot",null))}};import"./form.css";var G=R(m,({defaultValues:i,defaultValuesData:e,resolver:t,mode:r,reValidateMode:n,children:s})=>React.createElement("rui-form",{mode:r,reValidateMode:n,"attr:revalidate-mode":n,"prop:defaultValues":i,"prop:resolver":t,"attr:data-default-values":e??(i!==void 0?JSON.stringify(i):void 0)},s));export{d as FormStore,c as RUI_CONTROL_ATTR,D as RUI_FIELD_DESCRIPTION_ATTR,N as RUI_FIELD_ERROR_ATTR,_ as RUI_FIELD_LABEL_ATTR,P as RUI_FIELD_MANAGED_ATTR,G as RuiForm,m as RuiFormElement,p as createRulesResolver,U as findFieldControl,h as formContext,B as readControlValue,F as runRulesResolver,j as writeControlValue};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/form/form.script.tsx", "../../../../src/components/ui/form/form-context.ts", "../../../../src/components/ui/form/resolvers.ts", "../../../../src/components/ui/form/form-store.ts", "../../../../src/components/ui/form/control-protocol.ts", "../../../../src/components/ui/form/form.tsx"],
4
+ "sourcesContent": ["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 { RadiantElement, customElement, event, onEvent, onUpdated, prop } from '@ecopages/radiant';\nimport { provideContext } from '@ecopages/radiant/context';\nimport type { ContextProvider } from '@ecopages/radiant/context';\nimport type { EventEmitter } from '@ecopages/radiant/tools/event-emitter';\nimport {\n\tformContext,\n\ttype FormContextActions,\n\ttype FormContextValue,\n\ttype FormFieldPresentation,\n} from './form-context';\nimport { FormStore } from './form-store';\nimport type { FieldValues, Resolver, ValidationMode } from './types';\nimport { RUI_FORM_DEFAULT_VALUES_ATTR } from './control-protocol';\n\nexport type RuiFormProps<T extends FieldValues = FieldValues> = {\n\tdefaultValues?: Partial<T>;\n\t/** JSON default values for SSR / JSX attribute channel. */\n\tdefaultValuesData?: string;\n\tresolver?: Resolver<T>;\n\tmode?: ValidationMode;\n\treValidateMode?: ValidationMode;\n};\n\nexport type RuiFormSubmitDetail<T extends FieldValues = FieldValues> = {\n\tvalues: T;\n};\n\nexport type RuiFormInvalidDetail<T extends FieldValues = FieldValues> = {\n\terrors: Partial<Record<keyof T, { message?: string }>>;\n};\n\nconst noopUnregister = () => {};\n\nconst initialFormActions: FormContextActions = {\n\tregister: () => noopUnregister,\n\tupdateFieldOptions: () => {},\n\thandleFieldChange: () => {},\n\thandleFieldBlur: () => {},\n\tsubscribePresentation: () => noopUnregister,\n};\n\n/**\n * `<rui-form>` \u2014 form coordinator with RHF-like validation and field registration.\n *\n * Owns the {@link FormStore}, publishes field presentation via {@link formContext},\n * and exposes registration / validation entry points for `<rui-field>` connectors.\n *\n * @element rui-form\n * @fires rui-submit - Emitted when validation passes; `detail.values` holds field values.\n * @fires rui-invalid - Emitted when validation fails on submit.\n */\n@customElement('rui-form')\nexport class RuiForm extends RadiantElement {\n\t@provideContext({\n\t\tcontext: formContext,\n\t\tinitialValue: {\n\t\t\tready: false,\n\t\t\trevision: 0,\n\t\t\tfields: {},\n\t\t\tactions: initialFormActions,\n\t\t},\n\t})\n\tformProvider: ContextProvider<typeof formContext>;\n\n\t@event({ name: 'rui-submit', bubbles: true, composed: true })\n\tsubmitEvent: EventEmitter<RuiFormSubmitDetail>;\n\n\t@event({ name: 'rui-invalid', bubbles: true, composed: true })\n\tinvalidEvent: EventEmitter<RuiFormInvalidDetail>;\n\n\t@prop({ type: Object, defaultValue: {} }) defaultValues: FieldValues;\n\t@prop({ type: String, attribute: RUI_FORM_DEFAULT_VALUES_ATTR }) defaultValuesData?: string;\n\t@prop({ type: Object }) resolver?: Resolver<FieldValues>;\n\t@prop({ type: String, reflect: true, defaultValue: 'onSubmit' }) mode: ValidationMode;\n\t@prop({ type: String, reflect: true, attribute: 'revalidate-mode', defaultValue: 'onChange' })\n\treValidateMode: ValidationMode;\n\n\tprivate store: FormStore | undefined;\n\tprivate unsubscribeStore?: () => void;\n\tprivate readonly nativeFormId = `rui-form-native-${Math.random().toString(36).slice(2, 9)}`;\n\tprivate lastPublishedRevision = -1;\n\tprivate lastPublishedContext: FormContextValue | undefined;\n\tprivate readonly presentationListeners = new Set<(value: FormContextValue) => void>();\n\tprivate readonly formActions: FormContextActions = {\n\t\tregister: (registration) => {\n\t\t\tconst store = this.ensureStore();\n\t\t\tconst unregister = store.register(registration);\n\t\t\tthis.publishFormContext({ force: true });\n\t\t\treturn () => {\n\t\t\t\tunregister();\n\t\t\t\tthis.publishFormContext({ force: true });\n\t\t\t};\n\t\t},\n\t\tupdateFieldOptions: (name, options) => {\n\t\t\tthis.ensureStore().updateFieldOptions(name, options);\n\t\t},\n\t\thandleFieldChange: (name) => {\n\t\t\tvoid this.ensureStore()\n\t\t\t\t.handleFieldChange(name)\n\t\t\t\t.then(() => this.publishFormContext({ force: true }));\n\t\t},\n\t\thandleFieldBlur: (name) => {\n\t\t\tvoid this.ensureStore()\n\t\t\t\t.handleFieldBlur(name)\n\t\t\t\t.then(() => this.publishFormContext({ force: true }));\n\t\t},\n\t\tsubscribePresentation: (listener) => {\n\t\t\tthis.presentationListeners.add(listener);\n\t\t\tif (this.lastPublishedContext) {\n\t\t\t\tlistener(this.lastPublishedContext);\n\t\t\t}\n\t\t\treturn () => this.presentationListeners.delete(listener);\n\t\t},\n\t};\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tthis.ensureStore();\n\t\tthis.publishFormContext({ force: true });\n\t\tqueueMicrotask(() => this.wireOrphanSubmitButtons());\n\t}\n\n\tprivate queryNativeForm(): HTMLFormElement | null {\n\t\treturn this.getRef<HTMLFormElement>('form') ?? this.querySelector('form.rui-form');\n\t}\n\n\t/** Link submit buttons left on the host (outside the inner `<form>`) after slot projection. */\n\tprivate wireOrphanSubmitButtons(): void {\n\t\tconst formEl = this.queryNativeForm();\n\t\tif (!formEl) {\n\t\t\treturn;\n\t\t}\n\t\tif (!formEl.id) {\n\t\t\tformEl.id = this.nativeFormId;\n\t\t}\n\t\tfor (const btn of this.querySelectorAll<HTMLButtonElement>('button[type=submit]')) {\n\t\t\tif (formEl.contains(btn)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbtn.setAttribute('form', formEl.id);\n\t\t}\n\t}\n\n\tprivate runFormSubmit(): void {\n\t\tconst store = this.ensureStore();\n\t\tvoid store.handleSubmit(\n\t\t\t(values) => {\n\t\t\t\tthis.submitEvent.emit({ values });\n\t\t\t},\n\t\t\t(errors) => {\n\t\t\t\tthis.invalidEvent.emit({ errors });\n\t\t\t},\n\t\t);\n\t}\n\n\toverride disconnectedCallback(): void {\n\t\tthis.unsubscribeStore?.();\n\t\tthis.unsubscribeStore = undefined;\n\t\tsuper.disconnectedCallback();\n\t}\n\n\tprivate resolveMode(): ValidationMode {\n\t\tconst fromAttr = this.getAttribute('mode');\n\t\tif (fromAttr) {\n\t\t\treturn fromAttr as ValidationMode;\n\t\t}\n\t\treturn this.mode ?? 'onSubmit';\n\t}\n\n\tprivate resolveReValidateMode(): ValidationMode {\n\t\tconst fromAttr = this.getAttribute('revalidate-mode');\n\t\tif (fromAttr) {\n\t\t\treturn fromAttr as ValidationMode;\n\t\t}\n\t\treturn this.reValidateMode ?? 'onChange';\n\t}\n\n\tprivate resolveDefaultValues(): FieldValues {\n\t\tif (this.defaultValues && Object.keys(this.defaultValues).length > 0) {\n\t\t\treturn this.defaultValues;\n\t\t}\n\t\tconst raw = this.defaultValuesData ?? this.getAttribute(RUI_FORM_DEFAULT_VALUES_ATTR);\n\t\tif (!raw) {\n\t\t\treturn this.defaultValues ?? {};\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(raw) as FieldValues;\n\t\t\treturn parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};\n\t\t} catch {\n\t\t\treturn this.defaultValues ?? {};\n\t\t}\n\t}\n\n\tprivate ensureStore(): FormStore {\n\t\tif (this.store) {\n\t\t\treturn this.store;\n\t\t}\n\n\t\tthis.store = new FormStore({\n\t\t\tdefaultValues: this.resolveDefaultValues(),\n\t\t\tresolver: typeof this.resolver === 'function' ? this.resolver : undefined,\n\t\t\tmode: this.resolveMode(),\n\t\t\treValidateMode: this.resolveReValidateMode(),\n\t\t});\n\t\tthis.unsubscribeStore = this.store.subscribe(() => this.publishFormContext());\n\t\treturn this.store;\n\t}\n\n\tprivate buildFieldPresentations(store: FormStore): Record<string, FormFieldPresentation> {\n\t\tconst fields: Record<string, FormFieldPresentation> = {};\n\t\tfor (const name of store.getRegisteredFieldNames()) {\n\t\t\tconst message = store.getFieldError(name);\n\t\t\tconst show = store.shouldDisplayFieldError(name);\n\t\t\tfields[name] = {\n\t\t\t\terror: show ? message : undefined,\n\t\t\t\tinvalid: Boolean(show && message),\n\t\t\t};\n\t\t}\n\t\treturn fields;\n\t}\n\n\tprivate publishFormContext(options?: { force?: boolean }): void {\n\t\tconst store = this.store;\n\t\tif (!store) {\n\t\t\treturn;\n\t\t}\n\t\tconst revision = store.getRevision();\n\t\tif (!options?.force && revision === this.lastPublishedRevision) {\n\t\t\treturn;\n\t\t}\n\t\tthis.lastPublishedRevision = revision;\n\t\tconst nextContext = {\n\t\t\tready: true,\n\t\t\trevision,\n\t\t\tfields: this.buildFieldPresentations(store),\n\t\t\tactions: this.formActions,\n\t\t} satisfies FormContextValue;\n\t\tthis.lastPublishedContext = nextContext;\n\t\tthis.formProvider.setContext(nextContext);\n\t\tfor (const listener of this.presentationListeners) {\n\t\t\tlistener(nextContext);\n\t\t}\n\t}\n\n\t@onUpdated(['defaultValues', 'defaultValuesData', 'mode', 'reValidateMode'])\n\tsyncOptions(): void {\n\t\tconst store = this.ensureStore();\n\t\tstore.updateOptions({\n\t\t\tdefaultValues: this.resolveDefaultValues(),\n\t\t\tmode: this.resolveMode(),\n\t\t\treValidateMode: this.resolveReValidateMode(),\n\t\t});\n\t}\n\n\t@onUpdated(['resolver'])\n\tsyncResolver(): void {\n\t\tif (typeof this.resolver === 'function') {\n\t\t\tthis.ensureStore().updateOptions({ resolver: this.resolver });\n\t\t}\n\t}\n\n\t/** Save clicks must run validation even when the button is not associated with the inner `<form>`. */\n\t@onEvent({ selector: 'button[type=submit]', type: 'click' })\n\tonSubmitButtonClick(event: Event): void {\n\t\tevent.preventDefault();\n\t\tthis.runFormSubmit();\n\t}\n\n\t@onEvent({ ref: 'form', type: 'submit' })\n\tonNativeSubmit(event: SubmitEvent): void {\n\t\tevent.preventDefault();\n\t\tthis.runFormSubmit();\n\t}\n\n\t@onEvent({ ref: 'form', type: 'reset' })\n\tonNativeReset(): void {\n\t\tthis.store?.reset();\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<form class=\"rui-form\" data-ref=\"form\" noValidate>\n\t\t\t\t<slot></slot>\n\t\t\t</form>\n\t\t);\n\t}\n}\n", "import { createContext } from '@ecopages/radiant/context';\nimport type { FieldRegistration, RegisterOptions } from './types';\n\n/** UI slice for one field, computed by `<rui-form>` from the store. */\nexport type FormFieldPresentation = {\n\terror?: string;\n\tinvalid: boolean;\n};\n\nexport type FormContextActions = {\n\tregister: (registration: FieldRegistration) => () => void;\n\tupdateFieldOptions: (name: string, options: RegisterOptions) => void;\n\thandleFieldChange: (name: string) => void;\n\thandleFieldBlur: (name: string) => void;\n\t/** Notified whenever the form publishes a new presentation snapshot. */\n\tsubscribePresentation: (listener: (value: FormContextValue) => void) => () => void;\n};\n\nexport type FormContextValue = {\n\t/** False until `<rui-form>` has created its store and bound real actions. */\n\tready: boolean;\n\t/** Bumps when form store state changes. */\n\trevision: number;\n\t/** Per-field messages and invalid flags; only the form writes this map. */\n\tfields: Record<string, FormFieldPresentation>;\n\t/** Registration and validation triggers; fields call these instead of touching the store. */\n\tactions: FormContextActions;\n};\n\nexport const formContext = createContext<FormContextValue>(Symbol.for('rui-form-context'));\n", "import type { FieldRules, FieldValues, Resolver, ResolverContext, ResolverResult } from './types';\n\nfunction resolveRuleMessage(\n\tfallback: string,\n\trule: boolean | string | number | { value: unknown; message: string } | undefined,\n): string | undefined {\n\tif (rule === undefined || rule === false) {\n\t\treturn undefined;\n\t}\n\tif (typeof rule === 'string') {\n\t\treturn rule;\n\t}\n\tif (typeof rule === 'object' && rule !== null && 'message' in rule && typeof rule.message === 'string') {\n\t\treturn rule.message;\n\t}\n\treturn fallback;\n}\n\nfunction ruleValue<T>(rule: T | { value: T; message: string }): T {\n\tif (typeof rule === 'object' && rule !== null && 'value' in rule) {\n\t\treturn rule.value as T;\n\t}\n\treturn rule as T;\n}\n\nasync function validateFieldRules(\n\t_valueName: string,\n\tvalue: unknown,\n\trules: FieldRules | undefined,\n\tvalues: FieldValues,\n): Promise<string | undefined> {\n\tif (!rules) {\n\t\treturn undefined;\n\t}\n\n\tif (rules.required) {\n\t\tconst empty =\n\t\t\tvalue === undefined ||\n\t\t\tvalue === null ||\n\t\t\tvalue === '' ||\n\t\t\t(Array.isArray(value) && value.length === 0) ||\n\t\t\tvalue === false;\n\t\tif (empty) {\n\t\t\treturn resolveRuleMessage('This field is required', rules.required) ?? 'This field is required';\n\t\t}\n\t}\n\n\tif (typeof value === 'number' || (typeof value === 'string' && value !== '' && !Number.isNaN(Number(value)))) {\n\t\tconst num = typeof value === 'number' ? value : Number(value);\n\t\tif (rules.min !== undefined && num < ruleValue(rules.min)) {\n\t\t\treturn resolveRuleMessage(`Minimum value is ${ruleValue(rules.min)}`, rules.min);\n\t\t}\n\t\tif (rules.max !== undefined && num > ruleValue(rules.max)) {\n\t\t\treturn resolveRuleMessage(`Maximum value is ${ruleValue(rules.max)}`, rules.max);\n\t\t}\n\t}\n\n\tconst str = value == null ? '' : String(value);\n\tif (rules.minLength !== undefined && str.length < ruleValue(rules.minLength)) {\n\t\treturn resolveRuleMessage(`Minimum length is ${ruleValue(rules.minLength)}`, rules.minLength);\n\t}\n\tif (rules.maxLength !== undefined && str.length > ruleValue(rules.maxLength)) {\n\t\treturn resolveRuleMessage(`Maximum length is ${ruleValue(rules.maxLength)}`, rules.maxLength);\n\t}\n\tif (rules.pattern !== undefined) {\n\t\tconst pattern = ruleValue(rules.pattern);\n\t\tif (!pattern.test(str)) {\n\t\t\tconst patternRule = rules.pattern;\n\t\t\tconst message =\n\t\t\t\ttypeof patternRule === 'object' && 'message' in patternRule && typeof patternRule.message === 'string'\n\t\t\t\t\t? patternRule.message\n\t\t\t\t\t: 'Invalid format';\n\t\t\treturn message;\n\t\t}\n\t}\n\n\tif (rules.validate) {\n\t\tif (typeof rules.validate === 'function') {\n\t\t\tconst result = await rules.validate(value, values);\n\t\t\tif (result === true || result === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (result === false) {\n\t\t\t\treturn 'Invalid value';\n\t\t\t}\n\t\t\treturn String(result);\n\t\t}\n\n\t\tfor (const key of Object.keys(rules.validate)) {\n\t\t\tconst fn = rules.validate[key];\n\t\t\tconst result = await fn(value, values);\n\t\t\tif (result !== true && result !== undefined) {\n\t\t\t\treturn result === false ? 'Invalid value' : String(result);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport async function runRulesResolver<T extends FieldValues>(\n\tvalues: T,\n\tcontext: ResolverContext,\n\tnames?: string[],\n): Promise<ResolverResult<T>> {\n\tconst targetNames = names ?? Object.keys(context.fields);\n\tconst errors: Partial<Record<keyof T, { type: string; message: string }>> = {};\n\n\tfor (const name of targetNames) {\n\t\tconst rules = context.fields[name]?.rules;\n\t\tconst message = await validateFieldRules(name, values[name], rules, values);\n\t\tif (message) {\n\t\t\terrors[name as keyof T] = { type: 'validation', message };\n\t\t}\n\t}\n\n\tif (Object.keys(errors).length > 0) {\n\t\treturn { values: {} as Record<string, never>, errors };\n\t}\n\n\treturn { values, errors: {} as Record<string, never> };\n}\n\nexport function createRulesResolver<T extends FieldValues>(): Resolver<T> {\n\treturn (values, context, options) => runRulesResolver(values, context, options.names);\n}\n", "import { createRulesResolver } from './resolvers';\nimport type {\n\tFieldError,\n\tFieldRegistration,\n\tFieldRules,\n\tFieldValues,\n\tFormState,\n\tRegisterOptions,\n\tResolver,\n\tValidationMode,\n} from './types';\n\nexport type FormStoreOptions<T extends FieldValues> = {\n\tdefaultValues?: Partial<T>;\n\tvalues?: Partial<T>;\n\tresolver?: Resolver<T>;\n\tmode?: ValidationMode;\n\treValidateMode?: ValidationMode;\n};\n\ntype InternalField = FieldRegistration & {\n\trules?: FieldRules;\n\tdefaultValue?: unknown;\n\tgetRules?: () => FieldRules | undefined;\n};\n\nfunction normalizeResolver<T extends FieldValues>(resolver: unknown): Resolver<T> {\n\tif (typeof resolver === 'function') {\n\t\treturn resolver as Resolver<T>;\n\t}\n\treturn createRulesResolver<T>();\n}\n\nexport class FormStore<T extends FieldValues = FieldValues> {\n\tmode: ValidationMode;\n\treValidateMode: ValidationMode;\n\tprivate resolver: Resolver<T>;\n\tprivate readonly fields = new Map<string, InternalField>();\n\tprivate listeners = new Set<() => void>();\n\tprivate revision = 0;\n\tprivate defaultValues: Partial<T>;\n\n\tvalues: T;\n\terrors: Partial<Record<keyof T, FieldError>> = {};\n\ttouched: Partial<Record<keyof T, boolean>> = {};\n\tdirty: Partial<Record<keyof T, boolean>> = {};\n\tisSubmitting = false;\n\tisSubmitted = false;\n\n\tconstructor(options: FormStoreOptions<T> = {}) {\n\t\tthis.mode = options.mode ?? 'onSubmit';\n\t\tthis.reValidateMode = options.reValidateMode ?? 'onChange';\n\t\tthis.resolver = normalizeResolver(options.resolver);\n\t\tthis.defaultValues = { ...(options.defaultValues ?? {}) } as Partial<T>;\n\t\tthis.values = { ...this.defaultValues, ...(options.values ?? {}) } as T;\n\t}\n\n\t/** Update options without dropping registered fields. */\n\tupdateOptions(options: FormStoreOptions<T>): void {\n\t\tif (options.mode !== undefined) {\n\t\t\tthis.mode = options.mode;\n\t\t}\n\t\tif (options.reValidateMode !== undefined) {\n\t\t\tthis.reValidateMode = options.reValidateMode;\n\t\t}\n\t\tif (options.resolver !== undefined) {\n\t\t\tthis.resolver = normalizeResolver(options.resolver);\n\t\t}\n\t\tif (options.defaultValues !== undefined) {\n\t\t\tthis.defaultValues = { ...(options.defaultValues as Partial<T>) };\n\t\t}\n\t}\n\n\tsubscribe(listener: () => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\tprivate notify(): void {\n\t\tthis.revision += 1;\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener();\n\t\t}\n\t}\n\n\tgetRevision(): number {\n\t\treturn this.revision;\n\t}\n\n\tgetRegisteredFieldNames(): string[] {\n\t\treturn Array.from(this.fields.keys());\n\t}\n\n\tregister(registration: FieldRegistration): () => void {\n\t\tconst existing = this.fields.get(registration.name);\n\t\tconst entry: InternalField = {\n\t\t\t...registration,\n\t\t\trules: registration.rules ?? existing?.rules,\n\t\t\tdefaultValue: registration.defaultValue ?? existing?.defaultValue,\n\t\t\tgetRules: registration.getRules ?? existing?.getRules,\n\t\t};\n\t\tthis.fields.set(registration.name, entry);\n\n\t\tif (this.values[registration.name as keyof T] === undefined && entry.defaultValue !== undefined) {\n\t\t\tthis.values = { ...this.values, [registration.name]: entry.defaultValue } as T;\n\t\t}\n\n\t\tentry.setValue(this.values[registration.name as keyof T]);\n\n\t\treturn () => {\n\t\t\tconst current = this.fields.get(registration.name);\n\t\t\tif (current === entry) {\n\t\t\t\tthis.fields.delete(registration.name);\n\t\t\t\tthis.notify();\n\t\t\t}\n\t\t};\n\t}\n\n\tupdateFieldOptions(name: string, options: RegisterOptions): void {\n\t\tconst field = this.fields.get(name);\n\t\tif (!field) {\n\t\t\treturn;\n\t\t}\n\t\tif (options.rules !== undefined) {\n\t\t\tfield.rules = options.rules;\n\t\t}\n\t\tif (options.defaultValue !== undefined) {\n\t\t\tfield.defaultValue = options.defaultValue;\n\t\t}\n\t}\n\n\tgetValues(): T {\n\t\tconst next = { ...this.values } as T;\n\t\tfor (const [name, field] of this.fields) {\n\t\t\tnext[name as keyof T] = field.getValue() as T[keyof T];\n\t\t}\n\t\treturn next;\n\t}\n\n\tsetValue(name: keyof T, value: unknown, options?: { shouldDirty?: boolean; shouldTouch?: boolean }): void {\n\t\tthis.values = { ...this.values, [name]: value } as T;\n\t\tconst field = this.fields.get(String(name));\n\t\tfield?.setValue(value);\n\t\tif (options?.shouldDirty) {\n\t\t\tthis.dirty = { ...this.dirty, [name]: true };\n\t\t}\n\t\tif (options?.shouldTouch) {\n\t\t\tthis.touched = { ...this.touched, [name]: true };\n\t\t}\n\t\tthis.notify();\n\t}\n\n\tmarkTouched(name: string): void {\n\t\tthis.touched = { ...this.touched, [name]: true };\n\t\tthis.notify();\n\t}\n\n\tmarkDirty(name: string): void {\n\t\tthis.dirty = { ...this.dirty, [name]: true };\n\t\tthis.notify();\n\t}\n\n\tclearErrors(name?: string): void {\n\t\tif (name) {\n\t\t\tconst next = { ...this.errors };\n\t\t\tdelete next[name as keyof T];\n\t\t\tthis.errors = next;\n\t\t\tthis.notify();\n\t\t\treturn;\n\t\t}\n\t\tthis.errors = {};\n\t\tthis.notify();\n\t}\n\n\tgetFieldError(name: string): string | undefined {\n\t\treturn this.errors[name as keyof T]?.message;\n\t}\n\n\tgetState(): FormState<T> {\n\t\tconst values = this.getValues();\n\t\tconst errorCount = Object.keys(this.errors).length;\n\t\treturn {\n\t\t\tvalues,\n\t\t\terrors: this.errors,\n\t\t\ttouched: this.touched,\n\t\t\tdirty: this.dirty,\n\t\t\tisSubmitting: this.isSubmitting,\n\t\t\tisSubmitted: this.isSubmitted,\n\t\t\tisValid: errorCount === 0,\n\t\t};\n\t}\n\n\tasync validateField(name: string): Promise<boolean> {\n\t\tconst values = this.getValues();\n\t\tconst result = await this.resolver(values, { fields: this.buildResolverFields() }, { names: [name] });\n\t\tconst message = result.errors[name as keyof T]?.message;\n\t\tconst next = { ...this.errors };\n\t\tif (message) {\n\t\t\tnext[name as keyof T] = { type: 'validation', message };\n\t\t} else {\n\t\t\tdelete next[name as keyof T];\n\t\t}\n\t\tthis.errors = next;\n\t\tthis.notify();\n\t\treturn !message;\n\t}\n\n\tasync validateAll(): Promise<boolean> {\n\t\tconst values = this.getValues();\n\t\tconst result = await this.resolver(values, { fields: this.buildResolverFields() }, {});\n\t\tthis.errors = result.errors as Partial<Record<keyof T, FieldError>>;\n\t\tthis.notify();\n\t\treturn Object.keys(this.errors).length === 0;\n\t}\n\n\tshouldValidate(name: string, trigger: 'change' | 'blur'): boolean {\n\t\tconst mode = this.isSubmitted ? this.reValidateMode : this.mode;\n\n\t\tswitch (mode) {\n\t\t\tcase 'all':\n\t\t\t\treturn true;\n\t\t\tcase 'onChange':\n\t\t\t\treturn trigger === 'change';\n\t\t\tcase 'onBlur':\n\t\t\t\treturn trigger === 'blur';\n\t\t\tcase 'onTouched':\n\t\t\t\treturn trigger === 'blur' && Boolean(this.touched[name as keyof T]);\n\t\t\tcase 'onSubmit':\n\t\t\t\treturn false;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync handleFieldChange(name: string): Promise<void> {\n\t\tconst field = this.fields.get(name);\n\t\tif (!field) {\n\t\t\treturn;\n\t\t}\n\t\tconst value = field.getValue();\n\t\tthis.values = { ...this.values, [name]: value } as T;\n\t\tthis.dirty = { ...this.dirty, [name]: true };\n\t\tif (this.shouldValidate(name, 'change')) {\n\t\t\tawait this.validateField(name);\n\t\t} else {\n\t\t\tthis.notify();\n\t\t}\n\t}\n\n\tasync handleFieldBlur(name: string): Promise<void> {\n\t\tthis.markTouched(name);\n\t\tif (this.shouldValidate(name, 'blur')) {\n\t\t\tawait this.validateField(name);\n\t\t} else {\n\t\t\tthis.notify();\n\t\t}\n\t}\n\n\t/** Whether a field's validation message should be shown in the UI (RHF-style). */\n\tshouldDisplayFieldError(name: string): boolean {\n\t\tconst message = this.errors[name as keyof T]?.message;\n\t\tif (!message) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.isSubmitted) {\n\t\t\treturn true;\n\t\t}\n\n\t\tswitch (this.mode) {\n\t\t\tcase 'all':\n\t\t\t\treturn Boolean(this.dirty[name as keyof T] || this.touched[name as keyof T]);\n\t\t\tcase 'onChange':\n\t\t\t\treturn Boolean(this.dirty[name as keyof T]);\n\t\t\tcase 'onBlur':\n\t\t\tcase 'onTouched':\n\t\t\t\treturn Boolean(this.touched[name as keyof T]);\n\t\t\tcase 'onSubmit':\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync handleSubmit(\n\t\tonValid: (values: T) => void | Promise<void>,\n\t\tonInvalid?: (errors: Partial<Record<keyof T, FieldError>>) => void,\n\t): Promise<void> {\n\t\tthis.isSubmitting = true;\n\t\tthis.isSubmitted = true;\n\n\t\tfor (const name of this.fields.keys()) {\n\t\t\tthis.touched = { ...this.touched, [name]: true };\n\t\t}\n\t\tthis.notify();\n\n\t\tconst valid = await this.validateAll();\n\t\tthis.isSubmitting = false;\n\t\tthis.notify();\n\n\t\tif (valid) {\n\t\t\tawait onValid(this.getValues());\n\t\t\treturn;\n\t\t}\n\n\t\tonInvalid?.(this.errors);\n\t}\n\n\treset(values?: Partial<T>): void {\n\t\tconst nextDefaults = values ?? { ...this.defaultValues, ...this.collectFieldDefaults() };\n\t\tthis.values = { ...nextDefaults } as T;\n\t\tthis.errors = {};\n\t\tthis.touched = {};\n\t\tthis.dirty = {};\n\t\tthis.isSubmitted = false;\n\t\tthis.isSubmitting = false;\n\t\tfor (const field of this.fields.values()) {\n\t\t\tfield.setValue(this.values[field.name as keyof T]);\n\t\t}\n\t\tthis.notify();\n\t}\n\n\tprivate collectFieldDefaults(): Partial<T> {\n\t\tconst defaults: Partial<T> = {};\n\t\tfor (const [name, field] of this.fields) {\n\t\t\tif (field.defaultValue !== undefined) {\n\t\t\t\tdefaults[name as keyof T] = field.defaultValue as T[keyof T];\n\t\t\t}\n\t\t}\n\t\treturn defaults;\n\t}\n\n\tprivate buildResolverFields(): Record<string, { rules?: FieldRules }> {\n\t\tconst fields: Record<string, { rules?: FieldRules }> = {};\n\t\tfor (const [name, field] of this.fields) {\n\t\t\tconst rules = field.getRules?.() ?? field.rules;\n\t\t\tfields[name] = { rules };\n\t\t}\n\t\treturn fields;\n\t}\n}\n", "export const RUI_CONTROL_ATTR = 'data-rui-control';\nexport const RUI_FIELD_MANAGED_ATTR = 'data-rui-field-managed';\nexport const RUI_FIELD_LABEL_ATTR = 'data-rui-field-label';\nexport const RUI_FIELD_DESCRIPTION_ATTR = 'data-rui-field-description';\nexport const RUI_FIELD_ERROR_ATTR = 'data-rui-field-error';\nexport const RUI_FIELD_DEFAULT_VALUE_ATTR = 'data-default-value';\nexport const RUI_FORM_DEFAULT_VALUES_ATTR = 'data-default-values';\n\nconst HOST_CONTROL_TAGS = new Set([\n\t'rui-combobox',\n\t'rui-checkbox',\n\t'rui-switch',\n\t'rui-radio-group',\n\t'rui-slider',\n\t'rui-spinbutton',\n\t'rui-listbox',\n]);\n\nconst CONTROL_SELECTOR = `[${RUI_CONTROL_ATTR}], ${Array.from(HOST_CONTROL_TAGS).join(', ')}, input, textarea`;\n\ntype FieldSlotHost = HTMLElement & { getSlotElements?: (name?: string) => Element[] };\n\n/** Radiant slot projection moves authored children into the render tree; search projected nodes first. */\nfunction forEachFieldContentNode(root: HTMLElement, visit: (node: Element) => void): void {\n\tconst slotHost = root as FieldSlotHost;\n\tconst visited = new Set<Element>();\n\tconst safeVisit = (node: Element) => {\n\t\tif (visited.has(node)) {\n\t\t\treturn;\n\t\t}\n\t\tvisited.add(node);\n\t\tvisit(node);\n\t};\n\n\tif (typeof slotHost.getSlotElements === 'function') {\n\t\tfor (const node of slotHost.getSlotElements()) {\n\t\t\tsafeVisit(node);\n\t\t}\n\t}\n\n\tconst renderRoot = root.querySelector('.rui-field') ?? root;\n\tfor (const child of Array.from(renderRoot.children)) {\n\t\tsafeVisit(child);\n\t}\n\tfor (const node of renderRoot.querySelectorAll('*')) {\n\t\tsafeVisit(node);\n\t}\n\n\tfor (const child of Array.from(root.children)) {\n\t\tsafeVisit(child);\n\t}\n\n\tconst shadow = root.shadowRoot;\n\tif (shadow) {\n\t\tfor (const child of Array.from(shadow.children)) {\n\t\t\tsafeVisit(child);\n\t\t}\n\t}\n}\n\nfunction queryFieldContent(root: HTMLElement, selector: string): HTMLElement | null {\n\tlet found: HTMLElement | null = null;\n\tforEachFieldContentNode(root, (node) => {\n\t\tif (found || !(node instanceof HTMLElement)) {\n\t\t\treturn;\n\t\t}\n\t\tif (node.matches(selector)) {\n\t\t\tfound = node;\n\t\t\treturn;\n\t\t}\n\t\tfound = node.querySelector<HTMLElement>(selector);\n\t});\n\treturn found;\n}\n\nfunction pickPrimaryFieldControl(candidates: HTMLElement[]): HTMLElement | null {\n\tif (candidates.length === 0) {\n\t\treturn null;\n\t}\n\tif (candidates.length === 1) {\n\t\treturn candidates[0];\n\t}\n\n\tconst active = document.activeElement;\n\tif (active instanceof HTMLElement) {\n\t\tfor (const candidate of candidates) {\n\t\t\tif (candidate === active || candidate.contains(active)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst visible = candidates.filter(\n\t\t(el) => el.isConnected && el.getClientRects().length > 0 && !el.closest('[hidden],[aria-hidden=\"true\"]'),\n\t);\n\tconst pool = visible.length > 0 ? visible : candidates.filter((el) => el.isConnected);\n\treturn pool[pool.length - 1] ?? candidates[candidates.length - 1];\n}\n\nfunction listFieldControlsInRenderTree(root: HTMLElement): HTMLElement[] {\n\tconst renderRoot = root.querySelector('.rui-field');\n\tif (!(renderRoot instanceof HTMLElement)) {\n\t\treturn [];\n\t}\n\treturn Array.from(renderRoot.querySelectorAll<HTMLElement>(CONTROL_SELECTOR)).filter((el) => root.contains(el));\n}\n\nexport function findFieldControl(root: HTMLElement): HTMLElement | null {\n\tconst fromRenderTree = pickPrimaryFieldControl(listFieldControlsInRenderTree(root));\n\tif (fromRenderTree) {\n\t\treturn fromRenderTree;\n\t}\n\n\tlet found: HTMLElement | null = null;\n\tforEachFieldContentNode(root, (node) => {\n\t\tif (found) {\n\t\t\treturn;\n\t\t}\n\t\tconst match = findControlInSubtree(node);\n\t\tif (match) {\n\t\t\tfound = match;\n\t\t}\n\t});\n\tif (found) {\n\t\treturn found;\n\t}\n\tconst renderRoot = root.querySelector('.rui-field') ?? root;\n\treturn findControlInSubtree(renderRoot);\n}\n\nfunction findControlInSubtree(node: Element): HTMLElement | null {\n\tif (!(node instanceof HTMLElement)) {\n\t\treturn null;\n\t}\n\tif (node.matches(`[${RUI_CONTROL_ATTR}]`)) {\n\t\treturn node;\n\t}\n\tif (HOST_CONTROL_TAGS.has(node.localName)) {\n\t\treturn node;\n\t}\n\tif (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {\n\t\treturn node;\n\t}\n\tconst nested = node.querySelector<HTMLElement>(CONTROL_SELECTOR);\n\treturn nested;\n}\n\nexport function readControlValue(control: HTMLElement): unknown {\n\tif (control instanceof HTMLInputElement) {\n\t\tif (control.type === 'checkbox') {\n\t\t\treturn control.checked;\n\t\t}\n\t\tif (control.type === 'number' || control.type === 'range') {\n\t\t\treturn control.value === '' ? '' : Number(control.value);\n\t\t}\n\t\treturn control.value;\n\t}\n\n\tif (control instanceof HTMLTextAreaElement) {\n\t\treturn control.value;\n\t}\n\n\tconst host = resolveControlHost(control);\n\n\tif (host.localName === 'rui-checkbox' || host.localName === 'rui-switch') {\n\t\tconst input = host.querySelector<HTMLInputElement>('input[type=\"checkbox\"]');\n\t\tif (input) {\n\t\t\treturn input.checked;\n\t\t}\n\t\treturn host.hasAttribute('checked');\n\t}\n\n\tif (host.localName === 'rui-combobox' || host.localName === 'rui-radio-group' || host.localName === 'rui-listbox') {\n\t\treturn (host as HTMLElement & { value?: string }).value ?? host.getAttribute('value') ?? '';\n\t}\n\n\tif (host.localName === 'rui-slider' || host.localName === 'rui-spinbutton') {\n\t\tconst propValue = (host as HTMLElement & { value?: number }).value;\n\t\tif (typeof propValue === 'number') {\n\t\t\treturn propValue;\n\t\t}\n\t\tconst raw = host.getAttribute('value');\n\t\treturn raw == null || raw === '' ? '' : Number(raw);\n\t}\n\n\treturn host.getAttribute('value') ?? '';\n}\n\nexport function writeControlValue(control: HTMLElement, value: unknown): void {\n\tif (control instanceof HTMLInputElement) {\n\t\tif (control.type === 'checkbox') {\n\t\t\tcontrol.checked = Boolean(value);\n\t\t\tconst ce = control.closest('rui-checkbox, rui-switch');\n\t\t\tif (ce) {\n\t\t\t\tif (value) {\n\t\t\t\t\tce.setAttribute('checked', '');\n\t\t\t\t} else {\n\t\t\t\t\tce.removeAttribute('checked');\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcontrol.value = value == null ? '' : String(value);\n\t\treturn;\n\t}\n\n\tif (control instanceof HTMLTextAreaElement) {\n\t\tcontrol.value = value == null ? '' : String(value);\n\t\treturn;\n\t}\n\n\tconst host = resolveControlHost(control);\n\n\tif (host.localName === 'rui-checkbox' || host.localName === 'rui-switch') {\n\t\tif (value) {\n\t\t\thost.setAttribute('checked', '');\n\t\t} else {\n\t\t\thost.removeAttribute('checked');\n\t\t}\n\t\treturn;\n\t}\n\n\thost.setAttribute('value', value == null ? '' : String(value));\n}\n\nfunction resolveControlHost(control: HTMLElement): HTMLElement {\n\tif (control.hasAttribute(RUI_CONTROL_ATTR)) {\n\t\tconst host = control.closest(Array.from(HOST_CONTROL_TAGS).join(',')) as HTMLElement | null;\n\t\treturn host ?? control;\n\t}\n\treturn control;\n}\n\nexport function findFieldLabel(root: HTMLElement): HTMLLabelElement | null {\n\treturn queryFieldContent(root, `[${RUI_FIELD_LABEL_ATTR}], label.rui-label`) as HTMLLabelElement | null;\n}\n\nexport function findFieldDescription(root: HTMLElement): HTMLElement | null {\n\treturn queryFieldContent(root, `[${RUI_FIELD_DESCRIPTION_ATTR}]`);\n}\n\nexport function findFieldError(root: HTMLElement): HTMLElement | null {\n\tconst all = findFieldErrorElements(root);\n\treturn all[0] ?? null;\n}\n\n/** Every error region in the field (handles duplicate slot-projection copies). */\nexport function findFieldErrorElements(root: HTMLElement): HTMLElement[] {\n\tconst renderRoot = root.querySelector('.rui-field');\n\tif (renderRoot instanceof HTMLElement) {\n\t\tconst inRender = Array.from(renderRoot.querySelectorAll<HTMLElement>(`[${RUI_FIELD_ERROR_ATTR}]`));\n\t\tconst visible = inRender.filter((el) => el.getClientRects().length > 0);\n\t\tif (visible.length > 0) {\n\t\t\treturn visible;\n\t\t}\n\t\tif (inRender.length > 0) {\n\t\t\treturn inRender;\n\t\t}\n\t}\n\tconst fromDom = Array.from(root.querySelectorAll<HTMLElement>(`[${RUI_FIELD_ERROR_ATTR}]`));\n\tif (fromDom.length > 0) {\n\t\treturn fromDom;\n\t}\n\tconst single = queryFieldContent(root, `[${RUI_FIELD_ERROR_ATTR}]`);\n\treturn single ? [single] : [];\n}\n\n/** Syncs the field name onto the native control and custom-element hosts. */\nexport function wireFieldControlName(\n\tcontrolHost: HTMLElement | null,\n\tariaTarget: HTMLElement | null,\n\tname: string,\n): void {\n\tif (!name) {\n\t\treturn;\n\t}\n\n\tif (ariaTarget instanceof HTMLInputElement || ariaTarget instanceof HTMLTextAreaElement) {\n\t\tariaTarget.name = name;\n\t}\n\n\tif (!controlHost) {\n\t\treturn;\n\t}\n\n\tif (controlHost === ariaTarget) {\n\t\treturn;\n\t}\n\n\tif (HOST_CONTROL_TAGS.has(controlHost.localName)) {\n\t\tcontrolHost.setAttribute('name', name);\n\t}\n}\n\n/** Element that receives `id`, `aria-invalid`, and `aria-describedby` from Field. */\nexport function getAriaControlTarget(control: HTMLElement): HTMLElement {\n\tif (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {\n\t\treturn control;\n\t}\n\n\tif (control.hasAttribute(RUI_CONTROL_ATTR)) {\n\t\treturn control;\n\t}\n\n\tconst marked = control.querySelector<HTMLElement>(`[${RUI_CONTROL_ATTR}]`);\n\tif (marked) {\n\t\treturn marked;\n\t}\n\n\tif (control.localName === 'rui-combobox') {\n\t\treturn control.querySelector<HTMLElement>('[data-combobox-input]') ?? control;\n\t}\n\n\tif (control.localName === 'rui-spinbutton') {\n\t\treturn control.querySelector<HTMLElement>('[data-spinbutton-input], input') ?? control;\n\t}\n\n\tif (control.localName === 'rui-slider') {\n\t\treturn control.querySelector<HTMLElement>('input[type=\"range\"]') ?? control;\n\t}\n\n\tif (control.localName === 'rui-checkbox' || control.localName === 'rui-switch') {\n\t\treturn control.querySelector<HTMLElement>('input') ?? control;\n\t}\n\n\tif (control.localName === 'rui-radio-group') {\n\t\tconst checked = control.querySelector<HTMLInputElement>('input[type=\"radio\"]:checked');\n\t\tconst first = control.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n\t\treturn checked ?? first ?? control;\n\t}\n\n\treturn control;\n}\n", "import type { WithChildren } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiFormProps } from './form.script';\nimport { RuiForm as RuiFormElement } from './form.script';\nimport './form.css';\n\nexport const RuiForm = defineRadiantView(\n\tRuiFormElement,\n\t({ defaultValues, defaultValuesData, resolver, mode, reValidateMode, children }: WithChildren<RuiFormProps>) => (\n\t\t<rui-form\n\t\t\tmode={mode}\n\t\t\treValidateMode={reValidateMode}\n\t\t\tattr:revalidate-mode={reValidateMode}\n\t\t\tprop:defaultValues={defaultValues}\n\t\t\tprop:resolver={resolver}\n\t\t\tattr:data-default-values={\n\t\t\t\tdefaultValuesData ?? (defaultValues !== undefined ? JSON.stringify(defaultValues) : undefined)\n\t\t\t}\n\t\t>\n\t\t\t{children}\n\t\t</rui-form>\n\t),\n);\n\nexport type { RuiFormSubmitDetail, RuiFormInvalidDetail } from './form.script';\n"],
5
+ "mappings": "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",
6
+ "names": ["RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "RadiantElement", "customElement", "event", "onEvent", "onUpdated", "prop", "provideContext", "createContext", "formContext", "resolveRuleMessage", "fallback", "rule", "ruleValue", "validateFieldRules", "_valueName", "value", "rules", "values", "num", "str", "patternRule", "result", "key", "fn", "runRulesResolver", "context", "names", "targetNames", "errors", "name", "message", "createRulesResolver", "options", "normalizeResolver", "resolver", "createRulesResolver", "FormStore", "options", "listener", "registration", "existing", "entry", "name", "field", "next", "value", "values", "errorCount", "message", "result", "trigger", "onValid", "onInvalid", "valid", "nextDefaults", "defaults", "fields", "rules", "RUI_CONTROL_ATTR", "RUI_FIELD_MANAGED_ATTR", "RUI_FIELD_LABEL_ATTR", "RUI_FIELD_DESCRIPTION_ATTR", "RUI_FIELD_ERROR_ATTR", "RUI_FORM_DEFAULT_VALUES_ATTR", "HOST_CONTROL_TAGS", "CONTROL_SELECTOR", "RUI_CONTROL_ATTR", "forEachFieldContentNode", "root", "visit", "slotHost", "visited", "safeVisit", "node", "renderRoot", "child", "shadow", "pickPrimaryFieldControl", "candidates", "active", "candidate", "visible", "el", "pool", "listFieldControlsInRenderTree", "root", "renderRoot", "CONTROL_SELECTOR", "findFieldControl", "fromRenderTree", "found", "forEachFieldContentNode", "node", "match", "findControlInSubtree", "RUI_CONTROL_ATTR", "HOST_CONTROL_TAGS", "readControlValue", "control", "host", "resolveControlHost", "input", "propValue", "raw", "writeControlValue", "value", "ce", "noopUnregister", "initialFormActions", "RuiForm", "customElement", "RadiantElement", "provideContext", "formContext", "event", "prop", "RUI_FORM_DEFAULT_VALUES_ATTR", "registration", "unregister", "name", "options", "listener", "formEl", "btn", "values", "errors", "fromAttr", "raw", "parsed", "FormStore", "store", "fields", "message", "show", "revision", "nextContext", "onUpdated", "onEvent", "RuiForm", "defineRadiantView", "defaultValues", "defaultValuesData", "resolver", "mode", "reValidateMode", "children"]
7
+ }
@@ -0,0 +1,3 @@
1
+ import type { FieldValues, Resolver, ResolverContext, ResolverResult } from './types';
2
+ export declare function runRulesResolver<T extends FieldValues>(values: T, context: ResolverContext, names?: string[]): Promise<ResolverResult<T>>;
3
+ export declare function createRulesResolver<T extends FieldValues>(): Resolver<T>;
@@ -0,0 +1,72 @@
1
+ export type ValidationMode = 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';
2
+ export type FieldValues = Record<string, unknown>;
3
+ export type FieldError = {
4
+ type?: string;
5
+ message?: string;
6
+ };
7
+ export type FieldRules = {
8
+ required?: boolean | string;
9
+ min?: number | {
10
+ value: number;
11
+ message: string;
12
+ };
13
+ max?: number | {
14
+ value: number;
15
+ message: string;
16
+ };
17
+ minLength?: number | {
18
+ value: number;
19
+ message: string;
20
+ };
21
+ maxLength?: number | {
22
+ value: number;
23
+ message: string;
24
+ };
25
+ pattern?: RegExp | {
26
+ value: RegExp;
27
+ message: string;
28
+ };
29
+ validate?: ((value: unknown, values: FieldValues) => boolean | string | Promise<boolean | string>) | Record<string, (value: unknown, values: FieldValues) => boolean | string | Promise<boolean | string>>;
30
+ };
31
+ export type ResolverSuccess<T extends FieldValues> = {
32
+ values: T;
33
+ errors: Record<string, never>;
34
+ };
35
+ export type ResolverError<T extends FieldValues> = {
36
+ values: Record<string, never>;
37
+ errors: Partial<Record<keyof T, FieldError>>;
38
+ };
39
+ export type ResolverResult<T extends FieldValues> = ResolverSuccess<T> | ResolverError<T>;
40
+ export type Resolver<T extends FieldValues = FieldValues> = (values: T, context: ResolverContext, options: ResolverOptions<T>) => ResolverResult<T> | Promise<ResolverResult<T>>;
41
+ export type ResolverContext = {
42
+ names?: string[];
43
+ fields: Record<string, {
44
+ rules?: FieldRules;
45
+ }>;
46
+ };
47
+ export type ResolverOptions<_T extends FieldValues> = {
48
+ criteriaMode?: 'firstError' | 'all';
49
+ names?: string[];
50
+ };
51
+ export type FormState<T extends FieldValues = FieldValues> = {
52
+ values: T;
53
+ errors: Partial<Record<keyof T, FieldError>>;
54
+ touched: Partial<Record<keyof T, boolean>>;
55
+ dirty: Partial<Record<keyof T, boolean>>;
56
+ isSubmitting: boolean;
57
+ isSubmitted: boolean;
58
+ isValid: boolean;
59
+ };
60
+ export type RegisterOptions = {
61
+ rules?: FieldRules;
62
+ defaultValue?: unknown;
63
+ shouldUnregister?: boolean;
64
+ };
65
+ export type FieldRegistration = RegisterOptions & {
66
+ name: string;
67
+ getValue: () => unknown;
68
+ setValue: (value: unknown) => void;
69
+ getRules?: () => FieldRules | undefined;
70
+ onBlur?: () => void;
71
+ onChange?: () => void;
72
+ };
@@ -0,0 +1,47 @@
1
+ @reference '../../../styles/radiant-ui.css';
2
+
3
+ rui-grid {
4
+ @apply block;
5
+ }
6
+
7
+ @layer components {
8
+ .rui-grid {
9
+ @apply w-full overflow-hidden rounded-container border border-border text-sm shadow-sm;
10
+ display: table;
11
+ border-collapse: separate;
12
+ border-spacing: 0;
13
+ }
14
+
15
+ .rui-grid__row {
16
+ display: table-row;
17
+ }
18
+
19
+ .rui-grid__row:not(:first-child):hover .rui-grid__cell {
20
+ @apply bg-surface/60;
21
+ }
22
+
23
+ .rui-grid__cell {
24
+ display: table-cell;
25
+ @apply border-b border-r border-border bg-background px-control-x py-2.5 align-middle text-on-background;
26
+ }
27
+
28
+ .rui-grid__row .rui-grid__cell:last-child {
29
+ @apply border-r-0;
30
+ }
31
+
32
+ .rui-grid__row:last-child .rui-grid__cell {
33
+ @apply border-b-0;
34
+ }
35
+
36
+ .rui-grid__row:first-child .rui-grid__cell {
37
+ @apply bg-surface font-semibold text-on-background;
38
+ }
39
+
40
+ .rui-grid__cell a {
41
+ @apply font-medium text-link no-underline underline-offset-2 hover:underline;
42
+ }
43
+
44
+ .rui-grid__cell:focus-visible {
45
+ @apply relative z-dropdown outline-none ring-2 ring-inset ring-focus-ring;
46
+ }
47
+ }
@@ -0,0 +1,7 @@
1
+ import type { JsxRenderable } from '@ecopages/jsx';
2
+ import type { RadiantSlotProps } from '../../../types';
3
+ import type { RuiGridProps } from './grid.script';
4
+ import './grid.css';
5
+ export declare const RuiGrid: import("../../../lib/radiant-view").RadiantViewComponent<RuiGridProps & RadiantSlotProps & {
6
+ rows: JsxRenderable[][];
7
+ }>;
@@ -0,0 +1,26 @@
1
+ import { RadiantElement } from '@ecopages/radiant';
2
+ export type RuiGridProps = {
3
+ label?: string;
4
+ };
5
+ type RuiGridBindings = {
6
+ label: string;
7
+ };
8
+ /**
9
+ * `<rui-grid>` — an interactive grid navigated with arrow keys.
10
+ *
11
+ * Implements a simplified APG Grid pattern for layout/data cells with
12
+ * `role="grid"`, `role="row"`, and `role="gridcell"`. Row length is taken from
13
+ * the authored markup — do not pass a separate column count.
14
+ *
15
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/grid/
16
+ * @element rui-grid
17
+ */
18
+ export declare class RuiGrid extends RadiantElement<RuiGridBindings> {
19
+ label: string;
20
+ private readonly resolvedAriaLabel;
21
+ connectedCallback(): void;
22
+ private getRows;
23
+ onKeydown(event: KeyboardEvent): void;
24
+ render(): import("@ecopages/jsx").JsxRenderable;
25
+ }
26
+ export {};
@@ -0,0 +1,2 @@
1
+ export { RuiGrid as RuiGridElement, type RuiGridProps } from './grid.script';
2
+ export { RuiGrid } from './grid';
@@ -0,0 +1,2 @@
1
+ import{RadiantElement as x,customElement as E,onEvent as h,prop as b}from"@ecopages/radiant";function c(t,e){t.forEach((r,n)=>{r.tabIndex=n===e?0:-1})}var i=@E("rui-grid")class extends x{@b({type:String,defaultValue:""})label;resolvedAriaLabel=this.$.label.map(e=>e||void 0);connectedCallback(){super.connectedCallback(),queueMicrotask(()=>{let e=Array.from(this.querySelectorAll('[role="gridcell"]'));c(e,0)})}getRows(){return Array.from(this.querySelectorAll('[role="row"]'))}@h({selector:'[role="gridcell"]',type:"keydown"})onKeydown(e){let r=this.getRows(),n=e.target.closest('[role="gridcell"]');if(!n)return;let l=n.parentElement,a=r.indexOf(l),s=Array.from(l.querySelectorAll('[role="gridcell"]')),d=s.indexOf(n),u=a,o=d;switch(e.key){case"ArrowRight":o=Math.min(s.length-1,d+1);break;case"ArrowLeft":o=Math.max(0,d-1);break;case"ArrowDown":u=Math.min(r.length-1,a+1);break;case"ArrowUp":u=Math.max(0,a-1);break;case"Home":o=0;break;case"End":o=s.length-1;break;default:return}e.preventDefault();let A=r[u],m=Array.from(A.querySelectorAll('[role="gridcell"]')),p=m[Math.min(o,m.length-1)],g=Array.from(this.querySelectorAll('[role="gridcell"]'));c(g,g.indexOf(p)),p.focus()}render(){return React.createElement("div",{class:"rui-grid",role:"grid","aria-label":this.resolvedAriaLabel},React.createElement("slot",null))}};var w=Symbol.for("@ecopages/storybook-radiant.viewElement"),f=Symbol.for("@ecopages/storybook-radiant.scriptModule"),R=Symbol.for("@ecopages/storybook-radiant.scriptExport");function y(t,e){t[w]=e;let r=e[f];r&&(t[f]=r,t[R]=e[R]??e.name)}function T(t,e){let r=(n=>e(n));return y(r,t),r}import"./grid.css";var M=T(i,({slot:t,label:e,rows:r})=>React.createElement("rui-grid",{slot:t,label:e},r.map(n=>React.createElement("div",{class:"rui-grid__row",role:"row"},n.map(l=>React.createElement("div",{class:"rui-grid__cell",role:"gridcell",tabindex:-1},l))))));export{M as RuiGrid,i as RuiGridElement};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/components/ui/grid/grid.script.tsx", "../../../../src/lib/roving-tabindex.ts", "../../../../src/lib/radiant-view.ts", "../../../../src/components/ui/grid/grid.tsx"],
4
+ "sourcesContent": ["import { RadiantElement, customElement, onEvent, prop } from '@ecopages/radiant';\nimport { applyRovingTabindex } from '../../../lib/roving-tabindex';\n\nexport type RuiGridProps = {\n\tlabel?: string;\n};\n\ntype RuiGridBindings = {\n\tlabel: string;\n};\n\n/**\n * `<rui-grid>` \u2014 an interactive grid navigated with arrow keys.\n *\n * Implements a simplified APG Grid pattern for layout/data cells with\n * `role=\"grid\"`, `role=\"row\"`, and `role=\"gridcell\"`. Row length is taken from\n * the authored markup \u2014 do not pass a separate column count.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/grid/\n * @element rui-grid\n */\n@customElement('rui-grid')\nexport class RuiGrid extends RadiantElement<RuiGridBindings> {\n\t@prop({ type: String, defaultValue: '' }) label: string;\n\n\tprivate readonly resolvedAriaLabel = this.$.label.map((label) => label || undefined);\n\n\toverride connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tqueueMicrotask(() => {\n\t\t\tconst cells = Array.from(this.querySelectorAll<HTMLElement>('[role=\"gridcell\"]'));\n\t\t\tapplyRovingTabindex(cells, 0);\n\t\t});\n\t}\n\n\tprivate getRows(): HTMLElement[] {\n\t\treturn Array.from(this.querySelectorAll<HTMLElement>('[role=\"row\"]'));\n\t}\n\n\t@onEvent({ selector: '[role=\"gridcell\"]', type: 'keydown' })\n\tonKeydown(event: KeyboardEvent): void {\n\t\tconst rows = this.getRows();\n\t\tconst current = (event.target as HTMLElement).closest('[role=\"gridcell\"]') as HTMLElement | null;\n\t\tif (!current) return;\n\n\t\tconst row = current.parentElement as HTMLElement;\n\t\tconst rowIndex = rows.indexOf(row);\n\t\tconst cells = Array.from(row.querySelectorAll<HTMLElement>('[role=\"gridcell\"]'));\n\t\tconst colIndex = cells.indexOf(current);\n\n\t\tlet nextRow = rowIndex;\n\t\tlet nextCol = colIndex;\n\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowRight':\n\t\t\t\tnextCol = Math.min(cells.length - 1, colIndex + 1);\n\t\t\t\tbreak;\n\t\t\tcase 'ArrowLeft':\n\t\t\t\tnextCol = Math.max(0, colIndex - 1);\n\t\t\t\tbreak;\n\t\t\tcase 'ArrowDown':\n\t\t\t\tnextRow = Math.min(rows.length - 1, rowIndex + 1);\n\t\t\t\tbreak;\n\t\t\tcase 'ArrowUp':\n\t\t\t\tnextRow = Math.max(0, rowIndex - 1);\n\t\t\t\tbreak;\n\t\t\tcase 'Home':\n\t\t\t\tnextCol = 0;\n\t\t\t\tbreak;\n\t\t\tcase 'End':\n\t\t\t\tnextCol = cells.length - 1;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\tevent.preventDefault();\n\t\tconst targetRow = rows[nextRow];\n\t\tconst targetCells = Array.from(targetRow.querySelectorAll<HTMLElement>('[role=\"gridcell\"]'));\n\t\tconst target = targetCells[Math.min(nextCol, targetCells.length - 1)];\n\t\tconst allCells = Array.from(this.querySelectorAll<HTMLElement>('[role=\"gridcell\"]'));\n\t\tapplyRovingTabindex(allCells, allCells.indexOf(target));\n\t\ttarget.focus();\n\t}\n\n\toverride render() {\n\t\treturn (\n\t\t\t<div class=\"rui-grid\" role=\"grid\" 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 { JsxRenderable } from '@ecopages/jsx';\nimport type { RadiantSlotProps } from '../../../types';\nimport { defineRadiantView } from '../../../lib/radiant-view';\nimport type { RuiGridProps } from './grid.script';\nimport { RuiGrid as RuiGridElement } from './grid.script';\nimport './grid.css';\n\nexport const RuiGrid = defineRadiantView(\n\tRuiGridElement,\n\t({ slot, label, rows }: RuiGridProps & RadiantSlotProps & { rows: JsxRenderable[][] }) => (\n\t\t<rui-grid slot={slot} label={label}>\n\t\t\t{rows.map((row) => (\n\t\t\t\t<div class=\"rui-grid__row\" role=\"row\">\n\t\t\t\t\t{row.map((cell) => (\n\t\t\t\t\t\t<div class=\"rui-grid__cell\" role=\"gridcell\" tabindex={-1}>\n\t\t\t\t\t\t\t{cell}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t))}\n\t\t</rui-grid>\n\t),\n);\n"],
5
+ "mappings": "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",
6
+ "names": ["RadiantElement", "customElement", "onEvent", "prop", "applyRovingTabindex", "items", "activeIndex", "item", "index", "RuiGrid", "customElement", "RadiantElement", "prop", "label", "cells", "applyRovingTabindex", "onEvent", "event", "rows", "current", "row", "rowIndex", "colIndex", "nextRow", "nextCol", "targetRow", "targetCells", "target", "allCells", "RADIANT_VIEW_ELEMENT", "RADIANT_SCRIPT_MODULE", "RADIANT_SCRIPT_EXPORT", "linkViewToElement", "view", "element", "ssrModule", "defineRadiantView", "render", "args", "RuiGrid", "defineRadiantView", "slot", "label", "rows", "row", "cell"]
7
+ }
@@ -0,0 +1 @@
1
+ export { RuiInput, type RuiInputProps, type RuiInputSize } from './input';
@@ -0,0 +1,2 @@
1
+ var t="data-rui-control";var T=new Set(["rui-combobox","rui-checkbox","rui-switch","rui-radio-group","rui-slider","rui-spinbutton","rui-listbox"]),L=`[${t}], ${Array.from(T).join(", ")}, input, textarea`;import"./input.css";function p(...e){return e.filter(Boolean).join(" ")}function d({value:e,type:n="text",placeholder:r,disabled:i,name:l,size:u="md",class:o,id:a,"aria-label":s,"on:input":c,"on:change":f,"on:blur":m,...E}){return React.createElement("input",{id:a,type:n,[t]:"","data-rui-control-type":"text",class:p("rui-input",`rui-input--${u}`,o),value:e,placeholder:r,disabled:i,name:l,"aria-label":s,"on:input":c,"on:change":f,"on:blur":m,...E})}export{d as RuiInput};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/components/ui/form/control-protocol.ts", "../../../../src/components/ui/input/input.tsx"],
4
+ "sourcesContent": ["export const RUI_CONTROL_ATTR = 'data-rui-control';\nexport const RUI_FIELD_MANAGED_ATTR = 'data-rui-field-managed';\nexport const RUI_FIELD_LABEL_ATTR = 'data-rui-field-label';\nexport const RUI_FIELD_DESCRIPTION_ATTR = 'data-rui-field-description';\nexport const RUI_FIELD_ERROR_ATTR = 'data-rui-field-error';\nexport const RUI_FIELD_DEFAULT_VALUE_ATTR = 'data-default-value';\nexport const RUI_FORM_DEFAULT_VALUES_ATTR = 'data-default-values';\n\nconst HOST_CONTROL_TAGS = new Set([\n\t'rui-combobox',\n\t'rui-checkbox',\n\t'rui-switch',\n\t'rui-radio-group',\n\t'rui-slider',\n\t'rui-spinbutton',\n\t'rui-listbox',\n]);\n\nconst CONTROL_SELECTOR = `[${RUI_CONTROL_ATTR}], ${Array.from(HOST_CONTROL_TAGS).join(', ')}, input, textarea`;\n\ntype FieldSlotHost = HTMLElement & { getSlotElements?: (name?: string) => Element[] };\n\n/** Radiant slot projection moves authored children into the render tree; search projected nodes first. */\nfunction forEachFieldContentNode(root: HTMLElement, visit: (node: Element) => void): void {\n\tconst slotHost = root as FieldSlotHost;\n\tconst visited = new Set<Element>();\n\tconst safeVisit = (node: Element) => {\n\t\tif (visited.has(node)) {\n\t\t\treturn;\n\t\t}\n\t\tvisited.add(node);\n\t\tvisit(node);\n\t};\n\n\tif (typeof slotHost.getSlotElements === 'function') {\n\t\tfor (const node of slotHost.getSlotElements()) {\n\t\t\tsafeVisit(node);\n\t\t}\n\t}\n\n\tconst renderRoot = root.querySelector('.rui-field') ?? root;\n\tfor (const child of Array.from(renderRoot.children)) {\n\t\tsafeVisit(child);\n\t}\n\tfor (const node of renderRoot.querySelectorAll('*')) {\n\t\tsafeVisit(node);\n\t}\n\n\tfor (const child of Array.from(root.children)) {\n\t\tsafeVisit(child);\n\t}\n\n\tconst shadow = root.shadowRoot;\n\tif (shadow) {\n\t\tfor (const child of Array.from(shadow.children)) {\n\t\t\tsafeVisit(child);\n\t\t}\n\t}\n}\n\nfunction queryFieldContent(root: HTMLElement, selector: string): HTMLElement | null {\n\tlet found: HTMLElement | null = null;\n\tforEachFieldContentNode(root, (node) => {\n\t\tif (found || !(node instanceof HTMLElement)) {\n\t\t\treturn;\n\t\t}\n\t\tif (node.matches(selector)) {\n\t\t\tfound = node;\n\t\t\treturn;\n\t\t}\n\t\tfound = node.querySelector<HTMLElement>(selector);\n\t});\n\treturn found;\n}\n\nfunction pickPrimaryFieldControl(candidates: HTMLElement[]): HTMLElement | null {\n\tif (candidates.length === 0) {\n\t\treturn null;\n\t}\n\tif (candidates.length === 1) {\n\t\treturn candidates[0];\n\t}\n\n\tconst active = document.activeElement;\n\tif (active instanceof HTMLElement) {\n\t\tfor (const candidate of candidates) {\n\t\t\tif (candidate === active || candidate.contains(active)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst visible = candidates.filter(\n\t\t(el) => el.isConnected && el.getClientRects().length > 0 && !el.closest('[hidden],[aria-hidden=\"true\"]'),\n\t);\n\tconst pool = visible.length > 0 ? visible : candidates.filter((el) => el.isConnected);\n\treturn pool[pool.length - 1] ?? candidates[candidates.length - 1];\n}\n\nfunction listFieldControlsInRenderTree(root: HTMLElement): HTMLElement[] {\n\tconst renderRoot = root.querySelector('.rui-field');\n\tif (!(renderRoot instanceof HTMLElement)) {\n\t\treturn [];\n\t}\n\treturn Array.from(renderRoot.querySelectorAll<HTMLElement>(CONTROL_SELECTOR)).filter((el) => root.contains(el));\n}\n\nexport function findFieldControl(root: HTMLElement): HTMLElement | null {\n\tconst fromRenderTree = pickPrimaryFieldControl(listFieldControlsInRenderTree(root));\n\tif (fromRenderTree) {\n\t\treturn fromRenderTree;\n\t}\n\n\tlet found: HTMLElement | null = null;\n\tforEachFieldContentNode(root, (node) => {\n\t\tif (found) {\n\t\t\treturn;\n\t\t}\n\t\tconst match = findControlInSubtree(node);\n\t\tif (match) {\n\t\t\tfound = match;\n\t\t}\n\t});\n\tif (found) {\n\t\treturn found;\n\t}\n\tconst renderRoot = root.querySelector('.rui-field') ?? root;\n\treturn findControlInSubtree(renderRoot);\n}\n\nfunction findControlInSubtree(node: Element): HTMLElement | null {\n\tif (!(node instanceof HTMLElement)) {\n\t\treturn null;\n\t}\n\tif (node.matches(`[${RUI_CONTROL_ATTR}]`)) {\n\t\treturn node;\n\t}\n\tif (HOST_CONTROL_TAGS.has(node.localName)) {\n\t\treturn node;\n\t}\n\tif (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {\n\t\treturn node;\n\t}\n\tconst nested = node.querySelector<HTMLElement>(CONTROL_SELECTOR);\n\treturn nested;\n}\n\nexport function readControlValue(control: HTMLElement): unknown {\n\tif (control instanceof HTMLInputElement) {\n\t\tif (control.type === 'checkbox') {\n\t\t\treturn control.checked;\n\t\t}\n\t\tif (control.type === 'number' || control.type === 'range') {\n\t\t\treturn control.value === '' ? '' : Number(control.value);\n\t\t}\n\t\treturn control.value;\n\t}\n\n\tif (control instanceof HTMLTextAreaElement) {\n\t\treturn control.value;\n\t}\n\n\tconst host = resolveControlHost(control);\n\n\tif (host.localName === 'rui-checkbox' || host.localName === 'rui-switch') {\n\t\tconst input = host.querySelector<HTMLInputElement>('input[type=\"checkbox\"]');\n\t\tif (input) {\n\t\t\treturn input.checked;\n\t\t}\n\t\treturn host.hasAttribute('checked');\n\t}\n\n\tif (host.localName === 'rui-combobox' || host.localName === 'rui-radio-group' || host.localName === 'rui-listbox') {\n\t\treturn (host as HTMLElement & { value?: string }).value ?? host.getAttribute('value') ?? '';\n\t}\n\n\tif (host.localName === 'rui-slider' || host.localName === 'rui-spinbutton') {\n\t\tconst propValue = (host as HTMLElement & { value?: number }).value;\n\t\tif (typeof propValue === 'number') {\n\t\t\treturn propValue;\n\t\t}\n\t\tconst raw = host.getAttribute('value');\n\t\treturn raw == null || raw === '' ? '' : Number(raw);\n\t}\n\n\treturn host.getAttribute('value') ?? '';\n}\n\nexport function writeControlValue(control: HTMLElement, value: unknown): void {\n\tif (control instanceof HTMLInputElement) {\n\t\tif (control.type === 'checkbox') {\n\t\t\tcontrol.checked = Boolean(value);\n\t\t\tconst ce = control.closest('rui-checkbox, rui-switch');\n\t\t\tif (ce) {\n\t\t\t\tif (value) {\n\t\t\t\t\tce.setAttribute('checked', '');\n\t\t\t\t} else {\n\t\t\t\t\tce.removeAttribute('checked');\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcontrol.value = value == null ? '' : String(value);\n\t\treturn;\n\t}\n\n\tif (control instanceof HTMLTextAreaElement) {\n\t\tcontrol.value = value == null ? '' : String(value);\n\t\treturn;\n\t}\n\n\tconst host = resolveControlHost(control);\n\n\tif (host.localName === 'rui-checkbox' || host.localName === 'rui-switch') {\n\t\tif (value) {\n\t\t\thost.setAttribute('checked', '');\n\t\t} else {\n\t\t\thost.removeAttribute('checked');\n\t\t}\n\t\treturn;\n\t}\n\n\thost.setAttribute('value', value == null ? '' : String(value));\n}\n\nfunction resolveControlHost(control: HTMLElement): HTMLElement {\n\tif (control.hasAttribute(RUI_CONTROL_ATTR)) {\n\t\tconst host = control.closest(Array.from(HOST_CONTROL_TAGS).join(',')) as HTMLElement | null;\n\t\treturn host ?? control;\n\t}\n\treturn control;\n}\n\nexport function findFieldLabel(root: HTMLElement): HTMLLabelElement | null {\n\treturn queryFieldContent(root, `[${RUI_FIELD_LABEL_ATTR}], label.rui-label`) as HTMLLabelElement | null;\n}\n\nexport function findFieldDescription(root: HTMLElement): HTMLElement | null {\n\treturn queryFieldContent(root, `[${RUI_FIELD_DESCRIPTION_ATTR}]`);\n}\n\nexport function findFieldError(root: HTMLElement): HTMLElement | null {\n\tconst all = findFieldErrorElements(root);\n\treturn all[0] ?? null;\n}\n\n/** Every error region in the field (handles duplicate slot-projection copies). */\nexport function findFieldErrorElements(root: HTMLElement): HTMLElement[] {\n\tconst renderRoot = root.querySelector('.rui-field');\n\tif (renderRoot instanceof HTMLElement) {\n\t\tconst inRender = Array.from(renderRoot.querySelectorAll<HTMLElement>(`[${RUI_FIELD_ERROR_ATTR}]`));\n\t\tconst visible = inRender.filter((el) => el.getClientRects().length > 0);\n\t\tif (visible.length > 0) {\n\t\t\treturn visible;\n\t\t}\n\t\tif (inRender.length > 0) {\n\t\t\treturn inRender;\n\t\t}\n\t}\n\tconst fromDom = Array.from(root.querySelectorAll<HTMLElement>(`[${RUI_FIELD_ERROR_ATTR}]`));\n\tif (fromDom.length > 0) {\n\t\treturn fromDom;\n\t}\n\tconst single = queryFieldContent(root, `[${RUI_FIELD_ERROR_ATTR}]`);\n\treturn single ? [single] : [];\n}\n\n/** Syncs the field name onto the native control and custom-element hosts. */\nexport function wireFieldControlName(\n\tcontrolHost: HTMLElement | null,\n\tariaTarget: HTMLElement | null,\n\tname: string,\n): void {\n\tif (!name) {\n\t\treturn;\n\t}\n\n\tif (ariaTarget instanceof HTMLInputElement || ariaTarget instanceof HTMLTextAreaElement) {\n\t\tariaTarget.name = name;\n\t}\n\n\tif (!controlHost) {\n\t\treturn;\n\t}\n\n\tif (controlHost === ariaTarget) {\n\t\treturn;\n\t}\n\n\tif (HOST_CONTROL_TAGS.has(controlHost.localName)) {\n\t\tcontrolHost.setAttribute('name', name);\n\t}\n}\n\n/** Element that receives `id`, `aria-invalid`, and `aria-describedby` from Field. */\nexport function getAriaControlTarget(control: HTMLElement): HTMLElement {\n\tif (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {\n\t\treturn control;\n\t}\n\n\tif (control.hasAttribute(RUI_CONTROL_ATTR)) {\n\t\treturn control;\n\t}\n\n\tconst marked = control.querySelector<HTMLElement>(`[${RUI_CONTROL_ATTR}]`);\n\tif (marked) {\n\t\treturn marked;\n\t}\n\n\tif (control.localName === 'rui-combobox') {\n\t\treturn control.querySelector<HTMLElement>('[data-combobox-input]') ?? control;\n\t}\n\n\tif (control.localName === 'rui-spinbutton') {\n\t\treturn control.querySelector<HTMLElement>('[data-spinbutton-input], input') ?? control;\n\t}\n\n\tif (control.localName === 'rui-slider') {\n\t\treturn control.querySelector<HTMLElement>('input[type=\"range\"]') ?? control;\n\t}\n\n\tif (control.localName === 'rui-checkbox' || control.localName === 'rui-switch') {\n\t\treturn control.querySelector<HTMLElement>('input') ?? control;\n\t}\n\n\tif (control.localName === 'rui-radio-group') {\n\t\tconst checked = control.querySelector<HTMLInputElement>('input[type=\"radio\"]:checked');\n\t\tconst first = control.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n\t\treturn checked ?? first ?? control;\n\t}\n\n\treturn control;\n}\n", "import { RUI_CONTROL_ATTR } from '../form/control-protocol';\nimport './input.css';\n\nexport type RuiInputSize = 'sm' | 'md' | 'lg';\n\nexport type RuiInputProps = {\n\tvalue?: string;\n\ttype?: string;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tname?: string;\n\tsize?: RuiInputSize;\n\tclass?: string;\n\tid?: string;\n\t'aria-label'?: string;\n\t'on:input'?: (event: Event) => void;\n\t'on:change'?: (event: Event) => void;\n\t'on:blur'?: (event: Event) => void;\n};\n\nfunction cx(...parts: Array<string | false | null | undefined>): string {\n\treturn parts.filter(Boolean).join(' ');\n}\n\n/**\n * Presentational wrapper around a native `<input>`.\n *\n * No custom element \u2014 Field owns labeling, `aria-*`, and validation wiring.\n * Marked with `data-rui-control` so `<rui-field>` can discover it.\n */\nexport function RuiInput({\n\tvalue,\n\ttype = 'text',\n\tplaceholder,\n\tdisabled,\n\tname,\n\tsize = 'md',\n\tclass: className,\n\tid,\n\t'aria-label': ariaLabel,\n\t'on:input': onInput,\n\t'on:change': onChange,\n\t'on:blur': onBlur,\n\t...rest\n}: RuiInputProps) {\n\treturn (\n\t\t<input\n\t\t\tid={id}\n\t\t\ttype={type}\n\t\t\t{...{ [RUI_CONTROL_ATTR]: '' }}\n\t\t\tdata-rui-control-type=\"text\"\n\t\t\tclass={cx('rui-input', `rui-input--${size}`, className)}\n\t\t\tvalue={value}\n\t\t\tplaceholder={placeholder}\n\t\t\tdisabled={disabled}\n\t\t\tname={name}\n\t\t\taria-label={ariaLabel}\n\t\t\ton:input={onInput}\n\t\t\ton:change={onChange}\n\t\t\ton:blur={onBlur}\n\t\t\t{...rest}\n\t\t/>\n\t);\n}\n"],
5
+ "mappings": "AAAO,IAAMA,EAAmB,mBAQhC,IAAMC,EAAoB,IAAI,IAAI,CACjC,eACA,eACA,aACA,kBACA,aACA,iBACA,aACD,CAAC,EAEKC,EAAmB,IAAIC,CAAgB,MAAM,MAAM,KAAKF,CAAiB,EAAE,KAAK,IAAI,CAAC,oBCjB3F,MAAO,cAmBP,SAASG,KAAMC,EAAyD,CACvE,OAAOA,EAAM,OAAO,OAAO,EAAE,KAAK,GAAG,CACtC,CAQO,SAASC,EAAS,CACxB,MAAAC,EACA,KAAAC,EAAO,OACP,YAAAC,EACA,SAAAC,EACA,KAAAC,EACA,KAAAC,EAAO,KACP,MAAOC,EACP,GAAAC,EACA,aAAcC,EACd,WAAYC,EACZ,YAAaC,EACb,UAAWC,EACX,GAAGC,CACJ,EAAkB,CACjB,OACC,oBAAC,SACA,GAAIL,EACJ,KAAMN,EACA,CAACY,CAAgB,EAAG,GAC1B,wBAAsB,OACtB,MAAOhB,EAAG,YAAa,cAAcQ,CAAI,GAAIC,CAAS,EACtD,MAAON,EACP,YAAaE,EACb,SAAUC,EACV,KAAMC,EACN,aAAYI,EACZ,WAAUC,EACV,YAAWC,EACX,UAASC,EACR,GAAGC,EACL,CAEF",
6
+ "names": ["RUI_CONTROL_ATTR", "HOST_CONTROL_TAGS", "CONTROL_SELECTOR", "RUI_CONTROL_ATTR", "cx", "parts", "RuiInput", "value", "type", "placeholder", "disabled", "name", "size", "className", "id", "ariaLabel", "onInput", "onChange", "onBlur", "rest", "RUI_CONTROL_ATTR"]
7
+ }