@proyecto-viviana/solid-stately 0.2.7 → 0.4.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/README.md +30 -283
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -2
- package/dist/autocomplete/index.d.ts +1 -1
- package/dist/calendar/IncompleteDate.d.ts +61 -0
- package/dist/calendar/createCalendarState.d.ts +41 -16
- package/dist/calendar/createDateFieldState.d.ts +39 -9
- package/dist/calendar/createDatePickerState.d.ts +60 -0
- package/dist/calendar/createRangeCalendarState.d.ts +37 -10
- package/dist/calendar/createTimeFieldState.d.ts +34 -7
- package/dist/calendar/index.d.ts +7 -6
- package/dist/checkbox/createCheckboxGroupState.d.ts +6 -6
- package/dist/checkbox/index.d.ts +1 -1
- package/dist/collections/ListCollection.d.ts +1 -1
- package/dist/collections/createListState.d.ts +11 -8
- package/dist/collections/createMenuState.d.ts +7 -7
- package/dist/collections/createSelectionState.d.ts +22 -10
- package/dist/collections/getChildNodes.d.ts +11 -0
- package/dist/collections/index.d.ts +6 -5
- package/dist/collections/types.d.ts +12 -12
- package/dist/color/Color.d.ts +1 -1
- package/dist/color/createColorAreaState.d.ts +4 -2
- package/dist/color/createColorFieldState.d.ts +38 -12
- package/dist/color/createColorSliderState.d.ts +9 -2
- package/dist/color/createColorWheelState.d.ts +13 -2
- package/dist/color/getColorChannels.d.ts +1 -1
- package/dist/color/index.d.ts +6 -6
- package/dist/color/types.d.ts +4 -4
- package/dist/combobox/createComboBoxState.d.ts +34 -13
- package/dist/combobox/index.d.ts +1 -1
- package/dist/data/createAsyncList.d.ts +4 -4
- package/dist/data/createListData.d.ts +2 -2
- package/dist/data/createTreeData.d.ts +6 -2
- package/dist/data/index.d.ts +3 -3
- package/dist/datepicker/index.d.ts +2 -4
- package/dist/disclosure/createDisclosureState.d.ts +2 -2
- package/dist/disclosure/index.d.ts +1 -1
- package/dist/dnd/createDragState.d.ts +2 -2
- package/dist/dnd/createDraggableCollectionState.d.ts +2 -2
- package/dist/dnd/createDropState.d.ts +2 -2
- package/dist/dnd/createDroppableCollectionState.d.ts +3 -3
- package/dist/dnd/index.d.ts +5 -5
- package/dist/dnd/types.d.ts +19 -17
- package/dist/form/createFormValidationState.d.ts +3 -3
- package/dist/form/index.d.ts +1 -1
- package/dist/grid/createGridState.d.ts +2 -2
- package/dist/grid/index.d.ts +2 -2
- package/dist/grid/types.d.ts +27 -11
- package/dist/index.d.ts +31 -30
- package/dist/index.js +9077 -7246
- package/dist/index.js.map +1 -1
- package/dist/menu/index.d.ts +2 -2
- package/dist/numberfield/createNumberFieldState.d.ts +2 -2
- package/dist/numberfield/index.d.ts +1 -1
- package/dist/overlays/createOverlayTriggerState.d.ts +2 -2
- package/dist/overlays/index.d.ts +1 -1
- package/dist/radio/createRadioGroupState.d.ts +7 -7
- package/dist/radio/index.d.ts +1 -1
- package/dist/searchfield/createSearchFieldState.d.ts +2 -2
- package/dist/searchfield/index.d.ts +2 -2
- package/dist/select/createSelectState.d.ts +14 -8
- package/dist/select/index.d.ts +1 -1
- package/dist/selection/Selection.d.ts +16 -0
- package/dist/selection/SelectionManager.d.ts +110 -0
- package/dist/selection/createMultipleSelectionState.d.ts +59 -0
- package/dist/selection/index.d.ts +6 -5
- package/dist/slider/createSliderState.d.ts +3 -3
- package/dist/slider/index.d.ts +2 -2
- package/dist/steplist/createStepListState.d.ts +48 -0
- package/dist/steplist/index.d.ts +1 -0
- package/dist/table/TableCollection.d.ts +33 -3
- package/dist/table/createTableColumnResizeState.d.ts +54 -0
- package/dist/table/createTableState.d.ts +2 -2
- package/dist/table/createTreeGridState.d.ts +15 -0
- package/dist/table/index.d.ts +6 -3
- package/dist/table/types.d.ts +68 -10
- package/dist/tabs/createTabListState.d.ts +5 -5
- package/dist/tabs/index.d.ts +1 -1
- package/dist/textfield/createTextFieldState.d.ts +2 -2
- package/dist/textfield/index.d.ts +1 -1
- package/dist/toast/createToastState.d.ts +24 -2
- package/dist/toast/index.d.ts +1 -1
- package/dist/toggle/createToggleGroupState.d.ts +4 -4
- package/dist/toggle/createToggleState.d.ts +2 -2
- package/dist/toggle/index.d.ts +2 -2
- package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tree/TreeCollection.d.ts +2 -2
- package/dist/tree/createTreeState.d.ts +2 -2
- package/dist/tree/index.d.ts +3 -3
- package/dist/tree/types.d.ts +27 -13
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/reactivity.d.ts +1 -1
- package/package.json +29 -30
- package/src/autocomplete/createAutocompleteState.d.ts +19 -17
- package/src/autocomplete/createAutocompleteState.ts +24 -30
- package/src/autocomplete/index.d.ts +6 -2
- package/src/autocomplete/index.ts +1 -1
- package/src/calendar/IncompleteDate.ts +430 -0
- package/src/calendar/createCalendarState.d.ts +139 -117
- package/src/calendar/createCalendarState.ts +308 -94
- package/src/calendar/createDateFieldState.d.ts +130 -95
- package/src/calendar/createDateFieldState.ts +447 -334
- package/src/calendar/createDatePickerState.ts +347 -0
- package/src/calendar/createRangeCalendarState.d.ts +154 -129
- package/src/calendar/createRangeCalendarState.ts +411 -85
- package/src/calendar/createTimeFieldState.d.ts +81 -79
- package/src/calendar/createTimeFieldState.ts +187 -108
- package/src/calendar/index.d.ts +80 -7
- package/src/calendar/index.ts +23 -12
- package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
- package/src/checkbox/createCheckboxGroupState.ts +20 -29
- package/src/checkbox/index.d.ts +6 -2
- package/src/checkbox/index.ts +1 -1
- package/src/collections/ListCollection.d.ts +25 -22
- package/src/collections/ListCollection.ts +6 -6
- package/src/collections/createListState.d.ts +73 -58
- package/src/collections/createListState.ts +67 -35
- package/src/collections/createMenuState.d.ts +36 -31
- package/src/collections/createMenuState.ts +21 -12
- package/src/collections/createSelectionState.d.ts +78 -65
- package/src/collections/createSelectionState.ts +66 -52
- package/src/collections/getChildNodes.ts +98 -0
- package/src/collections/index.d.ts +38 -6
- package/src/collections/index.ts +14 -10
- package/src/collections/types.d.ts +101 -101
- package/src/collections/types.ts +12 -12
- package/src/color/Color.d.ts +20 -5
- package/src/color/Color.ts +357 -254
- package/src/color/createColorAreaState.d.ts +67 -65
- package/src/color/createColorAreaState.ts +21 -13
- package/src/color/createColorFieldState.d.ts +46 -44
- package/src/color/createColorFieldState.ts +285 -149
- package/src/color/createColorSliderState.d.ts +58 -56
- package/src/color/createColorSliderState.ts +45 -14
- package/src/color/createColorWheelState.d.ts +42 -40
- package/src/color/createColorWheelState.ts +101 -64
- package/src/color/getColorChannels.ts +7 -5
- package/src/color/index.d.ts +36 -7
- package/src/color/index.ts +6 -9
- package/src/color/types.d.ts +68 -63
- package/src/color/types.ts +11 -19
- package/src/combobox/createComboBoxState.d.ts +107 -105
- package/src/combobox/createComboBoxState.ts +184 -89
- package/src/combobox/index.d.ts +10 -2
- package/src/combobox/index.ts +1 -1
- package/src/data/createAsyncList.ts +89 -63
- package/src/data/createListData.ts +67 -67
- package/src/data/createTreeData.ts +304 -91
- package/src/data/index.ts +3 -3
- package/src/datepicker/index.ts +6 -10
- package/src/disclosure/createDisclosureState.d.ts +47 -43
- package/src/disclosure/createDisclosureState.ts +7 -21
- package/src/disclosure/index.d.ts +10 -2
- package/src/disclosure/index.ts +1 -1
- package/src/dnd/createDragState.d.ts +51 -42
- package/src/dnd/createDragState.ts +12 -14
- package/src/dnd/createDraggableCollectionState.d.ts +52 -41
- package/src/dnd/createDraggableCollectionState.ts +14 -21
- package/src/dnd/createDropState.d.ts +64 -45
- package/src/dnd/createDropState.ts +22 -32
- package/src/dnd/createDroppableCollectionState.d.ts +88 -59
- package/src/dnd/createDroppableCollectionState.ts +40 -43
- package/src/dnd/index.d.ts +55 -6
- package/src/dnd/index.ts +6 -18
- package/src/dnd/types.d.ts +170 -157
- package/src/dnd/types.ts +24 -22
- package/src/form/createFormValidationState.d.ts +47 -45
- package/src/form/createFormValidationState.ts +30 -63
- package/src/form/index.d.ts +16 -2
- package/src/form/index.ts +1 -1
- package/src/grid/createGridState.d.ts +7 -4
- package/src/grid/createGridState.ts +78 -51
- package/src/grid/index.d.ts +3 -3
- package/src/grid/index.ts +2 -8
- package/src/grid/types.d.ts +136 -129
- package/src/grid/types.ts +34 -18
- package/src/index.d.ts +311 -26
- package/src/index.ts +59 -89
- package/src/menu/index.ts +2 -4
- package/src/numberfield/createNumberFieldState.d.ts +56 -54
- package/src/numberfield/createNumberFieldState.ts +26 -31
- package/src/numberfield/index.d.ts +6 -2
- package/src/numberfield/index.ts +1 -1
- package/src/overlays/createOverlayTriggerState.d.ts +22 -20
- package/src/overlays/createOverlayTriggerState.ts +3 -4
- package/src/overlays/index.d.ts +6 -2
- package/src/overlays/index.ts +1 -1
- package/src/radio/createRadioGroupState.d.ts +54 -52
- package/src/radio/createRadioGroupState.ts +27 -29
- package/src/radio/index.d.ts +7 -2
- package/src/radio/index.ts +1 -1
- package/src/searchfield/createSearchFieldState.d.ts +16 -14
- package/src/searchfield/createSearchFieldState.ts +5 -11
- package/src/searchfield/index.d.ts +3 -3
- package/src/searchfield/index.ts +2 -5
- package/src/select/createSelectState.d.ts +63 -61
- package/src/select/createSelectState.ts +42 -29
- package/src/select/index.d.ts +2 -2
- package/src/select/index.ts +1 -5
- package/src/selection/Selection.ts +28 -0
- package/src/selection/SelectionManager.ts +526 -0
- package/src/selection/createMultipleSelectionState.ts +247 -0
- package/src/selection/index.ts +12 -7
- package/src/slider/createSliderState.d.ts +60 -60
- package/src/slider/createSliderState.ts +55 -33
- package/src/slider/index.d.ts +3 -3
- package/src/slider/index.ts +2 -6
- package/src/ssr/index.d.ts +1 -1
- package/src/ssr/index.ts +2 -2
- package/src/steplist/createStepListState.ts +191 -0
- package/src/steplist/index.ts +5 -0
- package/src/table/TableCollection.d.ts +42 -40
- package/src/table/TableCollection.ts +266 -99
- package/src/table/createTableColumnResizeState.ts +242 -0
- package/src/table/createTableState.d.ts +7 -4
- package/src/table/createTableState.ts +30 -7
- package/src/table/createTreeGridState.ts +146 -0
- package/src/table/index.d.ts +14 -4
- package/src/table/index.ts +13 -3
- package/src/table/types.d.ts +98 -92
- package/src/table/types.ts +83 -13
- package/src/tabs/createTabListState.d.ts +59 -57
- package/src/tabs/createTabListState.ts +34 -39
- package/src/tabs/index.d.ts +8 -2
- package/src/tabs/index.ts +1 -1
- package/src/textfield/createTextFieldState.d.ts +16 -14
- package/src/textfield/createTextFieldState.ts +7 -18
- package/src/textfield/index.d.ts +6 -2
- package/src/textfield/index.ts +1 -1
- package/src/toast/createToastState.d.ts +92 -86
- package/src/toast/createToastState.ts +90 -52
- package/src/toast/index.d.ts +12 -2
- package/src/toast/index.ts +2 -1
- package/src/toggle/createToggleGroupState.ts +11 -21
- package/src/toggle/createToggleState.d.ts +19 -19
- package/src/toggle/createToggleState.ts +2 -16
- package/src/toggle/index.d.ts +2 -2
- package/src/toggle/index.ts +2 -6
- package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
- package/src/tooltip/createTooltipTriggerState.ts +12 -11
- package/src/tooltip/index.d.ts +7 -2
- package/src/tooltip/index.ts +1 -1
- package/src/tree/TreeCollection.d.ts +26 -23
- package/src/tree/TreeCollection.ts +29 -22
- package/src/tree/createTreeState.d.ts +7 -4
- package/src/tree/createTreeState.ts +70 -41
- package/src/tree/index.d.ts +10 -4
- package/src/tree/index.ts +3 -3
- package/src/tree/types.d.ts +131 -126
- package/src/tree/types.ts +30 -13
- package/src/utils/index.d.ts +2 -2
- package/src/utils/index.ts +1 -1
- package/src/utils/reactivity.d.ts +2 -2
- package/src/utils/reactivity.ts +3 -3
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatePickerState for Solid-Stately
|
|
3
|
+
*
|
|
4
|
+
* Provides unified state management for DatePicker components.
|
|
5
|
+
* Based on @react-stately/datepicker useDatePickerState
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createSignal, createMemo, type Accessor } from "solid-js";
|
|
9
|
+
import {
|
|
10
|
+
type DateValue,
|
|
11
|
+
type CalendarDateTime,
|
|
12
|
+
Time,
|
|
13
|
+
DateFormatter,
|
|
14
|
+
toCalendarDate,
|
|
15
|
+
toCalendarDateTime,
|
|
16
|
+
toZoned,
|
|
17
|
+
getLocalTimeZone,
|
|
18
|
+
} from "@internationalized/date";
|
|
19
|
+
import { access, type MaybeAccessor } from "../utils";
|
|
20
|
+
import type { ValidationState } from "./createCalendarState";
|
|
21
|
+
import type { TimeValue } from "./createTimeFieldState";
|
|
22
|
+
|
|
23
|
+
export type Granularity = "day" | "hour" | "minute" | "second";
|
|
24
|
+
|
|
25
|
+
export type DatePickerStateProps<T extends DateValue = DateValue> = DatePickerStateOptions<T>;
|
|
26
|
+
|
|
27
|
+
export interface DatePickerStateOptions<T extends DateValue = DateValue> {
|
|
28
|
+
value?: MaybeAccessor<T | null>;
|
|
29
|
+
defaultValue?: T | null;
|
|
30
|
+
onChange?: (value: T | null) => void;
|
|
31
|
+
minValue?: MaybeAccessor<DateValue | undefined>;
|
|
32
|
+
maxValue?: MaybeAccessor<DateValue | undefined>;
|
|
33
|
+
isDisabled?: MaybeAccessor<boolean>;
|
|
34
|
+
isReadOnly?: MaybeAccessor<boolean>;
|
|
35
|
+
isRequired?: MaybeAccessor<boolean>;
|
|
36
|
+
granularity?: Granularity;
|
|
37
|
+
hourCycle?: 12 | 24;
|
|
38
|
+
hideTimeZone?: boolean;
|
|
39
|
+
placeholderValue?: DateValue;
|
|
40
|
+
shouldCloseOnSelect?: boolean | (() => boolean);
|
|
41
|
+
defaultOpen?: boolean;
|
|
42
|
+
isOpen?: MaybeAccessor<boolean | undefined>;
|
|
43
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
44
|
+
isDateUnavailable?: (date: DateValue) => boolean;
|
|
45
|
+
validationState?: MaybeAccessor<ValidationState | undefined>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface DatePickerState<T extends DateValue = DateValue> {
|
|
49
|
+
value: Accessor<T | null>;
|
|
50
|
+
defaultValue: T | null;
|
|
51
|
+
setValue: (value: T | null) => void;
|
|
52
|
+
dateValue: Accessor<DateValue | null>;
|
|
53
|
+
setDateValue: (value: DateValue) => void;
|
|
54
|
+
timeValue: Accessor<TimeValue | null>;
|
|
55
|
+
setTimeValue: (value: TimeValue) => void;
|
|
56
|
+
granularity: Granularity;
|
|
57
|
+
hasTime: boolean;
|
|
58
|
+
isOpen: Accessor<boolean>;
|
|
59
|
+
open: () => void;
|
|
60
|
+
close: () => void;
|
|
61
|
+
setOpen: (isOpen: boolean) => void;
|
|
62
|
+
isInvalid: Accessor<boolean>;
|
|
63
|
+
validationState: Accessor<ValidationState | undefined>;
|
|
64
|
+
builtinValidation: Accessor<{ isInvalid: boolean }>;
|
|
65
|
+
showEra: Accessor<boolean>;
|
|
66
|
+
formatValue: (locale: string, fieldOptions?: Intl.DateTimeFormatOptions) => string;
|
|
67
|
+
getDateFormatter: (locale: string, formatOptions?: Intl.DateTimeFormatOptions) => DateFormatter;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Provides unified state management for a DatePicker component.
|
|
72
|
+
*/
|
|
73
|
+
export function createDatePickerState<T extends DateValue = DateValue>(
|
|
74
|
+
props: DatePickerStateOptions<T> = {},
|
|
75
|
+
): DatePickerState<T> {
|
|
76
|
+
const timeZone = getLocalTimeZone();
|
|
77
|
+
|
|
78
|
+
// Internal signals for value and overlay state
|
|
79
|
+
const [internalValue, setInternalValue] = createSignal<T | null>(props.defaultValue ?? null);
|
|
80
|
+
const [internalOpen, setInternalOpen] = createSignal(props.defaultOpen ?? false);
|
|
81
|
+
|
|
82
|
+
// Transient selections for date and time
|
|
83
|
+
const [selectedDate, setSelectedDate] = createSignal<DateValue | null>(null);
|
|
84
|
+
const [selectedTime, setSelectedTime] = createSignal<TimeValue | null>(null);
|
|
85
|
+
|
|
86
|
+
// Controlled vs uncontrolled value
|
|
87
|
+
const value = createMemo<T | null>(() => {
|
|
88
|
+
const controlled = access(props.value);
|
|
89
|
+
return controlled !== undefined ? controlled : internalValue();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Granularity: auto-detect from value type if not provided
|
|
93
|
+
const granularity = createMemo<Granularity>(() => {
|
|
94
|
+
if (props.granularity) return props.granularity;
|
|
95
|
+
const v = value();
|
|
96
|
+
if (v && "hour" in v) {
|
|
97
|
+
if ("second" in v) return "second";
|
|
98
|
+
return "minute";
|
|
99
|
+
}
|
|
100
|
+
return "day";
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const hasTime = createMemo(() => {
|
|
104
|
+
const g = granularity();
|
|
105
|
+
return g === "hour" || g === "minute" || g === "second";
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Derived states
|
|
109
|
+
const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
|
|
110
|
+
const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
|
|
111
|
+
const validationState = createMemo(() => access(props.validationState));
|
|
112
|
+
const isOpen: Accessor<boolean> = () => access(props.isOpen) ?? internalOpen();
|
|
113
|
+
|
|
114
|
+
// Date and time portions of the current value
|
|
115
|
+
const dateValue = createMemo<DateValue | null>(() => {
|
|
116
|
+
const v = value();
|
|
117
|
+
if (!v) return null;
|
|
118
|
+
return toCalendarDate(v);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const timeValue = createMemo<TimeValue | null>(() => {
|
|
122
|
+
const v = value();
|
|
123
|
+
if (!v || !("hour" in v)) return null;
|
|
124
|
+
return v as unknown as TimeValue;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Helper to resolve placeholder time (midnight fallback)
|
|
128
|
+
const getPlaceholderTime = (): TimeValue => {
|
|
129
|
+
const placeholder = props.placeholderValue;
|
|
130
|
+
if (placeholder && "hour" in placeholder) {
|
|
131
|
+
return placeholder as TimeValue;
|
|
132
|
+
}
|
|
133
|
+
return new Time(0, 0, 0);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Commit combined date + time value
|
|
137
|
+
const commitValue = (date: DateValue, time: TimeValue | null) => {
|
|
138
|
+
if (isDisabled() || isReadOnly()) return;
|
|
139
|
+
|
|
140
|
+
let combined: DateValue;
|
|
141
|
+
if (time && hasTime()) {
|
|
142
|
+
combined = toCalendarDateTime(date, time);
|
|
143
|
+
} else {
|
|
144
|
+
combined = date;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Preserve ZonedDateTime timezone if the current committed value has one
|
|
148
|
+
const current = value();
|
|
149
|
+
if (current && "timeZone" in current && !("timeZone" in combined)) {
|
|
150
|
+
combined = toZoned(combined as CalendarDateTime, current.timeZone);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Clear transient states
|
|
154
|
+
setSelectedDate(null);
|
|
155
|
+
setSelectedTime(null);
|
|
156
|
+
|
|
157
|
+
// Update internal state if uncontrolled
|
|
158
|
+
const controlled = access(props.value);
|
|
159
|
+
if (controlled === undefined) {
|
|
160
|
+
setInternalValue(() => combined as T);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (props.onChange) {
|
|
164
|
+
props.onChange(combined as T);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Set the full value directly
|
|
169
|
+
const setValue = (newValue: T | null) => {
|
|
170
|
+
if (isDisabled() || isReadOnly()) return;
|
|
171
|
+
|
|
172
|
+
const controlled = access(props.value);
|
|
173
|
+
if (controlled === undefined) {
|
|
174
|
+
setInternalValue(() => newValue);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (props.onChange) {
|
|
178
|
+
props.onChange(newValue);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Clear transient states
|
|
182
|
+
setSelectedDate(null);
|
|
183
|
+
setSelectedTime(null);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Select a date (may commit if time is already selected or shouldCloseOnSelect)
|
|
187
|
+
const setDateValue = (date: DateValue) => {
|
|
188
|
+
if (isDisabled() || isReadOnly()) return;
|
|
189
|
+
|
|
190
|
+
const currentTime = selectedTime();
|
|
191
|
+
const currentValue = value();
|
|
192
|
+
const shouldClose =
|
|
193
|
+
typeof props.shouldCloseOnSelect === "function"
|
|
194
|
+
? props.shouldCloseOnSelect()
|
|
195
|
+
: (props.shouldCloseOnSelect ?? true);
|
|
196
|
+
|
|
197
|
+
if (hasTime() && (currentTime || shouldClose)) {
|
|
198
|
+
// Preserve existing time from committed value if user hasn't explicitly selected a new time
|
|
199
|
+
const existingTime =
|
|
200
|
+
currentTime ??
|
|
201
|
+
(currentValue && "hour" in currentValue ? (currentValue as unknown as TimeValue) : null);
|
|
202
|
+
const timePart = existingTime ?? getPlaceholderTime();
|
|
203
|
+
commitValue(date, timePart);
|
|
204
|
+
|
|
205
|
+
if (shouldClose) {
|
|
206
|
+
setOpen(false);
|
|
207
|
+
}
|
|
208
|
+
} else if (hasTime()) {
|
|
209
|
+
// Store date transiently, waiting for time selection
|
|
210
|
+
setSelectedDate(date);
|
|
211
|
+
} else {
|
|
212
|
+
// No time needed, commit immediately
|
|
213
|
+
commitValue(date, null);
|
|
214
|
+
|
|
215
|
+
if (shouldClose) {
|
|
216
|
+
setOpen(false);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Select a time (may commit if date is already selected)
|
|
222
|
+
const setTimeValue = (time: TimeValue) => {
|
|
223
|
+
if (isDisabled() || isReadOnly()) return;
|
|
224
|
+
|
|
225
|
+
const currentDate = selectedDate();
|
|
226
|
+
const currentValue = value();
|
|
227
|
+
|
|
228
|
+
if (currentDate) {
|
|
229
|
+
// We have a transient date, commit combined
|
|
230
|
+
commitValue(currentDate, time);
|
|
231
|
+
} else if (currentValue && hasTime()) {
|
|
232
|
+
// We have an existing value with time, update the time portion
|
|
233
|
+
const datePart = toCalendarDate(currentValue);
|
|
234
|
+
commitValue(datePart, time);
|
|
235
|
+
} else {
|
|
236
|
+
// Store time transiently, waiting for date selection
|
|
237
|
+
setSelectedTime(time);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Open / close helpers
|
|
242
|
+
const open = () => setOpen(true);
|
|
243
|
+
const close = () => setOpen(false);
|
|
244
|
+
|
|
245
|
+
const setOpen = (openValue: boolean) => {
|
|
246
|
+
if (openValue) {
|
|
247
|
+
if (access(props.isOpen) === undefined) {
|
|
248
|
+
setInternalOpen(true);
|
|
249
|
+
}
|
|
250
|
+
props.onOpenChange?.(true);
|
|
251
|
+
} else {
|
|
252
|
+
// When closing, only commit if both date and time are available (when time is required).
|
|
253
|
+
// Don't auto-commit partial selections with placeholder time to avoid phantom values.
|
|
254
|
+
const currentValue = value();
|
|
255
|
+
const currentDate = selectedDate();
|
|
256
|
+
const currentTime = selectedTime();
|
|
257
|
+
|
|
258
|
+
if (!currentValue && currentDate && hasTime() && currentTime) {
|
|
259
|
+
// Both date and time explicitly selected — safe to commit
|
|
260
|
+
commitValue(currentDate, currentTime);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Clear any remaining transient state
|
|
264
|
+
setSelectedDate(null);
|
|
265
|
+
setSelectedTime(null);
|
|
266
|
+
if (access(props.isOpen) === undefined) {
|
|
267
|
+
setInternalOpen(false);
|
|
268
|
+
}
|
|
269
|
+
props.onOpenChange?.(false);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Builtin validation against minValue, maxValue, and isDateUnavailable
|
|
274
|
+
const builtinValidation = createMemo(() => {
|
|
275
|
+
const v = value();
|
|
276
|
+
if (!v) return { isInvalid: false };
|
|
277
|
+
|
|
278
|
+
const minValue = access(props.minValue);
|
|
279
|
+
const maxValue = access(props.maxValue);
|
|
280
|
+
|
|
281
|
+
if (minValue && v.compare(minValue) < 0) return { isInvalid: true };
|
|
282
|
+
if (maxValue && v.compare(maxValue) > 0) return { isInvalid: true };
|
|
283
|
+
if (props.isDateUnavailable?.(v)) return { isInvalid: true };
|
|
284
|
+
|
|
285
|
+
return { isInvalid: false };
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const isInvalid = createMemo(() => {
|
|
289
|
+
return builtinValidation().isInvalid || validationState() === "invalid";
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Show era for BC dates
|
|
293
|
+
const showEra = createMemo(() => {
|
|
294
|
+
const v = value();
|
|
295
|
+
return v ? v.year < 1 : false;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Format the current value using DateFormatter with era support for BC dates
|
|
299
|
+
const formatValue = (locale: string, fieldOptions?: Intl.DateTimeFormatOptions): string => {
|
|
300
|
+
const v = value();
|
|
301
|
+
if (!v) return "";
|
|
302
|
+
|
|
303
|
+
const options: Intl.DateTimeFormatOptions = { ...fieldOptions };
|
|
304
|
+
|
|
305
|
+
// Add era for BC dates
|
|
306
|
+
if (v.year < 1) {
|
|
307
|
+
options.era = "short";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const formatter = new DateFormatter(locale, options);
|
|
311
|
+
return formatter.format(v.toDate(timeZone));
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Get a DateFormatter instance
|
|
315
|
+
const getDateFormatter = (
|
|
316
|
+
locale: string,
|
|
317
|
+
formatOptions?: Intl.DateTimeFormatOptions,
|
|
318
|
+
): DateFormatter => {
|
|
319
|
+
return new DateFormatter(locale, formatOptions);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
value,
|
|
324
|
+
defaultValue: props.defaultValue ?? null,
|
|
325
|
+
setValue,
|
|
326
|
+
dateValue,
|
|
327
|
+
setDateValue,
|
|
328
|
+
timeValue,
|
|
329
|
+
setTimeValue,
|
|
330
|
+
get granularity() {
|
|
331
|
+
return granularity();
|
|
332
|
+
},
|
|
333
|
+
get hasTime() {
|
|
334
|
+
return hasTime();
|
|
335
|
+
},
|
|
336
|
+
isOpen,
|
|
337
|
+
open,
|
|
338
|
+
close,
|
|
339
|
+
setOpen,
|
|
340
|
+
isInvalid,
|
|
341
|
+
validationState,
|
|
342
|
+
builtinValidation,
|
|
343
|
+
showEra,
|
|
344
|
+
formatValue,
|
|
345
|
+
getDateFormatter,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
@@ -4,143 +4,168 @@
|
|
|
4
4
|
* Provides state management for range calendar components.
|
|
5
5
|
* Based on @react-stately/calendar useRangeCalendarState
|
|
6
6
|
*/
|
|
7
|
-
import { type Accessor } from
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import { type Accessor } from "solid-js";
|
|
8
|
+
import {
|
|
9
|
+
type Calendar as InternationalizedCalendar,
|
|
10
|
+
type CalendarDate,
|
|
11
|
+
type CalendarIdentifier,
|
|
12
|
+
type DateValue,
|
|
13
|
+
} from "@internationalized/date";
|
|
14
|
+
import { type MaybeAccessor } from "../utils";
|
|
15
|
+
import type { CalendarDayOfWeek, ValidationState } from "./createCalendarState";
|
|
16
|
+
type RangeCalendarSelectionAlignment = "start" | "center" | "end";
|
|
17
|
+
type RangeCalendarPageBehavior = "single" | "visible";
|
|
11
18
|
export interface DateRange {
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
start: CalendarDate;
|
|
20
|
+
end: CalendarDate;
|
|
14
21
|
}
|
|
15
22
|
export interface RangeValue<T> {
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
start: T;
|
|
24
|
+
end: T;
|
|
18
25
|
}
|
|
19
26
|
export interface RangeCalendarStateProps<T extends DateValue = DateValue> {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
/** The current value (controlled). */
|
|
28
|
+
value?: MaybeAccessor<RangeValue<T> | null>;
|
|
29
|
+
/** The default value (uncontrolled). */
|
|
30
|
+
defaultValue?: RangeValue<T> | null;
|
|
31
|
+
/** Handler called when the value changes. */
|
|
32
|
+
onChange?: (value: RangeValue<T> | null) => void;
|
|
33
|
+
/** The minimum allowed date. */
|
|
34
|
+
minValue?: MaybeAccessor<DateValue | undefined>;
|
|
35
|
+
/** The maximum allowed date. */
|
|
36
|
+
maxValue?: MaybeAccessor<DateValue | undefined>;
|
|
37
|
+
/** Whether the calendar is disabled. */
|
|
38
|
+
isDisabled?: MaybeAccessor<boolean>;
|
|
39
|
+
/** Whether the calendar is read-only. */
|
|
40
|
+
isReadOnly?: MaybeAccessor<boolean>;
|
|
41
|
+
/** The date that is focused when the calendar first mounts. */
|
|
42
|
+
focusedValue?: MaybeAccessor<DateValue | null | undefined>;
|
|
43
|
+
/** The default focused date (uncontrolled). */
|
|
44
|
+
defaultFocusedValue?: DateValue;
|
|
45
|
+
/** Handler called when the focused date changes. */
|
|
46
|
+
onFocusChange?: (date: CalendarDate) => void;
|
|
47
|
+
/** The locale to use for formatting. */
|
|
48
|
+
locale?: MaybeAccessor<string | undefined>;
|
|
49
|
+
/** Creates a calendar object for a locale calendar identifier. */
|
|
50
|
+
createCalendar?: (identifier: CalendarIdentifier) => InternationalizedCalendar;
|
|
51
|
+
/** Callback to determine if a date is unavailable. */
|
|
52
|
+
isDateUnavailable?: (date: DateValue) => boolean;
|
|
53
|
+
/** The number of months to display at once. */
|
|
54
|
+
visibleMonths?: MaybeAccessor<number | undefined>;
|
|
55
|
+
/** Controls how the initial visible months are aligned around the current value. */
|
|
56
|
+
selectionAlignment?: MaybeAccessor<RangeCalendarSelectionAlignment | undefined>;
|
|
57
|
+
/** Controls whether pagination moves by one month or the visible month count. */
|
|
58
|
+
pageBehavior?: MaybeAccessor<RangeCalendarPageBehavior | undefined>;
|
|
59
|
+
/** Controls which days are disabled. */
|
|
60
|
+
isDateDisabled?: (date: DateValue) => boolean;
|
|
61
|
+
/** Validation state. */
|
|
62
|
+
validationState?: MaybeAccessor<ValidationState | undefined>;
|
|
63
|
+
/** Whether to allow selecting the same date for start and end. */
|
|
64
|
+
allowsNonContiguousRanges?: boolean;
|
|
65
|
+
/** The first day of the week (0 = Sunday, 1 = Monday, etc.). */
|
|
66
|
+
firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
54
67
|
}
|
|
55
68
|
export interface RangeCalendarState<T extends DateValue = DateValue> {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
69
|
+
/** The currently selected date range. */
|
|
70
|
+
value: Accessor<RangeValue<T> | null>;
|
|
71
|
+
/** Sets the selected date range. */
|
|
72
|
+
setValue: (value: RangeValue<T> | null) => void;
|
|
73
|
+
/** The currently focused date. */
|
|
74
|
+
focusedDate: Accessor<CalendarDate>;
|
|
75
|
+
/** Sets the focused date. */
|
|
76
|
+
setFocusedDate: (date: CalendarDate) => void;
|
|
77
|
+
/** The anchor date when selecting a range (first click). */
|
|
78
|
+
anchorDate: Accessor<CalendarDate | null>;
|
|
79
|
+
/** Sets the anchor date. */
|
|
80
|
+
setAnchorDate: (date: CalendarDate | null) => void;
|
|
81
|
+
/** The highlighted range during selection. */
|
|
82
|
+
highlightedRange: Accessor<RangeValue<CalendarDate> | null>;
|
|
83
|
+
/** Whether the calendar is disabled. */
|
|
84
|
+
isDisabled: Accessor<boolean>;
|
|
85
|
+
/** Whether the calendar is read-only. */
|
|
86
|
+
isReadOnly: Accessor<boolean>;
|
|
87
|
+
/** The visible date range (first and last day of visible month(s)). */
|
|
88
|
+
visibleRange: Accessor<{
|
|
89
|
+
start: CalendarDate;
|
|
90
|
+
end: CalendarDate;
|
|
91
|
+
}>;
|
|
92
|
+
/** The timezone used for date calculations. */
|
|
93
|
+
timeZone: string;
|
|
94
|
+
/** The locale used for formatting and locale-specific week starts. */
|
|
95
|
+
locale: Accessor<string>;
|
|
96
|
+
/** The explicitly requested first weekday, or undefined for the locale default. */
|
|
97
|
+
firstDayOfWeek: Accessor<CalendarDayOfWeek | undefined>;
|
|
98
|
+
/** The validation state. */
|
|
99
|
+
validationState: Accessor<ValidationState | undefined>;
|
|
100
|
+
/** Whether the current value is invalid according to validation state. */
|
|
101
|
+
isValueInvalid: Accessor<boolean>;
|
|
102
|
+
/** Whether a date is within the selected range. */
|
|
103
|
+
isSelected: (date: DateValue) => boolean;
|
|
104
|
+
/** Whether a date is the start of the selection. */
|
|
105
|
+
isSelectionStart: (date: DateValue) => boolean;
|
|
106
|
+
/** Whether a date is the end of the selection. */
|
|
107
|
+
isSelectionEnd: (date: DateValue) => boolean;
|
|
108
|
+
/** Whether a date is focused. */
|
|
109
|
+
isCellFocused: (date: DateValue) => boolean;
|
|
110
|
+
/** Whether a date is unavailable. */
|
|
111
|
+
isCellUnavailable: (date: DateValue) => boolean;
|
|
112
|
+
/** Whether a date is disabled. */
|
|
113
|
+
isCellDisabled: (date: DateValue) => boolean;
|
|
114
|
+
/** Whether a date is outside the visible range. */
|
|
115
|
+
isOutsideVisibleRange: (date: DateValue) => boolean;
|
|
116
|
+
/** Whether a date is invalid. */
|
|
117
|
+
isInvalid: (date: DateValue) => boolean;
|
|
118
|
+
/** Moves focus to the previous page (month). */
|
|
119
|
+
focusPreviousPage: () => void;
|
|
120
|
+
/** Moves focus to the next page (month). */
|
|
121
|
+
focusNextPage: () => void;
|
|
122
|
+
/** Moves focus to the previous section (year). */
|
|
123
|
+
focusPreviousSection: () => void;
|
|
124
|
+
/** Moves focus to the next section (year). */
|
|
125
|
+
focusNextSection: () => void;
|
|
126
|
+
/** Whether the previous visible range would be outside the allowed date range. */
|
|
127
|
+
isPreviousVisibleRangeInvalid: () => boolean;
|
|
128
|
+
/** Whether the next visible range would be outside the allowed date range. */
|
|
129
|
+
isNextVisibleRangeInvalid: () => boolean;
|
|
130
|
+
/** Moves focus to the previous day. */
|
|
131
|
+
focusPreviousDay: () => void;
|
|
132
|
+
/** Moves focus to the next day. */
|
|
133
|
+
focusNextDay: () => void;
|
|
134
|
+
/** Moves focus to the previous week. */
|
|
135
|
+
focusPreviousWeek: () => void;
|
|
136
|
+
/** Moves focus to the next week. */
|
|
137
|
+
focusNextWeek: () => void;
|
|
138
|
+
/** Moves focus to the start of the month. */
|
|
139
|
+
focusPageStart: () => void;
|
|
140
|
+
/** Moves focus to the end of the month. */
|
|
141
|
+
focusPageEnd: () => void;
|
|
142
|
+
/** Selects the focused date. */
|
|
143
|
+
selectFocusedDate: () => void;
|
|
144
|
+
/** Selects a specific date. */
|
|
145
|
+
selectDate: (date: CalendarDate) => void;
|
|
146
|
+
/** Whether focus is currently within the calendar. */
|
|
147
|
+
isFocused: Accessor<boolean>;
|
|
148
|
+
/** Sets whether focus is within the calendar. */
|
|
149
|
+
setFocused: (focused: boolean) => void;
|
|
150
|
+
/** Gets the dates for a week in the visible range. */
|
|
151
|
+
getDatesInWeek: (weekIndex: number, monthStartDate?: CalendarDate) => (CalendarDate | null)[];
|
|
152
|
+
/** Gets the number of weeks in a month. */
|
|
153
|
+
getWeeksInMonth: (date?: CalendarDate) => number;
|
|
154
|
+
/** The week day headers. */
|
|
155
|
+
weekDays: Accessor<string[]>;
|
|
156
|
+
/** The title for the calendar (formatted month and year). */
|
|
157
|
+
title: Accessor<string>;
|
|
158
|
+
/** The number of visible months. */
|
|
159
|
+
visibleMonths: number;
|
|
160
|
+
/** Whether the user is currently selecting a range. */
|
|
161
|
+
isDragging: Accessor<boolean>;
|
|
162
|
+
/** Sets whether the user is dragging to select. */
|
|
163
|
+
setDragging: (dragging: boolean) => void;
|
|
141
164
|
}
|
|
142
165
|
/**
|
|
143
166
|
* Provides state management for a range calendar component.
|
|
144
167
|
*/
|
|
145
|
-
export declare function createRangeCalendarState<T extends DateValue = CalendarDate>(
|
|
146
|
-
|
|
168
|
+
export declare function createRangeCalendarState<T extends DateValue = CalendarDate>(
|
|
169
|
+
props?: RangeCalendarStateProps<T>,
|
|
170
|
+
): RangeCalendarState<T>;
|
|
171
|
+
//# sourceMappingURL=createRangeCalendarState.d.ts.map
|