@dou-ui/form-builder 0.0.0-stage → 0.1.0-alpha.1

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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/dist/builder/canvas.js +695 -0
  4. package/dist/builder/canvas.js.map +1 -0
  5. package/dist/builder/completion.js +49 -0
  6. package/dist/builder/completion.js.map +1 -0
  7. package/dist/builder/condition-editor.js +6 -0
  8. package/dist/builder/condition-editor.js.map +1 -0
  9. package/dist/builder/condition-editor.vue_vue_type_script_setup_true_lang.js +410 -0
  10. package/dist/builder/condition-editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
  11. package/dist/builder/condition.js +159 -0
  12. package/dist/builder/condition.js.map +1 -0
  13. package/dist/builder/context.js +477 -0
  14. package/dist/builder/context.js.map +1 -0
  15. package/dist/builder/core/commands.js +373 -0
  16. package/dist/builder/core/commands.js.map +1 -0
  17. package/dist/builder/core/history.js +76 -0
  18. package/dist/builder/core/history.js.map +1 -0
  19. package/dist/builder/core/transaction.js +298 -0
  20. package/dist/builder/core/transaction.js.map +1 -0
  21. package/dist/builder/drag.js +466 -0
  22. package/dist/builder/drag.js.map +1 -0
  23. package/dist/builder/expression-box.js +19 -0
  24. package/dist/builder/expression-box.js.map +1 -0
  25. package/dist/builder/expression-input.js +6 -0
  26. package/dist/builder/expression-input.js.map +1 -0
  27. package/dist/builder/expression-input.vue_vue_type_script_setup_true_lang.js +117 -0
  28. package/dist/builder/expression-input.vue_vue_type_script_setup_true_lang.js.map +1 -0
  29. package/dist/builder/extension.js +47 -0
  30. package/dist/builder/extension.js.map +1 -0
  31. package/dist/builder/form-builder.js +6 -0
  32. package/dist/builder/form-builder.js.map +1 -0
  33. package/dist/builder/form-builder.vue_vue_type_script_setup_true_lang.js +450 -0
  34. package/dist/builder/form-builder.vue_vue_type_script_setup_true_lang.js.map +1 -0
  35. package/dist/builder/index.js +22 -0
  36. package/dist/builder/index.js.map +1 -0
  37. package/dist/builder/inspector.js +6 -0
  38. package/dist/builder/inspector.js.map +1 -0
  39. package/dist/builder/inspector.vue_vue_type_script_setup_true_lang.js +442 -0
  40. package/dist/builder/inspector.vue_vue_type_script_setup_true_lang.js.map +1 -0
  41. package/dist/builder/json-dialog.js +6 -0
  42. package/dist/builder/json-dialog.js.map +1 -0
  43. package/dist/builder/json-dialog.vue_vue_type_script_setup_true_lang.js +281 -0
  44. package/dist/builder/json-dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
  45. package/dist/builder/locale.js +1271 -0
  46. package/dist/builder/locale.js.map +1 -0
  47. package/dist/builder/logic-fields.js +18 -0
  48. package/dist/builder/logic-fields.js.map +1 -0
  49. package/dist/builder/logic.js +332 -0
  50. package/dist/builder/logic.js.map +1 -0
  51. package/dist/builder/menu.js +92 -0
  52. package/dist/builder/menu.js.map +1 -0
  53. package/dist/builder/outline-row.js +6 -0
  54. package/dist/builder/outline-row.js.map +1 -0
  55. package/dist/builder/outline-row.vue_vue_type_script_setup_true_lang.js +88 -0
  56. package/dist/builder/outline-row.vue_vue_type_script_setup_true_lang.js.map +1 -0
  57. package/dist/builder/outline.js +6 -0
  58. package/dist/builder/outline.js.map +1 -0
  59. package/dist/builder/outline.vue_vue_type_script_setup_true_lang.js +111 -0
  60. package/dist/builder/outline.vue_vue_type_script_setup_true_lang.js.map +1 -0
  61. package/dist/builder/palette.js +6 -0
  62. package/dist/builder/palette.js.map +1 -0
  63. package/dist/builder/palette.vue_vue_type_script_setup_true_lang.js +96 -0
  64. package/dist/builder/palette.vue_vue_type_script_setup_true_lang.js.map +1 -0
  65. package/dist/builder/preview.js +6 -0
  66. package/dist/builder/preview.js.map +1 -0
  67. package/dist/builder/preview.vue_vue_type_script_setup_true_lang.js +111 -0
  68. package/dist/builder/preview.vue_vue_type_script_setup_true_lang.js.map +1 -0
  69. package/dist/builder/settings-binding.js +57 -0
  70. package/dist/builder/settings-binding.js.map +1 -0
  71. package/dist/builder/settings.js +487 -0
  72. package/dist/builder/settings.js.map +1 -0
  73. package/dist/builder/span-field.js +104 -0
  74. package/dist/builder/span-field.js.map +1 -0
  75. package/dist/code/expression-input.js +6 -0
  76. package/dist/code/expression-input.js.map +1 -0
  77. package/dist/code/expression-input.vue_vue_type_script_setup_true_lang.js +88 -0
  78. package/dist/code/expression-input.vue_vue_type_script_setup_true_lang.js.map +1 -0
  79. package/dist/code/extension.js +202 -0
  80. package/dist/code/extension.js.map +1 -0
  81. package/dist/code/index.js +19 -0
  82. package/dist/code/index.js.map +1 -0
  83. package/dist/code/language.js +43 -0
  84. package/dist/code/language.js.map +1 -0
  85. package/dist/collaboration/index.js +382 -0
  86. package/dist/collaboration/index.js.map +1 -0
  87. package/dist/collaboration/order.js +88 -0
  88. package/dist/collaboration/order.js.map +1 -0
  89. package/dist/core/check.js +314 -0
  90. package/dist/core/check.js.map +1 -0
  91. package/dist/core/document.js +112 -0
  92. package/dist/core/document.js.map +1 -0
  93. package/dist/core/expression.js +854 -0
  94. package/dist/core/expression.js.map +1 -0
  95. package/dist/core/formulas.js +242 -0
  96. package/dist/core/formulas.js.map +1 -0
  97. package/dist/core/rules.js +83 -0
  98. package/dist/core/rules.js.map +1 -0
  99. package/dist/core/values.js +80 -0
  100. package/dist/core/values.js.map +1 -0
  101. package/dist/fields/builtin.js +167 -0
  102. package/dist/fields/builtin.js.map +1 -0
  103. package/dist/fields/components.js +155 -0
  104. package/dist/fields/components.js.map +1 -0
  105. package/dist/fields/define.js +44 -0
  106. package/dist/fields/define.js.map +1 -0
  107. package/dist/fields/format.js +125 -0
  108. package/dist/fields/format.js.map +1 -0
  109. package/dist/fields/options.js +54 -0
  110. package/dist/fields/options.js.map +1 -0
  111. package/dist/index.js +20 -0
  112. package/dist/locale.js +161 -0
  113. package/dist/locale.js.map +1 -0
  114. package/dist/renderer/containers.js +430 -0
  115. package/dist/renderer/containers.js.map +1 -0
  116. package/dist/renderer/context.js +21 -0
  117. package/dist/renderer/context.js.map +1 -0
  118. package/dist/renderer/form-renderer.js +6 -0
  119. package/dist/renderer/form-renderer.js.map +1 -0
  120. package/dist/renderer/form-renderer.vue_vue_type_script_setup_true_lang.js +272 -0
  121. package/dist/renderer/form-renderer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  122. package/dist/renderer/index.js +19 -0
  123. package/dist/renderer/index.js.map +1 -0
  124. package/dist/renderer/node-view.js +385 -0
  125. package/dist/renderer/node-view.js.map +1 -0
  126. package/dist/style.css +1 -0
  127. package/dist/style.d.ts +1 -0
  128. package/dist/style.js +27 -0
  129. package/dist/types/builder/canvas.d.ts +5 -0
  130. package/dist/types/builder/completion.d.ts +35 -0
  131. package/dist/types/builder/condition-editor.vue.d.ts +17 -0
  132. package/dist/types/builder/condition.d.ts +37 -0
  133. package/dist/types/builder/context.d.ts +149 -0
  134. package/dist/types/builder/core/commands.d.ts +74 -0
  135. package/dist/types/builder/core/history.d.ts +23 -0
  136. package/dist/types/builder/core/transaction.d.ts +108 -0
  137. package/dist/types/builder/drag.d.ts +52 -0
  138. package/dist/types/builder/expression-box.d.ts +8 -0
  139. package/dist/types/builder/expression-input.vue.d.ts +29 -0
  140. package/dist/types/builder/extension.d.ts +149 -0
  141. package/dist/types/builder/form-builder.vue.d.ts +24 -0
  142. package/dist/types/builder/index.d.ts +76 -0
  143. package/dist/types/builder/inspector.vue.d.ts +3 -0
  144. package/dist/types/builder/json-dialog.vue.d.ts +10 -0
  145. package/dist/types/builder/locale.d.ts +195 -0
  146. package/dist/types/builder/logic-fields.d.ts +2 -0
  147. package/dist/types/builder/logic.d.ts +57 -0
  148. package/dist/types/builder/menu.d.ts +23 -0
  149. package/dist/types/builder/outline-row.vue.d.ts +19 -0
  150. package/dist/types/builder/outline.vue.d.ts +3 -0
  151. package/dist/types/builder/palette.vue.d.ts +3 -0
  152. package/dist/types/builder/preview.vue.d.ts +3 -0
  153. package/dist/types/builder/settings-binding.d.ts +23 -0
  154. package/dist/types/builder/settings.d.ts +49 -0
  155. package/dist/types/builder/span-field.d.ts +4 -0
  156. package/dist/types/builder/types.d.ts +59 -0
  157. package/dist/types/code/expression-input.vue.d.ts +23 -0
  158. package/dist/types/code/extension.d.ts +45 -0
  159. package/dist/types/code/index.d.ts +12 -0
  160. package/dist/types/code/language.d.ts +6 -0
  161. package/dist/types/collaboration/index.d.ts +64 -0
  162. package/dist/types/collaboration/order.d.ts +20 -0
  163. package/dist/types/core/check.d.ts +68 -0
  164. package/dist/types/core/document.d.ts +36 -0
  165. package/dist/types/core/expression.d.ts +175 -0
  166. package/dist/types/core/formulas.d.ts +44 -0
  167. package/dist/types/core/rules.d.ts +23 -0
  168. package/dist/types/core/types.d.ts +211 -0
  169. package/dist/types/core/values.d.ts +22 -0
  170. package/dist/types/fields/builtin.d.ts +9 -0
  171. package/dist/types/fields/components.d.ts +177 -0
  172. package/dist/types/fields/define.d.ts +93 -0
  173. package/dist/types/fields/format.d.ts +76 -0
  174. package/dist/types/fields/options.d.ts +11 -0
  175. package/dist/types/index.d.ts +3 -0
  176. package/dist/types/locale.d.ts +45 -0
  177. package/dist/types/renderer/containers.d.ts +20 -0
  178. package/dist/types/renderer/context.d.ts +186 -0
  179. package/dist/types/renderer/form-renderer.vue.d.ts +25 -0
  180. package/dist/types/renderer/index.d.ts +81 -0
  181. package/dist/types/renderer/node-view.d.ts +56 -0
  182. package/package.json +88 -4
@@ -0,0 +1,6 @@
1
+ import outline_row_vue_vue_type_script_setup_true_lang_default from "./outline-row.vue_vue_type_script_setup_true_lang.js";
2
+ // src/builder/outline-row.vue
3
+ var outline_row_default = outline_row_vue_vue_type_script_setup_true_lang_default;
4
+ export { outline_row_default as default };
5
+
6
+ //# sourceMappingURL=outline-row.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline-row.js","names":["_sfc_main"],"sources":["../../src/builder/outline-row.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { ChevronRightIcon } from '@dou-ui/icons'\nimport type { FormNode } from '../core/types'\nimport type { FormFieldType } from '../fields/define'\nimport { useBuilderState } from './context'\nimport { iconOf, labelOf, nodeTitle } from './settings'\n\ndefineOptions({ name: 'DouFormBuilderOutlineRow' })\n\n// One row is its own component, so a change to one node of a large form\n// draws only that row again.\nconst props = defineProps<{\n node: FormNode\n level: number\n position: number\n size: number\n container: boolean\n open: boolean\n selected: boolean\n}>()\nconst emit = defineEmits<{ press: [event: MouseEvent]; toggle: [] }>()\n\nconst state = useBuilderState()\nconst types = computed(() => state.fields as Map<string, FormFieldType>)\n\nconst title = computed(() => nodeTitle(props.node, types.value, state.messages))\nconst secondary = computed(() => {\n const node = props.node\n const type = labelOf(node.type, types.value.get(node.type), state.messages)\n const name = 'name' in node && node.name && node.name !== title.value ? node.name : ''\n return title.value === type ? name : name || type\n})\n\n/** The extensions' marks of the node, as dots in their colors. */\nconst marks = computed(() => state.extensions().marks.flatMap((mark) => mark(state, props.node)))\n</script>\n\n<template>\n <div\n :id=\"state.domId(node.id, 'outline')\"\n class=\"dou-form-builder__outline-row\"\n :class=\"{ 'is-selected': selected }\"\n role=\"treeitem\"\n :aria-level=\"level\"\n :aria-posinset=\"position\"\n :aria-setsize=\"size\"\n :aria-selected=\"selected\"\n :aria-expanded=\"container ? open : undefined\"\n :style=\"{ '--dou-form-builder-level': level - 1 }\"\n @click=\"emit('press', $event)\"\n >\n <button\n v-if=\"container\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"dou-form-builder__outline-toggle\"\n :class=\"{ 'is-open': open }\"\n :aria-label=\"open ? state.messages.outline.collapse : state.messages.outline.expand\"\n @click.stop=\"emit('toggle')\"\n >\n <ChevronRightIcon />\n </button>\n <span v-else class=\"dou-form-builder__outline-spacer\" />\n <component\n :is=\"iconOf(node.type, types.get(node.type))\"\n class=\"dou-form-builder__outline-icon\"\n />\n <span class=\"dou-form-builder__outline-title\">{{ title }}</span>\n <span class=\"dou-form-builder__outline-name\">{{ secondary }}</span>\n <span\n v-for=\"mark in marks\"\n :key=\"mark.key\"\n class=\"dou-form-builder__outline-mark\"\n :style=\"mark.color ? { '--dou-form-builder-mark': mark.color } : undefined\"\n :title=\"mark.label\"\n aria-hidden=\"true\"\n />\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,88 @@
1
+ import { iconOf, labelOf, nodeTitle } from "./settings.js";
2
+ import { useBuilderState } from "./context.js";
3
+ import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, resolveDynamicComponent, toDisplayString, unref, withModifiers } from "vue";
4
+ import { ChevronRightIcon } from "@dou-ui/icons";
5
+ // src/builder/outline-row.vue?vue&type=script&setup=true&lang.ts
6
+ var _hoisted_1 = [
7
+ "id",
8
+ "aria-level",
9
+ "aria-posinset",
10
+ "aria-setsize",
11
+ "aria-selected",
12
+ "aria-expanded"
13
+ ];
14
+ var _hoisted_2 = ["aria-label"];
15
+ var _hoisted_3 = {
16
+ key: 1,
17
+ class: "dou-form-builder__outline-spacer"
18
+ };
19
+ var _hoisted_4 = { class: "dou-form-builder__outline-title" };
20
+ var _hoisted_5 = { class: "dou-form-builder__outline-name" };
21
+ var _hoisted_6 = ["title"];
22
+ var outline_row_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
23
+ name: "DouFormBuilderOutlineRow",
24
+ __name: "outline-row",
25
+ props: {
26
+ node: {},
27
+ level: {},
28
+ position: {},
29
+ size: {},
30
+ container: { type: Boolean },
31
+ open: { type: Boolean },
32
+ selected: { type: Boolean }
33
+ },
34
+ emits: ["press", "toggle"],
35
+ setup(__props, { emit: __emit }) {
36
+ const props = __props;
37
+ const emit = __emit;
38
+ const state = useBuilderState();
39
+ const types = computed(() => state.fields);
40
+ const title = computed(() => nodeTitle(props.node, types.value, state.messages));
41
+ const secondary = computed(() => {
42
+ const node = props.node;
43
+ const type = labelOf(node.type, types.value.get(node.type), state.messages);
44
+ const name = "name" in node && node.name && node.name !== title.value ? node.name : "";
45
+ return title.value === type ? name : name || type;
46
+ });
47
+ /** The extensions' marks of the node, as dots in their colors. */
48
+ const marks = computed(() => state.extensions().marks.flatMap((mark) => mark(state, props.node)));
49
+ return (_ctx, _cache) => {
50
+ return openBlock(), createElementBlock("div", {
51
+ id: unref(state).domId(__props.node.id, "outline"),
52
+ class: normalizeClass(["dou-form-builder__outline-row", { "is-selected": __props.selected }]),
53
+ role: "treeitem",
54
+ "aria-level": __props.level,
55
+ "aria-posinset": __props.position,
56
+ "aria-setsize": __props.size,
57
+ "aria-selected": __props.selected,
58
+ "aria-expanded": __props.container ? __props.open : void 0,
59
+ style: normalizeStyle({ "--dou-form-builder-level": __props.level - 1 }),
60
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("press", $event))
61
+ }, [
62
+ __props.container ? (openBlock(), createElementBlock("button", {
63
+ key: 0,
64
+ type: "button",
65
+ tabindex: "-1",
66
+ class: normalizeClass(["dou-form-builder__outline-toggle", { "is-open": __props.open }]),
67
+ "aria-label": __props.open ? unref(state).messages.outline.collapse : unref(state).messages.outline.expand,
68
+ onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("toggle"), ["stop"]))
69
+ }, [createVNode(unref(ChevronRightIcon))], 10, _hoisted_2)) : (openBlock(), createElementBlock("span", _hoisted_3)),
70
+ (openBlock(), createBlock(resolveDynamicComponent(unref(iconOf)(__props.node.type, types.value.get(__props.node.type))), { class: "dou-form-builder__outline-icon" })),
71
+ createElementVNode("span", _hoisted_4, toDisplayString(title.value), 1),
72
+ createElementVNode("span", _hoisted_5, toDisplayString(secondary.value), 1),
73
+ (openBlock(true), createElementBlock(Fragment, null, renderList(marks.value, (mark) => {
74
+ return openBlock(), createElementBlock("span", {
75
+ key: mark.key,
76
+ class: "dou-form-builder__outline-mark",
77
+ style: normalizeStyle(mark.color ? { "--dou-form-builder-mark": mark.color } : void 0),
78
+ title: mark.label,
79
+ "aria-hidden": "true"
80
+ }, null, 12, _hoisted_6);
81
+ }), 128))
82
+ ], 14, _hoisted_1);
83
+ };
84
+ }
85
+ });
86
+ export { outline_row_vue_vue_type_script_setup_true_lang_default as default };
87
+
88
+ //# sourceMappingURL=outline-row.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline-row.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_normalizeClass","_normalizeStyle","_withModifiers","_createVNode","_createBlock","_resolveDynamicComponent","_createElementVNode","_toDisplayString","_Fragment","_renderList"],"sources":["../../src/builder/outline-row.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { ChevronRightIcon } from '@dou-ui/icons'\nimport type { FormNode } from '../core/types'\nimport type { FormFieldType } from '../fields/define'\nimport { useBuilderState } from './context'\nimport { iconOf, labelOf, nodeTitle } from './settings'\n\ndefineOptions({ name: 'DouFormBuilderOutlineRow' })\n\n// One row is its own component, so a change to one node of a large form\n// draws only that row again.\nconst props = defineProps<{\n node: FormNode\n level: number\n position: number\n size: number\n container: boolean\n open: boolean\n selected: boolean\n}>()\nconst emit = defineEmits<{ press: [event: MouseEvent]; toggle: [] }>()\n\nconst state = useBuilderState()\nconst types = computed(() => state.fields as Map<string, FormFieldType>)\n\nconst title = computed(() => nodeTitle(props.node, types.value, state.messages))\nconst secondary = computed(() => {\n const node = props.node\n const type = labelOf(node.type, types.value.get(node.type), state.messages)\n const name = 'name' in node && node.name && node.name !== title.value ? node.name : ''\n return title.value === type ? name : name || type\n})\n\n/** The extensions' marks of the node, as dots in their colors. */\nconst marks = computed(() => state.extensions().marks.flatMap((mark) => mark(state, props.node)))\n</script>\n\n<template>\n <div\n :id=\"state.domId(node.id, 'outline')\"\n class=\"dou-form-builder__outline-row\"\n :class=\"{ 'is-selected': selected }\"\n role=\"treeitem\"\n :aria-level=\"level\"\n :aria-posinset=\"position\"\n :aria-setsize=\"size\"\n :aria-selected=\"selected\"\n :aria-expanded=\"container ? open : undefined\"\n :style=\"{ '--dou-form-builder-level': level - 1 }\"\n @click=\"emit('press', $event)\"\n >\n <button\n v-if=\"container\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"dou-form-builder__outline-toggle\"\n :class=\"{ 'is-open': open }\"\n :aria-label=\"open ? state.messages.outline.collapse : state.messages.outline.expand\"\n @click.stop=\"emit('toggle')\"\n >\n <ChevronRightIcon />\n </button>\n <span v-else class=\"dou-form-builder__outline-spacer\" />\n <component\n :is=\"iconOf(node.type, types.get(node.type))\"\n class=\"dou-form-builder__outline-icon\"\n />\n <span class=\"dou-form-builder__outline-title\">{{ title }}</span>\n <span class=\"dou-form-builder__outline-name\">{{ secondary }}</span>\n <span\n v-for=\"mark in marks\"\n :key=\"mark.key\"\n class=\"dou-form-builder__outline-mark\"\n :style=\"mark.color ? { '--dou-form-builder-mark': mark.color } : undefined\"\n :title=\"mark.label\"\n aria-hidden=\"true\"\n />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYA,MAAM,QAAQ;EASd,MAAM,OAAO;EAEb,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,QAAQ,eAAe,MAAM,MAAoC;EAEvE,MAAM,QAAQ,eAAe,UAAU,MAAM,MAAM,MAAM,OAAO,MAAM,QAAQ,CAAC;EAC/E,MAAM,YAAY,eAAe;GAC/B,MAAM,OAAO,MAAM;GACnB,MAAM,OAAO,QAAQ,KAAK,MAAM,MAAM,MAAM,IAAI,KAAK,IAAI,GAAG,MAAM,QAAQ;GAC1E,MAAM,OAAO,UAAU,QAAQ,KAAK,QAAQ,KAAK,SAAS,MAAM,QAAQ,KAAK,OAAO;GACpF,OAAO,MAAM,UAAU,OAAO,OAAO,QAAQ;EAC/C,CAAC;;EAGD,MAAM,QAAQ,eAAe,MAAM,WAAW,CAAC,CAAC,MAAM,SAAS,SAAS,KAAK,OAAO,MAAM,IAAI,CAAC,CAAC;;GAI9F,OAAAA,UAAA,GAAAC,mBAuCM,OAAA;IAtCH,IAAIC,MAAA,KAAA,CAAK,CAAC,MAAM,QAAA,KAAK,IAAE,SAAA;IACxB,OAAKC,eAAA,CAAC,iCAA+B,EAAA,eACZ,QAAA,SAAQ,CAAA,CAAA;IACjC,MAAK;IACJ,cAAY,QAAA;IACZ,iBAAe,QAAA;IACf,gBAAc,QAAA;IACd,iBAAe,QAAA;IACf,iBAAe,QAAA,YAAY,QAAA,OAAO,KAAA;IAClC,OAAKC,eAAA,EAAA,4BAAgC,QAAA,QAAK,EAAA,CAAA;IAC1C,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAU,MAAM;;IAGpB,QAAA,aADRJ,UAAA,GAAAC,mBAUS,UAAA;;KARP,MAAK;KACL,UAAS;KACT,OAAKE,eAAA,CAAC,oCAAkC,EAAA,WACnB,QAAA,KAAI,CAAA,CAAA;KACxB,cAAY,QAAA,OAAOD,MAAA,KAAA,CAAK,CAAC,SAAS,QAAQ,WAAWA,MAAA,KAAA,CAAK,CAAC,SAAS,QAAQ;KAC5E,SAAK,OAAA,OAAA,OAAA,KAAAG,eAAA,WAAO,KAAI,QAAA,GAAA,CAAA,MAAA,CAAA;I,IAEjBC,YAAoBJ,MAAA,gBAAA,CAAA,C,uBAEtBF,UAAA,GAAAC,mBAAwD,QAAxD,UAAwD;KACxDD,UAAA,GAAAO,YAGEC,wBAFKN,MAAA,MAAA,CAAM,CAAC,QAAA,KAAK,MAAM,MAAA,MAAM,IAAI,QAAA,KAAK,IAAI,CAAA,CAAA,GAAA,EAC1C,OAAM,iCAAgC,CAAA;IAExCO,mBAAgE,QAAhE,YAAgEC,gBAAf,MAAA,KAAK,GAAA,CAAA;IACtDD,mBAAmE,QAAnE,YAAmEC,gBAAnB,UAAA,KAAS,GAAA,CAAA;KACzDV,UAAA,IAAA,GAAAC,mBAOEU,UAAA,MAAAC,WANe,MAAA,QAAR,SAAI;KADb,OAAAZ,UAAA,GAAAC,mBAOE,QAAA;MALC,KAAK,KAAK;MACX,OAAM;MACL,OAAKG,eAAE,KAAK,QAAK,EAAA,2BAAgC,KAAK,MAAK,IAAK,KAAA,CAAS;MACzE,OAAO,KAAK;MACb,eAAY"}
@@ -0,0 +1,6 @@
1
+ import outline_vue_vue_type_script_setup_true_lang_default from "./outline.vue_vue_type_script_setup_true_lang.js";
2
+ // src/builder/outline.vue
3
+ var outline_default = outline_vue_vue_type_script_setup_true_lang_default;
4
+ export { outline_default as default };
5
+
6
+ //# sourceMappingURL=outline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.js","names":["_sfc_main"],"sources":["../../src/builder/outline.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, shallowReactive, watch } from 'vue'\nimport { isContainer } from '../core/document'\nimport type { FormNode } from '../core/types'\nimport { useBuilderState } from './context'\nimport type { BuilderView } from './context'\nimport { formNodePlaces } from './core/transaction'\nimport OutlineRow from './outline-row.vue'\n\ndefineOptions({ name: 'DouFormBuilderOutline' })\n\nconst state = useBuilderState()\n/** Containers folded away. */\nconst folded = shallowReactive(new Set<string>())\n\ninterface Row {\n node: FormNode\n level: number\n position: number\n size: number\n container: boolean\n}\n\nconst rows = computed(() => {\n const result: Row[] = []\n const visit = (nodes: readonly FormNode[], level: number) => {\n nodes.forEach((node, index) => {\n const container = isContainer(node) && node.children.length > 0\n result.push({ node, level, position: index + 1, size: nodes.length, container })\n if (container && !folded.has(node.id))\n visit((node as { children: FormNode[] }).children, level + 1)\n })\n }\n visit(state.document.nodes, 1)\n return result\n})\n\n// A folded container opens when something inside it is selected.\nwatch(\n () => state.selection,\n (ids) => {\n const map = formNodePlaces(state.document)\n for (const id of ids) {\n let parent = map.get(id)?.parent\n while (parent) {\n folded.delete(parent.id)\n parent = map.get(parent.id)?.parent\n }\n }\n },\n)\n\nconst view: BuilderView = {\n name: 'outline',\n order: () => rows.value.map((row) => row.node.id),\n expand: (id) => {\n if (!folded.has(id)) return false\n folded.delete(id)\n return true\n },\n collapse: (id) => {\n const node = formNodePlaces(state.document).get(id)?.node\n if (!node || !isContainer(node) || !node.children.length || folded.has(id)) return false\n folded.add(id)\n return true\n },\n}\n\nfunction toggle(id: string) {\n if (folded.has(id)) folded.delete(id)\n else folded.add(id)\n}\n\nfunction press(event: MouseEvent, id: string) {\n state.pick(id, event, view)\n}\n\nconst active = computed(() => {\n const last = state.selection[state.selection.length - 1]\n return last ? state.domId(last, 'outline') : undefined\n})\n</script>\n\n<template>\n <div\n class=\"dou-form-builder__outline\"\n role=\"tree\"\n tabindex=\"0\"\n aria-multiselectable=\"true\"\n :aria-label=\"state.messages.outline.label\"\n :aria-activedescendant=\"active\"\n @keydown=\"state.keydown($event, view)\"\n >\n <OutlineRow\n v-for=\"row in rows\"\n :key=\"row.node.id\"\n :node=\"row.node\"\n :level=\"row.level\"\n :position=\"row.position\"\n :size=\"row.size\"\n :container=\"row.container\"\n :open=\"row.container && !folded.has(row.node.id)\"\n :selected=\"state.selected.value.has(row.node.id)\"\n @press=\"press($event, row.node.id)\"\n @toggle=\"toggle(row.node.id)\"\n />\n <div v-if=\"!rows.length\" class=\"dou-form-builder__outline-empty\">\n {{ state.messages.canvas.empty }}\n </div>\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,111 @@
1
+ import { isContainer } from "../core/document.js";
2
+ import { formNodePlaces } from "./core/transaction.js";
3
+ import { useBuilderState } from "./context.js";
4
+ import outline_row_default from "./outline-row.js";
5
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, defineComponent, openBlock, renderList, shallowReactive, toDisplayString, unref, watch } from "vue";
6
+ // src/builder/outline.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1 = ["aria-label", "aria-activedescendant"];
8
+ var _hoisted_2 = {
9
+ key: 0,
10
+ class: "dou-form-builder__outline-empty"
11
+ };
12
+ var outline_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
13
+ name: "DouFormBuilderOutline",
14
+ __name: "outline",
15
+ setup(__props) {
16
+ const state = useBuilderState();
17
+ /** Containers folded away. */
18
+ const folded = shallowReactive(/* @__PURE__ */ new Set());
19
+ const rows = computed(() => {
20
+ const result = [];
21
+ const visit = (nodes, level) => {
22
+ nodes.forEach((node, index) => {
23
+ const container = isContainer(node) && node.children.length > 0;
24
+ result.push({
25
+ node,
26
+ level,
27
+ position: index + 1,
28
+ size: nodes.length,
29
+ container
30
+ });
31
+ if (container && !folded.has(node.id)) visit(node.children, level + 1);
32
+ });
33
+ };
34
+ visit(state.document.nodes, 1);
35
+ return result;
36
+ });
37
+ watch(() => state.selection, (ids) => {
38
+ const map = formNodePlaces(state.document);
39
+ for (const id of ids) {
40
+ let parent = map.get(id)?.parent;
41
+ while (parent) {
42
+ folded.delete(parent.id);
43
+ parent = map.get(parent.id)?.parent;
44
+ }
45
+ }
46
+ });
47
+ const view = {
48
+ name: "outline",
49
+ order: () => rows.value.map((row) => row.node.id),
50
+ expand: (id) => {
51
+ if (!folded.has(id)) return false;
52
+ folded.delete(id);
53
+ return true;
54
+ },
55
+ collapse: (id) => {
56
+ const node = formNodePlaces(state.document).get(id)?.node;
57
+ if (!node || !isContainer(node) || !node.children.length || folded.has(id)) return false;
58
+ folded.add(id);
59
+ return true;
60
+ }
61
+ };
62
+ function toggle(id) {
63
+ if (folded.has(id)) folded.delete(id);
64
+ else folded.add(id);
65
+ }
66
+ function press(event, id) {
67
+ state.pick(id, event, view);
68
+ }
69
+ const active = computed(() => {
70
+ const last = state.selection[state.selection.length - 1];
71
+ return last ? state.domId(last, "outline") : void 0;
72
+ });
73
+ return (_ctx, _cache) => {
74
+ return openBlock(), createElementBlock("div", {
75
+ class: "dou-form-builder__outline",
76
+ role: "tree",
77
+ tabindex: "0",
78
+ "aria-multiselectable": "true",
79
+ "aria-label": unref(state).messages.outline.label,
80
+ "aria-activedescendant": active.value,
81
+ onKeydown: _cache[0] || (_cache[0] = ($event) => unref(state).keydown($event, view))
82
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(rows.value, (row) => {
83
+ return openBlock(), createBlock(outline_row_default, {
84
+ key: row.node.id,
85
+ node: row.node,
86
+ level: row.level,
87
+ position: row.position,
88
+ size: row.size,
89
+ container: row.container,
90
+ open: row.container && !unref(folded).has(row.node.id),
91
+ selected: unref(state).selected.value.has(row.node.id),
92
+ onPress: ($event) => press($event, row.node.id),
93
+ onToggle: ($event) => toggle(row.node.id)
94
+ }, null, 8, [
95
+ "node",
96
+ "level",
97
+ "position",
98
+ "size",
99
+ "container",
100
+ "open",
101
+ "selected",
102
+ "onPress",
103
+ "onToggle"
104
+ ]);
105
+ }), 128)), !rows.value.length ? (openBlock(), createElementBlock("div", _hoisted_2, toDisplayString(unref(state).messages.canvas.empty), 1)) : createCommentVNode("", true)], 40, _hoisted_1);
106
+ };
107
+ }
108
+ });
109
+ export { outline_vue_vue_type_script_setup_true_lang_default as default };
110
+
111
+ //# sourceMappingURL=outline.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_Fragment","_renderList","_createBlock","OutlineRow","_toDisplayString","_createCommentVNode"],"sources":["../../src/builder/outline.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, shallowReactive, watch } from 'vue'\nimport { isContainer } from '../core/document'\nimport type { FormNode } from '../core/types'\nimport { useBuilderState } from './context'\nimport type { BuilderView } from './context'\nimport { formNodePlaces } from './core/transaction'\nimport OutlineRow from './outline-row.vue'\n\ndefineOptions({ name: 'DouFormBuilderOutline' })\n\nconst state = useBuilderState()\n/** Containers folded away. */\nconst folded = shallowReactive(new Set<string>())\n\ninterface Row {\n node: FormNode\n level: number\n position: number\n size: number\n container: boolean\n}\n\nconst rows = computed(() => {\n const result: Row[] = []\n const visit = (nodes: readonly FormNode[], level: number) => {\n nodes.forEach((node, index) => {\n const container = isContainer(node) && node.children.length > 0\n result.push({ node, level, position: index + 1, size: nodes.length, container })\n if (container && !folded.has(node.id))\n visit((node as { children: FormNode[] }).children, level + 1)\n })\n }\n visit(state.document.nodes, 1)\n return result\n})\n\n// A folded container opens when something inside it is selected.\nwatch(\n () => state.selection,\n (ids) => {\n const map = formNodePlaces(state.document)\n for (const id of ids) {\n let parent = map.get(id)?.parent\n while (parent) {\n folded.delete(parent.id)\n parent = map.get(parent.id)?.parent\n }\n }\n },\n)\n\nconst view: BuilderView = {\n name: 'outline',\n order: () => rows.value.map((row) => row.node.id),\n expand: (id) => {\n if (!folded.has(id)) return false\n folded.delete(id)\n return true\n },\n collapse: (id) => {\n const node = formNodePlaces(state.document).get(id)?.node\n if (!node || !isContainer(node) || !node.children.length || folded.has(id)) return false\n folded.add(id)\n return true\n },\n}\n\nfunction toggle(id: string) {\n if (folded.has(id)) folded.delete(id)\n else folded.add(id)\n}\n\nfunction press(event: MouseEvent, id: string) {\n state.pick(id, event, view)\n}\n\nconst active = computed(() => {\n const last = state.selection[state.selection.length - 1]\n return last ? state.domId(last, 'outline') : undefined\n})\n</script>\n\n<template>\n <div\n class=\"dou-form-builder__outline\"\n role=\"tree\"\n tabindex=\"0\"\n aria-multiselectable=\"true\"\n :aria-label=\"state.messages.outline.label\"\n :aria-activedescendant=\"active\"\n @keydown=\"state.keydown($event, view)\"\n >\n <OutlineRow\n v-for=\"row in rows\"\n :key=\"row.node.id\"\n :node=\"row.node\"\n :level=\"row.level\"\n :position=\"row.position\"\n :size=\"row.size\"\n :container=\"row.container\"\n :open=\"row.container && !folded.has(row.node.id)\"\n :selected=\"state.selected.value.has(row.node.id)\"\n @press=\"press($event, row.node.id)\"\n @toggle=\"toggle(row.node.id)\"\n />\n <div v-if=\"!rows.length\" class=\"dou-form-builder__outline-empty\">\n {{ state.messages.canvas.empty }}\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import palette_vue_vue_type_script_setup_true_lang_default from "./palette.vue_vue_type_script_setup_true_lang.js";
2
+ // src/builder/palette.vue
3
+ var palette_default = palette_vue_vue_type_script_setup_true_lang_default;
4
+ export { palette_default as default };
5
+
6
+ //# sourceMappingURL=palette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.js","names":["_sfc_main"],"sources":["../../src/builder/palette.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { DouInput } from 'dou-ui'\nimport { SearchIcon } from '@dou-ui/icons'\nimport type { FormFieldType } from '../fields/define'\nimport { formatMessage } from '../locale'\nimport { useBuilderState } from './context'\nimport { containerTypes, groupOf, iconOf, labelOf, paletteGroups } from './settings'\n\ndefineOptions({ name: 'DouFormBuilderPalette' })\n\nconst state = useBuilderState()\nconst query = ref('')\n\ninterface Entry {\n type: string\n label: string\n icon: FormFieldType['icon']\n}\n\nconst groups = computed(() => {\n const messages = state.messages\n const types = state.fields as Map<string, FormFieldType>\n const byGroup = new Map<string, Entry[]>()\n const add = (type: string, spec?: FormFieldType) => {\n const group = groupOf(type, spec)\n const list = byGroup.get(group) ?? []\n list.push({ type, label: labelOf(type, spec, messages), icon: iconOf(type, spec) })\n byGroup.set(group, list)\n }\n for (const [type, spec] of types) add(type, spec)\n for (const type of containerTypes) add(type)\n\n const search = query.value.trim().toLowerCase()\n const order = [\n ...paletteGroups,\n ...[...byGroup.keys()].filter((key) => !paletteGroups.includes(key)),\n ]\n return order\n .map((key) => ({\n key,\n label: messages.groups[key] ?? key,\n entries: (byGroup.get(key) ?? []).filter(\n (entry) =>\n !search ||\n entry.label.toLowerCase().includes(search) ||\n entry.type.toLowerCase().includes(search),\n ),\n }))\n .filter((group) => group.entries.length)\n})\n</script>\n\n<template>\n <div class=\"dou-form-builder__palette\">\n <div class=\"dou-form-builder__palette-search\">\n <DouInput\n v-model=\"query\"\n size=\"sm\"\n clearable\n :placeholder=\"state.messages.palette.search\"\n :aria-label=\"state.messages.palette.search\"\n >\n <template #prefix><SearchIcon /></template>\n </DouInput>\n </div>\n <div class=\"dou-form-builder__palette-body\">\n <section\n v-for=\"group in groups\"\n :key=\"group.key\"\n class=\"dou-form-builder__palette-group\"\n :aria-label=\"group.label\"\n >\n <div class=\"dou-form-builder__palette-heading\" aria-hidden=\"true\">{{ group.label }}</div>\n <div class=\"dou-form-builder__palette-items\">\n <button\n v-for=\"entry in group.entries\"\n :key=\"entry.type\"\n type=\"button\"\n class=\"dou-form-builder__palette-item\"\n :data-type=\"entry.type\"\n :aria-label=\"formatMessage(state.messages.palette.add, { label: entry.label })\"\n :disabled=\"state.readonly\"\n @click=\"!state.dragger.value?.swallowClick() && state.add(entry.type)\"\n @pointerdown=\"state.dragger.value?.pressType($event, entry.type, entry.label)\"\n >\n <component :is=\"entry.icon\" class=\"dou-form-builder__palette-icon\" />\n <span class=\"dou-form-builder__palette-label\">{{ entry.label }}</span>\n </button>\n </div>\n </section>\n <div v-if=\"!groups.length\" class=\"dou-form-builder__palette-empty\">\n {{ state.messages.palette.empty }}\n </div>\n </div>\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import { formatMessage } from "../locale.js";
2
+ import { containerTypes, groupOf, iconOf, labelOf, paletteGroups } from "./settings.js";
3
+ import { useBuilderState } from "./context.js";
4
+ import { DouInput } from "dou-ui";
5
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, openBlock, ref, renderList, resolveDynamicComponent, toDisplayString, unref, withCtx } from "vue";
6
+ import { SearchIcon } from "@dou-ui/icons";
7
+ // src/builder/palette.vue?vue&type=script&setup=true&lang.ts
8
+ var _hoisted_1 = { class: "dou-form-builder__palette" };
9
+ var _hoisted_2 = { class: "dou-form-builder__palette-search" };
10
+ var _hoisted_3 = { class: "dou-form-builder__palette-body" };
11
+ var _hoisted_4 = ["aria-label"];
12
+ var _hoisted_5 = {
13
+ class: "dou-form-builder__palette-heading",
14
+ "aria-hidden": "true"
15
+ };
16
+ var _hoisted_6 = { class: "dou-form-builder__palette-items" };
17
+ var _hoisted_7 = [
18
+ "data-type",
19
+ "aria-label",
20
+ "disabled",
21
+ "onClick",
22
+ "onPointerdown"
23
+ ];
24
+ var _hoisted_8 = { class: "dou-form-builder__palette-label" };
25
+ var _hoisted_9 = {
26
+ key: 0,
27
+ class: "dou-form-builder__palette-empty"
28
+ };
29
+ var palette_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
30
+ name: "DouFormBuilderPalette",
31
+ __name: "palette",
32
+ setup(__props) {
33
+ const state = useBuilderState();
34
+ const query = ref("");
35
+ const groups = computed(() => {
36
+ const messages = state.messages;
37
+ const types = state.fields;
38
+ const byGroup = /* @__PURE__ */ new Map();
39
+ const add = (type, spec) => {
40
+ const group = groupOf(type, spec);
41
+ const list = byGroup.get(group) ?? [];
42
+ list.push({
43
+ type,
44
+ label: labelOf(type, spec, messages),
45
+ icon: iconOf(type, spec)
46
+ });
47
+ byGroup.set(group, list);
48
+ };
49
+ for (const [type, spec] of types) add(type, spec);
50
+ for (const type of containerTypes) add(type);
51
+ const search = query.value.trim().toLowerCase();
52
+ return [...paletteGroups, ...[...byGroup.keys()].filter((key) => !paletteGroups.includes(key))].map((key) => ({
53
+ key,
54
+ label: messages.groups[key] ?? key,
55
+ entries: (byGroup.get(key) ?? []).filter((entry) => !search || entry.label.toLowerCase().includes(search) || entry.type.toLowerCase().includes(search))
56
+ })).filter((group) => group.entries.length);
57
+ });
58
+ return (_ctx, _cache) => {
59
+ return openBlock(), createElementBlock("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [createVNode(unref(DouInput), {
60
+ modelValue: query.value,
61
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => query.value = $event),
62
+ size: "sm",
63
+ clearable: "",
64
+ placeholder: unref(state).messages.palette.search,
65
+ "aria-label": unref(state).messages.palette.search
66
+ }, {
67
+ prefix: withCtx(() => [createVNode(unref(SearchIcon))]),
68
+ _: 1
69
+ }, 8, [
70
+ "modelValue",
71
+ "placeholder",
72
+ "aria-label"
73
+ ])]), createElementVNode("div", _hoisted_3, [(openBlock(true), createElementBlock(Fragment, null, renderList(groups.value, (group) => {
74
+ return openBlock(), createElementBlock("section", {
75
+ key: group.key,
76
+ class: "dou-form-builder__palette-group",
77
+ "aria-label": group.label
78
+ }, [createElementVNode("div", _hoisted_5, toDisplayString(group.label), 1), createElementVNode("div", _hoisted_6, [(openBlock(true), createElementBlock(Fragment, null, renderList(group.entries, (entry) => {
79
+ return openBlock(), createElementBlock("button", {
80
+ key: entry.type,
81
+ type: "button",
82
+ class: "dou-form-builder__palette-item",
83
+ "data-type": entry.type,
84
+ "aria-label": unref(formatMessage)(unref(state).messages.palette.add, { label: entry.label }),
85
+ disabled: unref(state).readonly,
86
+ onClick: ($event) => !unref(state).dragger.value?.swallowClick() && unref(state).add(entry.type),
87
+ onPointerdown: ($event) => unref(state).dragger.value?.pressType($event, entry.type, entry.label)
88
+ }, [(openBlock(), createBlock(resolveDynamicComponent(entry.icon), { class: "dou-form-builder__palette-icon" })), createElementVNode("span", _hoisted_8, toDisplayString(entry.label), 1)], 40, _hoisted_7);
89
+ }), 128))])], 8, _hoisted_4);
90
+ }), 128)), !groups.value.length ? (openBlock(), createElementBlock("div", _hoisted_9, toDisplayString(unref(state).messages.palette.empty), 1)) : createCommentVNode("", true)])]);
91
+ };
92
+ }
93
+ });
94
+ export { palette_vue_vue_type_script_setup_true_lang_default as default };
95
+
96
+ //# sourceMappingURL=palette.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_withCtx","_Fragment","_renderList","_toDisplayString","_createBlock","_resolveDynamicComponent","_createCommentVNode"],"sources":["../../src/builder/palette.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { DouInput } from 'dou-ui'\nimport { SearchIcon } from '@dou-ui/icons'\nimport type { FormFieldType } from '../fields/define'\nimport { formatMessage } from '../locale'\nimport { useBuilderState } from './context'\nimport { containerTypes, groupOf, iconOf, labelOf, paletteGroups } from './settings'\n\ndefineOptions({ name: 'DouFormBuilderPalette' })\n\nconst state = useBuilderState()\nconst query = ref('')\n\ninterface Entry {\n type: string\n label: string\n icon: FormFieldType['icon']\n}\n\nconst groups = computed(() => {\n const messages = state.messages\n const types = state.fields as Map<string, FormFieldType>\n const byGroup = new Map<string, Entry[]>()\n const add = (type: string, spec?: FormFieldType) => {\n const group = groupOf(type, spec)\n const list = byGroup.get(group) ?? []\n list.push({ type, label: labelOf(type, spec, messages), icon: iconOf(type, spec) })\n byGroup.set(group, list)\n }\n for (const [type, spec] of types) add(type, spec)\n for (const type of containerTypes) add(type)\n\n const search = query.value.trim().toLowerCase()\n const order = [\n ...paletteGroups,\n ...[...byGroup.keys()].filter((key) => !paletteGroups.includes(key)),\n ]\n return order\n .map((key) => ({\n key,\n label: messages.groups[key] ?? key,\n entries: (byGroup.get(key) ?? []).filter(\n (entry) =>\n !search ||\n entry.label.toLowerCase().includes(search) ||\n entry.type.toLowerCase().includes(search),\n ),\n }))\n .filter((group) => group.entries.length)\n})\n</script>\n\n<template>\n <div class=\"dou-form-builder__palette\">\n <div class=\"dou-form-builder__palette-search\">\n <DouInput\n v-model=\"query\"\n size=\"sm\"\n clearable\n :placeholder=\"state.messages.palette.search\"\n :aria-label=\"state.messages.palette.search\"\n >\n <template #prefix><SearchIcon /></template>\n </DouInput>\n </div>\n <div class=\"dou-form-builder__palette-body\">\n <section\n v-for=\"group in groups\"\n :key=\"group.key\"\n class=\"dou-form-builder__palette-group\"\n :aria-label=\"group.label\"\n >\n <div class=\"dou-form-builder__palette-heading\" aria-hidden=\"true\">{{ group.label }}</div>\n <div class=\"dou-form-builder__palette-items\">\n <button\n v-for=\"entry in group.entries\"\n :key=\"entry.type\"\n type=\"button\"\n class=\"dou-form-builder__palette-item\"\n :data-type=\"entry.type\"\n :aria-label=\"formatMessage(state.messages.palette.add, { label: entry.label })\"\n :disabled=\"state.readonly\"\n @click=\"!state.dragger.value?.swallowClick() && state.add(entry.type)\"\n @pointerdown=\"state.dragger.value?.pressType($event, entry.type, entry.label)\"\n >\n <component :is=\"entry.icon\" class=\"dou-form-builder__palette-icon\" />\n <span class=\"dou-form-builder__palette-label\">{{ entry.label }}</span>\n </button>\n </div>\n </section>\n <div v-if=\"!groups.length\" class=\"dou-form-builder__palette-empty\">\n {{ state.messages.palette.empty }}\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import preview_vue_vue_type_script_setup_true_lang_default from "./preview.vue_vue_type_script_setup_true_lang.js";
2
+ // src/builder/preview.vue
3
+ var preview_default = preview_vue_vue_type_script_setup_true_lang_default;
4
+ export { preview_default as default };
5
+
6
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","names":["_sfc_main"],"sources":["../../src/builder/preview.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, reactive, shallowRef, watch } from 'vue'\nimport { DouButton, DouSegmentedControl } from 'dou-ui'\nimport type { FormModel } from 'dou-ui'\nimport { CheckCircleIcon, MonitorIcon, SmartphoneIcon, TabletIcon } from '@dou-ui/icons'\nimport FormRenderer from '../renderer/form-renderer.vue'\nimport { useBuilderState } from './context'\n\n/**\n * The form as people will fill it in, at the width of a desktop, a tablet\n * or a phone. The values are kept while the form is edited, so switching\n * back and forth does not lose them; submitting shows what would be sent.\n */\ndefineOptions({ name: 'DouFormBuilderPreview' })\n\ntype Device = 'desktop' | 'tablet' | 'phone'\n\nconst state = useBuilderState()\nconst messages = computed(() => state.messages.preview)\nconst fields = computed(() => [...state.fields.values()])\n\nconst device = shallowRef<Device>('desktop')\nconst widths: Record<Device, number | undefined> = { desktop: undefined, tablet: 768, phone: 375 }\nconst icons = { desktop: MonitorIcon, tablet: TabletIcon, phone: SmartphoneIcon }\nconst devices = computed(() =>\n (['desktop', 'tablet', 'phone'] as const).map((value) => ({\n value,\n label: messages.value.devices[value],\n })),\n)\n\nconst model = reactive<FormModel>({})\nconst result = shallowRef<FormModel | null>(null)\nwatch(\n () => state.document,\n () => (result.value = null),\n)\nconst shown = computed(() => (result.value ? JSON.stringify(result.value, null, 2) : ''))\n</script>\n\n<template>\n <div class=\"dou-form-builder__preview\" role=\"region\" :aria-label=\"messages.label\">\n <div class=\"dou-form-builder__preview-bar\">\n <DouSegmentedControl\n v-model=\"device\"\n :options=\"devices\"\n :aria-label=\"messages.device\"\n size=\"sm\"\n class=\"dou-form-builder__preview-devices\"\n >\n <template #option=\"{ option }\">\n <span class=\"dou-form-builder__preview-device\">\n <component :is=\"icons[option.value as Device]\" />\n <span>{{ option.label }}</span>\n </span>\n </template>\n </DouSegmentedControl>\n </div>\n <div class=\"dou-form-builder__preview-scroll\">\n <div\n class=\"dou-form-builder__preview-frame\"\n :data-device=\"device\"\n :style=\"widths[device] ? { width: `${widths[device]}px` } : undefined\"\n >\n <FormRenderer\n :form=\"state.document\"\n :model=\"model\"\n :fields=\"fields\"\n :functions=\"state.functions()\"\n :sources=\"state.sources()\"\n :validators=\"state.validators()\"\n @submit=\"result = $event\"\n @invalid=\"result = null\"\n @reset=\"result = null\"\n >\n <div class=\"dou-form-builder__preview-footer\">\n <DouButton native-type=\"submit\" tone=\"primary\">{{ messages.submit }}</DouButton>\n <DouButton native-type=\"reset\" variant=\"outline\">{{ messages.reset }}</DouButton>\n </div>\n </FormRenderer>\n <div v-if=\"result\" class=\"dou-form-builder__preview-result\" role=\"status\">\n <div class=\"dou-form-builder__preview-result-title\">\n <CheckCircleIcon />\n <span>{{ messages.passed }}</span>\n </div>\n <pre class=\"dou-form-builder__preview-result-code\">{{ shown }}</pre>\n </div>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,111 @@
1
+ import form_renderer_default from "../renderer/form-renderer.js";
2
+ import { useBuilderState } from "./context.js";
3
+ import { DouButton, DouSegmentedControl } from "dou-ui";
4
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeStyle, openBlock, reactive, resolveDynamicComponent, shallowRef, toDisplayString, unref, watch, withCtx } from "vue";
5
+ import { CheckCircleIcon, MonitorIcon, SmartphoneIcon, TabletIcon } from "@dou-ui/icons";
6
+ // src/builder/preview.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1 = ["aria-label"];
8
+ var _hoisted_2 = { class: "dou-form-builder__preview-bar" };
9
+ var _hoisted_3 = { class: "dou-form-builder__preview-device" };
10
+ var _hoisted_4 = { class: "dou-form-builder__preview-scroll" };
11
+ var _hoisted_5 = ["data-device"];
12
+ var _hoisted_6 = { class: "dou-form-builder__preview-footer" };
13
+ var _hoisted_7 = {
14
+ key: 0,
15
+ class: "dou-form-builder__preview-result",
16
+ role: "status"
17
+ };
18
+ var _hoisted_8 = { class: "dou-form-builder__preview-result-title" };
19
+ var _hoisted_9 = { class: "dou-form-builder__preview-result-code" };
20
+ var preview_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
21
+ name: "DouFormBuilderPreview",
22
+ __name: "preview",
23
+ setup(__props) {
24
+ const state = useBuilderState();
25
+ const messages = computed(() => state.messages.preview);
26
+ const fields = computed(() => [...state.fields.values()]);
27
+ const device = shallowRef("desktop");
28
+ const widths = {
29
+ desktop: void 0,
30
+ tablet: 768,
31
+ phone: 375
32
+ };
33
+ const icons = {
34
+ desktop: MonitorIcon,
35
+ tablet: TabletIcon,
36
+ phone: SmartphoneIcon
37
+ };
38
+ const devices = computed(() => [
39
+ "desktop",
40
+ "tablet",
41
+ "phone"
42
+ ].map((value) => ({
43
+ value,
44
+ label: messages.value.devices[value]
45
+ })));
46
+ const model = reactive({});
47
+ const result = shallowRef(null);
48
+ watch(() => state.document, () => result.value = null);
49
+ const shown = computed(() => result.value ? JSON.stringify(result.value, null, 2) : "");
50
+ return (_ctx, _cache) => {
51
+ return openBlock(), createElementBlock("div", {
52
+ class: "dou-form-builder__preview",
53
+ role: "region",
54
+ "aria-label": messages.value.label
55
+ }, [createElementVNode("div", _hoisted_2, [createVNode(unref(DouSegmentedControl), {
56
+ modelValue: device.value,
57
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => device.value = $event),
58
+ options: devices.value,
59
+ "aria-label": messages.value.device,
60
+ size: "sm",
61
+ class: "dou-form-builder__preview-devices"
62
+ }, {
63
+ option: withCtx(({ option }) => [createElementVNode("span", _hoisted_3, [(openBlock(), createBlock(resolveDynamicComponent(icons[option.value]))), createElementVNode("span", null, toDisplayString(option.label), 1)])]),
64
+ _: 1
65
+ }, 8, [
66
+ "modelValue",
67
+ "options",
68
+ "aria-label"
69
+ ])]), createElementVNode("div", _hoisted_4, [createElementVNode("div", {
70
+ class: "dou-form-builder__preview-frame",
71
+ "data-device": device.value,
72
+ style: normalizeStyle(widths[device.value] ? { width: `${widths[device.value]}px` } : void 0)
73
+ }, [createVNode(form_renderer_default, {
74
+ form: unref(state).document,
75
+ model,
76
+ fields: fields.value,
77
+ functions: unref(state).functions(),
78
+ sources: unref(state).sources(),
79
+ validators: unref(state).validators(),
80
+ onSubmit: _cache[1] || (_cache[1] = ($event) => result.value = $event),
81
+ onInvalid: _cache[2] || (_cache[2] = ($event) => result.value = null),
82
+ onReset: _cache[3] || (_cache[3] = ($event) => result.value = null)
83
+ }, {
84
+ default: withCtx(() => [createElementVNode("div", _hoisted_6, [createVNode(unref(DouButton), {
85
+ "native-type": "submit",
86
+ tone: "primary"
87
+ }, {
88
+ default: withCtx(() => [createTextVNode(toDisplayString(messages.value.submit), 1)]),
89
+ _: 1
90
+ }), createVNode(unref(DouButton), {
91
+ "native-type": "reset",
92
+ variant: "outline"
93
+ }, {
94
+ default: withCtx(() => [createTextVNode(toDisplayString(messages.value.reset), 1)]),
95
+ _: 1
96
+ })])]),
97
+ _: 1
98
+ }, 8, [
99
+ "form",
100
+ "model",
101
+ "fields",
102
+ "functions",
103
+ "sources",
104
+ "validators"
105
+ ]), result.value ? (openBlock(), createElementBlock("div", _hoisted_7, [createElementVNode("div", _hoisted_8, [createVNode(unref(CheckCircleIcon)), createElementVNode("span", null, toDisplayString(messages.value.passed), 1)]), createElementVNode("pre", _hoisted_9, toDisplayString(shown.value), 1)])) : createCommentVNode("", true)], 12, _hoisted_5)])], 8, _hoisted_1);
106
+ };
107
+ }
108
+ });
109
+ export { preview_vue_vue_type_script_setup_true_lang_default as default };
110
+
111
+ //# sourceMappingURL=preview.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_withCtx","_createBlock","_resolveDynamicComponent","_toDisplayString","_normalizeStyle","FormRenderer","_createTextVNode","_createCommentVNode"],"sources":["../../src/builder/preview.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, reactive, shallowRef, watch } from 'vue'\nimport { DouButton, DouSegmentedControl } from 'dou-ui'\nimport type { FormModel } from 'dou-ui'\nimport { CheckCircleIcon, MonitorIcon, SmartphoneIcon, TabletIcon } from '@dou-ui/icons'\nimport FormRenderer from '../renderer/form-renderer.vue'\nimport { useBuilderState } from './context'\n\n/**\n * The form as people will fill it in, at the width of a desktop, a tablet\n * or a phone. The values are kept while the form is edited, so switching\n * back and forth does not lose them; submitting shows what would be sent.\n */\ndefineOptions({ name: 'DouFormBuilderPreview' })\n\ntype Device = 'desktop' | 'tablet' | 'phone'\n\nconst state = useBuilderState()\nconst messages = computed(() => state.messages.preview)\nconst fields = computed(() => [...state.fields.values()])\n\nconst device = shallowRef<Device>('desktop')\nconst widths: Record<Device, number | undefined> = { desktop: undefined, tablet: 768, phone: 375 }\nconst icons = { desktop: MonitorIcon, tablet: TabletIcon, phone: SmartphoneIcon }\nconst devices = computed(() =>\n (['desktop', 'tablet', 'phone'] as const).map((value) => ({\n value,\n label: messages.value.devices[value],\n })),\n)\n\nconst model = reactive<FormModel>({})\nconst result = shallowRef<FormModel | null>(null)\nwatch(\n () => state.document,\n () => (result.value = null),\n)\nconst shown = computed(() => (result.value ? JSON.stringify(result.value, null, 2) : ''))\n</script>\n\n<template>\n <div class=\"dou-form-builder__preview\" role=\"region\" :aria-label=\"messages.label\">\n <div class=\"dou-form-builder__preview-bar\">\n <DouSegmentedControl\n v-model=\"device\"\n :options=\"devices\"\n :aria-label=\"messages.device\"\n size=\"sm\"\n class=\"dou-form-builder__preview-devices\"\n >\n <template #option=\"{ option }\">\n <span class=\"dou-form-builder__preview-device\">\n <component :is=\"icons[option.value as Device]\" />\n <span>{{ option.label }}</span>\n </span>\n </template>\n </DouSegmentedControl>\n </div>\n <div class=\"dou-form-builder__preview-scroll\">\n <div\n class=\"dou-form-builder__preview-frame\"\n :data-device=\"device\"\n :style=\"widths[device] ? { width: `${widths[device]}px` } : undefined\"\n >\n <FormRenderer\n :form=\"state.document\"\n :model=\"model\"\n :fields=\"fields\"\n :functions=\"state.functions()\"\n :sources=\"state.sources()\"\n :validators=\"state.validators()\"\n @submit=\"result = $event\"\n @invalid=\"result = null\"\n @reset=\"result = null\"\n >\n <div class=\"dou-form-builder__preview-footer\">\n <DouButton native-type=\"submit\" tone=\"primary\">{{ messages.submit }}</DouButton>\n <DouButton native-type=\"reset\" variant=\"outline\">{{ messages.reset }}</DouButton>\n </div>\n </FormRenderer>\n <div v-if=\"result\" class=\"dou-form-builder__preview-result\" role=\"status\">\n <div class=\"dou-form-builder__preview-result-title\">\n <CheckCircleIcon />\n <span>{{ messages.passed }}</span>\n </div>\n <pre class=\"dou-form-builder__preview-result-code\">{{ shown }}</pre>\n </div>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAiBA,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,WAAW,eAAe,MAAM,SAAS,OAAO;EACtD,MAAM,SAAS,eAAe,CAAC,GAAG,MAAM,OAAO,OAAO,CAAC,CAAC;EAExD,MAAM,SAAS,WAAmB,SAAS;EAC3C,MAAM,SAA6C;GAAE,SAAS,KAAA;GAAW,QAAQ;GAAK,OAAO;EAAI;EACjG,MAAM,QAAQ;GAAE,SAAS;GAAa,QAAQ;GAAY,OAAO;EAAe;EAChF,MAAM,UAAU,eACb;GAAC;GAAW;GAAU;EAAO,CAAC,CAAW,KAAK,WAAW;GACxD;GACA,OAAO,SAAS,MAAM,QAAQ;EAChC,EAAE,CACJ;EAEA,MAAM,QAAQ,SAAoB,CAAC,CAAC;EACpC,MAAM,SAAS,WAA6B,IAAI;EAChD,YACQ,MAAM,gBACL,OAAO,QAAQ,IACxB;EACA,MAAM,QAAQ,eAAgB,OAAO,QAAQ,KAAK,UAAU,OAAO,OAAO,MAAM,CAAC,IAAI,EAAG;;GAItF,OAAAA,UAAA,GAAAC,mBAgDM,OAAA;IAhDD,OAAM;IAA4B,MAAK;IAAU,cAAY,SAAA,MAAS;G,IACzEC,mBAeM,OAfN,YAeM,CAdJC,YAasBC,MAAA,mBAAA,GAAA;IAZX,YAAA,OAAA;IAAA,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,OAAM,QAAA;IACd,SAAS,QAAA;IACT,cAAY,SAAA,MAAS;IACtB,MAAK;IACL,OAAM;;IAEK,QAAMC,SAIR,EAJY,aAAM,CACzBH,mBAGO,QAHP,YAGO,EAFLF,UAAA,GAAAM,YAAiDC,wBAAjC,MAAM,OAAO,MAAK,CAAA,IAClCL,mBAA+B,QAAA,MAAAM,gBAAtB,OAAO,KAAK,GAAA,CAAA,C;;;;;;G,MAK7BN,mBA8BM,OA9BN,YA8BM,CA7BJA,mBA4BM,OAAA;IA3BJ,OAAM;IACL,eAAa,OAAA;IACb,OAAKO,eAAE,OAAO,OAAA,SAAM,EAAA,OAAA,GAAgB,OAAO,OAAA,OAAM,IAAA,IAAU,KAAA,CAAS;G,IAErEN,YAeeO,uBAAA;IAdZ,MAAMN,MAAA,KAAA,CAAK,CAAC;IACL;IACP,QAAQ,OAAA;IACR,WAAWA,MAAA,KAAA,CAAK,CAAC,UAAS;IAC1B,SAASA,MAAA,KAAA,CAAK,CAAC,QAAO;IACtB,YAAYA,MAAA,KAAA,CAAK,CAAC,WAAU;IAC5B,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAS;IACjB,WAAO,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAM;IACf,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAM;;IAEd,SAAAC,cAGM,CAHNH,mBAGM,OAHN,YAGM,CAFJC,YAAgFC,MAAA,SAAA,GAAA;KAArE,eAAY;KAAS,MAAK;;KAAU,SAAAC,cAAqB,CAAlBM,gBAAAH,gBAAA,SAAA,MAAS,MAAM,GAAA,CAAA,C;;I,IACjEL,YAAiFC,MAAA,SAAA,GAAA;KAAtE,eAAY;KAAQ,SAAQ;;KAAU,SAAAC,cAAoB,CAAjBM,gBAAAH,gBAAA,SAAA,MAAS,KAAK,GAAA,CAAA,C;;;;;;;;;;;G,IAG3D,OAAA,SAAXR,UAAA,GAAAC,mBAMM,OANN,YAMM,CALJC,mBAGM,OAHN,YAGM,CAFJC,YAAmBC,MAAA,eAAA,CAAA,GACnBF,mBAAkC,QAAA,MAAAM,gBAAzB,SAAA,MAAS,MAAM,GAAA,CAAA,C,IAE1BN,mBAAoE,OAApE,YAAoEM,gBAAd,MAAA,KAAK,GAAA,CAAA,C"}
@@ -0,0 +1,57 @@
1
+ import { cloneValue, getPath, isList, setPath, unsetPath, valueNodes } from "../core/document.js";
2
+ import { equalValues, isEmptyValue } from "../core/expression.js";
3
+ import { startValue } from "../core/values.js";
4
+ import { reactive, shallowRef, watch } from "vue";
5
+ // src/builder/settings-binding.ts
6
+ /**
7
+ * Edit an object (a node, its `props`, or the document's settings)
8
+ * through a settings form. The form's values start as a copy of the
9
+ * object; when one changes, the keys it touches are handed to `apply`
10
+ * as a patch. A setting set back to what leaving it out means (its
11
+ * `default`, or empty) is left out of the object again.
12
+ */
13
+ function useSettingsBinding(options) {
14
+ const model = shallowRef(reactive({}));
15
+ /** The object the last patch made, which the form already shows. */
16
+ let own;
17
+ watch([options.source, () => options.key?.()], ([data, key], previous) => {
18
+ if (data && data === own && previous?.[1] === key) return;
19
+ own = void 0;
20
+ const copy = cloneValue(data ?? {});
21
+ model.value = reactive(options.prepare ? options.prepare(copy) : copy);
22
+ }, { immediate: true });
23
+ /** What leaving a setting out means: its default, else its type's empty value. */
24
+ function fallback(node) {
25
+ if (isList(node)) return node.default ?? [];
26
+ const type = options.fields().get(node.type);
27
+ return type && "name" in node ? startValue(type, node) : void 0;
28
+ }
29
+ watch(model, (values) => {
30
+ const data = options.source() ?? {};
31
+ const patch = {};
32
+ for (const node of valueNodes(options.form().nodes)) {
33
+ const path = node.name;
34
+ const next = getPath(values, path);
35
+ const before = getPath(data, path);
36
+ const base = fallback(node);
37
+ const now = next === void 0 ? base : next;
38
+ if (equalValues(now, before === void 0 ? base : before)) continue;
39
+ const drop = equalValues(now, base) || isEmptyValue(now);
40
+ const [key, ...rest] = path.split(".");
41
+ if (!rest.length) {
42
+ patch[key] = drop ? void 0 : cloneValue(now);
43
+ continue;
44
+ }
45
+ const top = key;
46
+ const holder = cloneValue((top in patch ? patch[top] : data[top]) ?? {});
47
+ if (drop) unsetPath(holder, rest.join("."));
48
+ else setPath(holder, rest.join("."), cloneValue(now));
49
+ patch[key] = isEmptyValue(holder) ? void 0 : holder;
50
+ }
51
+ if (Object.keys(patch).length) own = options.apply(patch);
52
+ }, { deep: true });
53
+ return model;
54
+ }
55
+ export { useSettingsBinding };
56
+
57
+ //# sourceMappingURL=settings-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-binding.js","names":[],"sources":["../../src/builder/settings-binding.ts"],"sourcesContent":["import { reactive, shallowRef, watch } from 'vue'\nimport type { Ref } from 'vue'\nimport { cloneValue, getPath, isList, setPath, unsetPath, valueNodes } from '../core/document'\nimport { equalValues, isEmptyValue } from '../core/expression'\nimport { startValue } from '../core/values'\nimport type { FormDocument, FormNode } from '../core/types'\nimport type { FormFieldType } from '../fields/define'\n\ntype Data = Record<string, unknown>\n\n/**\n * Edit an object (a node, its `props`, or the document's settings)\n * through a settings form. The form's values start as a copy of the\n * object; when one changes, the keys it touches are handed to `apply`\n * as a patch. A setting set back to what leaving it out means (its\n * `default`, or empty) is left out of the object again.\n */\nexport function useSettingsBinding(options: {\n source: () => Data | undefined\n /** What the object belongs to, e.g. the node's id: a new key shows a new copy even if the object is the same. */\n key?: () => unknown\n form: () => FormDocument\n fields: () => Map<string, FormFieldType>\n /** Prepare a copy of the object for the form, e.g. turn plain options into objects. */\n prepare?: (data: Data) => Data\n /** Write the patch; returns the object as it now is. */\n apply: (patch: Data) => Data | undefined\n}): Ref<Data> {\n const model = shallowRef<Data>(reactive({}))\n /** The object the last patch made, which the form already shows. */\n let own: Data | undefined\n\n watch(\n [options.source, () => options.key?.()],\n ([data, key], previous) => {\n if (data && data === own && previous?.[1] === key) return\n own = undefined\n const copy = cloneValue(data ?? {}) as Data\n model.value = reactive(options.prepare ? options.prepare(copy) : copy)\n },\n { immediate: true },\n )\n\n /** What leaving a setting out means: its default, else its type's empty value. */\n function fallback(node: FormNode) {\n if (isList(node)) return node.default ?? []\n const type = options.fields().get(node.type)\n return type && 'name' in node ? startValue(type, node) : undefined\n }\n\n watch(\n model,\n (values) => {\n const data = options.source() ?? {}\n const patch: Data = {}\n for (const node of valueNodes(options.form().nodes)) {\n const path = node.name!\n const next = getPath(values, path)\n const before = getPath(data, path)\n const base = fallback(node)\n const now = next === undefined ? base : next\n const was = before === undefined ? base : before\n if (equalValues(now, was)) continue\n const drop = equalValues(now, base) || isEmptyValue(now)\n const [key, ...rest] = path.split('.')\n if (!rest.length) {\n patch[key!] = drop ? undefined : cloneValue(now)\n continue\n }\n // A setting inside an object: copy the object and change only that key.\n const top = key!\n const holder = cloneValue((top in patch ? patch[top] : data[top]) ?? {}) as Data\n if (drop) unsetPath(holder, rest.join('.'))\n else setPath(holder, rest.join('.'), cloneValue(now))\n patch[key!] = isEmptyValue(holder) ? undefined : holder\n }\n if (Object.keys(patch).length) own = options.apply(patch)\n },\n { deep: true },\n )\n\n return model\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAgB,mBAAmB,SAUrB;CACZ,MAAM,QAAQ,WAAiB,SAAS,CAAC,CAAC,CAAC;;CAE3C,IAAI;CAEJ,MACE,CAAC,QAAQ,cAAc,QAAQ,MAAM,CAAC,IACrC,CAAC,MAAM,MAAM,aAAa;EACzB,IAAI,QAAQ,SAAS,OAAO,WAAW,OAAO,KAAK;EACnD,MAAM,KAAA;EACN,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC;EAClC,MAAM,QAAQ,SAAS,QAAQ,UAAU,QAAQ,QAAQ,IAAI,IAAI,IAAI;CACvE,GACA,EAAE,WAAW,KAAK,CACpB;;CAGA,SAAS,SAAS,MAAgB;EAChC,IAAI,OAAO,IAAI,GAAG,OAAO,KAAK,WAAW,CAAC;EAC1C,MAAM,OAAO,QAAQ,OAAO,CAAC,CAAC,IAAI,KAAK,IAAI;EAC3C,OAAO,QAAQ,UAAU,OAAO,WAAW,MAAM,IAAI,IAAI,KAAA;CAC3D;CAEA,MACE,QACC,WAAW;EACV,MAAM,OAAO,QAAQ,OAAO,KAAK,CAAC;EAClC,MAAM,QAAc,CAAC;EACrB,KAAK,MAAM,QAAQ,WAAW,QAAQ,KAAK,CAAC,CAAC,KAAK,GAAG;GACnD,MAAM,OAAO,KAAK;GAClB,MAAM,OAAO,QAAQ,QAAQ,IAAI;GACjC,MAAM,SAAS,QAAQ,MAAM,IAAI;GACjC,MAAM,OAAO,SAAS,IAAI;GAC1B,MAAM,MAAM,SAAS,KAAA,IAAY,OAAO;GAExC,IAAI,YAAY,KADJ,WAAW,KAAA,IAAY,OAAO,MAClB,GAAG;GAC3B,MAAM,OAAO,YAAY,KAAK,IAAI,KAAK,aAAa,GAAG;GACvD,MAAM,CAAC,KAAK,GAAG,QAAQ,KAAK,MAAM,GAAG;GACrC,IAAI,CAAC,KAAK,QAAQ;IAChB,MAAM,OAAQ,OAAO,KAAA,IAAY,WAAW,GAAG;IAC/C;GACF;GAEA,MAAM,MAAM;GACZ,MAAM,SAAS,YAAY,OAAO,QAAQ,MAAM,OAAO,KAAK,SAAS,CAAC,CAAC;GACvE,IAAI,MAAM,UAAU,QAAQ,KAAK,KAAK,GAAG,CAAC;QACrC,QAAQ,QAAQ,KAAK,KAAK,GAAG,GAAG,WAAW,GAAG,CAAC;GACpD,MAAM,OAAQ,aAAa,MAAM,IAAI,KAAA,IAAY;EACnD;EACA,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,QAAQ,MAAM,QAAQ,MAAM,KAAK;CAC1D,GACA,EAAE,MAAM,KAAK,CACf;CAEA,OAAO;AACT"}