@shwfed/nuxt 0.11.7 → 0.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
  export { FieldsInstance } from '../dist/runtime/components/fields-instance.js';
3
+ export { FieldsSlotProps } from '../dist/runtime/components/ui/fields/slot-props.js';
3
4
 
4
5
  type Wrap<T> = T extends object ? Readonly<{
5
6
  [P in keyof T]?: Wrap<T[P]>;
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
3
  "configKey": "shwfed",
4
- "version": "0.11.7",
4
+ "version": "0.11.8",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -1,8 +1,10 @@
1
1
  import { Effect } from 'effect';
2
2
  import { type FieldsConfigInput } from './ui/fields/Fields.vue.js';
3
+ import type { FieldsSlotProps } from './ui/fields/slot-props.js';
3
4
  export { CalendarFieldC, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, NumberFieldC, SelectFieldC, SlotFieldC, StringFieldC, CURRENT_COMPATIBILITY_DATE, KIND, SUPPORTED_COMPATIBILITY_DATES, createFieldsConfig, } from './ui/fields/Fields.vue.js';
4
5
  export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, } from './ui/fields/Fields.vue.js';
5
6
  export type { FieldsInstance } from './fields-instance.js';
7
+ export type { FieldsSlotProps } from './ui/fields/slot-props.js';
6
8
  declare const _default: typeof __VLS_export;
7
9
  export default _default;
8
10
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -259,11 +261,7 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
259
261
  style?: string | undefined;
260
262
  }>) => any) | undefined;
261
263
  "onInitial-value-ready"?: (() => any) | undefined;
262
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
263
- [x: string]: ((props: {
264
- style: import("vue").CSSProperties;
265
- }) => any) | undefined;
266
- }>;
264
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, Record<string, (_props?: FieldsSlotProps) => unknown>>;
267
265
  type __VLS_WithSlots<T, S> = T & {
268
266
  new (): {
269
267
  $slots: S;
@@ -11,6 +11,7 @@ const props = defineProps({
11
11
  config: { type: null, required: false }
12
12
  });
13
13
  const emit = defineEmits(["update:config", "initial-value-ready"]);
14
+ const slots = defineSlots();
14
15
  const modelValue = defineModel("modelValue", { type: Object, ...{
15
16
  default: () => ({})
16
17
  } });
@@ -83,7 +84,7 @@ export {
83
84
  @initial-value-ready="handleInitialValueReady"
84
85
  >
85
86
  <template
86
- v-for="(_, slotName) in $slots"
87
+ v-for="(_, slotName) in slots"
87
88
  #[slotName]="slotProps"
88
89
  >
89
90
  <slot
@@ -1,8 +1,10 @@
1
1
  import { Effect } from 'effect';
2
2
  import { type FieldsConfigInput } from './ui/fields/Fields.vue.js';
3
+ import type { FieldsSlotProps } from './ui/fields/slot-props.js';
3
4
  export { CalendarFieldC, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, NumberFieldC, SelectFieldC, SlotFieldC, StringFieldC, CURRENT_COMPATIBILITY_DATE, KIND, SUPPORTED_COMPATIBILITY_DATES, createFieldsConfig, } from './ui/fields/Fields.vue.js';
4
5
  export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, } from './ui/fields/Fields.vue.js';
5
6
  export type { FieldsInstance } from './fields-instance.js';
7
+ export type { FieldsSlotProps } from './ui/fields/slot-props.js';
6
8
  declare const _default: typeof __VLS_export;
7
9
  export default _default;
8
10
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -259,11 +261,7 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
259
261
  style?: string | undefined;
260
262
  }>) => any) | undefined;
261
263
  "onInitial-value-ready"?: (() => any) | undefined;
262
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
263
- [x: string]: ((props: {
264
- style: import("vue").CSSProperties;
265
- }) => any) | undefined;
266
- }>;
264
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, Record<string, (_props?: FieldsSlotProps) => unknown>>;
267
265
  type __VLS_WithSlots<T, S> = T & {
268
266
  new (): {
269
267
  $slots: S;
@@ -234,6 +234,9 @@ function normalizeOptionalString(value) {
234
234
  function getGeneralErrorKey(field) {
235
235
  return `general.${field}`;
236
236
  }
237
+ function getNodeErrorKey(itemId, field) {
238
+ return `${itemId}.${field}`;
239
+ }
237
240
  function clearError(key) {
238
241
  const nextErrors = {};
239
242
  for (const [errorKey, errorValue] of Object.entries(validationErrors.value)) {
@@ -243,6 +246,18 @@ function clearError(key) {
243
246
  }
244
247
  validationErrors.value = nextErrors;
245
248
  }
249
+ function clearNodeError(itemId, field) {
250
+ clearError(getNodeErrorKey(itemId, field));
251
+ }
252
+ function clearNodeErrors(itemId) {
253
+ const nextErrors = {};
254
+ for (const [errorKey, errorValue] of Object.entries(validationErrors.value)) {
255
+ if (!errorKey.startsWith(`${itemId}.`)) {
256
+ Reflect.set(nextErrors, errorKey, errorValue);
257
+ }
258
+ }
259
+ validationErrors.value = nextErrors;
260
+ }
246
261
  function isCopyableNode(node) {
247
262
  if (!node) {
248
263
  return false;
@@ -300,6 +315,7 @@ function addDropdown() {
300
315
  }
301
316
  function deleteItem(itemId) {
302
317
  draftTree.value = removeButtonConfiguratorSubtree(draftTree.value, itemId);
318
+ clearNodeErrors(itemId);
303
319
  if (selectedItemId.value === itemId) {
304
320
  selectedItemId.value = "general";
305
321
  }
@@ -329,6 +345,22 @@ function updateSelectedTitle(value) {
329
345
  };
330
346
  });
331
347
  }
348
+ function updateSelectedNodeId(value) {
349
+ const node = selectedNode.value;
350
+ if (!node || !isCopyableNode(node)) {
351
+ return;
352
+ }
353
+ clearNodeError(node.itemId, "id");
354
+ draftTree.value = updateButtonConfiguratorNode(draftTree.value, node.itemId, (item) => {
355
+ if (item.type === "group") {
356
+ return item;
357
+ }
358
+ return {
359
+ ...item,
360
+ id: String(value).trim()
361
+ };
362
+ });
363
+ }
332
364
  function updateSelectedTooltip(value) {
333
365
  const node = selectedNode.value;
334
366
  if (!node || node.item.type !== "button") {
@@ -486,6 +518,41 @@ function showImportErrorWithCopyAction(message, onClick) {
486
518
  }
487
519
  });
488
520
  }
521
+ function validateEditableNodeIds() {
522
+ const errors = {};
523
+ const idOwners = {};
524
+ let firstInvalidItemId;
525
+ for (const node of draftTree.value.nodes) {
526
+ if (!isCopyableNode(node)) {
527
+ continue;
528
+ }
529
+ const idResult = z.uuid().safeParse(node.item.id);
530
+ if (!idResult.success) {
531
+ errors[getNodeErrorKey(node.itemId, "id")] = t("button-id-invalid");
532
+ firstInvalidItemId = firstInvalidItemId ?? node.itemId;
533
+ continue;
534
+ }
535
+ const existingIdOwner = idOwners[node.item.id];
536
+ if (existingIdOwner !== void 0) {
537
+ errors[getNodeErrorKey(node.itemId, "id")] = t("button-id-duplicate");
538
+ errors[getNodeErrorKey(existingIdOwner, "id")] = t("button-id-duplicate");
539
+ firstInvalidItemId = firstInvalidItemId ?? node.itemId;
540
+ continue;
541
+ }
542
+ idOwners[node.item.id] = node.itemId;
543
+ }
544
+ if (Object.keys(errors).length === 0) {
545
+ return true;
546
+ }
547
+ validationErrors.value = {
548
+ ...validationErrors.value,
549
+ ...errors
550
+ };
551
+ if (firstInvalidItemId) {
552
+ selectedItemId.value = firstInvalidItemId;
553
+ }
554
+ return false;
555
+ }
489
556
  function buildDraftConfig() {
490
557
  const generalStyleResult = ButtonsStyleC.safeParse(draftStyle.value);
491
558
  if (!generalStyleResult.success) {
@@ -496,6 +563,9 @@ function buildDraftConfig() {
496
563
  selectedItemId.value = "general";
497
564
  return;
498
565
  }
566
+ if (!validateEditableNodeIds()) {
567
+ return;
568
+ }
499
569
  const bodyResult = ButtonBodyC.safeParse({
500
570
  gap: draftGap.value,
501
571
  style: generalStyleResult.data,
@@ -535,7 +605,8 @@ function buildAiPromptHeaderMarkdown() {
535
605
  function buildMarkdownNotes() {
536
606
  return [
537
607
  "## \u6CE8\u610F\u4E8B\u9879",
538
- "- \u6240\u6709 `group.id`\u3001\u6309\u94AE `id`\u3001\u4E0B\u62C9\u6309\u94AE `id` \u90FD\u5FC5\u987B\u4FDD\u7559\u73B0\u6709 UUID\uFF0C\u4E0D\u8981\u751F\u6210\u65B0\u7684 ID \u66FF\u6362\u5DF2\u6709\u9879\u3002",
608
+ "- \u5148\u5728\u4EE3\u7801\u91CC\u751F\u6210\u5E76\u4F7F\u7528 UUID\uFF0C\u518D\u628A\u540C\u4E00\u4E2A `id` \u7C98\u8D34\u5230\u6309\u94AE\u914D\u7F6E\u4E2D\u3002",
609
+ "- \u6309\u94AE\u548C\u4E0B\u62C9\u6309\u94AE\u7684 `id` \u90FD\u5FC5\u987B\u552F\u4E00\u4E14\u7B26\u5408 UUID \u683C\u5F0F\u3002",
539
610
  "- `groups` \u662F\u6839\u7EA7\u6309\u94AE\u7EC4\u5217\u8868\uFF1B\u4E0B\u62C9\u6309\u94AE\u53EA\u80FD\u5B58\u5728\u4E8E\u6309\u94AE\u7EC4\u5185\u3002",
540
611
  "- \u4E0B\u62C9\u6309\u94AE\u7684 `items` \u53EA\u80FD\u662F\u6309\u94AE\u5217\u8868\uFF0C\u4E0D\u80FD\u518D\u5D4C\u5957\u4E0B\u62C9\u6309\u94AE\u6216\u6309\u94AE\u7EC4\u3002",
541
612
  "- `style` \u5FC5\u987B\u662F\u8FD4\u56DE\u6837\u5F0F\u5BF9\u8C61\u7684 CEL \u8868\u8FBE\u5F0F\u5B57\u7B26\u4E32\uFF1B\u5982\u679C\u4E0D\u80FD\u786E\u5B9A\u4E0A\u4E0B\u6587\u53D8\u91CF\uFF0C\u8BF7\u5148\u8BF4\u660E\u9650\u5236\u3002"
@@ -623,7 +694,7 @@ function buildPasteConfigErrorMarkdown(source, error) {
623
694
  "",
624
695
  "## \u5F53\u524D\u4EFB\u52A1",
625
696
  "\u7528\u6237\u628A\u4E00\u6BB5\u914D\u7F6E\u7C98\u8D34\u56DE\u6309\u94AE\u914D\u7F6E\u5668\u65F6\u5931\u8D25\u4E86\u3002\u8BF7\u57FA\u4E8E\u4E0B\u9762\u7684\u539F\u59CB\u5185\u5BB9\u548C\u62A5\u9519\u4FEE\u590D\u5F53\u524D\u914D\u7F6E\u3002",
626
- "\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u91CD\u5EFA\u73B0\u6709\u9879\uFF0C\u4E5F\u4E0D\u8981\u66FF\u73B0\u6709\u9879\u751F\u6210\u65B0\u7684 UUID\u3002",
697
+ "\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\u3002\u4EE3\u7801\u91CC\u7684 UUID \u5E94\u89C6\u4E3A\u6765\u6E90\uFF0C\u4E0D\u8981\u65E0\u610F\u4E49\u5730\u91CD\u5EFA\u73B0\u6709\u9879\u3002",
627
698
  "\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002",
628
699
  "",
629
700
  buildPasteConfigErrorDetails(source, error),
@@ -830,6 +901,14 @@ function confirmChanges() {
830
901
  {{ selectedItemId === "general" ? t("general-description") : t("detail-description") }}
831
902
  </p>
832
903
 
904
+ <p
905
+ v-if="selectedNode && validationErrors[getNodeErrorKey(selectedNode.itemId, 'id')]"
906
+ data-slot="button-configurator-id-error"
907
+ class="mt-4 text-xs text-red-500"
908
+ >
909
+ {{ validationErrors[getNodeErrorKey(selectedItemId, "id")] }}
910
+ </p>
911
+
833
912
  <div class="mt-6 flex flex-wrap gap-2">
834
913
  <Button
835
914
  v-if="canAddGroup"
@@ -912,6 +991,18 @@ function confirmChanges() {
912
991
  data-slot="button-configurator-button"
913
992
  class="mt-6 grid gap-4"
914
993
  >
994
+ <label class="flex flex-col gap-2">
995
+ <span class="text-xs font-medium text-zinc-500">{{ t("button-id") }}</span>
996
+ <Input
997
+ data-slot="button-configurator-button-id"
998
+ :model-value="selectedButton.id"
999
+ :aria-invalid="validationErrors[getNodeErrorKey(selectedItemId, 'id')] ? 'true' : void 0"
1000
+ :placeholder="t('button-id-placeholder')"
1001
+ class="font-mono text-sm"
1002
+ @update:model-value="updateSelectedNodeId"
1003
+ />
1004
+ </label>
1005
+
915
1006
  <label class="flex flex-col gap-2">
916
1007
  <span class="text-xs font-medium text-zinc-500">{{ t("button-title") }}</span>
917
1008
  <Locale
@@ -999,6 +1090,18 @@ function confirmChanges() {
999
1090
  data-slot="button-configurator-dropdown"
1000
1091
  class="mt-6 grid gap-4"
1001
1092
  >
1093
+ <label class="flex flex-col gap-2">
1094
+ <span class="text-xs font-medium text-zinc-500">{{ t("dropdown-id") }}</span>
1095
+ <Input
1096
+ data-slot="button-configurator-dropdown-id"
1097
+ :model-value="selectedDropdown.id"
1098
+ :aria-invalid="validationErrors[getNodeErrorKey(selectedItemId, 'id')] ? 'true' : void 0"
1099
+ :placeholder="t('button-id-placeholder')"
1100
+ class="font-mono text-sm"
1101
+ @update:model-value="updateSelectedNodeId"
1102
+ />
1103
+ </label>
1104
+
1002
1105
  <label class="flex flex-col gap-2">
1003
1106
  <span class="text-xs font-medium text-zinc-500">{{ t("dropdown-title") }}</span>
1004
1107
  <Locale
@@ -1155,7 +1258,7 @@ function confirmChanges() {
1155
1258
  "copy-config-failed": "复制配置失败,请先修正当前配置中的错误。",
1156
1259
  "general": "总览",
1157
1260
  "general-description": "在这里管理按钮组和按钮组之间的间距。",
1158
- "detail-description": "编辑当前选中节点,或调整它的子项顺序。",
1261
+ "detail-description": "先在代码里准备好 UUID / Effect,再把 UUID 粘贴到这里继续配置。",
1159
1262
  "search": "搜索按钮",
1160
1263
  "no-matches": "没有匹配的按钮。",
1161
1264
  "group-label": "按钮组",
@@ -1171,6 +1274,11 @@ function confirmChanges() {
1171
1274
  "general-style": "通用样式表达式",
1172
1275
  "general-style-placeholder": "例如返回一个 style map,例如 display: flex",
1173
1276
  "general-style-invalid": "样式表达式无效",
1277
+ "button-id": "按钮 ID",
1278
+ "dropdown-id": "下拉 ID",
1279
+ "button-id-placeholder": "粘贴代码里已经生成的 UUID",
1280
+ "button-id-invalid": "按钮 ID 必须是合法的 UUID",
1281
+ "button-id-duplicate": "按钮 ID 不能重复",
1174
1282
  "button-title": "按钮名称",
1175
1283
  "tooltip": "提示",
1176
1284
  "button-icon": "按钮图标",
@@ -1207,7 +1315,7 @@ function confirmChanges() {
1207
1315
  "copy-config-failed": "Failed to copy the config. Fix the current config errors first.",
1208
1316
  "general": "General",
1209
1317
  "general-description": "Manage button groups and the gap between them.",
1210
- "detail-description": "Edit the selected node and reorder its children.",
1318
+ "detail-description": "Prepare the UUID or effect in code first, then paste the UUID here to continue configuring.",
1211
1319
  "search": "Search buttons",
1212
1320
  "no-matches": "No matching buttons.",
1213
1321
  "group-label": "Group",
@@ -1223,6 +1331,11 @@ function confirmChanges() {
1223
1331
  "general-style": "Shared style expression",
1224
1332
  "general-style-placeholder": "Return a style map, for example display: flex",
1225
1333
  "general-style-invalid": "The style expression is invalid",
1334
+ "button-id": "Button ID",
1335
+ "dropdown-id": "Dropdown ID",
1336
+ "button-id-placeholder": "Paste the UUID generated in code",
1337
+ "button-id-invalid": "Button ID must be a valid UUID",
1338
+ "button-id-duplicate": "Button ID must be unique",
1226
1339
  "button-title": "Button title",
1227
1340
  "tooltip": "Tooltip",
1228
1341
  "button-icon": "Button icon",
@@ -1259,7 +1372,7 @@ function confirmChanges() {
1259
1372
  "copy-config-failed": "設定のコピーに失敗しました。現在の設定エラーを先に修正してください。",
1260
1373
  "general": "全体",
1261
1374
  "general-description": "ボタングループとその間隔を管理します。",
1262
- "detail-description": "選択中ノードを編集し、子項目を並び替えます。",
1375
+ "detail-description": "先にコード側で UUID / Effect を準備し、その UUID をここへ貼り付けて設定を続けます。",
1263
1376
  "search": "ボタンを検索",
1264
1377
  "no-matches": "一致するボタンがありません。",
1265
1378
  "group-label": "グループ",
@@ -1275,6 +1388,11 @@ function confirmChanges() {
1275
1388
  "general-style": "共通スタイル式",
1276
1389
  "general-style-placeholder": "例: style map を返す式。例: display: flex",
1277
1390
  "general-style-invalid": "スタイル式が無効です",
1391
+ "button-id": "ボタン ID",
1392
+ "dropdown-id": "ドロップダウン ID",
1393
+ "button-id-placeholder": "コードで生成した UUID を貼り付け",
1394
+ "button-id-invalid": "ボタン ID は有効な UUID である必要があります",
1395
+ "button-id-duplicate": "ボタン ID は重複できません",
1278
1396
  "button-title": "ボタン名",
1279
1397
  "tooltip": "ツールチップ",
1280
1398
  "button-icon": "ボタンアイコン",
@@ -1311,7 +1429,7 @@ function confirmChanges() {
1311
1429
  "copy-config-failed": "설정 복사에 실패했습니다. 먼저 현재 설정 오류를 수정해 주세요.",
1312
1430
  "general": "전체",
1313
1431
  "general-description": "버튼 그룹과 그룹 간 간격을 관리합니다.",
1314
- "detail-description": "선택한 노드를 편집하고 하위 항목 순서를 조정합니다.",
1432
+ "detail-description": "먼저 코드에서 UUID / Effect 준비한 뒤, 같은 UUID 를 여기 붙여 넣어 설정을 이어갑니다.",
1315
1433
  "search": "버튼 검색",
1316
1434
  "no-matches": "일치하는 버튼이 없습니다.",
1317
1435
  "group-label": "그룹",
@@ -1327,6 +1445,11 @@ function confirmChanges() {
1327
1445
  "general-style": "공통 스타일 식",
1328
1446
  "general-style-placeholder": "예: style map 을 반환하는 식. 예: display: flex",
1329
1447
  "general-style-invalid": "스타일 식이 올바르지 않습니다",
1448
+ "button-id": "버튼 ID",
1449
+ "dropdown-id": "드롭다운 ID",
1450
+ "button-id-placeholder": "코드에서 생성한 UUID 붙여 넣기",
1451
+ "button-id-invalid": "버튼 ID 는 올바른 UUID 여야 합니다",
1452
+ "button-id-duplicate": "버튼 ID 는 중복될 수 없습니다",
1330
1453
  "button-title": "버튼 이름",
1331
1454
  "tooltip": "툴팁",
1332
1455
  "button-icon": "버튼 아이콘",
@@ -3,6 +3,7 @@ import type { CSSProperties } from 'vue';
3
3
  export { CalendarFieldC, CURRENT_COMPATIBILITY_DATE, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, KIND, NumberFieldC, SelectFieldC, SlotFieldC, SUPPORTED_COMPATIBILITY_DATES, StringFieldC, ValidationRuleC, createFieldsConfig, validationC, } from './schema.js';
4
4
  export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, ValidationRule, } from './schema.js';
5
5
  export type { FieldsInstance } from '../../fields-instance.js';
6
+ export type { FieldsSlotProps } from './slot-props.js';
6
7
  declare const _default: typeof __VLS_export;
7
8
  export default _default;
8
9
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -499,13 +500,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
499
500
  style?: string | undefined;
500
501
  }>) => any) | undefined;
501
502
  "onInitial-value-ready"?: (() => any) | undefined;
502
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
503
- [x: string]: ((props: {
504
- style: CSSProperties;
505
- }) => any) | undefined;
506
- } & {
507
- default?: (props: {}) => any;
508
- }>;
503
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, Record<string, (_props?: Readonly<{
504
+ style: CSSProperties;
505
+ valid: Effect.Effect<boolean, never>;
506
+ form: Readonly<Record<string, unknown>>;
507
+ }>) => unknown>>;
509
508
  type __VLS_WithSlots<T, S> = T & {
510
509
  new (): {
511
510
  $slots: S;
@@ -7,7 +7,7 @@ import { Icon } from "@iconify/vue";
7
7
  import { Effect } from "effect";
8
8
  import { format, parse } from "date-fns";
9
9
  import { deleteProperty, getProperty, hasProperty, setProperty } from "dot-prop";
10
- import { nextTick, ref, toRaw, useId, watch, watchEffect } from "vue";
10
+ import { computed, nextTick, readonly, ref, toRaw, useId, watch, watchEffect } from "vue";
11
11
  import { useI18n } from "vue-i18n";
12
12
  import { useCheating } from "#imports";
13
13
  import { Calendar } from "../calendar";
@@ -28,6 +28,7 @@ const defaultConfig = createFieldsConfig({
28
28
  const props = defineProps({
29
29
  config: { type: null, required: true }
30
30
  });
31
+ defineSlots();
31
32
  const emit = defineEmits(["update:config", "initial-value-ready"]);
32
33
  const config = computedAsync(async () => FieldsConfigC.parse(await props.config.pipe(Effect.runPromise) ?? defaultConfig));
33
34
  const { t, locale } = useI18n();
@@ -318,12 +319,14 @@ function handleConfiguratorConfirm(nextConfig) {
318
319
  displayConfig.value = cloneConfig(nextConfig);
319
320
  emit("update:config", nextConfig);
320
321
  }
322
+ const valid = Effect.async((resume) => {
323
+ void waitForReady().then(() => {
324
+ resume(Effect.sync(() => validateFields()));
325
+ });
326
+ });
327
+ const slotForm = computed(() => readonly(modelValue.value));
321
328
  const fieldsApi = {
322
- valid: Effect.async((resume) => {
323
- void waitForReady().then(() => {
324
- resume(Effect.sync(() => validateFields()));
325
- });
326
- })
329
+ valid
327
330
  };
328
331
  defineExpose(fieldsApi);
329
332
  watch(config, (value) => {
@@ -427,7 +430,9 @@ export {
427
430
  <slot
428
431
  v-if="field.type === 'slot'"
429
432
  :name="field.id"
433
+ :form="slotForm"
430
434
  :style="getFieldStyle(field)"
435
+ :valid="valid"
431
436
  />
432
437
  <div
433
438
  v-else-if="field.type === 'empty'"
@@ -3,6 +3,7 @@ import type { CSSProperties } from 'vue';
3
3
  export { CalendarFieldC, CURRENT_COMPATIBILITY_DATE, EmptyFieldC, FieldC, FieldsBodyC, FieldsBodyInputC, FieldsConfigC, FieldsConfigInputC, KIND, NumberFieldC, SelectFieldC, SlotFieldC, SUPPORTED_COMPATIBILITY_DATES, StringFieldC, ValidationRuleC, createFieldsConfig, validationC, } from './schema.js';
4
4
  export type { EmptyField, Field, FieldsBody, FieldsBodyInput, FieldsConfig, FieldsConfigInput, SlotField, ValidationRule, } from './schema.js';
5
5
  export type { FieldsInstance } from '../../fields-instance.js';
6
+ export type { FieldsSlotProps } from './slot-props.js';
6
7
  declare const _default: typeof __VLS_export;
7
8
  export default _default;
8
9
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
@@ -499,13 +500,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
499
500
  style?: string | undefined;
500
501
  }>) => any) | undefined;
501
502
  "onInitial-value-ready"?: (() => any) | undefined;
502
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
503
- [x: string]: ((props: {
504
- style: CSSProperties;
505
- }) => any) | undefined;
506
- } & {
507
- default?: (props: {}) => any;
508
- }>;
503
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, Record<string, (_props?: Readonly<{
504
+ style: CSSProperties;
505
+ valid: Effect.Effect<boolean, never>;
506
+ form: Readonly<Record<string, unknown>>;
507
+ }>) => unknown>>;
509
508
  type __VLS_WithSlots<T, S> = T & {
510
509
  new (): {
511
510
  $slots: S;
@@ -0,0 +1,7 @@
1
+ import type { Effect } from 'effect';
2
+ import type { CSSProperties } from 'vue';
3
+ export type FieldsSlotProps = Readonly<{
4
+ style: CSSProperties;
5
+ valid: Effect.Effect<boolean, never>;
6
+ form: Readonly<Record<string, unknown>>;
7
+ }>;
@@ -489,6 +489,17 @@ function updateSelectedFieldTitle(value) {
489
489
  title: value
490
490
  }));
491
491
  }
492
+ function updateSelectedFieldId(value) {
493
+ const selected = selectedField.value;
494
+ if (!selected || selected.field.type !== "slot") {
495
+ return;
496
+ }
497
+ clearFieldError(selected.draftId, "id");
498
+ updateDraftField(selected.draftId, (field) => field.type === "slot" ? {
499
+ ...field,
500
+ id: String(value).trim()
501
+ } : field);
502
+ }
492
503
  async function copySelectedFieldId() {
493
504
  const selected = selectedField.value;
494
505
  if (!selected) {
@@ -932,6 +943,12 @@ function getSchemaIssues(field) {
932
943
  }
933
944
  }
934
945
  }
946
+ function getSchemaIssueMessage(issue, path) {
947
+ if (path[0] === "id") {
948
+ return t("field-id-invalid");
949
+ }
950
+ return issue.message;
951
+ }
935
952
  function normalizeIssuePath(path) {
936
953
  const normalizedPath = [];
937
954
  for (const segment of path) {
@@ -987,11 +1004,12 @@ function validateDraftFields() {
987
1004
  firstInvalidItemId = firstInvalidItemId ?? item.draftId;
988
1005
  }
989
1006
  for (const issue of getSchemaIssues(item.field)) {
990
- const issueKey = getIssueErrorKey(item.draftId, normalizeIssuePath(issue.path));
1007
+ const issuePath = normalizeIssuePath(issue.path);
1008
+ const issueKey = getIssueErrorKey(item.draftId, issuePath);
991
1009
  if (!issueKey) {
992
1010
  continue;
993
1011
  }
994
- setError(errors, issueKey, issue.message);
1012
+ setError(errors, issueKey, getSchemaIssueMessage(issue, issuePath));
995
1013
  firstInvalidItemId = firstInvalidItemId ?? item.draftId;
996
1014
  }
997
1015
  }
@@ -1091,7 +1109,8 @@ function buildDslGuideMarkdown() {
1091
1109
  "- `select.options` \u901A\u5E38\u8FD4\u56DE\u5217\u8868\uFF1B`label` / `value` / `key` \u7528\u6765\u63CF\u8FF0\u5355\u4E2A\u9009\u9879\u5982\u4F55\u6620\u5C04\u3002",
1092
1110
  "",
1093
1111
  "### 3. \u5B57\u6BB5\u7C7B\u578B\u7EA6\u675F",
1094
- "- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u5305\u542B\u7A33\u5B9A\u7684 UUID `id`\u3002",
1112
+ "- \u4EE3\u7801\u91CC\u5E94\u5148\u751F\u6210\u5E76\u4F7F\u7528 UUID\uFF0C\u518D\u628A\u540C\u4E00\u4E2A `id` \u7C98\u8D34\u56DE\u5B57\u6BB5\u914D\u7F6E\u3002",
1113
+ "- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u5305\u542B\u552F\u4E00\u4E14\u5408\u6CD5\u7684 UUID `id`\u3002",
1095
1114
  "- \u53EA\u6709\u975E `slot` / `empty` \u5B57\u6BB5\u53EF\u4EE5\u914D\u7F6E `path`\uFF0C\u5E76\u53C2\u4E0E\u8868\u5355\u503C\u8BFB\u5199\u3002",
1096
1115
  "- `slot` \u548C `empty` \u5B57\u6BB5\u90FD\u4E0D\u4F1A\u7ED1\u5B9A\u6A21\u578B\u503C\uFF0C\u53EA\u5141\u8BB8 `id`\u3001`type` \u548C\u53EF\u9009\u7684 `style`\u3002"
1097
1116
  ].join("\n");
@@ -1099,7 +1118,8 @@ function buildDslGuideMarkdown() {
1099
1118
  function buildMarkdownNotes() {
1100
1119
  return [
1101
1120
  "## \u6CE8\u610F\u4E8B\u9879",
1102
- "- \u6240\u6709\u5B57\u6BB5\u90FD\u5FC5\u987B\u4FDD\u7559\u73B0\u6709 `id`\uFF0C\u4E0D\u8981\u751F\u6210\u65B0\u7684 `id` \u66FF\u6362\u5DF2\u6709\u5B57\u6BB5\u3002",
1121
+ "- \u5148\u5728\u4EE3\u7801\u91CC\u751F\u6210\u5E76\u4F7F\u7528 UUID\uFF0C\u518D\u628A\u540C\u4E00\u4E2A `id` \u7C98\u8D34\u5230\u5B57\u6BB5\u914D\u7F6E\u4E2D\u3002",
1122
+ "- \u6240\u6709\u5B57\u6BB5 `id` \u90FD\u5FC5\u987B\u552F\u4E00\u4E14\u7B26\u5408 UUID \u683C\u5F0F\u3002",
1103
1123
  "- `slot` \u4E0E `empty` \u5B57\u6BB5\u53EA\u80FD\u4F7F\u7528 `id`\u3001`type` \u548C\u53EF\u9009\u7684 `style`\u3002",
1104
1124
  "- \u975E `slot` / `empty` \u5B57\u6BB5\u7684 `path` \u5FC5\u987B\u552F\u4E00\u4E14\u4E0D\u80FD\u4E3A\u7A7A\u3002",
1105
1125
  "- \u8868\u8FBE\u5F0F\u5B57\u6BB5\u5FC5\u987B\u4E25\u683C\u9075\u5B88 schema \u7EA6\u675F\uFF1B\u5982\u679C schema \u4E0D\u652F\u6301\uFF0C\u5C31\u76F4\u63A5\u8BF4\u660E\u9650\u5236\u3002"
@@ -1187,7 +1207,7 @@ function buildPasteConfigErrorMarkdown(source, error) {
1187
1207
  "",
1188
1208
  "## \u5F53\u524D\u4EFB\u52A1",
1189
1209
  "\u7528\u6237\u628A\u4E00\u6BB5\u914D\u7F6E\u7C98\u8D34\u56DE\u5B57\u6BB5\u914D\u7F6E\u5668\u65F6\u5931\u8D25\u4E86\u3002\u8BF7\u57FA\u4E8E\u4E0B\u9762\u7684\u539F\u59CB\u5185\u5BB9\u548C\u62A5\u9519\u4FEE\u590D\u5F53\u524D\u914D\u7F6E\u3002",
1190
- "\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u53D1\u660E schema \u4E2D\u4E0D\u5B58\u5728\u7684\u5B57\u6BB5\uFF0C\u4E5F\u4E0D\u8981\u66FF\u73B0\u6709\u5B57\u6BB5\u751F\u6210\u65B0\u7684 ID\u3002",
1210
+ "\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u53D1\u660E schema \u4E2D\u4E0D\u5B58\u5728\u7684\u5B57\u6BB5\u3002\u4EE3\u7801\u91CC\u7684 UUID \u5E94\u89C6\u4E3A\u6765\u6E90\uFF0C\u4E0D\u8981\u65E0\u610F\u4E49\u5730\u91CD\u5EFA\u73B0\u6709\u5B57\u6BB5\u3002",
1191
1211
  "\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002",
1192
1212
  "",
1193
1213
  buildPasteConfigErrorDetails(source, error),
@@ -1525,6 +1545,26 @@ function confirmChanges() {
1525
1545
  {{ validationErrors[getFieldErrorKey(selectedField.draftId, "id")] }}
1526
1546
  </p>
1527
1547
 
1548
+ <section
1549
+ v-if="selectedField.field.type === 'slot'"
1550
+ data-slot="fields-configurator-field-id-section"
1551
+ class="flex flex-col gap-2"
1552
+ >
1553
+ <label class="flex flex-col gap-2">
1554
+ <span class="text-xs font-medium text-zinc-500">
1555
+ {{ t("field-id") }}
1556
+ </span>
1557
+ <Input
1558
+ data-slot="fields-configurator-field-id-input"
1559
+ :model-value="selectedField.field.id"
1560
+ :aria-invalid="validationErrors[getFieldErrorKey(selectedField.draftId, 'id')] ? 'true' : void 0"
1561
+ :placeholder="t('field-id-placeholder')"
1562
+ class="font-mono text-sm"
1563
+ @update:model-value="updateSelectedFieldId"
1564
+ />
1565
+ </label>
1566
+ </section>
1567
+
1528
1568
  <section
1529
1569
  v-if="!isPassiveField(selectedField.field)"
1530
1570
  data-slot="fields-configurator-field-path-section"
@@ -2188,6 +2228,8 @@ function confirmChanges() {
2188
2228
  "field-type-empty": "空白",
2189
2229
  "field-type-slot": "插槽",
2190
2230
  "field-id": "字段 ID",
2231
+ "field-id-placeholder": "粘贴代码里已经生成的 UUID",
2232
+ "field-id-invalid": "字段 ID 必须是合法的 UUID",
2191
2233
  "field-id-duplicate": "字段 ID 不能重复",
2192
2234
  "field-path": "字段路径",
2193
2235
  "field-path-placeholder": "例如 profile.name",
@@ -2291,6 +2333,8 @@ function confirmChanges() {
2291
2333
  "field-type-empty": "空白",
2292
2334
  "field-type-slot": "スロット",
2293
2335
  "field-id": "フィールド ID",
2336
+ "field-id-placeholder": "コードで生成した UUID を貼り付け",
2337
+ "field-id-invalid": "フィールド ID は有効な UUID である必要があります",
2294
2338
  "field-id-duplicate": "フィールド ID は重複できません",
2295
2339
  "field-path": "フィールドパス",
2296
2340
  "field-path-placeholder": "例: profile.name",
@@ -2394,6 +2438,8 @@ function confirmChanges() {
2394
2438
  "field-type-empty": "Empty",
2395
2439
  "field-type-slot": "Slot",
2396
2440
  "field-id": "Field ID",
2441
+ "field-id-placeholder": "Paste the UUID generated in code",
2442
+ "field-id-invalid": "Field ID must be a valid UUID",
2397
2443
  "field-id-duplicate": "Field ID must be unique",
2398
2444
  "field-path": "Field path",
2399
2445
  "field-path-placeholder": "For example profile.name",
package/dist/types.d.mts CHANGED
@@ -6,6 +6,8 @@ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<
6
6
 
7
7
  export { type FieldsInstance } from '../dist/runtime/components/fields-instance.js'
8
8
 
9
+ export { type FieldsSlotProps } from '../dist/runtime/components/ui/fields/slot-props.js'
10
+
9
11
  export { default } from './module.mjs'
10
12
 
11
13
  export { type Env } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
- "version": "0.11.7",
3
+ "version": "0.11.8",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",