@proyecto-viviana/solidaria-components 0.2.1 → 0.2.3

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 (62) hide show
  1. package/dist/Color.d.ts +2 -6
  2. package/dist/Color.d.ts.map +1 -1
  3. package/dist/ComboBox.d.ts +3 -3
  4. package/dist/ComboBox.d.ts.map +1 -1
  5. package/dist/GridList.d.ts +2 -2
  6. package/dist/GridList.d.ts.map +1 -1
  7. package/dist/ListBox.d.ts +5 -5
  8. package/dist/ListBox.d.ts.map +1 -1
  9. package/dist/Menu.d.ts +3 -3
  10. package/dist/Menu.d.ts.map +1 -1
  11. package/dist/Select.d.ts +3 -3
  12. package/dist/Select.d.ts.map +1 -1
  13. package/dist/Table.d.ts +2 -2
  14. package/dist/Table.d.ts.map +1 -1
  15. package/dist/Tabs.d.ts +1 -1
  16. package/dist/Tabs.d.ts.map +1 -1
  17. package/dist/index.js +56 -56
  18. package/dist/index.js.map +2 -2
  19. package/dist/index.ssr.js +56 -56
  20. package/dist/index.ssr.js.map +2 -2
  21. package/package.json +10 -8
  22. package/src/Autocomplete.tsx +174 -0
  23. package/src/Breadcrumbs.tsx +264 -0
  24. package/src/Button.tsx +238 -0
  25. package/src/Calendar.tsx +471 -0
  26. package/src/Checkbox.tsx +387 -0
  27. package/src/Color.tsx +1370 -0
  28. package/src/ComboBox.tsx +824 -0
  29. package/src/DateField.tsx +337 -0
  30. package/src/DatePicker.tsx +367 -0
  31. package/src/Dialog.tsx +262 -0
  32. package/src/Disclosure.tsx +439 -0
  33. package/src/GridList.tsx +511 -0
  34. package/src/Landmark.tsx +203 -0
  35. package/src/Link.tsx +201 -0
  36. package/src/ListBox.tsx +346 -0
  37. package/src/Menu.tsx +544 -0
  38. package/src/Meter.tsx +157 -0
  39. package/src/Modal.tsx +433 -0
  40. package/src/NumberField.tsx +542 -0
  41. package/src/Popover.tsx +540 -0
  42. package/src/ProgressBar.tsx +162 -0
  43. package/src/RadioGroup.tsx +356 -0
  44. package/src/RangeCalendar.tsx +462 -0
  45. package/src/SearchField.tsx +479 -0
  46. package/src/Select.tsx +734 -0
  47. package/src/Separator.tsx +130 -0
  48. package/src/Slider.tsx +500 -0
  49. package/src/Switch.tsx +213 -0
  50. package/src/Table.tsx +857 -0
  51. package/src/Tabs.tsx +552 -0
  52. package/src/TagGroup.tsx +421 -0
  53. package/src/TextField.tsx +271 -0
  54. package/src/TimeField.tsx +455 -0
  55. package/src/Toast.tsx +503 -0
  56. package/src/Toolbar.tsx +160 -0
  57. package/src/Tooltip.tsx +423 -0
  58. package/src/Tree.tsx +551 -0
  59. package/src/VisuallyHidden.tsx +60 -0
  60. package/src/contexts.ts +74 -0
  61. package/src/index.ts +620 -0
  62. package/src/utils.tsx +329 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Checkbox and CheckboxGroup components for solidaria-components
3
+ *
4
+ * Pre-wired headless checkbox components that combine state + aria hooks.
5
+ * Port of react-aria-components/src/Checkbox.tsx
6
+ */
7
+
8
+ import {
9
+ type JSX,
10
+ type Accessor,
11
+ type ParentProps,
12
+ createContext,
13
+ useContext,
14
+ createMemo,
15
+ splitProps,
16
+ } from 'solid-js';
17
+ import {
18
+ createCheckbox,
19
+ createCheckboxGroup,
20
+ createCheckboxGroupItem,
21
+ createFocusRing,
22
+ createHover,
23
+ type AriaCheckboxProps,
24
+ type AriaCheckboxGroupProps,
25
+ } from '@proyecto-viviana/solidaria';
26
+ import {
27
+ createToggleState,
28
+ createCheckboxGroupState,
29
+ type CheckboxGroupState,
30
+ } from '@proyecto-viviana/solid-stately';
31
+ import { VisuallyHidden } from './VisuallyHidden';
32
+ import {
33
+ type RenderChildren,
34
+ type ClassNameOrFunction,
35
+ type StyleOrFunction,
36
+ type SlotProps,
37
+ useRenderProps,
38
+ filterDOMProps,
39
+ } from './utils';
40
+
41
+ // ============================================
42
+ // TYPES
43
+ // ============================================
44
+
45
+ export interface CheckboxGroupRenderProps {
46
+ /** Whether the checkbox group is disabled. */
47
+ isDisabled: boolean;
48
+ /** Whether the checkbox group is read only. */
49
+ isReadOnly: boolean;
50
+ /** Whether the checkbox group is required. */
51
+ isRequired: boolean;
52
+ /** Whether the checkbox group is invalid. */
53
+ isInvalid: boolean;
54
+ /** State of the checkbox group. */
55
+ state: CheckboxGroupState;
56
+ }
57
+
58
+ export interface CheckboxRenderProps {
59
+ /** Whether the checkbox is selected. */
60
+ isSelected: boolean;
61
+ /** Whether the checkbox is indeterminate. */
62
+ isIndeterminate: boolean;
63
+ /** Whether the checkbox is currently hovered with a mouse. */
64
+ isHovered: boolean;
65
+ /** Whether the checkbox is currently in a pressed state. */
66
+ isPressed: boolean;
67
+ /** Whether the checkbox is focused, either via a mouse or keyboard. */
68
+ isFocused: boolean;
69
+ /** Whether the checkbox is keyboard focused. */
70
+ isFocusVisible: boolean;
71
+ /** Whether the checkbox is disabled. */
72
+ isDisabled: boolean;
73
+ /** Whether the checkbox is read only. */
74
+ isReadOnly: boolean;
75
+ /** Whether the checkbox is invalid. */
76
+ isInvalid: boolean;
77
+ /** Whether the checkbox is required. */
78
+ isRequired: boolean;
79
+ }
80
+
81
+ export interface CheckboxGroupProps
82
+ extends Omit<AriaCheckboxGroupProps, 'children' | 'label' | 'description' | 'errorMessage'>,
83
+ SlotProps {
84
+ /** The children of the component. A function may be provided to receive render props. */
85
+ children?: RenderChildren<CheckboxGroupRenderProps>;
86
+ /** The CSS className for the element. */
87
+ class?: ClassNameOrFunction<CheckboxGroupRenderProps>;
88
+ /** The inline style for the element. */
89
+ style?: StyleOrFunction<CheckboxGroupRenderProps>;
90
+ }
91
+
92
+ export interface CheckboxProps
93
+ extends Omit<AriaCheckboxProps, 'children'>,
94
+ SlotProps {
95
+ /** The children of the component. A function may be provided to receive render props. */
96
+ children?: RenderChildren<CheckboxRenderProps>;
97
+ /** The CSS className for the element. */
98
+ class?: ClassNameOrFunction<CheckboxRenderProps>;
99
+ /** The inline style for the element. */
100
+ style?: StyleOrFunction<CheckboxRenderProps>;
101
+ /** Whether the checkbox is indeterminate. */
102
+ isIndeterminate?: boolean;
103
+ }
104
+
105
+ // ============================================
106
+ // CONTEXT
107
+ // ============================================
108
+
109
+ export const CheckboxGroupContext = createContext<CheckboxGroupProps | null>(null);
110
+ export const CheckboxGroupStateContext = createContext<CheckboxGroupState | null>(null);
111
+ export const CheckboxContext = createContext<CheckboxProps | null>(null);
112
+
113
+ // ============================================
114
+ // CHECKBOX GROUP COMPONENT
115
+ // ============================================
116
+
117
+ /**
118
+ * A checkbox group allows a user to select multiple items from a list of options.
119
+ *
120
+ * @example
121
+ * ```tsx
122
+ * <CheckboxGroup>
123
+ * <Checkbox value="one">Option 1</Checkbox>
124
+ * <Checkbox value="two">Option 2</Checkbox>
125
+ * </CheckboxGroup>
126
+ * ```
127
+ */
128
+ export function CheckboxGroup(props: ParentProps<CheckboxGroupProps>): JSX.Element {
129
+ const [local, ariaProps] = splitProps(props, [
130
+ 'class',
131
+ 'style',
132
+ 'slot',
133
+ ]);
134
+
135
+ // Create checkbox group state
136
+ // Use getters to ensure props are read lazily inside reactive contexts
137
+ const state = createCheckboxGroupState({
138
+ get value() { return ariaProps.value; },
139
+ get defaultValue() { return ariaProps.defaultValue; },
140
+ get onChange() { return ariaProps.onChange; },
141
+ get isDisabled() { return ariaProps.isDisabled; },
142
+ get isReadOnly() { return ariaProps.isReadOnly; },
143
+ get isRequired() { return ariaProps.isRequired; },
144
+ get isInvalid() { return ariaProps.isInvalid; },
145
+ });
146
+
147
+ // Create checkbox group aria props
148
+ const groupAria = createCheckboxGroup(() => ariaProps, state);
149
+
150
+ // Render props values
151
+ const renderValues = createMemo<CheckboxGroupRenderProps>(() => ({
152
+ isDisabled: state.isDisabled,
153
+ isReadOnly: state.isReadOnly,
154
+ isRequired: ariaProps.isRequired ?? false,
155
+ isInvalid: groupAria.isInvalid,
156
+ state,
157
+ }));
158
+
159
+ // Resolve render props
160
+ const renderProps = useRenderProps(
161
+ {
162
+ children: props.children,
163
+ class: local.class,
164
+ style: local.style,
165
+ defaultClassName: 'solidaria-CheckboxGroup',
166
+ },
167
+ renderValues
168
+ );
169
+
170
+ // Filter DOM props
171
+ const domProps = createMemo(() => filterDOMProps(ariaProps, { global: true }));
172
+
173
+ // Remove ref from spread props to avoid type conflicts
174
+ const cleanGroupProps = () => {
175
+ const { ref: _ref, ...rest } = groupAria.groupProps as Record<string, unknown>;
176
+ return rest;
177
+ };
178
+
179
+ // Resolve children - we need to pass render props if children is a function
180
+ // but we use props.children directly (not renderProps.renderChildren())
181
+ // to preserve SolidJS context propagation for nested components like Checkbox
182
+ const resolvedChildren = () => {
183
+ const children = props.children;
184
+ if (typeof children === 'function') {
185
+ return children(renderValues());
186
+ }
187
+ return children;
188
+ };
189
+
190
+ return (
191
+ <CheckboxGroupStateContext.Provider value={state}>
192
+ <div
193
+ {...domProps()}
194
+ {...cleanGroupProps()}
195
+ class={renderProps.class()}
196
+ style={renderProps.style()}
197
+ data-disabled={state.isDisabled || undefined}
198
+ data-readonly={state.isReadOnly || undefined}
199
+ data-required={ariaProps.isRequired || undefined}
200
+ data-invalid={groupAria.isInvalid || undefined}
201
+ >
202
+ {resolvedChildren()}
203
+ </div>
204
+ </CheckboxGroupStateContext.Provider>
205
+ );
206
+ }
207
+
208
+ // ============================================
209
+ // CHECKBOX COMPONENT
210
+ // ============================================
211
+
212
+ /**
213
+ * A checkbox allows a user to select multiple items from a list of individual items,
214
+ * or to mark one individual item as selected.
215
+ *
216
+ * @example
217
+ * ```tsx
218
+ * <Checkbox>
219
+ * {({ isSelected }) => (
220
+ * <>
221
+ * <span class={`checkbox ${isSelected ? 'checked' : ''}`}>
222
+ * {isSelected && '✓'}
223
+ * </span>
224
+ * <span>Accept terms</span>
225
+ * </>
226
+ * )}
227
+ * </Checkbox>
228
+ * ```
229
+ */
230
+ export function Checkbox(props: CheckboxProps): JSX.Element {
231
+ let inputRef: HTMLInputElement | null = null;
232
+
233
+ const [local, ariaProps] = splitProps(props, [
234
+ 'class',
235
+ 'style',
236
+ 'slot',
237
+ 'isIndeterminate',
238
+ ]);
239
+
240
+ // Check if we're inside a CheckboxGroup
241
+ const groupState = useContext(CheckboxGroupStateContext);
242
+
243
+ // Create appropriate state/aria hooks based on context
244
+ let isSelected: Accessor<boolean>;
245
+ let isPressed: Accessor<boolean>;
246
+ let isDisabled: boolean;
247
+ let isReadOnly: boolean;
248
+ let isInvalid: boolean;
249
+ let labelProps: JSX.LabelHTMLAttributes<HTMLLabelElement>;
250
+ let inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
251
+
252
+ if (groupState) {
253
+ // Inside a CheckboxGroup - use group item
254
+ const itemAria = createCheckboxGroupItem(
255
+ () => ({
256
+ ...ariaProps,
257
+ value: ariaProps.value ?? '',
258
+ children: typeof props.children === 'function' ? true : props.children,
259
+ }),
260
+ groupState,
261
+ () => inputRef
262
+ );
263
+ isSelected = itemAria.isSelected;
264
+ isPressed = itemAria.isPressed;
265
+ isDisabled = itemAria.isDisabled;
266
+ isReadOnly = itemAria.isReadOnly;
267
+ isInvalid = itemAria.isInvalid;
268
+ labelProps = itemAria.labelProps;
269
+ inputProps = itemAria.inputProps;
270
+ } else {
271
+ // Standalone checkbox
272
+ // Use getters to ensure props are read lazily inside reactive contexts
273
+ const state = createToggleState({
274
+ get isSelected() { return ariaProps.isSelected; },
275
+ get defaultSelected() { return ariaProps.defaultSelected; },
276
+ get onChange() { return ariaProps.onChange; },
277
+ get isReadOnly() { return ariaProps.isReadOnly; },
278
+ });
279
+
280
+ const checkboxAria = createCheckbox(
281
+ () => ({
282
+ ...ariaProps,
283
+ isIndeterminate: local.isIndeterminate,
284
+ children: typeof props.children === 'function' ? true : props.children,
285
+ }),
286
+ state,
287
+ () => inputRef
288
+ );
289
+ isSelected = checkboxAria.isSelected;
290
+ isPressed = checkboxAria.isPressed;
291
+ isDisabled = checkboxAria.isDisabled;
292
+ isReadOnly = checkboxAria.isReadOnly;
293
+ isInvalid = checkboxAria.isInvalid;
294
+ labelProps = checkboxAria.labelProps;
295
+ inputProps = checkboxAria.inputProps;
296
+ }
297
+
298
+ // Create focus ring
299
+ const { isFocused, isFocusVisible, focusProps } = createFocusRing();
300
+
301
+ // Create hover
302
+ const { isHovered, hoverProps } = createHover({
303
+ get isDisabled() {
304
+ return isDisabled || isReadOnly;
305
+ },
306
+ });
307
+
308
+ // Render props values
309
+ const renderValues = createMemo<CheckboxRenderProps>(() => ({
310
+ isSelected: isSelected(),
311
+ isIndeterminate: local.isIndeterminate ?? false,
312
+ isHovered: isHovered(),
313
+ isPressed: isPressed(),
314
+ isFocused: isFocused(),
315
+ isFocusVisible: isFocusVisible(),
316
+ isDisabled,
317
+ isReadOnly,
318
+ isInvalid,
319
+ isRequired: ariaProps.isRequired ?? false,
320
+ }));
321
+
322
+ // Resolve render props
323
+ const renderProps = useRenderProps(
324
+ {
325
+ children: props.children,
326
+ class: local.class,
327
+ style: local.style,
328
+ defaultClassName: 'solidaria-Checkbox',
329
+ },
330
+ renderValues
331
+ );
332
+
333
+ // Filter DOM props
334
+ const domProps = createMemo(() => {
335
+ const filtered = filterDOMProps(ariaProps, { global: true });
336
+ delete (filtered as Record<string, unknown>).id;
337
+ delete (filtered as Record<string, unknown>).onClick;
338
+ return filtered;
339
+ });
340
+
341
+ // Remove ref from spread props to avoid type conflicts
342
+ const cleanLabelProps = () => {
343
+ const { ref: _ref1, ...rest } = labelProps as Record<string, unknown>;
344
+ return rest;
345
+ };
346
+ const cleanHoverProps = () => {
347
+ const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
348
+ return rest;
349
+ };
350
+ const cleanInputProps = () => {
351
+ const { ref: _ref3, ...rest } = inputProps as Record<string, unknown>;
352
+ return rest;
353
+ };
354
+ const cleanFocusProps = () => {
355
+ const { ref: _ref4, ...rest } = focusProps as Record<string, unknown>;
356
+ return rest;
357
+ };
358
+
359
+ return (
360
+ <label
361
+ {...domProps()}
362
+ {...cleanLabelProps()}
363
+ {...cleanHoverProps()}
364
+ class={renderProps.class()}
365
+ style={renderProps.style()}
366
+ data-selected={isSelected() || undefined}
367
+ data-indeterminate={local.isIndeterminate || undefined}
368
+ data-pressed={isPressed() || undefined}
369
+ data-hovered={isHovered() || undefined}
370
+ data-focused={isFocused() || undefined}
371
+ data-focus-visible={isFocusVisible() || undefined}
372
+ data-disabled={isDisabled || undefined}
373
+ data-readonly={isReadOnly || undefined}
374
+ data-invalid={isInvalid || undefined}
375
+ data-required={ariaProps.isRequired || undefined}
376
+ >
377
+ <VisuallyHidden>
378
+ <input
379
+ ref={(el) => (inputRef = el)}
380
+ {...cleanInputProps()}
381
+ {...cleanFocusProps()}
382
+ />
383
+ </VisuallyHidden>
384
+ {renderProps.renderChildren()}
385
+ </label>
386
+ );
387
+ }