@proyecto-viviana/solidaria-components 0.2.9 → 0.3.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 (222) hide show
  1. package/README.md +39 -272
  2. package/dist/ActionBar.d.ts +21 -13
  3. package/dist/ActionBar.d.ts.map +1 -1
  4. package/dist/ActionGroup.d.ts +8 -8
  5. package/dist/ActionGroup.d.ts.map +1 -1
  6. package/dist/Alert.d.ts +5 -5
  7. package/dist/Alert.d.ts.map +1 -1
  8. package/dist/Autocomplete.d.ts +5 -5
  9. package/dist/Autocomplete.d.ts.map +1 -1
  10. package/dist/Breadcrumbs.d.ts +18 -7
  11. package/dist/Breadcrumbs.d.ts.map +1 -1
  12. package/dist/Button.d.ts +24 -5
  13. package/dist/Button.d.ts.map +1 -1
  14. package/dist/Calendar.d.ts +38 -7
  15. package/dist/Calendar.d.ts.map +1 -1
  16. package/dist/Checkbox.d.ts +32 -7
  17. package/dist/Checkbox.d.ts.map +1 -1
  18. package/dist/Collection.d.ts +19 -14
  19. package/dist/Collection.d.ts.map +1 -1
  20. package/dist/Color.d.ts +103 -14
  21. package/dist/Color.d.ts.map +1 -1
  22. package/dist/ColorEditor.d.ts +6 -6
  23. package/dist/ColorEditor.d.ts.map +1 -1
  24. package/dist/ComboBox.d.ts +85 -19
  25. package/dist/ComboBox.d.ts.map +1 -1
  26. package/dist/ContextualHelpTrigger.d.ts +2 -2
  27. package/dist/ContextualHelpTrigger.d.ts.map +1 -1
  28. package/dist/DateField.d.ts +8 -6
  29. package/dist/DateField.d.ts.map +1 -1
  30. package/dist/DatePicker.d.ts +53 -22
  31. package/dist/DatePicker.d.ts.map +1 -1
  32. package/dist/DateRangePickerContext.d.ts +30 -0
  33. package/dist/DateRangePickerContext.d.ts.map +1 -0
  34. package/dist/Dialog.d.ts +5 -5
  35. package/dist/Dialog.d.ts.map +1 -1
  36. package/dist/Disclosure.d.ts +23 -5
  37. package/dist/Disclosure.d.ts.map +1 -1
  38. package/dist/DragAndDrop.d.ts +6 -6
  39. package/dist/DragAndDrop.d.ts.map +1 -1
  40. package/dist/DragPreview.d.ts +2 -2
  41. package/dist/DragPreview.d.ts.map +1 -1
  42. package/dist/DropZone.d.ts +4 -4
  43. package/dist/DropZone.d.ts.map +1 -1
  44. package/dist/FieldError.d.ts +9 -5
  45. package/dist/FieldError.d.ts.map +1 -1
  46. package/dist/FileTrigger.d.ts +3 -3
  47. package/dist/FileTrigger.d.ts.map +1 -1
  48. package/dist/Focusable.d.ts +2 -2
  49. package/dist/Focusable.d.ts.map +1 -1
  50. package/dist/Form.d.ts +18 -4
  51. package/dist/Form.d.ts.map +1 -1
  52. package/dist/GridList.d.ts +32 -12
  53. package/dist/GridList.d.ts.map +1 -1
  54. package/dist/HiddenDateInput.d.ts +26 -0
  55. package/dist/HiddenDateInput.d.ts.map +1 -0
  56. package/dist/HiddenTimeInput.d.ts +25 -0
  57. package/dist/HiddenTimeInput.d.ts.map +1 -0
  58. package/dist/Icon.d.ts +5 -5
  59. package/dist/Icon.d.ts.map +1 -1
  60. package/dist/Keyboard.d.ts +1 -1
  61. package/dist/Landmark.d.ts +3 -3
  62. package/dist/Landmark.d.ts.map +1 -1
  63. package/dist/Link.d.ts +10 -4
  64. package/dist/Link.d.ts.map +1 -1
  65. package/dist/ListBox.d.ts +32 -12
  66. package/dist/ListBox.d.ts.map +1 -1
  67. package/dist/ListDropTargetDelegate.d.ts +6 -6
  68. package/dist/ListDropTargetDelegate.d.ts.map +1 -1
  69. package/dist/Menu.d.ts +65 -14
  70. package/dist/Menu.d.ts.map +1 -1
  71. package/dist/Meter.d.ts +3 -3
  72. package/dist/Meter.d.ts.map +1 -1
  73. package/dist/Modal.d.ts +5 -5
  74. package/dist/Modal.d.ts.map +1 -1
  75. package/dist/NumberField.d.ts +8 -12
  76. package/dist/NumberField.d.ts.map +1 -1
  77. package/dist/Popover.d.ts +28 -5
  78. package/dist/Popover.d.ts.map +1 -1
  79. package/dist/Pressable.d.ts +2 -2
  80. package/dist/Pressable.d.ts.map +1 -1
  81. package/dist/ProgressBar.d.ts +5 -3
  82. package/dist/ProgressBar.d.ts.map +1 -1
  83. package/dist/RadioGroup.d.ts +43 -9
  84. package/dist/RadioGroup.d.ts.map +1 -1
  85. package/dist/RangeCalendar.d.ts +34 -7
  86. package/dist/RangeCalendar.d.ts.map +1 -1
  87. package/dist/RouterProvider.d.ts +2 -2
  88. package/dist/RouterProvider.d.ts.map +1 -1
  89. package/dist/SearchField.d.ts +23 -20
  90. package/dist/SearchField.d.ts.map +1 -1
  91. package/dist/Select.d.ts +41 -11
  92. package/dist/Select.d.ts.map +1 -1
  93. package/dist/SelectionIndicator.d.ts +3 -3
  94. package/dist/SelectionIndicator.d.ts.map +1 -1
  95. package/dist/Separator.d.ts +9 -3
  96. package/dist/Separator.d.ts.map +1 -1
  97. package/dist/SharedElementTransition.d.ts +6 -4
  98. package/dist/SharedElementTransition.d.ts.map +1 -1
  99. package/dist/Slider.d.ts +12 -8
  100. package/dist/Slider.d.ts.map +1 -1
  101. package/dist/StepList.d.ts +90 -0
  102. package/dist/StepList.d.ts.map +1 -0
  103. package/dist/Switch.d.ts +11 -5
  104. package/dist/Switch.d.ts.map +1 -1
  105. package/dist/Table.d.ts +187 -23
  106. package/dist/Table.d.ts.map +1 -1
  107. package/dist/Tabs.d.ts +45 -9
  108. package/dist/Tabs.d.ts.map +1 -1
  109. package/dist/TagGroup.d.ts +12 -10
  110. package/dist/TagGroup.d.ts.map +1 -1
  111. package/dist/Text.d.ts +2 -2
  112. package/dist/TextField.d.ts +15 -11
  113. package/dist/TextField.d.ts.map +1 -1
  114. package/dist/TimeField.d.ts +6 -6
  115. package/dist/TimeField.d.ts.map +1 -1
  116. package/dist/Toast.d.ts +29 -14
  117. package/dist/Toast.d.ts.map +1 -1
  118. package/dist/ToggleButton.d.ts +11 -5
  119. package/dist/ToggleButton.d.ts.map +1 -1
  120. package/dist/ToggleButtonGroup.d.ts +7 -7
  121. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  122. package/dist/Toolbar.d.ts +7 -3
  123. package/dist/Toolbar.d.ts.map +1 -1
  124. package/dist/Tooltip.d.ts +50 -8
  125. package/dist/Tooltip.d.ts.map +1 -1
  126. package/dist/Tree.d.ts +66 -17
  127. package/dist/Tree.d.ts.map +1 -1
  128. package/dist/Virtualizer.d.ts +12 -12
  129. package/dist/Virtualizer.d.ts.map +1 -1
  130. package/dist/VirtualizerLayouts.d.ts +2 -2
  131. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  132. package/dist/VisuallyHidden.d.ts +1 -1
  133. package/dist/VisuallyHidden.d.ts.map +1 -1
  134. package/dist/contexts.d.ts +5 -1
  135. package/dist/contexts.d.ts.map +1 -1
  136. package/dist/index.d.ts +73 -71
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +23247 -18564
  139. package/dist/index.js.map +1 -1
  140. package/dist/index.jsx +18110 -0
  141. package/dist/index.jsx.map +1 -0
  142. package/dist/useDragAndDrop.d.ts +13 -13
  143. package/dist/useDragAndDrop.d.ts.map +1 -1
  144. package/dist/utils.d.ts +2 -2
  145. package/dist/utils.d.ts.map +1 -1
  146. package/dist/virtualizer/Layout.d.ts +1 -1
  147. package/dist/virtualizer/Layout.d.ts.map +1 -1
  148. package/package.json +31 -32
  149. package/src/ActionBar.tsx +75 -72
  150. package/src/ActionGroup.tsx +53 -61
  151. package/src/Alert.tsx +17 -42
  152. package/src/Autocomplete.tsx +39 -44
  153. package/src/Breadcrumbs.tsx +149 -80
  154. package/src/Button.tsx +267 -70
  155. package/src/Calendar.tsx +218 -138
  156. package/src/Checkbox.tsx +413 -121
  157. package/src/Collection.tsx +67 -58
  158. package/src/Color.tsx +803 -380
  159. package/src/ColorEditor.tsx +131 -149
  160. package/src/ComboBox.tsx +414 -249
  161. package/src/ContextualHelpTrigger.tsx +86 -74
  162. package/src/DateField.tsx +185 -91
  163. package/src/DatePicker.tsx +524 -213
  164. package/src/DateRangePickerContext.tsx +44 -0
  165. package/src/Dialog.tsx +156 -118
  166. package/src/Disclosure.tsx +127 -80
  167. package/src/DragAndDrop.tsx +60 -54
  168. package/src/DragPreview.tsx +13 -11
  169. package/src/DropZone.tsx +42 -22
  170. package/src/FieldError.tsx +45 -23
  171. package/src/FileTrigger.tsx +19 -19
  172. package/src/Focusable.tsx +21 -24
  173. package/src/Form.tsx +71 -16
  174. package/src/GridList.tsx +273 -197
  175. package/src/HiddenDateInput.tsx +153 -0
  176. package/src/HiddenTimeInput.tsx +133 -0
  177. package/src/Icon.tsx +22 -43
  178. package/src/Keyboard.tsx +3 -3
  179. package/src/Landmark.tsx +37 -63
  180. package/src/Link.tsx +125 -75
  181. package/src/ListBox.tsx +332 -233
  182. package/src/ListDropTargetDelegate.ts +81 -80
  183. package/src/Menu.tsx +1023 -274
  184. package/src/Meter.tsx +38 -56
  185. package/src/Modal.tsx +243 -175
  186. package/src/NumberField.tsx +139 -143
  187. package/src/Popover.tsx +386 -233
  188. package/src/Pressable.tsx +21 -21
  189. package/src/ProgressBar.tsx +48 -57
  190. package/src/RadioGroup.tsx +524 -122
  191. package/src/RangeCalendar.tsx +157 -90
  192. package/src/RouterProvider.tsx +30 -47
  193. package/src/SearchField.tsx +362 -143
  194. package/src/Select.tsx +656 -233
  195. package/src/SelectionIndicator.tsx +18 -15
  196. package/src/Separator.tsx +47 -49
  197. package/src/SharedElementTransition.tsx +103 -97
  198. package/src/Slider.tsx +138 -98
  199. package/src/StepList.tsx +272 -0
  200. package/src/Switch.tsx +93 -46
  201. package/src/Table.tsx +1308 -342
  202. package/src/Tabs.tsx +324 -103
  203. package/src/TagGroup.tsx +139 -126
  204. package/src/Text.tsx +3 -3
  205. package/src/TextField.tsx +389 -79
  206. package/src/TimeField.tsx +136 -76
  207. package/src/Toast.tsx +209 -157
  208. package/src/ToggleButton.tsx +47 -37
  209. package/src/ToggleButtonGroup.tsx +39 -34
  210. package/src/Toolbar.tsx +54 -69
  211. package/src/Tooltip.tsx +387 -119
  212. package/src/Tree.tsx +651 -368
  213. package/src/Virtualizer.tsx +208 -180
  214. package/src/VirtualizerLayouts.ts +45 -30
  215. package/src/VisuallyHidden.tsx +19 -19
  216. package/src/contexts.ts +29 -37
  217. package/src/index.ts +110 -195
  218. package/src/useDragAndDrop.ts +87 -71
  219. package/src/utils.tsx +40 -55
  220. package/src/virtualizer/Layout.ts +14 -22
  221. package/dist/index.ssr.js +0 -16996
  222. package/dist/index.ssr.js.map +0 -1
package/src/Checkbox.tsx CHANGED
@@ -11,23 +11,27 @@ import {
11
11
  createContext,
12
12
  useContext,
13
13
  createMemo,
14
+ createSignal,
15
+ createUniqueId,
14
16
  splitProps,
15
- } from 'solid-js';
17
+ Show,
18
+ } from "solid-js";
16
19
  import {
17
20
  createCheckbox,
18
21
  createCheckboxGroup,
19
22
  createCheckboxGroupItem,
20
23
  createFocusRing,
21
24
  createHover,
25
+ mergeProps,
22
26
  type AriaCheckboxProps,
23
27
  type AriaCheckboxGroupProps,
24
- } from '@proyecto-viviana/solidaria';
28
+ } from "@proyecto-viviana/solidaria";
25
29
  import {
26
30
  createToggleState,
27
31
  createCheckboxGroupState,
28
32
  type CheckboxGroupState,
29
- } from '@proyecto-viviana/solid-stately';
30
- import { VisuallyHidden } from './VisuallyHidden';
33
+ } from "@proyecto-viviana/solid-stately";
34
+ import { VisuallyHidden } from "./VisuallyHidden";
31
35
  import {
32
36
  type RenderChildren,
33
37
  type ClassNameOrFunction,
@@ -35,11 +39,19 @@ import {
35
39
  type SlotProps,
36
40
  useRenderProps,
37
41
  filterDOMProps,
38
- } from './utils';
42
+ } from "./utils";
43
+ import { FormContext, type FormProps } from "./Form";
39
44
 
40
- // ============================================
41
- // TYPES
42
- // ============================================
45
+ type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
46
+
47
+ function assignRef<T>(ref: RefLike<T>, el: T): void {
48
+ if (!ref) return;
49
+ if (typeof ref === "function") {
50
+ ref(el);
51
+ } else {
52
+ ref.current = el;
53
+ }
54
+ }
43
55
 
44
56
  export interface CheckboxGroupRenderProps {
45
57
  /** Whether the checkbox group is disabled. */
@@ -78,40 +90,107 @@ export interface CheckboxRenderProps {
78
90
  }
79
91
 
80
92
  export interface CheckboxGroupProps
81
- extends Omit<AriaCheckboxGroupProps, 'children' | 'label' | 'description' | 'errorMessage'>,
82
- SlotProps {
93
+ extends Omit<AriaCheckboxGroupProps, "children" | "label">, SlotProps {
83
94
  /** The children of the component. A function may be provided to receive render props. */
84
95
  children?: RenderChildren<CheckboxGroupRenderProps>;
85
96
  /** The CSS className for the element. */
86
97
  class?: ClassNameOrFunction<CheckboxGroupRenderProps>;
87
98
  /** The inline style for the element. */
88
99
  style?: StyleOrFunction<CheckboxGroupRenderProps>;
100
+ /** Ref for the checkbox group root element. */
101
+ ref?: RefLike<HTMLDivElement>;
89
102
  }
90
103
 
91
- export interface CheckboxProps
92
- extends Omit<AriaCheckboxProps, 'children'>,
93
- SlotProps {
104
+ export interface CheckboxProps extends Omit<AriaCheckboxProps, "children">, SlotProps {
94
105
  /** The children of the component. A function may be provided to receive render props. */
95
106
  children?: RenderChildren<CheckboxRenderProps>;
96
107
  /** The CSS className for the element. */
97
108
  class?: ClassNameOrFunction<CheckboxRenderProps>;
98
109
  /** The inline style for the element. */
99
110
  style?: StyleOrFunction<CheckboxRenderProps>;
111
+ /** Custom renderer for the outer label element. */
112
+ render?: (
113
+ props: JSX.LabelHTMLAttributes<HTMLLabelElement>,
114
+ renderProps: CheckboxRenderProps,
115
+ ) => JSX.Element;
116
+ /** Ref for the outer label element. */
117
+ ref?: RefLike<HTMLLabelElement>;
118
+ /** Ref for the underlying input element. */
119
+ inputRef?: RefLike<HTMLInputElement>;
100
120
  /** Whether the checkbox is indeterminate. */
101
121
  isIndeterminate?: boolean;
122
+ /** A description for the checkbox. */
123
+ description?: JSX.Element;
124
+ /** An error message for the checkbox. */
125
+ errorMessage?: JSX.Element;
126
+ /** Handler called when hover starts. */
127
+ onHoverStart?: () => void;
128
+ /** Handler called when hover ends. */
129
+ onHoverEnd?: () => void;
130
+ /** Handler called when hover state changes. */
131
+ onHoverChange?: (isHovered: boolean) => void;
102
132
  }
103
133
 
104
- // ============================================
105
- // CONTEXT
106
- // ============================================
107
-
108
134
  export const CheckboxGroupContext = createContext<CheckboxGroupProps | null>(null);
109
135
  export const CheckboxGroupStateContext = createContext<CheckboxGroupState | null>(null);
110
- export const CheckboxContext = createContext<CheckboxProps | null>(null);
136
+ export interface CheckboxContextValue extends CheckboxProps {
137
+ slots?: Record<string, CheckboxProps>;
138
+ }
139
+ export const CheckboxContext = createContext<CheckboxContextValue | null>(null);
140
+
141
+ type PropsWithValidationBehavior = {
142
+ validationBehavior?: "aria" | "native";
143
+ };
144
+
145
+ function withFormValidationBehavior<T extends PropsWithValidationBehavior>(
146
+ props: T,
147
+ formContext: FormProps | null,
148
+ ): T {
149
+ if (!formContext?.validationBehavior) {
150
+ return props;
151
+ }
111
152
 
112
- // ============================================
113
- // CHECKBOX GROUP COMPONENT
114
- // ============================================
153
+ return new Proxy(props, {
154
+ get(target, property, receiver) {
155
+ const localValue = Reflect.get(target, property, receiver);
156
+ if (property === "validationBehavior" && localValue === undefined) {
157
+ return formContext.validationBehavior;
158
+ }
159
+
160
+ return localValue;
161
+ },
162
+ has(target, property) {
163
+ return (
164
+ Reflect.has(target, property) ||
165
+ (property === "validationBehavior" && formContext.validationBehavior !== undefined)
166
+ );
167
+ },
168
+ ownKeys(target) {
169
+ const keys = new Set(Reflect.ownKeys(target));
170
+ if (formContext.validationBehavior !== undefined) {
171
+ keys.add("validationBehavior");
172
+ }
173
+
174
+ return Array.from(keys);
175
+ },
176
+ getOwnPropertyDescriptor(target, property) {
177
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
178
+ if (descriptor) {
179
+ return descriptor;
180
+ }
181
+
182
+ if (property === "validationBehavior" && formContext.validationBehavior !== undefined) {
183
+ return {
184
+ enumerable: true,
185
+ configurable: true,
186
+ get: () => formContext.validationBehavior,
187
+ };
188
+ }
189
+
190
+ return undefined;
191
+ },
192
+ });
193
+ }
115
194
 
116
195
  /**
117
196
  * A checkbox group allows a user to select multiple items from a list of options.
@@ -125,28 +204,42 @@ export const CheckboxContext = createContext<CheckboxProps | null>(null);
125
204
  * ```
126
205
  */
127
206
  export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
128
- const [local, ariaProps] = splitProps(props, [
129
- 'class',
130
- 'style',
131
- 'slot',
207
+ const formContext = useContext(FormContext);
208
+ const mergedProps = withFormValidationBehavior(props, formContext);
209
+ const [local, ariaProps] = splitProps(mergedProps, [
210
+ "class",
211
+ "style",
212
+ "slot",
213
+ "ref",
214
+ "description",
215
+ "errorMessage",
216
+ "children",
132
217
  ]);
133
218
 
134
- // Create checkbox group state
135
219
  // Use getters to ensure props are read lazily inside reactive contexts
136
- const state = createCheckboxGroupState({
137
- get value() { return ariaProps.value; },
138
- get defaultValue() { return ariaProps.defaultValue; },
139
- get onChange() { return ariaProps.onChange; },
140
- get isDisabled() { return ariaProps.isDisabled; },
141
- get isReadOnly() { return ariaProps.isReadOnly; },
142
- get isRequired() { return ariaProps.isRequired; },
143
- get isInvalid() { return ariaProps.isInvalid; },
144
- });
220
+ const state = createCheckboxGroupState(() => ({
221
+ value: ariaProps.value,
222
+ defaultValue: ariaProps.defaultValue,
223
+ onChange: ariaProps.onChange,
224
+ isDisabled: ariaProps.isDisabled,
225
+ isReadOnly: ariaProps.isReadOnly,
226
+ isRequired: ariaProps.isRequired,
227
+ isInvalid: ariaProps.isInvalid,
228
+ validationState: ariaProps.validationState,
229
+ validate: ariaProps.validate,
230
+ validationBehavior: ariaProps.validationBehavior,
231
+ name: ariaProps.name,
232
+ }));
145
233
 
146
- // Create checkbox group aria props
147
- const groupAria = createCheckboxGroup(() => ariaProps, state);
234
+ const groupAria = createCheckboxGroup(
235
+ () => ({
236
+ ...ariaProps,
237
+ description: local.description,
238
+ errorMessage: local.errorMessage,
239
+ }),
240
+ state,
241
+ );
148
242
 
149
- // Render props values
150
243
  const renderValues = createMemo<CheckboxGroupRenderProps>(() => ({
151
244
  isDisabled: state.isDisabled,
152
245
  isReadOnly: state.isReadOnly,
@@ -155,35 +248,67 @@ export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
155
248
  state,
156
249
  }));
157
250
 
158
- // Resolve render props
159
251
  const renderProps = useRenderProps(
160
252
  {
161
- children: props.children,
253
+ children: local.children,
162
254
  class: local.class,
163
255
  style: local.style,
164
- defaultClassName: 'solidaria-CheckboxGroup',
256
+ defaultClassName: "solidaria-CheckboxGroup",
165
257
  },
166
- renderValues
258
+ renderValues,
167
259
  );
168
260
 
169
- // Filter DOM props
170
261
  const domProps = createMemo(() => filterDOMProps(ariaProps, { global: true }));
171
262
 
172
- // Remove ref from spread props to avoid type conflicts
173
263
  const cleanGroupProps = () => {
174
264
  const { ref: _ref, ...rest } = groupAria.groupProps as Record<string, unknown>;
175
265
  return rest;
176
266
  };
267
+ const setGroupRef = (el: HTMLDivElement) => {
268
+ assignRef(local.ref, el);
269
+ };
270
+ const groupDescribedBy = () => {
271
+ const ids = [
272
+ (cleanGroupProps() as { "aria-describedby"?: string })["aria-describedby"],
273
+ local.description ? groupAria.descriptionProps.id : undefined,
274
+ groupAria.isInvalid && local.errorMessage ? groupAria.errorMessageProps.id : undefined,
275
+ ]
276
+ .filter(Boolean)
277
+ .join(" ")
278
+ .split(" ")
279
+ .filter(Boolean);
280
+ return ids.length ? Array.from(new Set(ids)).join(" ") : undefined;
281
+ };
177
282
 
178
- // Resolve children - we need to pass render props if children is a function
179
- // but we use props.children directly (not renderProps.renderChildren())
180
- // to preserve SolidJS context propagation for nested components like Checkbox
181
- const resolvedChildren = () => {
182
- const children = props.children;
183
- if (typeof children === 'function') {
184
- return children(renderValues());
185
- }
186
- return children;
283
+ const GroupChildren = () => {
284
+ const childRenderValues: CheckboxGroupRenderProps = {
285
+ get isDisabled() {
286
+ return state.isDisabled;
287
+ },
288
+ get isReadOnly() {
289
+ return state.isReadOnly;
290
+ },
291
+ get isRequired() {
292
+ return state.isRequired();
293
+ },
294
+ get isInvalid() {
295
+ return groupAria.isInvalid;
296
+ },
297
+ get state() {
298
+ return state;
299
+ },
300
+ };
301
+ const renderedChildren = createMemo(() => {
302
+ const children = local.children;
303
+ if (typeof children === "function") {
304
+ return children.length > 0
305
+ ? children(childRenderValues)
306
+ : (children as unknown as () => JSX.Element)();
307
+ }
308
+ return children;
309
+ });
310
+
311
+ return <>{renderedChildren()}</>;
187
312
  };
188
313
 
189
314
  return (
@@ -191,6 +316,8 @@ export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
191
316
  <div
192
317
  {...domProps()}
193
318
  {...cleanGroupProps()}
319
+ ref={setGroupRef}
320
+ aria-describedby={groupDescribedBy()}
194
321
  class={renderProps.class()}
195
322
  style={renderProps.style()}
196
323
  data-disabled={state.isDisabled || undefined}
@@ -198,16 +325,22 @@ export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
198
325
  data-required={ariaProps.isRequired || undefined}
199
326
  data-invalid={groupAria.isInvalid || undefined}
200
327
  >
201
- {resolvedChildren()}
328
+ <GroupChildren />
329
+ <Show when={local.description}>
330
+ <div {...(groupAria.descriptionProps as unknown as JSX.HTMLAttributes<HTMLDivElement>)}>
331
+ {local.description}
332
+ </div>
333
+ </Show>
334
+ <Show when={groupAria.isInvalid && local.errorMessage}>
335
+ <div {...(groupAria.errorMessageProps as unknown as JSX.HTMLAttributes<HTMLDivElement>)}>
336
+ {local.errorMessage}
337
+ </div>
338
+ </Show>
202
339
  </div>
203
340
  </CheckboxGroupStateContext.Provider>
204
341
  );
205
342
  }
206
343
 
207
- // ============================================
208
- // CHECKBOX COMPONENT
209
- // ============================================
210
-
211
344
  /**
212
345
  * A checkbox allows a user to select multiple items from a list of individual items,
213
346
  * or to mark one individual item as selected.
@@ -227,84 +360,123 @@ export function CheckboxGroup(props: CheckboxGroupProps): JSX.Element {
227
360
  * ```
228
361
  */
229
362
  export function Checkbox(props: CheckboxProps): JSX.Element {
230
- let inputRef: HTMLInputElement | null = null;
363
+ const [inputElement, setInputElement] = createSignal<HTMLInputElement | null>(null);
364
+ const formContext = useContext(FormContext);
365
+ const contextProps = useContext(CheckboxContext);
366
+ const contextSlotProps = contextProps?.slots?.[props.slot ?? "default"];
367
+ const contextBaseProps = createMemo<CheckboxProps>(() => {
368
+ if (!contextProps) return {};
369
+ const { slots: _slots, ...rest } = contextProps;
370
+ return rest;
371
+ });
372
+ const mergedProps = contextProps
373
+ ? (mergeProps(contextBaseProps(), contextSlotProps ?? {}, props) as CheckboxProps)
374
+ : props;
375
+ const propsWithFormBehavior = withFormValidationBehavior(mergedProps, formContext);
376
+ const inputRefs = createMemo(
377
+ () =>
378
+ [contextBaseProps().inputRef, contextSlotProps?.inputRef, props.inputRef].filter(
379
+ Boolean,
380
+ ) as RefLike<HTMLInputElement>[],
381
+ );
231
382
 
232
- const [local, ariaProps] = splitProps(props, [
233
- 'class',
234
- 'style',
235
- 'slot',
236
- 'isIndeterminate',
383
+ const [local, ariaProps] = splitProps(propsWithFormBehavior, [
384
+ "class",
385
+ "style",
386
+ "render",
387
+ "ref",
388
+ "inputRef",
389
+ "slot",
390
+ "isIndeterminate",
391
+ "description",
392
+ "errorMessage",
393
+ "onHoverStart",
394
+ "onHoverEnd",
395
+ "onHoverChange",
237
396
  ]);
397
+ const descriptionId = createUniqueId();
398
+ const errorMessageId = createUniqueId();
399
+
400
+ const inputAriaProps = createMemo(() => {
401
+ const clean: Record<string, unknown> = {};
402
+ for (const key in ariaProps as Record<string, unknown>) {
403
+ if (!key.startsWith("data-")) {
404
+ clean[key] = (ariaProps as Record<string, unknown>)[key];
405
+ }
406
+ }
407
+ return clean as typeof ariaProps;
408
+ });
238
409
 
239
- // Check if we're inside a CheckboxGroup
240
410
  const groupState = useContext(CheckboxGroupStateContext);
241
411
 
242
- // Create appropriate state/aria hooks based on context
243
412
  let isSelected: Accessor<boolean>;
244
413
  let isPressed: Accessor<boolean>;
245
- let isDisabled: boolean;
246
- let isReadOnly: boolean;
247
- let isInvalid: boolean;
414
+ let isDisabled: Accessor<boolean>;
415
+ let isReadOnly: Accessor<boolean>;
416
+ let isInvalid: Accessor<boolean>;
248
417
  let labelProps: JSX.LabelHTMLAttributes<HTMLLabelElement>;
249
- let inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
418
+ let inputProps: () => JSX.InputHTMLAttributes<HTMLInputElement>;
250
419
 
251
420
  if (groupState) {
252
- // Inside a CheckboxGroup - use group item
253
421
  const itemAria = createCheckboxGroupItem(
254
422
  () => ({
255
- ...ariaProps,
256
- value: ariaProps.value ?? '',
257
- children: typeof props.children === 'function' ? true : props.children,
423
+ ...inputAriaProps(),
424
+ value: inputAriaProps().value ?? "",
425
+ children: typeof mergedProps.children === "function" ? true : mergedProps.children,
258
426
  }),
259
427
  groupState,
260
- () => inputRef
428
+ inputElement,
261
429
  );
262
430
  isSelected = itemAria.isSelected;
263
431
  isPressed = itemAria.isPressed;
264
- isDisabled = itemAria.isDisabled;
265
- isReadOnly = itemAria.isReadOnly;
266
- isInvalid = itemAria.isInvalid;
267
432
  labelProps = itemAria.labelProps;
268
- inputProps = itemAria.inputProps;
433
+ inputProps = () => itemAria.inputProps;
269
434
  } else {
270
- // Standalone checkbox
271
435
  // Use getters to ensure props are read lazily inside reactive contexts
272
- const state = createToggleState({
273
- get isSelected() { return ariaProps.isSelected; },
274
- get defaultSelected() { return ariaProps.defaultSelected; },
275
- get onChange() { return ariaProps.onChange; },
276
- get isReadOnly() { return ariaProps.isReadOnly; },
277
- });
436
+ const state = createToggleState(() => ({
437
+ isSelected: ariaProps.isSelected,
438
+ defaultSelected: ariaProps.defaultSelected,
439
+ onChange: ariaProps.onChange,
440
+ isReadOnly: ariaProps.isReadOnly,
441
+ }));
278
442
 
279
443
  const checkboxAria = createCheckbox(
280
444
  () => ({
281
- ...ariaProps,
445
+ ...inputAriaProps(),
282
446
  isIndeterminate: local.isIndeterminate,
283
- children: typeof props.children === 'function' ? true : props.children,
447
+ children: typeof mergedProps.children === "function" ? true : mergedProps.children,
284
448
  }),
285
449
  state,
286
- () => inputRef
450
+ inputElement,
287
451
  );
288
452
  isSelected = checkboxAria.isSelected;
289
453
  isPressed = checkboxAria.isPressed;
290
- isDisabled = checkboxAria.isDisabled;
291
- isReadOnly = checkboxAria.isReadOnly;
292
- isInvalid = checkboxAria.isInvalid;
293
454
  labelProps = checkboxAria.labelProps;
294
- inputProps = checkboxAria.inputProps;
455
+ inputProps = () => checkboxAria.inputProps;
295
456
  }
457
+ isDisabled = () => inputProps().disabled === true;
458
+ isReadOnly = () => inputProps()["aria-readonly"] === true;
459
+ isInvalid = () => inputProps()["aria-invalid"] === true;
460
+ const describedBy = () => {
461
+ const ids = [
462
+ ariaProps["aria-describedby"],
463
+ local.description ? descriptionId : undefined,
464
+ isInvalid() && local.errorMessage ? errorMessageId : undefined,
465
+ ].filter(Boolean);
466
+ return ids.length ? ids.join(" ") : undefined;
467
+ };
296
468
 
297
- // Create focus ring
298
469
  const { isFocused, isFocusVisible, focusProps } = createFocusRing();
299
470
 
300
- // Create hover
301
471
  const { isHovered, hoverProps } = createHover({
302
472
  get isDisabled() {
303
- return isDisabled || isReadOnly;
473
+ return isDisabled() || isReadOnly();
304
474
  },
475
+ onHoverStart: local.onHoverStart,
476
+ onHoverEnd: local.onHoverEnd,
477
+ onHoverChange: local.onHoverChange,
305
478
  });
306
479
 
307
- // Render props values
308
480
  const renderValues = createMemo<CheckboxRenderProps>(() => ({
309
481
  isSelected: isSelected(),
310
482
  isIndeterminate: local.isIndeterminate ?? false,
@@ -312,24 +484,58 @@ export function Checkbox(props: CheckboxProps): JSX.Element {
312
484
  isPressed: isPressed(),
313
485
  isFocused: isFocused(),
314
486
  isFocusVisible: isFocusVisible(),
315
- isDisabled,
316
- isReadOnly,
317
- isInvalid,
487
+ isDisabled: isDisabled(),
488
+ isReadOnly: isReadOnly(),
489
+ isInvalid: isInvalid(),
318
490
  isRequired: ariaProps.isRequired ?? false,
319
491
  }));
320
492
 
321
- // Resolve render props
322
493
  const renderProps = useRenderProps(
323
494
  {
324
- children: props.children,
495
+ children: mergedProps.children,
325
496
  class: local.class,
326
497
  style: local.style,
327
- defaultClassName: 'solidaria-Checkbox',
498
+ defaultClassName: "solidaria-Checkbox",
328
499
  },
329
- renderValues
500
+ renderValues,
330
501
  );
502
+ const childRenderValues: CheckboxRenderProps = {
503
+ get isSelected() {
504
+ return isSelected();
505
+ },
506
+ get isIndeterminate() {
507
+ return local.isIndeterminate ?? false;
508
+ },
509
+ get isHovered() {
510
+ return isHovered();
511
+ },
512
+ get isPressed() {
513
+ return isPressed();
514
+ },
515
+ get isFocused() {
516
+ return isFocused();
517
+ },
518
+ get isFocusVisible() {
519
+ return isFocusVisible();
520
+ },
521
+ get isDisabled() {
522
+ return isDisabled();
523
+ },
524
+ get isReadOnly() {
525
+ return isReadOnly();
526
+ },
527
+ get isInvalid() {
528
+ return isInvalid();
529
+ },
530
+ get isRequired() {
531
+ return ariaProps.isRequired ?? false;
532
+ },
533
+ };
534
+ const checkboxChildren = () => {
535
+ const children = mergedProps.children;
536
+ return typeof children === "function" ? children(childRenderValues) : children;
537
+ };
331
538
 
332
- // Filter DOM props
333
539
  const domProps = createMemo(() => {
334
540
  const filtered = filterDOMProps(ariaProps, { global: true });
335
541
  delete (filtered as Record<string, unknown>).id;
@@ -337,7 +543,6 @@ export function Checkbox(props: CheckboxProps): JSX.Element {
337
543
  return filtered;
338
544
  });
339
545
 
340
- // Remove ref from spread props to avoid type conflicts
341
546
  const cleanLabelProps = () => {
342
547
  const { ref: _ref1, ...rest } = labelProps as Record<string, unknown>;
343
548
  return rest;
@@ -347,40 +552,127 @@ export function Checkbox(props: CheckboxProps): JSX.Element {
347
552
  return rest;
348
553
  };
349
554
  const cleanInputProps = () => {
350
- const { ref: _ref3, ...rest } = inputProps as Record<string, unknown>;
555
+ const {
556
+ ref: _ref3,
557
+ onFocus: _onFocus,
558
+ onBlur: _onBlur,
559
+ ...rest
560
+ } = inputProps() as Record<string, unknown>;
351
561
  return rest;
352
562
  };
353
563
  const cleanFocusProps = () => {
354
- const { ref: _ref4, ...rest } = focusProps as Record<string, unknown>;
564
+ const {
565
+ ref: _ref4,
566
+ onFocus: _onFocus,
567
+ onBlur: _onBlur,
568
+ ...rest
569
+ } = focusProps as Record<string, unknown>;
355
570
  return rest;
356
571
  };
357
-
358
- return (
572
+ const handleInputFocus: JSX.EventHandler<HTMLInputElement, FocusEvent> = (event) => {
573
+ (
574
+ inputProps() as unknown as { onFocus?: JSX.EventHandler<HTMLInputElement, FocusEvent> }
575
+ ).onFocus?.(event);
576
+ (
577
+ focusProps as unknown as { onFocus?: JSX.EventHandler<HTMLInputElement, FocusEvent> }
578
+ ).onFocus?.(event);
579
+ };
580
+ const handleInputBlur: JSX.EventHandler<HTMLInputElement, FocusEvent> = (event) => {
581
+ (
582
+ inputProps() as unknown as { onBlur?: JSX.EventHandler<HTMLInputElement, FocusEvent> }
583
+ ).onBlur?.(event);
584
+ (focusProps as unknown as { onBlur?: JSX.EventHandler<HTMLInputElement, FocusEvent> }).onBlur?.(
585
+ event,
586
+ );
587
+ };
588
+ const setLabelRef = (el: HTMLLabelElement) => {
589
+ assignRef(local.ref, el);
590
+ };
591
+ const setInputRef = (el: HTMLInputElement) => {
592
+ setInputElement(el);
593
+ for (const ref of inputRefs()) {
594
+ assignRef(ref, el);
595
+ }
596
+ };
597
+ const hiddenInput = (
598
+ <VisuallyHidden>
599
+ <input
600
+ ref={setInputRef}
601
+ {...cleanInputProps()}
602
+ {...cleanFocusProps()}
603
+ onFocus={handleInputFocus}
604
+ onBlur={handleInputBlur}
605
+ aria-describedby={describedBy()}
606
+ />
607
+ </VisuallyHidden>
608
+ );
609
+ const labelChildren = () => (
610
+ <>
611
+ {hiddenInput}
612
+ {checkboxChildren()}
613
+ <Show when={local.description}>
614
+ <span id={descriptionId} slot="description">
615
+ {local.description}
616
+ </span>
617
+ </Show>
618
+ <Show when={isInvalid() && local.errorMessage}>
619
+ <span id={errorMessageId} slot="errorMessage">
620
+ {local.errorMessage}
621
+ </span>
622
+ </Show>
623
+ </>
624
+ );
625
+ const rootProps = createMemo(
626
+ () =>
627
+ ({
628
+ ...domProps(),
629
+ ...cleanLabelProps(),
630
+ ...cleanHoverProps(),
631
+ class: renderProps.class(),
632
+ style: renderProps.style(),
633
+ slot: local.slot,
634
+ "data-selected": isSelected() || undefined,
635
+ "data-indeterminate": local.isIndeterminate || undefined,
636
+ "data-pressed": isPressed() || undefined,
637
+ "data-hovered": isHovered() || undefined,
638
+ "data-focused": isFocused() || undefined,
639
+ "data-focus-visible": isFocusVisible() || undefined,
640
+ "data-disabled": isDisabled() || undefined,
641
+ "data-readonly": isReadOnly() || undefined,
642
+ "data-invalid": isInvalid() || undefined,
643
+ "data-required": ariaProps.isRequired || undefined,
644
+ }) as JSX.LabelHTMLAttributes<HTMLLabelElement>,
645
+ );
646
+ const customRootProps = () =>
647
+ ({
648
+ ...rootProps(),
649
+ ref: setLabelRef,
650
+ children: labelChildren(),
651
+ }) as JSX.LabelHTMLAttributes<HTMLLabelElement>;
652
+
653
+ return local.render ? (
654
+ local.render(customRootProps(), renderValues())
655
+ ) : (
359
656
  <label
360
657
  {...domProps()}
361
658
  {...cleanLabelProps()}
362
659
  {...cleanHoverProps()}
660
+ ref={setLabelRef}
363
661
  class={renderProps.class()}
364
662
  style={renderProps.style()}
663
+ slot={local.slot}
365
664
  data-selected={isSelected() || undefined}
366
665
  data-indeterminate={local.isIndeterminate || undefined}
367
666
  data-pressed={isPressed() || undefined}
368
667
  data-hovered={isHovered() || undefined}
369
668
  data-focused={isFocused() || undefined}
370
669
  data-focus-visible={isFocusVisible() || undefined}
371
- data-disabled={isDisabled || undefined}
372
- data-readonly={isReadOnly || undefined}
373
- data-invalid={isInvalid || undefined}
670
+ data-disabled={isDisabled() || undefined}
671
+ data-readonly={isReadOnly() || undefined}
672
+ data-invalid={isInvalid() || undefined}
374
673
  data-required={ariaProps.isRequired || undefined}
375
674
  >
376
- <VisuallyHidden>
377
- <input
378
- ref={(el) => (inputRef = el)}
379
- {...cleanInputProps()}
380
- {...cleanFocusProps()}
381
- />
382
- </VisuallyHidden>
383
- {renderProps.renderChildren()}
675
+ {labelChildren()}
384
676
  </label>
385
677
  );
386
678
  }