@proyecto-viviana/solidaria-components 0.2.5 → 0.3.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/LICENSE +21 -0
- package/README.md +39 -272
- package/dist/ActionBar.d.ts +79 -0
- package/dist/ActionBar.d.ts.map +1 -0
- package/dist/ActionGroup.d.ts +74 -0
- package/dist/ActionGroup.d.ts.map +1 -0
- package/dist/Alert.d.ts +70 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Autocomplete.d.ts +5 -5
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Breadcrumbs.d.ts +27 -8
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +28 -5
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +51 -7
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +33 -8
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +130 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +210 -9
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +42 -0
- package/dist/ColorEditor.d.ts.map +1 -0
- package/dist/ComboBox.d.ts +146 -16
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +40 -0
- package/dist/ContextualHelpTrigger.d.ts.map +1 -0
- package/dist/DateField.d.ts +35 -8
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +101 -5
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DateRangePickerContext.d.ts +30 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -0
- package/dist/Dialog.d.ts +5 -5
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +25 -5
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +80 -0
- package/dist/DragAndDrop.d.ts.map +1 -0
- package/dist/DragPreview.d.ts +14 -0
- package/dist/DragPreview.d.ts.map +1 -0
- package/dist/DropZone.d.ts +27 -0
- package/dist/DropZone.d.ts.map +1 -0
- package/dist/FieldError.d.ts +27 -0
- package/dist/FieldError.d.ts.map +1 -0
- package/dist/FileTrigger.d.ts +26 -0
- package/dist/FileTrigger.d.ts.map +1 -0
- package/dist/Focusable.d.ts +27 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Form.d.ts +41 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +69 -10
- package/dist/GridList.d.ts.map +1 -1
- package/dist/HiddenDateInput.d.ts +26 -0
- package/dist/HiddenDateInput.d.ts.map +1 -0
- package/dist/HiddenTimeInput.d.ts +25 -0
- package/dist/HiddenTimeInput.d.ts.map +1 -0
- package/dist/Icon.d.ts +57 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Keyboard.d.ts +13 -0
- package/dist/Keyboard.d.ts.map +1 -0
- package/dist/Landmark.d.ts +3 -3
- package/dist/Landmark.d.ts.map +1 -1
- package/dist/Link.d.ts +10 -4
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +73 -11
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +38 -0
- package/dist/ListDropTargetDelegate.d.ts.map +1 -0
- package/dist/Menu.d.ts +79 -10
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +4 -4
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +6 -4
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +10 -12
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +32 -7
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +27 -0
- package/dist/Pressable.d.ts.map +1 -0
- package/dist/ProgressBar.d.ts +6 -4
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts +43 -9
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +39 -7
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +75 -0
- package/dist/RouterProvider.d.ts.map +1 -0
- package/dist/SearchField.d.ts +23 -21
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +48 -7
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +30 -0
- package/dist/SelectionIndicator.d.ts.map +1 -0
- package/dist/Separator.d.ts +9 -3
- package/dist/Separator.d.ts.map +1 -1
- package/dist/SharedElementTransition.d.ts +41 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +15 -8
- package/dist/Slider.d.ts.map +1 -1
- package/dist/StepList.d.ts +90 -0
- package/dist/StepList.d.ts.map +1 -0
- package/dist/Switch.d.ts +11 -5
- package/dist/Switch.d.ts.map +1 -1
- package/dist/Table.d.ts +222 -19
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +47 -10
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +22 -10
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +10 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/TextField.d.ts +19 -11
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +32 -7
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts +29 -14
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +36 -0
- package/dist/ToggleButton.d.ts.map +1 -0
- package/dist/ToggleButtonGroup.d.ts +33 -0
- package/dist/ToggleButtonGroup.d.ts.map +1 -0
- package/dist/Toolbar.d.ts +7 -3
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +58 -7
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +102 -11
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +61 -0
- package/dist/Virtualizer.d.ts.map +1 -0
- package/dist/VirtualizerLayouts.d.ts +82 -0
- package/dist/VirtualizerLayouts.d.ts.map +1 -0
- package/dist/VisuallyHidden.d.ts +4 -2
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +6 -1
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +73 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23342 -10644
- package/dist/index.js.map +1 -7
- package/dist/index.jsx +18110 -0
- package/dist/index.jsx.map +1 -0
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +79 -0
- package/dist/virtualizer/Layout.d.ts.map +1 -0
- package/package.json +33 -32
- package/src/ActionBar.tsx +251 -0
- package/src/ActionGroup.tsx +277 -0
- package/src/Alert.tsx +152 -0
- package/src/Autocomplete.tsx +39 -44
- package/src/Breadcrumbs.tsx +227 -72
- package/src/Button.tsx +315 -74
- package/src/Calendar.tsx +347 -141
- package/src/Checkbox.tsx +414 -123
- package/src/Collection.tsx +350 -0
- package/src/Color.tsx +1325 -284
- package/src/ColorEditor.tsx +213 -0
- package/src/ComboBox.tsx +644 -245
- package/src/ContextualHelpTrigger.tsx +195 -0
- package/src/DateField.tsx +274 -106
- package/src/DatePicker.tsx +892 -111
- package/src/DateRangePickerContext.tsx +44 -0
- package/src/Dialog.tsx +173 -104
- package/src/Disclosure.tsx +158 -105
- package/src/DragAndDrop.tsx +340 -0
- package/src/DragPreview.tsx +47 -0
- package/src/DropZone.tsx +233 -0
- package/src/FieldError.tsx +89 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +103 -0
- package/src/Form.tsx +140 -0
- package/src/GridList.tsx +542 -128
- package/src/HiddenDateInput.tsx +153 -0
- package/src/HiddenTimeInput.tsx +133 -0
- package/src/Icon.tsx +133 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Landmark.tsx +37 -63
- package/src/Link.tsx +132 -69
- package/src/ListBox.tsx +656 -106
- package/src/ListDropTargetDelegate.ts +283 -0
- package/src/Menu.tsx +1234 -132
- package/src/Meter.tsx +44 -58
- package/src/Modal.tsx +262 -166
- package/src/NumberField.tsx +267 -151
- package/src/Popover.tsx +452 -343
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +54 -59
- package/src/RadioGroup.tsx +533 -121
- package/src/RangeCalendar.tsx +249 -150
- package/src/RouterProvider.tsx +223 -0
- package/src/SearchField.tsx +460 -133
- package/src/Select.tsx +804 -233
- package/src/SelectionIndicator.tsx +108 -0
- package/src/Separator.tsx +47 -49
- package/src/SharedElementTransition.tsx +264 -0
- package/src/Slider.tsx +148 -98
- package/src/StepList.tsx +272 -0
- package/src/Switch.tsx +93 -46
- package/src/Table.tsx +1551 -225
- package/src/Tabs.tsx +377 -123
- package/src/TagGroup.tsx +233 -135
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +413 -86
- package/src/TimeField.tsx +232 -222
- package/src/Toast.tsx +306 -160
- package/src/ToggleButton.tsx +169 -0
- package/src/ToggleButtonGroup.tsx +141 -0
- package/src/Toolbar.tsx +61 -70
- package/src/Tooltip.tsx +473 -116
- package/src/Tree.tsx +1514 -175
- package/src/Virtualizer.tsx +730 -0
- package/src/VirtualizerLayouts.ts +280 -0
- package/src/VisuallyHidden.tsx +32 -38
- package/src/contexts.ts +29 -36
- package/src/index.ts +972 -620
- package/src/useDragAndDrop.ts +367 -0
- package/src/utils.tsx +69 -50
- package/src/virtualizer/Layout.ts +192 -0
- package/dist/index.ssr.js +0 -9785
- package/dist/index.ssr.js.map +0 -7
package/src/DatePicker.tsx
CHANGED
|
@@ -8,26 +8,42 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
10
|
createContext,
|
|
11
|
+
createEffect,
|
|
11
12
|
createMemo,
|
|
12
13
|
createSignal,
|
|
14
|
+
onCleanup,
|
|
13
15
|
splitProps,
|
|
14
16
|
useContext,
|
|
15
17
|
Show,
|
|
16
|
-
} from
|
|
18
|
+
} from "solid-js";
|
|
19
|
+
import { Portal } from "solid-js/web";
|
|
17
20
|
import {
|
|
18
21
|
createDatePicker,
|
|
22
|
+
createDateRangePicker,
|
|
23
|
+
createPopover,
|
|
24
|
+
FocusScope,
|
|
25
|
+
useUNSAFE_PortalContext,
|
|
19
26
|
type AriaDatePickerProps,
|
|
27
|
+
type AriaDateRangePickerProps,
|
|
20
28
|
type DatePickerState as AriaDatePickerState,
|
|
21
|
-
} from
|
|
29
|
+
} from "@proyecto-viviana/solidaria";
|
|
22
30
|
import {
|
|
23
31
|
createDateFieldState,
|
|
24
32
|
createCalendarState,
|
|
33
|
+
createRangeCalendarState,
|
|
34
|
+
createDatePickerState,
|
|
35
|
+
access,
|
|
25
36
|
type DateFieldState,
|
|
37
|
+
type DatePickerState,
|
|
38
|
+
type CalendarStateProps,
|
|
26
39
|
type CalendarState,
|
|
40
|
+
type RangeCalendarState,
|
|
27
41
|
type DateFieldStateProps,
|
|
28
42
|
type CalendarDate,
|
|
29
43
|
type DateValue,
|
|
30
|
-
|
|
44
|
+
type RangeCalendarStateProps,
|
|
45
|
+
type RangeValue,
|
|
46
|
+
} from "@proyecto-viviana/solid-stately";
|
|
31
47
|
import {
|
|
32
48
|
type RenderChildren,
|
|
33
49
|
type ClassNameOrFunction,
|
|
@@ -36,12 +52,18 @@ import {
|
|
|
36
52
|
useRenderProps,
|
|
37
53
|
dataAttr,
|
|
38
54
|
useIsHydrated,
|
|
39
|
-
} from
|
|
40
|
-
import { DateFieldContext } from
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
} from "./utils";
|
|
56
|
+
import { DateFieldContext } from "./DateField";
|
|
57
|
+
import { CalendarContext } from "./Calendar";
|
|
58
|
+
import { RangeCalendarContext } from "./RangeCalendar";
|
|
59
|
+
import { HiddenDateInput } from "./HiddenDateInput";
|
|
60
|
+
import { FormContext, type FormProps } from "./Form";
|
|
61
|
+
import {
|
|
62
|
+
DateRangePickerContext,
|
|
63
|
+
useDateRangePickerContext,
|
|
64
|
+
type DateRangePickerContextValue,
|
|
65
|
+
type DateRangePickerFieldContextValue,
|
|
66
|
+
} from "./DateRangePickerContext";
|
|
45
67
|
|
|
46
68
|
export interface DatePickerRenderProps {
|
|
47
69
|
/** Whether the picker is disabled. */
|
|
@@ -56,8 +78,13 @@ export interface DatePickerRenderProps {
|
|
|
56
78
|
isOpen: boolean;
|
|
57
79
|
}
|
|
58
80
|
|
|
81
|
+
export interface DateRangePickerRenderProps extends Omit<DatePickerRenderProps, "isInvalid"> {
|
|
82
|
+
isInvalid: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
59
85
|
export interface DatePickerContextValue {
|
|
60
86
|
fieldState: DateFieldState<DateValue>;
|
|
87
|
+
datePickerState: DatePickerState<DateValue>;
|
|
61
88
|
calendarState: CalendarState<DateValue>;
|
|
62
89
|
overlayState: {
|
|
63
90
|
isOpen: boolean;
|
|
@@ -65,23 +92,79 @@ export interface DatePickerContextValue {
|
|
|
65
92
|
close: () => void;
|
|
66
93
|
toggle: () => void;
|
|
67
94
|
};
|
|
95
|
+
triggerRef: () => HTMLElement | null;
|
|
96
|
+
setTriggerRef: (element: HTMLElement | null) => void;
|
|
68
97
|
pickerAria: ReturnType<typeof createDatePicker>;
|
|
69
98
|
}
|
|
70
99
|
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
100
|
+
export type DatePickerProps<T extends DateValue = DateValue> = Omit<
|
|
101
|
+
AriaDatePickerProps,
|
|
102
|
+
"id" | "isDisabled" | "isReadOnly" | "isRequired" | "minValue" | "maxValue"
|
|
103
|
+
> &
|
|
104
|
+
Omit<DateFieldStateProps<T>, "locale"> &
|
|
105
|
+
SlotProps & {
|
|
106
|
+
/** The children of the component. */
|
|
107
|
+
children?: JSX.Element;
|
|
108
|
+
/** The CSS className for the element. */
|
|
109
|
+
class?: ClassNameOrFunction<DatePickerRenderProps>;
|
|
110
|
+
/** The inline style for the element. */
|
|
111
|
+
style?: StyleOrFunction<DatePickerRenderProps>;
|
|
112
|
+
/** The locale to use for formatting. */
|
|
113
|
+
locale?: string;
|
|
114
|
+
/** Whether the calendar should close when a date is selected. */
|
|
115
|
+
shouldCloseOnSelect?: boolean;
|
|
116
|
+
/** Whether the overlay is open by default (uncontrolled). */
|
|
117
|
+
defaultOpen?: boolean;
|
|
118
|
+
/** Whether the overlay is open (controlled). */
|
|
119
|
+
isOpen?: boolean;
|
|
120
|
+
/** Callback when the overlay open state changes. */
|
|
121
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
122
|
+
/** The name for the hidden date input used in HTML form submission. */
|
|
123
|
+
name?: string;
|
|
124
|
+
/** The associated form id for the hidden date input. */
|
|
125
|
+
form?: string;
|
|
126
|
+
/** The number of months to display in the calendar popover. */
|
|
127
|
+
visibleMonths?: number;
|
|
128
|
+
/** Controls whether calendar paging advances by one month or by the visible month range. */
|
|
129
|
+
pageBehavior?: CalendarStateProps<T>["pageBehavior"];
|
|
130
|
+
/** Determines how visible months align around the initial focused date. */
|
|
131
|
+
selectionAlignment?: CalendarStateProps<T>["selectionAlignment"];
|
|
132
|
+
/** A function that determines whether a date is disabled. */
|
|
133
|
+
isDateDisabled?: (date: DateValue) => boolean;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export interface DateRangePickerProps<T extends DateValue = DateValue>
|
|
137
|
+
extends
|
|
138
|
+
Omit<AriaDateRangePickerProps, "id" | "isDisabled" | "isReadOnly">,
|
|
139
|
+
Omit<RangeCalendarStateProps<T>, "locale">,
|
|
74
140
|
SlotProps {
|
|
75
|
-
/** The children of the component. */
|
|
76
141
|
children?: JSX.Element;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
/** The inline style for the element. */
|
|
80
|
-
style?: StyleOrFunction<DatePickerRenderProps>;
|
|
81
|
-
/** The locale to use for formatting. */
|
|
142
|
+
class?: ClassNameOrFunction<DateRangePickerRenderProps>;
|
|
143
|
+
style?: StyleOrFunction<DateRangePickerRenderProps>;
|
|
82
144
|
locale?: string;
|
|
83
|
-
/** Whether the calendar should close when a date is selected. */
|
|
84
145
|
shouldCloseOnSelect?: boolean;
|
|
146
|
+
/** Whether the overlay is open by default (uncontrolled). */
|
|
147
|
+
defaultOpen?: boolean;
|
|
148
|
+
/** Whether the overlay is open (controlled). */
|
|
149
|
+
isOpen?: boolean;
|
|
150
|
+
/** Callback when the overlay open state changes. */
|
|
151
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
152
|
+
/** The granularity of the date/time fields. */
|
|
153
|
+
granularity?: "day" | "hour" | "minute" | "second";
|
|
154
|
+
/** Whether to show the hour in 12 or 24 hour format. */
|
|
155
|
+
hourCycle?: 12 | 24;
|
|
156
|
+
/** Whether to hide the time zone in date/time fields. */
|
|
157
|
+
hideTimeZone?: boolean;
|
|
158
|
+
/** The placeholder date used to determine segment structure. */
|
|
159
|
+
placeholderValue?: DateValue;
|
|
160
|
+
/** The name for the start date input used in HTML form submission. */
|
|
161
|
+
startName?: string;
|
|
162
|
+
/** The name for the end date input used in HTML form submission. */
|
|
163
|
+
endName?: string;
|
|
164
|
+
/** The associated form id for the hidden start/end date inputs. */
|
|
165
|
+
form?: string;
|
|
166
|
+
/** Controls whether native or ARIA validation should be used. */
|
|
167
|
+
validationBehavior?: "native" | "aria";
|
|
85
168
|
}
|
|
86
169
|
|
|
87
170
|
export interface DatePickerButtonRenderProps {
|
|
@@ -102,24 +185,74 @@ export interface DatePickerButtonProps extends SlotProps {
|
|
|
102
185
|
isDisabled?: boolean;
|
|
103
186
|
}
|
|
104
187
|
|
|
105
|
-
|
|
106
|
-
// CONTEXT
|
|
107
|
-
// ============================================
|
|
188
|
+
export interface DateRangePickerButtonProps extends DatePickerButtonProps {}
|
|
108
189
|
|
|
109
190
|
export const DatePickerContext = createContext<DatePickerContextValue | null>(null);
|
|
191
|
+
export const DatePickerStateContext = createContext<DateFieldState<DateValue> | null>(null);
|
|
192
|
+
export const DateRangePickerStateContext = createContext<RangeCalendarState<DateValue> | null>(
|
|
193
|
+
null,
|
|
194
|
+
);
|
|
195
|
+
export { DateRangePickerContext, useDateRangePickerContext } from "./DateRangePickerContext";
|
|
196
|
+
export type {
|
|
197
|
+
DateRangePickerContextValue,
|
|
198
|
+
DateRangePickerFieldContextValue,
|
|
199
|
+
} from "./DateRangePickerContext";
|
|
200
|
+
|
|
201
|
+
function withFormValidationBehavior<P extends object>(props: P, formContext: FormProps | null): P {
|
|
202
|
+
if (!formContext?.validationBehavior) {
|
|
203
|
+
return props;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return new Proxy(props, {
|
|
207
|
+
get(target, property, receiver) {
|
|
208
|
+
const localValue = Reflect.get(target, property, receiver);
|
|
209
|
+
if (property === "validationBehavior" && localValue === undefined) {
|
|
210
|
+
return formContext.validationBehavior;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return localValue;
|
|
214
|
+
},
|
|
215
|
+
has(target, property) {
|
|
216
|
+
return (
|
|
217
|
+
Reflect.has(target, property) ||
|
|
218
|
+
(property === "validationBehavior" && formContext.validationBehavior !== undefined)
|
|
219
|
+
);
|
|
220
|
+
},
|
|
221
|
+
ownKeys(target) {
|
|
222
|
+
const keys = new Set(Reflect.ownKeys(target));
|
|
223
|
+
if (formContext.validationBehavior !== undefined) {
|
|
224
|
+
keys.add("validationBehavior");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return Array.from(keys);
|
|
228
|
+
},
|
|
229
|
+
getOwnPropertyDescriptor(target, property) {
|
|
230
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
|
|
231
|
+
if (descriptor) {
|
|
232
|
+
return descriptor;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (property === "validationBehavior" && formContext.validationBehavior !== undefined) {
|
|
236
|
+
return {
|
|
237
|
+
enumerable: true,
|
|
238
|
+
configurable: true,
|
|
239
|
+
get: () => formContext.validationBehavior,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return undefined;
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
110
247
|
|
|
111
248
|
export function useDatePickerContext(): DatePickerContextValue {
|
|
112
249
|
const context = useContext(DatePickerContext);
|
|
113
250
|
if (!context) {
|
|
114
|
-
throw new Error(
|
|
251
|
+
throw new Error("DatePicker components must be used within a DatePicker");
|
|
115
252
|
}
|
|
116
253
|
return context;
|
|
117
254
|
}
|
|
118
255
|
|
|
119
|
-
// ============================================
|
|
120
|
-
// DATE PICKER COMPONENT
|
|
121
|
-
// ============================================
|
|
122
|
-
|
|
123
256
|
/**
|
|
124
257
|
* A date picker combines a DateField and a Calendar popover.
|
|
125
258
|
*
|
|
@@ -146,146 +279,524 @@ export function useDatePickerContext(): DatePickerContextValue {
|
|
|
146
279
|
* ```
|
|
147
280
|
*/
|
|
148
281
|
export function DatePicker<T extends DateValue = CalendarDate>(
|
|
149
|
-
props: DatePickerProps<T
|
|
282
|
+
props: DatePickerProps<T>,
|
|
150
283
|
): JSX.Element {
|
|
151
284
|
// Use hydration-safe pattern for client-only rendering
|
|
152
285
|
const isHydrated = useIsHydrated();
|
|
286
|
+
const formContext = useContext(FormContext);
|
|
153
287
|
|
|
154
288
|
return (
|
|
155
289
|
<Show
|
|
156
290
|
when={isHydrated()}
|
|
157
|
-
fallback={
|
|
291
|
+
fallback={
|
|
292
|
+
<div class="solidaria-DatePicker solidaria-DatePicker--placeholder" aria-hidden="true" />
|
|
293
|
+
}
|
|
158
294
|
>
|
|
159
|
-
<DatePickerInner {...props} />
|
|
295
|
+
<DatePickerInner {...props} __formContext={formContext} />
|
|
160
296
|
</Show>
|
|
161
297
|
);
|
|
162
298
|
}
|
|
163
299
|
|
|
300
|
+
type DatePickerInnerProps<T extends DateValue = DateValue> = DatePickerProps<T> & {
|
|
301
|
+
__formContext?: FormProps | null;
|
|
302
|
+
};
|
|
303
|
+
|
|
164
304
|
/**
|
|
165
305
|
* Internal DatePicker component that renders after client mount.
|
|
166
306
|
*/
|
|
167
307
|
function DatePickerInner<T extends DateValue = CalendarDate>(
|
|
168
|
-
props:
|
|
308
|
+
props: DatePickerInnerProps<T>,
|
|
169
309
|
): JSX.Element {
|
|
310
|
+
const formContext = props.__formContext ?? useContext(FormContext);
|
|
311
|
+
const mergedProps = withFormValidationBehavior(props, formContext);
|
|
170
312
|
const [local, stateProps, rest] = splitProps(
|
|
171
|
-
|
|
172
|
-
[
|
|
313
|
+
mergedProps,
|
|
314
|
+
["children", "class", "style", "slot", "shouldCloseOnSelect", "__formContext"],
|
|
173
315
|
[
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
316
|
+
"value",
|
|
317
|
+
"defaultValue",
|
|
318
|
+
"onChange",
|
|
319
|
+
"isOpen",
|
|
320
|
+
"defaultOpen",
|
|
321
|
+
"onOpenChange",
|
|
322
|
+
"minValue",
|
|
323
|
+
"maxValue",
|
|
324
|
+
"isInvalid",
|
|
325
|
+
"isDisabled",
|
|
326
|
+
"isReadOnly",
|
|
327
|
+
"isRequired",
|
|
328
|
+
"locale",
|
|
329
|
+
"granularity",
|
|
330
|
+
"hourCycle",
|
|
331
|
+
"hideTimeZone",
|
|
332
|
+
"placeholderValue",
|
|
333
|
+
"shouldForceLeadingZeros",
|
|
334
|
+
"createCalendar",
|
|
335
|
+
"validationState",
|
|
336
|
+
"validationBehavior",
|
|
337
|
+
"validate",
|
|
338
|
+
"description",
|
|
339
|
+
"errorMessage",
|
|
340
|
+
"isDateUnavailable",
|
|
341
|
+
"firstDayOfWeek",
|
|
342
|
+
"visibleMonths",
|
|
343
|
+
"pageBehavior",
|
|
344
|
+
"selectionAlignment",
|
|
345
|
+
"isDateDisabled",
|
|
346
|
+
],
|
|
191
347
|
);
|
|
192
348
|
|
|
193
|
-
|
|
194
|
-
const [
|
|
349
|
+
const [triggerRef, setTriggerRef] = createSignal<HTMLElement | null>(null);
|
|
350
|
+
const [fieldRef, setFieldRef] = createSignal<HTMLDivElement | null>(null);
|
|
351
|
+
|
|
352
|
+
// Unified state using createDatePickerState as single source of truth
|
|
353
|
+
const datePickerState = createDatePickerState<T>({
|
|
354
|
+
...(stateProps as unknown as import("@proyecto-viviana/solid-stately").DatePickerStateOptions<T>),
|
|
355
|
+
shouldCloseOnSelect: local.shouldCloseOnSelect,
|
|
356
|
+
});
|
|
195
357
|
|
|
196
358
|
const overlayState = {
|
|
197
|
-
get isOpen() {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
359
|
+
get isOpen() {
|
|
360
|
+
return datePickerState.isOpen();
|
|
361
|
+
},
|
|
362
|
+
open: datePickerState.open,
|
|
363
|
+
close: datePickerState.close,
|
|
364
|
+
toggle: () => datePickerState.setOpen(!datePickerState.isOpen()),
|
|
201
365
|
};
|
|
202
366
|
|
|
203
|
-
// Create
|
|
204
|
-
const fieldState = createDateFieldState({
|
|
367
|
+
// Create field state synced through datePickerState
|
|
368
|
+
const fieldState = createDateFieldState<T>({
|
|
205
369
|
...stateProps,
|
|
370
|
+
value: () => datePickerState.value(),
|
|
206
371
|
onChange: (value) => {
|
|
207
|
-
|
|
208
|
-
if (local.shouldCloseOnSelect !== false && value) {
|
|
209
|
-
overlayState.close();
|
|
210
|
-
}
|
|
372
|
+
datePickerState.setValue(value);
|
|
211
373
|
},
|
|
212
374
|
});
|
|
213
375
|
|
|
214
|
-
// Create calendar state
|
|
215
|
-
const calendarState = createCalendarState({
|
|
216
|
-
value: () =>
|
|
376
|
+
// Create calendar state synced through datePickerState
|
|
377
|
+
const calendarState = createCalendarState<T>({
|
|
378
|
+
value: () => datePickerState.value(),
|
|
217
379
|
onChange: (value) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
overlayState.close();
|
|
380
|
+
if (!value) {
|
|
381
|
+
return;
|
|
221
382
|
}
|
|
383
|
+
datePickerState.setDateValue(value);
|
|
222
384
|
},
|
|
223
385
|
minValue: stateProps.minValue,
|
|
224
386
|
maxValue: stateProps.maxValue,
|
|
225
387
|
isDisabled: stateProps.isDisabled,
|
|
226
388
|
isReadOnly: stateProps.isReadOnly,
|
|
227
389
|
locale: stateProps.locale,
|
|
390
|
+
createCalendar: stateProps.createCalendar as CalendarStateProps<T>["createCalendar"],
|
|
391
|
+
isDateUnavailable: stateProps.isDateUnavailable,
|
|
392
|
+
firstDayOfWeek: stateProps.firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined,
|
|
393
|
+
visibleMonths: stateProps.visibleMonths,
|
|
394
|
+
pageBehavior: stateProps.pageBehavior,
|
|
395
|
+
selectionAlignment: stateProps.selectionAlignment,
|
|
396
|
+
isDateDisabled: stateProps.isDateDisabled,
|
|
228
397
|
});
|
|
229
398
|
|
|
230
399
|
// Create date picker ARIA props
|
|
231
400
|
const pickerAria = createDatePicker(
|
|
232
|
-
|
|
401
|
+
() => ({
|
|
402
|
+
...(rest as Record<string, unknown>),
|
|
403
|
+
description: stateProps.description,
|
|
404
|
+
errorMessage: stateProps.errorMessage,
|
|
405
|
+
}),
|
|
233
406
|
fieldState as unknown as DateFieldState<DateValue>,
|
|
234
407
|
overlayState as AriaDatePickerState,
|
|
235
|
-
calendarState as unknown as CalendarState<DateValue
|
|
408
|
+
calendarState as unknown as CalendarState<DateValue>,
|
|
236
409
|
);
|
|
237
410
|
|
|
238
|
-
// Context value
|
|
239
411
|
const contextValue: DatePickerContextValue = {
|
|
240
412
|
fieldState: fieldState as unknown as DateFieldState<DateValue>,
|
|
413
|
+
datePickerState: datePickerState as unknown as DatePickerState<DateValue>,
|
|
241
414
|
calendarState: calendarState as unknown as CalendarState<DateValue>,
|
|
242
415
|
overlayState,
|
|
416
|
+
triggerRef,
|
|
417
|
+
setTriggerRef: (element) => {
|
|
418
|
+
if (!element) return;
|
|
419
|
+
const current = triggerRef();
|
|
420
|
+
if (!current || !current.isConnected) {
|
|
421
|
+
setTriggerRef(() => element);
|
|
422
|
+
}
|
|
423
|
+
},
|
|
243
424
|
pickerAria,
|
|
244
425
|
};
|
|
245
426
|
|
|
246
|
-
|
|
427
|
+
const isInvalid = createMemo(
|
|
428
|
+
() =>
|
|
429
|
+
fieldState.isInvalid() ||
|
|
430
|
+
datePickerState.builtinValidation().isInvalid ||
|
|
431
|
+
Boolean(stateProps.isInvalid),
|
|
432
|
+
);
|
|
433
|
+
|
|
247
434
|
const renderValues = createMemo<DatePickerRenderProps>(() => ({
|
|
248
435
|
isDisabled: fieldState.isDisabled(),
|
|
249
436
|
isReadOnly: fieldState.isReadOnly(),
|
|
250
437
|
isRequired: fieldState.isRequired(),
|
|
251
|
-
isInvalid:
|
|
438
|
+
isInvalid: isInvalid(),
|
|
252
439
|
isOpen: overlayState.isOpen,
|
|
253
440
|
}));
|
|
254
441
|
|
|
255
|
-
// Resolve render props
|
|
256
442
|
const renderProps = useRenderProps(
|
|
257
443
|
{
|
|
258
444
|
class: local.class,
|
|
259
445
|
style: local.style,
|
|
260
|
-
defaultClassName:
|
|
446
|
+
defaultClassName: "solidaria-DatePicker",
|
|
261
447
|
},
|
|
262
|
-
renderValues
|
|
448
|
+
renderValues,
|
|
263
449
|
);
|
|
264
450
|
|
|
451
|
+
const validationBehavior = () =>
|
|
452
|
+
(stateProps as { validationBehavior?: "aria" | "native" }).validationBehavior ??
|
|
453
|
+
formContext?.validationBehavior ??
|
|
454
|
+
"native";
|
|
455
|
+
|
|
265
456
|
return (
|
|
266
|
-
<
|
|
267
|
-
{
|
|
268
|
-
|
|
269
|
-
<
|
|
270
|
-
{
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
457
|
+
<DatePickerStateContext.Provider value={fieldState as unknown as DateFieldState<DateValue>}>
|
|
458
|
+
<DatePickerContext.Provider value={contextValue}>
|
|
459
|
+
{/* Also provide DateFieldContext so DateInput/DateSegment work inside DatePicker */}
|
|
460
|
+
<DateFieldContext.Provider
|
|
461
|
+
value={{
|
|
462
|
+
state: fieldState as unknown as DateFieldState<DateValue>,
|
|
463
|
+
aria: {
|
|
464
|
+
labelProps: pickerAria.labelProps,
|
|
465
|
+
inputProps: pickerAria.fieldProps,
|
|
466
|
+
descriptionProps: pickerAria.descriptionProps,
|
|
467
|
+
errorMessageProps: pickerAria.errorMessageProps,
|
|
468
|
+
},
|
|
469
|
+
}}
|
|
278
470
|
>
|
|
279
|
-
{
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
471
|
+
<CalendarContext.Provider value={calendarState as unknown as CalendarState<DateValue>}>
|
|
472
|
+
<div
|
|
473
|
+
ref={setFieldRef}
|
|
474
|
+
{...pickerAria.groupProps}
|
|
475
|
+
class={renderProps.class()}
|
|
476
|
+
style={renderProps.style()}
|
|
477
|
+
data-disabled={dataAttr(fieldState.isDisabled())}
|
|
478
|
+
data-readonly={dataAttr(fieldState.isReadOnly())}
|
|
479
|
+
data-required={dataAttr(fieldState.isRequired())}
|
|
480
|
+
data-invalid={dataAttr(isInvalid())}
|
|
481
|
+
data-open={dataAttr(overlayState.isOpen)}
|
|
482
|
+
>
|
|
483
|
+
{props.children}
|
|
484
|
+
</div>
|
|
485
|
+
<Show when={(rest as Record<string, unknown>).name}>
|
|
486
|
+
<HiddenDateInput
|
|
487
|
+
name={(rest as Record<string, unknown>).name as string | undefined}
|
|
488
|
+
form={(rest as Record<string, unknown>).form as string | undefined}
|
|
489
|
+
value={() => datePickerState.value()}
|
|
490
|
+
autoComplete={(rest as Record<string, unknown>).autoComplete as string | undefined}
|
|
491
|
+
isDisabled={fieldState.isDisabled()}
|
|
492
|
+
isRequired={fieldState.isRequired()}
|
|
493
|
+
validationBehavior={validationBehavior()}
|
|
494
|
+
validationState={fieldState}
|
|
495
|
+
focus={() => {
|
|
496
|
+
fieldRef()?.querySelector<HTMLElement>('[role="spinbutton"]')?.focus();
|
|
497
|
+
}}
|
|
498
|
+
minValue={() => access(stateProps.minValue) as DateValue | undefined}
|
|
499
|
+
maxValue={() => access(stateProps.maxValue) as DateValue | undefined}
|
|
500
|
+
granularity={datePickerState.granularity}
|
|
501
|
+
/>
|
|
502
|
+
</Show>
|
|
503
|
+
</CalendarContext.Provider>
|
|
504
|
+
</DateFieldContext.Provider>
|
|
505
|
+
</DatePickerContext.Provider>
|
|
506
|
+
</DatePickerStateContext.Provider>
|
|
283
507
|
);
|
|
284
508
|
}
|
|
285
509
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
510
|
+
export function DateRangePicker<T extends DateValue = CalendarDate>(
|
|
511
|
+
props: DateRangePickerProps<T>,
|
|
512
|
+
): JSX.Element {
|
|
513
|
+
const isHydrated = useIsHydrated();
|
|
514
|
+
return (
|
|
515
|
+
<Show
|
|
516
|
+
when={isHydrated()}
|
|
517
|
+
fallback={
|
|
518
|
+
<div
|
|
519
|
+
class="solidaria-DateRangePicker solidaria-DateRangePicker--placeholder"
|
|
520
|
+
aria-hidden="true"
|
|
521
|
+
/>
|
|
522
|
+
}
|
|
523
|
+
>
|
|
524
|
+
<DateRangePickerInner {...props} />
|
|
525
|
+
</Show>
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function DateRangePickerInner<T extends DateValue = CalendarDate>(
|
|
530
|
+
props: DateRangePickerProps<T>,
|
|
531
|
+
): JSX.Element {
|
|
532
|
+
const [local, overlayProps, stateProps, rest] = splitProps(
|
|
533
|
+
props,
|
|
534
|
+
["children", "class", "style", "slot", "shouldCloseOnSelect"],
|
|
535
|
+
["defaultOpen", "isOpen", "onOpenChange"],
|
|
536
|
+
[
|
|
537
|
+
"value",
|
|
538
|
+
"defaultValue",
|
|
539
|
+
"onChange",
|
|
540
|
+
"minValue",
|
|
541
|
+
"maxValue",
|
|
542
|
+
"isDisabled",
|
|
543
|
+
"isReadOnly",
|
|
544
|
+
"focusedValue",
|
|
545
|
+
"defaultFocusedValue",
|
|
546
|
+
"onFocusChange",
|
|
547
|
+
"locale",
|
|
548
|
+
"granularity",
|
|
549
|
+
"hourCycle",
|
|
550
|
+
"hideTimeZone",
|
|
551
|
+
"placeholderValue",
|
|
552
|
+
"createCalendar",
|
|
553
|
+
"isDateUnavailable",
|
|
554
|
+
"visibleMonths",
|
|
555
|
+
"isDateDisabled",
|
|
556
|
+
"validationState",
|
|
557
|
+
"allowsNonContiguousRanges",
|
|
558
|
+
"firstDayOfWeek",
|
|
559
|
+
"pageBehavior",
|
|
560
|
+
"selectionAlignment",
|
|
561
|
+
],
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
const [internalOpen, setInternalOpen] = createSignal(overlayProps.defaultOpen ?? false);
|
|
565
|
+
const isOpen = () => access(overlayProps.isOpen) ?? internalOpen();
|
|
566
|
+
const setOpen = (open: boolean) => {
|
|
567
|
+
if (access(overlayProps.isOpen) === undefined) {
|
|
568
|
+
setInternalOpen(open);
|
|
569
|
+
}
|
|
570
|
+
overlayProps.onOpenChange?.(open);
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
let triggerRef: HTMLElement | null = null;
|
|
574
|
+
const overlayState = {
|
|
575
|
+
get isOpen() {
|
|
576
|
+
return isOpen();
|
|
577
|
+
},
|
|
578
|
+
open: () => setOpen(true),
|
|
579
|
+
close: () => setOpen(false),
|
|
580
|
+
toggle: () => setOpen(!isOpen()),
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const [internalRangeValue, setInternalRangeValue] = createSignal<RangeValue<T> | null>(
|
|
584
|
+
stateProps.defaultValue ?? null,
|
|
585
|
+
);
|
|
586
|
+
const currentRangeValue = createMemo<RangeValue<T> | null>(() => {
|
|
587
|
+
const controlled = access(stateProps.value);
|
|
588
|
+
return controlled !== undefined ? controlled : internalRangeValue();
|
|
589
|
+
});
|
|
590
|
+
const setCommittedRangeValue = (value: RangeValue<T> | null) => {
|
|
591
|
+
if (access(stateProps.value) === undefined) {
|
|
592
|
+
setInternalRangeValue(() => value);
|
|
593
|
+
}
|
|
594
|
+
stateProps.onChange?.(value);
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
const calendarState = createRangeCalendarState({
|
|
598
|
+
...stateProps,
|
|
599
|
+
value: currentRangeValue,
|
|
600
|
+
onChange: (value) => {
|
|
601
|
+
setCommittedRangeValue(value);
|
|
602
|
+
if (local.shouldCloseOnSelect !== false && value?.start && value?.end) {
|
|
603
|
+
setOpen(false);
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const isInvalid = createMemo(
|
|
609
|
+
() =>
|
|
610
|
+
Boolean((rest as { isInvalid?: boolean }).isInvalid) ||
|
|
611
|
+
calendarState.validationState() === "invalid",
|
|
612
|
+
);
|
|
613
|
+
const isRequired = createMemo(() => Boolean((rest as { isRequired?: boolean }).isRequired));
|
|
614
|
+
const [startFieldValue, setStartFieldValue] = createSignal<T | null>(
|
|
615
|
+
currentRangeValue()?.start ?? null,
|
|
616
|
+
);
|
|
617
|
+
const [endFieldValue, setEndFieldValue] = createSignal<T | null>(
|
|
618
|
+
currentRangeValue()?.end ?? null,
|
|
619
|
+
);
|
|
620
|
+
const rangeGranularity = createMemo<"day" | "hour" | "minute" | "second">(() => {
|
|
621
|
+
if (stateProps.granularity) {
|
|
622
|
+
return stateProps.granularity;
|
|
623
|
+
}
|
|
624
|
+
const value = currentRangeValue()?.start ?? currentRangeValue()?.end;
|
|
625
|
+
if (value && "hour" in value) {
|
|
626
|
+
return "second" in value ? "second" : "minute";
|
|
627
|
+
}
|
|
628
|
+
return "day";
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
createEffect(() => {
|
|
632
|
+
const value = currentRangeValue();
|
|
633
|
+
setStartFieldValue(() => value?.start ?? null);
|
|
634
|
+
setEndFieldValue(() => value?.end ?? null);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
const setRangeFieldValue = (part: "start" | "end", nextValue: T | null) => {
|
|
638
|
+
if (part === "start") {
|
|
639
|
+
setStartFieldValue(() => nextValue);
|
|
640
|
+
} else {
|
|
641
|
+
setEndFieldValue(() => nextValue);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const nextStart = part === "start" ? nextValue : startFieldValue();
|
|
645
|
+
const nextEnd = part === "end" ? nextValue : endFieldValue();
|
|
646
|
+
|
|
647
|
+
setCommittedRangeValue(
|
|
648
|
+
nextStart && nextEnd ? ({ start: nextStart, end: nextEnd } as RangeValue<T>) : null,
|
|
649
|
+
);
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
const rangeFieldStateProps = {
|
|
653
|
+
minValue: stateProps.minValue,
|
|
654
|
+
maxValue: stateProps.maxValue,
|
|
655
|
+
isDisabled: stateProps.isDisabled,
|
|
656
|
+
isReadOnly: stateProps.isReadOnly,
|
|
657
|
+
isRequired,
|
|
658
|
+
locale: access(stateProps.locale),
|
|
659
|
+
granularity: rangeGranularity(),
|
|
660
|
+
hourCycle: stateProps.hourCycle,
|
|
661
|
+
hideTimeZone: stateProps.hideTimeZone,
|
|
662
|
+
placeholderValue: stateProps.placeholderValue,
|
|
663
|
+
validationState: () => (isInvalid() ? "invalid" : access(stateProps.validationState)),
|
|
664
|
+
isDateUnavailable: stateProps.isDateUnavailable,
|
|
665
|
+
} satisfies Partial<DateFieldStateProps<T>>;
|
|
666
|
+
|
|
667
|
+
const startFieldState = createDateFieldState<T>({
|
|
668
|
+
...rangeFieldStateProps,
|
|
669
|
+
value: startFieldValue,
|
|
670
|
+
onChange: (value) => setRangeFieldValue("start", value),
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
const endFieldState = createDateFieldState<T>({
|
|
674
|
+
...rangeFieldStateProps,
|
|
675
|
+
value: endFieldValue,
|
|
676
|
+
onChange: (value) => setRangeFieldValue("end", value),
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
const pickerAria = createDateRangePicker(
|
|
680
|
+
() => ({
|
|
681
|
+
...(rest as Record<string, unknown>),
|
|
682
|
+
description: (props as { description?: string }).description,
|
|
683
|
+
errorMessage: (props as { errorMessage?: string }).errorMessage,
|
|
684
|
+
}),
|
|
685
|
+
calendarState as unknown as RangeCalendarState<DateValue>,
|
|
686
|
+
overlayState as AriaDatePickerState,
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
const startFieldContext: DateRangePickerFieldContextValue = {
|
|
690
|
+
state: startFieldState as unknown as DateFieldState<DateValue>,
|
|
691
|
+
aria: {
|
|
692
|
+
labelProps: {},
|
|
693
|
+
get inputProps() {
|
|
694
|
+
return pickerAria.startInputProps;
|
|
695
|
+
},
|
|
696
|
+
get descriptionProps() {
|
|
697
|
+
return pickerAria.descriptionProps;
|
|
698
|
+
},
|
|
699
|
+
get errorMessageProps() {
|
|
700
|
+
return pickerAria.errorMessageProps;
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const endFieldContext: DateRangePickerFieldContextValue = {
|
|
706
|
+
state: endFieldState as unknown as DateFieldState<DateValue>,
|
|
707
|
+
aria: {
|
|
708
|
+
labelProps: {},
|
|
709
|
+
get inputProps() {
|
|
710
|
+
return pickerAria.endInputProps;
|
|
711
|
+
},
|
|
712
|
+
get descriptionProps() {
|
|
713
|
+
return pickerAria.descriptionProps;
|
|
714
|
+
},
|
|
715
|
+
get errorMessageProps() {
|
|
716
|
+
return pickerAria.errorMessageProps;
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
const contextValue: DateRangePickerContextValue = {
|
|
722
|
+
calendarState: calendarState as unknown as RangeCalendarState<DateValue>,
|
|
723
|
+
startFieldState: startFieldState as unknown as DateFieldState<DateValue>,
|
|
724
|
+
endFieldState: endFieldState as unknown as DateFieldState<DateValue>,
|
|
725
|
+
startFieldContext,
|
|
726
|
+
endFieldContext,
|
|
727
|
+
overlayState,
|
|
728
|
+
triggerRef: () => triggerRef,
|
|
729
|
+
setTriggerRef: (element) => {
|
|
730
|
+
if (!element) return;
|
|
731
|
+
if (!triggerRef || !triggerRef.isConnected) triggerRef = element;
|
|
732
|
+
},
|
|
733
|
+
pickerAria,
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const renderValues = createMemo<DateRangePickerRenderProps>(() => ({
|
|
737
|
+
isDisabled: calendarState.isDisabled(),
|
|
738
|
+
isReadOnly: calendarState.isReadOnly(),
|
|
739
|
+
isRequired: isRequired(),
|
|
740
|
+
isInvalid: isInvalid(),
|
|
741
|
+
isOpen: overlayState.isOpen,
|
|
742
|
+
}));
|
|
743
|
+
|
|
744
|
+
const renderProps = useRenderProps(
|
|
745
|
+
{
|
|
746
|
+
class: local.class,
|
|
747
|
+
style: local.style,
|
|
748
|
+
defaultClassName: "solidaria-DateRangePicker",
|
|
749
|
+
},
|
|
750
|
+
renderValues,
|
|
751
|
+
);
|
|
752
|
+
|
|
753
|
+
return (
|
|
754
|
+
<DateRangePickerStateContext.Provider
|
|
755
|
+
value={calendarState as unknown as RangeCalendarState<DateValue>}
|
|
756
|
+
>
|
|
757
|
+
<DateRangePickerContext.Provider value={contextValue}>
|
|
758
|
+
<RangeCalendarContext.Provider
|
|
759
|
+
value={calendarState as unknown as RangeCalendarState<DateValue>}
|
|
760
|
+
>
|
|
761
|
+
<div
|
|
762
|
+
{...pickerAria.groupProps}
|
|
763
|
+
class={renderProps.class()}
|
|
764
|
+
style={renderProps.style()}
|
|
765
|
+
data-disabled={dataAttr(calendarState.isDisabled())}
|
|
766
|
+
data-readonly={dataAttr(calendarState.isReadOnly())}
|
|
767
|
+
data-required={dataAttr(isRequired())}
|
|
768
|
+
data-invalid={dataAttr(isInvalid())}
|
|
769
|
+
data-open={dataAttr(overlayState.isOpen)}
|
|
770
|
+
>
|
|
771
|
+
{props.children}
|
|
772
|
+
</div>
|
|
773
|
+
<Show when={(rest as Record<string, unknown>).startName}>
|
|
774
|
+
<HiddenDateInput
|
|
775
|
+
name={(rest as Record<string, unknown>).startName as string | undefined}
|
|
776
|
+
form={(rest as Record<string, unknown>).form as string | undefined}
|
|
777
|
+
value={() => currentRangeValue()?.start ?? null}
|
|
778
|
+
isDisabled={access(stateProps.isDisabled) ?? false}
|
|
779
|
+
minValue={() => access(stateProps.minValue) as DateValue | undefined}
|
|
780
|
+
maxValue={() => access(stateProps.maxValue) as DateValue | undefined}
|
|
781
|
+
granularity={rangeGranularity()}
|
|
782
|
+
/>
|
|
783
|
+
</Show>
|
|
784
|
+
<Show when={(rest as Record<string, unknown>).endName}>
|
|
785
|
+
<HiddenDateInput
|
|
786
|
+
name={(rest as Record<string, unknown>).endName as string | undefined}
|
|
787
|
+
form={(rest as Record<string, unknown>).form as string | undefined}
|
|
788
|
+
value={() => currentRangeValue()?.end ?? null}
|
|
789
|
+
isDisabled={access(stateProps.isDisabled) ?? false}
|
|
790
|
+
minValue={() => access(stateProps.minValue) as DateValue | undefined}
|
|
791
|
+
maxValue={() => access(stateProps.maxValue) as DateValue | undefined}
|
|
792
|
+
granularity={rangeGranularity()}
|
|
793
|
+
/>
|
|
794
|
+
</Show>
|
|
795
|
+
</RangeCalendarContext.Provider>
|
|
796
|
+
</DateRangePickerContext.Provider>
|
|
797
|
+
</DateRangePickerStateContext.Provider>
|
|
798
|
+
);
|
|
799
|
+
}
|
|
289
800
|
|
|
290
801
|
/**
|
|
291
802
|
* A button that opens the date picker calendar.
|
|
@@ -293,33 +804,34 @@ function DatePickerInner<T extends DateValue = CalendarDate>(
|
|
|
293
804
|
export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
|
|
294
805
|
const context = useDatePickerContext();
|
|
295
806
|
|
|
296
|
-
// Render props values
|
|
297
807
|
const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
|
|
298
808
|
isDisabled: context.fieldState.isDisabled() || (props.isDisabled ?? false),
|
|
299
809
|
isOpen: context.overlayState.isOpen,
|
|
300
810
|
}));
|
|
301
811
|
|
|
302
|
-
// Resolve render props
|
|
303
812
|
const renderProps = useRenderProps(
|
|
304
813
|
{
|
|
305
814
|
children: props.children,
|
|
306
815
|
class: props.class,
|
|
307
816
|
style: props.style,
|
|
308
|
-
defaultClassName:
|
|
817
|
+
defaultClassName: "solidaria-DatePickerButton",
|
|
309
818
|
},
|
|
310
|
-
renderValues
|
|
819
|
+
renderValues,
|
|
311
820
|
);
|
|
312
821
|
|
|
313
822
|
// Determine children content - avoid Show for SSR hydration compatibility
|
|
314
823
|
const getChildren = () => {
|
|
315
|
-
if (typeof props.children ===
|
|
824
|
+
if (typeof props.children === "function") {
|
|
316
825
|
return renderProps.renderChildren();
|
|
317
826
|
}
|
|
318
|
-
return props.children ??
|
|
827
|
+
return props.children ?? "📅";
|
|
319
828
|
};
|
|
320
829
|
|
|
321
830
|
return (
|
|
322
831
|
<button
|
|
832
|
+
ref={(el) => {
|
|
833
|
+
context.setTriggerRef(el);
|
|
834
|
+
}}
|
|
323
835
|
{...context.pickerAria.buttonProps}
|
|
324
836
|
class={renderProps.class()}
|
|
325
837
|
style={renderProps.style()}
|
|
@@ -332,9 +844,45 @@ export function DatePickerButton(props: DatePickerButtonProps): JSX.Element {
|
|
|
332
844
|
);
|
|
333
845
|
}
|
|
334
846
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
847
|
+
export function DateRangePickerButton(props: DateRangePickerButtonProps): JSX.Element {
|
|
848
|
+
const context = useDateRangePickerContext();
|
|
849
|
+
|
|
850
|
+
const renderValues = createMemo<DatePickerButtonRenderProps>(() => ({
|
|
851
|
+
isDisabled: context.calendarState.isDisabled() || (props.isDisabled ?? false),
|
|
852
|
+
isOpen: context.overlayState.isOpen,
|
|
853
|
+
}));
|
|
854
|
+
|
|
855
|
+
const renderProps = useRenderProps(
|
|
856
|
+
{
|
|
857
|
+
children: props.children,
|
|
858
|
+
class: props.class,
|
|
859
|
+
style: props.style,
|
|
860
|
+
defaultClassName: "solidaria-DateRangePickerButton",
|
|
861
|
+
},
|
|
862
|
+
renderValues,
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
const getChildren = () => {
|
|
866
|
+
if (typeof props.children === "function") {
|
|
867
|
+
return renderProps.renderChildren();
|
|
868
|
+
}
|
|
869
|
+
return props.children ?? "📅";
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
return (
|
|
873
|
+
<button
|
|
874
|
+
ref={(el) => context.setTriggerRef(el)}
|
|
875
|
+
{...context.pickerAria.buttonProps}
|
|
876
|
+
class={renderProps.class()}
|
|
877
|
+
style={renderProps.style()}
|
|
878
|
+
disabled={context.calendarState.isDisabled() || props.isDisabled}
|
|
879
|
+
data-disabled={dataAttr(context.calendarState.isDisabled() || props.isDisabled)}
|
|
880
|
+
data-open={dataAttr(context.overlayState.isOpen)}
|
|
881
|
+
>
|
|
882
|
+
{getChildren()}
|
|
883
|
+
</button>
|
|
884
|
+
);
|
|
885
|
+
}
|
|
338
886
|
|
|
339
887
|
export interface DatePickerContentProps extends SlotProps {
|
|
340
888
|
/** The children of the component. */
|
|
@@ -345,23 +893,256 @@ export interface DatePickerContentProps extends SlotProps {
|
|
|
345
893
|
style?: JSX.CSSProperties;
|
|
346
894
|
}
|
|
347
895
|
|
|
896
|
+
export interface DateRangePickerContentProps extends DatePickerContentProps {}
|
|
897
|
+
|
|
898
|
+
export interface DatePickerLabelProps {
|
|
899
|
+
children?: JSX.Element;
|
|
900
|
+
class?: string;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
export function DatePickerLabel(props: DatePickerLabelProps): JSX.Element {
|
|
904
|
+
const context = useDatePickerContext();
|
|
905
|
+
return (
|
|
906
|
+
<span {...context.pickerAria.labelProps} class={props.class}>
|
|
907
|
+
{props.children}
|
|
908
|
+
</span>
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
export interface DatePickerDescriptionProps {
|
|
913
|
+
children?: JSX.Element;
|
|
914
|
+
class?: string;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
export function DatePickerDescription(props: DatePickerDescriptionProps): JSX.Element {
|
|
918
|
+
const context = useDatePickerContext();
|
|
919
|
+
return (
|
|
920
|
+
<p {...context.pickerAria.descriptionProps} class={props.class}>
|
|
921
|
+
{props.children}
|
|
922
|
+
</p>
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
export interface DatePickerErrorMessageProps {
|
|
927
|
+
children?: JSX.Element;
|
|
928
|
+
class?: string;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
export function DatePickerErrorMessage(props: DatePickerErrorMessageProps): JSX.Element {
|
|
932
|
+
const context = useDatePickerContext();
|
|
933
|
+
return (
|
|
934
|
+
<p {...context.pickerAria.errorMessageProps} class={props.class}>
|
|
935
|
+
{props.children}
|
|
936
|
+
</p>
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
export interface DateRangePickerLabelProps {
|
|
941
|
+
children?: JSX.Element;
|
|
942
|
+
class?: string;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
export function DateRangePickerLabel(props: DateRangePickerLabelProps): JSX.Element {
|
|
946
|
+
const context = useDateRangePickerContext();
|
|
947
|
+
return (
|
|
948
|
+
<span {...context.pickerAria.labelProps} class={props.class}>
|
|
949
|
+
{props.children}
|
|
950
|
+
</span>
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
export interface DateRangePickerDescriptionProps {
|
|
955
|
+
children?: JSX.Element;
|
|
956
|
+
class?: string;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
export function DateRangePickerDescription(props: DateRangePickerDescriptionProps): JSX.Element {
|
|
960
|
+
const context = useDateRangePickerContext();
|
|
961
|
+
return (
|
|
962
|
+
<p {...context.pickerAria.descriptionProps} class={props.class}>
|
|
963
|
+
{props.children}
|
|
964
|
+
</p>
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export interface DateRangePickerErrorMessageProps {
|
|
969
|
+
children?: JSX.Element;
|
|
970
|
+
class?: string;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
export function DateRangePickerErrorMessage(props: DateRangePickerErrorMessageProps): JSX.Element {
|
|
974
|
+
const context = useDateRangePickerContext();
|
|
975
|
+
return (
|
|
976
|
+
<p {...context.pickerAria.errorMessageProps} class={props.class}>
|
|
977
|
+
{props.children}
|
|
978
|
+
</p>
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function createEscapeDismissFallback(isOpen: () => boolean, close: () => void): void {
|
|
983
|
+
createEffect(() => {
|
|
984
|
+
if (!isOpen() || typeof document === "undefined") return;
|
|
985
|
+
|
|
986
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
987
|
+
if (event.key !== "Escape" || event.defaultPrevented || event.isComposing) return;
|
|
988
|
+
close();
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
document.addEventListener("keydown", onKeyDown);
|
|
992
|
+
onCleanup(() => document.removeEventListener("keydown", onKeyDown));
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
|
|
348
996
|
/**
|
|
349
997
|
* The content area of the date picker (typically contains a Calendar).
|
|
350
998
|
*/
|
|
351
999
|
export function DatePickerContent(props: DatePickerContentProps): JSX.Element {
|
|
352
1000
|
const context = useDatePickerContext();
|
|
1001
|
+
const portalContext = useUNSAFE_PortalContext();
|
|
1002
|
+
let contentRef: HTMLDivElement | undefined;
|
|
1003
|
+
const portalContainer = () => portalContext.getContainer?.() ?? undefined;
|
|
1004
|
+
|
|
1005
|
+
const popoverAria = createPopover(
|
|
1006
|
+
{
|
|
1007
|
+
triggerRef: () => context.triggerRef()?.parentElement ?? context.triggerRef(),
|
|
1008
|
+
popoverRef: () => contentRef ?? null,
|
|
1009
|
+
placement: "bottom start",
|
|
1010
|
+
offset: 8,
|
|
1011
|
+
isNonModal: false,
|
|
1012
|
+
isKeyboardDismissDisabled: false,
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
isOpen: () => context.overlayState.isOpen,
|
|
1016
|
+
open: context.overlayState.open,
|
|
1017
|
+
close: context.overlayState.close,
|
|
1018
|
+
toggle: context.overlayState.toggle,
|
|
1019
|
+
},
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
createEscapeDismissFallback(() => context.overlayState.isOpen, context.overlayState.close);
|
|
1023
|
+
|
|
1024
|
+
const cleanPopoverProps = () => {
|
|
1025
|
+
const {
|
|
1026
|
+
style: _style,
|
|
1027
|
+
ref: _ref,
|
|
1028
|
+
...rest
|
|
1029
|
+
} = popoverAria.popoverProps as Record<string, unknown>;
|
|
1030
|
+
return rest;
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
const mergedStyle = (): JSX.CSSProperties => {
|
|
1034
|
+
const popoverStyle = (popoverAria.popoverProps as Record<string, unknown>).style as
|
|
1035
|
+
| JSX.CSSProperties
|
|
1036
|
+
| undefined;
|
|
1037
|
+
return {
|
|
1038
|
+
...popoverStyle,
|
|
1039
|
+
...props.style,
|
|
1040
|
+
};
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
// Return focus to trigger when overlay closes
|
|
1044
|
+
createEffect(() => {
|
|
1045
|
+
const open = context.overlayState.isOpen;
|
|
1046
|
+
if (!open) {
|
|
1047
|
+
requestAnimationFrame(() => context.triggerRef()?.focus());
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
353
1050
|
|
|
354
1051
|
return (
|
|
355
1052
|
<Show when={context.overlayState.isOpen}>
|
|
356
|
-
<
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
1053
|
+
<Portal mount={portalContainer()}>
|
|
1054
|
+
<FocusScope contain restoreFocus>
|
|
1055
|
+
<div
|
|
1056
|
+
ref={contentRef}
|
|
1057
|
+
{...cleanPopoverProps()}
|
|
1058
|
+
{...context.pickerAria.dialogProps}
|
|
1059
|
+
tabIndex={-1}
|
|
1060
|
+
class={props.class ?? "solidaria-DatePickerContent"}
|
|
1061
|
+
style={mergedStyle()}
|
|
1062
|
+
data-placement={popoverAria.placement() ?? undefined}
|
|
1063
|
+
>
|
|
1064
|
+
{props.children}
|
|
1065
|
+
</div>
|
|
1066
|
+
</FocusScope>
|
|
1067
|
+
</Portal>
|
|
363
1068
|
</Show>
|
|
364
1069
|
);
|
|
365
1070
|
}
|
|
366
1071
|
|
|
1072
|
+
export function DateRangePickerContent(props: DateRangePickerContentProps): JSX.Element {
|
|
1073
|
+
const context = useDateRangePickerContext();
|
|
1074
|
+
const portalContext = useUNSAFE_PortalContext();
|
|
1075
|
+
let contentRef: HTMLDivElement | undefined;
|
|
1076
|
+
const portalContainer = () => portalContext.getContainer?.() ?? undefined;
|
|
1077
|
+
|
|
1078
|
+
const popoverAria = createPopover(
|
|
1079
|
+
{
|
|
1080
|
+
triggerRef: () => context.triggerRef()?.parentElement ?? context.triggerRef(),
|
|
1081
|
+
popoverRef: () => contentRef ?? null,
|
|
1082
|
+
placement: "bottom start",
|
|
1083
|
+
offset: 8,
|
|
1084
|
+
isNonModal: false,
|
|
1085
|
+
isKeyboardDismissDisabled: false,
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
isOpen: () => context.overlayState.isOpen,
|
|
1089
|
+
open: context.overlayState.open,
|
|
1090
|
+
close: context.overlayState.close,
|
|
1091
|
+
toggle: context.overlayState.toggle,
|
|
1092
|
+
},
|
|
1093
|
+
);
|
|
1094
|
+
|
|
1095
|
+
createEscapeDismissFallback(() => context.overlayState.isOpen, context.overlayState.close);
|
|
1096
|
+
|
|
1097
|
+
const cleanPopoverProps = () => {
|
|
1098
|
+
const {
|
|
1099
|
+
style: _style,
|
|
1100
|
+
ref: _ref,
|
|
1101
|
+
...rest
|
|
1102
|
+
} = popoverAria.popoverProps as Record<string, unknown>;
|
|
1103
|
+
return rest;
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
const mergedStyle = (): JSX.CSSProperties => {
|
|
1107
|
+
const popoverStyle = (popoverAria.popoverProps as Record<string, unknown>).style as
|
|
1108
|
+
| JSX.CSSProperties
|
|
1109
|
+
| undefined;
|
|
1110
|
+
return {
|
|
1111
|
+
...popoverStyle,
|
|
1112
|
+
...props.style,
|
|
1113
|
+
};
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
// Return focus to trigger when overlay closes
|
|
1117
|
+
createEffect(() => {
|
|
1118
|
+
const open = context.overlayState.isOpen;
|
|
1119
|
+
if (!open) {
|
|
1120
|
+
requestAnimationFrame(() => context.triggerRef()?.focus());
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
return (
|
|
1125
|
+
<Show when={context.overlayState.isOpen}>
|
|
1126
|
+
<Portal mount={portalContainer()}>
|
|
1127
|
+
<FocusScope contain restoreFocus>
|
|
1128
|
+
<div
|
|
1129
|
+
ref={contentRef}
|
|
1130
|
+
{...cleanPopoverProps()}
|
|
1131
|
+
{...context.pickerAria.dialogProps}
|
|
1132
|
+
tabIndex={-1}
|
|
1133
|
+
class={props.class ?? "solidaria-DateRangePickerContent"}
|
|
1134
|
+
style={mergedStyle()}
|
|
1135
|
+
data-placement={popoverAria.placement() ?? undefined}
|
|
1136
|
+
>
|
|
1137
|
+
{props.children}
|
|
1138
|
+
</div>
|
|
1139
|
+
</FocusScope>
|
|
1140
|
+
</Portal>
|
|
1141
|
+
</Show>
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
export { HiddenDateInput } from "./HiddenDateInput";
|
|
1146
|
+
export type { HiddenDateInputProps } from "./HiddenDateInput";
|
|
1147
|
+
|
|
367
1148
|
// DatePickerContextValue is already exported at declaration
|