@sectile/vue 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/accordion.d.ts +0 -1
- package/dist/alert-dialog.d.ts +0 -2
- package/dist/alert-dialog.js.map +1 -1
- package/dist/calendar.d.ts +2 -3
- package/dist/calendar.js +14 -14
- package/dist/calendar.js.map +1 -1
- package/dist/carousel.d.ts +22 -1
- package/dist/carousel.js +7 -1
- package/dist/carousel.js.map +1 -1
- package/dist/cascade-list.d.ts +339 -0
- package/dist/cascade-list.js +240 -0
- package/dist/cascade-list.js.map +1 -0
- package/dist/cascade-select.d.ts +11 -42
- package/dist/cascade-select.js +19 -27
- package/dist/cascade-select.js.map +1 -1
- package/dist/checkbox-group.d.ts +9 -1
- package/dist/checkbox-group.js +57 -13
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.d.ts +0 -1
- package/dist/checkbox.js +15 -8
- package/dist/checkbox.js.map +1 -1
- package/dist/color-picker.d.ts +0 -1
- package/dist/combobox.d.ts +0 -1
- package/dist/combobox.js +46 -6
- package/dist/combobox.js.map +1 -1
- package/dist/data-grid.d.ts +214 -0
- package/dist/data-grid.js +107 -0
- package/dist/data-grid.js.map +1 -0
- package/dist/data-table.d.ts +232 -0
- package/dist/data-table.js +116 -0
- package/dist/data-table.js.map +1 -0
- package/dist/data-tree-grid.d.ts +225 -0
- package/dist/data-tree-grid.js +98 -0
- package/dist/data-tree-grid.js.map +1 -0
- package/dist/date-field.d.ts +1 -2
- package/dist/date-field.js +1 -1
- package/dist/date-field.js.map +1 -1
- package/dist/date-picker.d.ts +37 -6
- package/dist/date-picker.js +3 -1
- package/dist/date-picker.js.map +1 -1
- package/dist/date-range-field.d.ts +2 -3
- package/dist/date-range-field.js +24 -6
- package/dist/date-range-field.js.map +1 -1
- package/dist/date-range-picker.d.ts +37 -6
- package/dist/date-range-picker.js +3 -1
- package/dist/date-range-picker.js.map +1 -1
- package/dist/date-time-field.d.ts +1 -2
- package/dist/date-time-field.js +1 -1
- package/dist/date-time-field.js.map +1 -1
- package/dist/date-time-picker.d.ts +38 -7
- package/dist/date-time-picker.js +3 -1
- package/dist/date-time-picker.js.map +1 -1
- package/dist/date-time-range-picker.d.ts +38 -7
- package/dist/date-time-range-picker.js +3 -1
- package/dist/date-time-range-picker.js.map +1 -1
- package/dist/dialog.d.ts +0 -2
- package/dist/dialog.js.map +1 -1
- package/dist/disclosure.d.ts +0 -1
- package/dist/drawer.d.ts +1 -2
- package/dist/editable.d.ts +0 -1
- package/dist/editable.js +25 -6
- package/dist/editable.js.map +1 -1
- package/dist/feed.d.ts +0 -1
- package/dist/form.d.ts +88 -115
- package/dist/form.js +461 -233
- package/dist/form.js.map +1 -1
- package/dist/grid.d.ts +0 -1
- package/dist/grid.js +51 -6
- package/dist/grid.js.map +1 -1
- package/dist/host-provider.d.ts +0 -13
- package/dist/host-provider.js +0 -16
- package/dist/host-provider.js.map +1 -1
- package/dist/index.d.ts +14 -39
- package/dist/index.js +6 -19
- package/dist/index.js.map +1 -1
- package/dist/internal/cascade-choice.d.ts +204 -0
- package/dist/internal/cascade-choice.js +161 -0
- package/dist/internal/cascade-choice.js.map +1 -0
- package/dist/internal/collection.d.ts +3 -8
- package/dist/internal/collection.js +14 -20
- package/dist/internal/collection.js.map +1 -1
- package/dist/internal/controlled-state.d.ts +0 -1
- package/dist/internal/date-picker.d.ts +78 -9
- package/dist/internal/date-picker.js +116 -27
- package/dist/internal/date-picker.js.map +1 -1
- package/dist/internal/decimal-input.d.ts +1 -0
- package/dist/internal/decimal-input.js +24 -0
- package/dist/internal/decimal-input.js.map +1 -0
- package/dist/internal/form-control-inventory.d.ts +4 -1
- package/dist/internal/form-control-inventory.js +1 -0
- package/dist/internal/form-control-inventory.js.map +1 -1
- package/dist/internal/form-control.d.ts +100 -2
- package/dist/internal/form-control.js +103 -1
- package/dist/internal/form-control.js.map +1 -1
- package/dist/internal/native-field.d.ts +0 -1
- package/dist/internal/native-field.js +11 -2
- package/dist/internal/native-field.js.map +1 -1
- package/dist/internal/native-input.d.ts +0 -1
- package/dist/internal/part-contract.d.ts +0 -1
- package/dist/internal/popup.d.ts +5 -27
- package/dist/internal/popup.js +14 -20
- package/dist/internal/popup.js.map +1 -1
- package/dist/internal/presence.d.ts +0 -1
- package/dist/internal/scheduled-task.d.ts +5 -0
- package/dist/internal/scheduled-task.js +28 -0
- package/dist/internal/scheduled-task.js.map +1 -0
- package/dist/internal/tabs-context.d.ts +2 -3
- package/dist/internal/tabular-components.d.ts +27 -0
- package/dist/internal/tabular-components.js +26 -0
- package/dist/internal/tabular-components.js.map +1 -0
- package/dist/internal/tabular-parts.d.ts +57 -0
- package/dist/internal/tabular-parts.js +239 -0
- package/dist/internal/tabular-parts.js.map +1 -0
- package/dist/internal/tabular-profile.d.ts +75 -0
- package/dist/internal/tabular-profile.js +209 -0
- package/dist/internal/tabular-profile.js.map +1 -0
- package/dist/internal/virtual-collection-model.d.ts +42 -0
- package/dist/internal/virtual-collection-model.js +241 -0
- package/dist/internal/virtual-collection-model.js.map +1 -0
- package/dist/internal/virtual-collection.d.ts +38 -0
- package/dist/internal/virtual-collection.js +87 -0
- package/dist/internal/virtual-collection.js.map +1 -0
- package/dist/{virtual.d.ts → internal/virtual-core.d.ts} +9 -4
- package/dist/{virtual.js → internal/virtual-core.js} +7 -7
- package/dist/internal/virtual-core.js.map +1 -0
- package/dist/internal/virtual-list.d.ts +214 -0
- package/dist/internal/virtual-list.js +401 -0
- package/dist/internal/virtual-list.js.map +1 -0
- package/dist/listbox.d.ts +0 -1
- package/dist/listbox.js +52 -6
- package/dist/listbox.js.map +1 -1
- package/dist/menu-button.d.ts +0 -1
- package/dist/menu.d.ts +0 -1
- package/dist/menubar.d.ts +0 -1
- package/dist/meter-group.d.ts +369 -0
- package/dist/meter-group.js +271 -0
- package/dist/meter-group.js.map +1 -0
- package/dist/meter.d.ts +191 -0
- package/dist/meter.js +133 -0
- package/dist/meter.js.map +1 -0
- package/dist/month-picker.d.ts +37 -6
- package/dist/month-picker.js +3 -1
- package/dist/month-picker.js.map +1 -1
- package/dist/month-range-picker.d.ts +37 -6
- package/dist/month-range-picker.js +3 -1
- package/dist/month-range-picker.js.map +1 -1
- package/dist/multi-thumb-slider.d.ts +0 -1
- package/dist/navigation-menu.d.ts +0 -1
- package/dist/number-field.d.ts +0 -1
- package/dist/pagination.d.ts +23 -2
- package/dist/pagination.js +6 -1
- package/dist/pagination.js.map +1 -1
- package/dist/pin-input.d.ts +0 -1
- package/dist/pin-input.js +1 -1
- package/dist/pin-input.js.map +1 -1
- package/dist/popover.d.ts +0 -2
- package/dist/popover.js.map +1 -1
- package/dist/primitive.d.ts +0 -1
- package/dist/primitive.js +8 -4
- package/dist/primitive.js.map +1 -1
- package/dist/progress.d.ts +148 -0
- package/dist/progress.js +110 -0
- package/dist/progress.js.map +1 -0
- package/dist/quantity-field.d.ts +0 -1
- package/dist/radio-group.d.ts +0 -1
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/range-calendar.d.ts +2 -3
- package/dist/rating.d.ts +0 -1
- package/dist/rating.js +10 -1
- package/dist/rating.js.map +1 -1
- package/dist/reorder.d.ts +1 -2
- package/dist/select.d.ts +18 -44
- package/dist/select.js +11 -13
- package/dist/select.js.map +1 -1
- package/dist/slider.d.ts +0 -1
- package/dist/spin-button.d.ts +0 -1
- package/dist/spin-button.js +17 -4
- package/dist/spin-button.js.map +1 -1
- package/dist/stepper.d.ts +0 -1
- package/dist/stepper.js +19 -2
- package/dist/stepper.js.map +1 -1
- package/dist/switch.d.ts +0 -1
- package/dist/tabs.d.ts +0 -1
- package/dist/tabs.js +4 -22
- package/dist/tabs.js.map +1 -1
- package/dist/tags-input.d.ts +0 -1
- package/dist/tags-input.js +18 -5
- package/dist/tags-input.js.map +1 -1
- package/dist/temporal-provider.d.ts +21 -0
- package/dist/temporal-provider.js +30 -0
- package/dist/temporal-provider.js.map +1 -0
- package/dist/text.d.ts +0 -1
- package/dist/text.js +18 -4
- package/dist/text.js.map +1 -1
- package/dist/time-field.d.ts +1 -2
- package/dist/time-field.js +1 -1
- package/dist/time-field.js.map +1 -1
- package/dist/time-range-field.d.ts +2 -2
- package/dist/time-range-field.js +6 -4
- package/dist/time-range-field.js.map +1 -1
- package/dist/timer.d.ts +0 -1
- package/dist/toast.d.ts +11 -2
- package/dist/toast.js +22 -9
- package/dist/toast.js.map +1 -1
- package/dist/toggle-button.d.ts +0 -1
- package/dist/toggle-group.d.ts +0 -1
- package/dist/toggle-group.js +1 -1
- package/dist/toggle-group.js.map +1 -1
- package/dist/toolbar.d.ts +0 -1
- package/dist/tooltip.d.ts +1 -3
- package/dist/tooltip.js.map +1 -1
- package/dist/tree-grid.d.ts +0 -1
- package/dist/tree-grid.js +62 -8
- package/dist/tree-grid.js.map +1 -1
- package/dist/tree-view.d.ts +0 -1
- package/dist/tree-view.js +51 -10
- package/dist/tree-view.js.map +1 -1
- package/dist/virtual-core.d.ts +1 -0
- package/dist/virtual-core.js +2 -0
- package/dist/virtual-core.js.map +1 -0
- package/dist/virtual-grid.d.ts +188 -0
- package/dist/virtual-grid.js +306 -0
- package/dist/virtual-grid.js.map +1 -0
- package/dist/virtual-list.d.ts +1 -0
- package/dist/virtual-list.js +2 -0
- package/dist/virtual-list.js.map +1 -0
- package/dist/virtual-masonry.d.ts +198 -0
- package/dist/virtual-masonry.js +221 -0
- package/dist/virtual-masonry.js.map +1 -0
- package/dist/virtual-spatial.d.ts +157 -0
- package/dist/virtual-spatial.js +159 -0
- package/dist/virtual-spatial.js.map +1 -0
- package/dist/window-splitter.d.ts +0 -1
- package/dist/year-picker.d.ts +37 -6
- package/dist/year-picker.js +3 -1
- package/dist/year-picker.js.map +1 -1
- package/dist/year-range-picker.d.ts +37 -6
- package/dist/year-range-picker.js +3 -1
- package/dist/year-range-picker.js.map +1 -1
- package/package.json +173 -89
- package/dist/accordion.d.ts.map +0 -1
- package/dist/alert-dialog.d.ts.map +0 -1
- package/dist/calendar.d.ts.map +0 -1
- package/dist/carousel.d.ts.map +0 -1
- package/dist/cascade-select.d.ts.map +0 -1
- package/dist/checkbox-group.d.ts.map +0 -1
- package/dist/checkbox.d.ts.map +0 -1
- package/dist/color-picker.d.ts.map +0 -1
- package/dist/combobox.d.ts.map +0 -1
- package/dist/date-field.d.ts.map +0 -1
- package/dist/date-picker.d.ts.map +0 -1
- package/dist/date-range-field.d.ts.map +0 -1
- package/dist/date-range-picker.d.ts.map +0 -1
- package/dist/date-time-field.d.ts.map +0 -1
- package/dist/date-time-picker.d.ts.map +0 -1
- package/dist/date-time-range-picker.d.ts.map +0 -1
- package/dist/dialog.d.ts.map +0 -1
- package/dist/disclosure.d.ts.map +0 -1
- package/dist/drawer.d.ts.map +0 -1
- package/dist/editable.d.ts.map +0 -1
- package/dist/feed.d.ts.map +0 -1
- package/dist/form.d.ts.map +0 -1
- package/dist/grid.d.ts.map +0 -1
- package/dist/host-provider.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/collection.d.ts.map +0 -1
- package/dist/internal/controlled-state.d.ts.map +0 -1
- package/dist/internal/date-picker.d.ts.map +0 -1
- package/dist/internal/form-control-inventory.d.ts.map +0 -1
- package/dist/internal/form-control.d.ts.map +0 -1
- package/dist/internal/native-field.d.ts.map +0 -1
- package/dist/internal/native-input.d.ts.map +0 -1
- package/dist/internal/part-contract.d.ts.map +0 -1
- package/dist/internal/popup.d.ts.map +0 -1
- package/dist/internal/presence.d.ts.map +0 -1
- package/dist/internal/tabs-context.d.ts.map +0 -1
- package/dist/listbox.d.ts.map +0 -1
- package/dist/menu-button.d.ts.map +0 -1
- package/dist/menu.d.ts.map +0 -1
- package/dist/menubar.d.ts.map +0 -1
- package/dist/month-picker.d.ts.map +0 -1
- package/dist/month-range-picker.d.ts.map +0 -1
- package/dist/multi-thumb-slider.d.ts.map +0 -1
- package/dist/navigation-menu.d.ts.map +0 -1
- package/dist/number-field.d.ts.map +0 -1
- package/dist/pagination.d.ts.map +0 -1
- package/dist/pin-input.d.ts.map +0 -1
- package/dist/popover.d.ts.map +0 -1
- package/dist/primitive.d.ts.map +0 -1
- package/dist/quantity-field.d.ts.map +0 -1
- package/dist/radio-group.d.ts.map +0 -1
- package/dist/range-calendar.d.ts.map +0 -1
- package/dist/rating.d.ts.map +0 -1
- package/dist/reorder.d.ts.map +0 -1
- package/dist/select.d.ts.map +0 -1
- package/dist/slider.d.ts.map +0 -1
- package/dist/spin-button.d.ts.map +0 -1
- package/dist/stepper.d.ts.map +0 -1
- package/dist/switch.d.ts.map +0 -1
- package/dist/tabs.d.ts.map +0 -1
- package/dist/tags-input.d.ts.map +0 -1
- package/dist/text.d.ts.map +0 -1
- package/dist/time-field.d.ts.map +0 -1
- package/dist/time-range-field.d.ts.map +0 -1
- package/dist/timer.d.ts.map +0 -1
- package/dist/toast.d.ts.map +0 -1
- package/dist/toggle-button.d.ts.map +0 -1
- package/dist/toggle-group.d.ts.map +0 -1
- package/dist/toolbar.d.ts.map +0 -1
- package/dist/tooltip.d.ts.map +0 -1
- package/dist/tree-grid.d.ts.map +0 -1
- package/dist/tree-view.d.ts.map +0 -1
- package/dist/virtual.d.ts.map +0 -1
- package/dist/virtual.js.map +0 -1
- package/dist/window-splitter.d.ts.map +0 -1
- package/dist/year-picker.d.ts.map +0 -1
- package/dist/year-range-picker.d.ts.map +0 -1
package/dist/form.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import { computed, defineComponent,
|
|
1
|
+
import { computed, defineComponent, h, inject, mergeProps, nextTick, onBeforeUnmount, onMounted, onScopeDispose, provide, shallowRef, watch, } from 'vue';
|
|
2
2
|
import { appendFormFieldPath, createForm, encodeFormFieldPath, } from '@sectile/dom/form';
|
|
3
|
+
import { tryCreateFormFieldPath } from '@sectile/form/path';
|
|
4
|
+
import { compositeControlCapabilities, hiddenInputSubmissionCapabilities, hiddenSelectSubmissionCapabilities, hiddenValueSubmissionCapabilities, nativeInputControlCapabilities, provideFormControlFieldContext, provideFormControlOwner, useCompositeFormControl, useFormControl, useNativeInputFormControl, } from './internal/form-control.js';
|
|
5
|
+
import { useNextTickTask } from './internal/scheduled-task.js';
|
|
3
6
|
import { Primitive } from './primitive.js';
|
|
4
7
|
import { useHostId } from './host-provider.js';
|
|
8
|
+
export function defineFormSubmission(definition) {
|
|
9
|
+
return Object.freeze({ ...definition });
|
|
10
|
+
}
|
|
11
|
+
export { compositeControlCapabilities, hiddenInputSubmissionCapabilities, hiddenSelectSubmissionCapabilities, hiddenValueSubmissionCapabilities, nativeInputControlCapabilities, provideFormControlOwner, useCompositeFormControl, useFormControl, useNativeInputFormControl, };
|
|
5
12
|
const formContextKey = Symbol('SectileForm');
|
|
6
13
|
const formFieldContextKey = Symbol('SectileFormField');
|
|
7
|
-
const formControlOwnerKey = Symbol('SectileFormControlOwner');
|
|
8
14
|
const emptyState = Object.freeze({
|
|
9
15
|
validationGeneration: 0,
|
|
10
16
|
validationStatus: 'idle',
|
|
@@ -48,16 +54,13 @@ const FormRootImpl = defineComponent({
|
|
|
48
54
|
},
|
|
49
55
|
slots: Object,
|
|
50
56
|
setup(props, { attrs, emit, expose, slots }) {
|
|
51
|
-
const root =
|
|
52
|
-
const summary =
|
|
53
|
-
const state = shallowRef(emptyState);
|
|
57
|
+
const root = shallowRef(null);
|
|
58
|
+
const summary = shallowRef(null);
|
|
54
59
|
const connection = shallowRef(null);
|
|
55
60
|
const participants = new Map();
|
|
61
|
+
const fieldDiagnostics = new Map();
|
|
56
62
|
let syncConfiguredIssues = () => { };
|
|
57
|
-
|
|
58
|
-
if (connection.value !== null)
|
|
59
|
-
state.value = connection.value.getSnapshot().state;
|
|
60
|
-
};
|
|
63
|
+
let syncFieldDiagnostics = () => { };
|
|
61
64
|
const register = (participant) => {
|
|
62
65
|
const existing = participants.get(participant.id);
|
|
63
66
|
existing?.unregister?.();
|
|
@@ -65,29 +68,27 @@ const FormRootImpl = defineComponent({
|
|
|
65
68
|
participants.set(participant.id, registered);
|
|
66
69
|
if (connection.value !== null)
|
|
67
70
|
registered.unregister = connection.value.registerParticipant(participant);
|
|
68
|
-
void nextTick(() =>
|
|
71
|
+
void nextTick(() => {
|
|
72
|
+
syncConfiguredIssues();
|
|
73
|
+
syncFieldDiagnostics();
|
|
74
|
+
});
|
|
69
75
|
return () => {
|
|
70
76
|
if (participants.get(participant.id) !== registered)
|
|
71
77
|
return;
|
|
72
78
|
registered.unregister?.();
|
|
73
79
|
participants.delete(participant.id);
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
void nextTick(() => {
|
|
81
|
+
syncConfiguredIssues();
|
|
82
|
+
syncFieldDiagnostics();
|
|
83
|
+
});
|
|
76
84
|
};
|
|
77
85
|
};
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
target.submitSucceeded(generation);
|
|
86
|
-
};
|
|
87
|
-
const rejectSubmission = (target, generation, reason) => {
|
|
88
|
-
if (connection.value !== target)
|
|
89
|
-
return;
|
|
90
|
-
target.submitFailed(generation, resolveIssueInputs(target, 'server', mapSubmissionError(props.mapSubmitError, reason)));
|
|
86
|
+
const setFieldDiagnostic = (id, issue) => {
|
|
87
|
+
if (issue === null)
|
|
88
|
+
fieldDiagnostics.delete(id);
|
|
89
|
+
else
|
|
90
|
+
fieldDiagnostics.set(id, issue);
|
|
91
|
+
void nextTick(syncFieldDiagnostics);
|
|
91
92
|
};
|
|
92
93
|
const submit = (payload) => {
|
|
93
94
|
const handler = props.onSubmit;
|
|
@@ -96,26 +97,29 @@ const FormRootImpl = defineComponent({
|
|
|
96
97
|
const target = connection.value;
|
|
97
98
|
if (target === null)
|
|
98
99
|
return;
|
|
99
|
-
payload.event.preventDefault();
|
|
100
|
-
if (target.getSnapshot().state.submissionStatus === 'submitting') {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const generation = target.submitStarted();
|
|
104
|
-
if (generation === null)
|
|
105
|
-
return;
|
|
106
100
|
let result;
|
|
107
101
|
try {
|
|
108
102
|
result = handler(toFormSubmitEvent(payload));
|
|
109
103
|
}
|
|
110
104
|
catch (error) {
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
issues: resolveIssueInputs(target, 'server', mapSubmissionError(props.mapSubmitError, error)),
|
|
108
|
+
};
|
|
113
109
|
}
|
|
110
|
+
const settle = (resolved) => (typeof resolved === 'object' && resolved !== null && resolved.ok === false
|
|
111
|
+
? {
|
|
112
|
+
ok: false,
|
|
113
|
+
issues: resolveIssueInputs(target, 'server', resolved.issues ?? [submissionErrorIssue()]),
|
|
114
|
+
}
|
|
115
|
+
: { ok: true });
|
|
114
116
|
if (isPromiseLike(result)) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
return Promise.resolve(result).then(settle, (error) => ({
|
|
118
|
+
ok: false,
|
|
119
|
+
issues: resolveIssueInputs(target, 'server', mapSubmissionError(props.mapSubmitError, error)),
|
|
120
|
+
}));
|
|
117
121
|
}
|
|
118
|
-
|
|
122
|
+
return settle(result);
|
|
119
123
|
};
|
|
120
124
|
const configuration = () => ({
|
|
121
125
|
...(summary.value === null ? {} : { summary: summary.value }),
|
|
@@ -123,10 +127,12 @@ const FormRootImpl = defineComponent({
|
|
|
123
127
|
...(props.validate === undefined ? {} : { validate: props.validate }),
|
|
124
128
|
validateOn: props.validateOn,
|
|
125
129
|
revalidateOn: props.revalidateOn,
|
|
126
|
-
onSubmit: submit,
|
|
127
|
-
onReset: () =>
|
|
130
|
+
...(props.onSubmit === undefined ? {} : { onSubmit: submit }),
|
|
131
|
+
onReset: () => {
|
|
132
|
+
emit('reset');
|
|
133
|
+
void nextTick(syncFieldDiagnostics);
|
|
134
|
+
},
|
|
128
135
|
onStateChange: (next) => {
|
|
129
|
-
state.value = next;
|
|
130
136
|
emit('stateChange', next);
|
|
131
137
|
},
|
|
132
138
|
});
|
|
@@ -136,6 +142,9 @@ const FormRootImpl = defineComponent({
|
|
|
136
142
|
return;
|
|
137
143
|
target.replaceIssues('form', resolveIssueInputs(target, 'form', props.issues));
|
|
138
144
|
};
|
|
145
|
+
syncFieldDiagnostics = () => {
|
|
146
|
+
connection.value?.replaceIssues('field', [...fieldDiagnostics.values()]);
|
|
147
|
+
};
|
|
139
148
|
const mount = () => {
|
|
140
149
|
if (root.value === null)
|
|
141
150
|
return;
|
|
@@ -148,16 +157,24 @@ const FormRootImpl = defineComponent({
|
|
|
148
157
|
registered.unregister = connection.value.registerParticipant(registered.participant);
|
|
149
158
|
}
|
|
150
159
|
syncConfiguredIssues();
|
|
151
|
-
|
|
160
|
+
syncFieldDiagnostics();
|
|
161
|
+
emit('stateChange', connection.value.state);
|
|
152
162
|
};
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
const mountTask = useNextTickTask(mount);
|
|
164
|
+
onMounted(mountTask.schedule);
|
|
165
|
+
onBeforeUnmount(() => {
|
|
166
|
+
mountTask.cancel();
|
|
167
|
+
connection.value?.destroy();
|
|
168
|
+
connection.value = null;
|
|
169
|
+
});
|
|
155
170
|
watch([
|
|
156
171
|
summary,
|
|
157
172
|
() => props.schema,
|
|
158
173
|
() => props.validate,
|
|
159
174
|
() => [...props.validateOn],
|
|
160
175
|
() => [...props.revalidateOn],
|
|
176
|
+
() => props.onSubmit,
|
|
177
|
+
() => props.mapSubmitError,
|
|
161
178
|
], () => {
|
|
162
179
|
void nextTick(() => connection.value?.reconfigure(configuration()));
|
|
163
180
|
});
|
|
@@ -167,30 +184,47 @@ const FormRootImpl = defineComponent({
|
|
|
167
184
|
submitSucceeded: (generation) => connection.value?.submitSucceeded(generation) ?? false,
|
|
168
185
|
submitFailed: (generation, issues = []) => connection.value?.submitFailed(generation, issues) ?? false,
|
|
169
186
|
replaceIssues: (source, issues) => connection.value?.replaceIssues(source, issues) ?? false,
|
|
187
|
+
reinitialize: (options) => connection.value?.reinitialize(options),
|
|
170
188
|
reset: () => connection.value?.reset(),
|
|
171
189
|
};
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
190
|
+
const formContext = {
|
|
191
|
+
summary,
|
|
192
|
+
register,
|
|
193
|
+
setFieldDiagnostic,
|
|
194
|
+
connection,
|
|
195
|
+
};
|
|
196
|
+
provide(formContextKey, formContext);
|
|
197
|
+
const validationStatus = useFormSelectorFromContext(formContext, () => (current) => current.validationStatus);
|
|
198
|
+
const submissionStatus = useFormSelectorFromContext(formContext, () => (current) => current.submissionStatus);
|
|
199
|
+
const selectedState = useFormSelectorFromContext(formContext, () => (current) => current);
|
|
200
|
+
const validationTrigger = useFormSelectorFromContext(formContext, () => (current) => current.validationTrigger);
|
|
201
|
+
const validationIntent = useFormSelectorFromContext(formContext, () => (current) => current.validationIntent);
|
|
202
|
+
const valid = useFormSelectorFromContext(formContext, () => (current) => current.valid);
|
|
203
|
+
const touched = useFormSelectorFromContext(formContext, () => (current) => current.touched);
|
|
204
|
+
const dirty = useFormSelectorFromContext(formContext, () => (current) => current.dirty);
|
|
205
|
+
const submitted = useFormSelectorFromContext(formContext, () => (current) => current.submitted);
|
|
206
|
+
const submitCount = useFormSelectorFromContext(formContext, () => (current) => current.submitCount);
|
|
207
|
+
const slotProps = Object.freeze({
|
|
208
|
+
get state() { return selectedState.value; },
|
|
209
|
+
get validationStatus() { return validationStatus.value; },
|
|
210
|
+
get validationTrigger() { return validationTrigger.value; },
|
|
211
|
+
get validationIntent() { return validationIntent.value; },
|
|
212
|
+
get submissionStatus() { return submissionStatus.value; },
|
|
213
|
+
get valid() { return valid.value; },
|
|
214
|
+
get touched() { return touched.value; },
|
|
215
|
+
get dirty() { return dirty.value; },
|
|
216
|
+
get submitted() { return submitted.value; },
|
|
217
|
+
get submitCount() { return submitCount.value; },
|
|
183
218
|
...actions,
|
|
184
|
-
})
|
|
185
|
-
provide(formContextKey, { state, summary, register, connection });
|
|
219
|
+
});
|
|
186
220
|
expose(actions);
|
|
187
221
|
return () => h('form', mergeProps(attrs, {
|
|
188
222
|
ref: (element) => { root.value = element; },
|
|
189
223
|
'data-scope': 'form',
|
|
190
224
|
'data-part': 'root',
|
|
191
|
-
'data-validation-status':
|
|
192
|
-
'data-submission-status':
|
|
193
|
-
}), slots['default']?.(slotProps
|
|
225
|
+
'data-validation-status': validationStatus.value,
|
|
226
|
+
'data-submission-status': submissionStatus.value,
|
|
227
|
+
}), slots['default']?.(slotProps) ?? []);
|
|
194
228
|
},
|
|
195
229
|
});
|
|
196
230
|
export const FormRoot = FormRootImpl;
|
|
@@ -200,6 +234,7 @@ function toFormSubmitEvent(payload) {
|
|
|
200
234
|
values: payload.values,
|
|
201
235
|
submitter: payload.submitter,
|
|
202
236
|
state: payload.state,
|
|
237
|
+
reinitialize: payload.reinitialize,
|
|
203
238
|
nativeEvent: payload.event,
|
|
204
239
|
get defaultPrevented() { return payload.event.defaultPrevented; },
|
|
205
240
|
preventDefault: () => payload.event.preventDefault(),
|
|
@@ -216,10 +251,8 @@ function isPromiseLike(value) {
|
|
|
216
251
|
function resolveIssueInputs(target, source, input) {
|
|
217
252
|
const fields = target.getSnapshot().state.fields;
|
|
218
253
|
return Object.freeze(input.map((issue, index) => {
|
|
219
|
-
const name = issue.path === undefined ?
|
|
220
|
-
const owner = name === undefined
|
|
221
|
-
? undefined
|
|
222
|
-
: fields.find((field) => field.name === name);
|
|
254
|
+
const name = issue.path === undefined ? null : safeEncodeFormFieldPath(issue.path);
|
|
255
|
+
const owner = name === null ? undefined : findNamedFieldOwner(fields, name);
|
|
223
256
|
return Object.freeze({
|
|
224
257
|
id: issue.id ?? `form-${source}-issue-${index + 1}`,
|
|
225
258
|
message: issue.message,
|
|
@@ -228,6 +261,23 @@ function resolveIssueInputs(target, source, input) {
|
|
|
228
261
|
});
|
|
229
262
|
}));
|
|
230
263
|
}
|
|
264
|
+
function safeEncodeFormFieldPath(path) {
|
|
265
|
+
const result = tryCreateFormFieldPath(path);
|
|
266
|
+
return result.ok ? encodeFormFieldPath(result.value) : null;
|
|
267
|
+
}
|
|
268
|
+
function findNamedFieldOwner(fields, issueName) {
|
|
269
|
+
let owner;
|
|
270
|
+
for (const candidate of fields) {
|
|
271
|
+
if (candidate.name === null
|
|
272
|
+
|| (issueName !== candidate.name
|
|
273
|
+
&& !issueName.startsWith(`${candidate.name}.`)
|
|
274
|
+
&& !issueName.startsWith(`${candidate.name}[`)))
|
|
275
|
+
continue;
|
|
276
|
+
if (owner === undefined || candidate.name.length > (owner.name?.length ?? 0))
|
|
277
|
+
owner = candidate;
|
|
278
|
+
}
|
|
279
|
+
return owner;
|
|
280
|
+
}
|
|
231
281
|
function mapSubmissionError(mapper, reason) {
|
|
232
282
|
if (mapper !== undefined) {
|
|
233
283
|
try {
|
|
@@ -250,6 +300,99 @@ function submissionErrorIssue() {
|
|
|
250
300
|
message: 'Form submission failed.',
|
|
251
301
|
});
|
|
252
302
|
}
|
|
303
|
+
function useFormSelectorFromContext(context, selectorSource, equalsSource = () => Object.is) {
|
|
304
|
+
const initialSelector = selectorSource();
|
|
305
|
+
const selected = shallowRef(initialSelector(context.connection.value?.state ?? emptyState));
|
|
306
|
+
let unsubscribe;
|
|
307
|
+
const stop = watch([context.connection, selectorSource, equalsSource], ([target, selector, equals]) => {
|
|
308
|
+
unsubscribe?.();
|
|
309
|
+
unsubscribe = undefined;
|
|
310
|
+
const next = selector(target?.state ?? emptyState);
|
|
311
|
+
if (!equals(selected.value, next))
|
|
312
|
+
selected.value = next;
|
|
313
|
+
if (target !== null) {
|
|
314
|
+
unsubscribe = target.subscribeForm(selector, (value) => {
|
|
315
|
+
selected.value = value;
|
|
316
|
+
}, { equals });
|
|
317
|
+
}
|
|
318
|
+
}, { immediate: true, flush: 'sync' });
|
|
319
|
+
onScopeDispose(() => {
|
|
320
|
+
unsubscribe?.();
|
|
321
|
+
stop();
|
|
322
|
+
});
|
|
323
|
+
return selected;
|
|
324
|
+
}
|
|
325
|
+
function useFormFieldSelectorFromContext(context, idSource, selectorSource, equalsSource = () => Object.is) {
|
|
326
|
+
const initialSelector = selectorSource();
|
|
327
|
+
const selected = shallowRef(initialSelector(context.connection.value?.getField(idSource()) ?? null));
|
|
328
|
+
let unsubscribe;
|
|
329
|
+
const stop = watch([context.connection, idSource, selectorSource, equalsSource], ([target, id, selector, equals]) => {
|
|
330
|
+
unsubscribe?.();
|
|
331
|
+
unsubscribe = undefined;
|
|
332
|
+
const next = selector(target?.getField(id) ?? null);
|
|
333
|
+
if (!equals(selected.value, next))
|
|
334
|
+
selected.value = next;
|
|
335
|
+
if (target !== null) {
|
|
336
|
+
unsubscribe = target.subscribeField(id, selector, (value) => {
|
|
337
|
+
selected.value = value;
|
|
338
|
+
}, { equals });
|
|
339
|
+
}
|
|
340
|
+
}, { immediate: true, flush: 'sync' });
|
|
341
|
+
onScopeDispose(() => {
|
|
342
|
+
unsubscribe?.();
|
|
343
|
+
stop();
|
|
344
|
+
});
|
|
345
|
+
return selected;
|
|
346
|
+
}
|
|
347
|
+
export function useFormSelector(selector, options = {}) {
|
|
348
|
+
const context = useFormContext('useFormSelector');
|
|
349
|
+
return useFormSelectorFromContext(context, () => selector, () => options.equals ?? Object.is);
|
|
350
|
+
}
|
|
351
|
+
export function useFormFieldSelector(id, selector, options = {}) {
|
|
352
|
+
const context = useFormContext('useFormFieldSelector');
|
|
353
|
+
return useFormFieldSelectorFromContext(context, () => id, () => selector, () => options.equals ?? Object.is);
|
|
354
|
+
}
|
|
355
|
+
export function useFormFieldController(id) {
|
|
356
|
+
const context = useFormContext('useFormFieldController');
|
|
357
|
+
const state = useFormFieldSelectorFromContext(context, () => id, () => (field) => field);
|
|
358
|
+
return Object.freeze({
|
|
359
|
+
state,
|
|
360
|
+
setMeta: (meta) => (context.connection.value?.setFieldMeta(id, meta) ?? false),
|
|
361
|
+
replaceIssues: (source, issues) => (context.connection.value?.replaceFieldIssues(id, source, issues) ?? false),
|
|
362
|
+
upsertIssue: (issue) => (context.connection.value?.upsertFieldIssue(id, issue) ?? false),
|
|
363
|
+
removeIssue: (issueId) => (context.connection.value?.removeFieldIssue(id, issueId) ?? false),
|
|
364
|
+
clearIssues: (source) => (context.connection.value?.clearFieldIssues(id, source) ?? false),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const FormSelectorRuntime = defineComponent({
|
|
368
|
+
name: 'SectileFormSelector',
|
|
369
|
+
props: {
|
|
370
|
+
select: { type: Function, required: true },
|
|
371
|
+
equals: { type: Function, default: Object.is },
|
|
372
|
+
},
|
|
373
|
+
slots: Object,
|
|
374
|
+
setup(props, { slots }) {
|
|
375
|
+
const context = useFormContext('FormSelector');
|
|
376
|
+
const selected = useFormSelectorFromContext(context, () => props.select, () => props.equals);
|
|
377
|
+
return () => slots['default']?.({ selected: selected.value }) ?? [];
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
export const FormSelector = FormSelectorRuntime;
|
|
381
|
+
const FormFieldSelectorRuntime = defineComponent({
|
|
382
|
+
name: 'SectileFormFieldSelector',
|
|
383
|
+
props: {
|
|
384
|
+
id: { type: String, required: true },
|
|
385
|
+
select: { type: Function, required: true },
|
|
386
|
+
equals: { type: Function, default: Object.is },
|
|
387
|
+
},
|
|
388
|
+
slots: Object,
|
|
389
|
+
setup(props, { slots }) {
|
|
390
|
+
const context = useFormContext('FormFieldSelector');
|
|
391
|
+
const selected = useFormFieldSelectorFromContext(context, () => props.id, () => props.select, () => props.equals);
|
|
392
|
+
return () => slots['default']?.({ selected: selected.value }) ?? [];
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
export const FormFieldSelector = FormFieldSelectorRuntime;
|
|
253
396
|
export const FormField = defineComponent({
|
|
254
397
|
name: 'SectileFormField',
|
|
255
398
|
inheritAttrs: false,
|
|
@@ -267,16 +410,21 @@ export const FormField = defineComponent({
|
|
|
267
410
|
const formContext = useFormContext('FormField');
|
|
268
411
|
const generatedId = useHostId();
|
|
269
412
|
const id = computed(() => props.id ?? `form-field-${generatedId}`);
|
|
270
|
-
const nameKey = computed(() => (props.name === undefined ? undefined :
|
|
271
|
-
const root =
|
|
413
|
+
const nameKey = computed(() => (props.name === undefined ? undefined : safeEncodeFormFieldPath(props.name) ?? undefined));
|
|
414
|
+
const root = shallowRef(null);
|
|
272
415
|
const controls = shallowRef([]);
|
|
273
416
|
const fallback = shallowRef(null);
|
|
417
|
+
const nativeFallbackProblem = shallowRef(null);
|
|
274
418
|
const appliedAttributes = new Map();
|
|
275
419
|
let observer;
|
|
276
420
|
let unregister;
|
|
421
|
+
let diagnosticId;
|
|
422
|
+
let observedTargets = [];
|
|
277
423
|
let warnedMultipleControls = false;
|
|
278
|
-
const
|
|
279
|
-
|
|
424
|
+
const activeRegistrations = computed(() => controls.value.filter((registration) => resolveElement(registration.element) !== null));
|
|
425
|
+
const activeControl = computed(() => (activeRegistrations.value.length === 1
|
|
426
|
+
? activeRegistrations.value[0]
|
|
427
|
+
: controls.value.length === 0 ? fallback.value : null));
|
|
280
428
|
const labelMode = computed(() => activeControl.value?.labelMode ?? 'for');
|
|
281
429
|
const semanticControl = () => {
|
|
282
430
|
const registration = activeControl.value;
|
|
@@ -285,7 +433,14 @@ export const FormField = defineComponent({
|
|
|
285
433
|
return resolveElement(registration.semanticControl ?? registration.element);
|
|
286
434
|
};
|
|
287
435
|
const effectiveControlId = computed(() => (semanticControl()?.getAttribute('id')?.trim() || `${id.value}-control`));
|
|
288
|
-
const fieldState =
|
|
436
|
+
const fieldState = useFormFieldSelectorFromContext(formContext, () => id.value, () => (field) => field);
|
|
437
|
+
const fieldActions = Object.freeze({
|
|
438
|
+
setMeta: (meta) => (formContext.connection.value?.setFieldMeta(id.value, meta) ?? false),
|
|
439
|
+
replaceIssues: (source, issues) => (formContext.connection.value?.replaceFieldIssues(id.value, source, issues) ?? false),
|
|
440
|
+
upsertIssue: (issue) => (formContext.connection.value?.upsertFieldIssue(id.value, issue) ?? false),
|
|
441
|
+
removeIssue: (issueId) => formContext.connection.value?.removeFieldIssue(id.value, issueId) ?? false,
|
|
442
|
+
clearIssues: (source) => (formContext.connection.value?.clearFieldIssues(id.value, source) ?? false),
|
|
443
|
+
});
|
|
289
444
|
const slotProps = computed(() => {
|
|
290
445
|
const current = fieldState.value;
|
|
291
446
|
const descriptionId = `${id.value}-description`;
|
|
@@ -303,6 +458,7 @@ export const FormField = defineComponent({
|
|
|
303
458
|
touched: current?.touched ?? false,
|
|
304
459
|
dirty: current?.dirty ?? false,
|
|
305
460
|
issues: current?.issues ?? [],
|
|
461
|
+
...fieldActions,
|
|
306
462
|
});
|
|
307
463
|
});
|
|
308
464
|
const attributesFor = (registration) => {
|
|
@@ -336,6 +492,13 @@ export const FormField = defineComponent({
|
|
|
336
492
|
assign('readonly', true);
|
|
337
493
|
else if (labelMode.value !== 'for' && props.readonly === true)
|
|
338
494
|
assign('aria-readonly', 'true');
|
|
495
|
+
if (labelMode.value !== 'for') {
|
|
496
|
+
const refreshValue = () => {
|
|
497
|
+
queueMicrotask(() => formContext.connection.value?.refreshParticipant(id.value));
|
|
498
|
+
};
|
|
499
|
+
attributes['onClick'] = refreshValue;
|
|
500
|
+
attributes['onKeydown'] = refreshValue;
|
|
501
|
+
}
|
|
339
502
|
return Object.freeze(attributes);
|
|
340
503
|
};
|
|
341
504
|
const restoreControlAttributes = () => {
|
|
@@ -365,8 +528,10 @@ export const FormField = defineComponent({
|
|
|
365
528
|
const capabilities = submission.capabilities ?? nativeSubmissionCapabilities(element);
|
|
366
529
|
const explicit = submission.explicit ?? registration.explicit;
|
|
367
530
|
const attributes = {};
|
|
368
|
-
if (capabilities.name === true &&
|
|
369
|
-
|
|
531
|
+
if (capabilities.name === true && nameKey.value !== undefined) {
|
|
532
|
+
const submissionName = safeEncodeSubmissionName(nameKey.value, submission.relativeName);
|
|
533
|
+
if (submissionName !== null)
|
|
534
|
+
attributes['name'] = submissionName;
|
|
370
535
|
}
|
|
371
536
|
if (capabilities.form === true && props.form !== undefined)
|
|
372
537
|
attributes['form'] = props.form;
|
|
@@ -382,47 +547,98 @@ export const FormField = defineComponent({
|
|
|
382
547
|
const discoverNativeFallback = () => {
|
|
383
548
|
if (controls.value.length > 0 || root.value === null) {
|
|
384
549
|
fallback.value = null;
|
|
550
|
+
nativeFallbackProblem.value = null;
|
|
385
551
|
return;
|
|
386
552
|
}
|
|
387
553
|
const candidates = nativeCandidates(root.value);
|
|
388
|
-
const
|
|
389
|
-
|
|
554
|
+
const discovered = createNativeFallbackRegistration(root.value, candidates);
|
|
555
|
+
nativeFallbackProblem.value = discovered.problem;
|
|
556
|
+
if (discovered.registration === null) {
|
|
390
557
|
fallback.value = null;
|
|
391
558
|
return;
|
|
392
559
|
}
|
|
393
|
-
const
|
|
394
|
-
element: () => element,
|
|
395
|
-
capabilities: nativeSubmissionCapabilities(element),
|
|
396
|
-
}));
|
|
560
|
+
const next = discovered.registration;
|
|
397
561
|
const current = fallback.value;
|
|
398
562
|
if (current !== null
|
|
399
|
-
&& resolveElement(current.semanticControl ?? current.element)
|
|
400
|
-
|
|
563
|
+
&& resolveElement(current.semanticControl ?? current.element)
|
|
564
|
+
=== resolveElement(next.semanticControl ?? next.element)
|
|
565
|
+
&& sameSubmissionElements(resolveSubmissionRegistrations(current), resolveSubmissionRegistrations(next)))
|
|
401
566
|
return;
|
|
402
|
-
fallback.value =
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
567
|
+
fallback.value = next;
|
|
568
|
+
};
|
|
569
|
+
const syncDiagnostic = () => {
|
|
570
|
+
const currentId = id.value;
|
|
571
|
+
if (diagnosticId !== undefined && diagnosticId !== currentId) {
|
|
572
|
+
formContext.setFieldDiagnostic(diagnosticId, null);
|
|
573
|
+
}
|
|
574
|
+
diagnosticId = currentId;
|
|
575
|
+
let message = null;
|
|
576
|
+
if (props.name !== undefined && nameKey.value === undefined) {
|
|
577
|
+
message = 'FormField received an invalid field path.';
|
|
578
|
+
}
|
|
579
|
+
else if (activeRegistrations.value.length > 1) {
|
|
580
|
+
message = 'FormField received multiple active controls. Register one composite control with explicit targets.';
|
|
581
|
+
}
|
|
582
|
+
else if (controls.value.length > 0 && activeRegistrations.value.length === 0) {
|
|
583
|
+
message = 'FormField has no mounted control target.';
|
|
584
|
+
}
|
|
585
|
+
else if (nativeFallbackProblem.value !== null) {
|
|
586
|
+
message = nativeFallbackProblem.value;
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
const registration = activeControl.value;
|
|
590
|
+
if (registration !== null && registration !== undefined) {
|
|
591
|
+
const invalidRelativeName = resolveSubmissionRegistrations(registration).some((submission) => submission.relativeName !== undefined
|
|
592
|
+
&& nameKey.value !== undefined
|
|
593
|
+
&& safeEncodeSubmissionName(nameKey.value, submission.relativeName) === null);
|
|
594
|
+
if (invalidRelativeName)
|
|
595
|
+
message = 'FormField received an invalid relative submission path.';
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
formContext.setFieldDiagnostic(currentId, message === null ? null : Object.freeze({
|
|
599
|
+
id: `${currentId}:composition`,
|
|
600
|
+
fieldId: currentId,
|
|
601
|
+
message,
|
|
602
|
+
source: 'field',
|
|
603
|
+
}));
|
|
604
|
+
};
|
|
605
|
+
const refreshParticipantTargets = () => {
|
|
606
|
+
const registration = activeControl.value;
|
|
607
|
+
const next = registration === null || registration === undefined
|
|
608
|
+
? root.value === null ? [] : [root.value]
|
|
609
|
+
: [
|
|
610
|
+
root.value,
|
|
611
|
+
resolveElement(registration.semanticControl ?? registration.element),
|
|
612
|
+
resolveElement(registration.focusTarget ?? registration.element),
|
|
613
|
+
resolveElement(registration.validationTarget ?? registration.element),
|
|
614
|
+
...resolveSubmissionRegistrations(registration).map((submission) => resolveElement(submission.element)),
|
|
615
|
+
].filter((element) => element !== null);
|
|
616
|
+
if (next.length === observedTargets.length
|
|
617
|
+
&& next.every((element, index) => element === observedTargets[index]))
|
|
618
|
+
return;
|
|
619
|
+
observedTargets = Object.freeze([...next]);
|
|
620
|
+
formContext.connection.value?.refreshParticipant(id.value);
|
|
410
621
|
};
|
|
411
622
|
const registerControl = (registration) => {
|
|
412
623
|
controls.value = [...controls.value, registration];
|
|
413
624
|
fallback.value = null;
|
|
414
625
|
void nextTick(() => {
|
|
415
|
-
|
|
416
|
-
if (!warnedMultipleControls && activeRegistrations.length > 1) {
|
|
626
|
+
if (!warnedMultipleControls && activeRegistrations.value.length > 1) {
|
|
417
627
|
warnedMultipleControls = true;
|
|
418
628
|
console.warn('[Sectile] FormField received multiple active control registrations. Register one composite control with explicit semantic, focus, and submission targets.');
|
|
419
629
|
}
|
|
420
630
|
applyControlAttributes();
|
|
631
|
+
syncDiagnostic();
|
|
632
|
+
refreshParticipantTargets();
|
|
421
633
|
});
|
|
422
634
|
return () => {
|
|
423
635
|
controls.value = controls.value.filter((candidate) => candidate !== registration);
|
|
424
636
|
discoverNativeFallback();
|
|
425
|
-
void nextTick(
|
|
637
|
+
void nextTick(() => {
|
|
638
|
+
applyControlAttributes();
|
|
639
|
+
syncDiagnostic();
|
|
640
|
+
refreshParticipantTargets();
|
|
641
|
+
});
|
|
426
642
|
};
|
|
427
643
|
};
|
|
428
644
|
const mount = () => {
|
|
@@ -435,6 +651,8 @@ export const FormField = defineComponent({
|
|
|
435
651
|
observer = new MutationObserver(() => {
|
|
436
652
|
discoverNativeFallback();
|
|
437
653
|
applyControlAttributes();
|
|
654
|
+
syncDiagnostic();
|
|
655
|
+
refreshParticipantTargets();
|
|
438
656
|
});
|
|
439
657
|
observer.observe(root.value, { childList: true, subtree: true });
|
|
440
658
|
applyControlAttributes();
|
|
@@ -449,6 +667,14 @@ export const FormField = defineComponent({
|
|
|
449
667
|
: resolveElement(registration.focusTarget ?? registration.semanticControl ?? registration.element)
|
|
450
668
|
?? root.value;
|
|
451
669
|
},
|
|
670
|
+
get validationTarget() {
|
|
671
|
+
const registration = activeControl.value;
|
|
672
|
+
return registration === undefined || registration === null
|
|
673
|
+
? root.value
|
|
674
|
+
: resolveElement(registration.validationTarget
|
|
675
|
+
?? registration.semanticControl
|
|
676
|
+
?? registration.element) ?? root.value;
|
|
677
|
+
},
|
|
452
678
|
get submissionElements() {
|
|
453
679
|
const registration = activeControl.value;
|
|
454
680
|
if (registration === undefined || registration === null)
|
|
@@ -465,34 +691,55 @@ export const FormField = defineComponent({
|
|
|
465
691
|
control?.focus();
|
|
466
692
|
return control !== null && document.activeElement === control;
|
|
467
693
|
},
|
|
468
|
-
|
|
694
|
+
reset: () => activeControl.value?.reset?.(),
|
|
695
|
+
get getValue() { return activeControl.value?.getValue; },
|
|
696
|
+
get isValueEqual() { return activeControl.value?.isValueEqual; },
|
|
697
|
+
...(nameKey.value === undefined ? {} : { name: nameKey.value }),
|
|
469
698
|
};
|
|
470
699
|
unregister = formContext.register(participant);
|
|
700
|
+
observedTargets = Object.freeze([
|
|
701
|
+
participant.element,
|
|
702
|
+
participant.semanticControl ?? participant.element,
|
|
703
|
+
participant.focusTarget ?? participant.element,
|
|
704
|
+
participant.validationTarget ?? participant.element,
|
|
705
|
+
...(participant.submissionElements ?? []),
|
|
706
|
+
]);
|
|
707
|
+
syncDiagnostic();
|
|
471
708
|
};
|
|
472
|
-
|
|
709
|
+
const mountTask = useNextTickTask(mount);
|
|
710
|
+
onMounted(mountTask.schedule);
|
|
473
711
|
onBeforeUnmount(() => {
|
|
712
|
+
mountTask.cancel();
|
|
474
713
|
observer?.disconnect();
|
|
714
|
+
observer = undefined;
|
|
475
715
|
restoreControlAttributes();
|
|
716
|
+
if (diagnosticId !== undefined)
|
|
717
|
+
formContext.setFieldDiagnostic(diagnosticId, null);
|
|
476
718
|
unregister?.();
|
|
719
|
+
unregister = undefined;
|
|
477
720
|
});
|
|
478
|
-
watch([id, nameKey],
|
|
721
|
+
watch([id, nameKey], mountTask.schedule);
|
|
479
722
|
watch([
|
|
480
723
|
activeControl,
|
|
481
724
|
effectiveControlId,
|
|
482
725
|
labelMode,
|
|
483
|
-
|
|
726
|
+
nameKey,
|
|
484
727
|
() => props.form,
|
|
485
728
|
() => props.required,
|
|
486
729
|
() => props.disabled,
|
|
487
730
|
() => props.readonly,
|
|
488
731
|
() => slotProps.value.valid,
|
|
489
|
-
],
|
|
732
|
+
], () => {
|
|
733
|
+
applyControlAttributes();
|
|
734
|
+
syncDiagnostic();
|
|
735
|
+
}, { flush: 'post' });
|
|
490
736
|
provide(formFieldContextKey, {
|
|
491
737
|
slotProps,
|
|
492
738
|
labelMode,
|
|
493
739
|
registerControl,
|
|
494
740
|
attributesFor,
|
|
495
741
|
});
|
|
742
|
+
provideFormControlFieldContext({ registerControl, attributesFor });
|
|
496
743
|
return () => h(Primitive, mergeProps(attrs, {
|
|
497
744
|
as: props.as,
|
|
498
745
|
asChild: props.asChild,
|
|
@@ -563,7 +810,8 @@ export const FormSummary = defineComponent({
|
|
|
563
810
|
slots: Object,
|
|
564
811
|
setup(props, { attrs, slots }) {
|
|
565
812
|
const form = useFormContext('FormSummary');
|
|
566
|
-
const
|
|
813
|
+
const valid = useFormSelectorFromContext(form, () => (state) => state.valid);
|
|
814
|
+
const slotProps = computed(() => Object.freeze({ valid: valid.value }));
|
|
567
815
|
return () => h(Primitive, mergeProps(attrs, {
|
|
568
816
|
as: props.as,
|
|
569
817
|
asChild: props.asChild,
|
|
@@ -571,7 +819,7 @@ export const FormSummary = defineComponent({
|
|
|
571
819
|
role: 'alert',
|
|
572
820
|
'aria-live': 'polite',
|
|
573
821
|
tabindex: -1,
|
|
574
|
-
hidden:
|
|
822
|
+
hidden: valid.value,
|
|
575
823
|
'data-scope': 'form',
|
|
576
824
|
'data-part': 'summary',
|
|
577
825
|
}), { default: () => slots['default']?.(slotProps.value) });
|
|
@@ -581,55 +829,54 @@ export const FormReset = defineComponent({
|
|
|
581
829
|
name: 'SectileFormReset', inheritAttrs: false,
|
|
582
830
|
props: { ...partProps, as: { ...partProps.as, default: 'button' } },
|
|
583
831
|
setup(props, { attrs, slots }) {
|
|
584
|
-
const form = useFormContext('FormReset');
|
|
585
|
-
const slotProps = useRootSlotProps(form);
|
|
586
832
|
return () => h(Primitive, mergeProps(attrs, {
|
|
587
833
|
as: props.as,
|
|
588
834
|
asChild: props.asChild,
|
|
589
835
|
...(props.as === 'button' && !props.asChild ? { type: 'reset' } : {}),
|
|
590
836
|
'data-scope': 'form',
|
|
591
837
|
'data-part': 'reset',
|
|
592
|
-
|
|
593
|
-
'data-submission-status': form.state.value.submissionStatus,
|
|
594
|
-
}), { default: () => slots['default']?.(slotProps.value) });
|
|
838
|
+
}), { default: () => slots['default']?.() });
|
|
595
839
|
},
|
|
596
840
|
});
|
|
597
841
|
export const FormSubmit = defineComponent({
|
|
598
842
|
name: 'SectileFormSubmit', inheritAttrs: false,
|
|
599
843
|
props: { ...partProps, as: { ...partProps.as, default: 'button' } },
|
|
844
|
+
slots: Object,
|
|
600
845
|
setup(props, { attrs, slots }) {
|
|
601
846
|
const form = useFormContext('FormSubmit');
|
|
602
|
-
const
|
|
603
|
-
|
|
847
|
+
const valid = useFormSelectorFromContext(form, () => (state) => state.valid);
|
|
848
|
+
const submissionStatus = useFormSelectorFromContext(form, () => (state) => state.submissionStatus);
|
|
849
|
+
const slotProps = computed(() => {
|
|
850
|
+
const submitting = submissionStatus.value === 'submitting';
|
|
851
|
+
return Object.freeze({
|
|
852
|
+
valid: valid.value,
|
|
853
|
+
submitting,
|
|
854
|
+
canSubmit: valid.value && !submitting,
|
|
855
|
+
submissionStatus: submissionStatus.value,
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
const blockPendingSubmit = (event) => {
|
|
859
|
+
if (!slotProps.value.submitting)
|
|
860
|
+
return;
|
|
861
|
+
event.preventDefault();
|
|
862
|
+
event.stopImmediatePropagation();
|
|
863
|
+
};
|
|
864
|
+
return () => h(Primitive, mergeProps({
|
|
865
|
+
onClickCapture: blockPendingSubmit,
|
|
866
|
+
}, attrs, {
|
|
604
867
|
as: props.as,
|
|
605
868
|
asChild: props.asChild,
|
|
606
869
|
...(props.as === 'button' && !props.asChild ? { type: 'submit' } : {}),
|
|
870
|
+
...(props.as === 'button' && !props.asChild && slotProps.value.submitting
|
|
871
|
+
? { disabled: true }
|
|
872
|
+
: {}),
|
|
873
|
+
...(props.asChild && slotProps.value.submitting ? { 'aria-disabled': 'true' } : {}),
|
|
607
874
|
'data-scope': 'form',
|
|
608
875
|
'data-part': 'submit',
|
|
609
|
-
'data-
|
|
610
|
-
'data-submission-status': form.state.value.submissionStatus,
|
|
876
|
+
'data-submission-status': slotProps.value.submissionStatus,
|
|
611
877
|
}), { default: () => slots['default']?.(slotProps.value) });
|
|
612
878
|
},
|
|
613
879
|
});
|
|
614
|
-
function useRootSlotProps(form) {
|
|
615
|
-
return computed(() => Object.freeze({
|
|
616
|
-
state: form.state.value,
|
|
617
|
-
validationStatus: form.state.value.validationStatus,
|
|
618
|
-
validationTrigger: form.state.value.validationTrigger,
|
|
619
|
-
validationIntent: form.state.value.validationIntent,
|
|
620
|
-
submissionStatus: form.state.value.submissionStatus,
|
|
621
|
-
valid: form.state.value.valid,
|
|
622
|
-
touched: form.state.value.touched,
|
|
623
|
-
dirty: form.state.value.dirty,
|
|
624
|
-
submitted: form.state.value.submitted,
|
|
625
|
-
submitCount: form.state.value.submitCount,
|
|
626
|
-
submitStarted: () => form.connection.value?.submitStarted() ?? null,
|
|
627
|
-
submitSucceeded: (generation) => form.connection.value?.submitSucceeded(generation) ?? false,
|
|
628
|
-
submitFailed: (generation, issues = []) => form.connection.value?.submitFailed(generation, issues) ?? false,
|
|
629
|
-
replaceIssues: (source, issues) => form.connection.value?.replaceIssues(source, issues) ?? false,
|
|
630
|
-
reset: () => form.connection.value?.reset(),
|
|
631
|
-
}));
|
|
632
|
-
}
|
|
633
880
|
function renderFieldPart(props, attrs, slots, part, attributes) {
|
|
634
881
|
return h(Primitive, mergeProps(attributes, attrs, {
|
|
635
882
|
as: props.as,
|
|
@@ -638,85 +885,6 @@ function renderFieldPart(props, attrs, slots, part, attributes) {
|
|
|
638
885
|
'data-part': part,
|
|
639
886
|
}), { default: () => slots.default?.() });
|
|
640
887
|
}
|
|
641
|
-
export function useFormControl(registration) {
|
|
642
|
-
const field = inject(formFieldContextKey, null);
|
|
643
|
-
const owned = inject(formControlOwnerKey, false);
|
|
644
|
-
const instance = getCurrentInstance();
|
|
645
|
-
const explicit = Object.freeze([
|
|
646
|
-
...new Set([
|
|
647
|
-
...(registration.explicit ?? []),
|
|
648
|
-
...explicitMetadataAttributes(instance?.vnode.props ?? null),
|
|
649
|
-
]),
|
|
650
|
-
]);
|
|
651
|
-
const normalized = Object.freeze({ ...registration, explicit });
|
|
652
|
-
const controlProps = computed(() => (field !== null && !owned ? field.attributesFor(normalized) : Object.freeze({})));
|
|
653
|
-
let unregister;
|
|
654
|
-
if (field !== null && !owned) {
|
|
655
|
-
onMounted(() => { unregister = field.registerControl(normalized); });
|
|
656
|
-
onBeforeUnmount(() => unregister?.());
|
|
657
|
-
}
|
|
658
|
-
return Object.freeze({ participating: field !== null && !owned, controlProps });
|
|
659
|
-
}
|
|
660
|
-
export const nativeInputControlCapabilities = Object.freeze({
|
|
661
|
-
id: true,
|
|
662
|
-
describedBy: true,
|
|
663
|
-
invalid: true,
|
|
664
|
-
required: true,
|
|
665
|
-
disabled: true,
|
|
666
|
-
readonly: true,
|
|
667
|
-
});
|
|
668
|
-
export const compositeControlCapabilities = Object.freeze({
|
|
669
|
-
id: true,
|
|
670
|
-
describedBy: true,
|
|
671
|
-
invalid: true,
|
|
672
|
-
labelledBy: true,
|
|
673
|
-
});
|
|
674
|
-
export const hiddenInputSubmissionCapabilities = Object.freeze({
|
|
675
|
-
name: true,
|
|
676
|
-
form: true,
|
|
677
|
-
required: true,
|
|
678
|
-
disabled: true,
|
|
679
|
-
});
|
|
680
|
-
export const hiddenSelectSubmissionCapabilities = hiddenInputSubmissionCapabilities;
|
|
681
|
-
export const hiddenValueSubmissionCapabilities = Object.freeze({
|
|
682
|
-
name: true,
|
|
683
|
-
form: true,
|
|
684
|
-
disabled: true,
|
|
685
|
-
});
|
|
686
|
-
export function useNativeInputFormControl(element) {
|
|
687
|
-
return useFormControl({
|
|
688
|
-
element: element,
|
|
689
|
-
semanticControl: element,
|
|
690
|
-
focusTarget: element,
|
|
691
|
-
labelMode: 'for',
|
|
692
|
-
capabilities: nativeInputControlCapabilities,
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
export function useCompositeFormControl(options) {
|
|
696
|
-
return useFormControl({
|
|
697
|
-
element: options.root,
|
|
698
|
-
semanticControl: options.root,
|
|
699
|
-
focusTarget: options.focusTarget ?? options.root,
|
|
700
|
-
labelMode: options.labelMode ?? 'labelledby',
|
|
701
|
-
capabilities: compositeControlCapabilities,
|
|
702
|
-
...(options.submissions === undefined ? {} : { submissions: options.submissions }),
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
export function provideFormControlOwner() {
|
|
706
|
-
provide(formControlOwnerKey, true);
|
|
707
|
-
}
|
|
708
|
-
export function createTypedForm() {
|
|
709
|
-
return Object.freeze({
|
|
710
|
-
Root: FormRoot,
|
|
711
|
-
Field: FormField,
|
|
712
|
-
Label: FormLabel,
|
|
713
|
-
Description: FormDescription,
|
|
714
|
-
Message: FormMessage,
|
|
715
|
-
Summary: FormSummary,
|
|
716
|
-
Reset: FormReset,
|
|
717
|
-
Submit: FormSubmit,
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
888
|
function useFormContext(part) {
|
|
721
889
|
const context = inject(formContextKey, null);
|
|
722
890
|
if (context === null)
|
|
@@ -730,7 +898,7 @@ function useFormFieldContext(part) {
|
|
|
730
898
|
return context;
|
|
731
899
|
}
|
|
732
900
|
function resolveElement(source) {
|
|
733
|
-
return typeof source === 'function' ? source() : source.value;
|
|
901
|
+
return typeof source === 'function' ? source() : source.value ?? null;
|
|
734
902
|
}
|
|
735
903
|
function resolveSubmissionRegistrations(registration) {
|
|
736
904
|
if (registration.submissions !== undefined) {
|
|
@@ -751,16 +919,91 @@ function sameSubmissionElements(left, right) {
|
|
|
751
919
|
return left.length === right.length && left.every((submission, index) => (resolveElement(submission.element) === resolveElement(right[index].element)));
|
|
752
920
|
}
|
|
753
921
|
function nativeCandidates(root) {
|
|
754
|
-
const selector = '
|
|
922
|
+
const selector = 'fieldset, input, select, textarea';
|
|
755
923
|
const candidates = [
|
|
756
924
|
...(root.matches(selector) ? [root] : []),
|
|
757
925
|
...root.querySelectorAll(selector),
|
|
758
926
|
];
|
|
759
927
|
return candidates.filter((candidate) => (candidate.closest('[data-scope="form"][data-part="field"]') === root));
|
|
760
928
|
}
|
|
929
|
+
function createNativeFallbackRegistration(root, candidates) {
|
|
930
|
+
const semantic = nativeSemanticControl(candidates);
|
|
931
|
+
if (semantic === undefined || isHiddenInput(semantic)) {
|
|
932
|
+
return {
|
|
933
|
+
registration: null,
|
|
934
|
+
problem: 'FormField has no visible native semantic or focus target.',
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
const submissions = candidates.filter(isFormSubmissionElement);
|
|
938
|
+
const visible = candidates.filter((candidate) => !isHiddenInput(candidate));
|
|
939
|
+
const fieldset = visible.find((candidate) => candidate.tagName === 'FIELDSET');
|
|
940
|
+
if (fieldset !== undefined) {
|
|
941
|
+
return {
|
|
942
|
+
registration: nativeFallbackForElement(fieldset, submissions),
|
|
943
|
+
problem: null,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
if (visible.length === 1) {
|
|
947
|
+
return {
|
|
948
|
+
registration: nativeFallbackForElement(semantic, submissions),
|
|
949
|
+
problem: null,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
if (isNativeCheckedGroup(visible)) {
|
|
953
|
+
const focusTarget = visible.find((candidate) => !candidate.disabled) ?? visible[0];
|
|
954
|
+
return {
|
|
955
|
+
registration: {
|
|
956
|
+
element: () => root,
|
|
957
|
+
semanticControl: () => root,
|
|
958
|
+
focusTarget: () => focusTarget,
|
|
959
|
+
validationTarget: () => root,
|
|
960
|
+
submissions: submissions.map((element) => ({
|
|
961
|
+
element: () => element,
|
|
962
|
+
capabilities: Object.freeze({
|
|
963
|
+
...nativeSubmissionCapabilities(element),
|
|
964
|
+
required: false,
|
|
965
|
+
}),
|
|
966
|
+
})),
|
|
967
|
+
labelMode: 'labelledby',
|
|
968
|
+
capabilities: compositeControlCapabilities,
|
|
969
|
+
},
|
|
970
|
+
problem: null,
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
registration: null,
|
|
975
|
+
problem: 'FormField contains multiple unrelated native controls. Use a fieldset or one composite registration.',
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
function nativeFallbackForElement(semantic, submissions) {
|
|
979
|
+
return {
|
|
980
|
+
element: () => semantic,
|
|
981
|
+
semanticControl: () => semantic,
|
|
982
|
+
focusTarget: () => semantic,
|
|
983
|
+
validationTarget: () => semantic,
|
|
984
|
+
submissions: submissions.map((element) => ({
|
|
985
|
+
element: () => element,
|
|
986
|
+
capabilities: nativeSubmissionCapabilities(element),
|
|
987
|
+
})),
|
|
988
|
+
labelMode: nativeLabelMode(semantic),
|
|
989
|
+
capabilities: nativeControlCapabilities(semantic),
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function isHiddenInput(element) {
|
|
993
|
+
return element.tagName === 'INPUT'
|
|
994
|
+
&& element.getAttribute('type')?.toLowerCase() === 'hidden';
|
|
995
|
+
}
|
|
996
|
+
function isNativeCheckedGroup(candidates) {
|
|
997
|
+
if (candidates.length < 2)
|
|
998
|
+
return false;
|
|
999
|
+
const types = candidates.map((candidate) => (candidate.tagName === 'INPUT'
|
|
1000
|
+
? candidate.type.toLowerCase()
|
|
1001
|
+
: null));
|
|
1002
|
+
return types.every((type) => type === types[0])
|
|
1003
|
+
&& (types[0] === 'checkbox' || types[0] === 'radio');
|
|
1004
|
+
}
|
|
761
1005
|
function nativeSemanticControl(candidates) {
|
|
762
|
-
return candidates.find((candidate) => (candidate
|
|
763
|
-
|| candidate.getAttribute('type')?.toLowerCase() !== 'hidden')) ?? candidates[0];
|
|
1006
|
+
return candidates.find((candidate) => !isHiddenInput(candidate)) ?? candidates[0];
|
|
764
1007
|
}
|
|
765
1008
|
function isFormSubmissionElement(element) {
|
|
766
1009
|
return ['INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName);
|
|
@@ -798,14 +1041,21 @@ function nativeSubmissionCapabilities(element) {
|
|
|
798
1041
|
readonly: tag === 'TEXTAREA' || (tag === 'INPUT' && supportsReadonly(element)),
|
|
799
1042
|
});
|
|
800
1043
|
}
|
|
801
|
-
function
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1044
|
+
function safeEncodeSubmissionName(base, relative) {
|
|
1045
|
+
try {
|
|
1046
|
+
if (relative === undefined)
|
|
1047
|
+
return safeEncodeFormFieldPath(base);
|
|
1048
|
+
return safeEncodeFormFieldPath(appendFormFieldPath(base, relative));
|
|
1049
|
+
}
|
|
1050
|
+
catch {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
805
1053
|
}
|
|
806
1054
|
function applyMetadata(element, attributes, explicitAttributes, applied) {
|
|
807
1055
|
const explicit = new Set(explicitAttributes ?? []);
|
|
808
1056
|
for (const [name, value] of Object.entries(attributes)) {
|
|
1057
|
+
if (name.startsWith('on') && typeof value === 'function')
|
|
1058
|
+
continue;
|
|
809
1059
|
if (value === undefined || value === false || explicit.has(name))
|
|
810
1060
|
continue;
|
|
811
1061
|
const previous = element.getAttribute(name);
|
|
@@ -825,28 +1075,6 @@ function applyMetadata(element, attributes, explicitAttributes, applied) {
|
|
|
825
1075
|
element.setAttribute(name, next);
|
|
826
1076
|
}
|
|
827
1077
|
}
|
|
828
|
-
function explicitMetadataAttributes(vnodeProps) {
|
|
829
|
-
if (vnodeProps === null)
|
|
830
|
-
return [];
|
|
831
|
-
const aliases = {
|
|
832
|
-
id: ['id'],
|
|
833
|
-
name: ['name'],
|
|
834
|
-
form: ['form'],
|
|
835
|
-
required: ['required'],
|
|
836
|
-
disabled: ['disabled'],
|
|
837
|
-
readonly: ['readonly', 'readOnly'],
|
|
838
|
-
'aria-describedby': ['aria-describedby', 'ariaDescribedby'],
|
|
839
|
-
'aria-errormessage': ['aria-errormessage', 'ariaErrormessage'],
|
|
840
|
-
'aria-invalid': ['aria-invalid', 'ariaInvalid'],
|
|
841
|
-
'aria-labelledby': ['aria-labelledby', 'ariaLabelledby'],
|
|
842
|
-
'aria-disabled': ['aria-disabled', 'ariaDisabled'],
|
|
843
|
-
'aria-required': ['aria-required', 'ariaRequired'],
|
|
844
|
-
'aria-readonly': ['aria-readonly', 'ariaReadonly'],
|
|
845
|
-
};
|
|
846
|
-
return Object.entries(aliases)
|
|
847
|
-
.filter(([, names]) => names.some((name) => Object.hasOwn(vnodeProps, name)))
|
|
848
|
-
.map(([attribute]) => attribute);
|
|
849
|
-
}
|
|
850
1078
|
function supportsReadonly(element) {
|
|
851
1079
|
return !new Set([
|
|
852
1080
|
'button', 'checkbox', 'color', 'file', 'hidden', 'image', 'radio', 'range',
|