@proyecto-viviana/solidaria 0.2.4 → 0.2.8
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/LICENSE +21 -0
- package/dist/actiongroup/createActionGroup.d.ts +29 -0
- package/dist/actiongroup/createActionGroup.d.ts.map +1 -0
- package/dist/actiongroup/index.d.ts +2 -0
- package/dist/actiongroup/index.d.ts.map +1 -0
- package/dist/autocomplete/createAutocomplete.d.ts +6 -2
- package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
- package/dist/breadcrumbs/createBreadcrumbs.d.ts +2 -0
- package/dist/breadcrumbs/createBreadcrumbs.d.ts.map +1 -1
- package/dist/button/createToggleButtonGroup.d.ts +32 -0
- package/dist/button/createToggleButtonGroup.d.ts.map +1 -0
- package/dist/button/index.d.ts +2 -0
- package/dist/button/index.d.ts.map +1 -1
- package/dist/calendar/createCalendarCell.d.ts +2 -0
- package/dist/calendar/createCalendarCell.d.ts.map +1 -1
- package/dist/calendar/createCalendarGrid.d.ts.map +1 -1
- package/dist/calendar/createRangeCalendarCell.d.ts +3 -1
- package/dist/calendar/createRangeCalendarCell.d.ts.map +1 -1
- package/dist/checkbox/createCheckboxGroup.d.ts +5 -1
- package/dist/checkbox/createCheckboxGroup.d.ts.map +1 -1
- package/dist/collections/index.d.ts +56 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/color/createColorArea.d.ts.map +1 -1
- package/dist/color/createColorSlider.d.ts.map +1 -1
- package/dist/color/createColorWheel.d.ts.map +1 -1
- package/dist/combobox/createComboBox.d.ts +6 -0
- package/dist/combobox/createComboBox.d.ts.map +1 -1
- package/dist/datepicker/createDatePicker.d.ts +6 -0
- package/dist/datepicker/createDatePicker.d.ts.map +1 -1
- package/dist/datepicker/createDateRangePicker.d.ts +40 -0
- package/dist/datepicker/createDateRangePicker.d.ts.map +1 -0
- package/dist/datepicker/createDateSegment.d.ts +1 -1
- package/dist/datepicker/createDateSegment.d.ts.map +1 -1
- package/dist/datepicker/createTimeSegment.d.ts +29 -0
- package/dist/datepicker/createTimeSegment.d.ts.map +1 -0
- package/dist/datepicker/index.d.ts +2 -0
- package/dist/datepicker/index.d.ts.map +1 -1
- package/dist/disclosure/createDisclosureGroup.d.ts +2 -1
- package/dist/disclosure/createDisclosureGroup.d.ts.map +1 -1
- package/dist/dnd/createDrag.d.ts.map +1 -1
- package/dist/dnd/createDraggableCollection.d.ts +4 -0
- package/dist/dnd/createDraggableCollection.d.ts.map +1 -1
- package/dist/dnd/createDraggableItem.d.ts.map +1 -1
- package/dist/dnd/createDrop.d.ts.map +1 -1
- package/dist/dnd/createDroppableCollection.d.ts +32 -1
- package/dist/dnd/createDroppableCollection.d.ts.map +1 -1
- package/dist/dnd/createDroppableItem.d.ts.map +1 -1
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/index.d.ts.map +1 -1
- package/dist/grid/createGrid.d.ts.map +1 -1
- package/dist/gridlist/createGridList.d.ts.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4659 -3452
- package/dist/index.js.map +1 -7
- package/dist/index.ssr.js +4659 -3452
- package/dist/index.ssr.js.map +1 -7
- package/dist/interactions/createFocus.d.ts.map +1 -1
- package/dist/interactions/createFocusWithin.d.ts.map +1 -1
- package/dist/link/createLink.d.ts +10 -0
- package/dist/link/createLink.d.ts.map +1 -1
- package/dist/listbox/createListBox.d.ts +1 -0
- package/dist/listbox/createListBox.d.ts.map +1 -1
- package/dist/listbox/createOption.d.ts.map +1 -1
- package/dist/menu/createMenu.d.ts +1 -0
- package/dist/menu/createMenu.d.ts.map +1 -1
- package/dist/meter/createMeter.d.ts.map +1 -1
- package/dist/numberfield/createNumberField.d.ts +18 -0
- package/dist/numberfield/createNumberField.d.ts.map +1 -1
- package/dist/overlays/createModal.d.ts +16 -0
- package/dist/overlays/createModal.d.ts.map +1 -1
- package/dist/overlays/createOverlay.d.ts.map +1 -1
- package/dist/overlays/index.d.ts +1 -1
- package/dist/overlays/index.d.ts.map +1 -1
- package/dist/popover/createOverlayPosition.d.ts.map +1 -1
- package/dist/popover/createPopover.d.ts.map +1 -1
- package/dist/progress/createProgressBar.d.ts.map +1 -1
- package/dist/radio/createRadioGroup.d.ts +2 -2
- package/dist/radio/createRadioGroup.d.ts.map +1 -1
- package/dist/searchfield/createSearchField.d.ts.map +1 -1
- package/dist/select/createHiddenSelect.d.ts.map +1 -1
- package/dist/select/createSelect.d.ts.map +1 -1
- package/dist/slider/createSlider.d.ts.map +1 -1
- package/dist/table/createTable.d.ts.map +1 -1
- package/dist/tabs/createTabs.d.ts +1 -1
- package/dist/tabs/createTabs.d.ts.map +1 -1
- package/dist/tag/createTag.d.ts.map +1 -1
- package/dist/tag/createTagGroup.d.ts.map +1 -1
- package/dist/toast/createToast.d.ts +4 -0
- package/dist/toast/createToast.d.ts.map +1 -1
- package/dist/toast/createToastRegion.d.ts.map +1 -1
- package/dist/toolbar/createToolbar.d.ts.map +1 -1
- package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
- package/dist/tree/createTree.d.ts.map +1 -1
- package/dist/tree/createTreeItem.d.ts.map +1 -1
- package/dist/tree/types.d.ts +4 -0
- package/dist/tree/types.d.ts.map +1 -1
- package/dist/utils/env.d.ts +1 -1
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/platform.d.ts.map +1 -1
- package/dist/visually-hidden/createVisuallyHidden.d.ts.map +1 -1
- package/package.json +8 -6
- package/src/actiongroup/createActionGroup.ts +324 -0
- package/src/actiongroup/index.ts +8 -0
- package/src/autocomplete/createAutocomplete.ts +32 -9
- package/src/breadcrumbs/createBreadcrumbs.ts +10 -15
- package/src/button/createButton.ts +1 -1
- package/src/button/createToggleButtonGroup.ts +128 -0
- package/src/button/index.ts +9 -0
- package/src/calendar/createCalendarCell.ts +6 -4
- package/src/calendar/createCalendarGrid.ts +27 -18
- package/src/calendar/createRangeCalendarCell.ts +26 -9
- package/src/checkbox/createCheckboxGroup.ts +21 -4
- package/src/collections/index.ts +242 -0
- package/src/color/createColorArea.ts +380 -314
- package/src/color/createColorField.ts +137 -137
- package/src/color/createColorSlider.ts +286 -197
- package/src/color/createColorSwatch.ts +40 -40
- package/src/color/createColorWheel.ts +218 -208
- package/src/color/index.ts +24 -24
- package/src/color/types.ts +116 -116
- package/src/combobox/createComboBox.ts +670 -647
- package/src/combobox/index.ts +6 -6
- package/src/datepicker/createDatePicker.ts +54 -16
- package/src/datepicker/createDateRangePicker.ts +246 -0
- package/src/datepicker/createDateSegment.ts +185 -31
- package/src/datepicker/createTimeSegment.ts +370 -0
- package/src/datepicker/index.ts +14 -0
- package/src/dialog/createDialog.ts +120 -120
- package/src/dialog/index.ts +2 -2
- package/src/dialog/types.ts +19 -19
- package/src/disclosure/createDisclosureGroup.ts +5 -2
- package/src/dnd/createDrag.ts +224 -209
- package/src/dnd/createDraggableCollection.ts +96 -63
- package/src/dnd/createDraggableItem.ts +259 -243
- package/src/dnd/createDrop.ts +322 -321
- package/src/dnd/createDroppableCollection.ts +682 -293
- package/src/dnd/createDroppableItem.ts +215 -213
- package/src/dnd/index.ts +55 -47
- package/src/dnd/types.ts +89 -89
- package/src/dnd/utils.ts +294 -294
- package/src/focus/createAutoFocus.ts +321 -321
- package/src/focus/createFocusRestore.ts +313 -313
- package/src/focus/createVirtualFocus.ts +396 -396
- package/src/form/createFormValidation.ts +224 -224
- package/src/form/index.ts +11 -11
- package/src/grid/createGrid.ts +3 -1
- package/src/gridlist/createGridList.ts +16 -0
- package/src/gridlist/createGridListItem.ts +1 -1
- package/src/i18n/NumberFormatter.ts +266 -266
- package/src/i18n/createCollator.ts +79 -79
- package/src/i18n/createDateFormatter.ts +83 -83
- package/src/i18n/createFilter.ts +131 -131
- package/src/i18n/createNumberFormatter.ts +52 -52
- package/src/i18n/index.ts +40 -40
- package/src/i18n/locale.tsx +188 -188
- package/src/i18n/utils.ts +99 -99
- package/src/index.ts +51 -0
- package/src/interactions/createFocus.ts +6 -5
- package/src/interactions/createFocusWithin.ts +6 -5
- package/src/interactions/createLongPress.ts +174 -174
- package/src/interactions/createMove.ts +289 -289
- package/src/interactions/createPress.ts +5 -5
- package/src/landmark/createLandmark.ts +377 -377
- package/src/landmark/index.ts +8 -8
- package/src/link/createLink.ts +23 -8
- package/src/listbox/createListBox.ts +308 -269
- package/src/listbox/createOption.ts +162 -151
- package/src/listbox/index.ts +12 -12
- package/src/live-announcer/announce.ts +322 -322
- package/src/live-announcer/index.ts +9 -9
- package/src/menu/createMenu.ts +405 -396
- package/src/menu/createMenuItem.ts +149 -149
- package/src/menu/createMenuTrigger.ts +88 -88
- package/src/menu/index.ts +18 -18
- package/src/meter/createMeter.ts +1 -6
- package/src/numberfield/createNumberField.ts +311 -268
- package/src/numberfield/index.ts +5 -5
- package/src/overlays/ariaHideOutside.ts +219 -219
- package/src/overlays/createInteractOutside.ts +149 -149
- package/src/overlays/createModal.tsx +238 -202
- package/src/overlays/createOverlay.ts +165 -155
- package/src/overlays/createOverlayTrigger.ts +85 -85
- package/src/overlays/createPreventScroll.ts +266 -266
- package/src/overlays/index.ts +48 -44
- package/src/popover/calculatePosition.ts +6 -6
- package/src/popover/createOverlayPosition.ts +7 -4
- package/src/popover/createPopover.ts +21 -7
- package/src/progress/createProgressBar.ts +6 -1
- package/src/radio/createRadioGroup.ts +88 -14
- package/src/searchfield/createSearchField.ts +241 -186
- package/src/searchfield/index.ts +2 -2
- package/src/select/createHiddenSelect.tsx +263 -236
- package/src/select/createSelect.ts +373 -395
- package/src/select/index.ts +14 -14
- package/src/slider/createSlider.ts +364 -349
- package/src/slider/index.ts +2 -2
- package/src/ssr/index.tsx +370 -370
- package/src/table/createTable.ts +3 -1
- package/src/table/createTableColumnHeader.ts +1 -1
- package/src/table/createTableRow.ts +1 -1
- package/src/tabs/createTabs.ts +80 -51
- package/src/tag/createTag.ts +135 -6
- package/src/tag/createTagGroup.ts +7 -2
- package/src/toast/createToast.ts +8 -2
- package/src/toast/createToastRegion.ts +0 -1
- package/src/toolbar/createToolbar.ts +75 -1
- package/src/tooltip/createTooltip.ts +79 -79
- package/src/tooltip/createTooltipTrigger.ts +226 -222
- package/src/tooltip/index.ts +6 -6
- package/src/tree/createTree.ts +261 -246
- package/src/tree/createTreeItem.ts +282 -233
- package/src/tree/createTreeSelectionCheckbox.ts +68 -68
- package/src/tree/index.ts +16 -16
- package/src/tree/types.ts +91 -87
- package/src/utils/env.ts +55 -54
- package/src/utils/platform.ts +16 -6
- package/src/visually-hidden/createVisuallyHidden.ts +139 -124
- package/src/visually-hidden/index.ts +6 -6
|
@@ -116,7 +116,7 @@ const AXIS_SIZE: Record<string, SizeAxis> = {
|
|
|
116
116
|
left: 'width',
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
-
const TOTAL_SIZE: Record<
|
|
119
|
+
const TOTAL_SIZE: Record<SizeAxis, 'totalWidth' | 'totalHeight'> = {
|
|
120
120
|
width: 'totalWidth',
|
|
121
121
|
height: 'totalHeight',
|
|
122
122
|
};
|
|
@@ -307,7 +307,7 @@ function computePosition(
|
|
|
307
307
|
if (placement === axis) {
|
|
308
308
|
const containerHeight = isContainerPositioned
|
|
309
309
|
? containerDimensions[size]
|
|
310
|
-
:
|
|
310
|
+
: containerDimensions[TOTAL_SIZE[size]];
|
|
311
311
|
position[FLIPPED_DIRECTION[axis] as keyof Position] = Math.floor(
|
|
312
312
|
containerHeight - childOffset[axis] + offset
|
|
313
313
|
);
|
|
@@ -333,7 +333,7 @@ function getMaxHeight(
|
|
|
333
333
|
const overlayTop =
|
|
334
334
|
(position.top != null
|
|
335
335
|
? position.top
|
|
336
|
-
:
|
|
336
|
+
: containerDimensions[TOTAL_SIZE.height] -
|
|
337
337
|
(position.bottom ?? 0) -
|
|
338
338
|
overlayHeight) - (containerDimensions.scroll.top ?? 0);
|
|
339
339
|
|
|
@@ -620,7 +620,7 @@ export function getRect(node: Element, ignoreScale: boolean) {
|
|
|
620
620
|
let width = bWidth;
|
|
621
621
|
let height = bHeight;
|
|
622
622
|
|
|
623
|
-
if (ignoreScale && node instanceof (node.ownerDocument.defaultView
|
|
623
|
+
if (ignoreScale && node instanceof (node.ownerDocument.defaultView?.HTMLElement ?? HTMLElement)) {
|
|
624
624
|
width = (node as HTMLElement).offsetWidth;
|
|
625
625
|
height = (node as HTMLElement).offsetHeight;
|
|
626
626
|
}
|
|
@@ -690,8 +690,8 @@ function isContainingBlock(node: Element): boolean {
|
|
|
690
690
|
/transform|perspective/.test(style.willChange) ||
|
|
691
691
|
style.filter !== 'none' ||
|
|
692
692
|
style.contain === 'paint' ||
|
|
693
|
-
('backdropFilter' in style && (
|
|
694
|
-
('WebkitBackdropFilter' in style && (
|
|
693
|
+
('backdropFilter' in style && style.getPropertyValue('backdrop-filter') !== 'none') ||
|
|
694
|
+
('WebkitBackdropFilter' in style && style.getPropertyValue('-webkit-backdrop-filter') !== 'none')
|
|
695
695
|
);
|
|
696
696
|
}
|
|
697
697
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createEffect, createSignal, onCleanup, type JSX } from 'solid-js';
|
|
9
|
+
import { useLocale } from '../i18n';
|
|
9
10
|
import {
|
|
10
11
|
calculatePosition,
|
|
11
12
|
getRect,
|
|
@@ -122,7 +123,8 @@ function translateRTL(position: string, direction: string): string {
|
|
|
122
123
|
* element, and updating the position when the window resizes.
|
|
123
124
|
*/
|
|
124
125
|
export function createOverlayPosition(props: AriaPositionProps): PositionAria {
|
|
125
|
-
const
|
|
126
|
+
const locale = useLocale();
|
|
127
|
+
const direction = () => locale().direction;
|
|
126
128
|
|
|
127
129
|
const arrowSize = () => props.arrowSize ?? 0;
|
|
128
130
|
const targetRef = () => props.targetRef();
|
|
@@ -178,7 +180,7 @@ export function createOverlayPosition(props: AriaPositionProps): PositionAria {
|
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
const result = calculatePosition({
|
|
181
|
-
placement: translateRTL(placement(), direction) as Placement,
|
|
183
|
+
placement: translateRTL(placement(), direction()) as Placement,
|
|
182
184
|
overlayNode,
|
|
183
185
|
targetNode,
|
|
184
186
|
scrollNode: scrollNode || overlayNode,
|
|
@@ -202,8 +204,9 @@ export function createOverlayPosition(props: AriaPositionProps): PositionAria {
|
|
|
202
204
|
overlay.style.left = '';
|
|
203
205
|
overlay.style.right = '';
|
|
204
206
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
const pos = result.position as Record<string, number | undefined>;
|
|
208
|
+
Object.keys(pos).forEach((key) => {
|
|
209
|
+
overlay.style.setProperty(key, pos[key] + 'px');
|
|
207
210
|
});
|
|
208
211
|
overlay.style.maxHeight = result.maxHeight != null ? result.maxHeight + 'px' : '';
|
|
209
212
|
|
|
@@ -109,12 +109,20 @@ export function createPopover(
|
|
|
109
109
|
// Overlay behavior (dismiss handling)
|
|
110
110
|
const { overlayProps, underlayProps } = createOverlay(
|
|
111
111
|
{
|
|
112
|
-
|
|
112
|
+
get isOpen() {
|
|
113
|
+
return state.isOpen();
|
|
114
|
+
},
|
|
113
115
|
onClose: state.close,
|
|
114
116
|
shouldCloseOnBlur: true,
|
|
115
|
-
isDismissable
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
get isDismissable() {
|
|
118
|
+
return !isNonModal() || isSubmenu();
|
|
119
|
+
},
|
|
120
|
+
get isKeyboardDismissDisabled() {
|
|
121
|
+
return isKeyboardDismissDisabled();
|
|
122
|
+
},
|
|
123
|
+
get shouldCloseOnInteractOutside() {
|
|
124
|
+
return shouldCloseOnInteractOutside;
|
|
125
|
+
},
|
|
118
126
|
},
|
|
119
127
|
() => groupRef() ?? popoverRef()
|
|
120
128
|
);
|
|
@@ -129,13 +137,19 @@ export function createPopover(
|
|
|
129
137
|
...props,
|
|
130
138
|
targetRef: triggerRef,
|
|
131
139
|
overlayRef: popoverRef,
|
|
132
|
-
|
|
133
|
-
|
|
140
|
+
get isOpen() {
|
|
141
|
+
return state.isOpen();
|
|
142
|
+
},
|
|
143
|
+
get onClose() {
|
|
144
|
+
return isNonModal() && !isSubmenu() ? state.close : null;
|
|
145
|
+
},
|
|
134
146
|
});
|
|
135
147
|
|
|
136
148
|
// Prevent scroll when modal popover is open
|
|
137
149
|
createPreventScroll({
|
|
138
|
-
isDisabled
|
|
150
|
+
get isDisabled() {
|
|
151
|
+
return isNonModal() || !state.isOpen();
|
|
152
|
+
},
|
|
139
153
|
});
|
|
140
154
|
|
|
141
155
|
// Aria-hide outside elements
|
|
@@ -57,6 +57,11 @@ function clamp(value: number, min: number, max: number): number {
|
|
|
57
57
|
return Math.min(Math.max(value, min), max);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function getSafeRange(min: number, max: number): number {
|
|
61
|
+
const range = max - min;
|
|
62
|
+
return Number.isFinite(range) && range > 0 ? range : 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
// ============================================
|
|
61
66
|
// IMPLEMENTATION
|
|
62
67
|
// ============================================
|
|
@@ -91,7 +96,7 @@ export function createProgressBar(
|
|
|
91
96
|
const formatOptions = p.formatOptions ?? { style: 'percent' as const };
|
|
92
97
|
|
|
93
98
|
const clampedValue = clamp(value, minValue, maxValue);
|
|
94
|
-
const percentage = (clampedValue - minValue) / (maxValue
|
|
99
|
+
const percentage = (clampedValue - minValue) / getSafeRange(minValue, maxValue);
|
|
95
100
|
|
|
96
101
|
// Format value label
|
|
97
102
|
let valueLabel = p.valueLabel;
|
|
@@ -12,9 +12,11 @@ import { createField } from '../label/createField';
|
|
|
12
12
|
import { createFocusWithin } from '../interactions/createFocusWithin';
|
|
13
13
|
import { mergeProps } from '../utils/mergeProps';
|
|
14
14
|
import { filterDOMProps } from '../utils/filterDOMProps';
|
|
15
|
+
import { focusSafely, getEventTarget } from '../utils';
|
|
16
|
+
import { useLocale } from '../i18n';
|
|
15
17
|
import { createId } from '../ssr';
|
|
16
18
|
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
17
|
-
import { type RadioGroupState } from '@proyecto-viviana/solid-stately';
|
|
19
|
+
import { type RadioGroupState, type ValidityState } from '@proyecto-viviana/solid-stately';
|
|
18
20
|
|
|
19
21
|
// ============================================
|
|
20
22
|
// TYPES
|
|
@@ -75,7 +77,7 @@ export interface RadioGroupAria {
|
|
|
75
77
|
/** Validation errors, if any. */
|
|
76
78
|
validationErrors: string[];
|
|
77
79
|
/** Validation details, if any. */
|
|
78
|
-
validationDetails:
|
|
80
|
+
validationDetails: ValidityState;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
// WeakMap to share data between radio group and radio items
|
|
@@ -102,19 +104,31 @@ export function createRadioGroup(
|
|
|
102
104
|
state: RadioGroupState
|
|
103
105
|
): RadioGroupAria {
|
|
104
106
|
const getProps = () => access(props);
|
|
107
|
+
const locale = useLocale();
|
|
105
108
|
|
|
106
109
|
const orientation = () => getProps().orientation ?? 'vertical';
|
|
107
110
|
const isReadOnly = () => getProps().isReadOnly ?? false;
|
|
108
111
|
const isRequired = () => getProps().isRequired ?? false;
|
|
109
112
|
const isDisabled = () => getProps().isDisabled ?? false;
|
|
110
113
|
const validationBehavior = () => getProps().validationBehavior ?? 'aria';
|
|
114
|
+
const displayValidation = () => state.displayValidation();
|
|
115
|
+
const validationErrors = () => displayValidation().validationErrors;
|
|
116
|
+
const validationDetails = () => displayValidation().validationDetails;
|
|
117
|
+
const isInvalid = () => displayValidation().isInvalid;
|
|
118
|
+
const fallbackErrorMessage = () => {
|
|
119
|
+
const errors = validationErrors();
|
|
120
|
+
return errors.length > 0 ? errors : undefined;
|
|
121
|
+
};
|
|
111
122
|
|
|
112
123
|
// Use field for label, description, error message
|
|
113
124
|
const { labelProps, fieldProps, descriptionProps, errorMessageProps } = createField({
|
|
125
|
+
get id() { return getProps().id; },
|
|
114
126
|
get label() { return getProps().label; },
|
|
127
|
+
get 'aria-label'() { return getProps()['aria-label']; },
|
|
128
|
+
get 'aria-labelledby'() { return getProps()['aria-labelledby']; },
|
|
115
129
|
get description() { return getProps().description; },
|
|
116
|
-
get errorMessage() { return getProps().errorMessage; },
|
|
117
|
-
get isInvalid() { return
|
|
130
|
+
get errorMessage() { return getProps().errorMessage ?? fallbackErrorMessage(); },
|
|
131
|
+
get isInvalid() { return isInvalid(); },
|
|
118
132
|
// Radio group is not an HTML input element so it
|
|
119
133
|
// shouldn't be labeled by a <label> element.
|
|
120
134
|
labelElementType: 'span',
|
|
@@ -147,12 +161,72 @@ export function createRadioGroup(
|
|
|
147
161
|
validationBehavior: validationBehavior(),
|
|
148
162
|
});
|
|
149
163
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
const getNavigableRadios = (root: HTMLElement): HTMLInputElement[] => {
|
|
165
|
+
return Array.from(root.querySelectorAll('input[type="radio"]'))
|
|
166
|
+
.filter((el): el is HTMLInputElement => {
|
|
167
|
+
return el instanceof HTMLInputElement && !el.matches(':disabled');
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Keyboard navigation parity with React Aria.
|
|
172
|
+
const onKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (e) => {
|
|
173
|
+
let nextDir: 'next' | 'prev' | null = null;
|
|
174
|
+
const currentOrientation = orientation();
|
|
175
|
+
const isHorizontal = currentOrientation !== 'vertical';
|
|
176
|
+
const isRTL = locale().direction === 'rtl' && isHorizontal;
|
|
177
|
+
|
|
178
|
+
switch (e.key) {
|
|
179
|
+
case 'ArrowRight':
|
|
180
|
+
nextDir = isRTL ? 'prev' : 'next';
|
|
181
|
+
break;
|
|
182
|
+
case 'ArrowLeft':
|
|
183
|
+
nextDir = isRTL ? 'next' : 'prev';
|
|
184
|
+
break;
|
|
185
|
+
case 'ArrowDown':
|
|
186
|
+
nextDir = 'next';
|
|
187
|
+
break;
|
|
188
|
+
case 'ArrowUp':
|
|
189
|
+
nextDir = 'prev';
|
|
190
|
+
break;
|
|
191
|
+
default:
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
|
|
197
|
+
const root = e.currentTarget;
|
|
198
|
+
if (!(root instanceof HTMLElement)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const radios = getNavigableRadios(root);
|
|
203
|
+
if (radios.length === 0) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const eventTarget = getEventTarget<Element>(e);
|
|
208
|
+
const activeElement = root.ownerDocument.activeElement;
|
|
209
|
+
|
|
210
|
+
const currentRadio = eventTarget instanceof HTMLInputElement && eventTarget.type === 'radio'
|
|
211
|
+
? eventTarget
|
|
212
|
+
: (activeElement instanceof HTMLInputElement && activeElement.type === 'radio' ? activeElement : null);
|
|
213
|
+
|
|
214
|
+
const currentIndex = currentRadio ? radios.indexOf(currentRadio) : -1;
|
|
215
|
+
|
|
216
|
+
let nextIndex: number;
|
|
217
|
+
if (nextDir === 'next') {
|
|
218
|
+
nextIndex = currentIndex >= 0 ? (currentIndex + 1) % radios.length : 0;
|
|
219
|
+
} else {
|
|
220
|
+
nextIndex = currentIndex >= 0 ? (currentIndex - 1 + radios.length) % radios.length : radios.length - 1;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const nextRadio = radios[nextIndex];
|
|
224
|
+
if (!nextRadio) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
focusSafely(nextRadio);
|
|
229
|
+
state.setSelectedValue(nextRadio.value);
|
|
156
230
|
};
|
|
157
231
|
|
|
158
232
|
return {
|
|
@@ -163,7 +237,7 @@ export function createRadioGroup(
|
|
|
163
237
|
{
|
|
164
238
|
role: 'radiogroup',
|
|
165
239
|
onKeyDown,
|
|
166
|
-
'aria-invalid':
|
|
240
|
+
'aria-invalid': isInvalid() || undefined,
|
|
167
241
|
'aria-errormessage': getProps()['aria-errormessage'],
|
|
168
242
|
'aria-readonly': isReadOnly() || undefined,
|
|
169
243
|
'aria-required': isRequired() || undefined,
|
|
@@ -177,13 +251,13 @@ export function createRadioGroup(
|
|
|
177
251
|
descriptionProps,
|
|
178
252
|
errorMessageProps,
|
|
179
253
|
get isInvalid() {
|
|
180
|
-
return
|
|
254
|
+
return isInvalid();
|
|
181
255
|
},
|
|
182
256
|
get validationErrors() {
|
|
183
|
-
return
|
|
257
|
+
return validationErrors();
|
|
184
258
|
},
|
|
185
259
|
get validationDetails() {
|
|
186
|
-
return
|
|
260
|
+
return validationDetails();
|
|
187
261
|
},
|
|
188
262
|
};
|
|
189
263
|
}
|