@proyecto-viviana/solidaria-components 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ActionBar.d.ts.map +1 -1
- package/dist/ActionBar.js +2 -11
- package/dist/ActionBar.js.map +1 -1
- package/dist/ActionBar.jsx +2 -11
- package/dist/ActionBar.jsx.map +1 -1
- package/dist/ActionGroup.d.ts.map +1 -1
- package/dist/ActionGroup.js +1 -1
- package/dist/ActionGroup.js.map +1 -1
- package/dist/ActionGroup.jsx +1 -1
- package/dist/ActionGroup.jsx.map +1 -1
- package/dist/Autocomplete.d.ts +2 -2
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Autocomplete.js +7 -7
- package/dist/Autocomplete.js.map +1 -1
- package/dist/Autocomplete.jsx +7 -7
- package/dist/Autocomplete.jsx.map +1 -1
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Breadcrumbs.js +18 -18
- package/dist/Breadcrumbs.js.map +1 -1
- package/dist/Breadcrumbs.jsx +21 -26
- package/dist/Breadcrumbs.jsx.map +1 -1
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Calendar.js +100 -65
- package/dist/Calendar.js.map +1 -1
- package/dist/Calendar.jsx +10 -1
- package/dist/Calendar.jsx.map +1 -1
- package/dist/Color.d.ts.map +1 -1
- package/dist/Color.js +15 -18
- package/dist/Color.js.map +1 -1
- package/dist/Color.jsx +15 -19
- package/dist/Color.jsx.map +1 -1
- package/dist/ComboBox.d.ts +4 -0
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ComboBox.js +21 -13
- package/dist/ComboBox.js.map +1 -1
- package/dist/ComboBox.jsx +23 -13
- package/dist/ComboBox.jsx.map +1 -1
- package/dist/DateField.d.ts +11 -2
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DateField2.js +237 -234
- package/dist/DateField2.js.map +1 -1
- package/dist/DateField2.jsx +134 -97
- package/dist/DateField2.jsx.map +1 -1
- package/dist/DatePicker.d.ts +42 -3
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DatePicker.js +6 -990
- package/dist/DatePicker.jsx +6 -681
- package/dist/DatePicker2.js +1171 -0
- package/dist/DatePicker2.js.map +1 -0
- package/dist/DatePicker2.jsx +820 -0
- package/dist/DatePicker2.jsx.map +1 -0
- package/dist/DateRangePickerContext.d.ts +2 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -1
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/Disclosure.js +3 -0
- package/dist/Disclosure.js.map +1 -1
- package/dist/Disclosure.jsx +1 -1
- package/dist/Disclosure.jsx.map +1 -1
- package/dist/ElementTag.d.ts +29 -0
- package/dist/ElementTag.d.ts.map +1 -0
- package/dist/ElementTag.js +220 -0
- package/dist/ElementTag.js.map +1 -0
- package/dist/ElementTag.jsx +67 -0
- package/dist/ElementTag.jsx.map +1 -0
- package/dist/GridList.d.ts.map +1 -1
- package/dist/GridList.js +2 -2
- package/dist/GridList.js.map +1 -1
- package/dist/GridList.jsx +2 -2
- package/dist/GridList.jsx.map +1 -1
- package/dist/Landmark.js +4 -3
- package/dist/Landmark.js.map +1 -1
- package/dist/Landmark.jsx +3 -3
- package/dist/Landmark.jsx.map +1 -1
- package/dist/Link.js +3 -2
- package/dist/Link.js.map +1 -1
- package/dist/Link.jsx +3 -3
- package/dist/Link.jsx.map +1 -1
- package/dist/ListBox.d.ts +2 -2
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListBox.js +327 -166
- package/dist/ListBox.js.map +1 -1
- package/dist/ListBox.jsx +161 -25
- package/dist/ListBox.jsx.map +1 -1
- package/dist/Modal.d.ts.map +1 -1
- package/dist/Modal.js +7 -5
- package/dist/Modal.js.map +1 -1
- package/dist/Modal.jsx +5 -5
- package/dist/Modal.jsx.map +1 -1
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Popover.js +16 -9
- package/dist/Popover.js.map +1 -1
- package/dist/Popover.jsx +25 -4
- package/dist/Popover.jsx.map +1 -1
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RangeCalendar.js +182 -46
- package/dist/RangeCalendar.js.map +1 -1
- package/dist/RangeCalendar.jsx +72 -2
- package/dist/RangeCalendar.jsx.map +1 -1
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/SearchField.js +26 -2
- package/dist/SearchField.js.map +1 -1
- package/dist/SearchField.jsx +26 -2
- package/dist/SearchField.jsx.map +1 -1
- package/dist/Select.d.ts +7 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/Select.js +64 -51
- package/dist/Select.js.map +1 -1
- package/dist/Select.jsx +35 -20
- package/dist/Select.jsx.map +1 -1
- package/dist/Separator.js +3 -2
- package/dist/Separator.js.map +1 -1
- package/dist/Separator.jsx +2 -2
- package/dist/Separator.jsx.map +1 -1
- package/dist/SharedElementTransition.d.ts.map +1 -1
- package/dist/SharedElementTransition.js +35 -33
- package/dist/SharedElementTransition.js.map +1 -1
- package/dist/SharedElementTransition.jsx +35 -33
- package/dist/SharedElementTransition.jsx.map +1 -1
- package/dist/StepList.d.ts.map +1 -1
- package/dist/StepList.js +2 -9
- package/dist/StepList.js.map +1 -1
- package/dist/StepList.jsx +1 -6
- package/dist/StepList.jsx.map +1 -1
- package/dist/Table.d.ts +2 -0
- package/dist/Table.d.ts.map +1 -1
- package/dist/Table.js +10 -3
- package/dist/Table.js.map +1 -1
- package/dist/Table.jsx +10 -3
- package/dist/Table.jsx.map +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/Tabs.js +3 -0
- package/dist/Tabs.js.map +1 -1
- package/dist/Tabs.jsx +1 -1
- package/dist/Tabs.jsx.map +1 -1
- package/dist/TagGroup.d.ts +3 -1
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/TagGroup.js +12 -8
- package/dist/TagGroup.js.map +1 -1
- package/dist/TagGroup.jsx +11 -8
- package/dist/TagGroup.jsx.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +9 -8
- package/dist/Text.js.map +1 -1
- package/dist/Text.jsx +3 -3
- package/dist/Text.jsx.map +1 -1
- package/dist/TextField.js +4 -1
- package/dist/TextField.js.map +1 -1
- package/dist/TextField.jsx +4 -1
- package/dist/TextField.jsx.map +1 -1
- package/dist/TimeField.d.ts +14 -50
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/TimeField.js +111 -205
- package/dist/TimeField.js.map +1 -1
- package/dist/TimeField.jsx +76 -82
- package/dist/TimeField.jsx.map +1 -1
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Tree.js +3 -3
- package/dist/Tree.js.map +1 -1
- package/dist/Tree.jsx +3 -3
- package/dist/Tree.jsx.map +1 -1
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/VisuallyHidden.js +3 -11
- package/dist/VisuallyHidden.js.map +1 -1
- package/dist/VisuallyHidden.jsx +3 -4
- package/dist/VisuallyHidden.jsx.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.jsx +6 -6
- package/dist/useDragAndDrop.d.ts +0 -8
- package/dist/useDragAndDrop.d.ts.map +1 -1
- package/dist/useDragAndDrop2.js +6 -2
- package/dist/useDragAndDrop2.js.map +1 -1
- package/dist/useDragAndDrop2.jsx +6 -2
- package/dist/useDragAndDrop2.jsx.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/utils.jsx +2 -2
- package/dist/utils.jsx.map +1 -1
- package/package.json +3 -9
- package/src/ActionBar.tsx +10 -12
- package/src/ActionGroup.tsx +5 -3
- package/src/Autocomplete.tsx +14 -9
- package/src/Breadcrumbs.tsx +57 -42
- package/src/Calendar.tsx +30 -1
- package/src/Color.tsx +42 -27
- package/src/ComboBox.tsx +49 -17
- package/src/DateField.tsx +285 -52
- package/src/DatePicker.tsx +345 -57
- package/src/DateRangePickerContext.tsx +2 -0
- package/src/Disclosure.tsx +4 -0
- package/src/ElementTag.tsx +116 -0
- package/src/GridList.tsx +8 -2
- package/src/Landmark.tsx +4 -4
- package/src/Link.tsx +4 -4
- package/src/ListBox.tsx +323 -21
- package/src/Modal.tsx +21 -5
- package/src/Popover.tsx +28 -7
- package/src/RangeCalendar.tsx +120 -1
- package/src/SearchField.tsx +53 -1
- package/src/Select.tsx +114 -22
- package/src/Separator.tsx +3 -3
- package/src/SharedElementTransition.tsx +92 -36
- package/src/StepList.tsx +0 -7
- package/src/Table.tsx +19 -4
- package/src/Tabs.tsx +1 -0
- package/src/TagGroup.tsx +26 -6
- package/src/Text.tsx +5 -4
- package/src/TextField.tsx +1 -1
- package/src/TimeField.tsx +113 -196
- package/src/Tree.tsx +14 -4
- package/src/VisuallyHidden.tsx +8 -12
- package/src/index.ts +3 -8
- package/src/useDragAndDrop.ts +8 -10
- package/src/utils.tsx +3 -3
- package/dist/DatePicker.js.map +0 -1
- package/dist/DatePicker.jsx.map +0 -1
- package/dist/HiddenTimeInput.d.ts +0 -25
- package/dist/HiddenTimeInput.d.ts.map +0 -1
- package/dist/HiddenTimeInput.js +0 -104
- package/dist/HiddenTimeInput.js.map +0 -1
- package/dist/HiddenTimeInput.jsx +0 -69
- package/dist/HiddenTimeInput.jsx.map +0 -1
- package/src/HiddenTimeInput.tsx +0 -133
package/src/DatePicker.tsx
CHANGED
|
@@ -20,13 +20,19 @@ import {
|
|
|
20
20
|
import { Portal } from "solid-js/web";
|
|
21
21
|
import {
|
|
22
22
|
createDatePicker,
|
|
23
|
+
createDateField,
|
|
23
24
|
createDateRangePicker,
|
|
25
|
+
createEnterAnimation,
|
|
26
|
+
createFocusRing,
|
|
27
|
+
createHover,
|
|
24
28
|
createPopover,
|
|
29
|
+
mergeProps,
|
|
25
30
|
FocusScope,
|
|
26
31
|
useUNSAFE_PortalContext,
|
|
27
32
|
type AriaDatePickerProps,
|
|
28
33
|
type AriaDateRangePickerProps,
|
|
29
34
|
type DatePickerState as AriaDatePickerState,
|
|
35
|
+
type PlacementAxis,
|
|
30
36
|
} from "@proyecto-viviana/solidaria";
|
|
31
37
|
import {
|
|
32
38
|
createDateFieldState,
|
|
@@ -176,6 +182,25 @@ export interface DatePickerButtonRenderProps {
|
|
|
176
182
|
isDisabled: boolean;
|
|
177
183
|
/** Whether the calendar is open. */
|
|
178
184
|
isOpen: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Whether the button is currently pressed. Provided by the single
|
|
187
|
+
* DatePicker (fed from `createDatePicker().isButtonPressed()`); the range
|
|
188
|
+
* variant does not yet publish press state, so this is optional.
|
|
189
|
+
*/
|
|
190
|
+
isPressed?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Whether the button is hovered. Fed from the trigger's own `createHover`;
|
|
193
|
+
* mirrors RAC `Button`'s `isHovered` renderProp so the styled S2
|
|
194
|
+
* `baseColor("gray-100")` hover-step background compiles into the className.
|
|
195
|
+
*/
|
|
196
|
+
isHovered?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Whether the button is keyboard-focused (focus-visible). Fed from
|
|
199
|
+
* `createFocusRing`; mirrors RAC `Button`'s `isFocusVisible` so the styled
|
|
200
|
+
* S2 `focusRing()` outline (a renderProps-gated class, not a CSS
|
|
201
|
+
* `[data-focus-visible]` selector) compiles into the className.
|
|
202
|
+
*/
|
|
203
|
+
isFocusVisible?: boolean;
|
|
179
204
|
}
|
|
180
205
|
|
|
181
206
|
export interface DatePickerButtonProps extends SlotProps {
|
|
@@ -187,6 +212,12 @@ export interface DatePickerButtonProps extends SlotProps {
|
|
|
187
212
|
style?: StyleOrFunction<DatePickerButtonRenderProps>;
|
|
188
213
|
/** Whether the button is disabled. */
|
|
189
214
|
isDisabled?: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* Ref callback for the underlying button element. Mirrors S2's CalendarButton,
|
|
217
|
+
* whose `buttonRef` feeds `pressScale(buttonRef)`; the styled layer forwards a
|
|
218
|
+
* setter here so it can size the press transform against the real element.
|
|
219
|
+
*/
|
|
220
|
+
ref?: (element: HTMLButtonElement) => void;
|
|
190
221
|
}
|
|
191
222
|
|
|
192
223
|
export interface DateRangePickerButtonProps extends DatePickerButtonProps {}
|
|
@@ -454,6 +485,23 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
|
|
|
454
485
|
isDateDisabled: stateProps.isDateDisabled,
|
|
455
486
|
});
|
|
456
487
|
|
|
488
|
+
// RAC's useDatePicker re-mounts a FRESH useCalendarState every popover open
|
|
489
|
+
// and passes autoFocus:true, so useCalendarState seeds isFocused=true and the
|
|
490
|
+
// focused cell auto-focuses (useCalendarState.mjs line 80:
|
|
491
|
+
// `useState(props.autoFocus || false)`). The port SHARES one persistent
|
|
492
|
+
// calendarState across opens, so isFocused would stay whatever it last was
|
|
493
|
+
// (false after the first close). Mirror the fresh-mount behavior by flagging
|
|
494
|
+
// the shared state focused on each false->true open transition; the
|
|
495
|
+
// CalendarCell focus effect then pulls DOM focus to the value/today cell.
|
|
496
|
+
let wasOpen = false;
|
|
497
|
+
createEffect(() => {
|
|
498
|
+
const open = datePickerState.isOpen();
|
|
499
|
+
if (open && !wasOpen) {
|
|
500
|
+
calendarState.setFocused(true);
|
|
501
|
+
}
|
|
502
|
+
wasOpen = open;
|
|
503
|
+
});
|
|
504
|
+
|
|
457
505
|
// Create date picker ARIA props
|
|
458
506
|
const pickerAria = createDatePicker(
|
|
459
507
|
() => ({
|
|
@@ -464,6 +512,18 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
|
|
|
464
512
|
fieldState as unknown as DateFieldState<DateValue>,
|
|
465
513
|
overlayState as AriaDatePickerState,
|
|
466
514
|
calendarState as unknown as CalendarState<DateValue>,
|
|
515
|
+
() => fieldRef(),
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
// The composed picker's fieldProps (stamped role="presentation") are fed
|
|
519
|
+
// through createDateField — mirroring RAC's DateField consuming
|
|
520
|
+
// useDatePicker().fieldProps. This is what actually names the segments and
|
|
521
|
+
// publishes the value description through the shared hookData WeakMap; without
|
|
522
|
+
// it the segments would be unnamed and carry no selected-date announcement.
|
|
523
|
+
const fieldAria = createDateField(
|
|
524
|
+
() => pickerAria.fieldProps,
|
|
525
|
+
fieldState as unknown as DateFieldState<DateValue>,
|
|
526
|
+
() => fieldRef(),
|
|
467
527
|
);
|
|
468
528
|
|
|
469
529
|
const contextValue: DatePickerContextValue = {
|
|
@@ -518,18 +578,35 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
|
|
|
518
578
|
<DateFieldContext.Provider
|
|
519
579
|
value={{
|
|
520
580
|
state: fieldState as unknown as DateFieldState<DateValue>,
|
|
581
|
+
// Read through getters so the DateInput/DateSegment consumers see the
|
|
582
|
+
// LIVE memo values (createDescription appends the value-description id
|
|
583
|
+
// via a deferred effect; froze snapshots would miss it). inputProps are
|
|
584
|
+
// the createDateField group props (role="presentation" here), which name
|
|
585
|
+
// the segments — NOT the raw pickerAria.fieldProps.
|
|
521
586
|
aria: {
|
|
522
|
-
labelProps
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
587
|
+
get labelProps() {
|
|
588
|
+
return pickerAria.labelProps;
|
|
589
|
+
},
|
|
590
|
+
get inputProps() {
|
|
591
|
+
return fieldAria.fieldProps;
|
|
592
|
+
},
|
|
593
|
+
get descriptionProps() {
|
|
594
|
+
return pickerAria.descriptionProps;
|
|
595
|
+
},
|
|
596
|
+
get errorMessageProps() {
|
|
597
|
+
return pickerAria.errorMessageProps;
|
|
598
|
+
},
|
|
526
599
|
},
|
|
527
600
|
}}
|
|
528
601
|
>
|
|
529
602
|
<CalendarContext.Provider value={calendarState as unknown as CalendarState<DateValue>}>
|
|
603
|
+
{/* BARE ROLELESS root — mirrors RAC `DatePicker`'s outer `<div>`. The
|
|
604
|
+
* presentation FieldGroup shell (rendered as a child via
|
|
605
|
+
* `DatePickerFieldGroup`) is what carries `pickerAria.groupProps`
|
|
606
|
+
* (role="presentation" + label/describedby + arrow-nav/press). A
|
|
607
|
+
* described node here would be a spurious AX entry the S2 oracle lacks. */}
|
|
530
608
|
<div
|
|
531
609
|
ref={setFieldRef}
|
|
532
|
-
{...pickerAria.groupProps}
|
|
533
610
|
class={renderProps.class()}
|
|
534
611
|
style={renderProps.style()}
|
|
535
612
|
data-disabled={dataAttr(fieldState.isDisabled())}
|
|
@@ -649,6 +726,10 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
|
649
726
|
};
|
|
650
727
|
|
|
651
728
|
let triggerRef: HTMLElement | null = null;
|
|
729
|
+
// The roleless root element — scopes the shared segment focus manager and the
|
|
730
|
+
// outer arrow-navigation across BOTH fields, mirroring the single DatePicker's
|
|
731
|
+
// `fieldRef`.
|
|
732
|
+
const [fieldRef, setFieldRef] = createSignal<HTMLDivElement | null>(null);
|
|
652
733
|
const overlayState = {
|
|
653
734
|
get isOpen() {
|
|
654
735
|
return isOpen();
|
|
@@ -683,6 +764,21 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
|
683
764
|
},
|
|
684
765
|
});
|
|
685
766
|
|
|
767
|
+
// Mirror the single DatePicker: RAC re-mounts a FRESH useRangeCalendarState
|
|
768
|
+
// per popover open with autoFocus:true, so isFocused seeds true and the focused
|
|
769
|
+
// cell auto-focuses AND publishes its range-selection prompt describedby
|
|
770
|
+
// ("Click to start selecting date range"). The port shares one persistent
|
|
771
|
+
// range state across opens, so flag it focused on each false->true open
|
|
772
|
+
// transition to reproduce that behavior.
|
|
773
|
+
let wasRangeOpen = false;
|
|
774
|
+
createEffect(() => {
|
|
775
|
+
const open = overlayState.isOpen;
|
|
776
|
+
if (open && !wasRangeOpen) {
|
|
777
|
+
calendarState.setFocused(true);
|
|
778
|
+
}
|
|
779
|
+
wasRangeOpen = open;
|
|
780
|
+
});
|
|
781
|
+
|
|
686
782
|
const isInvalid = createMemo(
|
|
687
783
|
() =>
|
|
688
784
|
Boolean((rest as { isInvalid?: boolean }).isInvalid) ||
|
|
@@ -767,14 +863,36 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
|
767
863
|
}),
|
|
768
864
|
calendarState as unknown as RangeCalendarState<DateValue>,
|
|
769
865
|
overlayState as AriaDatePickerState,
|
|
866
|
+
() => fieldRef(),
|
|
867
|
+
);
|
|
868
|
+
|
|
869
|
+
// Each range field's presentation field props (stamped role="presentation" +
|
|
870
|
+
// the SHARED focus manager) are fed through createDateField — mirroring RAC's
|
|
871
|
+
// two `<DateInput>`s consuming useDateRangePicker().start/endFieldProps. This
|
|
872
|
+
// names the segments ("month, Start Date" / "month, End Date") and publishes
|
|
873
|
+
// them through the shared hookData WeakMap; the shared focus manager makes arrow
|
|
874
|
+
// keys and auto-advance walk across the start/end boundary.
|
|
875
|
+
const startFieldAria = createDateField(
|
|
876
|
+
() => pickerAria.startFieldProps,
|
|
877
|
+
startFieldState as unknown as DateFieldState<DateValue>,
|
|
878
|
+
() => fieldRef(),
|
|
879
|
+
);
|
|
880
|
+
const endFieldAria = createDateField(
|
|
881
|
+
() => pickerAria.endFieldProps,
|
|
882
|
+
endFieldState as unknown as DateFieldState<DateValue>,
|
|
883
|
+
() => fieldRef(),
|
|
770
884
|
);
|
|
771
885
|
|
|
772
886
|
const startFieldContext: DateRangePickerFieldContextValue = {
|
|
773
887
|
state: startFieldState as unknown as DateFieldState<DateValue>,
|
|
774
888
|
aria: {
|
|
775
889
|
labelProps: {},
|
|
890
|
+
// The DateInput group carries createDateField's fieldProps (role="presentation"
|
|
891
|
+
// + arrow-nav bubbling + unicode-bidi isolate), mirroring the standalone
|
|
892
|
+
// DateField's `inputProps ← fieldProps` mapping. NO hiddenInputProps — the
|
|
893
|
+
// range owner renders its HiddenDateInput siblings below.
|
|
776
894
|
get inputProps() {
|
|
777
|
-
return
|
|
895
|
+
return startFieldAria.fieldProps;
|
|
778
896
|
},
|
|
779
897
|
get descriptionProps() {
|
|
780
898
|
return pickerAria.descriptionProps;
|
|
@@ -790,7 +908,7 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
|
790
908
|
aria: {
|
|
791
909
|
labelProps: {},
|
|
792
910
|
get inputProps() {
|
|
793
|
-
return
|
|
911
|
+
return endFieldAria.fieldProps;
|
|
794
912
|
},
|
|
795
913
|
get descriptionProps() {
|
|
796
914
|
return pickerAria.descriptionProps;
|
|
@@ -841,8 +959,15 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
|
841
959
|
<RangeCalendarContext.Provider
|
|
842
960
|
value={calendarState as unknown as RangeCalendarState<DateValue>}
|
|
843
961
|
>
|
|
962
|
+
{/* BARE ROLELESS root — mirrors RAC `DateRangePicker`'s outer `<div>`.
|
|
963
|
+
* The presentation FieldGroup shell (rendered as a child via the styled
|
|
964
|
+
* DateRangeDisplay) is what carries `pickerAria.groupProps` (role=
|
|
965
|
+
* "presentation" + label/describedby + outer arrow-nav/press). A described
|
|
966
|
+
* or role="group" node here would be a spurious AX entry the S2 oracle
|
|
967
|
+
* lacks. The ref scopes the shared segment focus manager across both
|
|
968
|
+
* fields (see createDateRangePicker). */}
|
|
844
969
|
<div
|
|
845
|
-
{
|
|
970
|
+
ref={setFieldRef}
|
|
846
971
|
class={renderProps.class()}
|
|
847
972
|
style={renderProps.style()}
|
|
848
973
|
data-disabled={dataAttr(calendarState.isDisabled())}
|
|
@@ -907,9 +1032,31 @@ function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
|
907
1032
|
export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
|
|
908
1033
|
const context = useDatePickerContext();
|
|
909
1034
|
|
|
1035
|
+
// Mirror RAC's `buttonProps.isDisabled = props.isDisabled || props.isReadOnly`
|
|
1036
|
+
// (a read-only picker can't open its calendar) — `createDatePicker` computes
|
|
1037
|
+
// this as `isButtonDisabled()`. Reading `fieldState.isDisabled()` alone would
|
|
1038
|
+
// miss the read-only case (read-only ≠ disabled on the field), so the trigger
|
|
1039
|
+
// would paint enabled while the S2 oracle dims it.
|
|
1040
|
+
const isDisabled = () => context.pickerAria.isButtonDisabled() || (props.isDisabled ?? false);
|
|
1041
|
+
|
|
1042
|
+
// Mirror RAC: the trigger is a `<Button>` whose own `useFocusRing`/`useHover`/
|
|
1043
|
+
// `usePress` drive its interaction paint state. The press signal is owned by
|
|
1044
|
+
// `createDatePicker` (`isButtonPressed`); focus-visible and hover are wired
|
|
1045
|
+
// here so the S2 `focusRing()`/`baseColor()` selectors (`data-focus-visible`,
|
|
1046
|
+
// `data-hovered`, `data-pressed`) resolve exactly as they do upstream.
|
|
1047
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
1048
|
+
const { isHovered, hoverProps } = createHover({
|
|
1049
|
+
get isDisabled() {
|
|
1050
|
+
return isDisabled();
|
|
1051
|
+
},
|
|
1052
|
+
});
|
|
1053
|
+
|
|
910
1054
|
const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
|
|
911
|
-
isDisabled:
|
|
1055
|
+
isDisabled: isDisabled(),
|
|
912
1056
|
isOpen: context.overlayState.isOpen,
|
|
1057
|
+
isPressed: context.pickerAria.isButtonPressed(),
|
|
1058
|
+
isHovered: isHovered(),
|
|
1059
|
+
isFocusVisible: isFocusVisible(),
|
|
913
1060
|
}));
|
|
914
1061
|
|
|
915
1062
|
const renderProps = useRenderProps(
|
|
@@ -932,17 +1079,30 @@ export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
|
|
|
932
1079
|
return props.children ?? "📅";
|
|
933
1080
|
};
|
|
934
1081
|
|
|
1082
|
+
const buttonProps = createMemo(() =>
|
|
1083
|
+
mergeProps(
|
|
1084
|
+
context.pickerAria.buttonProps as Record<string, unknown>,
|
|
1085
|
+
focusProps as Record<string, unknown>,
|
|
1086
|
+
hoverProps as Record<string, unknown>,
|
|
1087
|
+
),
|
|
1088
|
+
);
|
|
1089
|
+
|
|
935
1090
|
return (
|
|
936
1091
|
<button
|
|
937
1092
|
ref={(el) => {
|
|
938
1093
|
context.setTriggerRef(el);
|
|
1094
|
+
props.ref?.(el);
|
|
939
1095
|
}}
|
|
940
|
-
{...
|
|
1096
|
+
{...buttonProps()}
|
|
941
1097
|
class={renderProps.class()}
|
|
942
1098
|
style={renderProps.style()}
|
|
943
|
-
disabled={
|
|
944
|
-
data-disabled={dataAttr(
|
|
1099
|
+
disabled={isDisabled()}
|
|
1100
|
+
data-disabled={dataAttr(isDisabled())}
|
|
945
1101
|
data-open={dataAttr(context.overlayState.isOpen)}
|
|
1102
|
+
attr:data-hovered={isHovered() ? "true" : undefined}
|
|
1103
|
+
attr:data-focused={isFocused() ? "true" : undefined}
|
|
1104
|
+
attr:data-focus-visible={isFocusVisible() ? "true" : undefined}
|
|
1105
|
+
attr:data-pressed={context.pickerAria.isButtonPressed() ? "true" : undefined}
|
|
946
1106
|
>
|
|
947
1107
|
{getChildren()}
|
|
948
1108
|
</button>
|
|
@@ -952,9 +1112,30 @@ export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
|
|
|
952
1112
|
export function DateRangePickerButton(props: DateRangePickerButtonProps): JSX.Element {
|
|
953
1113
|
const context = useDateRangePickerContext();
|
|
954
1114
|
|
|
1115
|
+
// Mirror RAC's `buttonProps.isDisabled = props.isDisabled || props.isReadOnly`
|
|
1116
|
+
// (a read-only picker can't open its calendar) — `createDateRangePicker` computes
|
|
1117
|
+
// this as `isButtonDisabled()`. Reading `calendarState.isDisabled()` alone would
|
|
1118
|
+
// miss the read-only case, so the trigger would paint enabled while S2 dims it.
|
|
1119
|
+
const isDisabled = () => context.pickerAria.isButtonDisabled() || (props.isDisabled ?? false);
|
|
1120
|
+
|
|
1121
|
+
// Mirror RAC: the trigger is a `<Button>` whose own `useFocusRing`/`useHover`/
|
|
1122
|
+
// `usePress` drive its interaction paint state. The press signal is owned by
|
|
1123
|
+
// `createDateRangePicker` (`isButtonPressed`); focus-visible and hover are wired
|
|
1124
|
+
// here so the S2 `focusRing()`/`baseColor()` selectors (`data-focus-visible`,
|
|
1125
|
+
// `data-hovered`, `data-pressed`) resolve exactly as they do upstream.
|
|
1126
|
+
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
1127
|
+
const { isHovered, hoverProps } = createHover({
|
|
1128
|
+
get isDisabled() {
|
|
1129
|
+
return isDisabled();
|
|
1130
|
+
},
|
|
1131
|
+
});
|
|
1132
|
+
|
|
955
1133
|
const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
|
|
956
|
-
isDisabled:
|
|
1134
|
+
isDisabled: isDisabled(),
|
|
957
1135
|
isOpen: context.overlayState.isOpen,
|
|
1136
|
+
isPressed: context.pickerAria.isButtonPressed(),
|
|
1137
|
+
isHovered: isHovered(),
|
|
1138
|
+
isFocusVisible: isFocusVisible(),
|
|
958
1139
|
}));
|
|
959
1140
|
|
|
960
1141
|
const renderProps = useRenderProps(
|
|
@@ -976,26 +1157,56 @@ export function DateRangePickerButton(props: DateRangePickerButtonProps): JSX.El
|
|
|
976
1157
|
return props.children ?? "📅";
|
|
977
1158
|
};
|
|
978
1159
|
|
|
1160
|
+
const buttonProps = createMemo(() =>
|
|
1161
|
+
mergeProps(
|
|
1162
|
+
context.pickerAria.buttonProps as Record<string, unknown>,
|
|
1163
|
+
focusProps as Record<string, unknown>,
|
|
1164
|
+
hoverProps as Record<string, unknown>,
|
|
1165
|
+
),
|
|
1166
|
+
);
|
|
1167
|
+
|
|
979
1168
|
return (
|
|
980
1169
|
<button
|
|
981
|
-
ref={(el) =>
|
|
982
|
-
|
|
1170
|
+
ref={(el) => {
|
|
1171
|
+
context.setTriggerRef(el);
|
|
1172
|
+
props.ref?.(el);
|
|
1173
|
+
}}
|
|
1174
|
+
{...buttonProps()}
|
|
983
1175
|
class={renderProps.class()}
|
|
984
1176
|
style={renderProps.style()}
|
|
985
|
-
disabled={
|
|
986
|
-
data-disabled={dataAttr(
|
|
1177
|
+
disabled={isDisabled()}
|
|
1178
|
+
data-disabled={dataAttr(isDisabled())}
|
|
987
1179
|
data-open={dataAttr(context.overlayState.isOpen)}
|
|
1180
|
+
attr:data-hovered={isHovered() ? "true" : undefined}
|
|
1181
|
+
attr:data-focused={isFocused() ? "true" : undefined}
|
|
1182
|
+
attr:data-focus-visible={isFocusVisible() ? "true" : undefined}
|
|
1183
|
+
attr:data-pressed={context.pickerAria.isButtonPressed() ? "true" : undefined}
|
|
988
1184
|
>
|
|
989
1185
|
{getChildren()}
|
|
990
1186
|
</button>
|
|
991
1187
|
);
|
|
992
1188
|
}
|
|
993
1189
|
|
|
1190
|
+
/**
|
|
1191
|
+
* Render props for the popover surface, mirroring RAC `Popover`'s
|
|
1192
|
+
* enter/exit/placement renderProps. The styled layer keys the S2
|
|
1193
|
+
* opacity/translate enter transition on these (a renderProps-gated class, not a
|
|
1194
|
+
* CSS `[data-entering]` selector).
|
|
1195
|
+
*/
|
|
1196
|
+
export interface DatePickerContentRenderProps {
|
|
1197
|
+
/** Whether the popover is playing its enter transition (`data-entering`). */
|
|
1198
|
+
isEntering: boolean;
|
|
1199
|
+
/** Whether the popover is playing its exit transition (`data-exiting`). */
|
|
1200
|
+
isExiting: boolean;
|
|
1201
|
+
/** The resolved placement axis, for the direction-dependent translate. */
|
|
1202
|
+
placement: PlacementAxis | null;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
994
1205
|
export interface DatePickerContentProps extends SlotProps {
|
|
995
1206
|
/** The children of the component. */
|
|
996
1207
|
children?: JSX.Element;
|
|
997
|
-
/** The CSS className for the element. */
|
|
998
|
-
class?: string;
|
|
1208
|
+
/** The CSS className for the element, or a function of the render props. */
|
|
1209
|
+
class?: string | ((renderProps: DatePickerContentRenderProps) => string);
|
|
999
1210
|
/** The inline style for the element. */
|
|
1000
1211
|
style?: JSX.CSSProperties;
|
|
1001
1212
|
}
|
|
@@ -1023,10 +1234,12 @@ export interface DatePickerDescriptionProps {
|
|
|
1023
1234
|
|
|
1024
1235
|
export function DatePickerDescription(props: DatePickerDescriptionProps): JSX.Element {
|
|
1025
1236
|
const context = useDatePickerContext();
|
|
1237
|
+
// Rendered as <span> (→ AX role "text"), mirroring S2's <Text slot="description">
|
|
1238
|
+
// and the standalone DateFieldDescription — NOT a <p> (paragraph).
|
|
1026
1239
|
return (
|
|
1027
|
-
<
|
|
1240
|
+
<span {...context.pickerAria.descriptionProps} class={props.class}>
|
|
1028
1241
|
{props.children}
|
|
1029
|
-
</
|
|
1242
|
+
</span>
|
|
1030
1243
|
);
|
|
1031
1244
|
}
|
|
1032
1245
|
|
|
@@ -1037,10 +1250,12 @@ export interface DatePickerErrorMessageProps {
|
|
|
1037
1250
|
|
|
1038
1251
|
export function DatePickerErrorMessage(props: DatePickerErrorMessageProps): JSX.Element {
|
|
1039
1252
|
const context = useDatePickerContext();
|
|
1253
|
+
// Rendered as <span> (→ AX role "text"), mirroring S2's <FieldError> and the
|
|
1254
|
+
// standalone DateFieldErrorMessage — NOT a <p> and NOT role="alert".
|
|
1040
1255
|
return (
|
|
1041
|
-
<
|
|
1256
|
+
<span {...context.pickerAria.errorMessageProps} class={props.class}>
|
|
1042
1257
|
{props.children}
|
|
1043
|
-
</
|
|
1258
|
+
</span>
|
|
1044
1259
|
);
|
|
1045
1260
|
}
|
|
1046
1261
|
|
|
@@ -1065,10 +1280,12 @@ export interface DateRangePickerDescriptionProps {
|
|
|
1065
1280
|
|
|
1066
1281
|
export function DateRangePickerDescription(props: DateRangePickerDescriptionProps): JSX.Element {
|
|
1067
1282
|
const context = useDateRangePickerContext();
|
|
1283
|
+
// Rendered as <span> (→ AX role "text"), mirroring S2's <Text slot="description">
|
|
1284
|
+
// and the single DatePickerDescription — NOT a <p> (paragraph).
|
|
1068
1285
|
return (
|
|
1069
|
-
<
|
|
1286
|
+
<span {...context.pickerAria.descriptionProps} class={props.class}>
|
|
1070
1287
|
{props.children}
|
|
1071
|
-
</
|
|
1288
|
+
</span>
|
|
1072
1289
|
);
|
|
1073
1290
|
}
|
|
1074
1291
|
|
|
@@ -1079,10 +1296,12 @@ export interface DateRangePickerErrorMessageProps {
|
|
|
1079
1296
|
|
|
1080
1297
|
export function DateRangePickerErrorMessage(props: DateRangePickerErrorMessageProps): JSX.Element {
|
|
1081
1298
|
const context = useDateRangePickerContext();
|
|
1299
|
+
// Rendered as <span> (→ AX role "text"), mirroring S2's <FieldError> and the
|
|
1300
|
+
// single DatePickerErrorMessage — NOT a <p> and NOT role="alert".
|
|
1082
1301
|
return (
|
|
1083
|
-
<
|
|
1302
|
+
<span {...context.pickerAria.errorMessageProps} class={props.class}>
|
|
1084
1303
|
{props.children}
|
|
1085
|
-
</
|
|
1304
|
+
</span>
|
|
1086
1305
|
);
|
|
1087
1306
|
}
|
|
1088
1307
|
|
|
@@ -1106,13 +1325,16 @@ function createEscapeDismissFallback(isOpen: () => boolean, close: () => void):
|
|
|
1106
1325
|
export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
|
|
1107
1326
|
const context = useDatePickerContext();
|
|
1108
1327
|
const portalContext = useUNSAFE_PortalContext();
|
|
1109
|
-
let
|
|
1328
|
+
// A signal (not a plain `let`) so the enter-animation effect re-runs when the
|
|
1329
|
+
// section mounts on open — `createEnterAnimation` reads this to know the
|
|
1330
|
+
// element exists before removing the initial `data-entering` styles.
|
|
1331
|
+
const [contentRef, setContentRef] = createSignal<HTMLElement>();
|
|
1110
1332
|
const portalContainer = () => portalContext.getContainer?.() ?? undefined;
|
|
1111
1333
|
|
|
1112
1334
|
const popoverAria = createPopover(
|
|
1113
1335
|
{
|
|
1114
1336
|
triggerRef: () => context.triggerRef()?.parentElement ?? context.triggerRef(),
|
|
1115
|
-
popoverRef: () => contentRef ?? null,
|
|
1337
|
+
popoverRef: () => contentRef() ?? null,
|
|
1116
1338
|
placement: "bottom start",
|
|
1117
1339
|
offset: 8,
|
|
1118
1340
|
isNonModal: false,
|
|
@@ -1128,6 +1350,35 @@ export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
|
|
|
1128
1350
|
|
|
1129
1351
|
createEscapeDismissFallback(() => context.overlayState.isOpen, context.overlayState.close);
|
|
1130
1352
|
|
|
1353
|
+
// Mirror S2's `<Popover>` enter transition (RAC `useEnterAnimation`): the
|
|
1354
|
+
// surface mounts carrying `data-entering` (opacity 0 + a small placement-ward
|
|
1355
|
+
// translate), which an effect removes on the next frame so the CSS transition
|
|
1356
|
+
// plays. The exit half is out of scope for this unit — the popover unmounts on
|
|
1357
|
+
// close (no `isOpen || isExiting` gate), so `isExiting` stays false; only the
|
|
1358
|
+
// enter motion is certified (D2).
|
|
1359
|
+
const isEntering = createEnterAnimation(contentRef, () => context.overlayState.isOpen);
|
|
1360
|
+
|
|
1361
|
+
const renderProps = (): DatePickerContentRenderProps => ({
|
|
1362
|
+
isEntering: isEntering(),
|
|
1363
|
+
isExiting: false,
|
|
1364
|
+
// Seed the base placement so the ENTER frame already carries the
|
|
1365
|
+
// placement-ward `translate` (S2's popover motion is placement-gated:
|
|
1366
|
+
// `translateY: { placement: { bottom: { isEntering: -4 } } }`). RAC resolves
|
|
1367
|
+
// placement in a `useLayoutEffect` BEFORE the first paint, so no painted
|
|
1368
|
+
// frame is ever null-placement; the port's positioning `createEffect` lands
|
|
1369
|
+
// one frame later, so without this seed the entering frame drops `translateY`
|
|
1370
|
+
// and only `opacity` transitions (D2 caught the missing translate). The
|
|
1371
|
+
// desired placement is `bottom start` (see `createPopover` above), so the
|
|
1372
|
+
// base axis is always `bottom` until the measure refines it.
|
|
1373
|
+
placement: popoverAria.placement() ?? "bottom",
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
const resolvedClass = (): string => {
|
|
1377
|
+
const c = props.class;
|
|
1378
|
+
if (typeof c === "function") return c(renderProps());
|
|
1379
|
+
return c ?? "solidaria-DatePickerContent";
|
|
1380
|
+
};
|
|
1381
|
+
|
|
1131
1382
|
const cleanPopoverProps = () => {
|
|
1132
1383
|
const {
|
|
1133
1384
|
style: _style,
|
|
@@ -1147,28 +1398,39 @@ export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
|
|
|
1147
1398
|
};
|
|
1148
1399
|
};
|
|
1149
1400
|
|
|
1150
|
-
//
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
});
|
|
1401
|
+
// Close-focus restoration is delegated to the overlay's `FocusScope`
|
|
1402
|
+
// (`restoreFocus`) — exactly as RAC's DatePicker does, and as certified Select
|
|
1403
|
+
// and Menu do. The prior ad-hoc effect (`if (!open) triggerRef().focus()`)
|
|
1404
|
+
// fired once on mount with `open === false`, stealing focus to the trigger and
|
|
1405
|
+
// painting a phantom focus ring at rest.
|
|
1157
1406
|
|
|
1158
1407
|
return (
|
|
1159
1408
|
<Show when={context.overlayState.isOpen}>
|
|
1160
1409
|
<Portal mount={portalContainer()}>
|
|
1161
1410
|
<FocusScope contain restoreFocus>
|
|
1411
|
+
{/* Un-folded to mirror S2's `<Popover>` DOM. The OUTER role-null `<div>`
|
|
1412
|
+
* is the positioned surface that carries the enter motion + chrome —
|
|
1413
|
+
* this is the element RAC/S2 animate (`AriaPopover`'s div: role null, no
|
|
1414
|
+
* tabindex, accessible name computed from the calendar's textContent).
|
|
1415
|
+
* A nested `role="dialog"` element (RAC's `Dialog`, which S2's
|
|
1416
|
+
* CalendarPopover wraps the Calendar in) carries the dialog semantics.
|
|
1417
|
+
* Folding both onto one `<section role="dialog">` made D2 animate the
|
|
1418
|
+
* dialog node instead of the role-null div; the role-null wrapper is
|
|
1419
|
+
* pruned from the AX tree so D5/D6 are unaffected. */}
|
|
1162
1420
|
<div
|
|
1163
|
-
ref={
|
|
1421
|
+
ref={setContentRef}
|
|
1164
1422
|
{...cleanPopoverProps()}
|
|
1165
|
-
{
|
|
1166
|
-
tabIndex={-1}
|
|
1167
|
-
class={props.class ?? "solidaria-DatePickerContent"}
|
|
1423
|
+
class={resolvedClass()}
|
|
1168
1424
|
style={mergedStyle()}
|
|
1169
|
-
data-placement={popoverAria.placement() ??
|
|
1425
|
+
data-placement={popoverAria.placement() ?? "bottom"}
|
|
1426
|
+
data-entering={dataAttr(isEntering())}
|
|
1170
1427
|
>
|
|
1171
|
-
{
|
|
1428
|
+
{/* RAC's `Dialog` renders a `<section role="dialog">` by default — the
|
|
1429
|
+
* D5 roving trail records this element's tag, so it must be `section`
|
|
1430
|
+
* (a `<div>` here regressed the focus-trail tag match). */}
|
|
1431
|
+
<section {...context.pickerAria.dialogProps} tabIndex={-1}>
|
|
1432
|
+
{props.children}
|
|
1433
|
+
</section>
|
|
1172
1434
|
</div>
|
|
1173
1435
|
</FocusScope>
|
|
1174
1436
|
</Portal>
|
|
@@ -1179,13 +1441,16 @@ export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
|
|
|
1179
1441
|
export function DateRangePickerContent(props: DateRangePickerContentProps): JSX.Element {
|
|
1180
1442
|
const context = useDateRangePickerContext();
|
|
1181
1443
|
const portalContext = useUNSAFE_PortalContext();
|
|
1182
|
-
let
|
|
1444
|
+
// A signal (not a plain `let`) so the enter-animation effect re-runs when the
|
|
1445
|
+
// surface mounts on open — `createEnterAnimation` reads this to know the
|
|
1446
|
+
// element exists before removing the initial `data-entering` styles.
|
|
1447
|
+
const [contentRef, setContentRef] = createSignal<HTMLElement>();
|
|
1183
1448
|
const portalContainer = () => portalContext.getContainer?.() ?? undefined;
|
|
1184
1449
|
|
|
1185
1450
|
const popoverAria = createPopover(
|
|
1186
1451
|
{
|
|
1187
1452
|
triggerRef: () => context.triggerRef()?.parentElement ?? context.triggerRef(),
|
|
1188
|
-
popoverRef: () => contentRef ?? null,
|
|
1453
|
+
popoverRef: () => contentRef() ?? null,
|
|
1189
1454
|
placement: "bottom start",
|
|
1190
1455
|
offset: 8,
|
|
1191
1456
|
isNonModal: false,
|
|
@@ -1201,6 +1466,26 @@ export function DateRangePickerContent(props: DateRangePickerContentProps): JSX.
|
|
|
1201
1466
|
|
|
1202
1467
|
createEscapeDismissFallback(() => context.overlayState.isOpen, context.overlayState.close);
|
|
1203
1468
|
|
|
1469
|
+
// Mirror S2's `<Popover>` enter transition (RAC `useEnterAnimation`): the
|
|
1470
|
+
// surface mounts carrying `data-entering` (opacity 0 + a small placement-ward
|
|
1471
|
+
// translate), which an effect removes on the next frame so the CSS transition
|
|
1472
|
+
// plays. Only the enter half is certified (D2) — the popover unmounts on close.
|
|
1473
|
+
const isEntering = createEnterAnimation(contentRef, () => context.overlayState.isOpen);
|
|
1474
|
+
|
|
1475
|
+
const renderProps = (): DatePickerContentRenderProps => ({
|
|
1476
|
+
isEntering: isEntering(),
|
|
1477
|
+
isExiting: false,
|
|
1478
|
+
// Seed the base placement so the ENTER frame already carries the
|
|
1479
|
+
// placement-ward `translate` (see the single DatePickerContent note).
|
|
1480
|
+
placement: popoverAria.placement() ?? "bottom",
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1483
|
+
const resolvedClass = (): string => {
|
|
1484
|
+
const c = props.class;
|
|
1485
|
+
if (typeof c === "function") return c(renderProps());
|
|
1486
|
+
return c ?? "solidaria-DateRangePickerContent";
|
|
1487
|
+
};
|
|
1488
|
+
|
|
1204
1489
|
const cleanPopoverProps = () => {
|
|
1205
1490
|
const {
|
|
1206
1491
|
style: _style,
|
|
@@ -1220,28 +1505,31 @@ export function DateRangePickerContent(props: DateRangePickerContentProps): JSX.
|
|
|
1220
1505
|
};
|
|
1221
1506
|
};
|
|
1222
1507
|
|
|
1223
|
-
//
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
if (!open) {
|
|
1227
|
-
requestAnimationFrame(() => context.triggerRef()?.focus());
|
|
1228
|
-
}
|
|
1229
|
-
});
|
|
1508
|
+
// Close-focus restoration is delegated to the overlay's `FocusScope`
|
|
1509
|
+
// (`restoreFocus`) — see the single-value `DatePickerContent` note. The prior
|
|
1510
|
+
// ad-hoc effect fired on mount and painted a phantom focus ring at rest.
|
|
1230
1511
|
|
|
1231
1512
|
return (
|
|
1232
1513
|
<Show when={context.overlayState.isOpen}>
|
|
1233
1514
|
<Portal mount={portalContainer()}>
|
|
1234
1515
|
<FocusScope contain restoreFocus>
|
|
1516
|
+
{/* Un-folded to mirror S2's `<Popover>` DOM (see DatePickerContent).
|
|
1517
|
+
* OUTER role-null `<div>` = positioned surface carrying the enter
|
|
1518
|
+
* motion + chrome (the element RAC/S2 animate; pruned from the AX
|
|
1519
|
+
* tree). Nested `<section role="dialog">` carries the dialog
|
|
1520
|
+
* semantics — the D5 roving trail records this tag, so it must be
|
|
1521
|
+
* `section`, not a folded `div`. */}
|
|
1235
1522
|
<div
|
|
1236
|
-
ref={
|
|
1523
|
+
ref={setContentRef}
|
|
1237
1524
|
{...cleanPopoverProps()}
|
|
1238
|
-
{
|
|
1239
|
-
tabIndex={-1}
|
|
1240
|
-
class={props.class ?? "solidaria-DateRangePickerContent"}
|
|
1525
|
+
class={resolvedClass()}
|
|
1241
1526
|
style={mergedStyle()}
|
|
1242
|
-
data-placement={popoverAria.placement() ??
|
|
1527
|
+
data-placement={popoverAria.placement() ?? "bottom"}
|
|
1528
|
+
data-entering={dataAttr(isEntering())}
|
|
1243
1529
|
>
|
|
1244
|
-
{
|
|
1530
|
+
<section {...context.pickerAria.dialogProps} tabIndex={-1}>
|
|
1531
|
+
{props.children}
|
|
1532
|
+
</section>
|
|
1245
1533
|
</div>
|
|
1246
1534
|
</FocusScope>
|
|
1247
1535
|
</Portal>
|
|
@@ -11,6 +11,8 @@ export interface DateRangePickerFieldContextValue {
|
|
|
11
11
|
aria: {
|
|
12
12
|
labelProps: Record<string, unknown>;
|
|
13
13
|
inputProps: Record<string, unknown>;
|
|
14
|
+
/** Optional hidden validation <input> props (unused by the range fields). */
|
|
15
|
+
hiddenInputProps?: Record<string, unknown>;
|
|
14
16
|
descriptionProps: Record<string, unknown>;
|
|
15
17
|
errorMessageProps: Record<string, unknown>;
|
|
16
18
|
};
|
package/src/Disclosure.tsx
CHANGED
|
@@ -406,6 +406,10 @@ export function DisclosureTrigger(props: DisclosureTriggerProps): JSX.Element {
|
|
|
406
406
|
{...mergeProps(domProps() as Record<string, unknown>, getButtonProps(), cleanFocusProps())}
|
|
407
407
|
ref={(el) => assignRef(local.ref, el)}
|
|
408
408
|
type="button"
|
|
409
|
+
// Mirror react-aria's `useFocusable` (useFocusable.tsx:114-118): always emit
|
|
410
|
+
// an explicit tabIndex so the native trigger is a real, discoverable tab stop
|
|
411
|
+
// (Safari won't focus a native button otherwise). Disabled → attribute absent.
|
|
412
|
+
tabindex={isDisabled() ? undefined : 0}
|
|
409
413
|
class={local.class}
|
|
410
414
|
style={local.style}
|
|
411
415
|
data-expanded={dataAttr(isExpanded())}
|