@type32/yaml-editor-form 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -96,7 +96,7 @@ const data = ref({
96
96
  </script>
97
97
 
98
98
  <template>
99
- <YamlForm v-model="data" />
99
+ <YamlFormEditor v-model="data" />
100
100
  </template>
101
101
  ```
102
102
 
@@ -104,7 +104,7 @@ const data = ref({
104
104
 
105
105
  ```vue
106
106
  <script setup lang="ts">
107
- import type { YamlFieldType } from './useYamlFieldTypes'
107
+ import type { YamlFieldType } from '@type32/yaml-editor-form'
108
108
 
109
109
  const customTypes: YamlFieldType[] = [
110
110
  {
@@ -123,11 +123,15 @@ const data = ref({
123
123
  </script>
124
124
 
125
125
  <template>
126
- <YamlForm v-model="data" :field-types="customTypes">
127
- <template #field-image="{ modelValue, readonly }">
128
- <MyImagePicker v-model="modelValue" :disabled="readonly" />
126
+ <YamlFormEditor v-model="data" :field-types="customTypes">
127
+ <template #field-image="{ modelValue, readonly, updateModelValue }">
128
+ <MyImagePicker
129
+ :model-value="modelValue"
130
+ :disabled="readonly"
131
+ @update:model-value="updateModelValue"
132
+ />
129
133
  </template>
130
- </YamlForm>
134
+ </YamlFormEditor>
131
135
  </template>
132
136
  ```
133
137
 
@@ -136,7 +140,7 @@ const data = ref({
136
140
  ### Component Hierarchy
137
141
 
138
142
  ```
139
- YamlForm.vue (Entry Point)
143
+ YamlFormEditor.vue (Entry Point)
140
144
  └── YamlFormField.vue (Recursive Component)
141
145
  ├── YamlFieldInput.vue (Simple Types)
142
146
  │ ├── UInput (string)
@@ -147,7 +151,7 @@ YamlForm.vue (Entry Point)
147
151
  │ ├── UInputTags (string-array)
148
152
  │ └── Custom Slots (user-defined)
149
153
  └── YamlFormField.vue (Complex Types - Recursive)
150
- ├── Collapsible (objects/arrays)
154
+ ├── YamlCollapsible (objects/arrays)
151
155
  └── Array/Object rendering
152
156
  ```
153
157
 
@@ -160,7 +164,7 @@ YamlFieldInput (v-model)
160
164
 
161
165
  YamlFormField (v-model)
162
166
 
163
- YamlForm (v-model)
167
+ YamlFormEditor (v-model)
164
168
 
165
169
  Parent Component (data binding)
166
170
  ```
@@ -179,7 +183,7 @@ Components (Rendering)
179
183
 
180
184
  ## Component API
181
185
 
182
- ### YamlForm
186
+ ### YamlFormEditor
183
187
 
184
188
  Main entry point for the editor.
185
189
 
@@ -207,8 +211,33 @@ Main entry point for the editor.
207
211
  All custom field component slots are supported:
208
212
 
209
213
  ```vue
210
- <template #field-{component}="{ modelValue, readonly, valueType }">
214
+ <template #field-{component}="{ modelValue, readonly, valueType, updateModelValue }">
211
215
  <!-- Your custom component -->
216
+ <!-- Use :model-value and @update:model-value, NOT v-model -->
217
+ </template>
218
+ ```
219
+
220
+ **Slot Props:**
221
+ - `modelValue`: Current field value (read-only prop)
222
+ - `readonly`: Whether field is in read-only mode
223
+ - `valueType`: Type identifier string
224
+ - `updateModelValue`: Function to update value: `(newValue) => void`
225
+
226
+ **Important:** You cannot use `v-model` on slot props (they're read-only). Use `:model-value` and `@update:model-value` instead:
227
+
228
+ ```vue
229
+ <!-- ❌ WRONG - v-model doesn't work on slot props -->
230
+ <template #field-color="{ modelValue, readonly }">
231
+ <UColorPicker v-model="modelValue" :disabled="readonly" />
232
+ </template>
233
+
234
+ <!-- ✅ CORRECT - use updateModelValue function -->
235
+ <template #field-color="{ modelValue, readonly, updateModelValue }">
236
+ <UColorPicker
237
+ :model-value="modelValue"
238
+ :disabled="readonly"
239
+ @update:model-value="updateModelValue"
240
+ />
212
241
  </template>
213
242
  ```
214
243
 
@@ -240,7 +269,7 @@ Recursive component that handles individual fields.
240
269
 
241
270
  #### Slots
242
271
 
243
- Same as YamlForm - all custom field slots are forwarded.
272
+ Same as YamlFormEditor - all custom field slots are forwarded through the recursive hierarchy.
244
273
 
245
274
  ### YamlFieldInput
246
275
 
@@ -268,26 +297,73 @@ Renders input components for simple types.
268
297
  #### Slots
269
298
 
270
299
  ```vue
271
- <template #field-{component}="{ modelValue, readonly, valueType }">
300
+ <template #field-{component}="{ modelValue, readonly, valueType, updateModelValue }">
272
301
  <!-- Custom input component -->
302
+ <!-- Use updateModelValue function for two-way binding -->
273
303
  </template>
274
304
  ```
275
305
 
306
+ **Slot Props:**
307
+ - `modelValue`: Current value (read-only)
308
+ - `readonly`: Whether field is read-only
309
+ - `valueType`: Type identifier
310
+ - `updateModelValue`: Update function `(val) => void`
311
+
276
312
  ## Field Types
277
313
 
278
314
  ### Type Definition
279
315
 
280
316
  ```typescript
317
+ // Valid base types (type-safe!)
318
+ type YamlBaseType =
319
+ | 'string' // Text primitives
320
+ | 'number' // Numeric primitives
321
+ | 'boolean' // Boolean primitives
322
+ | 'date' // Date without time
323
+ | 'datetime' // Date with time
324
+ | 'string-array' // Array of strings (tags)
325
+ | 'array' // Generic array
326
+ | 'object' // Generic object
327
+ | 'null' // Null value
328
+
281
329
  interface YamlFieldType {
282
- type: string // Unique type identifier
330
+ type: string // Unique type identifier (e.g., 'color', 'email')
283
331
  label: string // Display name in dropdowns
284
332
  icon: string // Lucide icon name (i-lucide-*)
285
333
  defaultValue: any // Default value or factory function
334
+ baseType: YamlBaseType // REQUIRED: Base type for conversion rules
286
335
  component?: string // Optional: slot name for custom rendering
287
336
  detect?: (value: any) => boolean // Optional: auto-detection function
288
337
  }
289
338
  ```
290
339
 
340
+ **The `baseType` Field (Type-Safe!):**
341
+
342
+ The `baseType` field is **required** and must be one of the predefined base types. This provides:
343
+ - ✅ **TypeScript autocomplete** - IntelliSense suggests valid base types
344
+ - ✅ **Compile-time safety** - Typos are caught immediately
345
+ - ✅ **Conversion inheritance** - Custom types inherit conversion rules from their base
346
+ - ✅ **Clear semantics** - Explicit relationship between custom and base types
347
+
348
+ **Examples:**
349
+ ```typescript
350
+ // ✅ Valid - 'string' is a valid YamlBaseType
351
+ { type: 'color', baseType: 'string' }
352
+
353
+ // ✅ Valid - 'number' is a valid YamlBaseType
354
+ { type: 'percentage', baseType: 'number' }
355
+
356
+ // ❌ Invalid - TypeScript error (not a valid base type)
357
+ { type: 'custom', baseType: 'invalid' } // Type error!
358
+ ```
359
+
360
+ **Conversion Inheritance:**
361
+ - A `color` type with `baseType: 'string'` can convert to/from anything a string can
362
+ - A `percentage` type with `baseType: 'number'` inherits number conversions
363
+ - Custom types can also convert directly to/from their base type
364
+
365
+ This enables powerful type hierarchies without duplicating conversion logic.
366
+
291
367
  ### Built-in Types
292
368
 
293
369
  ```typescript
@@ -427,7 +503,7 @@ const customTypes: YamlFieldType[] = [
427
503
  ```
428
504
 
429
505
  ```vue
430
- <YamlForm v-model="data" :field-types="customTypes" />
506
+ <YamlFormEditor v-model="data" :field-types="customTypes" />
431
507
  ```
432
508
 
433
509
  ### Adding a Runtime Type (With Custom Component)
@@ -439,23 +515,25 @@ const customTypes: YamlFieldType[] = [
439
515
  label: 'Color',
440
516
  icon: 'i-lucide-palette',
441
517
  defaultValue: '#000000',
518
+ baseType: 'string', // Inherits string conversions
442
519
  component: 'color', // Enables slot
443
- detect: (value) => /^#[0-9A-Fa-f]{6}$/.test(value)
520
+ detect: (value) => typeof value === 'string' && /^#[0-9A-Fa-f]{6}$/.test(value)
444
521
  }
445
522
  ]
446
523
  ```
447
524
 
448
525
  ```vue
449
- <YamlForm v-model="data" :field-types="customTypes">
450
- <template #field-color="{ modelValue, readonly }">
526
+ <YamlFormEditor v-model="data" :field-types="customTypes">
527
+ <template #field-color="{ modelValue, readonly, updateModelValue }">
451
528
  <input
452
529
  type="color"
453
- v-model="modelValue"
530
+ :value="modelValue"
454
531
  :disabled="readonly"
532
+ @input="(e) => updateModelValue(e.target.value)"
455
533
  class="w-full h-10 rounded"
456
534
  />
457
535
  </template>
458
- </YamlForm>
536
+ </YamlFormEditor>
459
537
  ```
460
538
 
461
539
  ### Overriding Built-in Types
@@ -473,11 +551,15 @@ const customTypes: YamlFieldType[] = [
473
551
  ```
474
552
 
475
553
  ```vue
476
- <YamlForm v-model="data" :field-types="customTypes">
477
- <template #field-richtext="{ modelValue, readonly }">
478
- <MyRichTextEditor v-model="modelValue" :read-only="readonly" />
554
+ <YamlFormEditor v-model="data" :field-types="customTypes">
555
+ <template #field-richtext="{ modelValue, readonly, updateModelValue }">
556
+ <MyRichTextEditor
557
+ :model-value="modelValue"
558
+ :read-only="readonly"
559
+ @update:model-value="updateModelValue"
560
+ />
479
561
  </template>
480
- </YamlForm>
562
+ </YamlFormEditor>
481
563
  ```
482
564
 
483
565
  ## Schema System
@@ -558,7 +640,7 @@ const config = ref({
558
640
  </script>
559
641
 
560
642
  <template>
561
- <YamlForm v-model="config" />
643
+ <YamlFormEditor v-model="config" />
562
644
  </template>
563
645
  ```
564
646
 
@@ -577,7 +659,7 @@ const article = ref({
577
659
  </script>
578
660
 
579
661
  <template>
580
- <YamlForm v-model="article" />
662
+ <YamlFormEditor v-model="article" />
581
663
  </template>
582
664
  ```
583
665
 
@@ -594,7 +676,7 @@ const data = ref({
594
676
  </script>
595
677
 
596
678
  <template>
597
- <YamlForm v-model="data" />
679
+ <YamlFormEditor v-model="data" />
598
680
  </template>
599
681
  ```
600
682
 
@@ -602,7 +684,7 @@ const data = ref({
602
684
 
603
685
  ```vue
604
686
  <script setup lang="ts">
605
- import type { YamlFieldType } from './useYamlFieldTypes'
687
+ import type { YamlFieldType } from '@type32/yaml-editor-form'
606
688
 
607
689
  // Define custom types
608
690
  const customTypes: YamlFieldType[] = [
@@ -630,23 +712,25 @@ const post = ref({
630
712
  </script>
631
713
 
632
714
  <template>
633
- <YamlForm v-model="post" :field-types="customTypes">
715
+ <YamlFormEditor v-model="post" :field-types="customTypes">
634
716
  <!-- Image picker component -->
635
- <template #field-image="{ modelValue, readonly }">
717
+ <template #field-image="{ modelValue, readonly, updateModelValue }">
636
718
  <MyImagePicker
637
- v-model="modelValue"
719
+ :model-value="modelValue"
638
720
  :disabled="readonly"
721
+ @update:model-value="updateModelValue"
639
722
  />
640
723
  </template>
641
724
 
642
725
  <!-- Markdown editor component -->
643
- <template #field-markdown="{ modelValue, readonly }">
726
+ <template #field-markdown="{ modelValue, readonly, updateModelValue }">
644
727
  <MyMarkdownEditor
645
- v-model="modelValue"
728
+ :model-value="modelValue"
646
729
  :read-only="readonly"
730
+ @update:model-value="updateModelValue"
647
731
  />
648
732
  </template>
649
- </YamlForm>
733
+ </YamlFormEditor>
650
734
  </template>
651
735
  ```
652
736
 
@@ -672,7 +756,7 @@ const customTypes: YamlFieldType[] = [
672
756
  </script>
673
757
 
674
758
  <template>
675
- <YamlForm v-model="data" :field-types="customTypes" />
759
+ <YamlFormEditor v-model="data" :field-types="customTypes" />
676
760
  </template>
677
761
  ```
678
762
 
@@ -680,7 +764,7 @@ const customTypes: YamlFieldType[] = [
680
764
 
681
765
  ```vue
682
766
  <template>
683
- <YamlForm v-model="data" readonly />
767
+ <YamlFormEditor v-model="data" readonly />
684
768
  </template>
685
769
  ```
686
770
 
@@ -712,7 +796,7 @@ const complexData = ref({
712
796
  </script>
713
797
 
714
798
  <template>
715
- <YamlForm v-model="complexData" />
799
+ <YamlFormEditor v-model="complexData" />
716
800
  </template>
717
801
  ```
718
802
 
@@ -720,10 +804,10 @@ const complexData = ref({
720
804
 
721
805
  ```
722
806
  components/
723
- ├── YamlForm.vue ← Entry component
807
+ ├── YamlFormEditor.vue ← Entry component
724
808
  ├── YamlFormField.vue ← Recursive field component
725
809
  ├── YamlFieldInput.vue ← Input rendering component
726
- └── Collapsible.vue ← Collapsible UI component
810
+ └── YamlCollapsible.vue ← Collapsible UI component
727
811
 
728
812
  composables/
729
813
  └── useYamlFieldTypes.ts ← Type registry & composable
@@ -903,27 +987,89 @@ interface YamlFieldInputProps {
903
987
 
904
988
  ### Slot Forwarding
905
989
 
906
- Slots are automatically forwarded through the component hierarchy:
990
+ Slots are automatically forwarded through the component hierarchy using Vue 3's dynamic slot forwarding:
991
+
992
+ ```
993
+ YamlFormEditor (receives slot from parent)
994
+ ↓ forwards all slots with v-bind="slotProps"
995
+ YamlFormField (receives & forwards slots)
996
+ ↓ forwards all slots with v-bind="slotProps"
997
+ ↓ (recursively for nested structures)
998
+ YamlFieldInput (terminal - uses slot)
999
+ ↓ renders slot: #field-{component}
1000
+ ↓ provides props: { modelValue, readonly, valueType, updateModelValue }
1001
+ Custom Component
1002
+ ```
1003
+
1004
+ **How It Works:**
1005
+
1006
+ 1. **YamlFormEditor** (lines 89-92): Receives slots and forwards to YamlFormField
1007
+ 2. **YamlFormField** (lines 634-636): Forwards slots to YamlFieldInput OR itself (for recursion)
1008
+ 3. **YamlFieldInput** (lines 88-95): Final destination - renders slot with props
907
1009
 
1010
+ **Slot Props Flow:**
1011
+
1012
+ The `updateModelValue` function is created at the YamlFieldInput level and allows your custom component to update the value:
1013
+
1014
+ ```typescript
1015
+ // In YamlFieldInput.vue
1016
+ :update-model-value="(val: YamlValue) => modelValue = val"
908
1017
  ```
909
- YamlForm (defines slot)
910
- forwards
911
- YamlFormField (forwards slot)
912
- forwards
913
- YamlFieldInput (uses slot)
1018
+
1019
+ This function captures the parent's `modelValue` ref and updates it directly, maintaining reactivity throughout the hierarchy.
1020
+
1021
+ **Example with Nested Structure:**
1022
+
1023
+ ```vue
1024
+ <!-- Works at any depth! -->
1025
+ <YamlFormEditor v-model="data" :field-types="customTypes">
1026
+ <template #field-color="{ modelValue, updateModelValue }">
1027
+ <UColorPicker
1028
+ :model-value="modelValue"
1029
+ @update:model-value="updateModelValue"
1030
+ />
1031
+ </template>
1032
+ </YamlFormEditor>
914
1033
  ```
915
1034
 
916
- This allows custom components to work at any nesting level.
1035
+ Even if your color field is deeply nested (`data.theme.colors.primary`), the slot works identically because slots are forwarded at every level.
917
1036
 
918
1037
  ### Type Priority
919
1038
 
920
- When multiple types have `detect` functions that match:
1039
+ **Detection Order (NEW in v0.2.0):**
1040
+
1041
+ Custom types with `detect` functions are now checked **before** default types:
1042
+
1043
+ 1. **Custom types** (checked first) - Your custom types take priority
1044
+ 2. **Default types** (checked second) - Built-in types as fallback
1045
+ 3. First matching type wins
1046
+
1047
+ This means:
1048
+ - ✅ Your `color` type will be detected before the default `string` type
1049
+ - ✅ Custom types override default detection behavior
1050
+ - ✅ More specific types should still have more specific detect functions
921
1051
 
922
- 1. Types are checked in array order
923
- 2. First matching type wins
924
- 3. More specific types should come before general types
1052
+ **Example:**
1053
+ ```typescript
1054
+ // Custom color type checked FIRST
1055
+ const customTypes = [{
1056
+ type: 'color',
1057
+ baseType: 'string',
1058
+ detect: (v) => typeof v === 'string' && /^#[0-9A-Fa-f]{6}$/.test(v)
1059
+ }]
1060
+
1061
+ // Value '#FF0000' will match 'color' before 'string'
1062
+ ```
1063
+
1064
+ **Base Type Conversions:**
925
1065
 
926
- **Example order:**
1066
+ When using `baseType`, conversion rules follow this logic:
1067
+
1068
+ 1. Can convert between type and its baseType (e.g., `color` ↔ `string`)
1069
+ 2. Can convert to anything the baseType can (e.g., `color` → `number` because `string` → `number`)
1070
+ 3. Custom conversion rules take precedence over inherited rules
1071
+
1072
+ **Example order for default types:**
927
1073
  ```typescript
928
1074
  [
929
1075
  { type: 'datetime', detect: (v) => isDateTimeString(v) }, // Specific
@@ -1046,11 +1192,11 @@ For issues, questions, or feature requests, refer to the main Vertex project doc
1046
1192
 
1047
1193
  ### Core Components
1048
1194
 
1049
- - **YamlForm**: Entry point component for the editor
1195
+ - **YamlFormEditor**: Entry point component for the editor
1050
1196
  - **YamlFormField**: Recursive component handling individual fields
1051
1197
  - **YamlFieldInput**: Input rendering component for simple types
1198
+ - **YamlCollapsible**: UI component for collapsible sections
1052
1199
  - **useYamlFieldTypes**: Composable for type registry and management
1053
- - **Collapsible**: UI component for collapsible sections
1054
1200
 
1055
1201
  ### Key Concepts
1056
1202
 
@@ -1077,11 +1223,15 @@ For issues, questions, or feature requests, refer to the main Vertex project doc
1077
1223
 
1078
1224
  **Add Custom Component:**
1079
1225
  ```vue
1080
- <YamlForm>
1081
- <template #field-{type}="props">
1082
- <Component v-bind="props" />
1226
+ <YamlFormEditor v-model="data" :field-types="customTypes">
1227
+ <template #field-{type}="{ modelValue, readonly, updateModelValue }">
1228
+ <MyComponent
1229
+ :model-value="modelValue"
1230
+ :disabled="readonly"
1231
+ @update:model-value="updateModelValue"
1232
+ />
1083
1233
  </template>
1084
- </YamlForm>
1234
+ </YamlFormEditor>
1085
1235
  ```
1086
1236
 
1087
1237
  **Type Conversion:**
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@type32/yaml-editor-form",
3
3
  "configKey": "yamlEditorForm",
4
- "version": "0.1.2",
4
+ "version": "0.1.4",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -15,7 +15,7 @@ const module$1 = defineNuxtModule({
15
15
  _nuxt.options.alias["@type32/yaml-editor-form"] = resolver.resolve(
16
16
  "./runtime/types/types"
17
17
  );
18
- _nuxt.options.alias["@type32/yaml-editor-form"] = resolver.resolve(
18
+ _nuxt.options.alias["@type32/yaml-editor-form/css"] = resolver.resolve(
19
19
  "./runtime/assets/css"
20
20
  );
21
21
  _nuxt.options.css.unshift(resolver.resolve("./runtime/assets/css/main.css"));
@@ -1,3 +1,13 @@
1
+ type __VLS_Props = {
2
+ label: string;
3
+ defaultOpen?: boolean;
4
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
5
+ };
6
+ declare const open: import("vue").ModelRef<boolean, string, boolean, boolean>;
7
+ type __VLS_ModelProps = {
8
+ 'open'?: typeof open['value'];
9
+ };
10
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
1
11
  declare var __VLS_13: {}, __VLS_15: {}, __VLS_18: {};
2
12
  type __VLS_Slots = {} & {
3
13
  badge?: (props: typeof __VLS_13) => any;
@@ -6,37 +16,14 @@ type __VLS_Slots = {} & {
6
16
  } & {
7
17
  default?: (props: typeof __VLS_18) => any;
8
18
  };
9
- declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
10
- label: {
11
- type: StringConstructor;
12
- required: true;
13
- };
14
- defaultOpen: {
15
- type: BooleanConstructor;
16
- default: boolean;
17
- };
18
- open: {
19
- type: import("vue").PropType<boolean>;
20
- };
21
- }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
22
20
  "update:open": (value: boolean) => any;
23
- }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
24
- label: {
25
- type: StringConstructor;
26
- required: true;
27
- };
28
- defaultOpen: {
29
- type: BooleanConstructor;
30
- default: boolean;
31
- };
32
- open: {
33
- type: import("vue").PropType<boolean>;
34
- };
35
- }>> & Readonly<{
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
36
22
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
37
23
  }>, {
38
24
  defaultOpen: boolean;
39
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
25
+ size: "xl" | "lg" | "md" | "sm" | "xs";
26
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
27
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
28
  declare const _default: typeof __VLS_export;
42
29
  export default _default;
@@ -1,13 +1,8 @@
1
1
  <script setup>
2
2
  defineProps({
3
- label: {
4
- type: String,
5
- required: true
6
- },
7
- defaultOpen: {
8
- type: Boolean,
9
- default: false
10
- }
3
+ label: { type: String, required: true },
4
+ defaultOpen: { type: Boolean, required: false, default: false },
5
+ size: { type: String, required: false, default: "xs" }
11
6
  });
12
7
  const open = defineModel("open", { type: Boolean, default: void 0 });
13
8
  </script>
@@ -17,6 +12,7 @@ const open = defineModel("open", { type: Boolean, default: void 0 });
17
12
  v-model:open="open"
18
13
  :default-open="defaultOpen"
19
14
  class="group/collapsible"
15
+ :size="size"
20
16
  >
21
17
  <div class="flex items-center justify-between gap-2 w-full">
22
18
  <div class="flex items-center gap-2">
@@ -27,7 +23,7 @@ const open = defineModel("open", { type: Boolean, default: void 0 });
27
23
  class="size-2.5 text-muted transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
28
24
  />
29
25
  </div>
30
- <span class="text-xs font-medium text-highlighted tracking-tight">{{ label }}</span>
26
+ <span :class="`text-${size}`" class="font-medium text-highlighted tracking-tight">{{ label }}</span>
31
27
  <slot name="badge"/>
32
28
  </div>
33
29
 
@@ -1,3 +1,13 @@
1
+ type __VLS_Props = {
2
+ label: string;
3
+ defaultOpen?: boolean;
4
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
5
+ };
6
+ declare const open: import("vue").ModelRef<boolean, string, boolean, boolean>;
7
+ type __VLS_ModelProps = {
8
+ 'open'?: typeof open['value'];
9
+ };
10
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
1
11
  declare var __VLS_13: {}, __VLS_15: {}, __VLS_18: {};
2
12
  type __VLS_Slots = {} & {
3
13
  badge?: (props: typeof __VLS_13) => any;
@@ -6,37 +16,14 @@ type __VLS_Slots = {} & {
6
16
  } & {
7
17
  default?: (props: typeof __VLS_18) => any;
8
18
  };
9
- declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
10
- label: {
11
- type: StringConstructor;
12
- required: true;
13
- };
14
- defaultOpen: {
15
- type: BooleanConstructor;
16
- default: boolean;
17
- };
18
- open: {
19
- type: import("vue").PropType<boolean>;
20
- };
21
- }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
22
20
  "update:open": (value: boolean) => any;
23
- }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
24
- label: {
25
- type: StringConstructor;
26
- required: true;
27
- };
28
- defaultOpen: {
29
- type: BooleanConstructor;
30
- default: boolean;
31
- };
32
- open: {
33
- type: import("vue").PropType<boolean>;
34
- };
35
- }>> & Readonly<{
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
36
22
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
37
23
  }>, {
38
24
  defaultOpen: boolean;
39
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
25
+ size: "xl" | "lg" | "md" | "sm" | "xs";
26
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
27
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
28
  declare const _default: typeof __VLS_export;
42
29
  export default _default;
@@ -24,6 +24,7 @@ type __VLS_Props = {
24
24
  readonly?: boolean;
25
25
  /** Optional field type definition for custom component detection */
26
26
  fieldType?: YamlFieldType;
27
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
27
28
  };
28
29
  type __VLS_ModelProps = {
29
30
  modelValue: YamlValue;
@@ -33,6 +34,7 @@ declare var __VLS_2: string, __VLS_3: {
33
34
  modelValue: YamlValue;
34
35
  readonly: boolean;
35
36
  valueType: string;
37
+ updateModelValue: (val: YamlValue) => YamlValue;
36
38
  };
37
39
  type __VLS_Slots = {} & {
38
40
  [K in NonNullable<typeof __VLS_2>]?: (props: typeof __VLS_3) => any;
@@ -42,6 +44,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
42
44
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
43
45
  "onUpdate:modelValue"?: ((value: YamlValue) => any) | undefined;
44
46
  }>, {
47
+ size: "xl" | "lg" | "md" | "sm" | "xs";
45
48
  readonly: boolean;
46
49
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
50
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -5,7 +5,8 @@ const modelValue = defineModel({ type: [String, Number, Boolean, null, Date, Arr
5
5
  const props = defineProps({
6
6
  valueType: { type: String, required: true },
7
7
  readonly: { type: Boolean, required: false, default: false },
8
- fieldType: { type: Object, required: false }
8
+ fieldType: { type: Object, required: false },
9
+ size: { type: String, required: false, default: "xs" }
9
10
  });
10
11
  function isDateObject(val) {
11
12
  return val instanceof Date && !isNaN(val.getTime());
@@ -53,6 +54,7 @@ const customSlotName = computed(() => {
53
54
  :model-value="modelValue"
54
55
  :readonly="readonly"
55
56
  :value-type="valueType"
57
+ :update-model-value="(val) => modelValue = val"
56
58
  />
57
59
 
58
60
  <!-- Built-in Input Components -->
@@ -61,7 +63,7 @@ const customSlotName = computed(() => {
61
63
  <UInput
62
64
  v-if="valueType === 'string'"
63
65
  :model-value="String(modelValue)"
64
- size="xs"
66
+ :size="size"
65
67
  :disabled="readonly"
66
68
  placeholder="Enter text..."
67
69
  @update:model-value="(val) => modelValue = val"
@@ -71,7 +73,7 @@ const customSlotName = computed(() => {
71
73
  <UTextarea
72
74
  v-else-if="valueType === 'textarea'"
73
75
  :model-value="String(modelValue)"
74
- size="xs"
76
+ :size="size"
75
77
  :disabled="readonly"
76
78
  placeholder="Enter long text..."
77
79
  :rows="4"
@@ -83,7 +85,7 @@ const customSlotName = computed(() => {
83
85
  <UInputNumber
84
86
  v-else-if="valueType === 'number'"
85
87
  :model-value="Number(modelValue)"
86
- size="xs"
88
+ :size="size"
87
89
  :disabled="readonly"
88
90
  placeholder="0"
89
91
  @update:model-value="(val) => modelValue = val ?? 0"
@@ -93,7 +95,7 @@ const customSlotName = computed(() => {
93
95
  <USwitch
94
96
  v-else-if="valueType === 'boolean'"
95
97
  :model-value="Boolean(modelValue)"
96
- size="xs"
98
+ :size="size"
97
99
  :disabled="readonly"
98
100
  @update:model-value="(val) => modelValue = val"
99
101
  />
@@ -102,7 +104,7 @@ const customSlotName = computed(() => {
102
104
  <UInputDate
103
105
  v-else-if="valueType === 'date'"
104
106
  :model-value="typeof modelValue === 'string' ? stringToCalendarDate(modelValue) : jsDateToCalendarDate(/* @__PURE__ */ new Date())"
105
- size="xs"
107
+ :size="size"
106
108
  :disabled="readonly"
107
109
  @update:model-value="(val) => {
108
110
  if (val && 'year' in val) {
@@ -115,7 +117,7 @@ const customSlotName = computed(() => {
115
117
  <UInputDate
116
118
  v-else-if="valueType === 'datetime'"
117
119
  :model-value="typeof modelValue === 'string' ? stringToCalendarDateTime(modelValue) : jsDateToCalendarDateTime(/* @__PURE__ */ new Date())"
118
- size="xs"
120
+ :size="size"
119
121
  :disabled="readonly"
120
122
  granularity="second"
121
123
  @update:model-value="(val) => {
@@ -134,7 +136,7 @@ const customSlotName = computed(() => {
134
136
  <UInputTags
135
137
  v-else-if="valueType === 'string-array'"
136
138
  :model-value="Array.isArray(modelValue) ? modelValue : []"
137
- size="xs"
139
+ :size="size"
138
140
  :disabled="readonly"
139
141
  placeholder="Add tags..."
140
142
  @update:model-value="(val) => modelValue = val"
@@ -149,7 +151,7 @@ const customSlotName = computed(() => {
149
151
  <UInput
150
152
  v-else
151
153
  :model-value="String(modelValue || '')"
152
- size="xs"
154
+ :size="size"
153
155
  :disabled="readonly"
154
156
  placeholder="Enter value..."
155
157
  @update:model-value="(val) => modelValue = val"
@@ -24,6 +24,7 @@ type __VLS_Props = {
24
24
  readonly?: boolean;
25
25
  /** Optional field type definition for custom component detection */
26
26
  fieldType?: YamlFieldType;
27
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
27
28
  };
28
29
  type __VLS_ModelProps = {
29
30
  modelValue: YamlValue;
@@ -33,6 +34,7 @@ declare var __VLS_2: string, __VLS_3: {
33
34
  modelValue: YamlValue;
34
35
  readonly: boolean;
35
36
  valueType: string;
37
+ updateModelValue: (val: YamlValue) => YamlValue;
36
38
  };
37
39
  type __VLS_Slots = {} & {
38
40
  [K in NonNullable<typeof __VLS_2>]?: (props: typeof __VLS_3) => any;
@@ -42,6 +44,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
42
44
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
43
45
  "onUpdate:modelValue"?: ((value: YamlValue) => any) | undefined;
44
46
  }>, {
47
+ size: "xl" | "lg" | "md" | "sm" | "xs";
45
48
  readonly: boolean;
46
49
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
50
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  readonly?: boolean;
5
5
  /** Custom field type definitions (merged with defaults) */
6
6
  fieldTypes?: YamlFieldType[];
7
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
7
8
  };
8
9
  type __VLS_ModelProps = {
9
10
  /**
@@ -54,6 +55,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
54
55
  slug?: string;
55
56
  }) => any) | undefined;
56
57
  }>, {
58
+ size: "xl" | "lg" | "md" | "sm" | "xs";
57
59
  readonly: boolean;
58
60
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
59
61
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -5,7 +5,8 @@ const data = defineModel({ type: null, ...{ required: true } });
5
5
  const props = defineProps({
6
6
  filePath: { type: String, required: false },
7
7
  readonly: { type: Boolean, required: false, default: false },
8
- fieldTypes: { type: Array, required: false }
8
+ fieldTypes: { type: Array, required: false },
9
+ size: { type: String, required: false, default: "xs" }
9
10
  });
10
11
  const { getDefaultValue, getTypeMenuItems } = useYamlFieldTypes(props.fieldTypes);
11
12
  const isDirty = ref(false);
@@ -38,6 +39,7 @@ const addFieldOptions = computed(() => {
38
39
  :field-key="String(key)"
39
40
  :readonly="readonly"
40
41
  :field-types="fieldTypes"
42
+ :size="size"
41
43
  @remove="removeField(String(key))"
42
44
  @update:field-key="(newKey) => {
43
45
  if (newKey !== key) {
@@ -56,13 +58,13 @@ const addFieldOptions = computed(() => {
56
58
  <UDropdownMenu
57
59
  v-if="!readonly"
58
60
  :items="[addFieldOptions]"
59
- size="sm"
61
+ :size="size"
60
62
  >
61
63
  <UButton
62
64
  icon="i-lucide-plus"
63
65
  label="Add Field"
64
66
  variant="ghost"
65
- size="sm"
67
+ :size="size"
66
68
  />
67
69
  </UDropdownMenu>
68
70
  </div>
@@ -4,6 +4,7 @@ type __VLS_Props = {
4
4
  readonly?: boolean;
5
5
  /** Custom field type definitions (merged with defaults) */
6
6
  fieldTypes?: YamlFieldType[];
7
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
7
8
  };
8
9
  type __VLS_ModelProps = {
9
10
  /**
@@ -54,6 +55,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
54
55
  slug?: string;
55
56
  }) => any) | undefined;
56
57
  }>, {
58
+ size: "xl" | "lg" | "md" | "sm" | "xs";
57
59
  readonly: boolean;
58
60
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
59
61
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -25,6 +25,7 @@ type __VLS_Props = {
25
25
  depth?: number;
26
26
  /** Custom field type definitions (merged with defaults) */
27
27
  fieldTypes?: YamlFieldType[];
28
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
28
29
  };
29
30
  type __VLS_ModelProps = {
30
31
  modelValue: YamlValue;
@@ -43,6 +44,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
43
44
  "onUpdate:modelValue"?: ((value: YamlValue) => any) | undefined;
44
45
  "onUpdate:fieldKey"?: ((newKey: string) => any) | undefined;
45
46
  }>, {
47
+ size: "xl" | "lg" | "md" | "sm" | "xs";
46
48
  readonly: boolean;
47
49
  depth: number;
48
50
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -6,7 +6,8 @@ const props = defineProps({
6
6
  fieldKey: { type: String, required: true },
7
7
  readonly: { type: Boolean, required: false, default: false },
8
8
  depth: { type: Number, required: false, default: 0 },
9
- fieldTypes: { type: Array, required: false }
9
+ fieldTypes: { type: Array, required: false },
10
+ size: { type: String, required: false, default: "xs" }
10
11
  });
11
12
  const emit = defineEmits(["remove", "update:fieldKey"]);
12
13
  const {
@@ -67,6 +68,13 @@ function saveKey() {
67
68
  function isValidConversion(fromType, toType) {
68
69
  if (fromType === toType) return true;
69
70
  if (fromType === "null") return true;
71
+ const fromFieldType = getFieldType(fromType);
72
+ const toFieldType = getFieldType(toType);
73
+ if (!fromFieldType || !toFieldType) return false;
74
+ const fromBase = fromFieldType.baseType;
75
+ const toBase = toFieldType.baseType;
76
+ if (fromType === toBase || toType === fromBase) return true;
77
+ if (fromBase === toType || toBase === fromType) return true;
70
78
  const conversionRules = {
71
79
  // Primitives can convert to other primitives and arrays
72
80
  "string": ["number", "boolean", "date", "datetime", "string-array", "null"],
@@ -80,10 +88,12 @@ function isValidConversion(fromType, toType) {
80
88
  // Arrays can only convert to string-array or null (converting to primitives is unsafe)
81
89
  "array": ["string-array", "null"],
82
90
  // Objects can only convert to null (converting to primitives is useless)
83
- "object": ["null"]
91
+ "object": ["null"],
92
+ // Null is always terminal
93
+ "null": []
84
94
  };
85
- const allowedConversions = conversionRules[fromType] || [];
86
- return allowedConversions.includes(toType);
95
+ const allowedConversions = conversionRules[fromBase] || [];
96
+ return allowedConversions.includes(toBase);
87
97
  }
88
98
  function convertType(newType) {
89
99
  const currentType = valueType.value;
@@ -255,7 +265,7 @@ const addArrayItemOptions = computed(() => {
255
265
  <UInput
256
266
  v-if="isEditingKey"
257
267
  v-model="editingKeyValue"
258
- size="xs"
268
+ :size="size"
259
269
  autofocus
260
270
  class="mb-1"
261
271
  @blur="saveKey"
@@ -266,7 +276,7 @@ const addArrayItemOptions = computed(() => {
266
276
  <!-- Collapsible for non-edit mode -->
267
277
  <YamlCollapsible v-else v-model:open="isOpen" :default-open="true" :label="fieldKey">
268
278
  <template #badge>
269
- <UBadge size="xs" variant="soft" color="neutral">{{ itemCount }}</UBadge>
279
+ <UBadge :size="size" variant="soft" color="neutral">{{ itemCount }}</UBadge>
270
280
  </template>
271
281
 
272
282
  <template #actions>
@@ -276,7 +286,7 @@ const addArrayItemOptions = computed(() => {
276
286
  v-if="!readonly && !isEditingKey && !isArrayItem"
277
287
  icon="i-lucide-pencil"
278
288
  variant="ghost"
279
- size="xs"
289
+ :size="size"
280
290
  color="neutral"
281
291
  class="text-muted"
282
292
  @click.stop="startEditingKey"
@@ -286,12 +296,12 @@ const addArrayItemOptions = computed(() => {
286
296
  <UDropdownMenu
287
297
  :items="[typeOptions]"
288
298
  :disabled="readonly"
289
- size="xs"
299
+ :size="size"
290
300
  >
291
301
  <UButton
292
302
  :icon="selectedType?.icon || 'i-heroicons-circle-question-mark'"
293
303
  variant="soft"
294
- size="xs"
304
+ :size="size"
295
305
  :disabled="readonly"
296
306
  />
297
307
  </UDropdownMenu>
@@ -301,7 +311,7 @@ const addArrayItemOptions = computed(() => {
301
311
  v-if="!readonly"
302
312
  icon="i-lucide-trash"
303
313
  variant="ghost"
304
- size="xs"
314
+ :size="size"
305
315
  color="error"
306
316
  @click.stop="emit('remove')"
307
317
  />
@@ -337,6 +347,7 @@ const addArrayItemOptions = computed(() => {
337
347
  if (Array.isArray(modelValue)) modelValue[index] = val;
338
348
  }"
339
349
  @remove="removeArrayItem(index)"
350
+ :size="size"
340
351
  />
341
352
  </div>
342
353
  </div>
@@ -346,13 +357,13 @@ const addArrayItemOptions = computed(() => {
346
357
  <UDropdownMenu
347
358
  v-if="!readonly"
348
359
  :items="[addArrayItemOptions]"
349
- size="xs"
360
+ :size="size"
350
361
  >
351
362
  <UButton
352
363
  icon="i-lucide-plus"
353
364
  label="Add Item"
354
365
  variant="link"
355
- size="xs"
366
+ :size="size"
356
367
  color="neutral"
357
368
  />
358
369
  </UDropdownMenu>
@@ -363,7 +374,7 @@ const addArrayItemOptions = computed(() => {
363
374
  icon="i-lucide-copy-plus"
364
375
  label="From Template"
365
376
  variant="link"
366
- size="xs"
377
+ :size="size"
367
378
  color="neutral"
368
379
  @click="addArrayItemFromTemplate"
369
380
  />
@@ -390,6 +401,7 @@ const addArrayItemOptions = computed(() => {
390
401
  :readonly="readonly"
391
402
  :depth="depth + 1"
392
403
  :field-types="fieldTypes"
404
+ :size="size"
393
405
  @update:model-value="(val) => {
394
406
  if (typeof modelValue === 'object' && !Array.isArray(modelValue) && modelValue !== null && !isDateObject(modelValue)) {
395
407
  modelValue[key] = val;
@@ -410,13 +422,13 @@ const addArrayItemOptions = computed(() => {
410
422
  <UDropdownMenu
411
423
  v-if="!readonly"
412
424
  :items="[addFieldOptions]"
413
- size="xs"
425
+ :size="size"
414
426
  >
415
427
  <UButton
416
428
  icon="i-lucide-plus"
417
429
  label="Add Field"
418
430
  variant="link"
419
- size="xs"
431
+ :size="size"
420
432
  color="neutral"
421
433
  />
422
434
  </UDropdownMenu>
@@ -432,7 +444,7 @@ const addArrayItemOptions = computed(() => {
432
444
  <UInput
433
445
  v-if="isEditingKey"
434
446
  v-model="editingKeyValue"
435
- size="xs"
447
+ :size="size"
436
448
  autofocus
437
449
  @blur="saveKey"
438
450
  @keydown.enter="saveKey"
@@ -454,12 +466,12 @@ const addArrayItemOptions = computed(() => {
454
466
  <UDropdownMenu
455
467
  :items="[typeOptions]"
456
468
  :disabled="readonly"
457
- size="xs"
469
+ :size="size"
458
470
  >
459
471
  <UButton
460
472
  :icon="selectedType?.icon || 'i-lucide-circle-question-mark'"
461
473
  variant="soft"
462
- size="xs"
474
+ :size="size"
463
475
  :disabled="readonly"
464
476
  />
465
477
  </UDropdownMenu>
@@ -469,7 +481,7 @@ const addArrayItemOptions = computed(() => {
469
481
  v-if="!readonly"
470
482
  icon="i-lucide-trash"
471
483
  variant="ghost"
472
- size="xs"
484
+ :size="size"
473
485
  color="error"
474
486
  @click="emit('remove')"
475
487
  />
@@ -480,6 +492,7 @@ const addArrayItemOptions = computed(() => {
480
492
  v-model="modelValue"
481
493
  :value-type="valueType"
482
494
  :readonly="readonly"
495
+ :size="size"
483
496
  :field-type="getFieldType(valueType)"
484
497
  >
485
498
  <!-- Forward all custom field component slots -->
@@ -25,6 +25,7 @@ type __VLS_Props = {
25
25
  depth?: number;
26
26
  /** Custom field type definitions (merged with defaults) */
27
27
  fieldTypes?: YamlFieldType[];
28
+ size?: "xl" | "lg" | "md" | "sm" | "xs";
28
29
  };
29
30
  type __VLS_ModelProps = {
30
31
  modelValue: YamlValue;
@@ -43,6 +44,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
43
44
  "onUpdate:modelValue"?: ((value: YamlValue) => any) | undefined;
44
45
  "onUpdate:fieldKey"?: ((newKey: string) => any) | undefined;
45
46
  }>, {
47
+ size: "xl" | "lg" | "md" | "sm" | "xs";
46
48
  readonly: boolean;
47
49
  depth: number;
48
50
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -3,11 +3,15 @@ import type { YamlFieldType } from "../types/types.js";
3
3
  * Composable for managing YAML field types
4
4
  */
5
5
  export declare function useYamlFieldTypes(customTypes?: YamlFieldType[]): {
6
- fieldTypes: any;
6
+ fieldTypes: import("vue").ComputedRef<YamlFieldType[]>;
7
7
  getFieldType: (type: string) => YamlFieldType | undefined;
8
8
  detectFieldType: (value: any) => YamlFieldType;
9
9
  getDefaultValue: (type: string) => any;
10
10
  getIcon: (type: string) => string;
11
- getTypeMenuItems: (onSelect: (type: string) => void) => any;
11
+ getTypeMenuItems: (onSelect: (type: string) => void) => {
12
+ label: string;
13
+ icon: string;
14
+ onSelect: () => void;
15
+ }[];
12
16
  DEFAULT_FIELD_TYPES: YamlFieldType[];
13
17
  };
@@ -1,3 +1,4 @@
1
+ import { computed } from "vue";
1
2
  function isDateObject(value) {
2
3
  return value instanceof Date;
3
4
  }
@@ -19,6 +20,7 @@ export function useYamlFieldTypes(customTypes) {
19
20
  label: "Text",
20
21
  icon: "i-lucide-type",
21
22
  defaultValue: "",
23
+ baseType: "string",
22
24
  detect: (value) => typeof value === "string" && !isDateString(value) && !isDateTimeString(value)
23
25
  },
24
26
  {
@@ -26,6 +28,7 @@ export function useYamlFieldTypes(customTypes) {
26
28
  label: "Long Text",
27
29
  icon: "i-lucide-align-left",
28
30
  defaultValue: "",
31
+ baseType: "string",
29
32
  component: "textarea"
30
33
  },
31
34
  {
@@ -33,6 +36,7 @@ export function useYamlFieldTypes(customTypes) {
33
36
  label: "Number",
34
37
  icon: "i-lucide-hash",
35
38
  defaultValue: 0,
39
+ baseType: "number",
36
40
  detect: (value) => typeof value === "number"
37
41
  },
38
42
  {
@@ -40,6 +44,7 @@ export function useYamlFieldTypes(customTypes) {
40
44
  label: "Boolean",
41
45
  icon: "i-lucide-circle-check",
42
46
  defaultValue: false,
47
+ baseType: "boolean",
43
48
  detect: (value) => typeof value === "boolean"
44
49
  },
45
50
  {
@@ -47,6 +52,7 @@ export function useYamlFieldTypes(customTypes) {
47
52
  label: "Date",
48
53
  icon: "i-lucide-calendar",
49
54
  defaultValue: () => /* @__PURE__ */ new Date(),
55
+ baseType: "date",
50
56
  detect: (value) => isDateObject(value) || isDateString(value)
51
57
  },
52
58
  {
@@ -54,6 +60,7 @@ export function useYamlFieldTypes(customTypes) {
54
60
  label: "Date & Time",
55
61
  icon: "i-lucide-calendar-clock",
56
62
  defaultValue: () => /* @__PURE__ */ new Date(),
63
+ baseType: "datetime",
57
64
  detect: (value) => isDateTimeString(value)
58
65
  },
59
66
  {
@@ -61,6 +68,7 @@ export function useYamlFieldTypes(customTypes) {
61
68
  label: "Tags",
62
69
  icon: "i-lucide-tags",
63
70
  defaultValue: [],
71
+ baseType: "string-array",
64
72
  detect: (value) => isStringArray(value)
65
73
  },
66
74
  {
@@ -68,6 +76,7 @@ export function useYamlFieldTypes(customTypes) {
68
76
  label: "Array",
69
77
  icon: "i-lucide-list",
70
78
  defaultValue: [],
79
+ baseType: "array",
71
80
  detect: (value) => Array.isArray(value) && !isStringArray(value)
72
81
  },
73
82
  {
@@ -75,6 +84,7 @@ export function useYamlFieldTypes(customTypes) {
75
84
  label: "Object",
76
85
  icon: "i-lucide-box",
77
86
  defaultValue: {},
87
+ baseType: "object",
78
88
  detect: (value) => typeof value === "object" && value !== null && !Array.isArray(value) && !isDateObject(value)
79
89
  },
80
90
  {
@@ -82,22 +92,24 @@ export function useYamlFieldTypes(customTypes) {
82
92
  label: "Null",
83
93
  icon: "i-lucide-circle-slash",
84
94
  defaultValue: null,
95
+ baseType: "null",
85
96
  detect: (value) => value === null
86
97
  }
87
98
  ];
88
99
  const fieldTypes = computed(() => {
89
100
  const types = [...DEFAULT_FIELD_TYPES];
101
+ const newCustomTypes = [];
90
102
  if (customTypes) {
91
103
  for (const customType of customTypes) {
92
104
  const existingIndex = types.findIndex((t) => t.type === customType.type);
93
105
  if (existingIndex >= 0) {
94
106
  types[existingIndex] = customType;
95
107
  } else {
96
- types.push(customType);
108
+ newCustomTypes.push(customType);
97
109
  }
98
110
  }
99
111
  }
100
- return types;
112
+ return [...newCustomTypes, ...types];
101
113
  });
102
114
  const getFieldType = (type) => {
103
115
  return fieldTypes.value.find((t) => t.type === type);
@@ -10,20 +10,43 @@ export type YamlFormData<T extends object = {}> = {
10
10
  [key: string]: any;
11
11
  } & T
12
12
 
13
+ /**
14
+ * Base types that define conversion rules
15
+ * Custom types inherit conversion rules from these base types
16
+ */
17
+ export type YamlBaseType =
18
+ | 'string' // Text primitives
19
+ | 'number' // Numeric primitives
20
+ | 'boolean' // Boolean primitives
21
+ | 'date' // Date without time
22
+ | 'datetime' // Date with time
23
+ | 'string-array' // Array of strings (tags)
24
+ | 'array' // Generic array
25
+ | 'object' // Generic object
26
+ | 'null' // Null value
27
+
13
28
  /**
14
29
  * YAML Field Type Definition
15
30
  * Centralized schema for all field types supported by the YAML editor
16
31
  */
17
32
  export interface YamlFieldType {
18
- /** Unique type identifier */
33
+ /** Unique type identifier (can be custom, e.g. 'color', 'email', 'url') */
19
34
  type: string
20
- /** Display label */
35
+ /** Display label shown in dropdowns */
21
36
  label: string
22
- /** Lucide icon name */
37
+ /** Lucide icon name (e.g. 'i-lucide-palette') */
23
38
  icon: string
24
39
  /** Default value when creating new field of this type */
25
40
  defaultValue: any
26
- /** Optional slot name for custom component rendering */
41
+ /**
42
+ * Base type for conversion rules - determines what this type can convert to/from
43
+ * Custom types inherit conversion rules from their base type
44
+ * @example
45
+ * - color → 'string' (can convert to/from string, number, boolean, etc.)
46
+ * - percentage → 'number' (can convert to/from number, string, boolean)
47
+ */
48
+ baseType: YamlBaseType
49
+ /** Optional slot name for custom component rendering (e.g. 'color' → #field-color) */
27
50
  component?: string
28
51
  /** Optional detection function for auto-typing existing values */
29
52
  detect?: (value: any) => boolean
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@type32/yaml-editor-form",
3
- "version": "0.1.2",
4
- "description": "OFM Editor Component for Nuxt.",
3
+ "version": "0.1.4",
4
+ "description": "YAML Editor Form Component for Nuxt.",
5
5
  "repository": "https://github.com/CTRL-Neo-Studios/yaml-editor-form",
6
6
  "license": "MIT",
7
7
  "type": "module",