@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,36 +5,41 @@
|
|
|
5
5
|
* Based on @react-stately/datepicker useDateFieldState
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createSignal, createMemo, type Accessor } from
|
|
8
|
+
import { createSignal, createMemo, type Accessor } from "solid-js";
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
CalendarDate,
|
|
11
11
|
type CalendarDateTime,
|
|
12
|
-
type ZonedDateTime,
|
|
13
12
|
type DateValue,
|
|
14
13
|
today,
|
|
15
14
|
getLocalTimeZone,
|
|
16
15
|
DateFormatter,
|
|
17
16
|
toCalendarDate as intlToCalendarDate,
|
|
18
17
|
toCalendarDateTime,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
endOfMonth,
|
|
19
|
+
} from "@internationalized/date";
|
|
20
|
+
import { access, type MaybeAccessor } from "../utils";
|
|
21
|
+
import type { ValidationState } from "./createCalendarState";
|
|
22
|
+
import {
|
|
23
|
+
createFormValidationState,
|
|
24
|
+
DEFAULT_VALIDATION_RESULT,
|
|
25
|
+
VALID_VALIDITY_STATE,
|
|
26
|
+
type ValidationBehavior,
|
|
27
|
+
type ValidationFunction,
|
|
28
|
+
type ValidationResult,
|
|
29
|
+
type ValidityState,
|
|
30
|
+
} from "../form";
|
|
26
31
|
|
|
27
32
|
export type DateSegmentType =
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
33
|
+
| "year"
|
|
34
|
+
| "month"
|
|
35
|
+
| "day"
|
|
36
|
+
| "hour"
|
|
37
|
+
| "minute"
|
|
38
|
+
| "second"
|
|
39
|
+
| "dayPeriod"
|
|
40
|
+
| "era"
|
|
41
|
+
| "timeZoneName"
|
|
42
|
+
| "literal";
|
|
38
43
|
|
|
39
44
|
export interface DateSegment {
|
|
40
45
|
/** The type of segment. */
|
|
@@ -72,18 +77,26 @@ export interface DateFieldStateProps<T extends DateValue = DateValue> {
|
|
|
72
77
|
isReadOnly?: MaybeAccessor<boolean>;
|
|
73
78
|
/** Whether the field is required. */
|
|
74
79
|
isRequired?: MaybeAccessor<boolean>;
|
|
80
|
+
/** Whether the value is invalid (controlled). */
|
|
81
|
+
isInvalid?: boolean;
|
|
75
82
|
/** The locale to use for formatting. */
|
|
76
83
|
locale?: string;
|
|
77
84
|
/** The granularity of the date/time (day, hour, minute, second). */
|
|
78
|
-
granularity?:
|
|
85
|
+
granularity?: "day" | "hour" | "minute" | "second";
|
|
79
86
|
/** Whether to show the hour in 12 or 24 hour format. */
|
|
80
87
|
hourCycle?: 12 | 24;
|
|
81
88
|
/** Whether to hide the time zone. */
|
|
82
89
|
hideTimeZone?: boolean;
|
|
83
90
|
/** The placeholder date (determines segment structure). */
|
|
84
91
|
placeholderValue?: DateValue;
|
|
92
|
+
/** Whether to force leading zeroes in month/day segments. */
|
|
93
|
+
shouldForceLeadingZeros?: boolean;
|
|
85
94
|
/** Validation state. */
|
|
86
95
|
validationState?: MaybeAccessor<ValidationState | undefined>;
|
|
96
|
+
/** Validation behavior mode. */
|
|
97
|
+
validationBehavior?: ValidationBehavior;
|
|
98
|
+
/** Custom validation function. */
|
|
99
|
+
validate?: ValidationFunction<T>;
|
|
87
100
|
/** Description text. */
|
|
88
101
|
description?: string;
|
|
89
102
|
/** Error message. */
|
|
@@ -92,6 +105,8 @@ export interface DateFieldStateProps<T extends DateValue = DateValue> {
|
|
|
92
105
|
allowsNonContiguousRanges?: boolean;
|
|
93
106
|
/** Whether to create a date or datetime. */
|
|
94
107
|
createCalendar?: (name: string) => unknown;
|
|
108
|
+
/** Whether a date is unavailable. */
|
|
109
|
+
isDateUnavailable?: (date: DateValue) => boolean;
|
|
95
110
|
}
|
|
96
111
|
|
|
97
112
|
export interface DateFieldState<T extends DateValue = DateValue> {
|
|
@@ -124,33 +139,40 @@ export interface DateFieldState<T extends DateValue = DateValue> {
|
|
|
124
139
|
/** The validation state. */
|
|
125
140
|
validationState: Accessor<ValidationState | undefined>;
|
|
126
141
|
/** The granularity. */
|
|
127
|
-
granularity:
|
|
142
|
+
granularity: "day" | "hour" | "minute" | "second";
|
|
128
143
|
/** Whether the value is invalid. */
|
|
129
144
|
isInvalid: Accessor<boolean>;
|
|
145
|
+
/** Realtime validation results, including native and custom constraints. */
|
|
146
|
+
realtimeValidation: Accessor<ValidationResult>;
|
|
147
|
+
/** Currently displayed validation results. */
|
|
148
|
+
displayValidation: Accessor<ValidationResult>;
|
|
149
|
+
/** Updates the current validation result. */
|
|
150
|
+
updateValidation: (result: ValidationResult) => void;
|
|
151
|
+
/** Resets displayed validation to valid. */
|
|
152
|
+
resetValidation: () => void;
|
|
153
|
+
/** Commits realtime validation to displayed validation. */
|
|
154
|
+
commitValidation: () => void;
|
|
130
155
|
/** The locale. */
|
|
131
156
|
locale: string;
|
|
132
157
|
/** The time zone. */
|
|
133
158
|
timeZone: string;
|
|
159
|
+
/** The default value. */
|
|
160
|
+
defaultValue: T | null;
|
|
134
161
|
}
|
|
135
162
|
|
|
136
|
-
// ============================================
|
|
137
|
-
// IMPLEMENTATION
|
|
138
|
-
// ============================================
|
|
139
|
-
|
|
140
163
|
/**
|
|
141
164
|
* Provides state management for a date field component.
|
|
142
165
|
*/
|
|
143
166
|
export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
144
|
-
props: DateFieldStateProps<T> = {}
|
|
167
|
+
props: DateFieldStateProps<T> = {},
|
|
145
168
|
): DateFieldState<T> {
|
|
146
169
|
const timeZone = getLocalTimeZone();
|
|
147
|
-
const locale = props.locale ??
|
|
148
|
-
const granularity = props.granularity ??
|
|
170
|
+
const locale = props.locale ?? "en-US";
|
|
171
|
+
const granularity = props.granularity ?? "day";
|
|
149
172
|
|
|
150
173
|
// State signals
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
);
|
|
174
|
+
const initialValue = props.defaultValue ?? null;
|
|
175
|
+
const [internalValue, setInternalValue] = createSignal<T | null>(initialValue);
|
|
154
176
|
|
|
155
177
|
// Track partial values during editing
|
|
156
178
|
const [placeholderDate, setPlaceholderDate] = createSignal<Partial<DateParts>>({});
|
|
@@ -177,7 +199,7 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
177
199
|
const month = parts.month ?? placeholder.month;
|
|
178
200
|
const day = parts.day ?? placeholder.day;
|
|
179
201
|
|
|
180
|
-
if (granularity ===
|
|
202
|
+
if (granularity === "day") {
|
|
181
203
|
return intlToCalendarDate(placeholder).set({ year, month, day });
|
|
182
204
|
}
|
|
183
205
|
|
|
@@ -200,21 +222,67 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
200
222
|
const isDisabled = createMemo(() => access(props.isDisabled) ?? false);
|
|
201
223
|
const isReadOnly = createMemo(() => access(props.isReadOnly) ?? false);
|
|
202
224
|
const isRequired = createMemo(() => access(props.isRequired) ?? false);
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const isInvalid = createMemo(() => {
|
|
225
|
+
const explicitValidationState = createMemo(() => access(props.validationState));
|
|
226
|
+
const validationBehavior = () => props.validationBehavior ?? "native";
|
|
227
|
+
const builtinValidation = createMemo<ValidationResult>(() => {
|
|
207
228
|
const v = value();
|
|
208
|
-
if (!v) return
|
|
229
|
+
if (!v) return DEFAULT_VALIDATION_RESULT;
|
|
209
230
|
|
|
210
231
|
const minValue = access(props.minValue);
|
|
211
232
|
const maxValue = access(props.maxValue);
|
|
233
|
+
const validationDetails: ValidityState = { ...VALID_VALIDITY_STATE };
|
|
234
|
+
const validationErrors: string[] = [];
|
|
235
|
+
|
|
236
|
+
if (minValue && v.compare(minValue) < 0) {
|
|
237
|
+
validationDetails.rangeUnderflow = true;
|
|
238
|
+
validationErrors.push("Value is below the minimum date.");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (maxValue && v.compare(maxValue) > 0) {
|
|
242
|
+
validationDetails.rangeOverflow = true;
|
|
243
|
+
validationErrors.push("Value is above the maximum date.");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (props.isDateUnavailable?.(v)) {
|
|
247
|
+
validationDetails.customError = true;
|
|
248
|
+
validationErrors.push("Date is unavailable.");
|
|
249
|
+
}
|
|
212
250
|
|
|
213
|
-
if (
|
|
214
|
-
|
|
251
|
+
if (validationErrors.length === 0) {
|
|
252
|
+
return DEFAULT_VALIDATION_RESULT;
|
|
253
|
+
}
|
|
215
254
|
|
|
216
|
-
|
|
255
|
+
validationDetails.valid = false;
|
|
256
|
+
return {
|
|
257
|
+
isInvalid: true,
|
|
258
|
+
validationDetails,
|
|
259
|
+
validationErrors,
|
|
260
|
+
};
|
|
217
261
|
});
|
|
262
|
+
const validation = createFormValidationState<T>({
|
|
263
|
+
get value() {
|
|
264
|
+
return value();
|
|
265
|
+
},
|
|
266
|
+
get isInvalid() {
|
|
267
|
+
return props.isInvalid;
|
|
268
|
+
},
|
|
269
|
+
get validationState() {
|
|
270
|
+
return explicitValidationState();
|
|
271
|
+
},
|
|
272
|
+
get validationBehavior() {
|
|
273
|
+
return validationBehavior();
|
|
274
|
+
},
|
|
275
|
+
get validate() {
|
|
276
|
+
return props.validate;
|
|
277
|
+
},
|
|
278
|
+
get builtinValidation() {
|
|
279
|
+
return builtinValidation();
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
const isInvalid = createMemo(() => validation.displayValidation().isInvalid);
|
|
283
|
+
const validationState = createMemo<ValidationState | undefined>(
|
|
284
|
+
() => explicitValidationState() ?? (isInvalid() ? "invalid" : undefined),
|
|
285
|
+
);
|
|
218
286
|
|
|
219
287
|
// Generate segments based on granularity and locale
|
|
220
288
|
const segments = createMemo<DateSegment[]>(() => {
|
|
@@ -225,20 +293,21 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
225
293
|
const segs: DateSegment[] = [];
|
|
226
294
|
|
|
227
295
|
// Determine format options based on granularity
|
|
296
|
+
const shouldForceLeadingZeros = props.shouldForceLeadingZeros === true;
|
|
228
297
|
const formatOptions: Intl.DateTimeFormatOptions = {
|
|
229
|
-
year:
|
|
230
|
-
month:
|
|
231
|
-
day:
|
|
298
|
+
year: "numeric",
|
|
299
|
+
month: shouldForceLeadingZeros ? "2-digit" : "numeric",
|
|
300
|
+
day: shouldForceLeadingZeros ? "2-digit" : "numeric",
|
|
232
301
|
};
|
|
233
302
|
|
|
234
|
-
if (granularity !==
|
|
235
|
-
formatOptions.hour =
|
|
236
|
-
formatOptions.minute =
|
|
237
|
-
if (granularity ===
|
|
238
|
-
formatOptions.second =
|
|
303
|
+
if (granularity !== "day") {
|
|
304
|
+
formatOptions.hour = "2-digit";
|
|
305
|
+
formatOptions.minute = "2-digit";
|
|
306
|
+
if (granularity === "second") {
|
|
307
|
+
formatOptions.second = "2-digit";
|
|
239
308
|
}
|
|
240
309
|
if (props.hourCycle) {
|
|
241
|
-
formatOptions.hourCycle = props.hourCycle === 12 ?
|
|
310
|
+
formatOptions.hourCycle = props.hourCycle === 12 ? "h12" : "h23";
|
|
242
311
|
}
|
|
243
312
|
}
|
|
244
313
|
|
|
@@ -249,9 +318,9 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
249
318
|
for (const part of formattedParts) {
|
|
250
319
|
const type = mapPartType(part.type);
|
|
251
320
|
|
|
252
|
-
if (type ===
|
|
321
|
+
if (type === "literal") {
|
|
253
322
|
segs.push({
|
|
254
|
-
type:
|
|
323
|
+
type: "literal",
|
|
255
324
|
text: part.value,
|
|
256
325
|
isEditable: false,
|
|
257
326
|
isPlaceholder: false,
|
|
@@ -259,19 +328,20 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
259
328
|
});
|
|
260
329
|
} else if (type) {
|
|
261
330
|
const segValue = getSegmentValue(v, type);
|
|
262
|
-
const placeholderValue = getSegmentValue(placeholder, type);
|
|
263
331
|
const partValue = parts[type as keyof DateParts];
|
|
264
332
|
const hasValue = v !== null || partValue !== undefined;
|
|
265
333
|
|
|
266
334
|
segs.push({
|
|
267
335
|
type,
|
|
268
|
-
text: hasValue
|
|
336
|
+
text: hasValue
|
|
337
|
+
? part.value
|
|
338
|
+
: getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros),
|
|
269
339
|
value: segValue ?? partValue,
|
|
270
340
|
minValue: getMinValue(type),
|
|
271
341
|
maxValue: getMaxValue(type, v ?? placeholder),
|
|
272
342
|
isEditable: !isDisabled() && !isReadOnly(),
|
|
273
343
|
isPlaceholder: !hasValue,
|
|
274
|
-
placeholder: getPlaceholderText(type),
|
|
344
|
+
placeholder: getPlaceholderText(type, locale, granularity, shouldForceLeadingZeros),
|
|
275
345
|
});
|
|
276
346
|
}
|
|
277
347
|
}
|
|
@@ -320,9 +390,7 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
320
390
|
if (isDisabled() || isReadOnly()) return;
|
|
321
391
|
|
|
322
392
|
const v = value();
|
|
323
|
-
const current = v
|
|
324
|
-
? getSegmentValue(v, type)
|
|
325
|
-
: placeholderDate()[type as keyof DateParts];
|
|
393
|
+
const current = v ? getSegmentValue(v, type) : placeholderDate()[type as keyof DateParts];
|
|
326
394
|
const max = getMaxValue(type, v ?? props.placeholderValue ?? today(timeZone));
|
|
327
395
|
const min = getMinValue(type);
|
|
328
396
|
|
|
@@ -335,9 +403,7 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
335
403
|
if (isDisabled() || isReadOnly()) return;
|
|
336
404
|
|
|
337
405
|
const v = value();
|
|
338
|
-
const current = v
|
|
339
|
-
? getSegmentValue(v, type)
|
|
340
|
-
: placeholderDate()[type as keyof DateParts];
|
|
406
|
+
const current = v ? getSegmentValue(v, type) : placeholderDate()[type as keyof DateParts];
|
|
341
407
|
const max = getMaxValue(type, v ?? props.placeholderValue ?? today(timeZone));
|
|
342
408
|
const min = getMinValue(type);
|
|
343
409
|
|
|
@@ -348,11 +414,14 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
348
414
|
// Clear a segment
|
|
349
415
|
const clearSegment = (type: DateSegmentType) => {
|
|
350
416
|
if (isDisabled() || isReadOnly()) return;
|
|
417
|
+
if (type === "literal") return;
|
|
351
418
|
|
|
352
419
|
const v = value();
|
|
353
420
|
if (v) {
|
|
354
|
-
//
|
|
355
|
-
|
|
421
|
+
// Reset only the target segment to its minimum value instead of clearing the entire value
|
|
422
|
+
const min = getMinValue(type);
|
|
423
|
+
const updated = updateDatePart(v, type, min);
|
|
424
|
+
setValue(updated as T);
|
|
356
425
|
} else {
|
|
357
426
|
setPlaceholderDate((prev) => {
|
|
358
427
|
const next = { ...prev };
|
|
@@ -364,11 +433,37 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
364
433
|
|
|
365
434
|
// Confirm placeholder value
|
|
366
435
|
const confirmPlaceholder = () => {
|
|
436
|
+
if (isDisabled() || isReadOnly()) return;
|
|
437
|
+
|
|
438
|
+
const minValue = access(props.minValue);
|
|
439
|
+
const maxValue = access(props.maxValue);
|
|
440
|
+
const constrain = (candidate: DateValue): DateValue => {
|
|
441
|
+
if (minValue && candidate.compare(minValue) < 0) {
|
|
442
|
+
return minValue;
|
|
443
|
+
}
|
|
444
|
+
if (maxValue && candidate.compare(maxValue) > 0) {
|
|
445
|
+
return maxValue;
|
|
446
|
+
}
|
|
447
|
+
return candidate;
|
|
448
|
+
};
|
|
449
|
+
|
|
367
450
|
const parts = placeholderDate();
|
|
368
451
|
if (Object.keys(parts).length > 0) {
|
|
369
452
|
const dv = dateValue();
|
|
370
453
|
if (dv) {
|
|
371
|
-
|
|
454
|
+
const candidate = constrain(dv);
|
|
455
|
+
// Commit even if unavailable so isInvalid can flag it; don't leave field stuck
|
|
456
|
+
setValue(candidate as T);
|
|
457
|
+
}
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const current = value();
|
|
462
|
+
if (current) {
|
|
463
|
+
const constrained = constrain(current);
|
|
464
|
+
// Apply min/max constraints even if unavailable; isInvalid will flag unavailable dates
|
|
465
|
+
if (constrained.compare(current) !== 0) {
|
|
466
|
+
setValue(constrained as T);
|
|
372
467
|
}
|
|
373
468
|
}
|
|
374
469
|
};
|
|
@@ -376,19 +471,19 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
376
471
|
// Format the current value
|
|
377
472
|
const formatValue = (fieldOptions?: Intl.DateTimeFormatOptions): string => {
|
|
378
473
|
const v = value();
|
|
379
|
-
if (!v) return
|
|
474
|
+
if (!v) return "";
|
|
380
475
|
|
|
381
476
|
const options: Intl.DateTimeFormatOptions = fieldOptions ?? {
|
|
382
|
-
year:
|
|
383
|
-
month:
|
|
384
|
-
day:
|
|
477
|
+
year: "numeric",
|
|
478
|
+
month: props.shouldForceLeadingZeros === true ? "2-digit" : "numeric",
|
|
479
|
+
day: props.shouldForceLeadingZeros === true ? "2-digit" : "numeric",
|
|
385
480
|
};
|
|
386
481
|
|
|
387
|
-
if (granularity !==
|
|
388
|
-
options.hour =
|
|
389
|
-
options.minute =
|
|
390
|
-
if (granularity ===
|
|
391
|
-
options.second =
|
|
482
|
+
if (granularity !== "day") {
|
|
483
|
+
options.hour = "2-digit";
|
|
484
|
+
options.minute = "2-digit";
|
|
485
|
+
if (granularity === "second") {
|
|
486
|
+
options.second = "2-digit";
|
|
392
487
|
}
|
|
393
488
|
}
|
|
394
489
|
|
|
@@ -398,6 +493,7 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
398
493
|
|
|
399
494
|
return {
|
|
400
495
|
value,
|
|
496
|
+
defaultValue: initialValue,
|
|
401
497
|
dateValue,
|
|
402
498
|
setValue,
|
|
403
499
|
segments,
|
|
@@ -413,15 +509,16 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
413
509
|
validationState,
|
|
414
510
|
granularity,
|
|
415
511
|
isInvalid,
|
|
512
|
+
realtimeValidation: validation.realtimeValidation,
|
|
513
|
+
displayValidation: validation.displayValidation,
|
|
514
|
+
updateValidation: validation.updateValidation,
|
|
515
|
+
resetValidation: validation.resetValidation,
|
|
516
|
+
commitValidation: validation.commitValidation,
|
|
416
517
|
locale,
|
|
417
518
|
timeZone,
|
|
418
519
|
};
|
|
419
520
|
}
|
|
420
521
|
|
|
421
|
-
// ============================================
|
|
422
|
-
// HELPER TYPES & FUNCTIONS
|
|
423
|
-
// ============================================
|
|
424
|
-
|
|
425
522
|
interface DateParts {
|
|
426
523
|
year?: number;
|
|
427
524
|
month?: number;
|
|
@@ -433,50 +530,47 @@ interface DateParts {
|
|
|
433
530
|
|
|
434
531
|
function mapPartType(type: Intl.DateTimeFormatPartTypes): DateSegmentType | null {
|
|
435
532
|
switch (type) {
|
|
436
|
-
case
|
|
437
|
-
return
|
|
438
|
-
case
|
|
439
|
-
return
|
|
440
|
-
case
|
|
441
|
-
return
|
|
442
|
-
case
|
|
443
|
-
return
|
|
444
|
-
case
|
|
445
|
-
return
|
|
446
|
-
case
|
|
447
|
-
return
|
|
448
|
-
case
|
|
449
|
-
return
|
|
450
|
-
case
|
|
451
|
-
return
|
|
452
|
-
case
|
|
453
|
-
return
|
|
454
|
-
case
|
|
455
|
-
return
|
|
533
|
+
case "year":
|
|
534
|
+
return "year";
|
|
535
|
+
case "month":
|
|
536
|
+
return "month";
|
|
537
|
+
case "day":
|
|
538
|
+
return "day";
|
|
539
|
+
case "hour":
|
|
540
|
+
return "hour";
|
|
541
|
+
case "minute":
|
|
542
|
+
return "minute";
|
|
543
|
+
case "second":
|
|
544
|
+
return "second";
|
|
545
|
+
case "dayPeriod":
|
|
546
|
+
return "dayPeriod";
|
|
547
|
+
case "era":
|
|
548
|
+
return "era";
|
|
549
|
+
case "timeZoneName":
|
|
550
|
+
return "timeZoneName";
|
|
551
|
+
case "literal":
|
|
552
|
+
return "literal";
|
|
456
553
|
default:
|
|
457
554
|
return null;
|
|
458
555
|
}
|
|
459
556
|
}
|
|
460
557
|
|
|
461
|
-
function getSegmentValue(
|
|
462
|
-
date: DateValue | null,
|
|
463
|
-
type: DateSegmentType
|
|
464
|
-
): number | undefined {
|
|
558
|
+
function getSegmentValue(date: DateValue | null, type: DateSegmentType): number | undefined {
|
|
465
559
|
if (!date) return undefined;
|
|
466
560
|
|
|
467
561
|
switch (type) {
|
|
468
|
-
case
|
|
562
|
+
case "year":
|
|
469
563
|
return date.year;
|
|
470
|
-
case
|
|
564
|
+
case "month":
|
|
471
565
|
return date.month;
|
|
472
|
-
case
|
|
566
|
+
case "day":
|
|
473
567
|
return date.day;
|
|
474
|
-
case
|
|
475
|
-
return
|
|
476
|
-
case
|
|
477
|
-
return
|
|
478
|
-
case
|
|
479
|
-
return
|
|
568
|
+
case "hour":
|
|
569
|
+
return "hour" in date ? (date as CalendarDateTime).hour : undefined;
|
|
570
|
+
case "minute":
|
|
571
|
+
return "minute" in date ? (date as CalendarDateTime).minute : undefined;
|
|
572
|
+
case "second":
|
|
573
|
+
return "second" in date ? (date as CalendarDateTime).second : undefined;
|
|
480
574
|
default:
|
|
481
575
|
return undefined;
|
|
482
576
|
}
|
|
@@ -484,17 +578,17 @@ function getSegmentValue(
|
|
|
484
578
|
|
|
485
579
|
function getMinValue(type: DateSegmentType): number {
|
|
486
580
|
switch (type) {
|
|
487
|
-
case
|
|
581
|
+
case "year":
|
|
488
582
|
return 1;
|
|
489
|
-
case
|
|
583
|
+
case "month":
|
|
490
584
|
return 1;
|
|
491
|
-
case
|
|
585
|
+
case "day":
|
|
492
586
|
return 1;
|
|
493
|
-
case
|
|
587
|
+
case "hour":
|
|
494
588
|
return 0;
|
|
495
|
-
case
|
|
589
|
+
case "minute":
|
|
496
590
|
return 0;
|
|
497
|
-
case
|
|
591
|
+
case "second":
|
|
498
592
|
return 0;
|
|
499
593
|
default:
|
|
500
594
|
return 0;
|
|
@@ -503,59 +597,100 @@ function getMinValue(type: DateSegmentType): number {
|
|
|
503
597
|
|
|
504
598
|
function getMaxValue(type: DateSegmentType, date: DateValue): number {
|
|
505
599
|
switch (type) {
|
|
506
|
-
case
|
|
600
|
+
case "year":
|
|
507
601
|
return 9999;
|
|
508
|
-
case
|
|
602
|
+
case "month":
|
|
509
603
|
return 12;
|
|
510
|
-
case
|
|
604
|
+
case "day":
|
|
511
605
|
// Get days in month
|
|
512
|
-
return
|
|
513
|
-
case
|
|
606
|
+
return endOfMonth(date).day;
|
|
607
|
+
case "hour":
|
|
514
608
|
return 23;
|
|
515
|
-
case
|
|
609
|
+
case "minute":
|
|
516
610
|
return 59;
|
|
517
|
-
case
|
|
611
|
+
case "second":
|
|
518
612
|
return 59;
|
|
519
613
|
default:
|
|
520
614
|
return 0;
|
|
521
615
|
}
|
|
522
616
|
}
|
|
523
617
|
|
|
524
|
-
function getPlaceholderText(
|
|
618
|
+
function getPlaceholderText(
|
|
619
|
+
type: DateSegmentType,
|
|
620
|
+
locale: string,
|
|
621
|
+
granularity: "day" | "hour" | "minute" | "second",
|
|
622
|
+
shouldForceLeadingZeros = false,
|
|
623
|
+
): string {
|
|
624
|
+
if (type === "literal") return "";
|
|
625
|
+
if (type === "dayPeriod") return "AM";
|
|
626
|
+
|
|
627
|
+
const formatOptions: Intl.DateTimeFormatOptions = {
|
|
628
|
+
year: "numeric",
|
|
629
|
+
month: shouldForceLeadingZeros ? "2-digit" : "numeric",
|
|
630
|
+
day: shouldForceLeadingZeros ? "2-digit" : "numeric",
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
if (granularity !== "day") {
|
|
634
|
+
formatOptions.hour = "2-digit";
|
|
635
|
+
formatOptions.minute = "2-digit";
|
|
636
|
+
if (granularity === "second") {
|
|
637
|
+
formatOptions.second = "2-digit";
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const formatter = new DateFormatter(locale, formatOptions);
|
|
642
|
+
// Use a reference date with recognizable digits
|
|
643
|
+
const refDate = new Date(2222, 10, 22, 22, 22, 22);
|
|
644
|
+
const parts = formatter.formatToParts(refDate);
|
|
645
|
+
|
|
646
|
+
for (const part of parts) {
|
|
647
|
+
const mappedType = mapPartType(part.type);
|
|
648
|
+
if (mappedType === type) {
|
|
649
|
+
return part.value.replace(/\d/g, (d) => {
|
|
650
|
+
if (type === "year") return "y";
|
|
651
|
+
if (type === "month") return "m";
|
|
652
|
+
if (type === "day") return "d";
|
|
653
|
+
return "–";
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
525
658
|
switch (type) {
|
|
526
|
-
case
|
|
527
|
-
return
|
|
528
|
-
case
|
|
529
|
-
return
|
|
530
|
-
case
|
|
531
|
-
return
|
|
532
|
-
case
|
|
533
|
-
return
|
|
534
|
-
case
|
|
535
|
-
return
|
|
536
|
-
case
|
|
537
|
-
return
|
|
538
|
-
case 'dayPeriod':
|
|
539
|
-
return 'AM';
|
|
659
|
+
case "year":
|
|
660
|
+
return "yyyy";
|
|
661
|
+
case "month":
|
|
662
|
+
return "mm";
|
|
663
|
+
case "day":
|
|
664
|
+
return "dd";
|
|
665
|
+
case "hour":
|
|
666
|
+
return "––";
|
|
667
|
+
case "minute":
|
|
668
|
+
return "––";
|
|
669
|
+
case "second":
|
|
670
|
+
return "––";
|
|
540
671
|
default:
|
|
541
|
-
return
|
|
672
|
+
return "";
|
|
542
673
|
}
|
|
543
674
|
}
|
|
544
675
|
|
|
545
676
|
function updateDatePart(date: DateValue, type: DateSegmentType, value: number): DateValue {
|
|
677
|
+
if (typeof (date as { set?: unknown }).set !== "function") {
|
|
678
|
+
return date;
|
|
679
|
+
}
|
|
680
|
+
|
|
546
681
|
switch (type) {
|
|
547
|
-
case
|
|
548
|
-
return
|
|
549
|
-
case
|
|
550
|
-
return
|
|
551
|
-
case
|
|
552
|
-
return
|
|
553
|
-
case
|
|
554
|
-
return
|
|
555
|
-
case
|
|
556
|
-
return
|
|
557
|
-
case
|
|
558
|
-
return
|
|
682
|
+
case "year":
|
|
683
|
+
return date.set({ year: value });
|
|
684
|
+
case "month":
|
|
685
|
+
return date.set({ month: value });
|
|
686
|
+
case "day":
|
|
687
|
+
return date.set({ day: value });
|
|
688
|
+
case "hour":
|
|
689
|
+
return date.set({ hour: value });
|
|
690
|
+
case "minute":
|
|
691
|
+
return date.set({ minute: value });
|
|
692
|
+
case "second":
|
|
693
|
+
return date.set({ second: value });
|
|
559
694
|
default:
|
|
560
695
|
return date;
|
|
561
696
|
}
|