@proyecto-viviana/solid-stately 0.2.7 → 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/README.md +30 -283
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -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 +6 -6
- 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 +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 +27 -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 +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 +13 -11
- package/dist/index.d.ts +31 -30
- package/dist/index.js +7924 -7238
- 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 +8 -8
- package/dist/select/index.d.ts +1 -1
- package/dist/selection/index.d.ts +5 -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 +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 +8 -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 +19 -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/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 +263 -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 +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 +19 -18
- package/src/collections/createMenuState.d.ts +36 -31
- package/src/collections/createMenuState.ts +18 -12
- package/src/collections/createSelectionState.d.ts +78 -65
- package/src/collections/createSelectionState.ts +36 -32
- package/src/collections/index.d.ts +38 -6
- package/src/collections/index.ts +5 -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 +176 -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 +54 -6
- package/src/dnd/index.ts +5 -18
- package/src/dnd/types.d.ts +167 -156
- package/src/dnd/types.ts +20 -21
- 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 +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 +20 -18
- package/src/index.d.ts +310 -26
- package/src/index.ts +48 -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 +27 -27
- package/src/select/index.d.ts +2 -2
- package/src/select/index.ts +1 -5
- package/src/selection/index.ts +6 -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 +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 +24 -31
- 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 +48 -43
- package/src/toast/index.d.ts +12 -2
- package/src/toast/index.ts +1 -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 +39 -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 +22 -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
|
@@ -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
|
+
}
|
|
212
240
|
|
|
213
|
-
if (
|
|
214
|
-
|
|
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
|
+
}
|
|
215
250
|
|
|
216
|
-
|
|
251
|
+
if (validationErrors.length === 0) {
|
|
252
|
+
return DEFAULT_VALIDATION_RESULT;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
validationDetails.valid = false;
|
|
256
|
+
return {
|
|
257
|
+
isInvalid: true,
|
|
258
|
+
validationDetails,
|
|
259
|
+
validationErrors,
|
|
260
|
+
};
|
|
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
|
+
},
|
|
217
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 };
|
|
@@ -382,7 +451,9 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
382
451
|
if (Object.keys(parts).length > 0) {
|
|
383
452
|
const dv = dateValue();
|
|
384
453
|
if (dv) {
|
|
385
|
-
|
|
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);
|
|
386
457
|
}
|
|
387
458
|
return;
|
|
388
459
|
}
|
|
@@ -390,6 +461,7 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
390
461
|
const current = value();
|
|
391
462
|
if (current) {
|
|
392
463
|
const constrained = constrain(current);
|
|
464
|
+
// Apply min/max constraints even if unavailable; isInvalid will flag unavailable dates
|
|
393
465
|
if (constrained.compare(current) !== 0) {
|
|
394
466
|
setValue(constrained as T);
|
|
395
467
|
}
|
|
@@ -399,19 +471,19 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
399
471
|
// Format the current value
|
|
400
472
|
const formatValue = (fieldOptions?: Intl.DateTimeFormatOptions): string => {
|
|
401
473
|
const v = value();
|
|
402
|
-
if (!v) return
|
|
474
|
+
if (!v) return "";
|
|
403
475
|
|
|
404
476
|
const options: Intl.DateTimeFormatOptions = fieldOptions ?? {
|
|
405
|
-
year:
|
|
406
|
-
month:
|
|
407
|
-
day:
|
|
477
|
+
year: "numeric",
|
|
478
|
+
month: props.shouldForceLeadingZeros === true ? "2-digit" : "numeric",
|
|
479
|
+
day: props.shouldForceLeadingZeros === true ? "2-digit" : "numeric",
|
|
408
480
|
};
|
|
409
481
|
|
|
410
|
-
if (granularity !==
|
|
411
|
-
options.hour =
|
|
412
|
-
options.minute =
|
|
413
|
-
if (granularity ===
|
|
414
|
-
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";
|
|
415
487
|
}
|
|
416
488
|
}
|
|
417
489
|
|
|
@@ -421,6 +493,7 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
421
493
|
|
|
422
494
|
return {
|
|
423
495
|
value,
|
|
496
|
+
defaultValue: initialValue,
|
|
424
497
|
dateValue,
|
|
425
498
|
setValue,
|
|
426
499
|
segments,
|
|
@@ -436,15 +509,16 @@ export function createDateFieldState<T extends DateValue = CalendarDate>(
|
|
|
436
509
|
validationState,
|
|
437
510
|
granularity,
|
|
438
511
|
isInvalid,
|
|
512
|
+
realtimeValidation: validation.realtimeValidation,
|
|
513
|
+
displayValidation: validation.displayValidation,
|
|
514
|
+
updateValidation: validation.updateValidation,
|
|
515
|
+
resetValidation: validation.resetValidation,
|
|
516
|
+
commitValidation: validation.commitValidation,
|
|
439
517
|
locale,
|
|
440
518
|
timeZone,
|
|
441
519
|
};
|
|
442
520
|
}
|
|
443
521
|
|
|
444
|
-
// ============================================
|
|
445
|
-
// HELPER TYPES & FUNCTIONS
|
|
446
|
-
// ============================================
|
|
447
|
-
|
|
448
522
|
interface DateParts {
|
|
449
523
|
year?: number;
|
|
450
524
|
month?: number;
|
|
@@ -456,50 +530,47 @@ interface DateParts {
|
|
|
456
530
|
|
|
457
531
|
function mapPartType(type: Intl.DateTimeFormatPartTypes): DateSegmentType | null {
|
|
458
532
|
switch (type) {
|
|
459
|
-
case
|
|
460
|
-
return
|
|
461
|
-
case
|
|
462
|
-
return
|
|
463
|
-
case
|
|
464
|
-
return
|
|
465
|
-
case
|
|
466
|
-
return
|
|
467
|
-
case
|
|
468
|
-
return
|
|
469
|
-
case
|
|
470
|
-
return
|
|
471
|
-
case
|
|
472
|
-
return
|
|
473
|
-
case
|
|
474
|
-
return
|
|
475
|
-
case
|
|
476
|
-
return
|
|
477
|
-
case
|
|
478
|
-
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";
|
|
479
553
|
default:
|
|
480
554
|
return null;
|
|
481
555
|
}
|
|
482
556
|
}
|
|
483
557
|
|
|
484
|
-
function getSegmentValue(
|
|
485
|
-
date: DateValue | null,
|
|
486
|
-
type: DateSegmentType
|
|
487
|
-
): number | undefined {
|
|
558
|
+
function getSegmentValue(date: DateValue | null, type: DateSegmentType): number | undefined {
|
|
488
559
|
if (!date) return undefined;
|
|
489
560
|
|
|
490
561
|
switch (type) {
|
|
491
|
-
case
|
|
562
|
+
case "year":
|
|
492
563
|
return date.year;
|
|
493
|
-
case
|
|
564
|
+
case "month":
|
|
494
565
|
return date.month;
|
|
495
|
-
case
|
|
566
|
+
case "day":
|
|
496
567
|
return date.day;
|
|
497
|
-
case
|
|
498
|
-
return
|
|
499
|
-
case
|
|
500
|
-
return
|
|
501
|
-
case
|
|
502
|
-
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;
|
|
503
574
|
default:
|
|
504
575
|
return undefined;
|
|
505
576
|
}
|
|
@@ -507,17 +578,17 @@ function getSegmentValue(
|
|
|
507
578
|
|
|
508
579
|
function getMinValue(type: DateSegmentType): number {
|
|
509
580
|
switch (type) {
|
|
510
|
-
case
|
|
581
|
+
case "year":
|
|
511
582
|
return 1;
|
|
512
|
-
case
|
|
583
|
+
case "month":
|
|
513
584
|
return 1;
|
|
514
|
-
case
|
|
585
|
+
case "day":
|
|
515
586
|
return 1;
|
|
516
|
-
case
|
|
587
|
+
case "hour":
|
|
517
588
|
return 0;
|
|
518
|
-
case
|
|
589
|
+
case "minute":
|
|
519
590
|
return 0;
|
|
520
|
-
case
|
|
591
|
+
case "second":
|
|
521
592
|
return 0;
|
|
522
593
|
default:
|
|
523
594
|
return 0;
|
|
@@ -526,59 +597,100 @@ function getMinValue(type: DateSegmentType): number {
|
|
|
526
597
|
|
|
527
598
|
function getMaxValue(type: DateSegmentType, date: DateValue): number {
|
|
528
599
|
switch (type) {
|
|
529
|
-
case
|
|
600
|
+
case "year":
|
|
530
601
|
return 9999;
|
|
531
|
-
case
|
|
602
|
+
case "month":
|
|
532
603
|
return 12;
|
|
533
|
-
case
|
|
604
|
+
case "day":
|
|
534
605
|
// Get days in month
|
|
535
|
-
return
|
|
536
|
-
case
|
|
606
|
+
return endOfMonth(date).day;
|
|
607
|
+
case "hour":
|
|
537
608
|
return 23;
|
|
538
|
-
case
|
|
609
|
+
case "minute":
|
|
539
610
|
return 59;
|
|
540
|
-
case
|
|
611
|
+
case "second":
|
|
541
612
|
return 59;
|
|
542
613
|
default:
|
|
543
614
|
return 0;
|
|
544
615
|
}
|
|
545
616
|
}
|
|
546
617
|
|
|
547
|
-
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
|
+
|
|
548
658
|
switch (type) {
|
|
549
|
-
case
|
|
550
|
-
return
|
|
551
|
-
case
|
|
552
|
-
return
|
|
553
|
-
case
|
|
554
|
-
return
|
|
555
|
-
case
|
|
556
|
-
return
|
|
557
|
-
case
|
|
558
|
-
return
|
|
559
|
-
case
|
|
560
|
-
return
|
|
561
|
-
case 'dayPeriod':
|
|
562
|
-
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 "––";
|
|
563
671
|
default:
|
|
564
|
-
return
|
|
672
|
+
return "";
|
|
565
673
|
}
|
|
566
674
|
}
|
|
567
675
|
|
|
568
676
|
function updateDatePart(date: DateValue, type: DateSegmentType, value: number): DateValue {
|
|
677
|
+
if (typeof (date as { set?: unknown }).set !== "function") {
|
|
678
|
+
return date;
|
|
679
|
+
}
|
|
680
|
+
|
|
569
681
|
switch (type) {
|
|
570
|
-
case
|
|
571
|
-
return
|
|
572
|
-
case
|
|
573
|
-
return
|
|
574
|
-
case
|
|
575
|
-
return
|
|
576
|
-
case
|
|
577
|
-
return
|
|
578
|
-
case
|
|
579
|
-
return
|
|
580
|
-
case
|
|
581
|
-
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 });
|
|
582
694
|
default:
|
|
583
695
|
return date;
|
|
584
696
|
}
|