@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.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/dist/builder/canvas.js +695 -0
- package/dist/builder/canvas.js.map +1 -0
- package/dist/builder/completion.js +49 -0
- package/dist/builder/completion.js.map +1 -0
- package/dist/builder/condition-editor.js +6 -0
- package/dist/builder/condition-editor.js.map +1 -0
- package/dist/builder/condition-editor.vue_vue_type_script_setup_true_lang.js +410 -0
- package/dist/builder/condition-editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/condition.js +159 -0
- package/dist/builder/condition.js.map +1 -0
- package/dist/builder/context.js +477 -0
- package/dist/builder/context.js.map +1 -0
- package/dist/builder/core/commands.js +373 -0
- package/dist/builder/core/commands.js.map +1 -0
- package/dist/builder/core/history.js +76 -0
- package/dist/builder/core/history.js.map +1 -0
- package/dist/builder/core/transaction.js +298 -0
- package/dist/builder/core/transaction.js.map +1 -0
- package/dist/builder/drag.js +466 -0
- package/dist/builder/drag.js.map +1 -0
- package/dist/builder/expression-box.js +19 -0
- package/dist/builder/expression-box.js.map +1 -0
- package/dist/builder/expression-input.js +6 -0
- package/dist/builder/expression-input.js.map +1 -0
- package/dist/builder/expression-input.vue_vue_type_script_setup_true_lang.js +117 -0
- package/dist/builder/expression-input.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/extension.js +47 -0
- package/dist/builder/extension.js.map +1 -0
- package/dist/builder/form-builder.js +6 -0
- package/dist/builder/form-builder.js.map +1 -0
- package/dist/builder/form-builder.vue_vue_type_script_setup_true_lang.js +450 -0
- package/dist/builder/form-builder.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/index.js +22 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/builder/inspector.js +6 -0
- package/dist/builder/inspector.js.map +1 -0
- package/dist/builder/inspector.vue_vue_type_script_setup_true_lang.js +442 -0
- package/dist/builder/inspector.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/json-dialog.js +6 -0
- package/dist/builder/json-dialog.js.map +1 -0
- package/dist/builder/json-dialog.vue_vue_type_script_setup_true_lang.js +281 -0
- package/dist/builder/json-dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/locale.js +1271 -0
- package/dist/builder/locale.js.map +1 -0
- package/dist/builder/logic-fields.js +18 -0
- package/dist/builder/logic-fields.js.map +1 -0
- package/dist/builder/logic.js +332 -0
- package/dist/builder/logic.js.map +1 -0
- package/dist/builder/menu.js +92 -0
- package/dist/builder/menu.js.map +1 -0
- package/dist/builder/outline-row.js +6 -0
- package/dist/builder/outline-row.js.map +1 -0
- package/dist/builder/outline-row.vue_vue_type_script_setup_true_lang.js +88 -0
- package/dist/builder/outline-row.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/outline.js +6 -0
- package/dist/builder/outline.js.map +1 -0
- package/dist/builder/outline.vue_vue_type_script_setup_true_lang.js +111 -0
- package/dist/builder/outline.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/palette.js +6 -0
- package/dist/builder/palette.js.map +1 -0
- package/dist/builder/palette.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/builder/palette.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/preview.js +6 -0
- package/dist/builder/preview.js.map +1 -0
- package/dist/builder/preview.vue_vue_type_script_setup_true_lang.js +111 -0
- package/dist/builder/preview.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/builder/settings-binding.js +57 -0
- package/dist/builder/settings-binding.js.map +1 -0
- package/dist/builder/settings.js +487 -0
- package/dist/builder/settings.js.map +1 -0
- package/dist/builder/span-field.js +104 -0
- package/dist/builder/span-field.js.map +1 -0
- package/dist/code/expression-input.js +6 -0
- package/dist/code/expression-input.js.map +1 -0
- package/dist/code/expression-input.vue_vue_type_script_setup_true_lang.js +88 -0
- package/dist/code/expression-input.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/code/extension.js +202 -0
- package/dist/code/extension.js.map +1 -0
- package/dist/code/index.js +19 -0
- package/dist/code/index.js.map +1 -0
- package/dist/code/language.js +43 -0
- package/dist/code/language.js.map +1 -0
- package/dist/collaboration/index.js +382 -0
- package/dist/collaboration/index.js.map +1 -0
- package/dist/collaboration/order.js +88 -0
- package/dist/collaboration/order.js.map +1 -0
- package/dist/core/check.js +314 -0
- package/dist/core/check.js.map +1 -0
- package/dist/core/document.js +112 -0
- package/dist/core/document.js.map +1 -0
- package/dist/core/expression.js +854 -0
- package/dist/core/expression.js.map +1 -0
- package/dist/core/formulas.js +242 -0
- package/dist/core/formulas.js.map +1 -0
- package/dist/core/rules.js +83 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/values.js +80 -0
- package/dist/core/values.js.map +1 -0
- package/dist/fields/builtin.js +167 -0
- package/dist/fields/builtin.js.map +1 -0
- package/dist/fields/components.js +155 -0
- package/dist/fields/components.js.map +1 -0
- package/dist/fields/define.js +44 -0
- package/dist/fields/define.js.map +1 -0
- package/dist/fields/format.js +125 -0
- package/dist/fields/format.js.map +1 -0
- package/dist/fields/options.js +54 -0
- package/dist/fields/options.js.map +1 -0
- package/dist/index.js +20 -0
- package/dist/locale.js +161 -0
- package/dist/locale.js.map +1 -0
- package/dist/renderer/containers.js +430 -0
- package/dist/renderer/containers.js.map +1 -0
- package/dist/renderer/context.js +21 -0
- package/dist/renderer/context.js.map +1 -0
- package/dist/renderer/form-renderer.js +6 -0
- package/dist/renderer/form-renderer.js.map +1 -0
- package/dist/renderer/form-renderer.vue_vue_type_script_setup_true_lang.js +272 -0
- package/dist/renderer/form-renderer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/renderer/index.js +19 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/node-view.js +385 -0
- package/dist/renderer/node-view.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/style.d.ts +1 -0
- package/dist/style.js +27 -0
- package/dist/types/builder/canvas.d.ts +5 -0
- package/dist/types/builder/completion.d.ts +35 -0
- package/dist/types/builder/condition-editor.vue.d.ts +17 -0
- package/dist/types/builder/condition.d.ts +37 -0
- package/dist/types/builder/context.d.ts +149 -0
- package/dist/types/builder/core/commands.d.ts +74 -0
- package/dist/types/builder/core/history.d.ts +23 -0
- package/dist/types/builder/core/transaction.d.ts +108 -0
- package/dist/types/builder/drag.d.ts +52 -0
- package/dist/types/builder/expression-box.d.ts +8 -0
- package/dist/types/builder/expression-input.vue.d.ts +29 -0
- package/dist/types/builder/extension.d.ts +149 -0
- package/dist/types/builder/form-builder.vue.d.ts +24 -0
- package/dist/types/builder/index.d.ts +76 -0
- package/dist/types/builder/inspector.vue.d.ts +3 -0
- package/dist/types/builder/json-dialog.vue.d.ts +10 -0
- package/dist/types/builder/locale.d.ts +195 -0
- package/dist/types/builder/logic-fields.d.ts +2 -0
- package/dist/types/builder/logic.d.ts +57 -0
- package/dist/types/builder/menu.d.ts +23 -0
- package/dist/types/builder/outline-row.vue.d.ts +19 -0
- package/dist/types/builder/outline.vue.d.ts +3 -0
- package/dist/types/builder/palette.vue.d.ts +3 -0
- package/dist/types/builder/preview.vue.d.ts +3 -0
- package/dist/types/builder/settings-binding.d.ts +23 -0
- package/dist/types/builder/settings.d.ts +49 -0
- package/dist/types/builder/span-field.d.ts +4 -0
- package/dist/types/builder/types.d.ts +59 -0
- package/dist/types/code/expression-input.vue.d.ts +23 -0
- package/dist/types/code/extension.d.ts +45 -0
- package/dist/types/code/index.d.ts +12 -0
- package/dist/types/code/language.d.ts +6 -0
- package/dist/types/collaboration/index.d.ts +64 -0
- package/dist/types/collaboration/order.d.ts +20 -0
- package/dist/types/core/check.d.ts +68 -0
- package/dist/types/core/document.d.ts +36 -0
- package/dist/types/core/expression.d.ts +175 -0
- package/dist/types/core/formulas.d.ts +44 -0
- package/dist/types/core/rules.d.ts +23 -0
- package/dist/types/core/types.d.ts +211 -0
- package/dist/types/core/values.d.ts +22 -0
- package/dist/types/fields/builtin.d.ts +9 -0
- package/dist/types/fields/components.d.ts +177 -0
- package/dist/types/fields/define.d.ts +93 -0
- package/dist/types/fields/format.d.ts +76 -0
- package/dist/types/fields/options.d.ts +11 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/locale.d.ts +45 -0
- package/dist/types/renderer/containers.d.ts +20 -0
- package/dist/types/renderer/context.d.ts +186 -0
- package/dist/types/renderer/form-renderer.vue.d.ts +25 -0
- package/dist/types/renderer/index.d.ts +81 -0
- package/dist/types/renderer/node-view.d.ts +56 -0
- package/package.json +88 -4
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { builtinFunctions, checkExpression } from "../core/expression.js";
|
|
2
|
+
import { formatMessage } from "../locale.js";
|
|
3
|
+
import { useBuilderState } from "./context.js";
|
|
4
|
+
import { completeExpression } from "./completion.js";
|
|
5
|
+
import { DouAutocomplete } from "dou-ui";
|
|
6
|
+
import { computed, createCommentVNode, createElementBlock, createVNode, defineComponent, mergeProps, nextTick, openBlock, toDisplayString, unref, useId, useTemplateRef } from "vue";
|
|
7
|
+
// src/builder/expression-input.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
+
var expression_input_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
9
|
+
name: "DouFormBuilderExpression",
|
|
10
|
+
inheritAttrs: false,
|
|
11
|
+
__name: "expression-input",
|
|
12
|
+
props: {
|
|
13
|
+
modelValue: { default: "" },
|
|
14
|
+
names: { default: () => [] },
|
|
15
|
+
itemNames: { default: void 0 },
|
|
16
|
+
readsValue: { type: Boolean },
|
|
17
|
+
placeholder: { default: void 0 },
|
|
18
|
+
disabled: { type: Boolean },
|
|
19
|
+
readonly: { type: Boolean }
|
|
20
|
+
},
|
|
21
|
+
emits: ["update:modelValue"],
|
|
22
|
+
setup(__props, { emit: __emit }) {
|
|
23
|
+
const props = __props;
|
|
24
|
+
const emit = __emit;
|
|
25
|
+
const state = useBuilderState();
|
|
26
|
+
const root = useTemplateRef("root");
|
|
27
|
+
const errorId = `dou-form-builder-expression-${useId()}`;
|
|
28
|
+
const text = computed({
|
|
29
|
+
get: () => props.modelValue ?? "",
|
|
30
|
+
set: (value) => emit("update:modelValue", value)
|
|
31
|
+
});
|
|
32
|
+
const functions = computed(() => [.../* @__PURE__ */ new Set([...Object.keys(builtinFunctions), ...state.functionNames.value])]);
|
|
33
|
+
const problem = computed(() => {
|
|
34
|
+
const source = text.value;
|
|
35
|
+
if (!source.trim()) return void 0;
|
|
36
|
+
const found = checkExpression(source, {
|
|
37
|
+
fields: props.names.map((name) => name.path),
|
|
38
|
+
itemFields: props.itemNames ? props.itemNames.map((name) => name.path) : [],
|
|
39
|
+
functions: state.functionNames.value
|
|
40
|
+
})[0];
|
|
41
|
+
if (!found) return void 0;
|
|
42
|
+
const words = state.messages.expression.problems;
|
|
43
|
+
return formatMessage(words[found.code] ?? found.message, { text: found.text });
|
|
44
|
+
});
|
|
45
|
+
const input = () => root.value?.querySelector("textarea") ?? null;
|
|
46
|
+
/** The completions of the last fetch, by the text each one makes. */
|
|
47
|
+
let offered = /* @__PURE__ */ new Map();
|
|
48
|
+
function fetch(query) {
|
|
49
|
+
const caret = input()?.selectionStart ?? query.length;
|
|
50
|
+
const words = state.messages.expression;
|
|
51
|
+
const list = completeExpression(query, caret, {
|
|
52
|
+
names: props.names,
|
|
53
|
+
itemNames: props.itemNames,
|
|
54
|
+
value: props.readsValue,
|
|
55
|
+
functions: functions.value
|
|
56
|
+
}, {
|
|
57
|
+
values: words.values,
|
|
58
|
+
item: words.item,
|
|
59
|
+
value: words.value
|
|
60
|
+
});
|
|
61
|
+
offered = new Map(list.map((completion) => [completion.text, completion]));
|
|
62
|
+
return list.map((completion) => ({
|
|
63
|
+
value: completion.text,
|
|
64
|
+
label: completion.label,
|
|
65
|
+
description: completion.description
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
async function onSelect(option) {
|
|
69
|
+
const completion = offered.get(option.value);
|
|
70
|
+
await nextTick();
|
|
71
|
+
const element = input();
|
|
72
|
+
if (!completion || !element) return;
|
|
73
|
+
element.focus();
|
|
74
|
+
element.setSelectionRange(completion.caret, completion.caret);
|
|
75
|
+
}
|
|
76
|
+
return (_ctx, _cache) => {
|
|
77
|
+
return openBlock(), createElementBlock("div", {
|
|
78
|
+
ref_key: "root",
|
|
79
|
+
ref: root,
|
|
80
|
+
class: "dou-form-builder__expression"
|
|
81
|
+
}, [createVNode(unref(DouAutocomplete), mergeProps({
|
|
82
|
+
modelValue: text.value,
|
|
83
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => text.value = $event)
|
|
84
|
+
}, _ctx.$attrs, {
|
|
85
|
+
fetch,
|
|
86
|
+
debounce: 0,
|
|
87
|
+
highlight: false,
|
|
88
|
+
multiline: "",
|
|
89
|
+
disabled: __props.disabled,
|
|
90
|
+
readonly: __props.readonly,
|
|
91
|
+
invalid: !!problem.value,
|
|
92
|
+
placeholder: __props.placeholder,
|
|
93
|
+
"aria-invalid": problem.value ? "true" : void 0,
|
|
94
|
+
"aria-describedby": problem.value ? errorId : void 0,
|
|
95
|
+
spellcheck: "false",
|
|
96
|
+
autocomplete: "off",
|
|
97
|
+
class: "dou-form-builder__expression-input",
|
|
98
|
+
onSelect
|
|
99
|
+
}), null, 16, [
|
|
100
|
+
"modelValue",
|
|
101
|
+
"disabled",
|
|
102
|
+
"readonly",
|
|
103
|
+
"invalid",
|
|
104
|
+
"placeholder",
|
|
105
|
+
"aria-invalid",
|
|
106
|
+
"aria-describedby"
|
|
107
|
+
]), problem.value ? (openBlock(), createElementBlock("div", {
|
|
108
|
+
key: 0,
|
|
109
|
+
id: errorId,
|
|
110
|
+
class: "dou-form-builder__expression-error"
|
|
111
|
+
}, toDisplayString(problem.value), 1)) : createCommentVNode("", true)], 512);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
export { expression_input_vue_vue_type_script_setup_true_lang_default as default };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=expression-input.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression-input.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_openBlock","_createElementBlock","_createVNode","_unref","_mergeProps","_toDisplayString","_createCommentVNode"],"sources":["../../src/builder/expression-input.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, useId, useTemplateRef } from 'vue'\nimport { DouAutocomplete } from 'dou-ui'\nimport type { AutocompleteOption } from 'dou-ui'\nimport { builtinFunctions, checkExpression } from '../core/expression'\nimport { formatMessage } from '../locale'\nimport { completeExpression } from './completion'\nimport type { Completion } from './completion'\nimport { useBuilderState } from './context'\n\n/**\n * Expression text with completions of field paths and functions, and the\n * first problem of the expression shown under it as it is typed.\n */\ndefineOptions({ name: 'DouFormBuilderExpression', inheritAttrs: false })\n\ninterface Name {\n path: string\n label?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string | null\n /** The paths `values` can read. */\n names?: Name[]\n /** The paths `item` can read; left out where there is no list item. */\n itemNames?: Name[]\n /** Whether the expression can read `value`, as a check can. */\n readsValue?: boolean\n placeholder?: string\n disabled?: boolean\n /** Shown and selectable, but not changed; also when the form around is read-only. */\n readonly?: boolean\n }>(),\n { modelValue: '', names: () => [], itemNames: undefined, placeholder: undefined },\n)\nconst emit = defineEmits<{ 'update:modelValue': [value: string] }>()\n\nconst state = useBuilderState()\nconst root = useTemplateRef<HTMLElement>('root')\nconst errorId = `dou-form-builder-expression-${useId()}`\n\nconst text = computed({\n get: () => props.modelValue ?? '',\n set: (value: string) => emit('update:modelValue', value),\n})\n\nconst functions = computed(() => [\n ...new Set([...Object.keys(builtinFunctions), ...state.functionNames.value]),\n])\n\nconst problem = computed(() => {\n const source = text.value\n if (!source.trim()) return undefined\n const found = checkExpression(source, {\n fields: props.names.map((name) => name.path),\n itemFields: props.itemNames ? props.itemNames.map((name) => name.path) : [],\n functions: state.functionNames.value,\n })[0]\n if (!found) return undefined\n const words = state.messages.expression.problems\n return formatMessage(words[found.code] ?? found.message, { text: found.text })\n})\n\nconst input = () => root.value?.querySelector('textarea') ?? null\n\n/** The completions of the last fetch, by the text each one makes. */\nlet offered = new Map<string, Completion>()\n\nfunction fetch(query: string): AutocompleteOption[] {\n const element = input()\n const caret = element?.selectionStart ?? query.length\n const words = state.messages.expression\n const list = completeExpression(\n query,\n caret,\n {\n names: props.names,\n itemNames: props.itemNames,\n value: props.readsValue,\n functions: functions.value,\n },\n { values: words.values, item: words.item, value: words.value },\n )\n offered = new Map(list.map((completion) => [completion.text, completion]))\n return list.map((completion) => ({\n value: completion.text,\n label: completion.label,\n description: completion.description,\n }))\n}\n\nasync function onSelect(option: AutocompleteOption) {\n const completion = offered.get(option.value)\n await nextTick()\n const element = input()\n if (!completion || !element) return\n element.focus()\n element.setSelectionRange(completion.caret, completion.caret)\n}\n</script>\n\n<template>\n <div ref=\"root\" class=\"dou-form-builder__expression\">\n <DouAutocomplete\n v-model=\"text\"\n v-bind=\"$attrs\"\n :fetch=\"fetch\"\n :debounce=\"0\"\n :highlight=\"false\"\n multiline\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :invalid=\"!!problem\"\n :placeholder=\"placeholder\"\n :aria-invalid=\"problem ? 'true' : undefined\"\n :aria-describedby=\"problem ? errorId : undefined\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n class=\"dou-form-builder__expression-input\"\n @select=\"onSelect\"\n />\n <div v-if=\"problem\" :id=\"errorId\" class=\"dou-form-builder__expression-error\">\n {{ problem }}\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EAqBA,MAAM,QAAQ;EAgBd,MAAM,OAAO;EAEb,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,OAAO,eAA4B,MAAM;EAC/C,MAAM,UAAU,+BAA+B,MAAM;EAErD,MAAM,OAAO,SAAS;GACpB,WAAW,MAAM,cAAc;GAC/B,MAAM,UAAkB,KAAK,qBAAqB,KAAK;EACzD,CAAC;EAED,MAAM,YAAY,eAAe,CAC/B,mBAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,gBAAgB,GAAG,GAAG,MAAM,cAAc,KAAK,CAAC,CAC7E,CAAC;EAED,MAAM,UAAU,eAAe;GAC7B,MAAM,SAAS,KAAK;GACpB,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO,KAAA;GAC3B,MAAM,QAAQ,gBAAgB,QAAQ;IACpC,QAAQ,MAAM,MAAM,KAAK,SAAS,KAAK,IAAI;IAC3C,YAAY,MAAM,YAAY,MAAM,UAAU,KAAK,SAAS,KAAK,IAAI,IAAI,CAAC;IAC1E,WAAW,MAAM,cAAc;GACjC,CAAC,CAAC,CAAC;GACH,IAAI,CAAC,OAAO,OAAO,KAAA;GACnB,MAAM,QAAQ,MAAM,SAAS,WAAW;GACxC,OAAO,cAAc,MAAM,MAAM,SAAS,MAAM,SAAS,EAAE,MAAM,MAAM,KAAK,CAAC;EAC/E,CAAC;EAED,MAAM,cAAc,KAAK,OAAO,cAAc,UAAU,KAAK;;EAG7D,IAAI,0BAAU,IAAI,IAAwB;EAE1C,SAAS,MAAM,OAAqC;GAElD,MAAM,QADU,MACF,CAAA,EAAS,kBAAkB,MAAM;GAC/C,MAAM,QAAQ,MAAM,SAAS;GAC7B,MAAM,OAAO,mBACX,OACA,OACA;IACE,OAAO,MAAM;IACb,WAAW,MAAM;IACjB,OAAO,MAAM;IACb,WAAW,UAAU;GACvB,GACA;IAAE,QAAQ,MAAM;IAAQ,MAAM,MAAM;IAAM,OAAO,MAAM;GAAM,CAC/D;GACA,UAAU,IAAI,IAAI,KAAK,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC;GACzE,OAAO,KAAK,KAAK,gBAAgB;IAC/B,OAAO,WAAW;IAClB,OAAO,WAAW;IAClB,aAAa,WAAW;GAC1B,EAAE;EACJ;EAEA,eAAe,SAAS,QAA4B;GAClD,MAAM,aAAa,QAAQ,IAAI,OAAO,KAAK;GAC3C,MAAM,SAAS;GACf,MAAM,UAAU,MAAM;GACtB,IAAI,CAAC,cAAc,CAAC,SAAS;GAC7B,QAAQ,MAAM;GACd,QAAQ,kBAAkB,WAAW,OAAO,WAAW,KAAK;EAC9D;;GAIE,OAAAC,UAAA,GAAAC,mBAsBM,OAAA;IAtBG,SAAA;IAAJ,KAAI;IAAO,OAAM;G,IACpBC,YAiBEC,MAAA,eAAA,GAjBFC,WAiBE;IAhBS,YAAA,KAAA;IAAA,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,KAAI,QAAA;GACLL,GAAAA,KAAAA,QAAM;IACN;IACP,UAAU;IACV,WAAW;IACZ,WAAA;IACC,UAAU,QAAA;IACV,UAAU,QAAA;IACV,SAAO,CAAA,CAAI,QAAA;IACX,aAAa,QAAA;IACb,gBAAc,QAAA,QAAO,SAAY,KAAA;IACjC,oBAAkB,QAAA,QAAU,UAAU,KAAA;IACvC,YAAW;IACX,cAAa;IACb,OAAM;IACG;;;;;;;;;G,IAEA,QAAA,SAAXC,UAAA,GAAAC,mBAEM,OAAA;;IAFe,IAAI;IAAS,OAAM;GACnC,GAAAI,gBAAA,QAAA,KAAO,GAAA,CAAA,K"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/builder/extension.ts
|
|
2
|
+
/** Declares an extension; only gives it a type. */
|
|
3
|
+
function defineFormBuilderExtension(extension) {
|
|
4
|
+
return extension;
|
|
5
|
+
}
|
|
6
|
+
function flatten(list) {
|
|
7
|
+
const out = [];
|
|
8
|
+
for (const item of list) if (Array.isArray(item)) out.push(...flatten(item));
|
|
9
|
+
else out.push(item);
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
/** Sorts the extensions by priority and collects what they add. */
|
|
13
|
+
function resolveFormBuilderExtensions(list = []) {
|
|
14
|
+
const extensions = flatten(list).map((extension, index) => ({
|
|
15
|
+
extension,
|
|
16
|
+
index
|
|
17
|
+
})).sort((a, b) => (b.extension.priority ?? 100) - (a.extension.priority ?? 100) || a.index - b.index).map((entry) => entry.extension);
|
|
18
|
+
const names = /* @__PURE__ */ new Set();
|
|
19
|
+
for (const extension of extensions) {
|
|
20
|
+
if (names.has(extension.name)) throw new Error(`[@dou-ui/form-builder] the extension "${extension.name}" is added twice`);
|
|
21
|
+
names.add(extension.name);
|
|
22
|
+
}
|
|
23
|
+
const pick = (key) => extensions.flatMap((e) => e[key] ? [e[key]] : []);
|
|
24
|
+
return {
|
|
25
|
+
extensions,
|
|
26
|
+
fields: extensions.flatMap((e) => e.fields ?? []),
|
|
27
|
+
keymaps: pick("keymap"),
|
|
28
|
+
toolbars: pick("toolbar"),
|
|
29
|
+
menus: pick("menu"),
|
|
30
|
+
inspectors: pick("inspector"),
|
|
31
|
+
setups: pick("setup"),
|
|
32
|
+
marks: pick("marks"),
|
|
33
|
+
history: extensions.find((e) => e.history)?.history,
|
|
34
|
+
expressionInput: extensions.find((e) => e.expressionInput)?.expressionInput,
|
|
35
|
+
transactions: pick("transaction")
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The field types of a builder with extensions, for the renderer that
|
|
40
|
+
* shows its forms: the given ones, then the extensions'.
|
|
41
|
+
*/
|
|
42
|
+
function resolveFormBuilderFields(fields = [], extensions = []) {
|
|
43
|
+
return [...fields, ...resolveFormBuilderExtensions(extensions).fields];
|
|
44
|
+
}
|
|
45
|
+
export { defineFormBuilderExtension, resolveFormBuilderExtensions, resolveFormBuilderFields };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","names":[],"sources":["../../src/builder/extension.ts"],"sourcesContent":["import type { Component } from 'vue'\nimport type { FormNode } from '../core/types'\nimport type { FormFieldType } from '../fields/define'\nimport type { FormBuilderContext } from './context'\nimport type { FormBuilderTransaction } from './core/transaction'\n\n/** What an extension's hooks receive; the same as `useFormBuilder()`. */\nexport type FormBuilderExtensionContext = FormBuilderContext\n\n/**\n * A key handler: return `false` to let the key through to the next handler\n * and the builder's own keys; anything else means it was handled.\n */\nexport type FormBuilderKeyHandler = (event: KeyboardEvent) => boolean | void\n\n/** A button of the toolbar over the canvas. */\nexport interface FormBuilderToolbarItem {\n key: string\n /** Read out, and shown as the tooltip; shown beside the icon when there is no icon. */\n label: string\n icon?: Component\n run: () => void\n disabled?: boolean\n /** For a button that turns something on and off: whether it is on. */\n pressed?: boolean\n /**\n * The key that does the same, as a hotkey like `alt+r`: shown in the\n * tooltip and given to screen readers as `aria-keyshortcuts`. It is only\n * a hint; bind the key with `keymap`.\n */\n shortcut?: string\n}\n\n/** An item of a node's menu: its \"more\" button and the right-click menu. */\nexport interface FormBuilderMenuItem {\n key: string\n label: string\n icon?: Component\n run: () => void\n disabled?: boolean\n tone?: 'neutral' | 'danger'\n /** The key that does the same, as a hotkey like `alt+r`, shown at the end of the item. */\n shortcut?: string\n}\n\n/**\n * A mark on a node of the canvas and the outline, e.g. who else has it\n * selected: an outline in its color and a tag with its label.\n */\nexport interface FormBuilderNodeMark {\n key: string\n label: string\n /** Any CSS color; the text color when left out. */\n color?: string\n}\n\n/** An undo history that replaces the builder's own, e.g. one that only undoes this user's changes. */\nexport interface FormBuilderHistoryHandler {\n /** `false` when there was nothing to undo. */\n undo: () => boolean\n redo: () => boolean\n canUndo: () => boolean\n canRedo: () => boolean\n}\n\n/** A part of the inspector, under the built-in settings. */\nexport interface FormBuilderInspectorSection {\n key: string\n /** The heading over it. */\n title: string\n /**\n * Rendered with `props`; it can call `useFormBuilder()`. It is not made\n * read-only for you: read `useFormBuilder().readonly` and do not change\n * the form while it is `true`.\n */\n component: Component\n props?: Record<string, unknown>\n}\n\n/**\n * A piece of the builder: field types, keys, toolbar buttons, menu items,\n * parts of the inspector, and a say in every change.\n */\nexport interface FormBuilderExtension {\n name: string\n /** Higher runs first: its keys and hooks come before others. Defaults to 100. */\n priority?: number\n /**\n * Field types for the palette, besides the `fields` prop. Give the\n * renderer the same types (`resolveFormBuilderFields` collects them).\n */\n fields?: FormFieldType<any>[]\n /**\n * Replaces the text box for expressions (formulas, checks, conditions\n * written as text), e.g. with the code input of\n * `@dou-ui/form-builder/code`. It gets the built-in box's props:\n * `modelValue`, `names`, `itemNames`, `readsValue`, `placeholder`,\n * `disabled`, `readonly` (the builder's `readonly`), and emits\n * `update:modelValue`. The first one by priority\n * is used.\n */\n expressionInput?: Component\n /**\n * Keys, written as in `useHotkey` (e.g. `mod+e`, `shift+t`), while the\n * canvas or the outline has focus. They come before the builder's own.\n */\n keymap?: (context: FormBuilderExtensionContext) => Record<string, FormBuilderKeyHandler>\n /** Buttons after undo and redo, before the `toolbar` slot. */\n toolbar?: (context: FormBuilderExtensionContext) => FormBuilderToolbarItem[]\n /** Items for the menu of the selected nodes, after the built-in ones. */\n menu?: (context: FormBuilderExtensionContext, nodes: FormNode[]) => FormBuilderMenuItem[]\n /**\n * Parts of the inspector for a node, or the form when `node` is\n * `undefined`, after the built-in settings.\n */\n inspector?: (\n context: FormBuilderExtensionContext,\n node: FormNode | undefined,\n ) => FormBuilderInspectorSection[]\n /**\n * Marks for a node, read while the node renders: reactive state read\n * here updates them.\n */\n marks?: (context: FormBuilderExtensionContext, node: FormNode) => FormBuilderNodeMark[]\n /** Replaces the builder's undo history; the first extension that has one wins. */\n history?: (context: FormBuilderExtensionContext) => FormBuilderHistoryHandler\n /** Runs once the builder is mounted. Return a function to run when it unmounts. */\n setup?: (context: FormBuilderExtensionContext) => void | (() => void)\n /**\n * Sees every change before it reaches the document, in priority order,\n * and may add steps to it, e.g. to name new fields by a rule of the app.\n * Undo and redo do not come through here: they put back what was there.\n */\n transaction?: (tr: FormBuilderTransaction, context: FormBuilderExtensionContext) => void\n}\n\n/** Declares an extension; only gives it a type. */\nexport function defineFormBuilderExtension(extension: FormBuilderExtension): FormBuilderExtension {\n return extension\n}\n\n/** Everything the extensions contribute, gathered once per list. */\nexport interface ResolvedFormBuilderExtensions {\n extensions: FormBuilderExtension[]\n fields: FormFieldType<any>[]\n keymaps: NonNullable<FormBuilderExtension['keymap']>[]\n toolbars: NonNullable<FormBuilderExtension['toolbar']>[]\n menus: NonNullable<FormBuilderExtension['menu']>[]\n inspectors: NonNullable<FormBuilderExtension['inspector']>[]\n setups: NonNullable<FormBuilderExtension['setup']>[]\n marks: NonNullable<FormBuilderExtension['marks']>[]\n history: FormBuilderExtension['history']\n expressionInput: FormBuilderExtension['expressionInput']\n transactions: NonNullable<FormBuilderExtension['transaction']>[]\n}\n\ntype ExtensionList = readonly (FormBuilderExtension | readonly FormBuilderExtension[])[]\n\nfunction flatten(list: ExtensionList): FormBuilderExtension[] {\n const out: FormBuilderExtension[] = []\n for (const item of list) {\n if (Array.isArray(item)) out.push(...flatten(item))\n else out.push(item as FormBuilderExtension)\n }\n return out\n}\n\n/** Sorts the extensions by priority and collects what they add. */\nexport function resolveFormBuilderExtensions(\n list: ExtensionList = [],\n): ResolvedFormBuilderExtensions {\n const extensions = flatten(list)\n .map((extension, index) => ({ extension, index }))\n // oxlint-disable-next-line unicorn/no-array-sort -- sorts a fresh array\n .sort(\n (a, b) => (b.extension.priority ?? 100) - (a.extension.priority ?? 100) || a.index - b.index,\n )\n .map((entry) => entry.extension)\n\n const names = new Set<string>()\n for (const extension of extensions) {\n if (names.has(extension.name))\n throw new Error(`[@dou-ui/form-builder] the extension \"${extension.name}\" is added twice`)\n names.add(extension.name)\n }\n\n const pick = <K extends keyof FormBuilderExtension>(key: K) =>\n extensions.flatMap((e) => (e[key] ? [e[key]] : [])) as NonNullable<FormBuilderExtension[K]>[]\n\n return {\n extensions,\n fields: extensions.flatMap((e) => e.fields ?? []),\n keymaps: pick('keymap'),\n toolbars: pick('toolbar'),\n menus: pick('menu'),\n inspectors: pick('inspector'),\n setups: pick('setup'),\n marks: pick('marks'),\n history: extensions.find((e) => e.history)?.history,\n expressionInput: extensions.find((e) => e.expressionInput)?.expressionInput,\n transactions: pick('transaction'),\n }\n}\n\n/**\n * The field types of a builder with extensions, for the renderer that\n * shows its forms: the given ones, then the extensions'.\n */\nexport function resolveFormBuilderFields(\n fields: readonly FormFieldType<any>[] = [],\n extensions: ExtensionList = [],\n): FormFieldType<any>[] {\n return [...fields, ...resolveFormBuilderExtensions(extensions).fields]\n}\n"],"mappings":";;AAyIA,SAAgB,2BAA2B,WAAuD;CAChG,OAAO;AACT;AAmBA,SAAS,QAAQ,MAA6C;CAC5D,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,MACjB,IAAI,MAAM,QAAQ,IAAI,GAAG,IAAI,KAAK,GAAG,QAAQ,IAAI,CAAC;MAC7C,IAAI,KAAK,IAA4B;CAE5C,OAAO;AACT;;AAGA,SAAgB,6BACd,OAAsB,CAAC,GACQ;CAC/B,MAAM,aAAa,QAAQ,IAAI,CAAC,CAC7B,KAAK,WAAW,WAAW;EAAE;EAAW;CAAM,EAAE,CAAC,CAEjD,MACE,GAAG,OAAO,EAAE,UAAU,YAAY,QAAQ,EAAE,UAAU,YAAY,QAAQ,EAAE,QAAQ,EAAE,KACzF,CAAC,CACA,KAAK,UAAU,MAAM,SAAS;CAEjC,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,MAAM,IAAI,UAAU,IAAI,GAC1B,MAAM,IAAI,MAAM,yCAAyC,UAAU,KAAK,iBAAiB;EAC3F,MAAM,IAAI,UAAU,IAAI;CAC1B;CAEA,MAAM,QAA8C,QAClD,WAAW,SAAS,MAAO,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC,CAAE;CAEpD,OAAO;EACL;EACA,QAAQ,WAAW,SAAS,MAAM,EAAE,UAAU,CAAC,CAAC;EAChD,SAAS,KAAK,QAAQ;EACtB,UAAU,KAAK,SAAS;EACxB,OAAO,KAAK,MAAM;EAClB,YAAY,KAAK,WAAW;EAC5B,QAAQ,KAAK,OAAO;EACpB,OAAO,KAAK,OAAO;EACnB,SAAS,WAAW,MAAM,MAAM,EAAE,OAAO,CAAC,EAAE;EAC5C,iBAAiB,WAAW,MAAM,MAAM,EAAE,eAAe,CAAC,EAAE;EAC5D,cAAc,KAAK,aAAa;CAClC;AACF;;;;;AAMA,SAAgB,yBACd,SAAwC,CAAC,GACzC,aAA4B,CAAC,GACP;CACtB,OAAO,CAAC,GAAG,QAAQ,GAAG,6BAA6B,UAAU,CAAC,CAAC,MAAM;AACvE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import form_builder_vue_vue_type_script_setup_true_lang_default from "./form-builder.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
// src/builder/form-builder.vue
|
|
3
|
+
var form_builder_default = form_builder_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
export { form_builder_default as default };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=form-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-builder.js","names":["_sfc_main"],"sources":["../../src/builder/form-builder.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n shallowRef,\n useId,\n watch,\n} from 'vue'\nimport { DouDrawer, DouPopover, DouTabPanel, DouTabs, DouTooltip } from 'dou-ui'\nimport {\n BracesIcon,\n EyeIcon,\n ListChecksIcon,\n PanelLeftIcon,\n RedoIcon,\n SlidersIcon,\n UndoIcon,\n} from '@dou-ui/icons'\nimport { resolveFields } from '../fields/builtin'\nimport { createBuilderState, builderKey } from './context'\nimport type { FormBuilderMode } from './context'\nimport { resolveFormBuilderExtensions } from './extension'\nimport FormBuilderJson from './json-dialog.vue'\nimport FormBuilderPreview from './preview.vue'\nimport { useFormBuilderMessages } from './locale'\nimport { FormBuilderCanvas as FormBuilderCanvasComponent } from './canvas'\nimport type { HTMLAttributes } from 'vue'\nimport type { WithAttributes } from 'dou-ui'\nimport FormBuilderInspector from './inspector.vue'\nimport FormBuilderOutline from './outline.vue'\nimport FormBuilderPalette from './palette.vue'\nimport type {\n FormBuilderEmits,\n FormBuilderExpose,\n FormBuilderProps,\n FormBuilderSlots,\n} from './types'\n\n// 让模板里也能写这个组件转交的原生属性\nconst FormBuilderCanvas = FormBuilderCanvasComponent as WithAttributes<\n typeof FormBuilderCanvasComponent,\n HTMLAttributes\n>\n\ndefineOptions({ name: 'DouFormBuilder' })\n\nconst props = withDefaults(defineProps<FormBuilderProps>(), { readonly: false })\nconst emit = defineEmits<FormBuilderEmits>()\ndefineSlots<FormBuilderSlots>()\n\nconst extensions = computed(() => resolveFormBuilderExtensions(props.extensions))\nconst fields = computed(() => resolveFields([...(props.fields ?? []), ...extensions.value.fields]))\nconst messages = useFormBuilderMessages(() => props.messages)\n\n// The mode follows `v-model:mode` when it is bound, else the builder's own.\nconst ownMode = ref<FormBuilderMode>('design')\nconst mode = computed(() => props.mode ?? ownMode.value)\nconst previewing = computed(() => mode.value === 'preview')\n\nconst state = createBuilderState({\n uid: `dou-form-builder-${useId()}`,\n document: () => props.modelValue,\n selection: () => props.selection,\n fields: () => fields.value,\n messages: () => messages.value,\n emitDocument: (document, tr) => {\n emit('update:modelValue', document)\n emit('change', tr)\n },\n emitSelection: (ids) => emit('update:selection', ids),\n mode: () => mode.value,\n emitMode: (next) => {\n ownMode.value = next\n emit('update:mode', next)\n },\n functions: () => props.functions,\n sources: () => props.sources,\n validators: () => props.validators,\n readonly: () => props.readonly,\n extensions: () => extensions.value,\n})\nprovide(builderKey, state)\n\nconst jsonOpen = ref(false)\nconst toolbarItems = computed(() => extensions.value.toolbars.flatMap((toolbar) => toolbar(state)))\n\n// Extensions start once the builder is mounted, again when their hooks change, and stop when it goes.\nconst stops: (() => void)[] = []\nlet mounted = false\nlet setUp = false\nlet setupTimes: number[] = []\nlet setupWarned = false\n/** An extension made in the template is new on every render, and set up again each time. */\nfunction warnRepeatedSetup() {\n // the app's bundler replaces this, as for Vue's own warnings\n if (process.env.NODE_ENV === 'production' || setupWarned) return\n const now = Date.now()\n setupTimes = [...setupTimes.filter((time) => now - time < 1000), now]\n if (setupTimes.length < 4) return\n setupWarned = true\n console.warn(\n '[@dou-ui/form-builder] the extensions keep being set up again, which also clears undo each time. Create them once in <script setup> instead of in the template.',\n )\n}\nfunction teardownExtensions() {\n for (const stop of stops.splice(0)) stop()\n}\nfunction setupExtensions() {\n if (setUp) warnRepeatedSetup()\n setUp = true\n teardownExtensions()\n for (const setup of extensions.value.setups) {\n const stop = setup(state)\n if (typeof stop === 'function') stops.push(stop)\n }\n}\nonMounted(() => {\n mounted = true\n setupExtensions()\n})\n// A list written inline is new on every render; set up again only when the hooks change.\nwatch(\n () => [extensions.value.history, ...extensions.value.setups],\n (hooks, before) => {\n if (mounted && (hooks.length !== before.length || hooks.some((hook, i) => hook !== before[i])))\n setupExtensions()\n },\n)\nonBeforeUnmount(teardownExtensions)\n\nconst side = ref<'fields' | 'outline'>('fields')\n// A read-only builder has nothing to add, so its side panel is the outline alone.\nconst sideLabel = computed(() =>\n props.readonly ? messages.value.panels.outline : messages.value.panels.fields,\n)\nconst sideTitle = computed(() =>\n props.readonly ? messages.value.panels.outline : messages.value.panels.side,\n)\n// On narrow builders the fields and the outline open from the toolbar,\n// and close once a node is added or picked; focus moves to the canvas,\n// where the node now is.\nconst sideOpen = ref(false)\n\n// On phones (a builder narrower than 560px) the fields and the settings\n// are drawers from the bottom. The settings open at half height when a\n// node is picked, and can be pulled up.\nconst root = shallowRef<HTMLElement | null>(null)\nconst compact = ref(false)\nconst settingsOpen = ref(false)\nlet observer: ResizeObserver | undefined\nonMounted(() => {\n if (typeof ResizeObserver === 'undefined' || !root.value) return\n observer = new ResizeObserver(([entry]) => {\n compact.value = (entry?.contentRect.width ?? Infinity) < 560\n })\n observer.observe(root.value)\n})\nonBeforeUnmount(() => observer?.disconnect())\n// Picking several nodes by tapping is for phones only.\nwatch(compact, (on) => {\n if (!on) state.tapToggles.value = false\n})\n// A tap on a node (not a drag, and not a node just added) opens its settings.\nfunction onCanvasClick(event: MouseEvent) {\n if (!compact.value || state.tapToggles.value || state.dragger.value?.swallowClick()) return\n const node = (event.target as HTMLElement).closest('.dou-form-builder__node')\n if (!node || (event.target as HTMLElement).closest('.dou-form-builder__actions')) return\n void nextTick(() => {\n if (state.focused.value) settingsOpen.value = true\n })\n}\nwatch(\n () => state.selection,\n () => {\n if (!sideOpen.value) return\n sideOpen.value = false\n void nextTick(() => state.canvas.value?.focus({ preventScroll: true }))\n },\n)\n\nconst {\n transaction,\n dispatch,\n select,\n add,\n remove,\n duplicate,\n moveBy,\n moveTo,\n replace,\n wrap,\n addPage,\n selectParent,\n undo,\n redo,\n shownPage,\n setMode,\n} = state\ndefineExpose<FormBuilderExpose>({\n get document() {\n return state.document\n },\n get selection() {\n return state.selection\n },\n get fields() {\n return state.fields\n },\n get messages() {\n return state.messages\n },\n get canUndo() {\n return state.canUndo\n },\n get canRedo() {\n return state.canRedo\n },\n get readonly() {\n return state.readonly\n },\n get mode() {\n return state.mode\n },\n setMode,\n transaction,\n dispatch,\n select,\n add,\n remove,\n duplicate,\n moveBy,\n moveTo,\n replace,\n wrap,\n addPage,\n selectParent,\n undo,\n redo,\n shownPage,\n})\n</script>\n\n<template>\n <div\n ref=\"root\"\n :class=\"[\n 'dou-form-builder',\n compact && 'is-compact',\n previewing && 'is-previewing',\n readonly && 'is-readonly',\n ]\"\n >\n <slot>\n <div class=\"dou-form-builder__layout\">\n <aside v-if=\"!previewing\" class=\"dou-form-builder__side\" :aria-label=\"sideTitle\">\n <FormBuilderOutline v-if=\"readonly\" />\n <DouTabs v-else v-model=\"side\" size=\"sm\" class=\"dou-form-builder__side-tabs\">\n <DouTabPanel value=\"fields\" :label=\"messages.panels.fields\">\n <FormBuilderPalette />\n </DouTabPanel>\n <DouTabPanel value=\"outline\" :label=\"messages.panels.outline\">\n <FormBuilderOutline />\n </DouTabPanel>\n </DouTabs>\n </aside>\n <div class=\"dou-form-builder__main\">\n <div class=\"dou-form-builder__toolbar\" role=\"toolbar\">\n <template v-if=\"previewing\" />\n <button\n v-else-if=\"compact\"\n type=\"button\"\n class=\"dou-form-builder__tool dou-form-builder__side-toggle\"\n aria-haspopup=\"dialog\"\n @click=\"sideOpen = true\"\n >\n <PanelLeftIcon />\n <span>{{ sideLabel }}</span>\n </button>\n <DouPopover\n v-else\n v-model:open=\"sideOpen\"\n placement=\"bottom-start\"\n :aria-label=\"sideTitle\"\n class=\"dou-form-builder__side-popover\"\n >\n <button type=\"button\" class=\"dou-form-builder__tool dou-form-builder__side-toggle\">\n <PanelLeftIcon />\n <span>{{ sideLabel }}</span>\n </button>\n <template #content>\n <FormBuilderOutline v-if=\"readonly\" />\n <DouTabs v-else v-model=\"side\" size=\"sm\" class=\"dou-form-builder__side-tabs\">\n <DouTabPanel value=\"fields\" :label=\"messages.panels.fields\">\n <FormBuilderPalette />\n </DouTabPanel>\n <DouTabPanel value=\"outline\" :label=\"messages.panels.outline\">\n <FormBuilderOutline />\n </DouTabPanel>\n </DouTabs>\n </template>\n </DouPopover>\n <template v-if=\"!previewing && !readonly\">\n <DouTooltip :content=\"messages.actions.undo\" shortcut=\"mod+z\" placement=\"bottom\">\n <button\n type=\"button\"\n class=\"dou-form-builder__tool\"\n :disabled=\"!state.canUndo\"\n :aria-label=\"messages.actions.undo\"\n @click=\"state.undo()\"\n >\n <UndoIcon />\n </button>\n </DouTooltip>\n <DouTooltip\n :content=\"messages.actions.redo\"\n shortcut=\"mod+shift+z\"\n placement=\"bottom\"\n >\n <button\n type=\"button\"\n class=\"dou-form-builder__tool\"\n :disabled=\"!state.canRedo\"\n :aria-label=\"messages.actions.redo\"\n @click=\"state.redo()\"\n >\n <RedoIcon />\n </button>\n </DouTooltip>\n </template>\n <!-- A tool with its name on it needs a tip only for its key. -->\n <DouTooltip\n v-for=\"item in toolbarItems\"\n :key=\"item.key\"\n :content=\"item.label\"\n :shortcut=\"item.shortcut\"\n :disabled=\"!item.icon && !item.shortcut\"\n placement=\"bottom\"\n >\n <button\n type=\"button\"\n class=\"dou-form-builder__tool\"\n :disabled=\"item.disabled\"\n :aria-pressed=\"item.pressed\"\n :aria-label=\"item.icon ? item.label : undefined\"\n @click=\"item.run()\"\n >\n <component :is=\"item.icon\" v-if=\"item.icon\" />\n <span v-else>{{ item.label }}</span>\n </button>\n </DouTooltip>\n <slot name=\"toolbar\" />\n <span class=\"dou-form-builder__toolbar-spacer\" />\n <template v-if=\"compact && !previewing\">\n <DouTooltip :content=\"messages.actions.selectMany\" placement=\"bottom\">\n <button\n type=\"button\"\n class=\"dou-form-builder__tool dou-form-builder__select-toggle\"\n :aria-pressed=\"state.tapToggles.value\"\n :aria-label=\"messages.actions.selectMany\"\n @click=\"state.tapToggles.value = !state.tapToggles.value\"\n >\n <ListChecksIcon />\n </button>\n </DouTooltip>\n <DouTooltip :content=\"messages.panels.settings\" placement=\"bottom\">\n <button\n type=\"button\"\n class=\"dou-form-builder__tool dou-form-builder__settings-toggle\"\n aria-haspopup=\"dialog\"\n :aria-label=\"messages.panels.settings\"\n @click=\"settingsOpen = true\"\n >\n <SlidersIcon />\n </button>\n </DouTooltip>\n </template>\n <DouTooltip :content=\"messages.json.title\" placement=\"bottom\">\n <button\n type=\"button\"\n class=\"dou-form-builder__tool\"\n aria-haspopup=\"dialog\"\n :aria-label=\"messages.json.title\"\n @click=\"jsonOpen = true\"\n >\n <BracesIcon />\n </button>\n </DouTooltip>\n <button\n type=\"button\"\n class=\"dou-form-builder__tool dou-form-builder__preview-toggle\"\n :aria-pressed=\"previewing\"\n @click=\"state.setMode(previewing ? 'design' : 'preview')\"\n >\n <EyeIcon />\n <span>{{ messages.preview.toggle }}</span>\n </button>\n </div>\n <FormBuilderPreview v-if=\"previewing\" />\n <FormBuilderCanvas v-else @click.capture=\"onCanvasClick\" />\n </div>\n <aside\n v-if=\"!compact && !previewing\"\n class=\"dou-form-builder__side dou-form-builder__side--end\"\n :aria-label=\"messages.panels.settings\"\n >\n <FormBuilderInspector />\n </aside>\n </div>\n <template v-if=\"compact\">\n <DouDrawer\n v-model:open=\"sideOpen\"\n placement=\"bottom\"\n :title=\"sideTitle\"\n :snap-points=\"['50%', '90%']\"\n swipeable\n class=\"dou-form-builder__drawer\"\n >\n <FormBuilderOutline v-if=\"readonly\" />\n <DouTabs v-else v-model=\"side\" size=\"sm\" class=\"dou-form-builder__side-tabs\">\n <DouTabPanel value=\"fields\" :label=\"messages.panels.fields\">\n <FormBuilderPalette />\n </DouTabPanel>\n <DouTabPanel value=\"outline\" :label=\"messages.panels.outline\">\n <FormBuilderOutline />\n </DouTabPanel>\n </DouTabs>\n </DouDrawer>\n <DouDrawer\n v-model:open=\"settingsOpen\"\n placement=\"bottom\"\n :title=\"messages.panels.settings\"\n :snap-points=\"['50%', '90%']\"\n swipeable\n class=\"dou-form-builder__drawer\"\n >\n <FormBuilderInspector />\n </DouDrawer>\n </template>\n </slot>\n <FormBuilderJson v-model:open=\"jsonOpen\" />\n <div class=\"dou-form-builder__live\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ state.live.value }}\n </div>\n </div>\n</template>\n"],"mappings":""}
|