@ramathibodi/nuxt-commons 0.1.73 → 0.1.75
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 +115 -96
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/components/Alert.vue +58 -54
- package/dist/runtime/components/BarcodeReader.vue +130 -122
- package/dist/runtime/components/ExportCSV.vue +110 -102
- package/dist/runtime/components/FileBtn.vue +79 -67
- package/dist/runtime/components/ImportCSV.vue +151 -139
- package/dist/runtime/components/MrzReader.vue +168 -0
- package/dist/runtime/components/SplitterPanel.vue +67 -59
- package/dist/runtime/components/TabsGroup.vue +39 -31
- package/dist/runtime/components/TextBarcode.vue +66 -54
- package/dist/runtime/components/device/IdCardButton.vue +95 -83
- package/dist/runtime/components/device/IdCardWebSocket.vue +207 -195
- package/dist/runtime/components/device/Scanner.vue +350 -338
- package/dist/runtime/components/dialog/Confirm.vue +112 -100
- package/dist/runtime/components/dialog/Host.vue +88 -84
- package/dist/runtime/components/dialog/Index.vue +84 -72
- package/dist/runtime/components/dialog/Loading.vue +51 -39
- package/dist/runtime/components/dialog/default/Confirm.vue +112 -100
- package/dist/runtime/components/dialog/default/Loading.vue +60 -48
- package/dist/runtime/components/dialog/default/Notify.vue +82 -70
- package/dist/runtime/components/dialog/default/Printing.vue +46 -34
- package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -132
- package/dist/runtime/components/document/Form.vue +50 -42
- package/dist/runtime/components/document/TemplateBuilder.vue +536 -524
- package/dist/runtime/components/form/ActionPad.vue +156 -144
- package/dist/runtime/components/form/Birthdate.vue +116 -104
- package/dist/runtime/components/form/CheckboxGroup.vue +99 -87
- package/dist/runtime/components/form/CodeEditor.vue +45 -37
- package/dist/runtime/components/form/Date.vue +270 -258
- package/dist/runtime/components/form/DateTime.vue +220 -208
- package/dist/runtime/components/form/Dialog.vue +178 -166
- package/dist/runtime/components/form/EditPad.vue +157 -145
- package/dist/runtime/components/form/File.vue +295 -283
- package/dist/runtime/components/form/Hidden.vue +44 -32
- package/dist/runtime/components/form/Iterator.vue +538 -526
- package/dist/runtime/components/form/Login.vue +143 -131
- package/dist/runtime/components/form/Pad.vue +399 -387
- package/dist/runtime/components/form/SignPad.vue +226 -218
- package/dist/runtime/components/form/System.vue +34 -26
- package/dist/runtime/components/form/Table.vue +391 -379
- package/dist/runtime/components/form/TableData.vue +236 -224
- package/dist/runtime/components/form/Time.vue +177 -165
- package/dist/runtime/components/form/images/Capture.vue +245 -237
- package/dist/runtime/components/form/images/Edit.vue +133 -121
- package/dist/runtime/components/form/images/Field.vue +331 -320
- package/dist/runtime/components/form/images/Pad.vue +54 -42
- package/dist/runtime/components/label/Date.vue +37 -29
- package/dist/runtime/components/label/DateAgo.vue +102 -94
- package/dist/runtime/components/label/DateCount.vue +152 -144
- package/dist/runtime/components/label/Field.vue +111 -103
- package/dist/runtime/components/label/FormatMoney.vue +37 -29
- package/dist/runtime/components/label/Mask.vue +46 -38
- package/dist/runtime/components/label/Object.vue +21 -13
- package/dist/runtime/components/master/Autocomplete.vue +89 -81
- package/dist/runtime/components/master/Combobox.vue +88 -80
- package/dist/runtime/components/master/RadioGroup.vue +90 -78
- package/dist/runtime/components/master/Select.vue +70 -62
- package/dist/runtime/components/master/label.vue +55 -47
- package/dist/runtime/components/model/Autocomplete.vue +91 -79
- package/dist/runtime/components/model/Combobox.vue +90 -78
- package/dist/runtime/components/model/Pad.vue +114 -102
- package/dist/runtime/components/model/Select.vue +78 -72
- package/dist/runtime/components/model/Table.vue +370 -358
- package/dist/runtime/components/model/iterator.vue +497 -489
- package/dist/runtime/components/model/label.vue +58 -50
- package/dist/runtime/components/pdf/Print.vue +75 -63
- package/dist/runtime/components/pdf/View.vue +146 -134
- package/dist/runtime/composables/alert.d.ts +4 -0
- package/dist/runtime/composables/api.d.ts +4 -0
- package/dist/runtime/composables/dialog.d.ts +1 -1
- package/dist/runtime/composables/document/templateFormHidden.d.ts +4 -0
- package/dist/runtime/composables/graphql.d.ts +1 -1
- package/dist/runtime/composables/graphqlModel.d.ts +9 -9
- package/dist/runtime/composables/graphqlModelItem.d.ts +7 -7
- package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
- package/dist/runtime/composables/localStorageModel.d.ts +4 -0
- package/dist/runtime/composables/lookupList.d.ts +4 -0
- package/dist/runtime/composables/menu.d.ts +4 -0
- package/dist/runtime/composables/useMrzReader.d.ts +48 -0
- package/dist/runtime/composables/useMrzReader.js +423 -0
- package/dist/runtime/composables/useTesseract.d.ts +16 -0
- package/dist/runtime/composables/useTesseract.js +45 -0
- package/dist/runtime/composables/userPermission.d.ts +1 -1
- package/dist/runtime/labs/Calendar.vue +99 -99
- package/dist/runtime/labs/form/EditMobile.vue +152 -152
- package/dist/runtime/labs/form/TextFieldMask.vue +43 -43
- package/dist/runtime/plugins/clientConfig.d.ts +1 -1
- package/dist/runtime/plugins/default.d.ts +1 -1
- package/dist/runtime/plugins/dialogManager.d.ts +1 -1
- package/dist/runtime/plugins/permission.d.ts +1 -1
- package/dist/runtime/types/alert.d.ts +11 -11
- package/dist/runtime/types/clientConfig.d.ts +13 -13
- package/dist/runtime/types/dialogManager.d.ts +35 -35
- package/dist/runtime/types/formDialog.d.ts +5 -5
- package/dist/runtime/types/graphqlOperation.d.ts +23 -23
- package/dist/runtime/types/menu.d.ts +31 -31
- package/dist/runtime/types/modules.d.ts +7 -7
- package/dist/runtime/types/permission.d.ts +13 -13
- package/dist/runtime/utils/asset.d.ts +2 -0
- package/dist/runtime/utils/asset.js +49 -0
- package/package.json +131 -122
- package/scripts/enrich-vue-docs-from-ai.mjs +197 -0
- package/scripts/generate-ai-summary.mjs +321 -0
- package/scripts/generate-composables-md.mjs +129 -0
- package/scripts/postInstall.cjs +70 -70
- package/templates/.codegen/codegen.ts +32 -32
- package/templates/.codegen/plugin-schema-object.js +161 -161
- package/templates/public/tesseract/mrz.traineddata.gz +0 -0
- package/templates/public/tesseract/ocrb.traineddata.gz +0 -0
|
@@ -1,390 +1,402 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
import
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
v
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* FormPad is a schema-driven form field component that binds model data, renders field UI, and emits normalized updates.
|
|
4
|
+
* This doc block is consumed by vue-docgen for generated API documentation.
|
|
5
|
+
*/
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any,import/no-self-import */
|
|
7
|
+
import {
|
|
8
|
+
compile,
|
|
9
|
+
computed,
|
|
10
|
+
defineComponent,
|
|
11
|
+
defineOptions,
|
|
12
|
+
inject,
|
|
13
|
+
onMounted,
|
|
14
|
+
ref,
|
|
15
|
+
shallowRef,
|
|
16
|
+
watch,
|
|
17
|
+
withDefaults
|
|
18
|
+
} from 'vue'
|
|
19
|
+
import {watchDebounced} from '@vueuse/core'
|
|
20
|
+
import {useRules} from '../../composables/utils/validation'
|
|
21
|
+
import {useDocumentTemplate} from '../../composables/document/template'
|
|
22
|
+
import { isObject, isArray, isString, isPlainObject, isEqual, debounce } from 'lodash-es'
|
|
23
|
+
import FormPad from './Pad.vue'
|
|
24
|
+
|
|
25
|
+
defineOptions({
|
|
26
|
+
inheritAttrs: false,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
modelValue?: object // Bound value for v-model synchronization with the parent component.
|
|
31
|
+
originalData?: object // Original baseline data used for dirty-checking and reset behavior.
|
|
32
|
+
template?: any // Template object used to render dynamic form/pad structure.
|
|
33
|
+
templateScript?: string // Optional script expression used to post-process template behavior.
|
|
34
|
+
disabled?: boolean // disables user interaction for this field
|
|
35
|
+
readonly?: boolean // renders as read-only while keeping value visible
|
|
36
|
+
isolated?: boolean // boolean flag controlling runtime behavior
|
|
37
|
+
decoration?: object // Decoration config used to style or annotate generated fields.
|
|
38
|
+
parentTemplates?: string|string[] // Parent template code(s) used for template inheritance/extension.
|
|
39
|
+
dirtyClass?: string // CSS class applied when form state differs from original data.
|
|
40
|
+
dirtyOnCreate?: boolean // Marks new forms as dirty immediately on first render.
|
|
41
|
+
sanitizeDelay?: number // Debounce delay before sanitizing emitted form values.
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Public props accepted by FormPad.
|
|
46
|
+
* Document each prop field with intent, defaults, and side effects for clear generated docs.
|
|
47
|
+
*/
|
|
48
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
49
|
+
disabled: false,
|
|
50
|
+
readonly: false,
|
|
51
|
+
isolated: false,
|
|
52
|
+
decoration: () => { return {} },
|
|
53
|
+
parentTemplates: (): string[] => [],
|
|
54
|
+
dirtyClass: "form-data-dirty",
|
|
55
|
+
dirtyOnCreate: false,
|
|
56
|
+
sanitizeDelay: 2000,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Custom events emitted by FormPad.
|
|
61
|
+
* Parents can listen to these events to react to user actions and internal state changes.
|
|
62
|
+
*/
|
|
63
|
+
const emit = defineEmits(['update:modelValue'])
|
|
64
|
+
|
|
65
|
+
const disabled = ref(props.disabled)
|
|
66
|
+
const readonly = ref(props.readonly)
|
|
67
|
+
const decoration = ref(props.decoration)
|
|
68
|
+
|
|
69
|
+
watch(() => props.disabled, (newValue) => {
|
|
70
|
+
disabled.value = newValue
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
watch(() => props.readonly, (newValue) => {
|
|
74
|
+
readonly.value = newValue
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
watch(() => props.decoration, (newValue) => {
|
|
78
|
+
decoration.value = newValue
|
|
79
|
+
}, { deep: true })
|
|
80
|
+
|
|
81
|
+
const { rules } = useRules()
|
|
82
|
+
|
|
83
|
+
const trimmedTemplate = ref<string>('')
|
|
84
|
+
|
|
85
|
+
const vueFunctions = { ref, shallowRef, computed, watch, onMounted }
|
|
86
|
+
|
|
87
|
+
const templateScriptFunction = computed(() => {
|
|
88
|
+
let templateScript = props.templateScript?.trim() || 'return {}'
|
|
89
|
+
const pattern = /^\s*[{[].*[}\]]\s*$/
|
|
90
|
+
if (pattern.test(templateScript)) templateScript = 'return {}'
|
|
91
|
+
return Function('props', 'ctx', ...Object.keys(vueFunctions), templateScript)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const formPad = ref()
|
|
95
|
+
const formInjectKey = Symbol.for('vuetify:form')
|
|
96
|
+
const formInjected = ref()
|
|
97
|
+
|
|
98
|
+
const formData = ref<any>({})
|
|
99
|
+
|
|
100
|
+
function isBlankString(v: unknown): v is string {
|
|
101
|
+
return isString(v) && v.trim().length === 0
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const sanitizeBlankStrings = debounce(sanitizeBlankStringsRaw, props.sanitizeDelay)
|
|
105
|
+
|
|
106
|
+
function sanitizeBlankStringsRaw(val: any, original?: any): void {
|
|
107
|
+
if (!original && props.originalData) {
|
|
108
|
+
sanitizeBlankStrings(val, props.originalData)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (isArray(val)) {
|
|
113
|
+
for (let i = val.length - 1; i >= 0; i--) {
|
|
114
|
+
const item = val[i]
|
|
115
|
+
if (isBlankString(item)) {
|
|
116
|
+
val.splice(i, 1)
|
|
117
|
+
} else if (isPlainObject(item) || isArray(item)) {
|
|
118
|
+
sanitizeBlankStrings(item,{})
|
|
119
|
+
} else {
|
|
120
|
+
if (item && typeof item === 'string') val[i] = item.replace(/[ \t]+$/, '')
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (isPlainObject(val)) {
|
|
127
|
+
for (const key of Object.keys(val)) {
|
|
128
|
+
const v = val[key]
|
|
129
|
+
if (isBlankString(v)) {
|
|
130
|
+
if (original && !Object.keys(original).includes(key)) delete val[key]
|
|
131
|
+
else val[key] = null
|
|
132
|
+
} else if (isPlainObject(v) || isArray(v)) {
|
|
133
|
+
let originalChild = (original && (isPlainObject(original[key]) || isArray(original[key]))) ? original[key] : {}
|
|
134
|
+
sanitizeBlankStrings(v, originalChild)
|
|
135
|
+
} else {
|
|
136
|
+
if (v && typeof v === 'string') val[key] = v.replace(/[ \t]+$/, '')
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function autoSanitizedDisplay(item: any, separator: string = ","): string | undefined {
|
|
143
|
+
const isEmptyScalar = (v: any) =>
|
|
144
|
+
v === null ||
|
|
145
|
+
v === undefined ||
|
|
146
|
+
(typeof v === "string" && v.trim() === "") ||
|
|
147
|
+
(typeof v === "number" && Number.isNaN(v));
|
|
148
|
+
|
|
149
|
+
const toStr = (v: any): string | undefined => {
|
|
150
|
+
if (isEmptyScalar(v)) return undefined;
|
|
151
|
+
if (typeof v === "string") return v.trim();
|
|
152
|
+
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
|
|
153
|
+
if (typeof v === "symbol") return v.description ?? String(v);
|
|
154
|
+
if (typeof v === "function") return v.name ? `[Function ${v.name}]` : "[Function]";
|
|
155
|
+
return undefined;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// 1) empty -> undefined
|
|
159
|
+
if (isEmptyScalar(item)) return undefined;
|
|
160
|
+
|
|
161
|
+
// 2) array -> recurse + join
|
|
162
|
+
if (Array.isArray(item)) {
|
|
163
|
+
const parts = item
|
|
164
|
+
.map((x) => autoSanitizedDisplay(x, separator))
|
|
165
|
+
.filter((s): s is string => typeof s === "string" && s.trim() !== "");
|
|
166
|
+
return parts.length ? parts.join(separator) : undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// simple scalars
|
|
170
|
+
const scalar = toStr(item);
|
|
171
|
+
if (scalar !== undefined) return scalar;
|
|
172
|
+
|
|
173
|
+
// 3) object
|
|
174
|
+
if (typeof item === "object") {
|
|
175
|
+
// 3.1 label
|
|
176
|
+
if ("label" in item) {
|
|
177
|
+
const v = autoSanitizedDisplay((item as any).label, separator) ?? toStr((item as any).label);
|
|
178
|
+
if (v !== undefined) return v;
|
|
179
|
+
}
|
|
180
|
+
// 3.2 value
|
|
181
|
+
if ("value" in item) {
|
|
182
|
+
const v = autoSanitizedDisplay((item as any).value, separator) ?? toStr((item as any).value);
|
|
183
|
+
if (v !== undefined) return v;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 3.3 stringify attributes as key:value (recurse values)
|
|
187
|
+
const entries = Object.entries(item as Record<string, any>)
|
|
188
|
+
.map(([k, v]) => {
|
|
189
|
+
const rendered = autoSanitizedDisplay(v, separator) ?? toStr(v);
|
|
190
|
+
if (rendered === undefined || rendered.trim() === "") return undefined;
|
|
191
|
+
return `${k}:${rendered}`;
|
|
192
|
+
})
|
|
193
|
+
.filter((x): x is string => typeof x === "string" && x.trim() !== "");
|
|
194
|
+
|
|
195
|
+
return entries.length ? entries.join(separator) : undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// fallback
|
|
199
|
+
try {
|
|
200
|
+
const s = String(item);
|
|
201
|
+
return s.trim() ? s : undefined;
|
|
202
|
+
} catch {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
watch(formData, (newValue) => {
|
|
208
|
+
sanitizeBlankStrings(newValue)
|
|
209
|
+
emit('update:modelValue', newValue)
|
|
210
|
+
}, { deep: true })
|
|
211
|
+
|
|
212
|
+
watch(() => props.modelValue, (newValue) => {
|
|
213
|
+
formData.value = isObject(newValue) ? newValue : {}
|
|
214
|
+
}, { deep: true, immediate: true })
|
|
215
|
+
|
|
216
|
+
function diffPaths(
|
|
217
|
+
a: any,
|
|
218
|
+
b: any,
|
|
219
|
+
base: string[] = [],
|
|
220
|
+
out: string[] = []
|
|
221
|
+
): string[] {
|
|
222
|
+
const keys = new Set<string>([
|
|
223
|
+
...Object.keys(a ?? {}),
|
|
224
|
+
...Object.keys(b ?? {}),
|
|
225
|
+
])
|
|
226
|
+
|
|
227
|
+
for (const k of keys) {
|
|
228
|
+
const av = a?.[k]
|
|
229
|
+
const bv = b?.[k]
|
|
230
|
+
if (!av && !bv) continue // ignore when both are falsy
|
|
231
|
+
|
|
232
|
+
const path = [...base, k]
|
|
233
|
+
|
|
234
|
+
if (isPlainObject(av) && isPlainObject(bv)) {
|
|
235
|
+
diffPaths(av, bv, path, out)
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!isEqual(av, bv)) {
|
|
240
|
+
out.push(path.join('.'))
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return out
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const injectedClass = computed<Record<string, string>>(() => {
|
|
248
|
+
const data = (formData.value ?? {}) as Record<string, any>
|
|
249
|
+
const result: Record<string, string> = {}
|
|
250
|
+
|
|
251
|
+
if (!props.dirtyOnCreate && !props.originalData) return result
|
|
252
|
+
|
|
253
|
+
const original = (props.originalData ?? {}) as Record<string, any>
|
|
254
|
+
const cls = props.dirtyClass || 'form-data-dirty'
|
|
255
|
+
|
|
256
|
+
const paths = diffPaths(data, original)
|
|
257
|
+
for (const p of paths) result[p] = cls
|
|
258
|
+
|
|
259
|
+
return result
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const formComponent = shallowRef()
|
|
263
|
+
|
|
264
|
+
function buildFormComponent() {
|
|
265
|
+
if (!trimmedTemplate.value) return
|
|
266
|
+
const originalConsoleError = console.warn
|
|
267
|
+
console.warn = (error: any) => { throw new Error(error) } // eslint-disable-line
|
|
268
|
+
try {
|
|
269
|
+
const componentTemplate = '<form-pad ref="formPadTemplate" v-model="formComponentData" :originalData="originalData" :disabled="disabled" :readonly="readonly" :decoration="decoration" :isolated="isolated"><template v-slot="{ data,isDisabled,isReadonly,rules,formProvided,decoration,injectedClass }">' + trimmedTemplate.value + '</template></form-pad>'
|
|
270
|
+
compile(componentTemplate)
|
|
271
|
+
formComponent.value = defineComponent({
|
|
272
|
+
components: { FormPad },
|
|
273
|
+
props: {
|
|
274
|
+
modelValue: { type: Object, default: undefined },
|
|
275
|
+
originalData: { type: Object, default: undefined },
|
|
276
|
+
disabled: { type: Boolean, default: false },
|
|
277
|
+
readonly: { type: Boolean, default: false },
|
|
278
|
+
decoration: { type: Object, default: () => { return {} } },
|
|
279
|
+
isolated: { type: Boolean, default: false },
|
|
280
|
+
},
|
|
281
|
+
emits: ['update:modelValue'],
|
|
282
|
+
setup(props, ctx) {
|
|
283
|
+
const formComponentData = ref<any>({})
|
|
284
|
+
const formPadTemplate = ref<any>({})
|
|
285
|
+
watch(formComponentData, (newValue) => {
|
|
286
|
+
sanitizeBlankStrings(newValue)
|
|
287
|
+
ctx.emit('update:modelValue', newValue)
|
|
288
|
+
}, { deep: true })
|
|
289
|
+
watch(() => props.modelValue, (newValue) => {
|
|
290
|
+
formComponentData.value = isObject(newValue) ? newValue : {}
|
|
291
|
+
}, { deep: true, immediate: true })
|
|
292
|
+
const isValid = computed(() => formPadTemplate.value.isValid)
|
|
293
|
+
return {
|
|
294
|
+
formComponentData,
|
|
295
|
+
formPadTemplate,
|
|
296
|
+
reset: () => formPadTemplate.value.reset(),
|
|
297
|
+
validate: () => formPadTemplate.value.validate(),
|
|
298
|
+
resetValidate: () => formPadTemplate.value.resetValidate(),
|
|
299
|
+
isValid,
|
|
300
|
+
...templateScriptFunction.value(props, ctx, ...Object.values(vueFunctions)),
|
|
301
|
+
autoSanitizedDisplay
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
template: componentTemplate,
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
formComponent.value = null
|
|
309
|
+
console.error(e)
|
|
310
|
+
}
|
|
311
|
+
console.warn = originalConsoleError
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function reset() {
|
|
315
|
+
if (!formInjected.value) formPad.value.reset()
|
|
316
|
+
else formInjected.value.items.forEach((item: any) => item.reset())
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function validate() {
|
|
320
|
+
if (!formInjected.value) formPad.value.validate()
|
|
321
|
+
else formInjected.value.items.forEach((item: any) => item.validate())
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function resetValidate() {
|
|
325
|
+
if (!formInjected.value) formPad.value.resetValidate()
|
|
326
|
+
else formInjected.value.items.forEach((item: any) => item.resetValidate())
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const isValid = computed(() => {
|
|
330
|
+
validate()
|
|
331
|
+
return formInjected.value ? formInjected.value.isValid || false : formPad.value.isValid || false
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
onMounted(() => {
|
|
335
|
+
if (!props.isolated) formInjected.value = inject(formInjectKey, false)
|
|
336
|
+
buildFormComponent()
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
watchDebounced(() => props.template, (newValue) => {
|
|
340
|
+
trimmedTemplate.value = useDocumentTemplate(newValue,props.parentTemplates).trim() || ''
|
|
341
|
+
buildFormComponent()
|
|
342
|
+
}, { debounce: 500, maxWait: 5000, deep: true, immediate: true })
|
|
343
|
+
watchDebounced(() => props.templateScript, buildFormComponent, { debounce: 500, maxWait: 5000 })
|
|
344
|
+
|
|
345
|
+
defineExpose({
|
|
346
|
+
isValid,
|
|
347
|
+
disabled,
|
|
348
|
+
readonly,
|
|
349
|
+
reset,
|
|
350
|
+
validate,
|
|
351
|
+
resetValidate,
|
|
352
|
+
})
|
|
353
|
+
</script>
|
|
354
|
+
|
|
355
|
+
<template>
|
|
356
|
+
<v-form
|
|
357
|
+
v-if="!formInjected && !trimmedTemplate"
|
|
358
|
+
ref="formPad"
|
|
359
|
+
:disabled="disabled"
|
|
360
|
+
:readonly="readonly"
|
|
361
|
+
:class="$attrs.class"
|
|
362
|
+
autocomplete="off"
|
|
363
|
+
>
|
|
364
|
+
<template #default="formProvided">
|
|
365
|
+
<slot
|
|
366
|
+
:data="formData"
|
|
367
|
+
:form-provided="formProvided"
|
|
368
|
+
:is-disabled="disabled"
|
|
369
|
+
:is-readonly="readonly"
|
|
370
|
+
:rules="rules"
|
|
371
|
+
:decoration="decoration"
|
|
372
|
+
:injectedClass="injectedClass"
|
|
373
|
+
/>
|
|
374
|
+
</template>
|
|
375
|
+
</v-form>
|
|
376
|
+
<template v-if="formInjected && !trimmedTemplate">
|
|
377
|
+
<slot
|
|
378
|
+
:data="formData"
|
|
379
|
+
:form-provided="formInjected"
|
|
380
|
+
:is-disabled="disabled"
|
|
381
|
+
:is-readonly="readonly"
|
|
382
|
+
:rules="rules"
|
|
383
|
+
:decoration="decoration"
|
|
384
|
+
:injectedClass="injectedClass"
|
|
385
|
+
/>
|
|
386
|
+
</template>
|
|
387
|
+
<component
|
|
388
|
+
:is="formComponent"
|
|
389
|
+
v-if="trimmedTemplate"
|
|
390
|
+
ref="formPad"
|
|
391
|
+
v-model="formData"
|
|
392
|
+
:originalData="originalData"
|
|
393
|
+
:disabled="disabled"
|
|
394
|
+
:readonly="readonly"
|
|
395
|
+
:decoration="decoration"
|
|
396
|
+
:isolated="isolated"
|
|
397
|
+
:class="$attrs.class"
|
|
398
|
+
/>
|
|
399
|
+
</template>
|
|
388
400
|
<style>
|
|
389
401
|
.form-data-dirty:not(.v-input--error) :not(.v-chip):not(.v-chip *){color:color-mix(in srgb,currentColor 70%,rgb(var(--v-theme-primary)))!important;text-shadow:0 0 .02em currentColor}
|
|
390
402
|
</style>
|