ropav 0.12.0 → 0.12.2

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 (61) hide show
  1. package/AGENTS.md +150 -0
  2. package/README.md +5 -1
  3. package/dist/components/breadcrumbs/breadcrumbs-item.js +2 -0
  4. package/dist/components/calendar-year-picker/calendar-year-picker-grid.js +1 -0
  5. package/dist/components/checkbox/checkbox-root.js +2 -0
  6. package/dist/components/combo-box/combo-box-root.js +1 -0
  7. package/dist/components/date-input-group/date-input-group-hidden-input.js +2 -1
  8. package/dist/components/date-picker/date-picker-root.js +1 -0
  9. package/dist/components/date-range-picker/date-range-picker-root.js +1 -0
  10. package/dist/components/form/form-root.js +7 -1
  11. package/dist/components/radio-group/radio-group-root.js +2 -1
  12. package/dist/components/search-field/search-field-group.js +21 -1
  13. package/dist/components/select/select-root.js +1 -0
  14. package/dist/components/splitter/splitter.state.js +6 -1
  15. package/dist/components/switch/switch-root.js +2 -0
  16. package/dist/components/table/table-body.js +1 -1
  17. package/dist/components/table/table-cell.js +3 -1
  18. package/dist/components/table/table-column-resizer.js +2 -1
  19. package/dist/components/table/table-load-more.js +1 -1
  20. package/dist/components/table/use-grid-selection-announcement.js +3 -1
  21. package/dist/components/table/use-table-collection.js +3 -0
  22. package/dist/components/tabs/tabs-list-container.js +2 -1
  23. package/dist/composables/use-calendar-grid.js +2 -1
  24. package/dist/composables/use-checkbox-group-state.js +2 -0
  25. package/dist/composables/use-collection.js +12 -0
  26. package/dist/composables/use-color-field-state.js +1 -0
  27. package/dist/composables/use-color-field.js +31 -29
  28. package/dist/composables/use-combo-box-state.d.ts +2 -0
  29. package/dist/composables/use-combo-box-state.js +5 -2
  30. package/dist/composables/use-combo-box.js +2 -1
  31. package/dist/composables/use-date-field-state.js +1 -0
  32. package/dist/composables/use-date-picker-state.d.ts +2 -0
  33. package/dist/composables/use-date-picker-state.js +1 -0
  34. package/dist/composables/use-date-range-picker-state.d.ts +2 -0
  35. package/dist/composables/use-date-range-picker-state.js +2 -0
  36. package/dist/composables/use-description.js +1 -0
  37. package/dist/composables/use-disclosure-panel.js +2 -1
  38. package/dist/composables/use-drag.js +9 -6
  39. package/dist/composables/use-drop.js +2 -1
  40. package/dist/composables/use-droppable-collection.js +2 -1
  41. package/dist/composables/use-form-validation-state.d.ts +48 -5
  42. package/dist/composables/use-form-validation-state.js +76 -7
  43. package/dist/composables/use-input-otp.js +5 -2
  44. package/dist/composables/use-move.js +2 -1
  45. package/dist/composables/use-number-field-state.js +1 -0
  46. package/dist/composables/use-radio-group-state.js +2 -0
  47. package/dist/composables/use-select-state.d.ts +2 -0
  48. package/dist/composables/use-select-state.js +2 -0
  49. package/dist/composables/use-single-select-list-state.js +1 -0
  50. package/dist/composables/use-text-field.js +1 -0
  51. package/dist/composables/use-time-field-state.js +2 -1
  52. package/dist/composables/use-toolbar.js +3 -1
  53. package/dist/composables/use-virtualizer.js +1 -0
  54. package/dist/index.js +2 -2
  55. package/dist/ropav.min.css +1 -1
  56. package/dist/utils/create-context.js +2 -1
  57. package/dist/utils/platform.js +2 -1
  58. package/dist/utils/position.js +4 -2
  59. package/dist/version.js +1 -1
  60. package/package.json +4 -7
  61. package/dist/styles-compat-0.10.css +0 -3
@@ -176,7 +176,8 @@ var useComboBox = (options, state) => {
176
176
  },
177
177
  onPressStart: (event) => {
178
178
  if (event.pointerType === "touch") return;
179
- state.toggle(event.pointerType === "keyboard" || event.pointerType === "virtual" ? "first" : null, "manual");
179
+ const strategy = event.pointerType === "keyboard" || event.pointerType === "virtual" ? "first" : null;
180
+ state.toggle(strategy, "manual");
180
181
  focusInput();
181
182
  },
182
183
  preventFocusOnPress: true
@@ -170,6 +170,7 @@ var useDateFieldState = (options) => {
170
170
  const validation = useFormValidationState({
171
171
  builtinValidation,
172
172
  isInvalid: () => toValue(options.isInvalid),
173
+ isRequired,
173
174
  name: () => toValue(options.name),
174
175
  validate: () => options.validate,
175
176
  validationBehavior: () => toValue(options.validationBehavior),
@@ -24,6 +24,8 @@ export interface UseDatePickerStateOptions {
24
24
  hideTimeZone?: MaybeRefOrGetter<boolean | undefined>;
25
25
  shouldForceLeadingZeros?: MaybeRefOrGetter<boolean | undefined>;
26
26
  isInvalid?: MaybeRefOrGetter<boolean | undefined>;
27
+ /** Whether the field has to hold a value. Enforced by the validation state under `"aria"`. */
28
+ isRequired?: MaybeRefOrGetter<boolean | undefined>;
27
29
  validate?: (value: DateValue | null) => string | string[] | true | null | undefined;
28
30
  validationBehavior?: MaybeRefOrGetter<ValidationBehavior | undefined>;
29
31
  name?: MaybeRefOrGetter<string | undefined>;
@@ -54,6 +54,7 @@ var useDatePickerState = (options) => {
54
54
  const validation = useFormValidationState({
55
55
  builtinValidation: computed(() => getDateValidationResult(value.value, toValue(options.minValue), toValue(options.maxValue), options.isDateUnavailable, formatOpts.value)),
56
56
  isInvalid: () => toValue(options.isInvalid),
57
+ isRequired: () => toValue(options.isRequired),
57
58
  name: () => toValue(options.name),
58
59
  validate: () => options.validate,
59
60
  validationBehavior: () => toValue(options.validationBehavior),
@@ -35,6 +35,8 @@ export interface UseDateRangePickerStateOptions {
35
35
  hideTimeZone?: MaybeRefOrGetter<boolean | undefined>;
36
36
  shouldForceLeadingZeros?: MaybeRefOrGetter<boolean | undefined>;
37
37
  isInvalid?: MaybeRefOrGetter<boolean | undefined>;
38
+ /** Whether the field has to hold a value. Enforced by the validation state under `"aria"`. */
39
+ isRequired?: MaybeRefOrGetter<boolean | undefined>;
38
40
  validate?: (value: DateRange | null) => string | string[] | true | null | undefined;
39
41
  validationBehavior?: MaybeRefOrGetter<ValidationBehavior | undefined>;
40
42
  /** Name the start of the range is submitted under. */
@@ -78,7 +78,9 @@ var useDateRangePickerState = (options) => {
78
78
  }));
79
79
  const validation = useFormValidationState({
80
80
  builtinValidation: computed(() => getRangeValidationResult(value.value, toValue(options.minValue), toValue(options.maxValue), options.isDateUnavailable, formatOpts.value)),
81
+ isEmpty: (range) => !range?.start || !range.end,
81
82
  isInvalid: () => toValue(options.isInvalid),
83
+ isRequired: () => toValue(options.isRequired),
82
84
  name: () => [toValue(options.startName), toValue(options.endName)].filter(Boolean),
83
85
  validate: () => options.validate,
84
86
  validationBehavior: () => toValue(options.validationBehavior),
@@ -33,6 +33,7 @@ var useDescription = (description) => {
33
33
  id.value = void 0;
34
34
  return;
35
35
  }
36
+ if (typeof document === "undefined") return;
36
37
  let node = nodes.get(text);
37
38
  if (!node) {
38
39
  const element = document.createElement("div");
@@ -34,7 +34,8 @@ var useDisclosurePanel = (options) => {
34
34
  panel.style.setProperty("--disclosure-panel-height", height);
35
35
  };
36
36
  const applyPanelState = (panel, expanded) => {
37
- if (wasExpanded === null || !(typeof panel.getAnimations === "function")) {
37
+ const canAnimate = typeof panel.getAnimations === "function";
38
+ if (wasExpanded === null || !canAnimate) {
38
39
  if (expanded) {
39
40
  panel.removeAttribute("hidden");
40
41
  setSize(panel, "auto", "auto");
@@ -160,12 +160,15 @@ var useDrag = (options) => {
160
160
  };
161
161
  const onDragend = (event) => {
162
162
  event.stopPropagation();
163
- if (options.onDragEnd) options.onDragEnd({
164
- dropOperation: DROP_EFFECT_TO_DROP_OPERATION[globalDropEffect ?? event.dataTransfer?.dropEffect ?? "none"] ?? "cancel",
165
- type: "dragend",
166
- x: event.clientX,
167
- y: event.clientY
168
- });
163
+ if (options.onDragEnd) {
164
+ const dropEffect = event.dataTransfer?.dropEffect ?? "none";
165
+ options.onDragEnd({
166
+ dropOperation: DROP_EFFECT_TO_DROP_OPERATION[globalDropEffect ?? dropEffect] ?? "cancel",
167
+ type: "dragend",
168
+ x: event.clientX,
169
+ y: event.clientY
170
+ });
171
+ }
169
172
  draggingElement.value = null;
170
173
  releaseDropGuard?.();
171
174
  releaseDropGuard = null;
@@ -14,7 +14,8 @@ var DROP_ACTIVATE_TIMEOUT = 800;
14
14
  * Ported from React Aria's `getAllowedOperations`.
15
15
  */
16
16
  var getAllowedOperations = (event) => {
17
- let allowed = DROP_OPERATION_ALLOWED[event.dataTransfer?.effectAllowed ?? "uninitialized"] ?? DROP_OPERATION.all;
17
+ const effectAllowed = event.dataTransfer?.effectAllowed ?? "uninitialized";
18
+ let allowed = DROP_OPERATION_ALLOWED[effectAllowed] ?? DROP_OPERATION.all;
18
19
  if (globalAllowedDropOperations) allowed &= globalAllowedDropOperations;
19
20
  let allowedModifiers = DROP_OPERATION.none;
20
21
  if (isMac()) {
@@ -100,7 +100,8 @@ var useDroppableCollection = (options, state, element) => {
100
100
  if (inserted.length > 0) {
101
101
  selectionManager.setSelectedKeys(inserted);
102
102
  if (selectionManager.focusedKey.value === focusedKey) {
103
- selectionManager.setFocusedKey(inserted[0]);
103
+ const first = inserted[0];
104
+ selectionManager.setFocusedKey(first);
104
105
  if (selectionManager.selectionMode.value === "none") setInteractionModality("keyboard");
105
106
  }
106
107
  }
@@ -1,4 +1,4 @@
1
- import type { ComputedRef, MaybeRefOrGetter } from "vue";
1
+ import type { ComputedRef, MaybeRefOrGetter, Ref } from "vue";
2
2
  /**
3
3
  * Snapshot of an element's `ValidityState`.
4
4
  *
@@ -39,6 +39,8 @@ export type ValidationFunction<T> = (value: T) => string | string[] | true | nul
39
39
  export declare const VALID_VALIDITY_STATE: ValidationDetails;
40
40
  /** Failure that came from a prop, a `validate` function or the server rather than the browser. */
41
41
  export declare const CUSTOM_VALIDITY_STATE: ValidationDetails;
42
+ /** A required field with nothing in it. What the browser reports under `"native"`. */
43
+ export declare const MISSING_VALIDITY_STATE: ValidationDetails;
42
44
  export declare const DEFAULT_VALIDATION_RESULT: ValidationResult;
43
45
  /**
44
46
  * One verdict out of several, for a control whose value has more than one part.
@@ -51,6 +53,23 @@ export declare const mergeValidation: (...results: ValidationResult[]) => Valida
51
53
  export type ValidatableElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
52
54
  /** Freeze an element's live validity into a result the state layer can hold. */
53
55
  export declare const getNativeValidation: (element: ValidatableElement) => ValidationResult;
56
+ /**
57
+ * The kinds of control a required field can be. Each is a different sentence: a list asks to be
58
+ * chosen from, a checkbox to be ticked.
59
+ */
60
+ export type RequiredControl = "checkbox" | "radio" | "select" | "text";
61
+ /**
62
+ * What the browser would say about an empty control of this kind.
63
+ *
64
+ * Read off a detached probe rather than translated here. The platform already holds the sentence,
65
+ * in the *browser's* locale — which is the locale a validation message belongs in, since it sits
66
+ * beside the browser's own — and it is the one the same field would report under `"native"`.
67
+ *
68
+ * Empty on a server, where the field cannot have been revealed yet and so has nothing to say.
69
+ */
70
+ export declare const missingValueMessage: (kind: RequiredControl) => string;
71
+ /** Whether a value counts as nothing. Covers every shape a field holds when it is empty. */
72
+ export declare const isValueMissing: (value: unknown) => boolean;
54
73
  /** Errors a server returned, keyed by the `name` each field submits under. */
55
74
  export type FormValidationErrors = Record<string, string | string[]>;
56
75
  export interface FormContext {
@@ -58,6 +77,14 @@ export interface FormContext {
58
77
  validationErrors: ComputedRef<FormValidationErrors>;
59
78
  /** Default for every field inside, unless the field names its own. */
60
79
  validationBehavior: ComputedRef<ValidationBehavior>;
80
+ /**
81
+ * Bumped by every submit attempt.
82
+ *
83
+ * Under `"native"` a field learns of a failed submit from the browser, which fires `invalid` at
84
+ * it. Under `"aria"` the browser is not involved, so this is the only thing that tells a field
85
+ * holding an unrevealed error that it is now being asked for.
86
+ */
87
+ submitCount?: Readonly<Ref<number>>;
61
88
  }
62
89
  /**
63
90
  * React splits this in two — `FormValidationContext` in react-stately for the errors,
@@ -83,6 +110,18 @@ export interface UseFormValidationStateOptions<T> {
83
110
  name?: MaybeRefOrGetter<string | string[] | undefined>;
84
111
  /** Validity a composite field worked out from its own parts. */
85
112
  builtinValidation?: MaybeRefOrGetter<ValidationResult | undefined>;
113
+ /**
114
+ * Whether the field has to hold a value.
115
+ *
116
+ * Enforced here only under `"aria"`. Under `"native"` the control carries the `required`
117
+ * attribute and the browser reaches the same verdict itself, so a second one here would only
118
+ * be a chance to disagree.
119
+ */
120
+ isRequired?: MaybeRefOrGetter<boolean | undefined>;
121
+ /** Which sentence a missing value is reported with. @default "text" */
122
+ requiredControl?: RequiredControl;
123
+ /** Whether the value counts as nothing. @default {@link isValueMissing} */
124
+ isEmpty?: (value: T | null | undefined) => boolean;
86
125
  /**
87
126
  * A validation state owned by something above, which this field reports through instead of
88
127
  * keeping one of its own.
@@ -118,15 +157,19 @@ export declare const isEqualValidation: (a: ValidationResult | null, b: Validati
118
157
  * Decide what a field's validation currently says, ported from React Aria's
119
158
  * `packages/react-stately/src/form/useFormValidationState.ts` (react-stately 3.49.0).
120
159
  *
121
- * Knows nothing about the DOM — `useFormValidation` is what connects this to a real input.
122
- * Four sources feed it, in a fixed order of precedence:
160
+ * Holds no element of its own — `useFormValidation` is what connects this to a real input.
161
+ * Five sources feed it, in a fixed order of precedence:
123
162
  *
124
163
  * ```
125
- * realtime = controlled ?? server ?? client ?? builtin ?? valid
164
+ * realtime = controlled ?? server ?? client ?? required ?? builtin ?? valid
126
165
  * display(native) = controlled ?? server ?? committed
127
- * display(aria) = controlled ?? server ?? client ?? builtin ?? committed
166
+ * display(aria) = controlled ?? server ?? client ?? required* ?? builtin ?? committed
128
167
  * ```
129
168
  *
169
+ * `required` is the missing-value rule, and it is there for `"aria"` alone: under `"native"` the
170
+ * control carries the attribute and the browser reaches the verdict itself. The `*` is the reveal
171
+ * — it waits for a commit or a submit, so a form does not arrive with every required field red.
172
+ *
130
173
  * `realtimeValidation` is what the field pushes onto the input through `setCustomValidity`,
131
174
  * so the browser blocks submission the moment the value stops being acceptable.
132
175
  * `displayValidation` is what the user reads, and under `"native"` it moves only on commit —
@@ -20,6 +20,12 @@ var CUSTOM_VALIDITY_STATE = Object.freeze({
20
20
  customError: true,
21
21
  valid: false
22
22
  });
23
+ /** A required field with nothing in it. What the browser reports under `"native"`. */
24
+ var MISSING_VALIDITY_STATE = Object.freeze({
25
+ ...VALID_VALIDITY_STATE,
26
+ valid: false,
27
+ valueMissing: true
28
+ });
23
29
  var DEFAULT_VALIDATION_RESULT = Object.freeze({
24
30
  isInvalid: false,
25
31
  validationDetails: VALID_VALIDITY_STATE,
@@ -68,6 +74,41 @@ var getNativeValidation = (element) => {
68
74
  validationErrors: element.validationMessage ? [element.validationMessage] : []
69
75
  };
70
76
  };
77
+ var missingValueMessages = /* @__PURE__ */ new Map();
78
+ /**
79
+ * What the browser would say about an empty control of this kind.
80
+ *
81
+ * Read off a detached probe rather than translated here. The platform already holds the sentence,
82
+ * in the *browser's* locale — which is the locale a validation message belongs in, since it sits
83
+ * beside the browser's own — and it is the one the same field would report under `"native"`.
84
+ *
85
+ * Empty on a server, where the field cannot have been revealed yet and so has nothing to say.
86
+ */
87
+ var missingValueMessage = (kind) => {
88
+ const cached = missingValueMessages.get(kind);
89
+ if (cached !== void 0) return cached;
90
+ if (typeof document === "undefined") return "";
91
+ let probe;
92
+ if (kind === "select") probe = document.createElement("select");
93
+ else {
94
+ const input = document.createElement("input");
95
+ if (kind !== "text") {
96
+ input.type = kind;
97
+ input.name = "probe";
98
+ }
99
+ probe = input;
100
+ }
101
+ probe.required = true;
102
+ const message = probe.validationMessage;
103
+ missingValueMessages.set(kind, message);
104
+ return message;
105
+ };
106
+ /** Whether a value counts as nothing. Covers every shape a field holds when it is empty. */
107
+ var isValueMissing = (value) => {
108
+ if (value == null || value === "" || value === false) return true;
109
+ if (Array.isArray(value)) return value.length === 0;
110
+ return typeof value === "number" && Number.isNaN(value);
111
+ };
71
112
  /**
72
113
  * React splits this in two — `FormValidationContext` in react-stately for the errors,
73
114
  * `FormContext` in react-aria-components for the behaviour — only because the two live in
@@ -109,15 +150,19 @@ var isEqualValidation = (a, b) => {
109
150
  * Decide what a field's validation currently says, ported from React Aria's
110
151
  * `packages/react-stately/src/form/useFormValidationState.ts` (react-stately 3.49.0).
111
152
  *
112
- * Knows nothing about the DOM — `useFormValidation` is what connects this to a real input.
113
- * Four sources feed it, in a fixed order of precedence:
153
+ * Holds no element of its own — `useFormValidation` is what connects this to a real input.
154
+ * Five sources feed it, in a fixed order of precedence:
114
155
  *
115
156
  * ```
116
- * realtime = controlled ?? server ?? client ?? builtin ?? valid
157
+ * realtime = controlled ?? server ?? client ?? required ?? builtin ?? valid
117
158
  * display(native) = controlled ?? server ?? committed
118
- * display(aria) = controlled ?? server ?? client ?? builtin ?? committed
159
+ * display(aria) = controlled ?? server ?? client ?? required* ?? builtin ?? committed
119
160
  * ```
120
161
  *
162
+ * `required` is the missing-value rule, and it is there for `"aria"` alone: under `"native"` the
163
+ * control carries the attribute and the browser reaches the verdict itself. The `*` is the reveal
164
+ * — it waits for a commit or a submit, so a form does not arrive with every required field red.
165
+ *
121
166
  * `realtimeValidation` is what the field pushes onto the input through `setCustomValidity`,
122
167
  * so the browser blocks submission the moment the value stops being acceptable.
123
168
  * `displayValidation` is what the user reads, and under `"native"` it moves only on commit —
@@ -157,6 +202,17 @@ var useFormValidationState = (options) => {
157
202
  const result = toValue(options.builtinValidation);
158
203
  return result && !result.validationDetails.valid ? result : null;
159
204
  });
205
+ const requiredError = computed(() => {
206
+ if (validationBehavior.value !== "aria") return null;
207
+ if (!toValue(options.isRequired)) return null;
208
+ if (!(options.isEmpty ?? isValueMissing)(toValue(options.value))) return null;
209
+ const message = missingValueMessage(options.requiredControl ?? "text");
210
+ return {
211
+ isInvalid: true,
212
+ validationDetails: MISSING_VALIDITY_STATE,
213
+ validationErrors: message ? [message] : []
214
+ };
215
+ });
160
216
  const serverErrors = computed(() => form?.validationErrors.value ?? {});
161
217
  const serverErrorMessages = computed(() => {
162
218
  const name = toValue(options.name);
@@ -172,11 +228,22 @@ var useFormValidationState = (options) => {
172
228
  const nextNative = shallowRef(DEFAULT_VALIDATION_RESULT);
173
229
  /** What a commit has already revealed. */
174
230
  const committed = shallowRef(DEFAULT_VALIDATION_RESULT);
231
+ /**
232
+ * Whether the field has been asked to prove itself yet.
233
+ *
234
+ * A required field is empty from its first render, and `"aria"` shows a client error the moment
235
+ * it appears — so without this a form would arrive with every required field already red. It
236
+ * gates the missing-value verdict alone; a `validate` the caller wrote still reports as it types.
237
+ */
238
+ const isRevealed = shallowRef(false);
239
+ watch(() => form?.submitCount?.value ?? 0, (count) => {
240
+ if (count > 0) isRevealed.value = true;
241
+ });
175
242
  let isCommitQueued = false;
176
- const realtimeValidation = computed(() => controlledError.value ?? serverError.value ?? clientError.value ?? builtinValidation.value ?? DEFAULT_VALIDATION_RESULT);
243
+ const realtimeValidation = computed(() => controlledError.value ?? serverError.value ?? clientError.value ?? requiredError.value ?? builtinValidation.value ?? DEFAULT_VALIDATION_RESULT);
177
244
  const displayValidation = computed(() => {
178
245
  if (validationBehavior.value === "native") return controlledError.value ?? serverError.value ?? committed.value;
179
- return controlledError.value ?? serverError.value ?? clientError.value ?? builtinValidation.value ?? committed.value;
246
+ return controlledError.value ?? serverError.value ?? clientError.value ?? (isRevealed.value ? requiredError.value : null) ?? builtinValidation.value ?? committed.value;
180
247
  });
181
248
  const setCommitted = (result) => {
182
249
  if (!isEqualValidation(result, committed.value)) committed.value = result;
@@ -184,6 +251,7 @@ var useFormValidationState = (options) => {
184
251
  return {
185
252
  commitValidation: () => {
186
253
  isServerErrorCleared.value = true;
254
+ isRevealed.value = true;
187
255
  if (validationBehavior.value !== "native" || isCommitQueued) return;
188
256
  isCommitQueued = true;
189
257
  nextTick(() => {
@@ -196,6 +264,7 @@ var useFormValidationState = (options) => {
196
264
  realtimeValidation,
197
265
  resetValidation: () => {
198
266
  setCommitted(DEFAULT_VALIDATION_RESULT);
267
+ isRevealed.value = false;
199
268
  isCommitQueued = false;
200
269
  isServerErrorCleared.value = true;
201
270
  },
@@ -210,4 +279,4 @@ var useFormValidationState = (options) => {
210
279
  };
211
280
  };
212
281
  //#endregion
213
- export { CUSTOM_VALIDITY_STATE, DEFAULT_VALIDATION_RESULT, VALID_VALIDITY_STATE, getNativeValidation, isEqualValidation, mergeValidation, provideFormContext, useFormContext, useFormValidationState };
282
+ export { CUSTOM_VALIDITY_STATE, DEFAULT_VALIDATION_RESULT, MISSING_VALIDITY_STATE, VALID_VALIDITY_STATE, getNativeValidation, isEqualValidation, isValueMissing, mergeValidation, missingValueMessage, provideFormContext, useFormContext, useFormValidationState };
@@ -182,7 +182,9 @@ var useInputOTP = (options) => {
182
182
  let end = -1;
183
183
  let direction = void 0;
184
184
  if (_val.length !== 0 && _s !== null && _e !== null) {
185
- if (_s === _e && !(_s === _val.length && _val.length < _ml)) {
185
+ const isSingleCaret = _s === _e;
186
+ const isInsertMode = _s === _val.length && _val.length < _ml;
187
+ if (isSingleCaret && !isInsertMode) {
186
188
  const c = _s;
187
189
  if (c === 0) {
188
190
  start = 0;
@@ -196,7 +198,8 @@ var useInputOTP = (options) => {
196
198
  let offset = 0;
197
199
  if (_prev[0] !== null && _prev[1] !== null) {
198
200
  direction = c < _prev[1] ? "backward" : "forward";
199
- if (direction === "backward" && !(_prev[0] === _prev[1] && _prev[0] < _ml)) offset = -1;
201
+ const wasPreviouslyInserting = _prev[0] === _prev[1] && _prev[0] < _ml;
202
+ if (direction === "backward" && !wasPreviouslyInserting) offset = -1;
200
203
  }
201
204
  start = offset + c;
202
205
  end = offset + c + 1;
@@ -79,7 +79,8 @@ var useMove = (options) => {
79
79
  };
80
80
  const onPointermove = (event) => {
81
81
  if (event.pointerId !== pointerId) return;
82
- move(event, event.pointerType || "mouse", event.pageX - (lastPosition?.pageX ?? 0), event.pageY - (lastPosition?.pageY ?? 0));
82
+ const pointerType = event.pointerType || "mouse";
83
+ move(event, pointerType, event.pageX - (lastPosition?.pageX ?? 0), event.pageY - (lastPosition?.pageY ?? 0));
83
84
  lastPosition = {
84
85
  pageX: event.pageX,
85
86
  pageY: event.pageY
@@ -87,6 +87,7 @@ var useNumberFieldState = (options = {}) => {
87
87
  inputValue.value = format(numberValueState.value);
88
88
  const validation = useFormValidationState({
89
89
  isInvalid: () => toValue(options.isInvalid),
90
+ isRequired: () => toValue(options.isRequired),
90
91
  name: () => toValue(options.name),
91
92
  validate: () => toValue(options.validate),
92
93
  validationBehavior: () => toValue(options.validationBehavior),
@@ -38,7 +38,9 @@ var useRadioGroupState = (options = {}) => {
38
38
  const lastFocused = shallowRef(null);
39
39
  const validation = useFormValidationState({
40
40
  isInvalid: () => toValue(options.isInvalid),
41
+ isRequired,
41
42
  name: () => toValue(options.name),
43
+ requiredControl: "radio",
42
44
  validate: () => toValue(options.validate),
43
45
  validationBehavior: () => toValue(options.validationBehavior),
44
46
  value: () => selectedValue.value
@@ -40,6 +40,8 @@ export interface UseSelectStateOptions<T> {
40
40
  /** @default "all" */
41
41
  disabledBehavior?: MaybeRefOrGetter<DisabledBehavior | undefined>;
42
42
  isInvalid?: MaybeRefOrGetter<boolean | undefined>;
43
+ /** Whether the field has to hold a value. Enforced by the validation state under `"aria"`. */
44
+ isRequired?: MaybeRefOrGetter<boolean | undefined>;
43
45
  validate?: MaybeRefOrGetter<ValidationFunction<SelectedValue> | undefined>;
44
46
  validationBehavior?: MaybeRefOrGetter<ValidationBehavior | undefined>;
45
47
  name?: MaybeRefOrGetter<string | undefined>;
@@ -91,7 +91,9 @@ var useSelectState = (options) => {
91
91
  };
92
92
  const validation = useFormValidationState({
93
93
  isInvalid: options.isInvalid,
94
+ isRequired: options.isRequired,
94
95
  name: options.name,
96
+ requiredControl: "select",
95
97
  validate: options.validate,
96
98
  validationBehavior: options.validationBehavior,
97
99
  value: () => {
@@ -56,6 +56,7 @@ var useSingleSelectListState = (options = {}) => {
56
56
  * empty collection of the first render and keep that answer.
57
57
  */
58
58
  const selectedKey = computed(() => {
59
+ collection.size.value;
59
60
  const key = stored.state.value;
60
61
  if (stored.isControlled.value) return key ?? null;
61
62
  if (key != null && collection.getItem(key)) return key;
@@ -47,6 +47,7 @@ var useTextField = (options = {}) => {
47
47
  const isRequired = computed(() => Boolean(toValue(options.isRequired)));
48
48
  const validation = options.validationState ?? useFormValidationState({
49
49
  isInvalid: () => toValue(options.isInvalid),
50
+ isRequired: () => toValue(options.isRequired),
50
51
  name: () => toValue(options.name),
51
52
  validate: () => toValue(options.validate),
52
53
  validationBehavior: () => toValue(options.validationBehavior),
@@ -55,6 +55,7 @@ var useTimeFieldState = (options) => {
55
55
  if (current == null) return null;
56
56
  return "day" in current ? toTime(current) : current;
57
57
  });
58
+ const validateTime = options.validate ? () => options.validate(value.value) : void 0;
58
59
  return {
59
60
  ...useDateFieldState({
60
61
  createCalendar: () => new GregorianCalendar(),
@@ -77,7 +78,7 @@ var useTimeFieldState = (options) => {
77
78
  },
78
79
  placeholderValue: () => placeholderDate.value ?? void 0,
79
80
  shouldForceLeadingZeros: () => toValue(options.shouldForceLeadingZeros),
80
- validate: options.validate ? () => options.validate(value.value) : void 0,
81
+ validate: validateTime,
81
82
  validationBehavior: () => toValue(options.validationBehavior),
82
83
  value: () => toDateTime(value.value)
83
84
  }),
@@ -1,4 +1,5 @@
1
1
  import { focusableIn } from "../utils/focus.js";
2
+ import { willOpenKeyboard } from "../utils/platform.js";
2
3
  import { computed, onMounted, shallowRef, toValue } from "vue";
3
4
  //#region src/composables/use-toolbar.ts
4
5
  /**
@@ -45,10 +46,11 @@ var useToolbar = (options) => {
45
46
  if (!isOutermost.value) return;
46
47
  const element = toValue(options.element);
47
48
  const target = event.target;
48
- if (!element || !(target instanceof Node) || !element.contains(target)) return;
49
+ if (!element || !(target instanceof Element) || !element.contains(target)) return;
49
50
  const isReversed = orientation.value === "horizontal" && getComputedStyle(element).direction === "rtl";
50
51
  const forward = orientation.value === "horizontal" ? "ArrowRight" : "ArrowDown";
51
52
  const backward = orientation.value === "horizontal" ? "ArrowLeft" : "ArrowUp";
53
+ if ((event.key === forward || event.key === backward) && willOpenKeyboard(target)) return;
52
54
  if (event.key === forward) moveFocus(isReversed ? -1 : 1);
53
55
  else if (event.key === backward) moveFocus(isReversed ? 1 : -1);
54
56
  else if (event.key === "Tab") {
@@ -49,6 +49,7 @@ var useVirtualizer = (options) => {
49
49
  /** Set by `updateItemSize`, consumed by the next pass. */
50
50
  let pendingItemSizeChange = false;
51
51
  const pass = computed(() => {
52
+ revision.value;
52
53
  const collection = options.collection();
53
54
  const layout = options.layout();
54
55
  const layoutOptions = options.layoutOptions?.();