@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,167 @@
|
|
|
1
|
+
import { defineFormField } from "./define.js";
|
|
2
|
+
import { fitPathToOptions, fitToOptions } from "./options.js";
|
|
3
|
+
import { AlertField, CheckboxField, CheckboxGroupField, DividerField, RadioField, TextField, normalizeOptions } from "./components.js";
|
|
4
|
+
import { ColorValue, FilesValue, formatDate, formatNumber, formatOptions, formatPath, formatRange, formatText, formatToggle } from "./format.js";
|
|
5
|
+
import { DouCascader, DouColorPicker, DouDatePicker, DouInput, DouInputNumber, DouInputOtp, DouInputTag, DouRate, DouSegmentedControl, DouSelect, DouSlider, DouSwitch, DouTextarea, DouTimePicker, DouTreeSelect, DouUpload } from "dou-ui";
|
|
6
|
+
// src/fields/builtin.ts
|
|
7
|
+
var text = () => "";
|
|
8
|
+
var none = () => null;
|
|
9
|
+
var list = () => [];
|
|
10
|
+
var listWhen = (key) => (props) => props[key] ? [] : null;
|
|
11
|
+
/**
|
|
12
|
+
* The field types every renderer and builder knows. Each one is named
|
|
13
|
+
* after the component it renders, in kebab case without the `Dou`
|
|
14
|
+
* prefix: `input-number` renders `DouInputNumber`.
|
|
15
|
+
*/
|
|
16
|
+
var builtinFields = [
|
|
17
|
+
defineFormField({
|
|
18
|
+
type: "input",
|
|
19
|
+
component: DouInput,
|
|
20
|
+
empty: text,
|
|
21
|
+
format: formatText
|
|
22
|
+
}),
|
|
23
|
+
defineFormField({
|
|
24
|
+
type: "textarea",
|
|
25
|
+
component: DouTextarea,
|
|
26
|
+
empty: text
|
|
27
|
+
}),
|
|
28
|
+
defineFormField({
|
|
29
|
+
type: "input-number",
|
|
30
|
+
component: DouInputNumber,
|
|
31
|
+
format: formatNumber
|
|
32
|
+
}),
|
|
33
|
+
defineFormField({
|
|
34
|
+
type: "select",
|
|
35
|
+
optionsProp: "options",
|
|
36
|
+
fitValue: fitToOptions,
|
|
37
|
+
component: DouSelect,
|
|
38
|
+
defaultProps: { options: [] },
|
|
39
|
+
empty: listWhen("multiple")
|
|
40
|
+
}),
|
|
41
|
+
defineFormField({
|
|
42
|
+
type: "radio-group",
|
|
43
|
+
component: RadioField,
|
|
44
|
+
optionsProp: "options",
|
|
45
|
+
fitValue: fitToOptions,
|
|
46
|
+
empty: none
|
|
47
|
+
}),
|
|
48
|
+
defineFormField({
|
|
49
|
+
type: "checkbox-group",
|
|
50
|
+
component: CheckboxGroupField,
|
|
51
|
+
optionsProp: "options",
|
|
52
|
+
fitValue: fitToOptions,
|
|
53
|
+
empty: list
|
|
54
|
+
}),
|
|
55
|
+
defineFormField({
|
|
56
|
+
type: "checkbox",
|
|
57
|
+
component: CheckboxField,
|
|
58
|
+
empty: () => false,
|
|
59
|
+
format: formatToggle
|
|
60
|
+
}),
|
|
61
|
+
defineFormField({
|
|
62
|
+
type: "switch",
|
|
63
|
+
component: DouSwitch,
|
|
64
|
+
empty: () => false,
|
|
65
|
+
format: formatToggle
|
|
66
|
+
}),
|
|
67
|
+
defineFormField({
|
|
68
|
+
type: "segmented-control",
|
|
69
|
+
component: DouSegmentedControl,
|
|
70
|
+
optionsProp: "options",
|
|
71
|
+
fitValue: fitToOptions,
|
|
72
|
+
defaultProps: { options: [] },
|
|
73
|
+
empty: (props) => normalizeOptions(props.options)[0]?.value ?? null
|
|
74
|
+
}),
|
|
75
|
+
defineFormField({
|
|
76
|
+
type: "slider",
|
|
77
|
+
component: DouSlider,
|
|
78
|
+
format: formatRange,
|
|
79
|
+
empty: (props) => {
|
|
80
|
+
const min = props.min ?? 0;
|
|
81
|
+
return props.range ? [min, props.max ?? 100] : min;
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
84
|
+
defineFormField({
|
|
85
|
+
type: "rate",
|
|
86
|
+
component: DouRate,
|
|
87
|
+
empty: () => 0,
|
|
88
|
+
readonlyComponent: DouRate
|
|
89
|
+
}),
|
|
90
|
+
defineFormField({
|
|
91
|
+
type: "date-picker",
|
|
92
|
+
component: DouDatePicker,
|
|
93
|
+
empty: none,
|
|
94
|
+
format: formatDate
|
|
95
|
+
}),
|
|
96
|
+
defineFormField({
|
|
97
|
+
type: "time-picker",
|
|
98
|
+
component: DouTimePicker,
|
|
99
|
+
empty: none,
|
|
100
|
+
format: formatRange
|
|
101
|
+
}),
|
|
102
|
+
defineFormField({
|
|
103
|
+
type: "color-picker",
|
|
104
|
+
component: DouColorPicker,
|
|
105
|
+
empty: none,
|
|
106
|
+
readonlyComponent: ColorValue
|
|
107
|
+
}),
|
|
108
|
+
defineFormField({
|
|
109
|
+
type: "cascader",
|
|
110
|
+
optionsProp: "options",
|
|
111
|
+
fitValue: fitPathToOptions,
|
|
112
|
+
component: DouCascader,
|
|
113
|
+
format: formatPath,
|
|
114
|
+
defaultProps: { options: [] },
|
|
115
|
+
empty: none
|
|
116
|
+
}),
|
|
117
|
+
defineFormField({
|
|
118
|
+
type: "tree-select",
|
|
119
|
+
optionsProp: "data",
|
|
120
|
+
fitValue: fitToOptions,
|
|
121
|
+
component: DouTreeSelect,
|
|
122
|
+
format: formatOptions("data"),
|
|
123
|
+
defaultProps: { data: [] },
|
|
124
|
+
empty: listWhen("multiple")
|
|
125
|
+
}),
|
|
126
|
+
defineFormField({
|
|
127
|
+
type: "input-tag",
|
|
128
|
+
component: DouInputTag,
|
|
129
|
+
empty: list
|
|
130
|
+
}),
|
|
131
|
+
defineFormField({
|
|
132
|
+
type: "input-otp",
|
|
133
|
+
component: DouInputOtp,
|
|
134
|
+
empty: text
|
|
135
|
+
}),
|
|
136
|
+
defineFormField({
|
|
137
|
+
type: "upload",
|
|
138
|
+
component: DouUpload,
|
|
139
|
+
empty: list,
|
|
140
|
+
readonlyComponent: FilesValue
|
|
141
|
+
}),
|
|
142
|
+
defineFormField({
|
|
143
|
+
type: "text",
|
|
144
|
+
kind: "display",
|
|
145
|
+
component: TextField
|
|
146
|
+
}),
|
|
147
|
+
defineFormField({
|
|
148
|
+
type: "divider",
|
|
149
|
+
kind: "display",
|
|
150
|
+
component: DividerField
|
|
151
|
+
}),
|
|
152
|
+
defineFormField({
|
|
153
|
+
type: "alert",
|
|
154
|
+
kind: "display",
|
|
155
|
+
component: AlertField
|
|
156
|
+
})
|
|
157
|
+
];
|
|
158
|
+
/** The built-in types, then `extra` on top: a type of the same name replaces the built-in one. */
|
|
159
|
+
function resolveFields(extra = []) {
|
|
160
|
+
const map = /* @__PURE__ */ new Map();
|
|
161
|
+
for (const spec of builtinFields) map.set(spec.type, spec);
|
|
162
|
+
for (const spec of extra) map.set(spec.type, spec);
|
|
163
|
+
return map;
|
|
164
|
+
}
|
|
165
|
+
export { builtinFields, resolveFields };
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=builtin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.js","names":[],"sources":["../../src/fields/builtin.ts"],"sourcesContent":["import {\n DouCascader,\n DouColorPicker,\n DouDatePicker,\n DouInput,\n DouInputNumber,\n DouInputOtp,\n DouInputTag,\n DouRate,\n DouSegmentedControl,\n DouSelect,\n DouSlider,\n DouSwitch,\n DouTextarea,\n DouTimePicker,\n DouTreeSelect,\n DouUpload,\n} from 'dou-ui'\nimport { defineFormField } from './define'\nimport { fitPathToOptions, fitToOptions } from './options'\nimport type { FormFieldType } from './define'\nimport {\n AlertField,\n CheckboxField,\n CheckboxGroupField,\n DividerField,\n normalizeOptions,\n RadioField,\n TextField,\n} from './components'\nimport type { FormOption } from './components'\nimport {\n ColorValue,\n FilesValue,\n formatDate,\n formatNumber,\n formatOptions,\n formatPath,\n formatRange,\n formatText,\n formatToggle,\n} from './format'\n\nconst text = () => ''\nconst none = () => null\nconst list = () => []\nconst listWhen = (key: string) => (props: Record<string, unknown>) => (props[key] ? [] : null)\n\n/**\n * The field types every renderer and builder knows. Each one is named\n * after the component it renders, in kebab case without the `Dou`\n * prefix: `input-number` renders `DouInputNumber`.\n */\nexport const builtinFields: FormFieldType<any>[] = [\n defineFormField({ type: 'input', component: DouInput, empty: text, format: formatText }),\n defineFormField({ type: 'textarea', component: DouTextarea, empty: text }),\n defineFormField({ type: 'input-number', component: DouInputNumber, format: formatNumber }),\n defineFormField({\n type: 'select',\n optionsProp: 'options',\n fitValue: fitToOptions,\n component: DouSelect,\n defaultProps: { options: [] },\n empty: listWhen('multiple'),\n }),\n defineFormField({\n type: 'radio-group',\n component: RadioField,\n optionsProp: 'options',\n fitValue: fitToOptions,\n empty: none,\n }),\n defineFormField({\n type: 'checkbox-group',\n component: CheckboxGroupField,\n optionsProp: 'options',\n fitValue: fitToOptions,\n empty: list,\n }),\n defineFormField({\n type: 'checkbox',\n component: CheckboxField,\n empty: () => false,\n format: formatToggle,\n }),\n defineFormField({\n type: 'switch',\n component: DouSwitch,\n empty: () => false,\n format: formatToggle,\n }),\n defineFormField({\n type: 'segmented-control',\n component: DouSegmentedControl,\n optionsProp: 'options',\n fitValue: fitToOptions,\n defaultProps: { options: [] },\n empty: (props: { options?: FormOption[] }) => normalizeOptions(props.options)[0]?.value ?? null,\n }),\n defineFormField({\n type: 'slider',\n component: DouSlider,\n format: formatRange,\n empty: (props: { range?: boolean; min?: number; max?: number }) => {\n const min = props.min ?? 0\n return props.range ? [min, props.max ?? 100] : min\n },\n }),\n defineFormField({ type: 'rate', component: DouRate, empty: () => 0, readonlyComponent: DouRate }),\n defineFormField({\n type: 'date-picker',\n component: DouDatePicker,\n empty: none,\n format: formatDate,\n }),\n defineFormField({\n type: 'time-picker',\n component: DouTimePicker,\n empty: none,\n format: formatRange,\n }),\n defineFormField({\n type: 'color-picker',\n component: DouColorPicker,\n empty: none,\n readonlyComponent: ColorValue,\n }),\n defineFormField({\n type: 'cascader',\n optionsProp: 'options',\n fitValue: fitPathToOptions,\n component: DouCascader,\n format: formatPath,\n defaultProps: { options: [] },\n empty: none,\n }),\n defineFormField({\n type: 'tree-select',\n optionsProp: 'data',\n fitValue: fitToOptions,\n component: DouTreeSelect,\n format: formatOptions('data'),\n defaultProps: { data: [] },\n empty: listWhen('multiple'),\n }),\n defineFormField({ type: 'input-tag', component: DouInputTag, empty: list }),\n defineFormField({ type: 'input-otp', component: DouInputOtp, empty: text }),\n defineFormField({\n type: 'upload',\n component: DouUpload,\n empty: list,\n readonlyComponent: FilesValue,\n }),\n defineFormField({ type: 'text', kind: 'display', component: TextField }),\n defineFormField({ type: 'divider', kind: 'display', component: DividerField }),\n defineFormField({ type: 'alert', kind: 'display', component: AlertField }),\n]\n\n/** The built-in types, then `extra` on top: a type of the same name replaces the built-in one. */\nexport function resolveFields(extra: readonly FormFieldType<any>[] = []) {\n const map = new Map<string, FormFieldType>()\n for (const spec of builtinFields) map.set(spec.type, spec)\n for (const spec of extra) map.set(spec.type, spec)\n return map\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { DouAlert, DouCheckbox, DouCheckboxGroup, DouDivider, DouRadio, DouRadioGroup, DouText } from "dou-ui";
|
|
2
|
+
import { defineComponent, h } from "vue";
|
|
3
|
+
// src/fields/components.ts
|
|
4
|
+
function normalizeOptions(options) {
|
|
5
|
+
return (options ?? []).map((option) => typeof option === "object" ? {
|
|
6
|
+
value: option.value,
|
|
7
|
+
label: option.label ?? String(option.value),
|
|
8
|
+
disabled: option.disabled
|
|
9
|
+
} : {
|
|
10
|
+
value: option,
|
|
11
|
+
label: String(option),
|
|
12
|
+
disabled: false
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
var optionProps = {
|
|
16
|
+
options: {
|
|
17
|
+
type: Array,
|
|
18
|
+
default: () => []
|
|
19
|
+
},
|
|
20
|
+
direction: {
|
|
21
|
+
type: String,
|
|
22
|
+
default: "horizontal"
|
|
23
|
+
},
|
|
24
|
+
disabled: Boolean
|
|
25
|
+
};
|
|
26
|
+
/** One choice out of a few, all in view. */
|
|
27
|
+
var RadioField = defineComponent({
|
|
28
|
+
name: "DouFormRadioField",
|
|
29
|
+
props: {
|
|
30
|
+
...optionProps,
|
|
31
|
+
modelValue: {
|
|
32
|
+
type: [
|
|
33
|
+
String,
|
|
34
|
+
Number,
|
|
35
|
+
Boolean
|
|
36
|
+
],
|
|
37
|
+
default: void 0
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
emits: { "update:modelValue": (_value) => true },
|
|
41
|
+
setup(props, { emit }) {
|
|
42
|
+
return () => h(DouRadioGroup, {
|
|
43
|
+
modelValue: props.modelValue ?? void 0,
|
|
44
|
+
direction: props.direction,
|
|
45
|
+
disabled: props.disabled,
|
|
46
|
+
"onUpdate:modelValue": (value) => value !== void 0 && emit("update:modelValue", value)
|
|
47
|
+
}, () => normalizeOptions(props.options).map((option) => h(DouRadio, {
|
|
48
|
+
key: String(option.value),
|
|
49
|
+
value: option.value,
|
|
50
|
+
disabled: option.disabled
|
|
51
|
+
}, () => option.label)));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
/** Any number of choices out of a few, all in view. */
|
|
55
|
+
var CheckboxGroupField = defineComponent({
|
|
56
|
+
name: "DouFormCheckboxGroupField",
|
|
57
|
+
props: {
|
|
58
|
+
...optionProps,
|
|
59
|
+
modelValue: {
|
|
60
|
+
type: Array,
|
|
61
|
+
default: void 0
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
emits: { "update:modelValue": (_value) => true },
|
|
65
|
+
setup(props, { emit }) {
|
|
66
|
+
return () => h(DouCheckboxGroup, {
|
|
67
|
+
modelValue: props.modelValue ?? [],
|
|
68
|
+
direction: props.direction,
|
|
69
|
+
disabled: props.disabled,
|
|
70
|
+
"onUpdate:modelValue": (value) => emit("update:modelValue", value)
|
|
71
|
+
}, () => normalizeOptions(props.options).map((option) => h(DouCheckbox, {
|
|
72
|
+
key: String(option.value),
|
|
73
|
+
value: option.value,
|
|
74
|
+
disabled: option.disabled
|
|
75
|
+
}, () => option.label)));
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
/** A single box, e.g. to agree to terms; its text sits beside it. */
|
|
79
|
+
var CheckboxField = defineComponent({
|
|
80
|
+
name: "DouFormCheckboxField",
|
|
81
|
+
props: {
|
|
82
|
+
modelValue: {
|
|
83
|
+
type: Boolean,
|
|
84
|
+
default: false
|
|
85
|
+
},
|
|
86
|
+
text: String,
|
|
87
|
+
disabled: Boolean
|
|
88
|
+
},
|
|
89
|
+
emits: { "update:modelValue": (_value) => true },
|
|
90
|
+
setup(props, { emit }) {
|
|
91
|
+
return () => h(DouCheckbox, {
|
|
92
|
+
modelValue: props.modelValue,
|
|
93
|
+
disabled: props.disabled,
|
|
94
|
+
"onUpdate:modelValue": (value) => emit("update:modelValue", value === true)
|
|
95
|
+
}, props.text ? () => props.text : void 0);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
/** A heading or a paragraph between fields. */
|
|
99
|
+
var TextField = defineComponent({
|
|
100
|
+
name: "DouFormTextField",
|
|
101
|
+
props: {
|
|
102
|
+
content: {
|
|
103
|
+
type: String,
|
|
104
|
+
default: ""
|
|
105
|
+
},
|
|
106
|
+
/** `title` for a heading, `body` for a paragraph, `caption` for small print. */
|
|
107
|
+
variant: {
|
|
108
|
+
type: String,
|
|
109
|
+
default: "body"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
setup(props) {
|
|
113
|
+
return () => props.variant === "title" ? h(DouText, {
|
|
114
|
+
as: "h3",
|
|
115
|
+
size: "lg",
|
|
116
|
+
weight: "medium",
|
|
117
|
+
class: "dou-form-renderer__title"
|
|
118
|
+
}, () => props.content) : h(DouText, {
|
|
119
|
+
as: "p",
|
|
120
|
+
size: props.variant === "caption" ? "sm" : "md",
|
|
121
|
+
variant: "secondary",
|
|
122
|
+
class: "dou-form-renderer__paragraph"
|
|
123
|
+
}, () => props.content);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
var DividerField = defineComponent({
|
|
127
|
+
name: "DouFormDividerField",
|
|
128
|
+
props: {
|
|
129
|
+
text: String,
|
|
130
|
+
dashed: Boolean
|
|
131
|
+
},
|
|
132
|
+
setup(props) {
|
|
133
|
+
return () => h(DouDivider, { dashed: props.dashed }, props.text ? () => props.text : void 0);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
var AlertField = defineComponent({
|
|
137
|
+
name: "DouFormAlertField",
|
|
138
|
+
props: {
|
|
139
|
+
tone: {
|
|
140
|
+
type: String,
|
|
141
|
+
default: "neutral"
|
|
142
|
+
},
|
|
143
|
+
title: String,
|
|
144
|
+
content: String
|
|
145
|
+
},
|
|
146
|
+
setup(props) {
|
|
147
|
+
return () => h(DouAlert, {
|
|
148
|
+
tone: props.tone,
|
|
149
|
+
title: props.title
|
|
150
|
+
}, props.content ? () => props.content : void 0);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
export { AlertField, CheckboxField, CheckboxGroupField, DividerField, RadioField, TextField, normalizeOptions };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.js","names":[],"sources":["../../src/fields/components.ts"],"sourcesContent":["import { defineComponent, h } from 'vue'\nimport type { PropType } from 'vue'\nimport {\n DouAlert,\n DouCheckbox,\n DouCheckboxGroup,\n DouDivider,\n DouRadio,\n DouRadioGroup,\n DouText,\n} from 'dou-ui'\nimport type { CheckboxValue, RadioValue, Tone } from 'dou-ui'\n\n/** A choice as written in a document: a plain value is both value and label. */\nexport type FormOption =\n string | number | { value: string | number | boolean; label?: string; disabled?: boolean }\n\nexport function normalizeOptions(options: readonly FormOption[] | undefined) {\n return (options ?? []).map((option) =>\n typeof option === 'object'\n ? {\n value: option.value,\n label: option.label ?? String(option.value),\n disabled: option.disabled,\n }\n : { value: option, label: String(option), disabled: false },\n )\n}\n\nconst optionProps = {\n options: { type: Array as PropType<FormOption[]>, default: () => [] },\n direction: { type: String as PropType<'horizontal' | 'vertical'>, default: 'horizontal' },\n disabled: Boolean,\n}\n\n/** One choice out of a few, all in view. */\nexport const RadioField = defineComponent({\n name: 'DouFormRadioField',\n props: {\n ...optionProps,\n modelValue: { type: [String, Number, Boolean] as PropType<RadioValue>, default: undefined },\n },\n emits: { 'update:modelValue': (_value: RadioValue) => true },\n setup(props, { emit }) {\n return () =>\n h(\n DouRadioGroup,\n {\n modelValue: props.modelValue ?? undefined,\n direction: props.direction,\n disabled: props.disabled,\n 'onUpdate:modelValue': (value: RadioValue | undefined) =>\n value !== undefined && emit('update:modelValue', value),\n },\n () =>\n normalizeOptions(props.options).map((option) =>\n h(\n DouRadio,\n { key: String(option.value), value: option.value, disabled: option.disabled },\n () => option.label,\n ),\n ),\n )\n },\n})\n\n/** Any number of choices out of a few, all in view. */\nexport const CheckboxGroupField = defineComponent({\n name: 'DouFormCheckboxGroupField',\n props: {\n ...optionProps,\n modelValue: { type: Array as PropType<CheckboxValue[]>, default: undefined },\n },\n emits: { 'update:modelValue': (_value: CheckboxValue[]) => true },\n setup(props, { emit }) {\n return () =>\n h(\n DouCheckboxGroup,\n {\n modelValue: props.modelValue ?? [],\n direction: props.direction,\n disabled: props.disabled,\n 'onUpdate:modelValue': (value: CheckboxValue[]) => emit('update:modelValue', value),\n },\n () =>\n normalizeOptions(props.options).map((option) =>\n h(\n DouCheckbox,\n {\n key: String(option.value),\n value: option.value as CheckboxValue,\n disabled: option.disabled,\n },\n () => option.label,\n ),\n ),\n )\n },\n})\n\n/** A single box, e.g. to agree to terms; its text sits beside it. */\nexport const CheckboxField = defineComponent({\n name: 'DouFormCheckboxField',\n props: {\n modelValue: { type: Boolean, default: false },\n text: String,\n disabled: Boolean,\n },\n emits: { 'update:modelValue': (_value: boolean) => true },\n setup(props, { emit }) {\n return () =>\n h(\n DouCheckbox,\n {\n modelValue: props.modelValue,\n disabled: props.disabled,\n 'onUpdate:modelValue': (value: unknown) => emit('update:modelValue', value === true),\n },\n props.text ? () => props.text : undefined,\n )\n },\n})\n\n/** A heading or a paragraph between fields. */\nexport const TextField = defineComponent({\n name: 'DouFormTextField',\n props: {\n content: { type: String, default: '' },\n /** `title` for a heading, `body` for a paragraph, `caption` for small print. */\n variant: { type: String as PropType<'title' | 'body' | 'caption'>, default: 'body' },\n },\n setup(props) {\n return () =>\n props.variant === 'title'\n ? h(\n DouText,\n { as: 'h3', size: 'lg', weight: 'medium', class: 'dou-form-renderer__title' },\n () => props.content,\n )\n : h(\n DouText,\n {\n as: 'p',\n size: props.variant === 'caption' ? 'sm' : 'md',\n variant: 'secondary',\n class: 'dou-form-renderer__paragraph',\n },\n () => props.content,\n )\n },\n})\n\nexport const DividerField = defineComponent({\n name: 'DouFormDividerField',\n props: { text: String, dashed: Boolean },\n setup(props) {\n return () => h(DouDivider, { dashed: props.dashed }, props.text ? () => props.text : undefined)\n },\n})\n\nexport const AlertField = defineComponent({\n name: 'DouFormAlertField',\n props: {\n tone: { type: String as PropType<Tone>, default: 'neutral' },\n title: String,\n content: String,\n },\n setup(props) {\n return () =>\n h(\n DouAlert,\n { tone: props.tone, title: props.title },\n props.content ? () => props.content : undefined,\n )\n },\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { inject, markRaw } from "vue";
|
|
2
|
+
// src/fields/define.ts
|
|
3
|
+
/** Declare a field type. Gives the spec its types and leaves it as is otherwise. */
|
|
4
|
+
function defineFormField(spec) {
|
|
5
|
+
const result = {
|
|
6
|
+
...spec,
|
|
7
|
+
component: markRaw(spec.component)
|
|
8
|
+
};
|
|
9
|
+
if (spec.icon) result.icon = markRaw(spec.icon);
|
|
10
|
+
if (spec.readonlyComponent) result.readonlyComponent = markRaw(spec.readonlyComponent);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
/** Settings of a node: its type's `defaultProps` with its own `props` on top. */
|
|
14
|
+
function fieldProps(type, node) {
|
|
15
|
+
const base = type?.defaultProps;
|
|
16
|
+
if (!base) return node.props ?? {};
|
|
17
|
+
return node.props ? {
|
|
18
|
+
...base,
|
|
19
|
+
...node.props
|
|
20
|
+
} : base;
|
|
21
|
+
}
|
|
22
|
+
var formFieldKey = Symbol("DouFormField");
|
|
23
|
+
/**
|
|
24
|
+
* Inside a field type's component: the node it renders, its type and
|
|
25
|
+
* settings. The properties stay current when the node changes.
|
|
26
|
+
*/
|
|
27
|
+
function useFormField() {
|
|
28
|
+
const get = inject(formFieldKey, void 0);
|
|
29
|
+
if (!get) throw new Error("[@dou-ui/form-builder] useFormField() must be called inside a form field");
|
|
30
|
+
return {
|
|
31
|
+
get node() {
|
|
32
|
+
return get().node;
|
|
33
|
+
},
|
|
34
|
+
get type() {
|
|
35
|
+
return get().type;
|
|
36
|
+
},
|
|
37
|
+
get props() {
|
|
38
|
+
return get().props;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export { defineFormField, fieldProps, formFieldKey, useFormField };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=define.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","names":[],"sources":["../../src/fields/define.ts"],"sourcesContent":["import { inject, markRaw } from 'vue'\nimport type { Component, InjectionKey } from 'vue'\nimport type { FormFieldNode, FormNode } from '../core/types'\nimport type { FormFormatContext } from './format'\n\nexport type FormFieldProps = Record<string, unknown>\n\n/**\n * What a field type is: how it renders, what it starts with, and which\n * settings it takes. The renderer and the builder share the same list.\n */\nexport interface FormFieldType<P extends FormFieldProps = FormFieldProps> {\n /** The name nodes refer to it by in `type`. A type of the same name as a built-in one replaces it. */\n type: string\n /** Shown to people building the form. */\n label?: string\n /**\n * - `value`: holds a value at the node's `name`, wrapped in a form item\n * with its label, help and checks.\n * - `display`: only shows something, such as text or a divider.\n */\n kind?: 'value' | 'display'\n /** Settings a node starts with, below its own `props`. */\n defaultProps?: Partial<P>\n /**\n * The type's empty value, which a field starts with when its node sets\n * no `default`, e.g. `[]` for a list of choices. Defaults to `null`.\n */\n empty?: (props: P) => unknown\n /**\n * Renders the field. A `value` field gets `modelValue`, listens to\n * `update:modelValue`, and receives the settings as props (after\n * `toProps`). Call `useFormField()` inside it for the node itself.\n */\n component: Component\n /**\n * The prop that takes the list of options, for a type whose options can\n * come from a source (a node's `source`), e.g. `options` or `data`.\n */\n optionsProp?: string\n /**\n * Narrows a value to what new options offer, e.g. drops a city once the\n * province changes. Called when a source loads different options; the\n * value stays as it is when the type has none.\n */\n fitValue?: (value: unknown, options: readonly unknown[]) => unknown\n /**\n * The value as text, for a renderer that is `readonly`. Gets the\n * settings (with options loaded from a source). Not called for an empty\n * value, which shows as the renderer's `noValue` text. Defaults to the\n * labels of the chosen options for a type with `optionsProp`, else to\n * the value as plain text: lists joined, booleans as yes and no.\n */\n format?: (value: unknown, props: P, context: FormFormatContext) => string\n /**\n * Shows the value in a `readonly` renderer instead of `format`'s text,\n * e.g. a swatch for a color. Gets `modelValue` and the same props as\n * `component`, and `readonly` when it takes one.\n */\n readonlyComponent?: Component\n /** Turns the settings into the component's props, when they differ. */\n toProps?: (props: P, node: FormFieldNode) => Record<string, unknown>\n /** The builder's palette: the type's icon, e.g. one of `@dou-ui/icons`. */\n icon?: Component\n /**\n * The palette group the type is listed in: `input`, `choice`, `more`\n * (the default), `display`, or a name of your own, shown as it is.\n */\n group?: string\n /**\n * The settings the builder's inspector shows for the type, as nodes of\n * a form whose values are the node's `props`: a field named\n * `placeholder` edits `props.placeholder`, and expressions read the\n * other settings as `values`. A setting left at its `default` is not\n * written to the node.\n */\n settings?: FormNode[]\n}\n\n/** Declare a field type. Gives the spec its types and leaves it as is otherwise. */\nexport function defineFormField<P extends FormFieldProps = FormFieldProps>(\n spec: FormFieldType<P>,\n): FormFieldType<P> {\n const result = { ...spec, component: markRaw(spec.component) }\n if (spec.icon) result.icon = markRaw(spec.icon)\n if (spec.readonlyComponent) result.readonlyComponent = markRaw(spec.readonlyComponent)\n return result\n}\n\n/** Settings of a node: its type's `defaultProps` with its own `props` on top. */\nexport function fieldProps(type: FormFieldType | undefined, node: FormFieldNode): FormFieldProps {\n const base = type?.defaultProps as FormFieldProps | undefined\n if (!base) return node.props ?? {}\n return node.props ? { ...base, ...node.props } : base\n}\n\nexport interface FormFieldContext {\n /** The node being rendered. */\n node: FormFieldNode\n /** Its type. */\n type: FormFieldType\n /** Its settings, with the type's `defaultProps` filled in. */\n props: FormFieldProps\n}\n\nexport const formFieldKey: InjectionKey<() => FormFieldContext> = Symbol('DouFormField')\n\n/**\n * Inside a field type's component: the node it renders, its type and\n * settings. The properties stay current when the node changes.\n */\nexport function useFormField(): Readonly<FormFieldContext> {\n const get = inject(formFieldKey, undefined)\n if (!get)\n throw new Error('[@dou-ui/form-builder] useFormField() must be called inside a form field')\n return {\n get node() {\n return get().node\n },\n get type() {\n return get().type\n },\n get props() {\n return get().props\n },\n }\n}\n"],"mappings":";;;AAgFA,SAAgB,gBACd,MACkB;CAClB,MAAM,SAAS;EAAE,GAAG;EAAM,WAAW,QAAQ,KAAK,SAAS;CAAE;CAC7D,IAAI,KAAK,MAAM,OAAO,OAAO,QAAQ,KAAK,IAAI;CAC9C,IAAI,KAAK,mBAAmB,OAAO,oBAAoB,QAAQ,KAAK,iBAAiB;CACrF,OAAO;AACT;;AAGA,SAAgB,WAAW,MAAiC,MAAqC;CAC/F,MAAM,OAAO,MAAM;CACnB,IAAI,CAAC,MAAM,OAAO,KAAK,SAAS,CAAC;CACjC,OAAO,KAAK,QAAQ;EAAE,GAAG;EAAM,GAAG,KAAK;CAAM,IAAI;AACnD;AAWA,IAAa,eAAqD,OAAO,cAAc;;;;;AAMvF,SAAgB,eAA2C;CACzD,MAAM,MAAM,OAAO,cAAc,KAAA,CAAS;CAC1C,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,0EAA0E;CAC5F,OAAO;EACL,IAAI,OAAO;GACT,OAAO,IAAI,CAAC,CAAC;EACf;EACA,IAAI,OAAO;GACT,OAAO,IAAI,CAAC,CAAC;EACf;EACA,IAAI,QAAQ;GACV,OAAO,IAAI,CAAC,CAAC;EACf;CACF;AACF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { DouLink, formatDateValue } from "dou-ui";
|
|
2
|
+
import { defineComponent, h } from "vue";
|
|
3
|
+
// src/fields/format.ts
|
|
4
|
+
/** The label of each value the options offer, through groups (`options`) and levels (`children`). */
|
|
5
|
+
function labelsOf(options, into = /* @__PURE__ */ new Map()) {
|
|
6
|
+
if (!Array.isArray(options)) return into;
|
|
7
|
+
for (const option of options) {
|
|
8
|
+
if (option === null || typeof option !== "object") {
|
|
9
|
+
if (!into.has(option)) into.set(option, String(option));
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const record = option;
|
|
13
|
+
const value = "value" in record ? record.value : record.key;
|
|
14
|
+
if (value !== void 0 && !into.has(value)) into.set(value, typeof record.label === "string" ? record.label : String(value));
|
|
15
|
+
labelsOf(record.options, into);
|
|
16
|
+
labelsOf(record.children, into);
|
|
17
|
+
}
|
|
18
|
+
return into;
|
|
19
|
+
}
|
|
20
|
+
var warnedTypes = /* @__PURE__ */ new Set();
|
|
21
|
+
function warnObject(type) {
|
|
22
|
+
if (process.env.NODE_ENV === "production" || warnedTypes.has(type)) return;
|
|
23
|
+
warnedTypes.add(type);
|
|
24
|
+
console.warn(`[@dou-ui/form-builder] a value of field type "${type}" is an object and shows as JSON when read-only; give the type a \`format\` or \`readonlyComponent\``);
|
|
25
|
+
}
|
|
26
|
+
/** Plain text of any value: lists joined, `true` and `false` as yes and no. */
|
|
27
|
+
function formatValue(value, context) {
|
|
28
|
+
if (value === null || value === void 0) return "";
|
|
29
|
+
if (Array.isArray(value)) return value.map((item) => formatValue(item, context)).filter(Boolean).join(context.messages.separator);
|
|
30
|
+
if (typeof value === "boolean") return value ? context.messages.yes : context.messages.no;
|
|
31
|
+
if (value instanceof Date) return value.toLocaleString(context.locale.name);
|
|
32
|
+
if (typeof value === "object") {
|
|
33
|
+
const record = value;
|
|
34
|
+
if (typeof record.label === "string") return record.label;
|
|
35
|
+
if (typeof record.name === "string") return record.name;
|
|
36
|
+
warnObject(context.node.type);
|
|
37
|
+
return JSON.stringify(value);
|
|
38
|
+
}
|
|
39
|
+
return String(value);
|
|
40
|
+
}
|
|
41
|
+
/** The labels of the chosen options, from the setting that holds them (`options` by default). */
|
|
42
|
+
var formatOptions = (prop = "options") => (value, props, context) => {
|
|
43
|
+
const labels = labelsOf(props[prop]);
|
|
44
|
+
const label = (item) => labels.get(item) ?? formatValue(item, context);
|
|
45
|
+
return Array.isArray(value) ? value.map(label).join(context.messages.separator) : label(value);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* A path through levels of options as their labels, or a list of such
|
|
49
|
+
* paths; only the last label with `showLastOnly`, as the cascader shows it.
|
|
50
|
+
*/
|
|
51
|
+
function formatPath(value, props, context) {
|
|
52
|
+
if (!Array.isArray(value)) return formatValue(value, context);
|
|
53
|
+
const separator = typeof props.separator === "string" ? props.separator : " / ";
|
|
54
|
+
const one = (path) => {
|
|
55
|
+
const labels = [];
|
|
56
|
+
let level = props.options;
|
|
57
|
+
for (const step of path) {
|
|
58
|
+
const option = Array.isArray(level) ? level.find((entry) => entry !== null && typeof entry === "object" && entry.value === step) : void 0;
|
|
59
|
+
labels.push(option?.label ?? formatValue(step, context));
|
|
60
|
+
level = option?.children;
|
|
61
|
+
}
|
|
62
|
+
return props.showLastOnly ? labels[labels.length - 1] ?? "" : labels.join(separator);
|
|
63
|
+
};
|
|
64
|
+
return Array.isArray(value[0]) ? value.map(one).join(context.messages.separator) : one(value);
|
|
65
|
+
}
|
|
66
|
+
/** A switch or a single box: yes or no, by its `checkedValue` when it has one. */
|
|
67
|
+
function formatToggle(value, props, context) {
|
|
68
|
+
return value === ("checkedValue" in props && props.checkedValue !== void 0 ? props.checkedValue : true) ? context.messages.yes : context.messages.no;
|
|
69
|
+
}
|
|
70
|
+
/** Text typed into an input, hidden for a password. */
|
|
71
|
+
function formatText(value, props) {
|
|
72
|
+
const text = String(value);
|
|
73
|
+
return props.type === "password" ? "•".repeat(Math.min(text.length, 8)) : text;
|
|
74
|
+
}
|
|
75
|
+
/** A number, with `precision` decimals when it sets them. */
|
|
76
|
+
function formatNumber(value, props) {
|
|
77
|
+
if (typeof value !== "number") return String(value);
|
|
78
|
+
const precision = typeof props.precision === "number" ? props.precision : void 0;
|
|
79
|
+
return precision === void 0 ? String(value) : value.toFixed(precision);
|
|
80
|
+
}
|
|
81
|
+
/** One number or time, or both ends of a range. */
|
|
82
|
+
function formatRange(value, _props, context) {
|
|
83
|
+
return Array.isArray(value) ? value.map((item) => formatValue(item, context)).join(context.messages.rangeSeparator) : formatValue(value, context);
|
|
84
|
+
}
|
|
85
|
+
/** A date written the way the date picker writes it (`displayFormat`, else the locale's); a range as both ends. */
|
|
86
|
+
function formatDate(value, props, context) {
|
|
87
|
+
if (Array.isArray(value)) return value.map((item) => formatDate(item, props, context)).join(context.messages.rangeSeparator);
|
|
88
|
+
if (typeof value !== "string") return formatValue(value, context);
|
|
89
|
+
return formatDateValue(value, {
|
|
90
|
+
locale: context.locale,
|
|
91
|
+
type: typeof props.type === "string" ? props.type : void 0,
|
|
92
|
+
displayFormat: typeof props.displayFormat === "string" ? props.displayFormat : void 0
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** A color, with a swatch before its text. */
|
|
96
|
+
var ColorValue = defineComponent({
|
|
97
|
+
name: "DouFormColorValue",
|
|
98
|
+
props: { modelValue: {
|
|
99
|
+
type: String,
|
|
100
|
+
default: null
|
|
101
|
+
} },
|
|
102
|
+
setup(props) {
|
|
103
|
+
return () => h("span", { class: "dou-form-renderer__color" }, [h("span", {
|
|
104
|
+
class: "dou-form-renderer__swatch",
|
|
105
|
+
"aria-hidden": "true"
|
|
106
|
+
}, [h("span", { style: { background: props.modelValue ?? void 0 } })]), props.modelValue]);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
/** Uploaded files, each a link when it has a `url`. */
|
|
110
|
+
var FilesValue = defineComponent({
|
|
111
|
+
name: "DouFormFilesValue",
|
|
112
|
+
props: { modelValue: {
|
|
113
|
+
type: Array,
|
|
114
|
+
default: () => []
|
|
115
|
+
} },
|
|
116
|
+
setup(props) {
|
|
117
|
+
return () => h("ul", { class: "dou-form-renderer__files" }, props.modelValue.map((file, index) => h("li", { key: file.uid ?? index }, [file.url ? h(DouLink, {
|
|
118
|
+
href: file.url,
|
|
119
|
+
external: true
|
|
120
|
+
}, () => file.name) : file.name])));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
export { ColorValue, FilesValue, formatDate, formatNumber, formatOptions, formatPath, formatRange, formatText, formatToggle, formatValue };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../../src/fields/format.ts"],"sourcesContent":["import { defineComponent, h } from 'vue'\nimport type { PropType } from 'vue'\nimport { DouLink, formatDateValue } from 'dou-ui'\nimport type { DatePickerType, Locale } from 'dou-ui'\nimport type { FormFieldNode } from '../core/types'\nimport type { FormRendererMessages } from '../locale'\n\n/** What a field type's `format` gets besides the value and the settings. */\nexport interface FormFormatContext {\n /** The node being shown. */\n node: FormFieldNode\n /** The renderer's texts, e.g. `yes` and `no`. */\n messages: FormRendererMessages\n /** DouUI's current locale, e.g. for its date patterns. */\n locale: Locale\n}\n\n/** The label of each value the options offer, through groups (`options`) and levels (`children`). */\nfunction labelsOf(options: unknown, into = new Map<unknown, string>()) {\n if (!Array.isArray(options)) return into\n for (const option of options) {\n if (option === null || typeof option !== 'object') {\n if (!into.has(option)) into.set(option, String(option))\n continue\n }\n const record = option as {\n value?: unknown\n key?: unknown\n label?: unknown\n options?: unknown\n children?: unknown\n }\n const value = 'value' in record ? record.value : record.key\n if (value !== undefined && !into.has(value))\n into.set(value, typeof record.label === 'string' ? record.label : String(value))\n labelsOf(record.options, into)\n labelsOf(record.children, into)\n }\n return into\n}\n\nconst warnedTypes = new Set<string>()\nfunction warnObject(type: string) {\n if (process.env.NODE_ENV === 'production' || warnedTypes.has(type)) return\n warnedTypes.add(type)\n console.warn(\n `[@dou-ui/form-builder] a value of field type \"${type}\" is an object and shows as JSON when read-only; give the type a \\`format\\` or \\`readonlyComponent\\``,\n )\n}\n\n/** Plain text of any value: lists joined, `true` and `false` as yes and no. */\nexport function formatValue(value: unknown, context: FormFormatContext): string {\n if (value === null || value === undefined) return ''\n if (Array.isArray(value))\n return value\n .map((item) => formatValue(item, context))\n .filter(Boolean)\n .join(context.messages.separator)\n if (typeof value === 'boolean') return value ? context.messages.yes : context.messages.no\n if (value instanceof Date) return value.toLocaleString(context.locale.name)\n if (typeof value === 'object') {\n const record = value as { label?: unknown; name?: unknown }\n if (typeof record.label === 'string') return record.label\n if (typeof record.name === 'string') return record.name\n warnObject(context.node.type)\n return JSON.stringify(value)\n }\n return String(value)\n}\n\n/** The labels of the chosen options, from the setting that holds them (`options` by default). */\nexport const formatOptions =\n (prop = 'options') =>\n (value: unknown, props: Record<string, unknown>, context: FormFormatContext) => {\n const labels = labelsOf(props[prop])\n const label = (item: unknown) => labels.get(item) ?? formatValue(item, context)\n return Array.isArray(value) ? value.map(label).join(context.messages.separator) : label(value)\n }\n\n/**\n * A path through levels of options as their labels, or a list of such\n * paths; only the last label with `showLastOnly`, as the cascader shows it.\n */\nexport function formatPath(\n value: unknown,\n props: Record<string, unknown>,\n context: FormFormatContext,\n) {\n if (!Array.isArray(value)) return formatValue(value, context)\n const separator = typeof props.separator === 'string' ? props.separator : ' / '\n const one = (path: unknown[]) => {\n const labels: string[] = []\n let level: unknown = props.options\n for (const step of path) {\n const option = Array.isArray(level)\n ? (level.find(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n (entry as { value?: unknown }).value === step,\n ) as { label?: string; children?: unknown } | undefined)\n : undefined\n labels.push(option?.label ?? formatValue(step, context))\n level = option?.children\n }\n return props.showLastOnly ? (labels[labels.length - 1] ?? '') : labels.join(separator)\n }\n return Array.isArray(value[0])\n ? (value as unknown[][]).map(one).join(context.messages.separator)\n : one(value)\n}\n\n/** A switch or a single box: yes or no, by its `checkedValue` when it has one. */\nexport function formatToggle(\n value: unknown,\n props: Record<string, unknown>,\n context: FormFormatContext,\n) {\n const on = 'checkedValue' in props && props.checkedValue !== undefined ? props.checkedValue : true\n return value === on ? context.messages.yes : context.messages.no\n}\n\n/** Text typed into an input, hidden for a password. */\nexport function formatText(value: unknown, props: Record<string, unknown>) {\n const text = String(value)\n return props.type === 'password' ? '•'.repeat(Math.min(text.length, 8)) : text\n}\n\n/** A number, with `precision` decimals when it sets them. */\nexport function formatNumber(value: unknown, props: Record<string, unknown>) {\n if (typeof value !== 'number') return String(value)\n const precision = typeof props.precision === 'number' ? props.precision : undefined\n return precision === undefined ? String(value) : value.toFixed(precision)\n}\n\n/** One number or time, or both ends of a range. */\nexport function formatRange(\n value: unknown,\n _props: Record<string, unknown>,\n context: FormFormatContext,\n) {\n return Array.isArray(value)\n ? value.map((item) => formatValue(item, context)).join(context.messages.rangeSeparator)\n : formatValue(value, context)\n}\n\n/** A date written the way the date picker writes it (`displayFormat`, else the locale's); a range as both ends. */\nexport function formatDate(\n value: unknown,\n props: Record<string, unknown>,\n context: FormFormatContext,\n): string {\n if (Array.isArray(value))\n return value\n .map((item) => formatDate(item, props, context))\n .join(context.messages.rangeSeparator)\n if (typeof value !== 'string') return formatValue(value, context)\n return formatDateValue(value, {\n locale: context.locale,\n type: typeof props.type === 'string' ? (props.type as DatePickerType) : undefined,\n displayFormat: typeof props.displayFormat === 'string' ? props.displayFormat : undefined,\n })\n}\n\n/** A color, with a swatch before its text. */\nexport const ColorValue = defineComponent({\n name: 'DouFormColorValue',\n props: { modelValue: { type: String as PropType<string | null>, default: null } },\n setup(props) {\n return () =>\n h('span', { class: 'dou-form-renderer__color' }, [\n h('span', { class: 'dou-form-renderer__swatch', 'aria-hidden': 'true' }, [\n h('span', { style: { background: props.modelValue ?? undefined } }),\n ]),\n props.modelValue,\n ])\n },\n})\n\n/** Uploaded files, each a link when it has a `url`. */\nexport const FilesValue = defineComponent({\n name: 'DouFormFilesValue',\n props: {\n modelValue: {\n type: Array as PropType<{ uid?: string; name: string; url?: string }[]>,\n default: () => [],\n },\n },\n setup(props) {\n return () =>\n h(\n 'ul',\n { class: 'dou-form-renderer__files' },\n props.modelValue.map((file, index) =>\n h('li', { key: file.uid ?? index }, [\n file.url ? h(DouLink, { href: file.url, external: true }, () => file.name) : file.name,\n ]),\n ),\n )\n },\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/fields/options.ts
|
|
2
|
+
/** The values a list of options offers, through groups (`options`) and levels (`children`). */
|
|
3
|
+
function valuesOf(options, into = /* @__PURE__ */ new Set()) {
|
|
4
|
+
for (const option of options) {
|
|
5
|
+
if (option === null || typeof option !== "object") {
|
|
6
|
+
into.add(option);
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
const record = option;
|
|
10
|
+
if ("value" in record) into.add(record.value);
|
|
11
|
+
if (Array.isArray(record.options)) valuesOf(record.options, into);
|
|
12
|
+
if (Array.isArray(record.children)) valuesOf(record.children, into);
|
|
13
|
+
}
|
|
14
|
+
return into;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A value narrowed to what the options offer: a single value the options
|
|
18
|
+
* lack becomes `null`, and a list keeps only the items they have. The
|
|
19
|
+
* same value comes back when nothing is lost.
|
|
20
|
+
*/
|
|
21
|
+
function fitToOptions(value, options) {
|
|
22
|
+
if (value === null || value === void 0) return value;
|
|
23
|
+
const offered = valuesOf(options);
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
const kept = value.filter((item) => offered.has(item));
|
|
26
|
+
return kept.length === value.length ? value : kept;
|
|
27
|
+
}
|
|
28
|
+
return offered.has(value) ? value : null;
|
|
29
|
+
}
|
|
30
|
+
/** Whether a path of values, one per level, runs through the options' `children`. */
|
|
31
|
+
function hasPath(path, options) {
|
|
32
|
+
let level = options;
|
|
33
|
+
for (const step of path) {
|
|
34
|
+
const option = level.find((entry) => entry !== null && typeof entry === "object" && entry.value === step);
|
|
35
|
+
if (!option) return false;
|
|
36
|
+
level = Array.isArray(option.children) ? option.children : [];
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Like `fitToOptions`, for a value that is a path through levels of
|
|
42
|
+
* options, or a list of such paths.
|
|
43
|
+
*/
|
|
44
|
+
function fitPathToOptions(value, options) {
|
|
45
|
+
if (!Array.isArray(value) || value.length === 0) return value;
|
|
46
|
+
if (Array.isArray(value[0])) {
|
|
47
|
+
const kept = value.filter((path) => Array.isArray(path) && hasPath(path, options));
|
|
48
|
+
return kept.length === value.length ? value : kept;
|
|
49
|
+
}
|
|
50
|
+
return hasPath(value, options) ? value : null;
|
|
51
|
+
}
|
|
52
|
+
export { fitPathToOptions, fitToOptions };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","names":[],"sources":["../../src/fields/options.ts"],"sourcesContent":["/** The values a list of options offers, through groups (`options`) and levels (`children`). */\nfunction valuesOf(options: readonly unknown[], into = new Set<unknown>()) {\n for (const option of options) {\n if (option === null || typeof option !== 'object') {\n into.add(option)\n continue\n }\n const record = option as { value?: unknown; options?: unknown; children?: unknown }\n if ('value' in record) into.add(record.value)\n if (Array.isArray(record.options)) valuesOf(record.options, into)\n if (Array.isArray(record.children)) valuesOf(record.children, into)\n }\n return into\n}\n\n/**\n * A value narrowed to what the options offer: a single value the options\n * lack becomes `null`, and a list keeps only the items they have. The\n * same value comes back when nothing is lost.\n */\nexport function fitToOptions(value: unknown, options: readonly unknown[]): unknown {\n if (value === null || value === undefined) return value\n const offered = valuesOf(options)\n if (Array.isArray(value)) {\n const kept = value.filter((item) => offered.has(item))\n return kept.length === value.length ? value : kept\n }\n return offered.has(value) ? value : null\n}\n\n/** Whether a path of values, one per level, runs through the options' `children`. */\nfunction hasPath(path: readonly unknown[], options: readonly unknown[]): boolean {\n let level: readonly unknown[] = options\n for (const step of path) {\n const option = level.find(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n (entry as { value?: unknown }).value === step,\n ) as { children?: unknown } | undefined\n if (!option) return false\n level = Array.isArray(option.children) ? option.children : []\n }\n return true\n}\n\n/**\n * Like `fitToOptions`, for a value that is a path through levels of\n * options, or a list of such paths.\n */\nexport function fitPathToOptions(value: unknown, options: readonly unknown[]): unknown {\n if (!Array.isArray(value) || value.length === 0) return value\n if (Array.isArray(value[0])) {\n const kept = value.filter((path) => Array.isArray(path) && hasPath(path, options))\n return kept.length === value.length ? value : kept\n }\n return hasPath(value, options) ? value : null\n}\n"],"mappings":";;AACA,SAAS,SAAS,SAA6B,uBAAO,IAAI,IAAa,GAAG;CACxE,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;GACjD,KAAK,IAAI,MAAM;GACf;EACF;EACA,MAAM,SAAS;EACf,IAAI,WAAW,QAAQ,KAAK,IAAI,OAAO,KAAK;EAC5C,IAAI,MAAM,QAAQ,OAAO,OAAO,GAAG,SAAS,OAAO,SAAS,IAAI;EAChE,IAAI,MAAM,QAAQ,OAAO,QAAQ,GAAG,SAAS,OAAO,UAAU,IAAI;CACpE;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,aAAa,OAAgB,SAAsC;CACjF,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;CAClD,MAAM,UAAU,SAAS,OAAO;CAChC,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,MAAM,OAAO,MAAM,QAAQ,SAAS,QAAQ,IAAI,IAAI,CAAC;EACrD,OAAO,KAAK,WAAW,MAAM,SAAS,QAAQ;CAChD;CACA,OAAO,QAAQ,IAAI,KAAK,IAAI,QAAQ;AACtC;;AAGA,SAAS,QAAQ,MAA0B,SAAsC;CAC/E,IAAI,QAA4B;CAChC,KAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,SAAS,MAAM,MAClB,UACC,UAAU,QACV,OAAO,UAAU,YAChB,MAA8B,UAAU,IAC7C;EACA,IAAI,CAAC,QAAQ,OAAO;EACpB,QAAQ,MAAM,QAAQ,OAAO,QAAQ,IAAI,OAAO,WAAW,CAAC;CAC9D;CACA,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,OAAgB,SAAsC;CACrF,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG,OAAO;CACxD,IAAI,MAAM,QAAQ,MAAM,EAAE,GAAG;EAC3B,MAAM,OAAO,MAAM,QAAQ,SAAS,MAAM,QAAQ,IAAI,KAAK,QAAQ,MAAM,OAAO,CAAC;EACjF,OAAO,KAAK,WAAW,MAAM,SAAS,QAAQ;CAChD;CACA,OAAO,QAAQ,OAAO,OAAO,IAAI,QAAQ;AAC3C"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cloneValue, createId, emptyDocument, fieldNodes, findNode, getPath, isContainer, isField, isList, isPage, isSection, isSteps, isTabs, setPath, unsetPath, valueNodes, walkNodes } from "./core/document.js";
|
|
2
|
+
import { ExpressionError, builtinFunctions, checkExpression, compileExpression, equalValues, evaluateExpression, isEmptyValue, parseExpression, truthy } from "./core/expression.js";
|
|
3
|
+
import { defineFormField, fieldProps, useFormField } from "./fields/define.js";
|
|
4
|
+
import { emptyItem, fillValues, startValue, visibleValues } from "./core/values.js";
|
|
5
|
+
import { fitPathToOptions, fitToOptions } from "./fields/options.js";
|
|
6
|
+
import { normalizeOptions } from "./fields/components.js";
|
|
7
|
+
import { formatValue } from "./fields/format.js";
|
|
8
|
+
import { builtinFields, resolveFields } from "./fields/builtin.js";
|
|
9
|
+
import { formRendererLocales, formatMessage, useFormRendererMessages } from "./locale.js";
|
|
10
|
+
import { findFormulaCycles } from "./core/formulas.js";
|
|
11
|
+
import { rulesOf, toFormRule } from "./core/rules.js";
|
|
12
|
+
import { checkDocument, checkDocumentShape, migrateDocument } from "./core/check.js";
|
|
13
|
+
import { DouFormRenderer } from "./renderer/index.js";
|
|
14
|
+
import { FormBuilderTransaction, applyFormSteps, canContainFormNode, compactFormSteps, formNodePlaces } from "./builder/core/transaction.js";
|
|
15
|
+
import { FormBuilderHistory } from "./builder/core/history.js";
|
|
16
|
+
import { useFormBuilder } from "./builder/context.js";
|
|
17
|
+
import { defineFormBuilderExtension, resolveFormBuilderExtensions, resolveFormBuilderFields } from "./builder/extension.js";
|
|
18
|
+
import { formBuilderLocales, useFormBuilderMessages } from "./builder/locale.js";
|
|
19
|
+
import { DouFormBuilder, DouFormBuilderCanvas, DouFormBuilderInspector, DouFormBuilderOutline, DouFormBuilderPalette, DouFormBuilderPreview } from "./builder/index.js";
|
|
20
|
+
export { DouFormBuilder, DouFormBuilderCanvas, DouFormBuilderInspector, DouFormBuilderOutline, DouFormBuilderPalette, DouFormBuilderPreview, DouFormRenderer, ExpressionError, FormBuilderHistory, FormBuilderTransaction, applyFormSteps, builtinFields, builtinFunctions, canContainFormNode, checkDocument, checkDocumentShape, checkExpression, cloneValue, compactFormSteps, compileExpression, createId, DouFormRenderer as default, defineFormBuilderExtension, defineFormField, emptyDocument, emptyItem, equalValues, evaluateExpression, fieldNodes, fieldProps, fillValues, findFormulaCycles, findNode, fitPathToOptions, fitToOptions, formBuilderLocales, formNodePlaces, formRendererLocales, formatMessage, formatValue, getPath, isContainer, isEmptyValue, isField, isList, isPage, isSection, isSteps, isTabs, migrateDocument, normalizeOptions, parseExpression, resolveFields, resolveFormBuilderExtensions, resolveFormBuilderFields, rulesOf, setPath, startValue, toFormRule, truthy, unsetPath, useFormBuilder, useFormBuilderMessages, useFormField, useFormRendererMessages, valueNodes, visibleValues, walkNodes };
|