@type32/yaml-editor-form 1.0.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.
@@ -0,0 +1,64 @@
1
+ import type { YamlFieldType } from "../types/types.js";
2
+ type YamlValue = string | number | boolean | null | Date | YamlValue[] | {
3
+ [key: string]: YamlValue;
4
+ };
5
+ type __VLS_Props = {
6
+ fieldKey: string;
7
+ readonly?: boolean;
8
+ depth?: number;
9
+ /** Custom field type definitions (merged with defaults) */
10
+ fieldTypes?: YamlFieldType[];
11
+ };
12
+ type __VLS_ModelProps = {
13
+ modelValue: YamlValue;
14
+ };
15
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
16
+ declare var __VLS_147: string, __VLS_148: {
17
+ modelValue: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | {
18
+ [key: string]: string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null;
19
+ } | null)[] | {
20
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null;
21
+ } | null)[] | {
22
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
23
+ } | null)[] | {
24
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
25
+ } | null)[] | {
26
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
27
+ } | null)[] | {
28
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
29
+ } | null)[] | {
30
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
31
+ } | null)[] | {
32
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
33
+ } | null)[] | {
34
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
35
+ } | null)[] | {
36
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
37
+ } | null)[] | {
38
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
39
+ } | null)[] | {
40
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
41
+ } | null;
42
+ readonly: boolean;
43
+ valueType: string;
44
+ };
45
+ type __VLS_Slots = {} & {
46
+ [K in NonNullable<typeof __VLS_147>]?: (props: typeof __VLS_148) => any;
47
+ };
48
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
49
+ remove: () => any;
50
+ "update:modelValue": (value: YamlValue) => any;
51
+ "update:fieldKey": (newKey: string) => any;
52
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
53
+ onRemove?: (() => any) | undefined;
54
+ "onUpdate:modelValue"?: ((value: YamlValue) => any) | undefined;
55
+ "onUpdate:fieldKey"?: ((newKey: string) => any) | undefined;
56
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
57
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
58
+ declare const _default: typeof __VLS_export;
59
+ export default _default;
60
+ type __VLS_WithSlots<T, S> = T & {
61
+ new (): {
62
+ $slots: S;
63
+ };
64
+ };
@@ -0,0 +1,493 @@
1
+ <script setup>
2
+ import YamlCollapsible from "../components/YamlCollapsible.vue";
3
+ import YamlFieldInput from "../components/YamlFieldInput.vue";
4
+ import { useYamlFieldTypes } from "../composables/useYamlFieldTypes";
5
+ const modelValue = defineModel({ type: [String, Number, Boolean, null, Date, Array, Object], ...{ required: true } });
6
+ const props = defineProps({
7
+ fieldKey: { type: String, required: true },
8
+ readonly: { type: Boolean, required: false, default: false },
9
+ depth: { type: Number, required: false, default: 0 },
10
+ fieldTypes: { type: Array, required: false }
11
+ });
12
+ const emit = defineEmits(["remove", "update:fieldKey"]);
13
+ const {
14
+ fieldTypes,
15
+ getFieldType,
16
+ detectFieldType,
17
+ getDefaultValue,
18
+ getIcon,
19
+ getTypeMenuItems
20
+ } = useYamlFieldTypes(props.fieldTypes);
21
+ function isDateObject(val) {
22
+ return val instanceof Date && !isNaN(val.getTime());
23
+ }
24
+ function isDateString(val) {
25
+ if (typeof val !== "string") return false;
26
+ const isoDateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})?)?$/;
27
+ if (!isoDateRegex.test(val)) return false;
28
+ const date = new Date(val);
29
+ return !isNaN(date.getTime());
30
+ }
31
+ function isDateTimeString(val) {
32
+ if (typeof val !== "string") return false;
33
+ return /T\d{2}:\d{2}:\d{2}/.test(val) && isDateString(val);
34
+ }
35
+ function isStringArray(val) {
36
+ if (!Array.isArray(val)) return false;
37
+ if (val.length === 0) return false;
38
+ return val.every((item) => typeof item === "string");
39
+ }
40
+ function isPrimitiveArray(val) {
41
+ if (!Array.isArray(val)) return false;
42
+ if (val.length === 0) return false;
43
+ return val.every(
44
+ (item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null
45
+ );
46
+ }
47
+ const valueType = computed(() => {
48
+ const detectedType = detectFieldType(modelValue.value);
49
+ return detectedType.type;
50
+ });
51
+ const isEditingKey = ref(false);
52
+ const editingKeyValue = ref(props.fieldKey);
53
+ watch(() => props.fieldKey, (newKey) => {
54
+ editingKeyValue.value = newKey;
55
+ });
56
+ function startEditingKey() {
57
+ if (props.readonly) return;
58
+ editingKeyValue.value = props.fieldKey;
59
+ isEditingKey.value = true;
60
+ }
61
+ function saveKey() {
62
+ const trimmedValue = editingKeyValue.value?.trim();
63
+ if (trimmedValue && trimmedValue !== props.fieldKey) {
64
+ emit("update:fieldKey", trimmedValue);
65
+ }
66
+ isEditingKey.value = false;
67
+ }
68
+ function isValidConversion(fromType, toType) {
69
+ if (fromType === toType) return true;
70
+ if (fromType === "null") return true;
71
+ const conversionRules = {
72
+ // Primitives can convert to other primitives and arrays
73
+ "string": ["number", "boolean", "date", "datetime", "string-array", "null"],
74
+ "number": ["string", "boolean", "null"],
75
+ "boolean": ["string", "number", "null"],
76
+ // Dates can convert to strings and each other
77
+ "date": ["string", "datetime", "null"],
78
+ "datetime": ["string", "date", "null"],
79
+ // String arrays can convert to regular arrays and back to string
80
+ "string-array": ["array", "string", "null"],
81
+ // Arrays can only convert to string-array or null (converting to primitives is unsafe)
82
+ "array": ["string-array", "null"],
83
+ // Objects can only convert to null (converting to primitives is useless)
84
+ "object": ["null"]
85
+ };
86
+ const allowedConversions = conversionRules[fromType] || [];
87
+ return allowedConversions.includes(toType);
88
+ }
89
+ function convertType(newType) {
90
+ const currentType = valueType.value;
91
+ if (!isValidConversion(currentType, newType)) {
92
+ console.warn(`Invalid conversion from ${currentType} to ${newType}`);
93
+ return;
94
+ }
95
+ const isCurrentlyArray = Array.isArray(modelValue.value);
96
+ const isConvertingToNonArray = !["array", "string-array"].includes(newType);
97
+ if (newType === "string-array") {
98
+ if (Array.isArray(modelValue.value)) {
99
+ modelValue.value = modelValue.value.map((item) => String(item));
100
+ } else {
101
+ modelValue.value = [String(modelValue.value || "")];
102
+ }
103
+ return;
104
+ }
105
+ if (currentType === "date" && newType === "datetime" || currentType === "datetime" && newType === "date") {
106
+ const currentValue = modelValue.value;
107
+ if (newType === "datetime") {
108
+ if (typeof currentValue === "string") {
109
+ if (/^\d{4}-\d{2}-\d{2}$/.test(currentValue)) {
110
+ modelValue.value = `${currentValue}T00:00:00`;
111
+ } else {
112
+ modelValue.value = currentValue;
113
+ }
114
+ } else {
115
+ const date = isDateObject(currentValue) ? currentValue : /* @__PURE__ */ new Date();
116
+ modelValue.value = date.toISOString();
117
+ }
118
+ } else {
119
+ if (typeof currentValue === "string") {
120
+ modelValue.value = currentValue.split("T")[0];
121
+ } else {
122
+ const date = isDateObject(currentValue) ? currentValue : /* @__PURE__ */ new Date();
123
+ modelValue.value = date.toISOString().split("T")[0];
124
+ }
125
+ }
126
+ return;
127
+ }
128
+ if (isCurrentlyArray && isConvertingToNonArray) {
129
+ const arr = modelValue.value;
130
+ const firstItem = arr.length > 0 && arr[0] !== void 0 ? arr[0] : null;
131
+ if (firstItem !== null) {
132
+ switch (newType) {
133
+ case "string":
134
+ modelValue.value = isDateObject(firstItem) ? firstItem.toISOString() : String(firstItem);
135
+ return;
136
+ case "number":
137
+ modelValue.value = Number(firstItem) || 0;
138
+ return;
139
+ case "boolean":
140
+ modelValue.value = Boolean(firstItem);
141
+ return;
142
+ case "date":
143
+ const dateVal = typeof firstItem === "string" ? new Date(firstItem) : /* @__PURE__ */ new Date();
144
+ modelValue.value = dateVal.toISOString().split("T")[0];
145
+ return;
146
+ case "datetime":
147
+ const dateTimeVal = typeof firstItem === "string" ? new Date(firstItem) : /* @__PURE__ */ new Date();
148
+ modelValue.value = dateTimeVal.toISOString();
149
+ return;
150
+ }
151
+ }
152
+ }
153
+ modelValue.value = getDefaultValue(newType);
154
+ }
155
+ function addArrayItem(itemType) {
156
+ if (!Array.isArray(modelValue.value)) return;
157
+ if (itemType) {
158
+ modelValue.value.push(getDefaultValue(itemType));
159
+ return;
160
+ }
161
+ if (modelValue.value.length === 0) {
162
+ modelValue.value.push({});
163
+ return;
164
+ }
165
+ const firstItem = modelValue.value[0];
166
+ const detectedType = detectFieldType(firstItem);
167
+ modelValue.value.push(getDefaultValue(detectedType.type));
168
+ }
169
+ function removeArrayItem(index) {
170
+ if (!Array.isArray(modelValue.value)) return;
171
+ modelValue.value.splice(index, 1);
172
+ }
173
+ function addArrayItemFromTemplate() {
174
+ if (!Array.isArray(modelValue.value)) return;
175
+ let templateObject = null;
176
+ let maxFields = 0;
177
+ for (const item of modelValue.value) {
178
+ if (typeof item === "object" && !Array.isArray(item) && item !== null) {
179
+ const fieldCount = Object.keys(item).length;
180
+ if (fieldCount > maxFields) {
181
+ maxFields = fieldCount;
182
+ templateObject = item;
183
+ }
184
+ }
185
+ }
186
+ if (templateObject) {
187
+ const newObject = {};
188
+ for (const key in templateObject) {
189
+ const value = templateObject[key];
190
+ const detectedType = detectFieldType(value);
191
+ newObject[key] = getDefaultValue(detectedType.type);
192
+ }
193
+ modelValue.value.push(newObject);
194
+ } else {
195
+ modelValue.value.push({});
196
+ }
197
+ }
198
+ const hasObjectTemplate = computed(() => {
199
+ if (!Array.isArray(modelValue.value) || modelValue.value.length === 0) return false;
200
+ return modelValue.value.some(
201
+ (item) => typeof item === "object" && !Array.isArray(item) && item !== null && Object.keys(item).length > 0
202
+ );
203
+ });
204
+ function addObjectField(fieldType = "string") {
205
+ if (typeof modelValue.value !== "object" || Array.isArray(modelValue.value) || !modelValue.value || isDateObject(modelValue.value)) return;
206
+ const obj = modelValue.value;
207
+ const newKey = `field_${Object.keys(obj).length + 1}`;
208
+ obj[newKey] = getDefaultValue(fieldType);
209
+ }
210
+ function removeObjectField(key) {
211
+ if (typeof modelValue.value !== "object" || Array.isArray(modelValue.value) || !modelValue.value || isDateObject(modelValue.value)) return;
212
+ const obj = modelValue.value;
213
+ delete obj[key];
214
+ }
215
+ const isOpen = ref(true);
216
+ const isArrayItem = computed(() => {
217
+ return /^\[\d+\]$/.test(props.fieldKey);
218
+ });
219
+ const itemCount = computed(() => {
220
+ if (valueType.value === "array" && Array.isArray(modelValue.value)) {
221
+ return modelValue.value.length;
222
+ } else if (valueType.value === "object" && typeof modelValue.value === "object" && !Array.isArray(modelValue.value) && modelValue.value !== null) {
223
+ return Object.keys(modelValue.value).length;
224
+ }
225
+ return 0;
226
+ });
227
+ const indentClass = computed(() => {
228
+ return props.depth > 0 ? "pl-2" : "";
229
+ });
230
+ const typeOptions = computed(() => {
231
+ const currentType = valueType.value;
232
+ return fieldTypes.value.filter((ft) => isValidConversion(currentType, ft.type)).map((ft) => ({
233
+ label: ft.label,
234
+ icon: ft.icon,
235
+ onSelect: () => convertType(ft.type)
236
+ }));
237
+ });
238
+ const selectedType = computed(() => {
239
+ return {
240
+ icon: getIcon(valueType.value)
241
+ };
242
+ });
243
+ const addFieldOptions = computed(() => {
244
+ return getTypeMenuItems((type) => addObjectField(type));
245
+ });
246
+ const addArrayItemOptions = computed(() => {
247
+ return getTypeMenuItems((type) => addArrayItem(type));
248
+ });
249
+ </script>
250
+
251
+ <template>
252
+ <div :class="indentClass" class="space-y-1">
253
+ <!-- For Objects and Arrays: Use Collapsible -->
254
+ <template v-if="valueType === 'object' || valueType === 'array'">
255
+ <!-- Edit mode for field key -->
256
+ <UInput
257
+ v-if="isEditingKey"
258
+ v-model="editingKeyValue"
259
+ size="xs"
260
+ autofocus
261
+ class="mb-1"
262
+ @blur="saveKey"
263
+ @keydown.enter="saveKey"
264
+ @keydown.esc="isEditingKey = false"
265
+ />
266
+
267
+ <!-- Collapsible for non-edit mode -->
268
+ <YamlCollapsible v-else v-model:open="isOpen" :default-open="true" :label="fieldKey">
269
+ <template #badge>
270
+ <UBadge size="xs" variant="soft" color="neutral">{{ itemCount }}</UBadge>
271
+ </template>
272
+
273
+ <template #actions>
274
+ <div class="flex items-center gap-1">
275
+ <!-- Edit key button (hidden for array items) -->
276
+ <UButton
277
+ v-if="!readonly && !isEditingKey && !isArrayItem"
278
+ icon="i-lucide-pencil"
279
+ variant="ghost"
280
+ size="xs"
281
+ color="neutral"
282
+ class="text-muted"
283
+ @click.stop="startEditingKey"
284
+ />
285
+
286
+ <!-- Type selector -->
287
+ <UDropdownMenu
288
+ :items="[typeOptions]"
289
+ :disabled="readonly"
290
+ size="xs"
291
+ >
292
+ <UButton
293
+ :icon="selectedType?.icon || 'i-heroicons-circle-question-mark'"
294
+ variant="soft"
295
+ size="xs"
296
+ :disabled="readonly"
297
+ />
298
+ </UDropdownMenu>
299
+
300
+ <!-- Remove button -->
301
+ <UButton
302
+ v-if="!readonly"
303
+ icon="i-lucide-trash"
304
+ variant="ghost"
305
+ size="xs"
306
+ color="error"
307
+ @click.stop="emit('remove')"
308
+ />
309
+ </div>
310
+ </template>
311
+
312
+ <!-- Content inside collapsible - only arrays and objects -->
313
+ <!-- Array (Complex items - objects/arrays) -->
314
+ <div v-if="valueType === 'array'" class="space-y-2">
315
+ <div
316
+ v-if="Array.isArray(modelValue) && modelValue.length === 0"
317
+ class="text-center py-4 text-sm text-muted border border-dashed border-default rounded-lg"
318
+ >
319
+ <UIcon name="i-lucide-brackets" class="w-8 h-8 mx-auto mb-2 opacity-50" />
320
+ <p>Empty array</p>
321
+ <p v-if="!readonly" class="text-xs mt-1">Click "Add Item" below</p>
322
+ </div>
323
+
324
+ <template v-else-if="Array.isArray(modelValue)">
325
+ <div
326
+ v-for="(item, index) in modelValue"
327
+ :key="index"
328
+ class="flex items-start gap-2"
329
+ >
330
+ <div v-if="item !== void 0" class="flex-1">
331
+ <YamlFormField
332
+ :model-value="item"
333
+ :field-key="`[${index}]`"
334
+ :readonly="readonly"
335
+ :depth="depth + 1"
336
+ :field-types="fieldTypes"
337
+ @update:model-value="(val) => {
338
+ if (Array.isArray(modelValue)) modelValue[index] = val;
339
+ }"
340
+ @remove="removeArrayItem(index)"
341
+ />
342
+ </div>
343
+ </div>
344
+ </template>
345
+
346
+ <div class="flex items-center gap-2">
347
+ <UDropdownMenu
348
+ v-if="!readonly"
349
+ :items="[addArrayItemOptions]"
350
+ size="xs"
351
+ >
352
+ <UButton
353
+ icon="i-lucide-plus"
354
+ label="Add Item"
355
+ variant="link"
356
+ size="xs"
357
+ color="neutral"
358
+ />
359
+ </UDropdownMenu>
360
+
361
+ <!-- Add Item From Template button (only shown when array has objects) -->
362
+ <UButton
363
+ v-if="!readonly && hasObjectTemplate"
364
+ icon="i-lucide-copy-plus"
365
+ label="From Template"
366
+ variant="link"
367
+ size="xs"
368
+ color="neutral"
369
+ @click="addArrayItemFromTemplate"
370
+ />
371
+ </div>
372
+ </div>
373
+
374
+ <!-- Object -->
375
+ <div v-else-if="valueType === 'object'" class="space-y-2">
376
+ <div
377
+ v-if="typeof modelValue === 'object' && !Array.isArray(modelValue) && modelValue !== null && !isDateObject(modelValue) && Object.keys(modelValue).length === 0"
378
+ class="text-center py-4 text-sm text-muted border border-dashed border-default rounded-lg"
379
+ >
380
+ <UIcon name="i-lucide-box-transparent" class="w-8 h-8 mx-auto mb-2 opacity-50" />
381
+ <p>Empty object</p>
382
+ <p v-if="!readonly" class="text-xs mt-1">Click "Add Field" below</p>
383
+ </div>
384
+
385
+ <template v-else-if="typeof modelValue === 'object' && !Array.isArray(modelValue) && modelValue !== null && !isDateObject(modelValue)">
386
+ <template v-for="(value, key) in modelValue" :key="String(key)">
387
+ <YamlFormField
388
+ v-if="value !== void 0"
389
+ :model-value="value"
390
+ :field-key="String(key)"
391
+ :readonly="readonly"
392
+ :depth="depth + 1"
393
+ :field-types="fieldTypes"
394
+ @update:model-value="(val) => {
395
+ if (typeof modelValue === 'object' && !Array.isArray(modelValue) && modelValue !== null && !isDateObject(modelValue)) {
396
+ modelValue[key] = val;
397
+ }
398
+ }"
399
+ @remove="removeObjectField(String(key))"
400
+ @update:field-key="(newKey) => {
401
+ if (newKey !== key && typeof modelValue === 'object' && !Array.isArray(modelValue) && modelValue !== null && !isDateObject(modelValue) && value !== void 0) {
402
+ const obj = modelValue;
403
+ obj[newKey] = value;
404
+ delete obj[key];
405
+ }
406
+ }"
407
+ />
408
+ </template>
409
+ </template>
410
+
411
+ <UDropdownMenu
412
+ v-if="!readonly"
413
+ :items="[addFieldOptions]"
414
+ size="xs"
415
+ >
416
+ <UButton
417
+ icon="i-lucide-plus"
418
+ label="Add Field"
419
+ variant="link"
420
+ size="xs"
421
+ color="neutral"
422
+ />
423
+ </UDropdownMenu>
424
+ </div>
425
+ </YamlCollapsible>
426
+ </template>
427
+
428
+ <!-- For Simple Types: Regular Layout -->
429
+ <template v-else>
430
+ <div class="flex items-center gap-1">
431
+ <!-- Field Key (editable) -->
432
+ <div class="flex-1 min-w-0">
433
+ <UInput
434
+ v-if="isEditingKey"
435
+ v-model="editingKeyValue"
436
+ size="xs"
437
+ autofocus
438
+ @blur="saveKey"
439
+ @keydown.enter="saveKey"
440
+ @keydown.esc="isEditingKey = false"
441
+ />
442
+ <UButton
443
+ v-else
444
+ class="text-xs font-medium text-left justify-start px-0 py-0 truncate"
445
+ :label="fieldKey"
446
+ block
447
+ variant="link"
448
+ color="neutral"
449
+ :disabled="readonly || isArrayItem"
450
+ @click="startEditingKey"
451
+ />
452
+ </div>
453
+
454
+ <!-- Type selector -->
455
+ <UDropdownMenu
456
+ :items="[typeOptions]"
457
+ :disabled="readonly"
458
+ size="xs"
459
+ >
460
+ <UButton
461
+ :icon="selectedType?.icon || 'i-lucide-circle-question-mark'"
462
+ variant="soft"
463
+ size="xs"
464
+ :disabled="readonly"
465
+ />
466
+ </UDropdownMenu>
467
+
468
+ <!-- Remove button -->
469
+ <UButton
470
+ v-if="!readonly"
471
+ icon="i-lucide-trash"
472
+ variant="ghost"
473
+ size="xs"
474
+ color="error"
475
+ @click="emit('remove')"
476
+ />
477
+ </div>
478
+
479
+ <!-- Value Input for simple types -->
480
+ <YamlFieldInput
481
+ v-model="modelValue"
482
+ :value-type="valueType"
483
+ :readonly="readonly"
484
+ :field-type="getFieldType(valueType)"
485
+ >
486
+ <!-- Forward all custom field component slots -->
487
+ <template v-for="(_, name) in $slots" #[name]="slotProps">
488
+ <slot :name="name" v-bind="slotProps" />
489
+ </template>
490
+ </YamlFieldInput>
491
+ </template>
492
+ </div>
493
+ </template>
@@ -0,0 +1,64 @@
1
+ import type { YamlFieldType } from "../types/types.js";
2
+ type YamlValue = string | number | boolean | null | Date | YamlValue[] | {
3
+ [key: string]: YamlValue;
4
+ };
5
+ type __VLS_Props = {
6
+ fieldKey: string;
7
+ readonly?: boolean;
8
+ depth?: number;
9
+ /** Custom field type definitions (merged with defaults) */
10
+ fieldTypes?: YamlFieldType[];
11
+ };
12
+ type __VLS_ModelProps = {
13
+ modelValue: YamlValue;
14
+ };
15
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
16
+ declare var __VLS_147: string, __VLS_148: {
17
+ modelValue: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | {
18
+ [key: string]: string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null;
19
+ } | null)[] | {
20
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null;
21
+ } | null)[] | {
22
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
23
+ } | null)[] | {
24
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
25
+ } | null)[] | {
26
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
27
+ } | null)[] | {
28
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
29
+ } | null)[] | {
30
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
31
+ } | null)[] | {
32
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
33
+ } | null)[] | {
34
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
35
+ } | null)[] | {
36
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
37
+ } | null)[] | {
38
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
39
+ } | null)[] | {
40
+ [key: string]: string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | (string | number | boolean | Date | /*elided*/ any | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null)[] | /*elided*/ any | null;
41
+ } | null;
42
+ readonly: boolean;
43
+ valueType: string;
44
+ };
45
+ type __VLS_Slots = {} & {
46
+ [K in NonNullable<typeof __VLS_147>]?: (props: typeof __VLS_148) => any;
47
+ };
48
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
49
+ remove: () => any;
50
+ "update:modelValue": (value: YamlValue) => any;
51
+ "update:fieldKey": (newKey: string) => any;
52
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
53
+ onRemove?: (() => any) | undefined;
54
+ "onUpdate:modelValue"?: ((value: YamlValue) => any) | undefined;
55
+ "onUpdate:fieldKey"?: ((newKey: string) => any) | undefined;
56
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
57
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
58
+ declare const _default: typeof __VLS_export;
59
+ export default _default;
60
+ type __VLS_WithSlots<T, S> = T & {
61
+ new (): {
62
+ $slots: S;
63
+ };
64
+ };
@@ -0,0 +1,17 @@
1
+ import type { YamlFieldType } from "../types/types.js";
2
+ /**
3
+ * Default field types registry
4
+ * This is the single source of truth for all built-in field types
5
+ */
6
+ export declare const DEFAULT_FIELD_TYPES: YamlFieldType[];
7
+ /**
8
+ * Composable for managing YAML field types
9
+ */
10
+ export declare function useYamlFieldTypes(customTypes?: YamlFieldType[]): {
11
+ fieldTypes: any;
12
+ getFieldType: (type: string) => YamlFieldType | undefined;
13
+ detectFieldType: (value: any) => YamlFieldType;
14
+ getDefaultValue: (type: string) => any;
15
+ getIcon: (type: string) => string;
16
+ getTypeMenuItems: (onSelect: (type: string) => void) => any;
17
+ };