@auronui/vue 1.0.9 → 1.0.11
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/dist/cjs/index.cjs +186 -14
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/form/Form.js.map +1 -1
- package/dist/components/form/Form.vue_vue_type_script_setup_true_lang.js +123 -8
- package/dist/components/form/Form.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/FormField.js.map +1 -1
- package/dist/components/form/FormField.vue_vue_type_script_setup_true_lang.js +30 -2
- package/dist/components/form/FormField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/form.context.js.map +1 -1
- package/dist/components/form/validation.js +23 -3
- package/dist/components/form/validation.js.map +1 -1
- package/dist/components/select/SelectContent.js.map +1 -1
- package/dist/components/select/SelectContent.vue_vue_type_script_setup_true_lang.js +11 -2
- package/dist/components/select/SelectContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/index.d.ts +77 -4
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.js","names":[],"sources":["../../../src/components/form/Form.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, provide } from 'vue'\nimport { formContextKey } from './form.context'\nimport { runValidation } from './validation'\nimport type { ValidationMode, FieldRegistration, FormContext } from './form.context'\n\nconst props = withDefaults(\n defineProps<{\n validationMode?: ValidationMode\n isDisabled?: boolean\n class?: string\n }>(),\n {\n validationMode: 'on-submit',\n isDisabled: false,\n class: undefined,\n },\n)\n\nconst emit = defineEmits<{\n submit: [payload: { values: Record<string, unknown>; setErrors: (e: Record<string, string>) => void }]\n invalid: [errors: Record<string, string>]\n}>()\n\nconst errors = ref<Record<string, string>>({})\nconst isSubmitting = ref(false)\nconst fields = new Map<string, FieldRegistration>()\n\nfunction registerField(reg: FieldRegistration): void {\n fields.set(reg.name, reg)\n}\n\nfunction unregisterField(name: string): void {\n fields.delete(name)\n const next = { ...errors.value }\n delete next[name]\n errors.value = next\n}\n\nasync function triggerFieldValidation(name: string): Promise<void> {\n const field = fields.get(name)\n if (!field) return\n const error = await runValidation(field.getValue()
|
|
1
|
+
{"version":3,"file":"Form.js","names":[],"sources":["../../../src/components/form/Form.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, provide } from 'vue'\nimport { formContextKey } from './form.context'\nimport { runValidation } from './validation'\nimport type { ValidationMode, FieldRegistration, FormContext } from './form.context'\n\nconst props = withDefaults(\n defineProps<{\n validationMode?: ValidationMode\n isDisabled?: boolean\n class?: string\n }>(),\n {\n validationMode: 'on-submit',\n isDisabled: false,\n class: undefined,\n },\n)\n\nconst emit = defineEmits<{\n submit: [payload: { values: Record<string, unknown>; setErrors: (e: Record<string, string>) => void }]\n invalid: [errors: Record<string, string>]\n reset: []\n}>()\n\nconst errors = ref<Record<string, string>>({})\nconst isSubmitting = ref(false)\nconst isSubmitted = ref(false)\nconst submitCount = ref(0)\nconst fields = new Map<string, FieldRegistration>()\n\n// Reactive field count so computed properties re-evaluate when fields register/unregister\nconst fieldCount = ref(0)\n\nfunction registerField(reg: FieldRegistration): void {\n fields.set(reg.name, reg)\n fieldCount.value++\n}\n\nfunction unregisterField(name: string): void {\n fields.delete(name)\n fieldCount.value--\n const next = { ...errors.value }\n delete next[name]\n errors.value = next\n}\n\n// ── Computed state ──────────────────────────────────────────────────────────\n\nconst isValid = computed(() => {\n void fieldCount.value // track reactivity\n return Object.keys(errors.value).length === 0\n})\n\nconst isDirty = computed(() => {\n void fieldCount.value\n for (const field of fields.values()) {\n if (field.dirty.value) return true\n }\n return false\n})\n\nconst isTouched = computed(() => {\n void fieldCount.value\n for (const field of fields.values()) {\n if (field.touched.value) return true\n }\n return false\n})\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getAllValues(): Record<string, unknown> {\n const values: Record<string, unknown> = {}\n for (const [name, field] of fields.entries()) {\n values[name] = field.getValue()\n }\n return values\n}\n\n// ── Validation ───────────────────────────────────────────────────────────────\n\nasync function triggerFieldValidation(name: string): Promise<void> {\n const field = fields.get(name)\n if (!field) return\n const error = await runValidation(\n field.getValue(),\n field.rules,\n field.validate,\n { values: getAllValues() },\n )\n const next = { ...errors.value }\n if (error) {\n next[name] = error\n } else {\n delete next[name]\n }\n errors.value = next\n}\n\nasync function trigger(name?: string): Promise<boolean> {\n if (name) {\n await triggerFieldValidation(name)\n return !errors.value[name]\n }\n\n const results = await Promise.all(\n [...fields.entries()].map(async ([fieldName, field]) => {\n const error = await runValidation(\n field.getValue(),\n field.rules,\n field.validate,\n { values: getAllValues() },\n )\n return { name: fieldName, error }\n }),\n )\n\n const next: Record<string, string> = {}\n for (const { name: fieldName, error } of results) {\n if (error) next[fieldName] = error\n }\n errors.value = next\n return Object.keys(next).length === 0\n}\n\n// ── Error management ─────────────────────────────────────────────────────────\n\nfunction setErrors(newErrors: Record<string, string>): void {\n errors.value = { ...errors.value, ...newErrors }\n isSubmitting.value = false\n}\n\nfunction setError(name: string, message: string): void {\n errors.value = { ...errors.value, [name]: message }\n}\n\nfunction clearErrors(name?: string): void {\n if (name) {\n const next = { ...errors.value }\n delete next[name]\n errors.value = next\n } else {\n errors.value = {}\n }\n}\n\n// ── Values ───────────────────────────────────────────────────────────────────\n\nfunction getValues(): Record<string, unknown> {\n return getAllValues()\n}\n\nfunction setValue(name: string, value: unknown): void {\n const field = fields.get(name)\n if (field) field.setValue(value)\n}\n\n// ── Reset ────────────────────────────────────────────────────────────────────\n\nfunction reset(): void {\n for (const field of fields.values()) {\n field.reset()\n }\n errors.value = {}\n isSubmitting.value = false\n isSubmitted.value = false\n submitCount.value = 0\n emit('reset')\n}\n\n// ── Submit ───────────────────────────────────────────────────────────────────\n\nasync function handleSubmit(): Promise<void> {\n isSubmitting.value = true\n submitCount.value++\n\n const values = getAllValues()\n const context = { values }\n\n const results = await Promise.all(\n [...fields.entries()].map(async ([name, field]) => {\n const error = await runValidation(field.getValue(), field.rules, field.validate, context)\n return { name, error }\n }),\n )\n\n const nextErrors: Record<string, string> = {}\n for (const { name, error } of results) {\n if (error) nextErrors[name] = error\n }\n\n if (Object.keys(nextErrors).length > 0) {\n errors.value = nextErrors\n isSubmitting.value = false\n isSubmitted.value = true\n emit('invalid', nextErrors)\n return\n }\n\n errors.value = {}\n isSubmitted.value = true\n isSubmitting.value = false\n emit('submit', { values, setErrors })\n}\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\nconst ctx: FormContext = {\n errors,\n isSubmitting,\n isSubmitted,\n submitCount,\n isDisabled: computed(() => props.isDisabled),\n isValid,\n isDirty,\n isTouched,\n validationMode: computed(() => props.validationMode),\n registerField,\n unregisterField,\n triggerFieldValidation,\n setErrors,\n setError,\n clearErrors,\n getValues,\n setValue,\n trigger,\n reset,\n}\n\nprovide(formContextKey, ctx)\n\ndefineExpose({\n errors,\n isSubmitting,\n isSubmitted,\n submitCount,\n isValid,\n isDirty,\n isTouched,\n getValues,\n setValue,\n setErrors,\n setError,\n clearErrors,\n trigger,\n reset,\n})\n</script>\n\n<template>\n <form\n :class=\"props.class\"\n novalidate\n @submit.prevent=\"handleSubmit\"\n >\n <slot\n :is-submitting=\"isSubmitting\"\n :is-submitted=\"isSubmitted\"\n :submit-count=\"submitCount\"\n :is-disabled=\"props.isDisabled\"\n :is-valid=\"isValid\"\n :is-dirty=\"isDirty\"\n :is-touched=\"isTouched\"\n :errors=\"errors\"\n :get-values=\"getValues\"\n :set-value=\"setValue\"\n :set-errors=\"setErrors\"\n :set-error=\"setError\"\n :clear-errors=\"clearErrors\"\n :trigger=\"trigger\"\n :reset=\"reset\"\n />\n </form>\n</template>\n"],"mappings":""}
|
|
@@ -12,31 +12,75 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
|
|
|
12
12
|
},
|
|
13
13
|
class: { default: void 0 }
|
|
14
14
|
},
|
|
15
|
-
emits: [
|
|
16
|
-
|
|
15
|
+
emits: [
|
|
16
|
+
"submit",
|
|
17
|
+
"invalid",
|
|
18
|
+
"reset"
|
|
19
|
+
],
|
|
20
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
17
21
|
const props = __props;
|
|
18
22
|
const emit = __emit;
|
|
19
23
|
const errors = ref({});
|
|
20
24
|
const isSubmitting = ref(false);
|
|
25
|
+
const isSubmitted = ref(false);
|
|
26
|
+
const submitCount = ref(0);
|
|
21
27
|
const fields = /* @__PURE__ */ new Map();
|
|
28
|
+
const fieldCount = ref(0);
|
|
22
29
|
function registerField(reg) {
|
|
23
30
|
fields.set(reg.name, reg);
|
|
31
|
+
fieldCount.value++;
|
|
24
32
|
}
|
|
25
33
|
function unregisterField(name) {
|
|
26
34
|
fields.delete(name);
|
|
35
|
+
fieldCount.value--;
|
|
27
36
|
const next = { ...errors.value };
|
|
28
37
|
delete next[name];
|
|
29
38
|
errors.value = next;
|
|
30
39
|
}
|
|
40
|
+
const isValid = computed(() => {
|
|
41
|
+
fieldCount.value;
|
|
42
|
+
return Object.keys(errors.value).length === 0;
|
|
43
|
+
});
|
|
44
|
+
const isDirty = computed(() => {
|
|
45
|
+
fieldCount.value;
|
|
46
|
+
for (const field of fields.values()) if (field.dirty.value) return true;
|
|
47
|
+
return false;
|
|
48
|
+
});
|
|
49
|
+
const isTouched = computed(() => {
|
|
50
|
+
fieldCount.value;
|
|
51
|
+
for (const field of fields.values()) if (field.touched.value) return true;
|
|
52
|
+
return false;
|
|
53
|
+
});
|
|
54
|
+
function getAllValues() {
|
|
55
|
+
const values = {};
|
|
56
|
+
for (const [name, field] of fields.entries()) values[name] = field.getValue();
|
|
57
|
+
return values;
|
|
58
|
+
}
|
|
31
59
|
async function triggerFieldValidation(name) {
|
|
32
60
|
const field = fields.get(name);
|
|
33
61
|
if (!field) return;
|
|
34
|
-
const error = await runValidation(field.getValue(), field.rules, field.validate);
|
|
62
|
+
const error = await runValidation(field.getValue(), field.rules, field.validate, { values: getAllValues() });
|
|
35
63
|
const next = { ...errors.value };
|
|
36
64
|
if (error) next[name] = error;
|
|
37
65
|
else delete next[name];
|
|
38
66
|
errors.value = next;
|
|
39
67
|
}
|
|
68
|
+
async function trigger(name) {
|
|
69
|
+
if (name) {
|
|
70
|
+
await triggerFieldValidation(name);
|
|
71
|
+
return !errors.value[name];
|
|
72
|
+
}
|
|
73
|
+
const results = await Promise.all([...fields.entries()].map(async ([fieldName, field]) => {
|
|
74
|
+
return {
|
|
75
|
+
name: fieldName,
|
|
76
|
+
error: await runValidation(field.getValue(), field.rules, field.validate, { values: getAllValues() })
|
|
77
|
+
};
|
|
78
|
+
}));
|
|
79
|
+
const next = {};
|
|
80
|
+
for (const { name: fieldName, error } of results) if (error) next[fieldName] = error;
|
|
81
|
+
errors.value = next;
|
|
82
|
+
return Object.keys(next).length === 0;
|
|
83
|
+
}
|
|
40
84
|
function setErrors(newErrors) {
|
|
41
85
|
errors.value = {
|
|
42
86
|
...errors.value,
|
|
@@ -44,12 +88,43 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
|
|
|
44
88
|
};
|
|
45
89
|
isSubmitting.value = false;
|
|
46
90
|
}
|
|
91
|
+
function setError(name, message) {
|
|
92
|
+
errors.value = {
|
|
93
|
+
...errors.value,
|
|
94
|
+
[name]: message
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function clearErrors(name) {
|
|
98
|
+
if (name) {
|
|
99
|
+
const next = { ...errors.value };
|
|
100
|
+
delete next[name];
|
|
101
|
+
errors.value = next;
|
|
102
|
+
} else errors.value = {};
|
|
103
|
+
}
|
|
104
|
+
function getValues() {
|
|
105
|
+
return getAllValues();
|
|
106
|
+
}
|
|
107
|
+
function setValue(name, value) {
|
|
108
|
+
const field = fields.get(name);
|
|
109
|
+
if (field) field.setValue(value);
|
|
110
|
+
}
|
|
111
|
+
function reset() {
|
|
112
|
+
for (const field of fields.values()) field.reset();
|
|
113
|
+
errors.value = {};
|
|
114
|
+
isSubmitting.value = false;
|
|
115
|
+
isSubmitted.value = false;
|
|
116
|
+
submitCount.value = 0;
|
|
117
|
+
emit("reset");
|
|
118
|
+
}
|
|
47
119
|
async function handleSubmit() {
|
|
48
120
|
isSubmitting.value = true;
|
|
121
|
+
submitCount.value++;
|
|
122
|
+
const values = getAllValues();
|
|
123
|
+
const context = { values };
|
|
49
124
|
const results = await Promise.all([...fields.entries()].map(async ([name, field]) => {
|
|
50
125
|
return {
|
|
51
126
|
name,
|
|
52
|
-
error: await runValidation(field.getValue(), field.rules, field.validate)
|
|
127
|
+
error: await runValidation(field.getValue(), field.rules, field.validate, context)
|
|
53
128
|
};
|
|
54
129
|
}));
|
|
55
130
|
const nextErrors = {};
|
|
@@ -57,12 +132,13 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
|
|
|
57
132
|
if (Object.keys(nextErrors).length > 0) {
|
|
58
133
|
errors.value = nextErrors;
|
|
59
134
|
isSubmitting.value = false;
|
|
135
|
+
isSubmitted.value = true;
|
|
60
136
|
emit("invalid", nextErrors);
|
|
61
137
|
return;
|
|
62
138
|
}
|
|
63
139
|
errors.value = {};
|
|
64
|
-
|
|
65
|
-
|
|
140
|
+
isSubmitted.value = true;
|
|
141
|
+
isSubmitting.value = false;
|
|
66
142
|
emit("submit", {
|
|
67
143
|
values,
|
|
68
144
|
setErrors
|
|
@@ -71,12 +147,39 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
|
|
|
71
147
|
provide(formContextKey, {
|
|
72
148
|
errors,
|
|
73
149
|
isSubmitting,
|
|
150
|
+
isSubmitted,
|
|
151
|
+
submitCount,
|
|
74
152
|
isDisabled: computed(() => props.isDisabled),
|
|
153
|
+
isValid,
|
|
154
|
+
isDirty,
|
|
155
|
+
isTouched,
|
|
75
156
|
validationMode: computed(() => props.validationMode),
|
|
76
157
|
registerField,
|
|
77
158
|
unregisterField,
|
|
78
159
|
triggerFieldValidation,
|
|
79
|
-
setErrors
|
|
160
|
+
setErrors,
|
|
161
|
+
setError,
|
|
162
|
+
clearErrors,
|
|
163
|
+
getValues,
|
|
164
|
+
setValue,
|
|
165
|
+
trigger,
|
|
166
|
+
reset
|
|
167
|
+
});
|
|
168
|
+
__expose({
|
|
169
|
+
errors,
|
|
170
|
+
isSubmitting,
|
|
171
|
+
isSubmitted,
|
|
172
|
+
submitCount,
|
|
173
|
+
isValid,
|
|
174
|
+
isDirty,
|
|
175
|
+
isTouched,
|
|
176
|
+
getValues,
|
|
177
|
+
setValue,
|
|
178
|
+
setErrors,
|
|
179
|
+
setError,
|
|
180
|
+
clearErrors,
|
|
181
|
+
trigger,
|
|
182
|
+
reset
|
|
80
183
|
});
|
|
81
184
|
return (_ctx, _cache) => {
|
|
82
185
|
return openBlock(), createElementBlock("form", {
|
|
@@ -85,8 +188,20 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
|
|
|
85
188
|
onSubmit: withModifiers(handleSubmit, ["prevent"])
|
|
86
189
|
}, [renderSlot(_ctx.$slots, "default", {
|
|
87
190
|
isSubmitting: isSubmitting.value,
|
|
191
|
+
isSubmitted: isSubmitted.value,
|
|
192
|
+
submitCount: submitCount.value,
|
|
88
193
|
isDisabled: props.isDisabled,
|
|
89
|
-
|
|
194
|
+
isValid: isValid.value,
|
|
195
|
+
isDirty: isDirty.value,
|
|
196
|
+
isTouched: isTouched.value,
|
|
197
|
+
errors: errors.value,
|
|
198
|
+
getValues,
|
|
199
|
+
setValue,
|
|
200
|
+
setErrors,
|
|
201
|
+
setError,
|
|
202
|
+
clearErrors,
|
|
203
|
+
trigger,
|
|
204
|
+
reset
|
|
90
205
|
})], 34);
|
|
91
206
|
};
|
|
92
207
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/Form.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, provide } from 'vue'\nimport { formContextKey } from './form.context'\nimport { runValidation } from './validation'\nimport type { ValidationMode, FieldRegistration, FormContext } from './form.context'\n\nconst props = withDefaults(\n defineProps<{\n validationMode?: ValidationMode\n isDisabled?: boolean\n class?: string\n }>(),\n {\n validationMode: 'on-submit',\n isDisabled: false,\n class: undefined,\n },\n)\n\nconst emit = defineEmits<{\n submit: [payload: { values: Record<string, unknown>; setErrors: (e: Record<string, string>) => void }]\n invalid: [errors: Record<string, string>]\n}>()\n\nconst errors = ref<Record<string, string>>({})\nconst isSubmitting = ref(false)\nconst fields = new Map<string, FieldRegistration>()\n\nfunction registerField(reg: FieldRegistration): void {\n fields.set(reg.name, reg)\n}\n\nfunction unregisterField(name: string): void {\n fields.delete(name)\n const next = { ...errors.value }\n delete next[name]\n errors.value = next\n}\n\nasync function triggerFieldValidation(name: string): Promise<void> {\n const field = fields.get(name)\n if (!field) return\n const error = await runValidation(field.getValue(), field.rules, field.validate)\n const next = { ...errors.value }\n if (error) {\n next[name] = error\n } else {\n delete next[name]\n }\n errors.value = next\n}\n\nfunction setErrors(newErrors: Record<string, string>): void {\n errors.value = { ...errors.value, ...newErrors }\n isSubmitting.value = false\n}\n\nasync function handleSubmit(): Promise<void> {\n isSubmitting.value = true\n\n const results = await Promise.all(\n [...fields.entries()].map(async ([name, field]) => {\n const error = await runValidation(field.getValue(), field.rules, field.validate)\n return { name, error }\n }),\n )\n\n const nextErrors: Record<string, string> = {}\n for (const { name, error } of results) {\n if (error) nextErrors[name] = error\n }\n\n if (Object.keys(nextErrors).length > 0) {\n errors.value = nextErrors\n isSubmitting.value = false\n emit('invalid', nextErrors)\n return\n }\n\n errors.value = {}\n\n const values: Record<string, unknown> = {}\n for (const [name, field] of fields.entries()) {\n values[name] = field.getValue()\n }\n\n emit('submit', { values, setErrors })\n}\n\nconst ctx: FormContext = {\n errors,\n isSubmitting,\n isDisabled: computed(() => props.isDisabled),\n validationMode: computed(() => props.validationMode),\n registerField,\n unregisterField,\n triggerFieldValidation,\n setErrors,\n}\n\nprovide(formContextKey, ctx)\n</script>\n\n<template>\n <form\n :class=\"props.class\"\n novalidate\n @submit.prevent=\"handleSubmit\"\n >\n <slot\n :is-submitting=\"isSubmitting\"\n :is-disabled=\"props.isDisabled\"\n :errors=\"errors\"\n />\n </form>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAMA,MAAM,QAAQ;EAad,MAAM,OAAO;EAKb,MAAM,SAAS,IAA4B,EAAE,CAAA;EAC7C,MAAM,eAAe,IAAI,MAAK;EAC9B,MAAM,yBAAS,IAAI,KAA+B;EAElD,SAAS,cAAc,KAA8B;AACnD,UAAO,IAAI,IAAI,MAAM,IAAG;;EAG1B,SAAS,gBAAgB,MAAoB;AAC3C,UAAO,OAAO,KAAI;GAClB,MAAM,OAAO,EAAE,GAAG,OAAO,OAAM;AAC/B,UAAO,KAAK;AACZ,UAAO,QAAQ;;EAGjB,eAAe,uBAAuB,MAA6B;GACjE,MAAM,QAAQ,OAAO,IAAI,KAAI;AAC7B,OAAI,CAAC,MAAO;GACZ,MAAM,QAAQ,MAAM,cAAc,MAAM,UAAU,EAAE,MAAM,OAAO,MAAM,SAAQ;GAC/E,MAAM,OAAO,EAAE,GAAG,OAAO,OAAM;AAC/B,OAAI,MACF,MAAK,QAAQ;OAEb,QAAO,KAAK;AAEd,UAAO,QAAQ;;EAGjB,SAAS,UAAU,WAAyC;AAC1D,UAAO,QAAQ;IAAE,GAAG,OAAO;IAAO,GAAG;IAAU;AAC/C,gBAAa,QAAQ;;EAGvB,eAAe,eAA8B;AAC3C,gBAAa,QAAQ;GAErB,MAAM,UAAU,MAAM,QAAQ,IAC5B,CAAC,GAAG,OAAO,SAAS,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,WAAW;AAEjD,WAAO;KAAE;KAAM,OADD,MAAM,cAAc,MAAM,UAAU,EAAE,MAAM,OAAO,MAAM,SAAQ;KAC1D;KACrB,CACJ;GAEA,MAAM,aAAqC,EAAC;AAC5C,QAAK,MAAM,EAAE,MAAM,WAAW,QAC5B,KAAI,MAAO,YAAW,QAAQ;AAGhC,OAAI,OAAO,KAAK,WAAW,CAAC,SAAS,GAAG;AACtC,WAAO,QAAQ;AACf,iBAAa,QAAQ;AACrB,SAAK,WAAW,WAAU;AAC1B;;AAGF,UAAO,QAAQ,EAAC;GAEhB,MAAM,SAAkC,EAAC;AACzC,QAAK,MAAM,CAAC,MAAM,UAAU,OAAO,SAAS,CAC1C,QAAO,QAAQ,MAAM,UAAS;AAGhC,QAAK,UAAU;IAAE;IAAQ;IAAW,CAAA;;AActC,UAAQ,gBAXiB;GACvB;GACA;GACA,YAAY,eAAe,MAAM,WAAW;GAC5C,gBAAgB,eAAe,MAAM,eAAe;GACpD;GACA;GACA;GACA;GACF,CAE2B;;uBAIzB,mBAUO,QAAA;IATJ,OAAK,eAAE,MAAM,MAAK;IACnB,YAAA;IACC,UAAM,cAAU,cAAY,CAAA,UAAA,CAAA;OAE7B,WAIE,KAAA,QAAA,WAAA;IAHC,cAAe,aAAA;IACf,YAAa,MAAM;IACnB,QAAQ,OAAA"}
|
|
1
|
+
{"version":3,"file":"Form.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/Form.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, provide } from 'vue'\nimport { formContextKey } from './form.context'\nimport { runValidation } from './validation'\nimport type { ValidationMode, FieldRegistration, FormContext } from './form.context'\n\nconst props = withDefaults(\n defineProps<{\n validationMode?: ValidationMode\n isDisabled?: boolean\n class?: string\n }>(),\n {\n validationMode: 'on-submit',\n isDisabled: false,\n class: undefined,\n },\n)\n\nconst emit = defineEmits<{\n submit: [payload: { values: Record<string, unknown>; setErrors: (e: Record<string, string>) => void }]\n invalid: [errors: Record<string, string>]\n reset: []\n}>()\n\nconst errors = ref<Record<string, string>>({})\nconst isSubmitting = ref(false)\nconst isSubmitted = ref(false)\nconst submitCount = ref(0)\nconst fields = new Map<string, FieldRegistration>()\n\n// Reactive field count so computed properties re-evaluate when fields register/unregister\nconst fieldCount = ref(0)\n\nfunction registerField(reg: FieldRegistration): void {\n fields.set(reg.name, reg)\n fieldCount.value++\n}\n\nfunction unregisterField(name: string): void {\n fields.delete(name)\n fieldCount.value--\n const next = { ...errors.value }\n delete next[name]\n errors.value = next\n}\n\n// ── Computed state ──────────────────────────────────────────────────────────\n\nconst isValid = computed(() => {\n void fieldCount.value // track reactivity\n return Object.keys(errors.value).length === 0\n})\n\nconst isDirty = computed(() => {\n void fieldCount.value\n for (const field of fields.values()) {\n if (field.dirty.value) return true\n }\n return false\n})\n\nconst isTouched = computed(() => {\n void fieldCount.value\n for (const field of fields.values()) {\n if (field.touched.value) return true\n }\n return false\n})\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getAllValues(): Record<string, unknown> {\n const values: Record<string, unknown> = {}\n for (const [name, field] of fields.entries()) {\n values[name] = field.getValue()\n }\n return values\n}\n\n// ── Validation ───────────────────────────────────────────────────────────────\n\nasync function triggerFieldValidation(name: string): Promise<void> {\n const field = fields.get(name)\n if (!field) return\n const error = await runValidation(\n field.getValue(),\n field.rules,\n field.validate,\n { values: getAllValues() },\n )\n const next = { ...errors.value }\n if (error) {\n next[name] = error\n } else {\n delete next[name]\n }\n errors.value = next\n}\n\nasync function trigger(name?: string): Promise<boolean> {\n if (name) {\n await triggerFieldValidation(name)\n return !errors.value[name]\n }\n\n const results = await Promise.all(\n [...fields.entries()].map(async ([fieldName, field]) => {\n const error = await runValidation(\n field.getValue(),\n field.rules,\n field.validate,\n { values: getAllValues() },\n )\n return { name: fieldName, error }\n }),\n )\n\n const next: Record<string, string> = {}\n for (const { name: fieldName, error } of results) {\n if (error) next[fieldName] = error\n }\n errors.value = next\n return Object.keys(next).length === 0\n}\n\n// ── Error management ─────────────────────────────────────────────────────────\n\nfunction setErrors(newErrors: Record<string, string>): void {\n errors.value = { ...errors.value, ...newErrors }\n isSubmitting.value = false\n}\n\nfunction setError(name: string, message: string): void {\n errors.value = { ...errors.value, [name]: message }\n}\n\nfunction clearErrors(name?: string): void {\n if (name) {\n const next = { ...errors.value }\n delete next[name]\n errors.value = next\n } else {\n errors.value = {}\n }\n}\n\n// ── Values ───────────────────────────────────────────────────────────────────\n\nfunction getValues(): Record<string, unknown> {\n return getAllValues()\n}\n\nfunction setValue(name: string, value: unknown): void {\n const field = fields.get(name)\n if (field) field.setValue(value)\n}\n\n// ── Reset ────────────────────────────────────────────────────────────────────\n\nfunction reset(): void {\n for (const field of fields.values()) {\n field.reset()\n }\n errors.value = {}\n isSubmitting.value = false\n isSubmitted.value = false\n submitCount.value = 0\n emit('reset')\n}\n\n// ── Submit ───────────────────────────────────────────────────────────────────\n\nasync function handleSubmit(): Promise<void> {\n isSubmitting.value = true\n submitCount.value++\n\n const values = getAllValues()\n const context = { values }\n\n const results = await Promise.all(\n [...fields.entries()].map(async ([name, field]) => {\n const error = await runValidation(field.getValue(), field.rules, field.validate, context)\n return { name, error }\n }),\n )\n\n const nextErrors: Record<string, string> = {}\n for (const { name, error } of results) {\n if (error) nextErrors[name] = error\n }\n\n if (Object.keys(nextErrors).length > 0) {\n errors.value = nextErrors\n isSubmitting.value = false\n isSubmitted.value = true\n emit('invalid', nextErrors)\n return\n }\n\n errors.value = {}\n isSubmitted.value = true\n isSubmitting.value = false\n emit('submit', { values, setErrors })\n}\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\nconst ctx: FormContext = {\n errors,\n isSubmitting,\n isSubmitted,\n submitCount,\n isDisabled: computed(() => props.isDisabled),\n isValid,\n isDirty,\n isTouched,\n validationMode: computed(() => props.validationMode),\n registerField,\n unregisterField,\n triggerFieldValidation,\n setErrors,\n setError,\n clearErrors,\n getValues,\n setValue,\n trigger,\n reset,\n}\n\nprovide(formContextKey, ctx)\n\ndefineExpose({\n errors,\n isSubmitting,\n isSubmitted,\n submitCount,\n isValid,\n isDirty,\n isTouched,\n getValues,\n setValue,\n setErrors,\n setError,\n clearErrors,\n trigger,\n reset,\n})\n</script>\n\n<template>\n <form\n :class=\"props.class\"\n novalidate\n @submit.prevent=\"handleSubmit\"\n >\n <slot\n :is-submitting=\"isSubmitting\"\n :is-submitted=\"isSubmitted\"\n :submit-count=\"submitCount\"\n :is-disabled=\"props.isDisabled\"\n :is-valid=\"isValid\"\n :is-dirty=\"isDirty\"\n :is-touched=\"isTouched\"\n :errors=\"errors\"\n :get-values=\"getValues\"\n :set-value=\"setValue\"\n :set-errors=\"setErrors\"\n :set-error=\"setError\"\n :clear-errors=\"clearErrors\"\n :trigger=\"trigger\"\n :reset=\"reset\"\n />\n </form>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.js","names":[],"sources":["../../../src/components/form/FormField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport { useFormInject } from './form.context'\nimport { runValidation } from './validation'\nimport type { FieldRules, CustomValidator } from './validation'\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<{\n name: string\n rules?: FieldRules\n validate?: CustomValidator\n /** Override validation mode
|
|
1
|
+
{"version":3,"file":"FormField.js","names":[],"sources":["../../../src/components/form/FormField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport { useFormInject } from './form.context'\nimport { runValidation } from './validation'\nimport type { FieldRules, CustomValidator } from './validation'\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<{\n name: string\n defaultValue?: unknown\n rules?: FieldRules\n validate?: CustomValidator\n /** Override validation mode for this field. */\n validationMode?: 'on-submit' | 'on-blur' | 'on-change'\n}>()\n\nconst modelValue = defineModel<unknown>({ default: undefined })\n\nconst ctx = useFormInject()\n\n// ── Field state ──────────────────────────────────────────────────────────────\n\nconst localError = ref<string | undefined>(undefined)\nconst touched = ref(false)\nconst dirty = ref(props.defaultValue !== undefined && modelValue.value !== props.defaultValue)\n\nconst fieldError = computed<string | undefined>(() =>\n ctx ? ctx.errors.value[props.name] : localError.value,\n)\n\nconst hasBeenInvalid = ref(false)\nwatch(fieldError, (error) => { if (error) hasBeenInvalid.value = true })\n\nconst isInvalid = computed(() => !!fieldError.value)\nconst isDisabled = computed(() => ctx?.isDisabled.value ?? false)\nconst validationMode = computed(() => props.validationMode ?? ctx?.validationMode.value ?? 'on-submit')\n\n// ── Dirty tracking ───────────────────────────────────────────────────────────\n\nwatch(modelValue, (val) => {\n dirty.value = val !== props.defaultValue\n})\n\n// ── Registration ─────────────────────────────────────────────────────────────\n\nfunction resetField(): void {\n modelValue.value = props.defaultValue\n localError.value = undefined\n touched.value = false\n dirty.value = false\n hasBeenInvalid.value = false\n}\n\nonMounted(() => {\n ctx?.registerField({\n name: props.name,\n getValue: () => modelValue.value,\n getDefaultValue: () => props.defaultValue,\n setValue: (val: unknown) => { modelValue.value = val },\n reset: resetField,\n touched,\n dirty,\n rules: props.rules,\n validate: props.validate,\n })\n})\n\nonUnmounted(() => {\n ctx?.unregisterField(props.name)\n})\n\n// ── Validation ───────────────────────────────────────────────────────────────\n\nasync function triggerValidation(val: unknown): Promise<void> {\n const context = ctx ? { values: ctx.getValues() } : undefined\n const error = await runValidation(val, props.rules, props.validate, context)\n\n if (ctx) {\n const next = { ...ctx.errors.value }\n if (error) {\n next[props.name] = error\n } else {\n delete next[props.name]\n }\n ctx.errors.value = next\n } else {\n localError.value = error\n }\n}\n\nasync function handleUpdate(val: unknown): Promise<void> {\n modelValue.value = val\n if (validationMode.value === 'on-change' || hasBeenInvalid.value) {\n await triggerValidation(val)\n }\n}\n\nasync function handleBlur(): Promise<void> {\n touched.value = true\n if (validationMode.value === 'on-blur') {\n await triggerValidation(modelValue.value)\n }\n}\n\n// ── Slot bindings ─────────────────────────────────────────────────────────────\n\nconst fieldProps = computed(() => ({\n name: props.name,\n modelValue: modelValue.value,\n 'onUpdate:modelValue': handleUpdate,\n isInvalid: isInvalid.value,\n errorMessage: fieldError.value,\n isDisabled: isDisabled.value,\n onBlur: handleBlur,\n}))\n</script>\n\n<template>\n <slot\n :field-props=\"fieldProps\"\n :touched=\"touched\"\n :dirty=\"dirty\"\n :error=\"fieldError\"\n :is-invalid=\"isInvalid\"\n />\n</template>\n"],"mappings":""}
|
|
@@ -7,6 +7,7 @@ var FormField_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
7
7
|
__name: "FormField",
|
|
8
8
|
props: /* @__PURE__ */ mergeModels({
|
|
9
9
|
name: {},
|
|
10
|
+
defaultValue: {},
|
|
10
11
|
rules: {},
|
|
11
12
|
validate: { type: Function },
|
|
12
13
|
validationMode: {}
|
|
@@ -20,6 +21,8 @@ var FormField_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
20
21
|
const modelValue = useModel(__props, "modelValue");
|
|
21
22
|
const ctx = useFormInject();
|
|
22
23
|
const localError = ref(void 0);
|
|
24
|
+
const touched = ref(false);
|
|
25
|
+
const dirty = ref(props.defaultValue !== void 0 && modelValue.value !== props.defaultValue);
|
|
23
26
|
const fieldError = computed(() => ctx ? ctx.errors.value[props.name] : localError.value);
|
|
24
27
|
const hasBeenInvalid = ref(false);
|
|
25
28
|
watch(fieldError, (error) => {
|
|
@@ -28,10 +31,27 @@ var FormField_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
28
31
|
const isInvalid = computed(() => !!fieldError.value);
|
|
29
32
|
const isDisabled = computed(() => ctx?.isDisabled.value ?? false);
|
|
30
33
|
const validationMode = computed(() => props.validationMode ?? ctx?.validationMode.value ?? "on-submit");
|
|
34
|
+
watch(modelValue, (val) => {
|
|
35
|
+
dirty.value = val !== props.defaultValue;
|
|
36
|
+
});
|
|
37
|
+
function resetField() {
|
|
38
|
+
modelValue.value = props.defaultValue;
|
|
39
|
+
localError.value = void 0;
|
|
40
|
+
touched.value = false;
|
|
41
|
+
dirty.value = false;
|
|
42
|
+
hasBeenInvalid.value = false;
|
|
43
|
+
}
|
|
31
44
|
onMounted(() => {
|
|
32
45
|
ctx?.registerField({
|
|
33
46
|
name: props.name,
|
|
34
47
|
getValue: () => modelValue.value,
|
|
48
|
+
getDefaultValue: () => props.defaultValue,
|
|
49
|
+
setValue: (val) => {
|
|
50
|
+
modelValue.value = val;
|
|
51
|
+
},
|
|
52
|
+
reset: resetField,
|
|
53
|
+
touched,
|
|
54
|
+
dirty,
|
|
35
55
|
rules: props.rules,
|
|
36
56
|
validate: props.validate
|
|
37
57
|
});
|
|
@@ -40,7 +60,8 @@ var FormField_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
40
60
|
ctx?.unregisterField(props.name);
|
|
41
61
|
});
|
|
42
62
|
async function triggerValidation(val) {
|
|
43
|
-
const
|
|
63
|
+
const context = ctx ? { values: ctx.getValues() } : void 0;
|
|
64
|
+
const error = await runValidation(val, props.rules, props.validate, context);
|
|
44
65
|
if (ctx) {
|
|
45
66
|
const next = { ...ctx.errors.value };
|
|
46
67
|
if (error) next[props.name] = error;
|
|
@@ -53,6 +74,7 @@ var FormField_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
53
74
|
if (validationMode.value === "on-change" || hasBeenInvalid.value) await triggerValidation(val);
|
|
54
75
|
}
|
|
55
76
|
async function handleBlur() {
|
|
77
|
+
touched.value = true;
|
|
56
78
|
if (validationMode.value === "on-blur") await triggerValidation(modelValue.value);
|
|
57
79
|
}
|
|
58
80
|
const fieldProps = computed(() => ({
|
|
@@ -65,7 +87,13 @@ var FormField_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
65
87
|
onBlur: handleBlur
|
|
66
88
|
}));
|
|
67
89
|
return (_ctx, _cache) => {
|
|
68
|
-
return renderSlot(_ctx.$slots, "default", {
|
|
90
|
+
return renderSlot(_ctx.$slots, "default", {
|
|
91
|
+
fieldProps: fieldProps.value,
|
|
92
|
+
touched: touched.value,
|
|
93
|
+
dirty: dirty.value,
|
|
94
|
+
error: fieldError.value,
|
|
95
|
+
isInvalid: isInvalid.value
|
|
96
|
+
});
|
|
69
97
|
};
|
|
70
98
|
}
|
|
71
99
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/FormField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport { useFormInject } from './form.context'\nimport { runValidation } from './validation'\nimport type { FieldRules, CustomValidator } from './validation'\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<{\n name: string\n rules?: FieldRules\n validate?: CustomValidator\n /** Override validation mode
|
|
1
|
+
{"version":3,"file":"FormField.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/FormField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\nimport { useFormInject } from './form.context'\nimport { runValidation } from './validation'\nimport type { FieldRules, CustomValidator } from './validation'\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<{\n name: string\n defaultValue?: unknown\n rules?: FieldRules\n validate?: CustomValidator\n /** Override validation mode for this field. */\n validationMode?: 'on-submit' | 'on-blur' | 'on-change'\n}>()\n\nconst modelValue = defineModel<unknown>({ default: undefined })\n\nconst ctx = useFormInject()\n\n// ── Field state ──────────────────────────────────────────────────────────────\n\nconst localError = ref<string | undefined>(undefined)\nconst touched = ref(false)\nconst dirty = ref(props.defaultValue !== undefined && modelValue.value !== props.defaultValue)\n\nconst fieldError = computed<string | undefined>(() =>\n ctx ? ctx.errors.value[props.name] : localError.value,\n)\n\nconst hasBeenInvalid = ref(false)\nwatch(fieldError, (error) => { if (error) hasBeenInvalid.value = true })\n\nconst isInvalid = computed(() => !!fieldError.value)\nconst isDisabled = computed(() => ctx?.isDisabled.value ?? false)\nconst validationMode = computed(() => props.validationMode ?? ctx?.validationMode.value ?? 'on-submit')\n\n// ── Dirty tracking ───────────────────────────────────────────────────────────\n\nwatch(modelValue, (val) => {\n dirty.value = val !== props.defaultValue\n})\n\n// ── Registration ─────────────────────────────────────────────────────────────\n\nfunction resetField(): void {\n modelValue.value = props.defaultValue\n localError.value = undefined\n touched.value = false\n dirty.value = false\n hasBeenInvalid.value = false\n}\n\nonMounted(() => {\n ctx?.registerField({\n name: props.name,\n getValue: () => modelValue.value,\n getDefaultValue: () => props.defaultValue,\n setValue: (val: unknown) => { modelValue.value = val },\n reset: resetField,\n touched,\n dirty,\n rules: props.rules,\n validate: props.validate,\n })\n})\n\nonUnmounted(() => {\n ctx?.unregisterField(props.name)\n})\n\n// ── Validation ───────────────────────────────────────────────────────────────\n\nasync function triggerValidation(val: unknown): Promise<void> {\n const context = ctx ? { values: ctx.getValues() } : undefined\n const error = await runValidation(val, props.rules, props.validate, context)\n\n if (ctx) {\n const next = { ...ctx.errors.value }\n if (error) {\n next[props.name] = error\n } else {\n delete next[props.name]\n }\n ctx.errors.value = next\n } else {\n localError.value = error\n }\n}\n\nasync function handleUpdate(val: unknown): Promise<void> {\n modelValue.value = val\n if (validationMode.value === 'on-change' || hasBeenInvalid.value) {\n await triggerValidation(val)\n }\n}\n\nasync function handleBlur(): Promise<void> {\n touched.value = true\n if (validationMode.value === 'on-blur') {\n await triggerValidation(modelValue.value)\n }\n}\n\n// ── Slot bindings ─────────────────────────────────────────────────────────────\n\nconst fieldProps = computed(() => ({\n name: props.name,\n modelValue: modelValue.value,\n 'onUpdate:modelValue': handleUpdate,\n isInvalid: isInvalid.value,\n errorMessage: fieldError.value,\n isDisabled: isDisabled.value,\n onBlur: handleBlur,\n}))\n</script>\n\n<template>\n <slot\n :field-props=\"fieldProps\"\n :touched=\"touched\"\n :dirty=\"dirty\"\n :error=\"fieldError\"\n :is-invalid=\"isInvalid\"\n />\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAQA,MAAM,QAAQ;EASd,MAAM,aAAa,SAAoB,SAAA,aAAuB;EAE9D,MAAM,MAAM,eAAc;EAI1B,MAAM,aAAa,IAAwB,KAAA,EAAS;EACpD,MAAM,UAAU,IAAI,MAAK;EACzB,MAAM,QAAQ,IAAI,MAAM,iBAAiB,KAAA,KAAa,WAAW,UAAU,MAAM,aAAY;EAE7F,MAAM,aAAa,eACjB,MAAM,IAAI,OAAO,MAAM,MAAM,QAAQ,WAAW,MAClD;EAEA,MAAM,iBAAiB,IAAI,MAAK;AAChC,QAAM,aAAa,UAAU;AAAE,OAAI,MAAO,gBAAe,QAAQ;IAAM;EAEvE,MAAM,YAAY,eAAe,CAAC,CAAC,WAAW,MAAK;EACnD,MAAM,aAAa,eAAe,KAAK,WAAW,SAAS,MAAK;EAChE,MAAM,iBAAiB,eAAe,MAAM,kBAAkB,KAAK,eAAe,SAAS,YAAW;AAItG,QAAM,aAAa,QAAQ;AACzB,SAAM,QAAQ,QAAQ,MAAM;IAC7B;EAID,SAAS,aAAmB;AAC1B,cAAW,QAAQ,MAAM;AACzB,cAAW,QAAQ,KAAA;AACnB,WAAQ,QAAQ;AAChB,SAAM,QAAQ;AACd,kBAAe,QAAQ;;AAGzB,kBAAgB;AACd,QAAK,cAAc;IACjB,MAAM,MAAM;IACZ,gBAAgB,WAAW;IAC3B,uBAAuB,MAAM;IAC7B,WAAW,QAAiB;AAAE,gBAAW,QAAQ;;IACjD,OAAO;IACP;IACA;IACA,OAAO,MAAM;IACb,UAAU,MAAM;IACjB,CAAA;IACF;AAED,oBAAkB;AAChB,QAAK,gBAAgB,MAAM,KAAI;IAChC;EAID,eAAe,kBAAkB,KAA6B;GAC5D,MAAM,UAAU,MAAM,EAAE,QAAQ,IAAI,WAAW,EAAE,GAAG,KAAA;GACpD,MAAM,QAAQ,MAAM,cAAc,KAAK,MAAM,OAAO,MAAM,UAAU,QAAO;AAE3E,OAAI,KAAK;IACP,MAAM,OAAO,EAAE,GAAG,IAAI,OAAO,OAAM;AACnC,QAAI,MACF,MAAK,MAAM,QAAQ;QAEnB,QAAO,KAAK,MAAM;AAEpB,QAAI,OAAO,QAAQ;SAEnB,YAAW,QAAQ;;EAIvB,eAAe,aAAa,KAA6B;AACvD,cAAW,QAAQ;AACnB,OAAI,eAAe,UAAU,eAAe,eAAe,MACzD,OAAM,kBAAkB,IAAG;;EAI/B,eAAe,aAA4B;AACzC,WAAQ,QAAQ;AAChB,OAAI,eAAe,UAAU,UAC3B,OAAM,kBAAkB,WAAW,MAAK;;EAM5C,MAAM,aAAa,gBAAgB;GACjC,MAAM,MAAM;GACZ,YAAY,WAAW;GACvB,uBAAuB;GACvB,WAAW,UAAU;GACrB,cAAc,WAAW;GACzB,YAAY,WAAW;GACvB,QAAQ;GACT,EAAC;;UAIA,WAME,KAAA,QAAA,WAAA;IALC,YAAa,WAAA;IACb,SAAS,QAAA;IACT,OAAO,MAAA;IACP,OAAO,WAAA;IACP,WAAY,UAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.context.js","names":[],"sources":["../../../src/components/form/form.context.ts"],"sourcesContent":["import { inject, type Ref, type ComputedRef } from 'vue'\nimport { createContext } from '../../utils/context'\nimport type { FieldRules, CustomValidator } from './validation'\n\nexport type { FieldRules, CustomValidator }\n\nexport type ValidationMode = 'on-submit' | 'on-blur' | 'on-change'\n\nexport interface FieldRegistration {\n name: string\n getValue: () => unknown\n rules?: FieldRules\n validate?: CustomValidator\n}\n\nexport interface FormContext {\n errors: Ref<Record<string, string>>\n isSubmitting: Ref<boolean>\n isDisabled: ComputedRef<boolean>\n validationMode: ComputedRef<ValidationMode>\n registerField(reg: FieldRegistration): void\n unregisterField(name: string): void\n triggerFieldValidation(name: string): Promise<void>\n setErrors(newErrors: Record<string, string>): void\n}\n\nexport const {\n useProvide: useFormProvide,\n useInject: _useFormInjectStrict,\n key: formContextKey,\n} = createContext<FormContext>('Form')\n\n/**\n * Inject Form context. Returns null when called outside a <Form> — callers\n * handle standalone mode themselves.\n */\nexport function useFormInject(): FormContext | null {\n return inject(formContextKey, null)\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"form.context.js","names":[],"sources":["../../../src/components/form/form.context.ts"],"sourcesContent":["import { inject, type Ref, type ComputedRef } from 'vue'\nimport { createContext } from '../../utils/context'\nimport type { FieldRules, CustomValidator } from './validation'\n\nexport type { FieldRules, CustomValidator }\n\nexport type ValidationMode = 'on-submit' | 'on-blur' | 'on-change'\n\nexport interface FieldRegistration {\n name: string\n getValue: () => unknown\n getDefaultValue: () => unknown\n setValue: (value: unknown) => void\n reset: () => void\n touched: Ref<boolean>\n dirty: Ref<boolean>\n rules?: FieldRules\n validate?: CustomValidator\n}\n\nexport interface FormContext {\n errors: Ref<Record<string, string>>\n isSubmitting: Ref<boolean>\n isSubmitted: Ref<boolean>\n submitCount: Ref<number>\n isDisabled: ComputedRef<boolean>\n isValid: ComputedRef<boolean>\n isDirty: ComputedRef<boolean>\n isTouched: ComputedRef<boolean>\n validationMode: ComputedRef<ValidationMode>\n registerField(reg: FieldRegistration): void\n unregisterField(name: string): void\n triggerFieldValidation(name: string): Promise<void>\n setErrors(newErrors: Record<string, string>): void\n setError(name: string, message: string): void\n clearErrors(name?: string): void\n getValues(): Record<string, unknown>\n setValue(name: string, value: unknown): void\n trigger(name?: string): Promise<boolean>\n reset(): void\n}\n\nexport const {\n useProvide: useFormProvide,\n useInject: _useFormInjectStrict,\n key: formContextKey,\n} = createContext<FormContext>('Form')\n\n/**\n * Inject Form context. Returns null when called outside a <Form> — callers\n * handle standalone mode themselves.\n */\nexport function useFormInject(): FormContext | null {\n return inject(formContextKey, null)\n}\n"],"mappings":";;;AA0CA,IAAa,EACX,YAAY,gBACZ,WAAW,sBACX,KAAK,mBACH,cAA2B,OAAO;;;;;AAMtC,SAAgB,gBAAoC;AAClD,QAAO,OAAO,gBAAgB,KAAK"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//#region src/components/form/validation.ts
|
|
2
2
|
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3
|
+
var URL_REGEX = /^(https?:\/\/)?([\w-]+\.)+[\w-]+(\/[\w\-./?%&=]*)?$/i;
|
|
3
4
|
function isEmpty(value) {
|
|
4
5
|
if (value === void 0 || value === null || value === "" || value === false) return true;
|
|
5
6
|
if (Array.isArray(value) && value.length === 0) return true;
|
|
@@ -29,20 +30,35 @@ function resolvePatternRule(rule) {
|
|
|
29
30
|
};
|
|
30
31
|
return rule;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
function resolveStringRule(rule) {
|
|
34
|
+
if (typeof rule === "string") return {
|
|
35
|
+
value: rule,
|
|
36
|
+
message: void 0
|
|
37
|
+
};
|
|
38
|
+
return rule;
|
|
39
|
+
}
|
|
40
|
+
async function runValidation(value, rules, validate, context) {
|
|
33
41
|
if (!rules && !validate) return void 0;
|
|
34
42
|
if (rules?.required !== void 0) {
|
|
35
43
|
const { enabled, message } = resolveSimpleRule(rules.required);
|
|
36
44
|
if (enabled && isEmpty(value)) return message ?? "This field is required";
|
|
37
45
|
}
|
|
38
46
|
if (isEmpty(value)) {
|
|
39
|
-
if (validate) return await validate(value);
|
|
47
|
+
if (validate) return await validate(value, context);
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
42
50
|
if (rules?.email !== void 0) {
|
|
43
51
|
const { enabled, message } = resolveSimpleRule(rules.email);
|
|
44
52
|
if (enabled && !EMAIL_REGEX.test(String(value))) return message ?? "Enter a valid email address";
|
|
45
53
|
}
|
|
54
|
+
if (rules?.url !== void 0) {
|
|
55
|
+
const { enabled, message } = resolveSimpleRule(rules.url);
|
|
56
|
+
if (enabled && !URL_REGEX.test(String(value))) return message ?? "Enter a valid URL";
|
|
57
|
+
}
|
|
58
|
+
if (rules?.integer !== void 0) {
|
|
59
|
+
const { enabled, message } = resolveSimpleRule(rules.integer);
|
|
60
|
+
if (enabled && !Number.isInteger(Number(value))) return message ?? "Must be a whole number";
|
|
61
|
+
}
|
|
46
62
|
if (rules?.pattern !== void 0) {
|
|
47
63
|
const { value: regex, message } = resolvePatternRule(rules.pattern);
|
|
48
64
|
if (!regex.test(String(value))) return message ?? "Invalid format";
|
|
@@ -63,7 +79,11 @@ async function runValidation(value, rules, validate) {
|
|
|
63
79
|
const { value: maxVal, message } = resolveNumericRule(rules.max);
|
|
64
80
|
if (Number(value) > maxVal) return message ?? `Must be at most ${maxVal}`;
|
|
65
81
|
}
|
|
66
|
-
if (
|
|
82
|
+
if (rules?.matches !== void 0) {
|
|
83
|
+
const { value: fieldName, message } = resolveStringRule(rules.matches);
|
|
84
|
+
if (value !== context?.values[fieldName]) return message ?? `Must match ${fieldName}`;
|
|
85
|
+
}
|
|
86
|
+
if (validate) return await validate(value, context);
|
|
67
87
|
}
|
|
68
88
|
//#endregion
|
|
69
89
|
export { runValidation };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","names":[],"sources":["../../../src/components/form/validation.ts"],"sourcesContent":["type RuleWithMessage<T> = T | { value: T; message: string }\n\nexport interface FieldRules {\n required?: boolean | string\n email?: boolean | string\n pattern?: RuleWithMessage<RegExp>\n minLength?: RuleWithMessage<number>\n maxLength?: RuleWithMessage<number>\n min?: RuleWithMessage<number>\n max?: RuleWithMessage<number>\n}\n\nexport type CustomValidator = (value: unknown) => string | undefined | Promise<string | undefined>\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nfunction isEmpty(value: unknown): boolean {\n if (value === undefined || value === null || value === '' || value === false) return true\n if (Array.isArray(value) && value.length === 0) return true\n return false\n}\n\nfunction resolveSimpleRule(rule: boolean | string): { enabled: boolean; message: string | undefined } {\n if (typeof rule === 'string') return { enabled: true, message: rule }\n return { enabled: rule, message: undefined }\n}\n\nfunction resolveNumericRule(rule: number | { value: number; message: string }): { value: number; message: string | undefined } {\n if (typeof rule === 'number') return { value: rule, message: undefined }\n return rule\n}\n\nfunction resolvePatternRule(rule: RegExp | { value: RegExp; message: string }): { value: RegExp; message: string | undefined } {\n if (rule instanceof RegExp) return { value: rule, message: undefined }\n return rule\n}\n\nexport async function runValidation(\n value: unknown,\n rules?: FieldRules,\n validate?: CustomValidator,\n): Promise<string | undefined> {\n if (!rules && !validate) return undefined\n\n if (rules?.required !== undefined) {\n const { enabled, message } = resolveSimpleRule(rules.required)\n if (enabled && isEmpty(value)) return message ?? 'This field is required'\n }\n\n if (isEmpty(value)) {\n if (validate) return await validate(value)\n return undefined\n }\n\n if (rules?.email !== undefined) {\n const { enabled, message } = resolveSimpleRule(rules.email)\n if (enabled && !EMAIL_REGEX.test(String(value))) return message ?? 'Enter a valid email address'\n }\n\n if (rules?.pattern !== undefined) {\n const { value: regex, message } = resolvePatternRule(rules.pattern)\n if (!regex.test(String(value))) return message ?? 'Invalid format'\n }\n\n if (rules?.minLength !== undefined) {\n const { value: min, message } = resolveNumericRule(rules.minLength)\n const len = Array.isArray(value) ? value.length : String(value).length\n if (len < min) return message ?? `Must be at least ${min} characters`\n }\n\n if (rules?.maxLength !== undefined) {\n const { value: max, message } = resolveNumericRule(rules.maxLength)\n const len = Array.isArray(value) ? value.length : String(value).length\n if (len > max) return message ?? `Must be at most ${max} characters`\n }\n\n if (rules?.min !== undefined) {\n const { value: minVal, message } = resolveNumericRule(rules.min)\n if (Number(value) < minVal) return message ?? `Must be at least ${minVal}`\n }\n\n if (rules?.max !== undefined) {\n const { value: maxVal, message } = resolveNumericRule(rules.max)\n if (Number(value) > maxVal) return message ?? `Must be at most ${maxVal}`\n }\n\n if (validate) return await validate(value)\n\n return undefined\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"validation.js","names":[],"sources":["../../../src/components/form/validation.ts"],"sourcesContent":["type RuleWithMessage<T> = T | { value: T; message: string }\n\nexport interface FieldRules {\n required?: boolean | string\n email?: boolean | string\n url?: boolean | string\n integer?: boolean | string\n pattern?: RuleWithMessage<RegExp>\n minLength?: RuleWithMessage<number>\n maxLength?: RuleWithMessage<number>\n min?: RuleWithMessage<number>\n max?: RuleWithMessage<number>\n /** Match the value of another field by name (e.g. confirm password). */\n matches?: RuleWithMessage<string>\n}\n\nexport interface ValidationContext {\n /** All current form field values — available for cross-field rules and custom validators. */\n values: Record<string, unknown>\n}\n\nexport type CustomValidator = (\n value: unknown,\n context?: ValidationContext,\n) => string | undefined | Promise<string | undefined>\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst URL_REGEX = /^(https?:\\/\\/)?([\\w-]+\\.)+[\\w-]+(\\/[\\w\\-./?%&=]*)?$/i\n\nfunction isEmpty(value: unknown): boolean {\n if (value === undefined || value === null || value === '' || value === false) return true\n if (Array.isArray(value) && value.length === 0) return true\n return false\n}\n\nfunction resolveSimpleRule(rule: boolean | string): { enabled: boolean; message: string | undefined } {\n if (typeof rule === 'string') return { enabled: true, message: rule }\n return { enabled: rule, message: undefined }\n}\n\nfunction resolveNumericRule(rule: number | { value: number; message: string }): { value: number; message: string | undefined } {\n if (typeof rule === 'number') return { value: rule, message: undefined }\n return rule\n}\n\nfunction resolvePatternRule(rule: RegExp | { value: RegExp; message: string }): { value: RegExp; message: string | undefined } {\n if (rule instanceof RegExp) return { value: rule, message: undefined }\n return rule\n}\n\nfunction resolveStringRule(rule: string | { value: string; message: string }): { value: string; message: string | undefined } {\n if (typeof rule === 'string') return { value: rule, message: undefined }\n return rule\n}\n\nexport async function runValidation(\n value: unknown,\n rules?: FieldRules,\n validate?: CustomValidator,\n context?: ValidationContext,\n): Promise<string | undefined> {\n if (!rules && !validate) return undefined\n\n if (rules?.required !== undefined) {\n const { enabled, message } = resolveSimpleRule(rules.required)\n if (enabled && isEmpty(value)) return message ?? 'This field is required'\n }\n\n if (isEmpty(value)) {\n if (validate) return await validate(value, context)\n return undefined\n }\n\n if (rules?.email !== undefined) {\n const { enabled, message } = resolveSimpleRule(rules.email)\n if (enabled && !EMAIL_REGEX.test(String(value))) return message ?? 'Enter a valid email address'\n }\n\n if (rules?.url !== undefined) {\n const { enabled, message } = resolveSimpleRule(rules.url)\n if (enabled && !URL_REGEX.test(String(value))) return message ?? 'Enter a valid URL'\n }\n\n if (rules?.integer !== undefined) {\n const { enabled, message } = resolveSimpleRule(rules.integer)\n if (enabled && !Number.isInteger(Number(value))) return message ?? 'Must be a whole number'\n }\n\n if (rules?.pattern !== undefined) {\n const { value: regex, message } = resolvePatternRule(rules.pattern)\n if (!regex.test(String(value))) return message ?? 'Invalid format'\n }\n\n if (rules?.minLength !== undefined) {\n const { value: min, message } = resolveNumericRule(rules.minLength)\n const len = Array.isArray(value) ? value.length : String(value).length\n if (len < min) return message ?? `Must be at least ${min} characters`\n }\n\n if (rules?.maxLength !== undefined) {\n const { value: max, message } = resolveNumericRule(rules.maxLength)\n const len = Array.isArray(value) ? value.length : String(value).length\n if (len > max) return message ?? `Must be at most ${max} characters`\n }\n\n if (rules?.min !== undefined) {\n const { value: minVal, message } = resolveNumericRule(rules.min)\n if (Number(value) < minVal) return message ?? `Must be at least ${minVal}`\n }\n\n if (rules?.max !== undefined) {\n const { value: maxVal, message } = resolveNumericRule(rules.max)\n if (Number(value) > maxVal) return message ?? `Must be at most ${maxVal}`\n }\n\n if (rules?.matches !== undefined) {\n const { value: fieldName, message } = resolveStringRule(rules.matches)\n const otherValue = context?.values[fieldName]\n if (value !== otherValue) return message ?? `Must match ${fieldName}`\n }\n\n if (validate) return await validate(value, context)\n\n return undefined\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectContent.js","names":[],"sources":["../../../src/components/select/SelectContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n SelectContent,\n SelectViewport,\n SelectPortal,\n injectSelectRootContext,\n} from 'reka-ui'\nimport { motion, AnimatePresence } from 'motion-v'\nimport { useSelectInject } from './Select.context'\n\nconst props = withDefaults(defineProps<{\n position?: 'item-aligned' | 'popper'\n sideOffset?: number\n class?: string\n}>(), {\n position: 'popper',\n sideOffset: 8,\n class: undefined,\n})\n\nconst ctx = useSelectInject()\nconst rootContext = injectSelectRootContext()\n</script>\n\n<template>\n <SelectPortal>\n <AnimatePresence>\n <!--\n No force-mount on SelectContent. With force-mount=true, Reka's\n SelectContentImpl (which contains DismissableLayer with\n disableOutsidePointerEvents=true) would mount immediately on page load and\n block all pointer events — including clicks on the trigger button.\n\n Without force-mount, when open=false, Reka teleports slot content into a\n DocumentFragment (after SelectContent's own onMounted). Components inside\n the slot (SelectItem) still get created and their setup() runs, so\n textValue-based registrations fire at setup time.\n\n v-show (not v-if) on the visual wrapper ensures SelectItem components are\n always instantiated when inside the DocumentFragment — their setup() fires\n and populates itemRegistry. The animated chrome is visually hidden via\n v-show when closed; enter/exit animation runs via motion.div bindings.\n -->\n <SelectContent\n :position=\"props.position\"\n :side-offset=\"props.sideOffset\"\n data-slot=\"popover\"\n >\n <motion.div\n v-show=\"rootContext.open.value\"\n :class=\"ctx.slots.value.popover()\"\n :animate=\"rootContext.open.value ? { opacity: 1, scale: 1 } : { opacity: 0, scale: 0.95 }\"\n :transition=\"{ duration: 0.15 }\"\n >\n <SelectViewport data-slot=\"list-box\">\n <slot />\n </SelectViewport>\n </motion.div>\n </SelectContent>\n </AnimatePresence>\n </SelectPortal>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"SelectContent.js","names":[],"sources":["../../../src/components/select/SelectContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n SelectContent,\n SelectViewport,\n SelectPortal,\n injectSelectRootContext,\n} from 'reka-ui'\nimport { motion, AnimatePresence } from 'motion-v'\nimport { ref, watch } from 'vue'\nimport { useSelectInject } from './Select.context'\n\nconst props = withDefaults(defineProps<{\n position?: 'item-aligned' | 'popper'\n sideOffset?: number\n class?: string\n}>(), {\n position: 'popper',\n sideOffset: 8,\n class: undefined,\n})\n\nconst ctx = useSelectInject()\nconst rootContext = injectSelectRootContext()\n\n// Suppress scroll-behavior:smooth for the first frame after open so Reka's\n// programmatic scrollTop jump to the selected item is instant.\nconst justOpened = ref(false)\nwatch(() => rootContext.open.value, (open) => {\n if (open) {\n justOpened.value = true\n setTimeout(() => { justOpened.value = false }, 100)\n }\n})\n</script>\n\n<template>\n <SelectPortal>\n <AnimatePresence>\n <!--\n No force-mount on SelectContent. With force-mount=true, Reka's\n SelectContentImpl (which contains DismissableLayer with\n disableOutsidePointerEvents=true) would mount immediately on page load and\n block all pointer events — including clicks on the trigger button.\n\n Without force-mount, when open=false, Reka teleports slot content into a\n DocumentFragment (after SelectContent's own onMounted). Components inside\n the slot (SelectItem) still get created and their setup() runs, so\n textValue-based registrations fire at setup time.\n\n v-show (not v-if) on the visual wrapper ensures SelectItem components are\n always instantiated when inside the DocumentFragment — their setup() fires\n and populates itemRegistry. The animated chrome is visually hidden via\n v-show when closed; enter/exit animation runs via motion.div bindings.\n -->\n <SelectContent\n :position=\"props.position\"\n :side-offset=\"props.sideOffset\"\n data-slot=\"popover\"\n >\n <motion.div\n v-show=\"rootContext.open.value\"\n :class=\"[ctx.slots.value.popover(), { 'select__popover--opening': justOpened }]\"\n :animate=\"rootContext.open.value ? { opacity: 1, scale: 1 } : { opacity: 0, scale: 0.95 }\"\n :transition=\"{ duration: 0.15 }\"\n >\n <SelectViewport data-slot=\"list-box\">\n <slot />\n </SelectViewport>\n </motion.div>\n </SelectContent>\n </AnimatePresence>\n </SelectPortal>\n</template>\n"],"mappings":""}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { motion } from "../../node_modules/.pnpm/motion-v@2.2.1_@vueuse_core@14.2.1_vue@3.5.32_typescript@6.0.2___react-dom@19.2.5_react_8b28b23614a2152514812dba6ef76a55/node_modules/motion-v/dist/es/components/motion/index.js";
|
|
2
2
|
import { AnimatePresence_default } from "../../node_modules/.pnpm/motion-v@2.2.1_@vueuse_core@14.2.1_vue@3.5.32_typescript@6.0.2___react-dom@19.2.5_react_8b28b23614a2152514812dba6ef76a55/node_modules/motion-v/dist/es/components/animate-presence/AnimatePresence.js";
|
|
3
3
|
import { useSelectInject } from "./Select.context.js";
|
|
4
|
-
import { createBlock, createVNode, defineComponent, normalizeClass, openBlock, renderSlot, unref, vShow, withCtx, withDirectives } from "vue";
|
|
4
|
+
import { createBlock, createVNode, defineComponent, normalizeClass, openBlock, ref, renderSlot, unref, vShow, watch, withCtx, withDirectives } from "vue";
|
|
5
5
|
import { SelectContent, SelectPortal, SelectViewport, injectSelectRootContext } from "reka-ui";
|
|
6
6
|
//#region src/components/select/SelectContent.vue?vue&type=script&setup=true&lang.ts
|
|
7
7
|
var SelectContent_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
@@ -15,6 +15,15 @@ var SelectContent_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */
|
|
|
15
15
|
const props = __props;
|
|
16
16
|
const ctx = useSelectInject();
|
|
17
17
|
const rootContext = injectSelectRootContext();
|
|
18
|
+
const justOpened = ref(false);
|
|
19
|
+
watch(() => rootContext.open.value, (open) => {
|
|
20
|
+
if (open) {
|
|
21
|
+
justOpened.value = true;
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
justOpened.value = false;
|
|
24
|
+
}, 100);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
18
27
|
return (_ctx, _cache) => {
|
|
19
28
|
return openBlock(), createBlock(unref(SelectPortal), null, {
|
|
20
29
|
default: withCtx(() => [createVNode(unref(AnimatePresence_default), null, {
|
|
@@ -24,7 +33,7 @@ var SelectContent_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */
|
|
|
24
33
|
"data-slot": "popover"
|
|
25
34
|
}, {
|
|
26
35
|
default: withCtx(() => [withDirectives(createVNode(unref(motion).div, {
|
|
27
|
-
class: normalizeClass(unref(ctx).slots.value.popover()),
|
|
36
|
+
class: normalizeClass([unref(ctx).slots.value.popover(), { "select__popover--opening": justOpened.value }]),
|
|
28
37
|
animate: unref(rootContext).open.value ? {
|
|
29
38
|
opacity: 1,
|
|
30
39
|
scale: 1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectContent.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/select/SelectContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n SelectContent,\n SelectViewport,\n SelectPortal,\n injectSelectRootContext,\n} from 'reka-ui'\nimport { motion, AnimatePresence } from 'motion-v'\nimport { useSelectInject } from './Select.context'\n\nconst props = withDefaults(defineProps<{\n position?: 'item-aligned' | 'popper'\n sideOffset?: number\n class?: string\n}>(), {\n position: 'popper',\n sideOffset: 8,\n class: undefined,\n})\n\nconst ctx = useSelectInject()\nconst rootContext = injectSelectRootContext()\n</script>\n\n<template>\n <SelectPortal>\n <AnimatePresence>\n <!--\n No force-mount on SelectContent. With force-mount=true, Reka's\n SelectContentImpl (which contains DismissableLayer with\n disableOutsidePointerEvents=true) would mount immediately on page load and\n block all pointer events — including clicks on the trigger button.\n\n Without force-mount, when open=false, Reka teleports slot content into a\n DocumentFragment (after SelectContent's own onMounted). Components inside\n the slot (SelectItem) still get created and their setup() runs, so\n textValue-based registrations fire at setup time.\n\n v-show (not v-if) on the visual wrapper ensures SelectItem components are\n always instantiated when inside the DocumentFragment — their setup() fires\n and populates itemRegistry. The animated chrome is visually hidden via\n v-show when closed; enter/exit animation runs via motion.div bindings.\n -->\n <SelectContent\n :position=\"props.position\"\n :side-offset=\"props.sideOffset\"\n data-slot=\"popover\"\n >\n <motion.div\n v-show=\"rootContext.open.value\"\n :class=\"ctx.slots.value.popover()\"\n :animate=\"rootContext.open.value ? { opacity: 1, scale: 1 } : { opacity: 0, scale: 0.95 }\"\n :transition=\"{ duration: 0.15 }\"\n >\n <SelectViewport data-slot=\"list-box\">\n <slot />\n </SelectViewport>\n </motion.div>\n </SelectContent>\n </AnimatePresence>\n </SelectPortal>\n</template>\n"],"mappings":";;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"SelectContent.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/select/SelectContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n SelectContent,\n SelectViewport,\n SelectPortal,\n injectSelectRootContext,\n} from 'reka-ui'\nimport { motion, AnimatePresence } from 'motion-v'\nimport { ref, watch } from 'vue'\nimport { useSelectInject } from './Select.context'\n\nconst props = withDefaults(defineProps<{\n position?: 'item-aligned' | 'popper'\n sideOffset?: number\n class?: string\n}>(), {\n position: 'popper',\n sideOffset: 8,\n class: undefined,\n})\n\nconst ctx = useSelectInject()\nconst rootContext = injectSelectRootContext()\n\n// Suppress scroll-behavior:smooth for the first frame after open so Reka's\n// programmatic scrollTop jump to the selected item is instant.\nconst justOpened = ref(false)\nwatch(() => rootContext.open.value, (open) => {\n if (open) {\n justOpened.value = true\n setTimeout(() => { justOpened.value = false }, 100)\n }\n})\n</script>\n\n<template>\n <SelectPortal>\n <AnimatePresence>\n <!--\n No force-mount on SelectContent. With force-mount=true, Reka's\n SelectContentImpl (which contains DismissableLayer with\n disableOutsidePointerEvents=true) would mount immediately on page load and\n block all pointer events — including clicks on the trigger button.\n\n Without force-mount, when open=false, Reka teleports slot content into a\n DocumentFragment (after SelectContent's own onMounted). Components inside\n the slot (SelectItem) still get created and their setup() runs, so\n textValue-based registrations fire at setup time.\n\n v-show (not v-if) on the visual wrapper ensures SelectItem components are\n always instantiated when inside the DocumentFragment — their setup() fires\n and populates itemRegistry. The animated chrome is visually hidden via\n v-show when closed; enter/exit animation runs via motion.div bindings.\n -->\n <SelectContent\n :position=\"props.position\"\n :side-offset=\"props.sideOffset\"\n data-slot=\"popover\"\n >\n <motion.div\n v-show=\"rootContext.open.value\"\n :class=\"[ctx.slots.value.popover(), { 'select__popover--opening': justOpened }]\"\n :animate=\"rootContext.open.value ? { opacity: 1, scale: 1 } : { opacity: 0, scale: 0.95 }\"\n :transition=\"{ duration: 0.15 }\"\n >\n <SelectViewport data-slot=\"list-box\">\n <slot />\n </SelectViewport>\n </motion.div>\n </SelectContent>\n </AnimatePresence>\n </SelectPortal>\n</template>\n"],"mappings":";;;;;;;;;;;;;;EAWA,MAAM,QAAQ;EAUd,MAAM,MAAM,iBAAgB;EAC5B,MAAM,cAAc,yBAAwB;EAI5C,MAAM,aAAa,IAAI,MAAK;AAC5B,cAAY,YAAY,KAAK,QAAQ,SAAS;AAC5C,OAAI,MAAM;AACR,eAAW,QAAQ;AACnB,qBAAiB;AAAE,gBAAW,QAAQ;OAAS,IAAG;;IAErD;;uBAIC,YAmCe,MAAA,aAAA,EAAA,MAAA;2BADK,CAjClB,YAiCkB,MAAA,wBAAA,EAAA,MAAA;4BADA,CAfhB,YAegB,MAAA,cAAA,EAAA;MAdb,UAAU,MAAM;MAChB,eAAa,MAAM;MACpB,aAAU;;6BAWG,CAAA,eATb,YASa,MAAA,OAAA,CAAA,KAAA;OAPV,OAAK,eAAA,CAAG,MAAA,IAAG,CAAC,MAAM,MAAM,SAAO,EAAA,EAAA,4BAAkC,WAAA,OAAU,CAAA,CAAA;OAC3E,SAAS,MAAA,YAAW,CAAC,KAAK,QAAK;QAAA,SAAA;QAAA,OAAA;QAAA,GAAA;QAAA,SAAA;QAAA,OAAA;QAAA;OAC/B,YAAY,EAAA,UAAA,KAAkB;;8BAId,CAFjB,YAEiB,MAAA,eAAA,EAAA,EAFD,aAAU,YAAU,EAAA;+BAC1B,CAAR,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA;;;;4CANF,MAAA,YAAW,CAAC,KAAK,MAAK,CAAA,CAAA,CAAA,CAAA"}
|