@proyecto-viviana/solid-stately 0.2.4 → 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 +30 -283
- package/dist/autocomplete/createAutocompleteState.d.ts +3 -2
- package/dist/autocomplete/index.d.ts +1 -1
- package/dist/calendar/createCalendarState.d.ts +22 -7
- package/dist/calendar/createDateFieldState.d.ts +30 -7
- package/dist/calendar/createDatePickerState.d.ts +60 -0
- package/dist/calendar/createRangeCalendarState.d.ts +27 -8
- package/dist/calendar/createTimeFieldState.d.ts +34 -7
- package/dist/calendar/index.d.ts +7 -6
- package/dist/checkbox/createCheckboxGroupState.d.ts +12 -3
- package/dist/checkbox/index.d.ts +1 -1
- package/dist/collections/ListCollection.d.ts +1 -1
- package/dist/collections/createListState.d.ts +8 -8
- package/dist/collections/createMenuState.d.ts +7 -7
- package/dist/collections/createSelectionState.d.ts +5 -5
- package/dist/collections/index.d.ts +5 -5
- package/dist/collections/types.d.ts +23 -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 +20 -0
- package/dist/color/index.d.ts +6 -6
- package/dist/color/types.d.ts +4 -4
- package/dist/combobox/createComboBoxState.d.ts +27 -13
- package/dist/combobox/index.d.ts +1 -1
- package/dist/data/createAsyncList.d.ts +111 -0
- package/dist/data/createListData.d.ts +65 -0
- package/dist/data/createTreeData.d.ts +65 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/datepicker/index.d.ts +8 -0
- 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 +16 -16
- 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 +17 -11
- package/dist/index.d.ts +31 -25
- package/dist/index.js +7990 -6264
- package/dist/index.js.map +1 -7
- package/dist/menu/index.d.ts +8 -0
- 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 +13 -4
- 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 +20 -3
- package/dist/select/index.d.ts +1 -1
- package/dist/selection/index.d.ts +11 -0
- 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 +3 -3
- package/dist/table/createTableColumnResizeState.d.ts +54 -0
- package/dist/table/createTableState.d.ts +2 -2
- package/dist/table/index.d.ts +5 -3
- package/dist/table/types.d.ts +10 -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 +15 -3
- package/dist/toast/index.d.ts +1 -1
- package/dist/toggle/createToggleGroupState.d.ts +45 -0
- package/dist/toggle/createToggleState.d.ts +2 -2
- package/dist/toggle/index.d.ts +2 -1
- package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tree/TreeCollection.d.ts +5 -4
- package/dist/tree/createTreeState.d.ts +2 -2
- package/dist/tree/index.d.ts +3 -3
- package/dist/tree/types.d.ts +19 -13
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/reactivity.d.ts +1 -1
- package/package.json +30 -30
- package/src/autocomplete/createAutocompleteState.d.ts +19 -17
- package/src/autocomplete/createAutocompleteState.ts +25 -32
- package/src/autocomplete/index.d.ts +6 -2
- package/src/autocomplete/index.ts +1 -1
- package/src/calendar/createCalendarState.d.ts +139 -117
- package/src/calendar/createCalendarState.ts +221 -74
- package/src/calendar/createDateFieldState.d.ts +130 -95
- package/src/calendar/createDateFieldState.ts +286 -151
- package/src/calendar/createDatePickerState.ts +347 -0
- package/src/calendar/createRangeCalendarState.d.ts +154 -129
- package/src/calendar/createRangeCalendarState.ts +293 -79
- 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 +21 -12
- package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
- package/src/checkbox/createCheckboxGroupState.ts +57 -30
- 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 +152 -146
- package/src/collections/createListState.d.ts +73 -58
- package/src/collections/createListState.ts +267 -264
- package/src/collections/createMenuState.d.ts +36 -31
- package/src/collections/createMenuState.ts +112 -106
- package/src/collections/createSelectionState.d.ts +78 -65
- package/src/collections/createSelectionState.ts +340 -336
- package/src/collections/index.d.ts +38 -6
- package/src/collections/index.ts +41 -46
- package/src/collections/types.d.ts +101 -101
- package/src/collections/types.ts +181 -169
- package/src/color/Color.d.ts +20 -5
- package/src/color/Color.ts +1054 -951
- package/src/color/createColorAreaState.d.ts +67 -65
- package/src/color/createColorAreaState.ts +301 -293
- package/src/color/createColorFieldState.d.ts +46 -44
- package/src/color/createColorFieldState.ts +428 -292
- package/src/color/createColorSliderState.d.ts +58 -56
- package/src/color/createColorSliderState.ts +272 -241
- package/src/color/createColorWheelState.d.ts +42 -40
- package/src/color/createColorWheelState.ts +248 -211
- package/src/color/getColorChannels.ts +36 -0
- package/src/color/index.d.ts +36 -7
- package/src/color/index.ts +44 -47
- package/src/color/types.d.ts +68 -63
- package/src/color/types.ts +119 -127
- package/src/combobox/createComboBoxState.d.ts +107 -105
- package/src/combobox/createComboBoxState.ts +790 -703
- package/src/combobox/index.d.ts +10 -2
- package/src/combobox/index.ts +13 -13
- package/src/data/createAsyncList.ts +403 -0
- package/src/data/createListData.ts +298 -0
- package/src/data/createTreeData.ts +646 -0
- package/src/data/index.ts +25 -0
- package/src/datepicker/index.ts +32 -0
- package/src/disclosure/createDisclosureState.d.ts +47 -43
- package/src/disclosure/createDisclosureState.ts +9 -23
- 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 +151 -153
- package/src/dnd/createDraggableCollectionState.d.ts +52 -41
- package/src/dnd/createDraggableCollectionState.ts +158 -165
- package/src/dnd/createDropState.d.ts +64 -45
- package/src/dnd/createDropState.ts +202 -212
- package/src/dnd/createDroppableCollectionState.d.ts +88 -59
- package/src/dnd/createDroppableCollectionState.ts +354 -357
- package/src/dnd/index.d.ts +54 -6
- package/src/dnd/index.ts +63 -76
- package/src/dnd/types.d.ts +167 -156
- package/src/dnd/types.ts +316 -317
- package/src/form/createFormValidationState.d.ts +47 -45
- package/src/form/createFormValidationState.ts +356 -389
- package/src/form/index.d.ts +16 -2
- package/src/form/index.ts +15 -15
- package/src/grid/createGridState.d.ts +7 -4
- package/src/grid/createGridState.ts +44 -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 +24 -17
- package/src/index.d.ts +310 -26
- package/src/index.ts +87 -79
- package/src/menu/index.ts +17 -0
- package/src/numberfield/createNumberFieldState.d.ts +56 -54
- package/src/numberfield/createNumberFieldState.ts +422 -383
- package/src/numberfield/index.d.ts +6 -2
- package/src/numberfield/index.ts +5 -5
- package/src/overlays/createOverlayTriggerState.d.ts +22 -20
- package/src/overlays/createOverlayTriggerState.ts +66 -67
- package/src/overlays/index.d.ts +6 -2
- package/src/overlays/index.ts +5 -5
- package/src/radio/createRadioGroupState.d.ts +54 -52
- package/src/radio/createRadioGroupState.ts +66 -30
- 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 +56 -62
- 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 +290 -181
- package/src/select/index.d.ts +2 -2
- package/src/select/index.ts +1 -5
- package/src/selection/index.ts +27 -0
- package/src/slider/createSliderState.d.ts +60 -60
- package/src/slider/createSliderState.ts +233 -211
- 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 +61 -64
- package/src/table/createTableColumnResizeState.ts +242 -0
- package/src/table/createTableState.d.ts +7 -4
- package/src/table/createTableState.ts +10 -7
- package/src/table/index.d.ts +14 -4
- package/src/table/index.ts +10 -3
- package/src/table/types.d.ts +98 -92
- package/src/table/types.ts +17 -13
- package/src/tabs/createTabListState.d.ts +59 -57
- package/src/tabs/createTabListState.ts +58 -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 -81
- package/src/toast/createToastState.ts +56 -44
- package/src/toast/index.d.ts +12 -2
- package/src/toast/index.ts +1 -1
- package/src/toggle/createToggleGroupState.ts +117 -0
- 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 +6 -4
- package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
- package/src/tooltip/createTooltipTriggerState.ts +184 -183
- package/src/tooltip/index.d.ts +7 -2
- package/src/tooltip/index.ts +6 -6
- package/src/tree/TreeCollection.d.ts +26 -23
- package/src/tree/TreeCollection.ts +215 -175
- package/src/tree/createTreeState.d.ts +7 -4
- package/src/tree/createTreeState.ts +390 -392
- package/src/tree/index.d.ts +10 -4
- package/src/tree/index.ts +13 -13
- package/src/tree/types.d.ts +131 -126
- package/src/tree/types.ts +183 -174
- 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
|
@@ -5,29 +5,35 @@
|
|
|
5
5
|
* Based on @react-stately/calendar useCalendarState
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createSignal, createMemo, type Accessor } from
|
|
8
|
+
import { createSignal, createMemo, createEffect, type Accessor } from "solid-js";
|
|
9
9
|
import {
|
|
10
|
+
type Calendar as InternationalizedCalendar,
|
|
10
11
|
type CalendarDate,
|
|
12
|
+
type CalendarIdentifier,
|
|
11
13
|
type DateValue,
|
|
14
|
+
GregorianCalendar,
|
|
12
15
|
today,
|
|
13
16
|
getLocalTimeZone,
|
|
17
|
+
isEqualCalendar,
|
|
14
18
|
isSameDay,
|
|
15
19
|
isSameMonth,
|
|
16
20
|
startOfMonth,
|
|
17
21
|
endOfMonth,
|
|
18
22
|
startOfWeek,
|
|
19
23
|
getWeeksInMonth,
|
|
20
|
-
getDayOfWeek,
|
|
21
24
|
DateFormatter,
|
|
25
|
+
createCalendar as intlCreateCalendar,
|
|
26
|
+
toCalendar as intlToCalendar,
|
|
22
27
|
toCalendarDate as intlToCalendarDate,
|
|
23
|
-
} from
|
|
24
|
-
import { access, type MaybeAccessor } from
|
|
28
|
+
} from "@internationalized/date";
|
|
29
|
+
import { access, type MaybeAccessor } from "../utils";
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
export type ValidationState = "valid" | "invalid";
|
|
32
|
+
export type CalendarPageBehavior = "single" | "visible";
|
|
33
|
+
export type CalendarSelectionAlignment = "start" | "center" | "end";
|
|
34
|
+
export type CalendarDayOfWeek = "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat";
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
const dayOfWeekNames: CalendarDayOfWeek[] = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
31
37
|
|
|
32
38
|
export interface CalendarStateProps<T extends DateValue = DateValue> {
|
|
33
39
|
/** The current value (controlled). */
|
|
@@ -35,7 +41,7 @@ export interface CalendarStateProps<T extends DateValue = DateValue> {
|
|
|
35
41
|
/** The default value (uncontrolled). */
|
|
36
42
|
defaultValue?: T | null;
|
|
37
43
|
/** Handler called when the value changes. */
|
|
38
|
-
onChange?: (value: T) => void;
|
|
44
|
+
onChange?: (value: T | null) => void;
|
|
39
45
|
/** The minimum allowed date. */
|
|
40
46
|
minValue?: MaybeAccessor<DateValue | undefined>;
|
|
41
47
|
/** The maximum allowed date. */
|
|
@@ -47,17 +53,23 @@ export interface CalendarStateProps<T extends DateValue = DateValue> {
|
|
|
47
53
|
/** Whether dates outside the visible range are automatically focused. */
|
|
48
54
|
autoFocus?: boolean;
|
|
49
55
|
/** The date that is focused when the calendar first mounts. */
|
|
50
|
-
focusedValue?: MaybeAccessor<DateValue | undefined>;
|
|
56
|
+
focusedValue?: MaybeAccessor<DateValue | null | undefined>;
|
|
51
57
|
/** The default focused date (uncontrolled). */
|
|
52
58
|
defaultFocusedValue?: DateValue;
|
|
53
59
|
/** Handler called when the focused date changes. */
|
|
54
60
|
onFocusChange?: (date: CalendarDate) => void;
|
|
55
61
|
/** The locale to use for formatting. */
|
|
56
|
-
locale?: string
|
|
62
|
+
locale?: MaybeAccessor<string | undefined>;
|
|
63
|
+
/** Creates a calendar object for a locale calendar identifier. */
|
|
64
|
+
createCalendar?: (identifier: CalendarIdentifier) => InternationalizedCalendar;
|
|
57
65
|
/** Callback that is called for each date in the calendar to determine if it is unavailable. */
|
|
58
66
|
isDateUnavailable?: (date: DateValue) => boolean;
|
|
59
67
|
/** The number of months to display at once. */
|
|
60
68
|
visibleMonths?: number;
|
|
69
|
+
/** Controls whether paging advances by one month or by the visible month range. */
|
|
70
|
+
pageBehavior?: CalendarPageBehavior;
|
|
71
|
+
/** Determines how the visible months align around the initial focused date. */
|
|
72
|
+
selectionAlignment?: CalendarSelectionAlignment;
|
|
61
73
|
/** Whether to automatically focus the calendar when it is mounted. */
|
|
62
74
|
autoFocusOnMount?: boolean;
|
|
63
75
|
/** Controls which days are disabled. */
|
|
@@ -87,8 +99,14 @@ export interface CalendarState<T extends DateValue = DateValue> {
|
|
|
87
99
|
visibleRange: Accessor<{ start: CalendarDate; end: CalendarDate }>;
|
|
88
100
|
/** The timezone used for date calculations. */
|
|
89
101
|
timeZone: string;
|
|
102
|
+
/** The locale used for formatting and locale-specific week starts. */
|
|
103
|
+
locale: Accessor<string>;
|
|
104
|
+
/** The explicitly requested first weekday, or undefined for the locale default. */
|
|
105
|
+
firstDayOfWeek: Accessor<CalendarDayOfWeek | undefined>;
|
|
90
106
|
/** The validation state. */
|
|
91
107
|
validationState: Accessor<ValidationState | undefined>;
|
|
108
|
+
/** Whether the current value is invalid according to validation state. */
|
|
109
|
+
isValueInvalid: Accessor<boolean>;
|
|
92
110
|
/** Whether a date is selected. */
|
|
93
111
|
isSelected: (date: DateValue) => boolean;
|
|
94
112
|
/** Whether a date is focused. */
|
|
@@ -143,84 +161,193 @@ export interface CalendarState<T extends DateValue = DateValue> {
|
|
|
143
161
|
isPaginating: Accessor<boolean>;
|
|
144
162
|
}
|
|
145
163
|
|
|
146
|
-
// ============================================
|
|
147
|
-
// IMPLEMENTATION
|
|
148
|
-
// ============================================
|
|
149
|
-
|
|
150
164
|
/**
|
|
151
165
|
* Provides state management for a calendar component.
|
|
152
166
|
*/
|
|
153
167
|
export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
154
|
-
props: CalendarStateProps<T> = {}
|
|
168
|
+
props: CalendarStateProps<T> = {},
|
|
155
169
|
): CalendarState<T> {
|
|
156
170
|
const timeZone = getLocalTimeZone();
|
|
157
|
-
const locale = props.locale ??
|
|
171
|
+
const locale = createMemo(() => access(props.locale) ?? "en-US");
|
|
172
|
+
const resolvedOptions = createMemo(() => new DateFormatter(locale()).resolvedOptions());
|
|
173
|
+
const calendar = createMemo(() =>
|
|
174
|
+
(props.createCalendar ?? intlCreateCalendar)(resolvedOptions().calendar as CalendarIdentifier),
|
|
175
|
+
);
|
|
158
176
|
const visibleMonths = props.visibleMonths ?? 1;
|
|
177
|
+
const firstDayOfWeekName = (): CalendarDayOfWeek | undefined =>
|
|
178
|
+
props.firstDayOfWeek == null ? undefined : dayOfWeekNames[props.firstDayOfWeek];
|
|
179
|
+
|
|
180
|
+
const toDisplayCalendarDate = (date: DateValue): CalendarDate =>
|
|
181
|
+
intlToCalendar(toCalendarDate(date), calendar());
|
|
182
|
+
|
|
183
|
+
const selectionAlignmentOffset = (): number => {
|
|
184
|
+
switch (props.selectionAlignment) {
|
|
185
|
+
case "end":
|
|
186
|
+
return Math.max(visibleMonths - 1, 0);
|
|
187
|
+
case "center": {
|
|
188
|
+
let halfDuration = Math.floor(visibleMonths / 2);
|
|
189
|
+
if (halfDuration > 0 && visibleMonths % 2 === 0) {
|
|
190
|
+
halfDuration--;
|
|
191
|
+
}
|
|
192
|
+
return halfDuration;
|
|
193
|
+
}
|
|
194
|
+
case "start":
|
|
195
|
+
default:
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const alignVisibleRangeStart = (date: CalendarDate): CalendarDate => {
|
|
201
|
+
const offset = selectionAlignmentOffset();
|
|
202
|
+
return startOfMonth(offset > 0 ? date.subtract({ months: offset }) : date);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const visibleRangeEnd = (start: CalendarDate): CalendarDate => {
|
|
206
|
+
let end = endOfMonth(start);
|
|
207
|
+
|
|
208
|
+
for (let i = 1; i < visibleMonths; i++) {
|
|
209
|
+
end = endOfMonth(end.add({ months: 1 }));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return end;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const constrainDate = (date: CalendarDate): CalendarDate => {
|
|
216
|
+
const minValue = access(props.minValue);
|
|
217
|
+
const maxValue = access(props.maxValue);
|
|
218
|
+
|
|
219
|
+
let constrained = date;
|
|
220
|
+
|
|
221
|
+
if (minValue && constrained.compare(toDisplayCalendarDate(minValue)) < 0) {
|
|
222
|
+
constrained = toDisplayCalendarDate(minValue);
|
|
223
|
+
}
|
|
224
|
+
if (maxValue && constrained.compare(toDisplayCalendarDate(maxValue)) > 0) {
|
|
225
|
+
constrained = toDisplayCalendarDate(maxValue);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return constrained;
|
|
229
|
+
};
|
|
159
230
|
|
|
160
231
|
// Determine the initially focused date
|
|
161
232
|
const getInitialFocusedDate = (): CalendarDate => {
|
|
162
233
|
const controlledFocused = access(props.focusedValue);
|
|
163
234
|
if (controlledFocused) {
|
|
164
|
-
return
|
|
235
|
+
return toDisplayCalendarDate(controlledFocused);
|
|
165
236
|
}
|
|
166
237
|
if (props.defaultFocusedValue) {
|
|
167
|
-
return
|
|
238
|
+
return toDisplayCalendarDate(props.defaultFocusedValue);
|
|
168
239
|
}
|
|
169
240
|
const controlledValue = access(props.value);
|
|
170
241
|
if (controlledValue) {
|
|
171
|
-
return
|
|
242
|
+
return toDisplayCalendarDate(controlledValue);
|
|
172
243
|
}
|
|
173
244
|
if (props.defaultValue) {
|
|
174
|
-
return
|
|
245
|
+
return toDisplayCalendarDate(props.defaultValue);
|
|
175
246
|
}
|
|
176
|
-
return today(timeZone);
|
|
247
|
+
return intlToCalendar(today(timeZone), calendar());
|
|
177
248
|
};
|
|
178
249
|
|
|
179
250
|
// State signals
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
);
|
|
183
|
-
const [
|
|
184
|
-
|
|
251
|
+
const initialFocusedDate = constrainDate(getInitialFocusedDate());
|
|
252
|
+
const [internalValue, setInternalValue] = createSignal<T | null>(props.defaultValue ?? null);
|
|
253
|
+
const [focusedDate, setFocusedDateInternal] = createSignal<CalendarDate>(initialFocusedDate);
|
|
254
|
+
const [visibleRangeStart, setVisibleRangeStart] = createSignal<CalendarDate>(
|
|
255
|
+
alignVisibleRangeStart(initialFocusedDate),
|
|
185
256
|
);
|
|
186
257
|
const [isFocused, setFocused] = createSignal(false);
|
|
187
258
|
const [isPaginating, setIsPaginating] = createSignal(false);
|
|
188
259
|
|
|
189
260
|
// Controlled vs uncontrolled value
|
|
190
|
-
const
|
|
261
|
+
const sourceValue = createMemo<T | null>(() => {
|
|
191
262
|
const controlled = access(props.value);
|
|
192
263
|
return controlled !== undefined ? controlled : internalValue();
|
|
193
264
|
});
|
|
265
|
+
const value = createMemo<T | null>(() => {
|
|
266
|
+
const currentValue = sourceValue();
|
|
267
|
+
return currentValue ? (toDisplayCalendarDate(currentValue) as unknown as T) : null;
|
|
268
|
+
});
|
|
194
269
|
|
|
195
270
|
// Derived states
|
|
196
271
|
const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
|
|
197
272
|
const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
|
|
198
273
|
const validationState = createMemo(() => access(props.validationState));
|
|
274
|
+
const isValueInvalid = createMemo(() => validationState() === "invalid");
|
|
199
275
|
|
|
200
|
-
// Visible range based on
|
|
276
|
+
// Visible range based on the paged range start.
|
|
201
277
|
const visibleRange = createMemo(() => {
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
let end = endOfMonth(focused);
|
|
278
|
+
const start = visibleRangeStart();
|
|
279
|
+
const end = visibleRangeEnd(start);
|
|
205
280
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
281
|
+
return { start, end };
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const syncVisibleRangeForFocusedDate = (nextFocusedDate: CalendarDate) => {
|
|
285
|
+
const range = visibleRange();
|
|
286
|
+
|
|
287
|
+
if (nextFocusedDate.compare(range.start) < 0) {
|
|
288
|
+
setVisibleRangeStart(startOfMonth(nextFocusedDate.subtract({ months: visibleMonths - 1 })));
|
|
289
|
+
} else if (nextFocusedDate.compare(range.end) > 0) {
|
|
290
|
+
setVisibleRangeStart(startOfMonth(nextFocusedDate));
|
|
209
291
|
}
|
|
292
|
+
};
|
|
210
293
|
|
|
211
|
-
|
|
294
|
+
createEffect(() => {
|
|
295
|
+
const controlledFocused = access(props.focusedValue);
|
|
296
|
+
if (!controlledFocused) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const nextFocusedDate = constrainDate(toDisplayCalendarDate(controlledFocused));
|
|
301
|
+
|
|
302
|
+
const currentFocusedDate = focusedDate();
|
|
303
|
+
if (
|
|
304
|
+
nextFocusedDate.compare(currentFocusedDate) !== 0 ||
|
|
305
|
+
!isEqualCalendar(nextFocusedDate.calendar, currentFocusedDate.calendar)
|
|
306
|
+
) {
|
|
307
|
+
setFocusedDateInternal(nextFocusedDate);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
syncVisibleRangeForFocusedDate(nextFocusedDate);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
createEffect(() => {
|
|
314
|
+
const controlledFocused = access(props.focusedValue);
|
|
315
|
+
if (controlledFocused) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const currentFocusedDate = focusedDate();
|
|
320
|
+
const nextFocusedDate = constrainDate(currentFocusedDate);
|
|
321
|
+
|
|
322
|
+
if (
|
|
323
|
+
nextFocusedDate.compare(currentFocusedDate) === 0 &&
|
|
324
|
+
isEqualCalendar(nextFocusedDate.calendar, currentFocusedDate.calendar)
|
|
325
|
+
) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
syncVisibleRangeForFocusedDate(nextFocusedDate);
|
|
330
|
+
setFocusedDateInternal(nextFocusedDate);
|
|
331
|
+
props.onFocusChange?.(nextFocusedDate);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
createEffect(() => {
|
|
335
|
+
const currentFocusedDate = focusedDate();
|
|
336
|
+
const nextFocusedDate = toDisplayCalendarDate(currentFocusedDate);
|
|
337
|
+
|
|
338
|
+
if (isEqualCalendar(currentFocusedDate.calendar, nextFocusedDate.calendar)) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
setFocusedDateInternal(nextFocusedDate);
|
|
343
|
+
setVisibleRangeStart(alignVisibleRangeStart(nextFocusedDate));
|
|
212
344
|
});
|
|
213
345
|
|
|
214
346
|
// Format week days for headers
|
|
215
347
|
const weekDays = createMemo(() => {
|
|
216
|
-
const formatter = new DateFormatter(locale, { weekday:
|
|
217
|
-
const startDay = props.firstDayOfWeek ?? 0;
|
|
348
|
+
const formatter = new DateFormatter(locale(), { weekday: "narrow" });
|
|
218
349
|
const days: string[] = [];
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
// Find the start of the week
|
|
222
|
-
const dayOfWeek = getDayOfWeek(base, locale);
|
|
223
|
-
const weekStart = base.subtract({ days: (dayOfWeek - startDay + 7) % 7 });
|
|
350
|
+
const weekStart = startOfWeek(today(timeZone), locale(), firstDayOfWeekName());
|
|
224
351
|
|
|
225
352
|
for (let i = 0; i < 7; i++) {
|
|
226
353
|
const day = weekStart.add({ days: i });
|
|
@@ -232,41 +359,44 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
232
359
|
|
|
233
360
|
// Title (formatted month/year)
|
|
234
361
|
const title = createMemo(() => {
|
|
235
|
-
const formatter = new DateFormatter(locale, {
|
|
236
|
-
month:
|
|
237
|
-
year:
|
|
362
|
+
const formatter = new DateFormatter(locale(), {
|
|
363
|
+
month: "long",
|
|
364
|
+
year: "numeric",
|
|
365
|
+
calendar: focusedDate().calendar.identifier,
|
|
238
366
|
});
|
|
239
|
-
|
|
367
|
+
const date = focusedDate();
|
|
368
|
+
const formattableMonth = date.calendar.getFormattableMonth?.(date) ?? date;
|
|
369
|
+
return formatter.format(formattableMonth.toDate(timeZone));
|
|
240
370
|
});
|
|
241
371
|
|
|
242
372
|
// Set value with onChange callback
|
|
243
373
|
const setValue = (newValue: T | null) => {
|
|
244
374
|
if (isDisabled() || isReadOnly()) return;
|
|
245
375
|
|
|
376
|
+
const oldValue = sourceValue();
|
|
377
|
+
const nextValue = newValue
|
|
378
|
+
? (convertValue(constrainDate(toDisplayCalendarDate(newValue)), oldValue) as T)
|
|
379
|
+
: null;
|
|
380
|
+
|
|
246
381
|
const controlled = access(props.value);
|
|
247
382
|
if (controlled === undefined) {
|
|
248
|
-
setInternalValue(() =>
|
|
383
|
+
setInternalValue(() => nextValue);
|
|
249
384
|
}
|
|
250
385
|
|
|
251
|
-
if (
|
|
252
|
-
props.onChange(
|
|
386
|
+
if (!Object.is(oldValue, nextValue) && props.onChange) {
|
|
387
|
+
props.onChange(nextValue);
|
|
253
388
|
}
|
|
254
389
|
};
|
|
255
390
|
|
|
256
391
|
// Set focused date with constraints
|
|
257
392
|
const setFocusedDate = (date: CalendarDate) => {
|
|
258
|
-
const
|
|
259
|
-
const maxValue = access(props.maxValue);
|
|
393
|
+
const constrained = constrainDate(date);
|
|
260
394
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (minValue && date.compare(toCalendarDate(minValue)) < 0) {
|
|
264
|
-
constrained = toCalendarDate(minValue);
|
|
265
|
-
}
|
|
266
|
-
if (maxValue && date.compare(toCalendarDate(maxValue)) > 0) {
|
|
267
|
-
constrained = toCalendarDate(maxValue);
|
|
395
|
+
if (Object.is(constrained, focusedDate())) {
|
|
396
|
+
return;
|
|
268
397
|
}
|
|
269
398
|
|
|
399
|
+
syncVisibleRangeForFocusedDate(constrained);
|
|
270
400
|
setFocusedDateInternal(constrained);
|
|
271
401
|
props.onFocusChange?.(constrained);
|
|
272
402
|
};
|
|
@@ -275,12 +405,12 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
275
405
|
const isSelected = (date: DateValue): boolean => {
|
|
276
406
|
const v = value();
|
|
277
407
|
if (!v) return false;
|
|
278
|
-
return isSameDay(
|
|
408
|
+
return isSameDay(toDisplayCalendarDate(date), toDisplayCalendarDate(v));
|
|
279
409
|
};
|
|
280
410
|
|
|
281
411
|
// Check if a date is focused
|
|
282
412
|
const isCellFocused = (date: DateValue): boolean => {
|
|
283
|
-
return isSameDay(
|
|
413
|
+
return isSameDay(toDisplayCalendarDate(date), focusedDate());
|
|
284
414
|
};
|
|
285
415
|
|
|
286
416
|
// Check if a date is unavailable
|
|
@@ -295,10 +425,10 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
295
425
|
|
|
296
426
|
const minValue = access(props.minValue);
|
|
297
427
|
const maxValue = access(props.maxValue);
|
|
298
|
-
const calDate =
|
|
428
|
+
const calDate = toDisplayCalendarDate(date);
|
|
299
429
|
|
|
300
|
-
if (minValue && calDate.compare(
|
|
301
|
-
if (maxValue && calDate.compare(
|
|
430
|
+
if (minValue && calDate.compare(toDisplayCalendarDate(minValue)) < 0) return true;
|
|
431
|
+
if (maxValue && calDate.compare(toDisplayCalendarDate(maxValue)) > 0) return true;
|
|
302
432
|
|
|
303
433
|
return false;
|
|
304
434
|
};
|
|
@@ -306,7 +436,7 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
306
436
|
// Check if a date is outside the visible range
|
|
307
437
|
const isOutsideVisibleRange = (date: DateValue): boolean => {
|
|
308
438
|
const range = visibleRange();
|
|
309
|
-
const calDate =
|
|
439
|
+
const calDate = toDisplayCalendarDate(date);
|
|
310
440
|
return !isSameMonth(calDate, range.start) && !isSameMonth(calDate, range.end);
|
|
311
441
|
};
|
|
312
442
|
|
|
@@ -318,13 +448,21 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
318
448
|
// Navigation methods
|
|
319
449
|
const focusPreviousPage = () => {
|
|
320
450
|
setIsPaginating(true);
|
|
321
|
-
|
|
451
|
+
const pageMonths = props.pageBehavior === "single" ? 1 : visibleMonths;
|
|
452
|
+
const nextFocusedDate = constrainDate(focusedDate().subtract({ months: pageMonths }));
|
|
453
|
+
setFocusedDateInternal(nextFocusedDate);
|
|
454
|
+
setVisibleRangeStart(startOfMonth(visibleRangeStart().subtract({ months: pageMonths })));
|
|
455
|
+
props.onFocusChange?.(nextFocusedDate);
|
|
322
456
|
setIsPaginating(false);
|
|
323
457
|
};
|
|
324
458
|
|
|
325
459
|
const focusNextPage = () => {
|
|
326
460
|
setIsPaginating(true);
|
|
327
|
-
|
|
461
|
+
const pageMonths = props.pageBehavior === "single" ? 1 : visibleMonths;
|
|
462
|
+
const nextFocusedDate = constrainDate(focusedDate().add({ months: pageMonths }));
|
|
463
|
+
setFocusedDateInternal(nextFocusedDate);
|
|
464
|
+
setVisibleRangeStart(startOfMonth(visibleRangeStart().add({ months: pageMonths })));
|
|
465
|
+
props.onFocusChange?.(nextFocusedDate);
|
|
328
466
|
setIsPaginating(false);
|
|
329
467
|
};
|
|
330
468
|
|
|
@@ -380,12 +518,12 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
380
518
|
// Get dates in a specific week
|
|
381
519
|
const getDatesInWeek = (
|
|
382
520
|
weekIndex: number,
|
|
383
|
-
monthStartDate?: CalendarDate
|
|
521
|
+
monthStartDate?: CalendarDate,
|
|
384
522
|
): (CalendarDate | null)[] => {
|
|
385
523
|
const startDate = monthStartDate ?? visibleRange().start;
|
|
386
524
|
|
|
387
525
|
const monthStart = startOfMonth(startDate);
|
|
388
|
-
const weekStart = startOfWeek(monthStart, locale);
|
|
526
|
+
const weekStart = startOfWeek(monthStart, locale(), firstDayOfWeekName());
|
|
389
527
|
|
|
390
528
|
const week: (CalendarDate | null)[] = [];
|
|
391
529
|
const firstDayOfWeek = weekStart.add({ weeks: weekIndex });
|
|
@@ -406,7 +544,7 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
406
544
|
// Get number of weeks in a month
|
|
407
545
|
const getWeeksInMonthFn = (date?: CalendarDate): number => {
|
|
408
546
|
const monthDate = date ?? focusedDate();
|
|
409
|
-
return getWeeksInMonth(monthDate, locale);
|
|
547
|
+
return getWeeksInMonth(monthDate, locale(), firstDayOfWeekName());
|
|
410
548
|
};
|
|
411
549
|
|
|
412
550
|
return {
|
|
@@ -418,7 +556,10 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
418
556
|
isReadOnly,
|
|
419
557
|
visibleRange,
|
|
420
558
|
timeZone,
|
|
559
|
+
locale,
|
|
560
|
+
firstDayOfWeek: firstDayOfWeekName,
|
|
421
561
|
validationState,
|
|
562
|
+
isValueInvalid,
|
|
422
563
|
isSelected,
|
|
423
564
|
isCellFocused,
|
|
424
565
|
isCellUnavailable,
|
|
@@ -448,10 +589,6 @@ export function createCalendarState<T extends DateValue = CalendarDate>(
|
|
|
448
589
|
};
|
|
449
590
|
}
|
|
450
591
|
|
|
451
|
-
// ============================================
|
|
452
|
-
// HELPER FUNCTIONS
|
|
453
|
-
// ============================================
|
|
454
|
-
|
|
455
592
|
/**
|
|
456
593
|
* Converts a DateValue to a CalendarDate.
|
|
457
594
|
*/
|
|
@@ -459,3 +596,13 @@ function toCalendarDate(date: DateValue): CalendarDate {
|
|
|
459
596
|
// Use the library function to convert
|
|
460
597
|
return intlToCalendarDate(date);
|
|
461
598
|
}
|
|
599
|
+
|
|
600
|
+
function convertValue(newValue: CalendarDate, oldValue?: DateValue | null): DateValue {
|
|
601
|
+
const localValue = intlToCalendar(newValue, oldValue?.calendar ?? new GregorianCalendar());
|
|
602
|
+
|
|
603
|
+
if (oldValue && "hour" in oldValue) {
|
|
604
|
+
return oldValue.set(localValue);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return localValue;
|
|
608
|
+
}
|