@proyecto-viviana/solidaria 0.2.2 → 0.2.4
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/autocomplete/createAutocomplete.d.ts +2 -2
- package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
- package/dist/index.js +233 -234
- package/dist/index.js.map +2 -2
- package/dist/index.ssr.js +233 -234
- package/dist/index.ssr.js.map +2 -2
- package/dist/interactions/PressEvent.d.ts +13 -10
- package/dist/interactions/PressEvent.d.ts.map +1 -1
- package/dist/interactions/createPress.d.ts.map +1 -1
- package/dist/interactions/index.d.ts +1 -1
- package/dist/interactions/index.d.ts.map +1 -1
- package/dist/select/createHiddenSelect.d.ts.map +1 -1
- package/dist/toolbar/createToolbar.d.ts.map +1 -1
- package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
- package/package.json +9 -7
- package/src/autocomplete/createAutocomplete.ts +341 -0
- package/src/autocomplete/index.ts +9 -0
- package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
- package/src/breadcrumbs/index.ts +8 -0
- package/src/button/createButton.ts +142 -0
- package/src/button/createToggleButton.ts +101 -0
- package/src/button/index.ts +4 -0
- package/src/button/types.ts +78 -0
- package/src/calendar/createCalendar.ts +138 -0
- package/src/calendar/createCalendarCell.ts +187 -0
- package/src/calendar/createCalendarGrid.ts +140 -0
- package/src/calendar/createRangeCalendar.ts +136 -0
- package/src/calendar/createRangeCalendarCell.ts +186 -0
- package/src/calendar/index.ts +34 -0
- package/src/checkbox/createCheckbox.ts +135 -0
- package/src/checkbox/createCheckboxGroup.ts +137 -0
- package/src/checkbox/createCheckboxGroupItem.ts +117 -0
- package/src/checkbox/createCheckboxGroupState.ts +193 -0
- package/src/checkbox/index.ts +13 -0
- package/src/color/createColorArea.ts +314 -0
- package/src/color/createColorField.ts +137 -0
- package/src/color/createColorSlider.ts +197 -0
- package/src/color/createColorSwatch.ts +40 -0
- package/src/color/createColorWheel.ts +208 -0
- package/src/color/index.ts +24 -0
- package/src/color/types.ts +116 -0
- package/src/combobox/createComboBox.ts +647 -0
- package/src/combobox/index.ts +6 -0
- package/src/combobox/intl/en-US.json +7 -0
- package/src/combobox/intl/es-ES.json +7 -0
- package/src/combobox/intl/index.ts +23 -0
- package/src/datepicker/createDateField.ts +154 -0
- package/src/datepicker/createDatePicker.ts +206 -0
- package/src/datepicker/createDateSegment.ts +229 -0
- package/src/datepicker/createTimeField.ts +154 -0
- package/src/datepicker/index.ts +28 -0
- package/src/dialog/createDialog.ts +120 -0
- package/src/dialog/index.ts +2 -0
- package/src/dialog/types.ts +19 -0
- package/src/disclosure/createDisclosure.ts +131 -0
- package/src/disclosure/createDisclosureGroup.ts +62 -0
- package/src/disclosure/index.ts +11 -0
- package/src/dnd/createDrag.ts +209 -0
- package/src/dnd/createDraggableCollection.ts +63 -0
- package/src/dnd/createDraggableItem.ts +243 -0
- package/src/dnd/createDrop.ts +321 -0
- package/src/dnd/createDroppableCollection.ts +293 -0
- package/src/dnd/createDroppableItem.ts +213 -0
- package/src/dnd/index.ts +47 -0
- package/src/dnd/types.ts +89 -0
- package/src/dnd/utils.ts +294 -0
- package/src/focus/FocusScope.tsx +408 -0
- package/src/focus/createAutoFocus.ts +321 -0
- package/src/focus/createFocusRestore.ts +313 -0
- package/src/focus/createVirtualFocus.ts +396 -0
- package/src/focus/index.ts +35 -0
- package/src/form/createFormReset.ts +51 -0
- package/src/form/createFormValidation.ts +224 -0
- package/src/form/index.ts +11 -0
- package/src/grid/GridKeyboardDelegate.ts +429 -0
- package/src/grid/createGrid.ts +261 -0
- package/src/grid/createGridCell.ts +182 -0
- package/src/grid/createGridRow.ts +153 -0
- package/src/grid/index.ts +18 -0
- package/src/grid/types.ts +133 -0
- package/src/gridlist/createGridList.ts +185 -0
- package/src/gridlist/createGridListItem.ts +180 -0
- package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
- package/src/gridlist/index.ts +16 -0
- package/src/gridlist/types.ts +81 -0
- package/src/i18n/NumberFormatter.ts +266 -0
- package/src/i18n/createCollator.ts +79 -0
- package/src/i18n/createDateFormatter.ts +83 -0
- package/src/i18n/createFilter.ts +131 -0
- package/src/i18n/createNumberFormatter.ts +52 -0
- package/src/i18n/createStringFormatter.ts +87 -0
- package/src/i18n/index.ts +40 -0
- package/src/i18n/locale.tsx +188 -0
- package/src/i18n/utils.ts +99 -0
- package/src/index.ts +670 -0
- package/src/interactions/FocusableProvider.tsx +44 -0
- package/src/interactions/PressEvent.ts +126 -0
- package/src/interactions/createFocus.ts +163 -0
- package/src/interactions/createFocusRing.ts +89 -0
- package/src/interactions/createFocusWithin.ts +206 -0
- package/src/interactions/createFocusable.ts +168 -0
- package/src/interactions/createHover.ts +254 -0
- package/src/interactions/createInteractionModality.ts +424 -0
- package/src/interactions/createKeyboard.ts +82 -0
- package/src/interactions/createLongPress.ts +174 -0
- package/src/interactions/createMove.ts +289 -0
- package/src/interactions/createPress.ts +834 -0
- package/src/interactions/index.ts +78 -0
- package/src/label/createField.ts +145 -0
- package/src/label/createLabel.ts +117 -0
- package/src/label/createLabels.ts +50 -0
- package/src/label/index.ts +19 -0
- package/src/landmark/createLandmark.ts +377 -0
- package/src/landmark/index.ts +8 -0
- package/src/link/createLink.ts +182 -0
- package/src/link/index.ts +1 -0
- package/src/listbox/createListBox.ts +269 -0
- package/src/listbox/createOption.ts +151 -0
- package/src/listbox/index.ts +12 -0
- package/src/live-announcer/announce.ts +322 -0
- package/src/live-announcer/index.ts +9 -0
- package/src/menu/createMenu.ts +396 -0
- package/src/menu/createMenuItem.ts +149 -0
- package/src/menu/createMenuTrigger.ts +88 -0
- package/src/menu/index.ts +18 -0
- package/src/meter/createMeter.ts +75 -0
- package/src/meter/index.ts +1 -0
- package/src/numberfield/createNumberField.ts +268 -0
- package/src/numberfield/index.ts +5 -0
- package/src/overlays/ariaHideOutside.ts +219 -0
- package/src/overlays/createInteractOutside.ts +149 -0
- package/src/overlays/createModal.tsx +202 -0
- package/src/overlays/createOverlay.ts +155 -0
- package/src/overlays/createOverlayTrigger.ts +85 -0
- package/src/overlays/createPreventScroll.ts +266 -0
- package/src/overlays/index.ts +44 -0
- package/src/popover/calculatePosition.ts +766 -0
- package/src/popover/createOverlayPosition.ts +356 -0
- package/src/popover/createPopover.ts +170 -0
- package/src/popover/index.ts +24 -0
- package/src/progress/createProgressBar.ts +128 -0
- package/src/progress/index.ts +5 -0
- package/src/radio/createRadio.ts +287 -0
- package/src/radio/createRadioGroup.ts +189 -0
- package/src/radio/createRadioGroupState.ts +201 -0
- package/src/radio/index.ts +23 -0
- package/src/searchfield/createSearchField.ts +186 -0
- package/src/searchfield/index.ts +2 -0
- package/src/select/createHiddenSelect.tsx +236 -0
- package/src/select/createSelect.ts +395 -0
- package/src/select/index.ts +14 -0
- package/src/selection/createTypeSelect.ts +201 -0
- package/src/selection/index.ts +6 -0
- package/src/separator/createSeparator.ts +82 -0
- package/src/separator/index.ts +6 -0
- package/src/slider/createSlider.ts +349 -0
- package/src/slider/index.ts +2 -0
- package/src/ssr/index.tsx +370 -0
- package/src/switch/createSwitch.ts +70 -0
- package/src/switch/index.ts +1 -0
- package/src/table/createTable.ts +526 -0
- package/src/table/createTableCell.ts +147 -0
- package/src/table/createTableColumnHeader.ts +115 -0
- package/src/table/createTableHeaderRow.ts +40 -0
- package/src/table/createTableRow.ts +155 -0
- package/src/table/createTableRowGroup.ts +32 -0
- package/src/table/createTableSelectAllCheckbox.ts +73 -0
- package/src/table/createTableSelectionCheckbox.ts +59 -0
- package/src/table/index.ts +30 -0
- package/src/table/types.ts +165 -0
- package/src/tabs/createTabs.ts +472 -0
- package/src/tabs/index.ts +14 -0
- package/src/tag/createTag.ts +194 -0
- package/src/tag/createTagGroup.ts +154 -0
- package/src/tag/index.ts +12 -0
- package/src/textfield/createTextField.ts +198 -0
- package/src/textfield/index.ts +5 -0
- package/src/toast/createToast.ts +118 -0
- package/src/toast/createToastRegion.ts +100 -0
- package/src/toast/index.ts +11 -0
- package/src/toggle/createToggle.ts +223 -0
- package/src/toggle/createToggleState.ts +94 -0
- package/src/toggle/index.ts +7 -0
- package/src/toolbar/createToolbar.ts +369 -0
- package/src/toolbar/index.ts +6 -0
- package/src/tooltip/createTooltip.ts +79 -0
- package/src/tooltip/createTooltipTrigger.ts +222 -0
- package/src/tooltip/index.ts +6 -0
- package/src/tree/createTree.ts +246 -0
- package/src/tree/createTreeItem.ts +233 -0
- package/src/tree/createTreeSelectionCheckbox.ts +68 -0
- package/src/tree/index.ts +16 -0
- package/src/tree/types.ts +87 -0
- package/src/utils/createDescription.ts +137 -0
- package/src/utils/dom.ts +327 -0
- package/src/utils/env.ts +54 -0
- package/src/utils/events.ts +106 -0
- package/src/utils/filterDOMProps.ts +116 -0
- package/src/utils/focus.ts +151 -0
- package/src/utils/geometry.ts +115 -0
- package/src/utils/globalListeners.ts +142 -0
- package/src/utils/index.ts +80 -0
- package/src/utils/mergeProps.ts +52 -0
- package/src/utils/platform.ts +52 -0
- package/src/utils/reactivity.ts +36 -0
- package/src/utils/textSelection.ts +114 -0
- package/src/visually-hidden/createVisuallyHidden.ts +124 -0
- package/src/visually-hidden/index.ts +6 -0
- package/dist/index.jsx +0 -15845
- package/dist/index.jsx.map +0 -7
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox hook for Solidaria
|
|
3
|
+
*
|
|
4
|
+
* Provides the behavior and accessibility implementation for a checkbox component.
|
|
5
|
+
* Checkboxes allow users to select multiple items from a list of individual items,
|
|
6
|
+
* or to mark one individual item as selected.
|
|
7
|
+
*
|
|
8
|
+
* This is a 1:1 port of @react-aria/checkbox's useCheckbox hook.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { JSX, Accessor, createEffect } from 'solid-js';
|
|
12
|
+
import { createToggle, type AriaToggleProps } from '../toggle';
|
|
13
|
+
import { type ToggleState } from '@proyecto-viviana/solid-stately';
|
|
14
|
+
import { createPress } from '../interactions/createPress';
|
|
15
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
16
|
+
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
17
|
+
|
|
18
|
+
// ============================================
|
|
19
|
+
// TYPES
|
|
20
|
+
// ============================================
|
|
21
|
+
|
|
22
|
+
export interface AriaCheckboxProps extends AriaToggleProps {
|
|
23
|
+
/**
|
|
24
|
+
* Indeterminism is presentational only.
|
|
25
|
+
* The indeterminate visual representation remains regardless of user interaction.
|
|
26
|
+
*/
|
|
27
|
+
isIndeterminate?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Whether the checkbox is required.
|
|
30
|
+
*/
|
|
31
|
+
isRequired?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The validation behavior for the checkbox.
|
|
34
|
+
* @default 'aria'
|
|
35
|
+
*/
|
|
36
|
+
validationBehavior?: 'aria' | 'native';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CheckboxAria {
|
|
40
|
+
/** Props for the label wrapper element. */
|
|
41
|
+
labelProps: JSX.LabelHTMLAttributes<HTMLLabelElement>;
|
|
42
|
+
/** Props for the input element. */
|
|
43
|
+
inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
44
|
+
/** Whether the checkbox is selected. */
|
|
45
|
+
isSelected: Accessor<boolean>;
|
|
46
|
+
/** Whether the checkbox is in a pressed state. */
|
|
47
|
+
isPressed: Accessor<boolean>;
|
|
48
|
+
/** Whether the checkbox is disabled. */
|
|
49
|
+
isDisabled: boolean;
|
|
50
|
+
/** Whether the checkbox is read only. */
|
|
51
|
+
isReadOnly: boolean;
|
|
52
|
+
/** Whether the checkbox is invalid. */
|
|
53
|
+
isInvalid: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================
|
|
57
|
+
// IMPLEMENTATION
|
|
58
|
+
// ============================================
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Provides the behavior and accessibility implementation for a checkbox component.
|
|
62
|
+
* Checkboxes allow users to select multiple items from a list of individual items,
|
|
63
|
+
* or to mark one individual item as selected.
|
|
64
|
+
*
|
|
65
|
+
* @param props - Props for the checkbox.
|
|
66
|
+
* @param state - State for the checkbox, as returned by `createToggleState`.
|
|
67
|
+
* @param inputRef - A ref accessor for the HTML input element.
|
|
68
|
+
*/
|
|
69
|
+
export function createCheckbox(
|
|
70
|
+
props: MaybeAccessor<AriaCheckboxProps>,
|
|
71
|
+
state: ToggleState,
|
|
72
|
+
inputRef: () => HTMLInputElement | null
|
|
73
|
+
): CheckboxAria {
|
|
74
|
+
const getProps = () => access(props);
|
|
75
|
+
|
|
76
|
+
// Get toggle aria props
|
|
77
|
+
const toggleResult = createToggle(props, state, inputRef);
|
|
78
|
+
const {
|
|
79
|
+
labelProps: baseLabelProps,
|
|
80
|
+
isSelected,
|
|
81
|
+
isPressed,
|
|
82
|
+
isDisabled,
|
|
83
|
+
isReadOnly,
|
|
84
|
+
isInvalid,
|
|
85
|
+
} = toggleResult;
|
|
86
|
+
|
|
87
|
+
// Handle indeterminate state
|
|
88
|
+
createEffect(() => {
|
|
89
|
+
const input = inputRef();
|
|
90
|
+
const isIndeterminate = getProps().isIndeterminate;
|
|
91
|
+
if (input) {
|
|
92
|
+
// indeterminate is a property, but it can only be set via javascript
|
|
93
|
+
// https://css-tricks.com/indeterminate-checkboxes/
|
|
94
|
+
input.indeterminate = !!isIndeterminate;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Reset validation state on label press for checkbox with a hidden input.
|
|
99
|
+
const { pressProps } = createPress({
|
|
100
|
+
get isDisabled() {
|
|
101
|
+
return isDisabled || isReadOnly;
|
|
102
|
+
},
|
|
103
|
+
onPress() {
|
|
104
|
+
// Validation state reset would be handled here if we had form validation
|
|
105
|
+
// For now, this is a no-op placeholder matching React-Aria's pattern
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
labelProps: mergeProps(
|
|
111
|
+
baseLabelProps as unknown as Record<string, unknown>,
|
|
112
|
+
pressProps as unknown as Record<string, unknown>,
|
|
113
|
+
{
|
|
114
|
+
// Prevent label from being focused when mouse down on it.
|
|
115
|
+
// Note, this does not prevent the input from being focused in the `click` event.
|
|
116
|
+
onMouseDown: (e: MouseEvent) => e.preventDefault(),
|
|
117
|
+
} as Record<string, unknown>
|
|
118
|
+
) as JSX.LabelHTMLAttributes<HTMLLabelElement>,
|
|
119
|
+
get inputProps() {
|
|
120
|
+
const p = getProps();
|
|
121
|
+
const { isRequired, validationBehavior = 'aria' } = p;
|
|
122
|
+
|
|
123
|
+
return mergeProps(toggleResult.inputProps, {
|
|
124
|
+
checked: isSelected(),
|
|
125
|
+
'aria-required': (isRequired && validationBehavior === 'aria') || undefined,
|
|
126
|
+
required: isRequired && validationBehavior === 'native',
|
|
127
|
+
}) as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
128
|
+
},
|
|
129
|
+
isSelected,
|
|
130
|
+
isPressed,
|
|
131
|
+
isDisabled,
|
|
132
|
+
isReadOnly,
|
|
133
|
+
isInvalid,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox group hook for Solidaria
|
|
3
|
+
*
|
|
4
|
+
* Provides the behavior and accessibility implementation for a checkbox group component.
|
|
5
|
+
* Checkbox groups allow users to select multiple items from a list of options.
|
|
6
|
+
*
|
|
7
|
+
* This is a 1:1 port of @react-aria/checkbox's useCheckboxGroup hook.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { JSX } from 'solid-js';
|
|
11
|
+
import { createField } from '../label';
|
|
12
|
+
import { createFocusWithin } from '../interactions/createFocusWithin';
|
|
13
|
+
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
14
|
+
import { mergeProps } from '../utils/mergeProps';
|
|
15
|
+
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
16
|
+
import { type CheckboxGroupState, type CheckboxGroupProps } from '@proyecto-viviana/solid-stately';
|
|
17
|
+
|
|
18
|
+
// ============================================
|
|
19
|
+
// TYPES
|
|
20
|
+
// ============================================
|
|
21
|
+
|
|
22
|
+
export interface AriaCheckboxGroupProps extends CheckboxGroupProps {
|
|
23
|
+
/** Defines a string value that labels the current element. */
|
|
24
|
+
'aria-label'?: string;
|
|
25
|
+
/** Identifies the element (or elements) that labels the current element. */
|
|
26
|
+
'aria-labelledby'?: string;
|
|
27
|
+
/** Identifies the element (or elements) that describes the object. */
|
|
28
|
+
'aria-describedby'?: string;
|
|
29
|
+
/** Identifies the element (or elements) that provide a detailed, extended description for the object. */
|
|
30
|
+
'aria-details'?: string;
|
|
31
|
+
/** A description for the field. Provides a hint such as specific requirements for what to choose. */
|
|
32
|
+
description?: JSX.Element;
|
|
33
|
+
/** An error message for the field. */
|
|
34
|
+
errorMessage?: JSX.Element;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CheckboxGroupAria {
|
|
38
|
+
/** Props for the checkbox group wrapper element. */
|
|
39
|
+
groupProps: JSX.HTMLAttributes<HTMLElement>;
|
|
40
|
+
/** Props for the checkbox group's visible label (if any). */
|
|
41
|
+
labelProps: JSX.HTMLAttributes<HTMLElement>;
|
|
42
|
+
/** Props for the checkbox group description element, if any. */
|
|
43
|
+
descriptionProps: JSX.HTMLAttributes<HTMLElement>;
|
|
44
|
+
/** Props for the checkbox group error message element, if any. */
|
|
45
|
+
errorMessageProps: JSX.HTMLAttributes<HTMLElement>;
|
|
46
|
+
/** Whether the checkbox group is invalid. */
|
|
47
|
+
isInvalid: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// WeakMap to share data between checkbox group and checkbox group items
|
|
51
|
+
export const checkboxGroupData = new WeakMap<
|
|
52
|
+
CheckboxGroupState,
|
|
53
|
+
{
|
|
54
|
+
name?: string;
|
|
55
|
+
form?: string;
|
|
56
|
+
descriptionId?: string;
|
|
57
|
+
errorMessageId?: string;
|
|
58
|
+
validationBehavior: 'aria' | 'native';
|
|
59
|
+
}
|
|
60
|
+
>();
|
|
61
|
+
|
|
62
|
+
// ============================================
|
|
63
|
+
// IMPLEMENTATION
|
|
64
|
+
// ============================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Provides the behavior and accessibility implementation for a checkbox group component.
|
|
68
|
+
* Checkbox groups allow users to select multiple items from a list of options.
|
|
69
|
+
*
|
|
70
|
+
* @param props - Props for the checkbox group.
|
|
71
|
+
* @param state - State for the checkbox group, as returned by `createCheckboxGroupState`.
|
|
72
|
+
*/
|
|
73
|
+
export function createCheckboxGroup(
|
|
74
|
+
props: MaybeAccessor<AriaCheckboxGroupProps>,
|
|
75
|
+
state: CheckboxGroupState
|
|
76
|
+
): CheckboxGroupAria {
|
|
77
|
+
const getProps = () => access(props);
|
|
78
|
+
|
|
79
|
+
const isInvalid = () => state.isInvalid;
|
|
80
|
+
|
|
81
|
+
// Use field for label association
|
|
82
|
+
const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField({
|
|
83
|
+
get label() { return getProps().label; },
|
|
84
|
+
get 'aria-label'() { return getProps()['aria-label']; },
|
|
85
|
+
get 'aria-labelledby'() { return getProps()['aria-labelledby']; },
|
|
86
|
+
get 'aria-describedby'() { return getProps()['aria-describedby']; },
|
|
87
|
+
get 'aria-details'() { return getProps()['aria-details']; },
|
|
88
|
+
get description() { return getProps().description; },
|
|
89
|
+
get errorMessage() { return getProps().errorMessage ?? (isInvalid() ? 'Invalid selection' : undefined); },
|
|
90
|
+
get isInvalid() { return isInvalid(); },
|
|
91
|
+
// Checkbox group is not an HTML input element so it
|
|
92
|
+
// shouldn't be labeled by a <label> element.
|
|
93
|
+
labelElementType: 'span',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Store data for checkbox group items
|
|
97
|
+
checkboxGroupData.set(state, {
|
|
98
|
+
name: getProps().name,
|
|
99
|
+
form: getProps().form,
|
|
100
|
+
descriptionId: descriptionProps.id,
|
|
101
|
+
errorMessageId: errorMessageProps.id,
|
|
102
|
+
validationBehavior: 'aria',
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Filter DOM props
|
|
106
|
+
const domProps = () => filterDOMProps(getProps() as unknown as Record<string, unknown>, { labelable: true });
|
|
107
|
+
|
|
108
|
+
// Handle focus within
|
|
109
|
+
const { focusWithinProps } = createFocusWithin({
|
|
110
|
+
get onBlurWithin() { return getProps().onBlur; },
|
|
111
|
+
get onFocusWithin() { return getProps().onFocus; },
|
|
112
|
+
get onFocusWithinChange() { return getProps().onFocusChange; },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
get groupProps() {
|
|
117
|
+
return mergeProps(domProps(), {
|
|
118
|
+
role: 'group',
|
|
119
|
+
'aria-disabled': state.isDisabled || undefined,
|
|
120
|
+
...fieldProps,
|
|
121
|
+
...focusWithinProps,
|
|
122
|
+
}) as JSX.HTMLAttributes<HTMLElement>;
|
|
123
|
+
},
|
|
124
|
+
get labelProps() {
|
|
125
|
+
return labelProps as JSX.HTMLAttributes<HTMLElement>;
|
|
126
|
+
},
|
|
127
|
+
get descriptionProps() {
|
|
128
|
+
return descriptionProps;
|
|
129
|
+
},
|
|
130
|
+
get errorMessageProps() {
|
|
131
|
+
return errorMessageProps;
|
|
132
|
+
},
|
|
133
|
+
get isInvalid() {
|
|
134
|
+
return isInvalid();
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox group item hook for Solidaria
|
|
3
|
+
*
|
|
4
|
+
* Provides the behavior and accessibility implementation for a checkbox component
|
|
5
|
+
* contained within a checkbox group.
|
|
6
|
+
*
|
|
7
|
+
* This is a 1:1 port of @react-aria/checkbox's useCheckboxGroupItem hook.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { JSX } from 'solid-js';
|
|
11
|
+
import { createCheckbox, type AriaCheckboxProps, type CheckboxAria } from './createCheckbox';
|
|
12
|
+
import { type ToggleState, type CheckboxGroupState } from '@proyecto-viviana/solid-stately';
|
|
13
|
+
import { checkboxGroupData } from './createCheckboxGroup';
|
|
14
|
+
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
15
|
+
|
|
16
|
+
// ============================================
|
|
17
|
+
// TYPES
|
|
18
|
+
// ============================================
|
|
19
|
+
|
|
20
|
+
export interface AriaCheckboxGroupItemProps extends Omit<AriaCheckboxProps, 'isSelected' | 'defaultSelected'> {
|
|
21
|
+
/** The value of the checkbox. */
|
|
22
|
+
value: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// IMPLEMENTATION
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Provides the behavior and accessibility implementation for a checkbox component
|
|
31
|
+
* contained within a checkbox group.
|
|
32
|
+
*
|
|
33
|
+
* @param props - Props for the checkbox.
|
|
34
|
+
* @param state - State for the checkbox group, as returned by `createCheckboxGroupState`.
|
|
35
|
+
* @param inputRef - A ref accessor for the HTML input element.
|
|
36
|
+
*/
|
|
37
|
+
export function createCheckboxGroupItem(
|
|
38
|
+
props: MaybeAccessor<AriaCheckboxGroupItemProps>,
|
|
39
|
+
state: CheckboxGroupState,
|
|
40
|
+
inputRef: () => HTMLInputElement | null
|
|
41
|
+
): CheckboxAria {
|
|
42
|
+
const getProps = () => access(props);
|
|
43
|
+
|
|
44
|
+
// Create toggle state that syncs with the group state
|
|
45
|
+
const toggleState: ToggleState = {
|
|
46
|
+
isSelected: () => state.isSelected(getProps().value),
|
|
47
|
+
defaultSelected: state.defaultValue.includes(getProps().value),
|
|
48
|
+
setSelected(isSelected: boolean) {
|
|
49
|
+
const value = getProps().value;
|
|
50
|
+
if (isSelected) {
|
|
51
|
+
state.addValue(value);
|
|
52
|
+
} else {
|
|
53
|
+
state.removeValue(value);
|
|
54
|
+
}
|
|
55
|
+
getProps().onChange?.(isSelected);
|
|
56
|
+
},
|
|
57
|
+
toggle() {
|
|
58
|
+
state.toggleValue(getProps().value);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Get group data
|
|
63
|
+
const getGroupData = () => checkboxGroupData.get(state);
|
|
64
|
+
|
|
65
|
+
// Build checkbox props
|
|
66
|
+
const checkboxProps = (): AriaCheckboxProps => {
|
|
67
|
+
const p = getProps();
|
|
68
|
+
const groupData = getGroupData();
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...p,
|
|
72
|
+
isReadOnly: p.isReadOnly ?? state.isReadOnly,
|
|
73
|
+
isDisabled: p.isDisabled ?? state.isDisabled,
|
|
74
|
+
name: p.name ?? groupData?.name,
|
|
75
|
+
form: p.form ?? groupData?.form,
|
|
76
|
+
isRequired: p.isRequired ?? state.isRequired(),
|
|
77
|
+
validationBehavior: p.validationBehavior ?? groupData?.validationBehavior ?? 'aria',
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Use the checkbox hook
|
|
82
|
+
const result = createCheckbox(checkboxProps, toggleState, inputRef);
|
|
83
|
+
|
|
84
|
+
// Add group-level aria-describedby
|
|
85
|
+
return {
|
|
86
|
+
...result,
|
|
87
|
+
get inputProps() {
|
|
88
|
+
const baseInputProps = result.inputProps;
|
|
89
|
+
const groupData = getGroupData();
|
|
90
|
+
|
|
91
|
+
const describedByIds: string[] = [];
|
|
92
|
+
|
|
93
|
+
// Add props aria-describedby
|
|
94
|
+
const propsDescribedBy = getProps()['aria-describedby'];
|
|
95
|
+
if (propsDescribedBy) {
|
|
96
|
+
describedByIds.push(propsDescribedBy);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add error message ID if group is invalid
|
|
100
|
+
if (state.isInvalid && groupData?.errorMessageId) {
|
|
101
|
+
describedByIds.push(groupData.errorMessageId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Add description ID
|
|
105
|
+
if (groupData?.descriptionId) {
|
|
106
|
+
describedByIds.push(groupData.descriptionId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const ariaDescribedBy = describedByIds.length > 0 ? describedByIds.join(' ') : undefined;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
...baseInputProps,
|
|
113
|
+
'aria-describedby': ariaDescribedBy,
|
|
114
|
+
} as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox group state for Solidaria
|
|
3
|
+
*
|
|
4
|
+
* Provides state management for a checkbox group component.
|
|
5
|
+
* Provides a name for the group, and manages selection and focus state.
|
|
6
|
+
*
|
|
7
|
+
* This is a 1:1 port of @react-stately/checkbox's useCheckboxGroupState.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createSignal, Accessor } from 'solid-js';
|
|
11
|
+
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
12
|
+
|
|
13
|
+
// ============================================
|
|
14
|
+
// TYPES
|
|
15
|
+
// ============================================
|
|
16
|
+
|
|
17
|
+
export interface CheckboxGroupProps {
|
|
18
|
+
/** The current selected values (controlled). */
|
|
19
|
+
value?: string[];
|
|
20
|
+
/** The default selected values (uncontrolled). */
|
|
21
|
+
defaultValue?: string[];
|
|
22
|
+
/** Handler that is called when the value changes. */
|
|
23
|
+
onChange?: (value: string[]) => void;
|
|
24
|
+
/** Whether the checkbox group is disabled. */
|
|
25
|
+
isDisabled?: boolean;
|
|
26
|
+
/** Whether the checkbox group is read only. */
|
|
27
|
+
isReadOnly?: boolean;
|
|
28
|
+
/** Whether the checkbox group is required. */
|
|
29
|
+
isRequired?: boolean;
|
|
30
|
+
/** Whether the checkbox group is invalid. */
|
|
31
|
+
isInvalid?: boolean;
|
|
32
|
+
/** The name of the checkbox group, used when submitting an HTML form. */
|
|
33
|
+
name?: string;
|
|
34
|
+
/** The form to associate the checkbox group with. */
|
|
35
|
+
form?: string;
|
|
36
|
+
/** The label for the checkbox group. */
|
|
37
|
+
label?: string;
|
|
38
|
+
/** Handler that is called when the checkbox group receives focus. */
|
|
39
|
+
onFocus?: (e: FocusEvent) => void;
|
|
40
|
+
/** Handler that is called when the checkbox group loses focus. */
|
|
41
|
+
onBlur?: (e: FocusEvent) => void;
|
|
42
|
+
/** Handler that is called when the checkbox group's focus status changes. */
|
|
43
|
+
onFocusChange?: (isFocused: boolean) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CheckboxGroupState {
|
|
47
|
+
/** Current selected values. */
|
|
48
|
+
readonly value: Accessor<readonly string[]>;
|
|
49
|
+
/** Default selected values. */
|
|
50
|
+
readonly defaultValue: readonly string[];
|
|
51
|
+
/** Whether the checkbox group is disabled. */
|
|
52
|
+
readonly isDisabled: boolean;
|
|
53
|
+
/** Whether the checkbox group is read only. */
|
|
54
|
+
readonly isReadOnly: boolean;
|
|
55
|
+
/** Whether the checkbox group is invalid. */
|
|
56
|
+
readonly isInvalid: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the checkboxes in the group are required.
|
|
59
|
+
* This changes to false once at least one item is selected.
|
|
60
|
+
*/
|
|
61
|
+
readonly isRequired: Accessor<boolean>;
|
|
62
|
+
/** Returns whether the given value is selected. */
|
|
63
|
+
isSelected(value: string): boolean;
|
|
64
|
+
/** Sets the selected values. */
|
|
65
|
+
setValue(value: string[]): void;
|
|
66
|
+
/** Adds a value to the set of selected values. */
|
|
67
|
+
addValue(value: string): void;
|
|
68
|
+
/** Removes a value from the set of selected values. */
|
|
69
|
+
removeValue(value: string): void;
|
|
70
|
+
/** Toggles a value in the set of selected values. */
|
|
71
|
+
toggleValue(value: string): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================
|
|
75
|
+
// IMPLEMENTATION
|
|
76
|
+
// ============================================
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Provides state management for a checkbox group component.
|
|
80
|
+
* Provides a name for the group, and manages selection and focus state.
|
|
81
|
+
*/
|
|
82
|
+
export function createCheckboxGroupState(
|
|
83
|
+
props: MaybeAccessor<CheckboxGroupProps> = {}
|
|
84
|
+
): CheckboxGroupState {
|
|
85
|
+
const getProps = () => access(props);
|
|
86
|
+
|
|
87
|
+
// Get initial values
|
|
88
|
+
const initialProps = getProps();
|
|
89
|
+
const initialValue = initialProps.value ?? initialProps.defaultValue ?? [];
|
|
90
|
+
|
|
91
|
+
// Create internal signal for uncontrolled mode
|
|
92
|
+
const [internalValue, setInternalValue] = createSignal<readonly string[]>(initialValue);
|
|
93
|
+
|
|
94
|
+
// Determine if controlled
|
|
95
|
+
const isControlled = () => getProps().value !== undefined;
|
|
96
|
+
|
|
97
|
+
// Get current value
|
|
98
|
+
const value: Accessor<readonly string[]> = () => {
|
|
99
|
+
const p = getProps();
|
|
100
|
+
return isControlled() ? (p.value ?? []) : internalValue();
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Check if required (true if isRequired and no items selected)
|
|
104
|
+
const isRequired: Accessor<boolean> = () => {
|
|
105
|
+
const p = getProps();
|
|
106
|
+
return !!p.isRequired && value().length === 0;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Check if invalid
|
|
110
|
+
const isInvalid = () => {
|
|
111
|
+
return getProps().isInvalid ?? false;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Set value
|
|
115
|
+
function setValue(newValue: string[]): void {
|
|
116
|
+
const p = getProps();
|
|
117
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!isControlled()) {
|
|
122
|
+
setInternalValue(newValue);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
p.onChange?.(newValue);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check if value is selected
|
|
129
|
+
function isSelected(checkValue: string): boolean {
|
|
130
|
+
return value().includes(checkValue);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add value
|
|
134
|
+
function addValue(addVal: string): void {
|
|
135
|
+
const p = getProps();
|
|
136
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const current = value();
|
|
141
|
+
if (!current.includes(addVal)) {
|
|
142
|
+
setValue([...current, addVal]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Remove value
|
|
147
|
+
function removeValue(removeVal: string): void {
|
|
148
|
+
const p = getProps();
|
|
149
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const current = value();
|
|
154
|
+
if (current.includes(removeVal)) {
|
|
155
|
+
setValue(current.filter((v) => v !== removeVal));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Toggle value
|
|
160
|
+
function toggleValue(toggleVal: string): void {
|
|
161
|
+
const p = getProps();
|
|
162
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const current = value();
|
|
167
|
+
if (current.includes(toggleVal)) {
|
|
168
|
+
setValue(current.filter((v) => v !== toggleVal));
|
|
169
|
+
} else {
|
|
170
|
+
setValue([...current, toggleVal]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
value,
|
|
176
|
+
defaultValue: initialProps.defaultValue ?? initialValue,
|
|
177
|
+
get isDisabled() {
|
|
178
|
+
return getProps().isDisabled ?? false;
|
|
179
|
+
},
|
|
180
|
+
get isReadOnly() {
|
|
181
|
+
return getProps().isReadOnly ?? false;
|
|
182
|
+
},
|
|
183
|
+
get isInvalid() {
|
|
184
|
+
return isInvalid();
|
|
185
|
+
},
|
|
186
|
+
isRequired,
|
|
187
|
+
isSelected,
|
|
188
|
+
setValue,
|
|
189
|
+
addValue,
|
|
190
|
+
removeValue,
|
|
191
|
+
toggleValue,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { createCheckbox } from './createCheckbox';
|
|
2
|
+
export type { AriaCheckboxProps, CheckboxAria } from './createCheckbox';
|
|
3
|
+
|
|
4
|
+
// Re-export state from solid-stately
|
|
5
|
+
export { createCheckboxGroupState } from '@proyecto-viviana/solid-stately';
|
|
6
|
+
export type { CheckboxGroupProps, CheckboxGroupState } from '@proyecto-viviana/solid-stately';
|
|
7
|
+
|
|
8
|
+
// ARIA hooks (solidaria-specific)
|
|
9
|
+
export { createCheckboxGroup, checkboxGroupData } from './createCheckboxGroup';
|
|
10
|
+
export type { AriaCheckboxGroupProps, CheckboxGroupAria } from './createCheckboxGroup';
|
|
11
|
+
|
|
12
|
+
export { createCheckboxGroupItem } from './createCheckboxGroupItem';
|
|
13
|
+
export type { AriaCheckboxGroupItemProps } from './createCheckboxGroupItem';
|