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.
- package/AGENTS.md +150 -0
- package/README.md +5 -1
- package/dist/components/breadcrumbs/breadcrumbs-item.js +2 -0
- package/dist/components/calendar-year-picker/calendar-year-picker-grid.js +1 -0
- package/dist/components/checkbox/checkbox-root.js +2 -0
- package/dist/components/combo-box/combo-box-root.js +1 -0
- package/dist/components/date-input-group/date-input-group-hidden-input.js +2 -1
- package/dist/components/date-picker/date-picker-root.js +1 -0
- package/dist/components/date-range-picker/date-range-picker-root.js +1 -0
- package/dist/components/form/form-root.js +7 -1
- package/dist/components/radio-group/radio-group-root.js +2 -1
- package/dist/components/search-field/search-field-group.js +21 -1
- package/dist/components/select/select-root.js +1 -0
- package/dist/components/splitter/splitter.state.js +6 -1
- package/dist/components/switch/switch-root.js +2 -0
- package/dist/components/table/table-body.js +1 -1
- package/dist/components/table/table-cell.js +3 -1
- package/dist/components/table/table-column-resizer.js +2 -1
- package/dist/components/table/table-load-more.js +1 -1
- package/dist/components/table/use-grid-selection-announcement.js +3 -1
- package/dist/components/table/use-table-collection.js +3 -0
- package/dist/components/tabs/tabs-list-container.js +2 -1
- package/dist/composables/use-calendar-grid.js +2 -1
- package/dist/composables/use-checkbox-group-state.js +2 -0
- package/dist/composables/use-collection.js +12 -0
- package/dist/composables/use-color-field-state.js +1 -0
- package/dist/composables/use-color-field.js +31 -29
- package/dist/composables/use-combo-box-state.d.ts +2 -0
- package/dist/composables/use-combo-box-state.js +5 -2
- package/dist/composables/use-combo-box.js +2 -1
- package/dist/composables/use-date-field-state.js +1 -0
- package/dist/composables/use-date-picker-state.d.ts +2 -0
- package/dist/composables/use-date-picker-state.js +1 -0
- package/dist/composables/use-date-range-picker-state.d.ts +2 -0
- package/dist/composables/use-date-range-picker-state.js +2 -0
- package/dist/composables/use-description.js +1 -0
- package/dist/composables/use-disclosure-panel.js +2 -1
- package/dist/composables/use-drag.js +9 -6
- package/dist/composables/use-drop.js +2 -1
- package/dist/composables/use-droppable-collection.js +2 -1
- package/dist/composables/use-form-validation-state.d.ts +48 -5
- package/dist/composables/use-form-validation-state.js +76 -7
- package/dist/composables/use-input-otp.js +5 -2
- package/dist/composables/use-move.js +2 -1
- package/dist/composables/use-number-field-state.js +1 -0
- package/dist/composables/use-radio-group-state.js +2 -0
- package/dist/composables/use-select-state.d.ts +2 -0
- package/dist/composables/use-select-state.js +2 -0
- package/dist/composables/use-single-select-list-state.js +1 -0
- package/dist/composables/use-text-field.js +1 -0
- package/dist/composables/use-time-field-state.js +2 -1
- package/dist/composables/use-toolbar.js +3 -1
- package/dist/composables/use-virtualizer.js +1 -0
- package/dist/index.js +2 -2
- package/dist/ropav.min.css +1 -1
- package/dist/utils/create-context.js +2 -1
- package/dist/utils/platform.js +2 -1
- package/dist/utils/position.js +4 -2
- package/dist/version.js +1 -1
- package/package.json +4 -7
- 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
|
-
|
|
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),
|
|
@@ -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
|
-
|
|
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)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
122
|
-
*
|
|
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
|
-
*
|
|
113
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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?.();
|