@type32/yaml-editor-form 0.1.4 → 0.1.5
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 +324 -1
- package/dist/module.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,6 +112,7 @@ const customTypes: YamlFieldType[] = [
|
|
|
112
112
|
label: 'Image',
|
|
113
113
|
icon: 'i-lucide-image',
|
|
114
114
|
defaultValue: '',
|
|
115
|
+
baseType: 'string',
|
|
115
116
|
component: 'image'
|
|
116
117
|
}
|
|
117
118
|
]
|
|
@@ -477,6 +478,7 @@ export const DEFAULT_FIELD_TYPES: YamlFieldType[] = [
|
|
|
477
478
|
label: 'Email',
|
|
478
479
|
icon: 'i-lucide-mail',
|
|
479
480
|
defaultValue: '',
|
|
481
|
+
baseType: 'string',
|
|
480
482
|
detect: (value) => typeof value === 'string' && /^[^@]+@[^@]+/.test(value)
|
|
481
483
|
}
|
|
482
484
|
]
|
|
@@ -497,6 +499,7 @@ const customTypes: YamlFieldType[] = [
|
|
|
497
499
|
label: 'URL',
|
|
498
500
|
icon: 'i-lucide-link',
|
|
499
501
|
defaultValue: 'https://',
|
|
502
|
+
baseType: 'string',
|
|
500
503
|
detect: (value) => typeof value === 'string' && value.startsWith('http')
|
|
501
504
|
}
|
|
502
505
|
]
|
|
@@ -545,6 +548,7 @@ const customTypes: YamlFieldType[] = [
|
|
|
545
548
|
label: 'Rich Text',
|
|
546
549
|
icon: 'i-lucide-file-text',
|
|
547
550
|
defaultValue: '',
|
|
551
|
+
baseType: 'string',
|
|
548
552
|
component: 'richtext' // Now uses custom component
|
|
549
553
|
}
|
|
550
554
|
]
|
|
@@ -693,6 +697,7 @@ const customTypes: YamlFieldType[] = [
|
|
|
693
697
|
label: 'Image',
|
|
694
698
|
icon: 'i-lucide-image',
|
|
695
699
|
defaultValue: '',
|
|
700
|
+
baseType: 'string',
|
|
696
701
|
component: 'image'
|
|
697
702
|
},
|
|
698
703
|
{
|
|
@@ -700,6 +705,7 @@ const customTypes: YamlFieldType[] = [
|
|
|
700
705
|
label: 'Markdown',
|
|
701
706
|
icon: 'i-lucide-file-text',
|
|
702
707
|
defaultValue: '',
|
|
708
|
+
baseType: 'string',
|
|
703
709
|
component: 'markdown'
|
|
704
710
|
}
|
|
705
711
|
]
|
|
@@ -744,13 +750,15 @@ const customTypes: YamlFieldType[] = [
|
|
|
744
750
|
label: 'UUID',
|
|
745
751
|
icon: 'i-lucide-fingerprint',
|
|
746
752
|
defaultValue: () => crypto.randomUUID(), // Function called each time
|
|
753
|
+
baseType: 'string',
|
|
747
754
|
detect: (v) => /^[0-9a-f]{8}-[0-9a-f]{4}-/.test(v)
|
|
748
755
|
},
|
|
749
756
|
{
|
|
750
757
|
type: 'timestamp',
|
|
751
758
|
label: 'Timestamp',
|
|
752
759
|
icon: 'i-lucide-clock',
|
|
753
|
-
defaultValue: () => new Date().toISOString()
|
|
760
|
+
defaultValue: () => new Date().toISOString(),
|
|
761
|
+
baseType: 'string'
|
|
754
762
|
}
|
|
755
763
|
]
|
|
756
764
|
</script>
|
|
@@ -834,6 +842,7 @@ types/
|
|
|
834
842
|
label: 'My Type',
|
|
835
843
|
icon: 'i-lucide-my-icon',
|
|
836
844
|
defaultValue: 'default',
|
|
845
|
+
baseType: 'string', // Required: specify base type for conversions
|
|
837
846
|
detect: (value) => /* detection logic */
|
|
838
847
|
}
|
|
839
848
|
```
|
|
@@ -1169,6 +1178,320 @@ Requires:
|
|
|
1169
1178
|
- reka-ui (via Nuxt UI)
|
|
1170
1179
|
- tailwindcss (via Nuxt)
|
|
1171
1180
|
|
|
1181
|
+
## Advanced Examples
|
|
1182
|
+
|
|
1183
|
+
### Custom String Array Type
|
|
1184
|
+
|
|
1185
|
+
Here's an example of a custom string array type with autocomplete suggestions:
|
|
1186
|
+
|
|
1187
|
+
```vue
|
|
1188
|
+
<script setup lang="ts">
|
|
1189
|
+
import type { YamlFieldType } from '@type32/yaml-editor-form'
|
|
1190
|
+
|
|
1191
|
+
const customTypes: YamlFieldType[] = [
|
|
1192
|
+
{
|
|
1193
|
+
type: 'skills',
|
|
1194
|
+
label: 'Skills',
|
|
1195
|
+
icon: 'i-lucide-sparkles',
|
|
1196
|
+
defaultValue: [],
|
|
1197
|
+
baseType: 'string-array', // Inherits array conversions
|
|
1198
|
+
component: 'skills',
|
|
1199
|
+
detect: (value) => {
|
|
1200
|
+
// Auto-detect arrays with skill-like strings
|
|
1201
|
+
return Array.isArray(value) &&
|
|
1202
|
+
value.length > 0 &&
|
|
1203
|
+
value.every(v => typeof v === 'string' && v.length < 30)
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
]
|
|
1207
|
+
|
|
1208
|
+
const profile = ref({
|
|
1209
|
+
name: 'John Doe',
|
|
1210
|
+
skills: ['Vue.js', 'TypeScript', 'Nuxt']
|
|
1211
|
+
})
|
|
1212
|
+
|
|
1213
|
+
// Predefined skill suggestions
|
|
1214
|
+
const skillSuggestions = [
|
|
1215
|
+
'Vue.js', 'React', 'Angular', 'TypeScript', 'JavaScript',
|
|
1216
|
+
'Node.js', 'Python', 'Nuxt', 'Next.js', 'Tailwind CSS'
|
|
1217
|
+
]
|
|
1218
|
+
</script>
|
|
1219
|
+
|
|
1220
|
+
<template>
|
|
1221
|
+
<YamlFormEditor v-model="profile" :field-types="customTypes">
|
|
1222
|
+
<!-- Custom skills input with autocomplete -->
|
|
1223
|
+
<template #field-skills="{ modelValue, readonly, updateModelValue }">
|
|
1224
|
+
<div class="space-y-2">
|
|
1225
|
+
<!-- Display current skills as badges -->
|
|
1226
|
+
<div class="flex flex-wrap gap-2">
|
|
1227
|
+
<UBadge
|
|
1228
|
+
v-for="(skill, index) in (modelValue as string[])"
|
|
1229
|
+
:key="index"
|
|
1230
|
+
color="primary"
|
|
1231
|
+
variant="soft"
|
|
1232
|
+
>
|
|
1233
|
+
{{ skill }}
|
|
1234
|
+
<UButton
|
|
1235
|
+
v-if="!readonly"
|
|
1236
|
+
icon="i-lucide-x"
|
|
1237
|
+
size="2xs"
|
|
1238
|
+
variant="ghost"
|
|
1239
|
+
:padded="false"
|
|
1240
|
+
@click="updateModelValue((modelValue as string[]).filter((_, i) => i !== index))"
|
|
1241
|
+
/>
|
|
1242
|
+
</UBadge>
|
|
1243
|
+
</div>
|
|
1244
|
+
|
|
1245
|
+
<!-- Add new skill with autocomplete -->
|
|
1246
|
+
<UInputMenu
|
|
1247
|
+
v-if="!readonly"
|
|
1248
|
+
:options="skillSuggestions"
|
|
1249
|
+
placeholder="Add skill..."
|
|
1250
|
+
@update:model-value="(newSkill: string) => {
|
|
1251
|
+
if (newSkill && !(modelValue as string[]).includes(newSkill)) {
|
|
1252
|
+
updateModelValue([...(modelValue as string[]), newSkill])
|
|
1253
|
+
}
|
|
1254
|
+
}"
|
|
1255
|
+
/>
|
|
1256
|
+
</div>
|
|
1257
|
+
</template>
|
|
1258
|
+
</YamlFormEditor>
|
|
1259
|
+
</template>
|
|
1260
|
+
```
|
|
1261
|
+
|
|
1262
|
+
### Custom Object Array Type
|
|
1263
|
+
|
|
1264
|
+
Here's an example of a custom object array type with card-based rendering:
|
|
1265
|
+
|
|
1266
|
+
```vue
|
|
1267
|
+
<script setup lang="ts">
|
|
1268
|
+
import type { YamlFieldType } from '@type32/yaml-editor-form'
|
|
1269
|
+
|
|
1270
|
+
const customTypes: YamlFieldType[] = [
|
|
1271
|
+
{
|
|
1272
|
+
type: 'contacts',
|
|
1273
|
+
label: 'Contacts',
|
|
1274
|
+
icon: 'i-lucide-users',
|
|
1275
|
+
defaultValue: [],
|
|
1276
|
+
baseType: 'array', // Inherits array conversions
|
|
1277
|
+
component: 'contacts',
|
|
1278
|
+
detect: (value) => {
|
|
1279
|
+
// Auto-detect arrays of contact-like objects
|
|
1280
|
+
return Array.isArray(value) &&
|
|
1281
|
+
value.length > 0 &&
|
|
1282
|
+
value.every(v =>
|
|
1283
|
+
v && typeof v === 'object' &&
|
|
1284
|
+
('name' in v || 'email' in v)
|
|
1285
|
+
)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
]
|
|
1289
|
+
|
|
1290
|
+
const data = ref({
|
|
1291
|
+
projectName: 'My Project',
|
|
1292
|
+
contacts: [
|
|
1293
|
+
{ name: 'Alice Johnson', email: 'alice@example.com', role: 'Designer' },
|
|
1294
|
+
{ name: 'Bob Smith', email: 'bob@example.com', role: 'Developer' }
|
|
1295
|
+
]
|
|
1296
|
+
})
|
|
1297
|
+
</script>
|
|
1298
|
+
|
|
1299
|
+
<template>
|
|
1300
|
+
<YamlFormEditor v-model="data" :field-types="customTypes">
|
|
1301
|
+
<!-- Custom contacts list with card UI -->
|
|
1302
|
+
<template #field-contacts="{ modelValue, readonly, updateModelValue }">
|
|
1303
|
+
<div class="space-y-3">
|
|
1304
|
+
<!-- Contact cards -->
|
|
1305
|
+
<UCard
|
|
1306
|
+
v-for="(contact, index) in (modelValue as any[])"
|
|
1307
|
+
:key="index"
|
|
1308
|
+
:ui="{ body: { padding: 'p-4' } }"
|
|
1309
|
+
>
|
|
1310
|
+
<div class="flex items-start justify-between gap-3">
|
|
1311
|
+
<div class="flex-1 space-y-2">
|
|
1312
|
+
<!-- Name -->
|
|
1313
|
+
<UInput
|
|
1314
|
+
:model-value="contact.name"
|
|
1315
|
+
placeholder="Name"
|
|
1316
|
+
:disabled="readonly"
|
|
1317
|
+
@update:model-value="(val: string) => {
|
|
1318
|
+
const updated = [...(modelValue as any[])]
|
|
1319
|
+
updated[index] = { ...contact, name: val }
|
|
1320
|
+
updateModelValue(updated)
|
|
1321
|
+
}"
|
|
1322
|
+
/>
|
|
1323
|
+
|
|
1324
|
+
<!-- Email -->
|
|
1325
|
+
<UInput
|
|
1326
|
+
:model-value="contact.email"
|
|
1327
|
+
type="email"
|
|
1328
|
+
placeholder="Email"
|
|
1329
|
+
icon="i-lucide-mail"
|
|
1330
|
+
:disabled="readonly"
|
|
1331
|
+
@update:model-value="(val: string) => {
|
|
1332
|
+
const updated = [...(modelValue as any[])]
|
|
1333
|
+
updated[index] = { ...contact, email: val }
|
|
1334
|
+
updateModelValue(updated)
|
|
1335
|
+
}"
|
|
1336
|
+
/>
|
|
1337
|
+
|
|
1338
|
+
<!-- Role -->
|
|
1339
|
+
<UInput
|
|
1340
|
+
:model-value="contact.role"
|
|
1341
|
+
placeholder="Role"
|
|
1342
|
+
icon="i-lucide-briefcase"
|
|
1343
|
+
:disabled="readonly"
|
|
1344
|
+
@update:model-value="(val: string) => {
|
|
1345
|
+
const updated = [...(modelValue as any[])]
|
|
1346
|
+
updated[index] = { ...contact, role: val }
|
|
1347
|
+
updateModelValue(updated)
|
|
1348
|
+
}"
|
|
1349
|
+
/>
|
|
1350
|
+
</div>
|
|
1351
|
+
|
|
1352
|
+
<!-- Remove button -->
|
|
1353
|
+
<UButton
|
|
1354
|
+
v-if="!readonly"
|
|
1355
|
+
icon="i-lucide-trash-2"
|
|
1356
|
+
color="red"
|
|
1357
|
+
variant="ghost"
|
|
1358
|
+
size="sm"
|
|
1359
|
+
@click="updateModelValue((modelValue as any[]).filter((_, i) => i !== index))"
|
|
1360
|
+
/>
|
|
1361
|
+
</div>
|
|
1362
|
+
</UCard>
|
|
1363
|
+
|
|
1364
|
+
<!-- Add contact button -->
|
|
1365
|
+
<UButton
|
|
1366
|
+
v-if="!readonly"
|
|
1367
|
+
icon="i-lucide-plus"
|
|
1368
|
+
label="Add Contact"
|
|
1369
|
+
variant="outline"
|
|
1370
|
+
block
|
|
1371
|
+
@click="updateModelValue([
|
|
1372
|
+
...(modelValue as any[]),
|
|
1373
|
+
{ name: '', email: '', role: '' }
|
|
1374
|
+
])"
|
|
1375
|
+
/>
|
|
1376
|
+
</div>
|
|
1377
|
+
</template>
|
|
1378
|
+
</YamlFormEditor>
|
|
1379
|
+
</template>
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
### Combined Example
|
|
1383
|
+
|
|
1384
|
+
You can use both custom array types together:
|
|
1385
|
+
|
|
1386
|
+
```vue
|
|
1387
|
+
<script setup lang="ts">
|
|
1388
|
+
import type { YamlFieldType } from '@type32/yaml-editor-form'
|
|
1389
|
+
|
|
1390
|
+
const customTypes: YamlFieldType[] = [
|
|
1391
|
+
// Custom string array
|
|
1392
|
+
{
|
|
1393
|
+
type: 'tags',
|
|
1394
|
+
label: 'Tags',
|
|
1395
|
+
icon: 'i-lucide-tag',
|
|
1396
|
+
defaultValue: [],
|
|
1397
|
+
baseType: 'string-array',
|
|
1398
|
+
component: 'tags',
|
|
1399
|
+
detect: (v) => Array.isArray(v) && v.every(i => typeof i === 'string')
|
|
1400
|
+
},
|
|
1401
|
+
// Custom object array
|
|
1402
|
+
{
|
|
1403
|
+
type: 'team',
|
|
1404
|
+
label: 'Team Members',
|
|
1405
|
+
icon: 'i-lucide-users',
|
|
1406
|
+
defaultValue: [],
|
|
1407
|
+
baseType: 'array',
|
|
1408
|
+
component: 'team',
|
|
1409
|
+
detect: (v) => Array.isArray(v) && v.every(i => i?.name || i?.email)
|
|
1410
|
+
}
|
|
1411
|
+
]
|
|
1412
|
+
|
|
1413
|
+
const project = ref({
|
|
1414
|
+
name: 'Website Redesign',
|
|
1415
|
+
tags: ['frontend', 'design', 'urgent'],
|
|
1416
|
+
team: [
|
|
1417
|
+
{ name: 'Alice', email: 'alice@example.com' },
|
|
1418
|
+
{ name: 'Bob', email: 'bob@example.com' }
|
|
1419
|
+
]
|
|
1420
|
+
})
|
|
1421
|
+
</script>
|
|
1422
|
+
|
|
1423
|
+
<template>
|
|
1424
|
+
<YamlFormEditor v-model="project" :field-types="customTypes">
|
|
1425
|
+
<!-- String array implementation -->
|
|
1426
|
+
<template #field-tags="{ modelValue, readonly, updateModelValue }">
|
|
1427
|
+
<UInputTags
|
|
1428
|
+
:model-value="modelValue as string[]"
|
|
1429
|
+
:disabled="readonly"
|
|
1430
|
+
placeholder="Add tags..."
|
|
1431
|
+
@update:model-value="updateModelValue"
|
|
1432
|
+
/>
|
|
1433
|
+
</template>
|
|
1434
|
+
|
|
1435
|
+
<!-- Object array implementation -->
|
|
1436
|
+
<template #field-team="{ modelValue, readonly, updateModelValue }">
|
|
1437
|
+
<!-- Your custom team member UI here -->
|
|
1438
|
+
<div class="space-y-2">
|
|
1439
|
+
<div
|
|
1440
|
+
v-for="(member, idx) in (modelValue as any[])"
|
|
1441
|
+
:key="idx"
|
|
1442
|
+
class="flex gap-2"
|
|
1443
|
+
>
|
|
1444
|
+
<UInput
|
|
1445
|
+
:model-value="member.name"
|
|
1446
|
+
placeholder="Name"
|
|
1447
|
+
:disabled="readonly"
|
|
1448
|
+
@update:model-value="(val: string) => {
|
|
1449
|
+
const updated = [...(modelValue as any[])]
|
|
1450
|
+
updated[idx] = { ...member, name: val }
|
|
1451
|
+
updateModelValue(updated)
|
|
1452
|
+
}"
|
|
1453
|
+
/>
|
|
1454
|
+
<UButton
|
|
1455
|
+
v-if="!readonly"
|
|
1456
|
+
icon="i-lucide-x"
|
|
1457
|
+
color="red"
|
|
1458
|
+
variant="ghost"
|
|
1459
|
+
@click="updateModelValue((modelValue as any[]).filter((_, i) => i !== idx))"
|
|
1460
|
+
/>
|
|
1461
|
+
</div>
|
|
1462
|
+
<UButton
|
|
1463
|
+
v-if="!readonly"
|
|
1464
|
+
icon="i-lucide-plus"
|
|
1465
|
+
label="Add Member"
|
|
1466
|
+
size="sm"
|
|
1467
|
+
@click="updateModelValue([...(modelValue as any[]), { name: '', email: '' }])"
|
|
1468
|
+
/>
|
|
1469
|
+
</div>
|
|
1470
|
+
</template>
|
|
1471
|
+
</YamlFormEditor>
|
|
1472
|
+
</template>
|
|
1473
|
+
```
|
|
1474
|
+
|
|
1475
|
+
### Key Patterns for Array Types
|
|
1476
|
+
|
|
1477
|
+
**String Arrays (`baseType: 'string-array'`):**
|
|
1478
|
+
- Use for specialized tag inputs, category lists, etc.
|
|
1479
|
+
- Can convert to/from regular arrays and strings
|
|
1480
|
+
- Good for: skills, tags, categories, keywords
|
|
1481
|
+
|
|
1482
|
+
**Object Arrays (`baseType: 'array'`):**
|
|
1483
|
+
- Use for collections with structured data
|
|
1484
|
+
- Provide custom UI for adding/editing/removing items
|
|
1485
|
+
- Good for: contacts, team members, products, events
|
|
1486
|
+
|
|
1487
|
+
**Important Notes:**
|
|
1488
|
+
1. **Type Assertions**: Use `(modelValue as string[])` or `(modelValue as any[])` for type safety
|
|
1489
|
+
2. **Immutability**: Always create new arrays when updating (spread operator `[...]`)
|
|
1490
|
+
3. **Index Management**: Track items by index for updates/deletions
|
|
1491
|
+
4. **Add Operations**: Spread existing array and add new items
|
|
1492
|
+
5. **Remove Operations**: Use `filter()` to remove by index
|
|
1493
|
+
6. **Update Operations**: Clone array, modify specific index, update entire array
|
|
1494
|
+
|
|
1172
1495
|
## License
|
|
1173
1496
|
|
|
1174
1497
|
This component is part of the Vertex project.
|
package/dist/module.json
CHANGED