@speykye/vue-form-engine 0.1.0 → 0.1.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/README.md +308 -0
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# @speykye/vue-form-engine
|
|
2
|
+
|
|
3
|
+
A lightweight UI-agnostic schema-driven form engine for Vue 3.
|
|
4
|
+
|
|
5
|
+
> Status: Experimental / 0.x. The API may change before 1.0.
|
|
6
|
+
|
|
7
|
+
Vue Form Engine is designed for complex business forms that involve conditional fields, custom business blocks, array fields, async validation, hidden payload strategies, and UI adapter requirements.
|
|
8
|
+
|
|
9
|
+
Use native UI form components for simple forms. Use Vue Form Engine when your forms need a reusable runtime protocol and schema-driven behavior.
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Vue has many excellent UI form components, such as Ant Design Vue, Element Plus, Vant, Naive UI, and others.
|
|
14
|
+
|
|
15
|
+
However, complex business forms often need more than UI components:
|
|
16
|
+
|
|
17
|
+
- Conditional field visibility
|
|
18
|
+
- Conditional disabled state
|
|
19
|
+
- Hidden field payload strategy
|
|
20
|
+
- Async validation with debounce
|
|
21
|
+
- Custom field registration
|
|
22
|
+
- Custom business block registration
|
|
23
|
+
- Array fields
|
|
24
|
+
- UI-library-independent schema
|
|
25
|
+
- Reusable form behavior across projects
|
|
26
|
+
|
|
27
|
+
Vue Form Engine provides a small runtime layer between your schema and your UI library.
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
Schema
|
|
31
|
+
↓
|
|
32
|
+
Core Runtime
|
|
33
|
+
↓
|
|
34
|
+
Vue Renderer
|
|
35
|
+
↓
|
|
36
|
+
UI Adapter
|
|
37
|
+
↓
|
|
38
|
+
Ant Design Vue / Custom Components
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
With Ant Design Vue adapter:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pnpm add @speykye/vue-form-engine @speykye/vue-form-engine-adapter-antdv ant-design-vue
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or with npm:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install @speykye/vue-form-engine @speykye/vue-form-engine-adapter-antdv ant-design-vue
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Basic setup
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { createApp } from 'vue';
|
|
59
|
+
import Antd from 'ant-design-vue';
|
|
60
|
+
import 'ant-design-vue/dist/reset.css';
|
|
61
|
+
|
|
62
|
+
import App from './App.vue';
|
|
63
|
+
import { createVueFormEngine } from '@speykye/vue-form-engine';
|
|
64
|
+
import { antdvAdapter } from '@speykye/vue-form-engine-adapter-antdv';
|
|
65
|
+
|
|
66
|
+
const app = createApp(App);
|
|
67
|
+
|
|
68
|
+
app.use(Antd);
|
|
69
|
+
|
|
70
|
+
app.use(
|
|
71
|
+
createVueFormEngine({
|
|
72
|
+
plugins: [antdvAdapter()]
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
app.mount('#app');
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Basic usage
|
|
80
|
+
|
|
81
|
+
```vue
|
|
82
|
+
<script setup lang="ts">
|
|
83
|
+
import { ref } from 'vue';
|
|
84
|
+
import type { FormSchema } from '@speykye/vue-form-engine';
|
|
85
|
+
|
|
86
|
+
const model = ref({});
|
|
87
|
+
|
|
88
|
+
const schema: FormSchema = {
|
|
89
|
+
sections: [
|
|
90
|
+
{
|
|
91
|
+
title: 'Account',
|
|
92
|
+
items: [
|
|
93
|
+
{
|
|
94
|
+
key: 'username',
|
|
95
|
+
type: 'input',
|
|
96
|
+
label: 'Username',
|
|
97
|
+
placeholder: 'Please enter username',
|
|
98
|
+
rules: [
|
|
99
|
+
{ required: true, message: 'Username is required' }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: 'userType',
|
|
104
|
+
type: 'select',
|
|
105
|
+
label: 'User Type',
|
|
106
|
+
options: [
|
|
107
|
+
{ label: 'Normal', value: 'normal' },
|
|
108
|
+
{ label: 'Enterprise', value: 'enterprise' }
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: 'companyName',
|
|
113
|
+
type: 'input',
|
|
114
|
+
label: 'Company Name',
|
|
115
|
+
visibleWhen: {
|
|
116
|
+
deps: ['userType'],
|
|
117
|
+
predicate: model => model.userType === 'enterprise'
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
hiddenValueStrategy: 'submitVisibleOnly'
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
function handleSubmit(values: Record<string, any>) {
|
|
127
|
+
console.log('submit values:', values);
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<template>
|
|
132
|
+
<DynamicFormEngine
|
|
133
|
+
v-model="model"
|
|
134
|
+
:schema="schema"
|
|
135
|
+
@submit="handleSubmit"
|
|
136
|
+
/>
|
|
137
|
+
</template>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Features
|
|
141
|
+
|
|
142
|
+
- UI-agnostic form runtime
|
|
143
|
+
- Schema-driven rendering
|
|
144
|
+
- Core / renderer / adapter architecture
|
|
145
|
+
- Field registry
|
|
146
|
+
- Block registry
|
|
147
|
+
- Custom field support
|
|
148
|
+
- Custom business block support
|
|
149
|
+
- Conditional visibility
|
|
150
|
+
- Conditional disabled state
|
|
151
|
+
- Hidden value strategy
|
|
152
|
+
- Array field support
|
|
153
|
+
- Async validation debounce
|
|
154
|
+
- Ant Design Vue adapter
|
|
155
|
+
- TypeScript support
|
|
156
|
+
|
|
157
|
+
## Hidden value strategy
|
|
158
|
+
|
|
159
|
+
Complex forms often hide fields based on user choices.
|
|
160
|
+
|
|
161
|
+
For example, when `userType` changes from `enterprise` to `normal`, the `companyName` field may become hidden. Without a payload strategy, the hidden value may still be submitted.
|
|
162
|
+
|
|
163
|
+
Vue Form Engine supports:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
hiddenValueStrategy: 'keep' | 'clear' | 'submitVisibleOnly'
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Recommended default for business forms:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
hiddenValueStrategy: 'submitVisibleOnly'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This keeps values in the local model but excludes hidden fields from the submitted payload.
|
|
176
|
+
|
|
177
|
+
## Custom fields
|
|
178
|
+
|
|
179
|
+
You can register custom field components through the form engine.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
formEngine.registerField('captcha', CaptchaField);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Then use it in schema:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
{
|
|
189
|
+
key: 'captcha',
|
|
190
|
+
type: 'captcha',
|
|
191
|
+
label: 'Captcha'
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
A field component should follow the standard field component protocol:
|
|
196
|
+
|
|
197
|
+
- Props: `field`, `modelValue`, `model`, `disabled`, `readonly`, `errorMessage`
|
|
198
|
+
- Emits: `update:modelValue`, `change`, `blur`
|
|
199
|
+
|
|
200
|
+
## Custom blocks
|
|
201
|
+
|
|
202
|
+
Use custom blocks for business components that control multiple model keys.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
formEngine.registerBlock('invitationCode', InvitationCodeBlock);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Schema example:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
{
|
|
212
|
+
kind: 'block',
|
|
213
|
+
key: 'invitationCodeBlock',
|
|
214
|
+
type: 'invitationCode',
|
|
215
|
+
props: {
|
|
216
|
+
invitationCodeKey: 'invitationCode',
|
|
217
|
+
confirmedInvitationCodeKey: 'confirmedInvitationCode',
|
|
218
|
+
invitationVerifiedKey: 'invitationVerified'
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Blocks are useful for scenarios such as:
|
|
224
|
+
|
|
225
|
+
- Invitation code verification
|
|
226
|
+
- Captcha groups
|
|
227
|
+
- Address picker with coordinate output
|
|
228
|
+
- Upload widgets
|
|
229
|
+
- Complex business-specific form sections
|
|
230
|
+
|
|
231
|
+
## Array fields
|
|
232
|
+
|
|
233
|
+
Array fields are useful for repeated object groups.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
{
|
|
237
|
+
kind: 'array',
|
|
238
|
+
key: 'contacts',
|
|
239
|
+
type: 'array',
|
|
240
|
+
label: 'Contacts',
|
|
241
|
+
minItems: 1,
|
|
242
|
+
maxItems: 5,
|
|
243
|
+
itemFields: [
|
|
244
|
+
{
|
|
245
|
+
key: 'name',
|
|
246
|
+
type: 'input',
|
|
247
|
+
label: 'Name'
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
key: 'phone',
|
|
251
|
+
type: 'input',
|
|
252
|
+
label: 'Phone'
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Submitted model:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
{
|
|
262
|
+
contacts: [
|
|
263
|
+
{
|
|
264
|
+
name: 'Alice',
|
|
265
|
+
phone: '123456'
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## When should you use this?
|
|
272
|
+
|
|
273
|
+
Use Vue Form Engine when your form has:
|
|
274
|
+
|
|
275
|
+
- Complex conditional logic
|
|
276
|
+
- Many reusable business fields
|
|
277
|
+
- Custom widgets or blocks
|
|
278
|
+
- Dynamic array fields
|
|
279
|
+
- Async validation
|
|
280
|
+
- Hidden value cleanup requirements
|
|
281
|
+
- Multiple UI library targets
|
|
282
|
+
- Internal platform or SaaS form requirements
|
|
283
|
+
|
|
284
|
+
## When should you not use this?
|
|
285
|
+
|
|
286
|
+
Do not use this engine for very simple forms.
|
|
287
|
+
|
|
288
|
+
For example, if your form only has a few static fields, native UI library forms are usually simpler and better:
|
|
289
|
+
|
|
290
|
+
```vue
|
|
291
|
+
<el-form>
|
|
292
|
+
<el-input />
|
|
293
|
+
</el-form>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Packages
|
|
297
|
+
|
|
298
|
+
- `@speykye/vue-form-engine-core` — core protocol and runtime utilities
|
|
299
|
+
- `@speykye/vue-form-engine` — Vue renderer and plugin
|
|
300
|
+
- `@speykye/vue-form-engine-adapter-antdv` — Ant Design Vue adapter
|
|
301
|
+
|
|
302
|
+
## Links
|
|
303
|
+
|
|
304
|
+
- GitHub: https://github.com/speykye/vue-form-engine
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speykye/vue-form-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
17
18
|
],
|
|
18
19
|
"peerDependencies": {
|
|
19
20
|
"vue": "^3.5.0"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@speykye/vue-form-engine-core": "^0.1.
|
|
23
|
+
"@speykye/vue-form-engine-core": "^0.1.1"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
|
25
26
|
"build": "vue-tsc --declaration --emitDeclarationOnly --outDir dist && vite build",
|