@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.
Files changed (242) hide show
  1. package/README.md +30 -283
  2. package/dist/autocomplete/createAutocompleteState.d.ts +2 -2
  3. package/dist/autocomplete/index.d.ts +1 -1
  4. package/dist/calendar/createCalendarState.d.ts +22 -7
  5. package/dist/calendar/createDateFieldState.d.ts +30 -7
  6. package/dist/calendar/createDatePickerState.d.ts +60 -0
  7. package/dist/calendar/createRangeCalendarState.d.ts +27 -8
  8. package/dist/calendar/createTimeFieldState.d.ts +34 -7
  9. package/dist/calendar/index.d.ts +7 -6
  10. package/dist/checkbox/createCheckboxGroupState.d.ts +6 -6
  11. package/dist/checkbox/index.d.ts +1 -1
  12. package/dist/collections/ListCollection.d.ts +1 -1
  13. package/dist/collections/createListState.d.ts +8 -8
  14. package/dist/collections/createMenuState.d.ts +7 -7
  15. package/dist/collections/createSelectionState.d.ts +5 -5
  16. package/dist/collections/index.d.ts +5 -5
  17. package/dist/collections/types.d.ts +12 -12
  18. package/dist/color/Color.d.ts +1 -1
  19. package/dist/color/createColorAreaState.d.ts +4 -2
  20. package/dist/color/createColorFieldState.d.ts +38 -12
  21. package/dist/color/createColorSliderState.d.ts +9 -2
  22. package/dist/color/createColorWheelState.d.ts +13 -2
  23. package/dist/color/getColorChannels.d.ts +1 -1
  24. package/dist/color/index.d.ts +6 -6
  25. package/dist/color/types.d.ts +4 -4
  26. package/dist/combobox/createComboBoxState.d.ts +27 -13
  27. package/dist/combobox/index.d.ts +1 -1
  28. package/dist/data/createAsyncList.d.ts +4 -4
  29. package/dist/data/createListData.d.ts +2 -2
  30. package/dist/data/createTreeData.d.ts +6 -2
  31. package/dist/data/index.d.ts +3 -3
  32. package/dist/datepicker/index.d.ts +2 -4
  33. package/dist/disclosure/createDisclosureState.d.ts +2 -2
  34. package/dist/disclosure/index.d.ts +1 -1
  35. package/dist/dnd/createDragState.d.ts +2 -2
  36. package/dist/dnd/createDraggableCollectionState.d.ts +2 -2
  37. package/dist/dnd/createDropState.d.ts +2 -2
  38. package/dist/dnd/createDroppableCollectionState.d.ts +3 -3
  39. package/dist/dnd/index.d.ts +5 -5
  40. package/dist/dnd/types.d.ts +16 -16
  41. package/dist/form/createFormValidationState.d.ts +3 -3
  42. package/dist/form/index.d.ts +1 -1
  43. package/dist/grid/createGridState.d.ts +2 -2
  44. package/dist/grid/index.d.ts +2 -2
  45. package/dist/grid/types.d.ts +13 -11
  46. package/dist/index.d.ts +31 -30
  47. package/dist/index.js +7924 -7238
  48. package/dist/index.js.map +1 -1
  49. package/dist/menu/index.d.ts +2 -2
  50. package/dist/numberfield/createNumberFieldState.d.ts +2 -2
  51. package/dist/numberfield/index.d.ts +1 -1
  52. package/dist/overlays/createOverlayTriggerState.d.ts +2 -2
  53. package/dist/overlays/index.d.ts +1 -1
  54. package/dist/radio/createRadioGroupState.d.ts +7 -7
  55. package/dist/radio/index.d.ts +1 -1
  56. package/dist/searchfield/createSearchFieldState.d.ts +2 -2
  57. package/dist/searchfield/index.d.ts +2 -2
  58. package/dist/select/createSelectState.d.ts +8 -8
  59. package/dist/select/index.d.ts +1 -1
  60. package/dist/selection/index.d.ts +5 -5
  61. package/dist/slider/createSliderState.d.ts +3 -3
  62. package/dist/slider/index.d.ts +2 -2
  63. package/dist/steplist/createStepListState.d.ts +48 -0
  64. package/dist/steplist/index.d.ts +1 -0
  65. package/dist/table/TableCollection.d.ts +3 -3
  66. package/dist/table/createTableColumnResizeState.d.ts +54 -0
  67. package/dist/table/createTableState.d.ts +2 -2
  68. package/dist/table/index.d.ts +5 -3
  69. package/dist/table/types.d.ts +10 -10
  70. package/dist/tabs/createTabListState.d.ts +5 -5
  71. package/dist/tabs/index.d.ts +1 -1
  72. package/dist/textfield/createTextFieldState.d.ts +2 -2
  73. package/dist/textfield/index.d.ts +1 -1
  74. package/dist/toast/createToastState.d.ts +8 -2
  75. package/dist/toast/index.d.ts +1 -1
  76. package/dist/toggle/createToggleGroupState.d.ts +4 -4
  77. package/dist/toggle/createToggleState.d.ts +2 -2
  78. package/dist/toggle/index.d.ts +2 -2
  79. package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
  80. package/dist/tooltip/index.d.ts +1 -1
  81. package/dist/tree/TreeCollection.d.ts +2 -2
  82. package/dist/tree/createTreeState.d.ts +2 -2
  83. package/dist/tree/index.d.ts +3 -3
  84. package/dist/tree/types.d.ts +19 -13
  85. package/dist/utils/index.d.ts +1 -1
  86. package/dist/utils/reactivity.d.ts +1 -1
  87. package/package.json +29 -30
  88. package/src/autocomplete/createAutocompleteState.d.ts +19 -17
  89. package/src/autocomplete/createAutocompleteState.ts +24 -30
  90. package/src/autocomplete/index.d.ts +6 -2
  91. package/src/autocomplete/index.ts +1 -1
  92. package/src/calendar/createCalendarState.d.ts +139 -117
  93. package/src/calendar/createCalendarState.ts +221 -74
  94. package/src/calendar/createDateFieldState.d.ts +130 -95
  95. package/src/calendar/createDateFieldState.ts +263 -151
  96. package/src/calendar/createDatePickerState.ts +347 -0
  97. package/src/calendar/createRangeCalendarState.d.ts +154 -129
  98. package/src/calendar/createRangeCalendarState.ts +293 -79
  99. package/src/calendar/createTimeFieldState.d.ts +81 -79
  100. package/src/calendar/createTimeFieldState.ts +187 -108
  101. package/src/calendar/index.d.ts +80 -7
  102. package/src/calendar/index.ts +21 -12
  103. package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
  104. package/src/checkbox/createCheckboxGroupState.ts +20 -29
  105. package/src/checkbox/index.d.ts +6 -2
  106. package/src/checkbox/index.ts +1 -1
  107. package/src/collections/ListCollection.d.ts +25 -22
  108. package/src/collections/ListCollection.ts +6 -6
  109. package/src/collections/createListState.d.ts +73 -58
  110. package/src/collections/createListState.ts +19 -18
  111. package/src/collections/createMenuState.d.ts +36 -31
  112. package/src/collections/createMenuState.ts +18 -12
  113. package/src/collections/createSelectionState.d.ts +78 -65
  114. package/src/collections/createSelectionState.ts +36 -32
  115. package/src/collections/index.d.ts +38 -6
  116. package/src/collections/index.ts +5 -10
  117. package/src/collections/types.d.ts +101 -101
  118. package/src/collections/types.ts +12 -12
  119. package/src/color/Color.d.ts +20 -5
  120. package/src/color/Color.ts +357 -254
  121. package/src/color/createColorAreaState.d.ts +67 -65
  122. package/src/color/createColorAreaState.ts +21 -13
  123. package/src/color/createColorFieldState.d.ts +46 -44
  124. package/src/color/createColorFieldState.ts +285 -149
  125. package/src/color/createColorSliderState.d.ts +58 -56
  126. package/src/color/createColorSliderState.ts +45 -14
  127. package/src/color/createColorWheelState.d.ts +42 -40
  128. package/src/color/createColorWheelState.ts +101 -64
  129. package/src/color/getColorChannels.ts +7 -5
  130. package/src/color/index.d.ts +36 -7
  131. package/src/color/index.ts +6 -9
  132. package/src/color/types.d.ts +68 -63
  133. package/src/color/types.ts +11 -19
  134. package/src/combobox/createComboBoxState.d.ts +107 -105
  135. package/src/combobox/createComboBoxState.ts +176 -89
  136. package/src/combobox/index.d.ts +10 -2
  137. package/src/combobox/index.ts +1 -1
  138. package/src/data/createAsyncList.ts +89 -63
  139. package/src/data/createListData.ts +67 -67
  140. package/src/data/createTreeData.ts +304 -91
  141. package/src/data/index.ts +3 -3
  142. package/src/datepicker/index.ts +6 -10
  143. package/src/disclosure/createDisclosureState.d.ts +47 -43
  144. package/src/disclosure/createDisclosureState.ts +7 -21
  145. package/src/disclosure/index.d.ts +10 -2
  146. package/src/disclosure/index.ts +1 -1
  147. package/src/dnd/createDragState.d.ts +51 -42
  148. package/src/dnd/createDragState.ts +12 -14
  149. package/src/dnd/createDraggableCollectionState.d.ts +52 -41
  150. package/src/dnd/createDraggableCollectionState.ts +14 -21
  151. package/src/dnd/createDropState.d.ts +64 -45
  152. package/src/dnd/createDropState.ts +22 -32
  153. package/src/dnd/createDroppableCollectionState.d.ts +88 -59
  154. package/src/dnd/createDroppableCollectionState.ts +40 -43
  155. package/src/dnd/index.d.ts +54 -6
  156. package/src/dnd/index.ts +5 -18
  157. package/src/dnd/types.d.ts +167 -156
  158. package/src/dnd/types.ts +20 -21
  159. package/src/form/createFormValidationState.d.ts +47 -45
  160. package/src/form/createFormValidationState.ts +30 -63
  161. package/src/form/index.d.ts +16 -2
  162. package/src/form/index.ts +1 -1
  163. package/src/grid/createGridState.d.ts +7 -4
  164. package/src/grid/createGridState.ts +44 -51
  165. package/src/grid/index.d.ts +3 -3
  166. package/src/grid/index.ts +2 -8
  167. package/src/grid/types.d.ts +136 -129
  168. package/src/grid/types.ts +20 -18
  169. package/src/index.d.ts +310 -26
  170. package/src/index.ts +48 -89
  171. package/src/menu/index.ts +2 -4
  172. package/src/numberfield/createNumberFieldState.d.ts +56 -54
  173. package/src/numberfield/createNumberFieldState.ts +26 -31
  174. package/src/numberfield/index.d.ts +6 -2
  175. package/src/numberfield/index.ts +1 -1
  176. package/src/overlays/createOverlayTriggerState.d.ts +22 -20
  177. package/src/overlays/createOverlayTriggerState.ts +3 -4
  178. package/src/overlays/index.d.ts +6 -2
  179. package/src/overlays/index.ts +1 -1
  180. package/src/radio/createRadioGroupState.d.ts +54 -52
  181. package/src/radio/createRadioGroupState.ts +27 -29
  182. package/src/radio/index.d.ts +7 -2
  183. package/src/radio/index.ts +1 -1
  184. package/src/searchfield/createSearchFieldState.d.ts +16 -14
  185. package/src/searchfield/createSearchFieldState.ts +5 -11
  186. package/src/searchfield/index.d.ts +3 -3
  187. package/src/searchfield/index.ts +2 -5
  188. package/src/select/createSelectState.d.ts +63 -61
  189. package/src/select/createSelectState.ts +27 -27
  190. package/src/select/index.d.ts +2 -2
  191. package/src/select/index.ts +1 -5
  192. package/src/selection/index.ts +6 -7
  193. package/src/slider/createSliderState.d.ts +60 -60
  194. package/src/slider/createSliderState.ts +55 -33
  195. package/src/slider/index.d.ts +3 -3
  196. package/src/slider/index.ts +2 -6
  197. package/src/ssr/index.d.ts +1 -1
  198. package/src/ssr/index.ts +2 -2
  199. package/src/steplist/createStepListState.ts +191 -0
  200. package/src/steplist/index.ts +5 -0
  201. package/src/table/TableCollection.d.ts +42 -40
  202. package/src/table/TableCollection.ts +61 -64
  203. package/src/table/createTableColumnResizeState.ts +242 -0
  204. package/src/table/createTableState.d.ts +7 -4
  205. package/src/table/createTableState.ts +10 -7
  206. package/src/table/index.d.ts +14 -4
  207. package/src/table/index.ts +10 -3
  208. package/src/table/types.d.ts +98 -92
  209. package/src/table/types.ts +17 -13
  210. package/src/tabs/createTabListState.d.ts +59 -57
  211. package/src/tabs/createTabListState.ts +24 -31
  212. package/src/tabs/index.d.ts +8 -2
  213. package/src/tabs/index.ts +1 -1
  214. package/src/textfield/createTextFieldState.d.ts +16 -14
  215. package/src/textfield/createTextFieldState.ts +7 -18
  216. package/src/textfield/index.d.ts +6 -2
  217. package/src/textfield/index.ts +1 -1
  218. package/src/toast/createToastState.d.ts +92 -86
  219. package/src/toast/createToastState.ts +48 -43
  220. package/src/toast/index.d.ts +12 -2
  221. package/src/toast/index.ts +1 -1
  222. package/src/toggle/createToggleGroupState.ts +11 -21
  223. package/src/toggle/createToggleState.d.ts +19 -19
  224. package/src/toggle/createToggleState.ts +2 -16
  225. package/src/toggle/index.d.ts +2 -2
  226. package/src/toggle/index.ts +2 -6
  227. package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
  228. package/src/tooltip/createTooltipTriggerState.ts +12 -11
  229. package/src/tooltip/index.d.ts +7 -2
  230. package/src/tooltip/index.ts +1 -1
  231. package/src/tree/TreeCollection.d.ts +26 -23
  232. package/src/tree/TreeCollection.ts +29 -22
  233. package/src/tree/createTreeState.d.ts +7 -4
  234. package/src/tree/createTreeState.ts +39 -41
  235. package/src/tree/index.d.ts +10 -4
  236. package/src/tree/index.ts +3 -3
  237. package/src/tree/types.d.ts +131 -126
  238. package/src/tree/types.ts +22 -13
  239. package/src/utils/index.d.ts +2 -2
  240. package/src/utils/index.ts +1 -1
  241. package/src/utils/reactivity.d.ts +2 -2
  242. 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 'solid-js';
8
+ import { createSignal, createMemo, type Accessor } from "solid-js";
9
9
  import {
10
- type CalendarDate,
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
- } from '@internationalized/date';
20
- import { access, type MaybeAccessor } from '../utils';
21
- import type { ValidationState } from './createCalendarState';
22
-
23
- // ============================================
24
- // TYPES
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
- | 'year'
29
- | 'month'
30
- | 'day'
31
- | 'hour'
32
- | 'minute'
33
- | 'second'
34
- | 'dayPeriod'
35
- | 'era'
36
- | 'timeZoneName'
37
- | 'literal';
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?: 'day' | 'hour' | 'minute' | 'second';
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: 'day' | 'hour' | 'minute' | 'second';
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 ?? 'en-US';
148
- const granularity = props.granularity ?? 'day';
170
+ const locale = props.locale ?? "en-US";
171
+ const granularity = props.granularity ?? "day";
149
172
 
150
173
  // State signals
151
- const [internalValue, setInternalValue] = createSignal<T | null>(
152
- props.defaultValue ?? null
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 === 'day') {
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 validationState = createMemo(() => access(props.validationState));
204
-
205
- // Check if value is invalid
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 false;
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 (minValue && v.compare(minValue) < 0) return true;
214
- if (maxValue && v.compare(maxValue) > 0) return true;
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
- return validationState() === 'invalid';
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: 'numeric',
230
- month: '2-digit',
231
- day: '2-digit',
298
+ year: "numeric",
299
+ month: shouldForceLeadingZeros ? "2-digit" : "numeric",
300
+ day: shouldForceLeadingZeros ? "2-digit" : "numeric",
232
301
  };
233
302
 
234
- if (granularity !== 'day') {
235
- formatOptions.hour = '2-digit';
236
- formatOptions.minute = '2-digit';
237
- if (granularity === 'second') {
238
- formatOptions.second = '2-digit';
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 ? 'h12' : 'h23';
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 === 'literal') {
321
+ if (type === "literal") {
253
322
  segs.push({
254
- type: 'literal',
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 ? part.value : getPlaceholderText(type),
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
- // Clear entire value if any segment is cleared
355
- setValue(null);
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
- setValue(constrain(dv) as T);
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: 'numeric',
406
- month: '2-digit',
407
- day: '2-digit',
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 !== 'day') {
411
- options.hour = '2-digit';
412
- options.minute = '2-digit';
413
- if (granularity === 'second') {
414
- options.second = '2-digit';
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 'year':
460
- return 'year';
461
- case 'month':
462
- return 'month';
463
- case 'day':
464
- return 'day';
465
- case 'hour':
466
- return 'hour';
467
- case 'minute':
468
- return 'minute';
469
- case 'second':
470
- return 'second';
471
- case 'dayPeriod':
472
- return 'dayPeriod';
473
- case 'era':
474
- return 'era';
475
- case 'timeZoneName':
476
- return 'timeZoneName';
477
- case 'literal':
478
- return 'literal';
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 'year':
562
+ case "year":
492
563
  return date.year;
493
- case 'month':
564
+ case "month":
494
565
  return date.month;
495
- case 'day':
566
+ case "day":
496
567
  return date.day;
497
- case 'hour':
498
- return 'hour' in date ? (date as CalendarDateTime).hour : undefined;
499
- case 'minute':
500
- return 'minute' in date ? (date as CalendarDateTime).minute : undefined;
501
- case 'second':
502
- return 'second' in date ? (date as CalendarDateTime).second : undefined;
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 'year':
581
+ case "year":
511
582
  return 1;
512
- case 'month':
583
+ case "month":
513
584
  return 1;
514
- case 'day':
585
+ case "day":
515
586
  return 1;
516
- case 'hour':
587
+ case "hour":
517
588
  return 0;
518
- case 'minute':
589
+ case "minute":
519
590
  return 0;
520
- case 'second':
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 'year':
600
+ case "year":
530
601
  return 9999;
531
- case 'month':
602
+ case "month":
532
603
  return 12;
533
- case 'day':
604
+ case "day":
534
605
  // Get days in month
535
- return date.calendar.getDaysInMonth?.(date) ?? 31;
536
- case 'hour':
606
+ return endOfMonth(date).day;
607
+ case "hour":
537
608
  return 23;
538
- case 'minute':
609
+ case "minute":
539
610
  return 59;
540
- case 'second':
611
+ case "second":
541
612
  return 59;
542
613
  default:
543
614
  return 0;
544
615
  }
545
616
  }
546
617
 
547
- function getPlaceholderText(type: DateSegmentType): string {
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 'year':
550
- return 'yyyy';
551
- case 'month':
552
- return 'mm';
553
- case 'day':
554
- return 'dd';
555
- case 'hour':
556
- return '––';
557
- case 'minute':
558
- return '––';
559
- case 'second':
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 'year':
571
- return (date as CalendarDate).set({ year: value });
572
- case 'month':
573
- return (date as CalendarDate).set({ month: value });
574
- case 'day':
575
- return (date as CalendarDate).set({ day: value });
576
- case 'hour':
577
- return (date as CalendarDateTime).set({ hour: value });
578
- case 'minute':
579
- return (date as CalendarDateTime).set({ minute: value });
580
- case 'second':
581
- return (date as CalendarDateTime).set({ second: value });
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
  }