@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.
- package/dist/Color.d.ts +2 -6
- package/dist/Color.d.ts.map +1 -1
- package/dist/ComboBox.d.ts +3 -3
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/GridList.d.ts +2 -2
- package/dist/GridList.d.ts.map +1 -1
- package/dist/ListBox.d.ts +5 -5
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/Menu.d.ts +3 -3
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Select.d.ts +3 -3
- package/dist/Select.d.ts.map +1 -1
- package/dist/Table.d.ts +2 -2
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/index.js +56 -56
- package/dist/index.js.map +2 -2
- package/dist/index.ssr.js +56 -56
- package/dist/index.ssr.js.map +2 -2
- package/package.json +10 -8
- package/src/Autocomplete.tsx +174 -0
- package/src/Breadcrumbs.tsx +264 -0
- package/src/Button.tsx +238 -0
- package/src/Calendar.tsx +471 -0
- package/src/Checkbox.tsx +387 -0
- package/src/Color.tsx +1370 -0
- package/src/ComboBox.tsx +824 -0
- package/src/DateField.tsx +337 -0
- package/src/DatePicker.tsx +367 -0
- package/src/Dialog.tsx +262 -0
- package/src/Disclosure.tsx +439 -0
- package/src/GridList.tsx +511 -0
- package/src/Landmark.tsx +203 -0
- package/src/Link.tsx +201 -0
- package/src/ListBox.tsx +346 -0
- package/src/Menu.tsx +544 -0
- package/src/Meter.tsx +157 -0
- package/src/Modal.tsx +433 -0
- package/src/NumberField.tsx +542 -0
- package/src/Popover.tsx +540 -0
- package/src/ProgressBar.tsx +162 -0
- package/src/RadioGroup.tsx +356 -0
- package/src/RangeCalendar.tsx +462 -0
- package/src/SearchField.tsx +479 -0
- package/src/Select.tsx +734 -0
- package/src/Separator.tsx +130 -0
- package/src/Slider.tsx +500 -0
- package/src/Switch.tsx +213 -0
- package/src/Table.tsx +857 -0
- package/src/Tabs.tsx +552 -0
- package/src/TagGroup.tsx +421 -0
- package/src/TextField.tsx +271 -0
- package/src/TimeField.tsx +455 -0
- package/src/Toast.tsx +503 -0
- package/src/Toolbar.tsx +160 -0
- package/src/Tooltip.tsx +423 -0
- package/src/Tree.tsx +551 -0
- package/src/VisuallyHidden.tsx +60 -0
- package/src/contexts.ts +74 -0
- package/src/index.ts +620 -0
- package/src/utils.tsx +329 -0
package/src/Checkbox.tsx
ADDED
|
@@ -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
|
+
}
|