@ramathibodi/nuxt-commons 0.1.56 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/document/TemplateBuilder.vue +133 -122
  3. package/dist/runtime/components/form/Pad.vue +39 -0
  4. package/dist/runtime/components/form/TableData.vue +180 -41
  5. package/dist/runtime/components/label/Object.vue +14 -0
  6. package/dist/runtime/components/master/Autocomplete.vue +25 -108
  7. package/dist/runtime/components/master/Combobox.vue +59 -83
  8. package/dist/runtime/components/model/Autocomplete.vue +23 -234
  9. package/dist/runtime/components/model/Combobox.vue +70 -0
  10. package/dist/runtime/composables/document/template.d.ts +4 -1
  11. package/dist/runtime/composables/document/template.js +22 -15
  12. package/dist/runtime/composables/document/templateFormHidden.d.ts +1 -1
  13. package/dist/runtime/composables/document/templateFormHidden.js +5 -2
  14. package/dist/runtime/composables/document/templateFormTable.d.ts +3 -2
  15. package/dist/runtime/composables/document/templateFormTable.js +36 -40
  16. package/dist/runtime/composables/document/templateFormTableData.d.ts +2 -0
  17. package/dist/runtime/composables/document/templateFormTableData.js +21 -0
  18. package/dist/runtime/composables/graphqlModel.d.ts +6 -6
  19. package/dist/runtime/composables/graphqlModelItem.d.ts +4 -4
  20. package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
  21. package/dist/runtime/composables/lookupList.d.ts +44 -0
  22. package/dist/runtime/composables/lookupList.js +194 -0
  23. package/dist/runtime/composables/lookupListMaster.d.ts +28 -0
  24. package/dist/runtime/composables/lookupListMaster.js +61 -0
  25. package/dist/runtime/types/menu.d.ts +7 -4
  26. package/dist/runtime/utils/formatter.d.ts +1 -0
  27. package/dist/runtime/utils/formatter.js +19 -0
  28. package/dist/runtime/utils/object.d.ts +1 -0
  29. package/dist/runtime/utils/object.js +46 -1
  30. package/package.json +2 -1
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.56",
7
+ "version": "0.1.57",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
@@ -3,14 +3,20 @@ import {computed, ref, watch} from 'vue'
3
3
  import * as prettier from 'prettier'
4
4
  import prettierPluginHtml from 'prettier/plugins/html'
5
5
  import {useDocumentTemplate, validationRulesRegex,optionStringToChoiceObject} from '../../composables/document/template'
6
- import {cloneDeep , filter} from "lodash-es";
6
+ import {autoActionHeader,templateToHeader} from "../../composables/document/templateFormTable";
7
+ import {cloneDeep} from "lodash-es";
8
+ import VueJsonPretty from 'vue-json-pretty';
9
+ import 'vue-json-pretty/lib/styles.css';
10
+ import {safeParseJSONDeep} from "../../utils/object";
7
11
 
8
12
  interface Props {
9
13
  title?: string
14
+ disableAdvanceMode?: boolean
10
15
  }
11
16
 
12
17
  const props = withDefaults(defineProps<Props>(), {
13
- title: "Document Template"
18
+ title: "Document Template",
19
+ disableAdvanceMode: false,
14
20
  })
15
21
 
16
22
  const emit = defineEmits(['update:modelValue'])
@@ -157,9 +163,10 @@ const notRequireLabel = ref(['Separator', 'CustomCode', 'FormFile', 'FormHidden'
157
163
  const notRequireWidth = ref(['Separator', 'FormHidden','DocumentForm'])
158
164
  const notRequireOptions = ref(['Separator','CustomCode', 'FormFile'])
159
165
  const notRequireRules = ref(['Separator','Header','CustomCode','FormHidden','DocumentForm'])
160
- const notRequireInputAttributes = ref(['CustomCode','Header', 'FormHidden','DocumentForm'])
166
+ const notRequireInputAttributes = ref(['CustomCode','Header','DocumentForm'])
161
167
  const notRequireColumnAttributes = ref(['Separator', 'FormHidden','DocumentForm'])
162
- const notRequireAdvancedSetting = ref(['Separator','CustomCode', 'FormHidden'])
168
+ const notRequireAdvancedSetting = ref(['Separator','CustomCode'])
169
+ const notRequireClassAndStyle = ref(['FormHidden'])
163
170
 
164
171
  const hasSpecificOption = ref(['FormHidden','FormTable','FormTableData'])
165
172
 
@@ -189,6 +196,7 @@ const ruleOptions = (inputType: string) => (value: any) => {
189
196
  color="primary"
190
197
  variant="flat"
191
198
  @click="convertToAdvanceMode()"
199
+ v-if="!disableAdvanceMode"
192
200
  >
193
201
  Convert To Advance
194
202
  </VBtn>
@@ -249,6 +257,9 @@ const ruleOptions = (inputType: string) => (value: any) => {
249
257
  <v-col cols="3"><v-text-field v-model="headerData.key" label="Key" :rules="[rules.require()]"></v-text-field></v-col>
250
258
  <v-col cols="3"><v-text-field v-model="headerData.width" label="Width"></v-text-field></v-col>
251
259
  </v-row>
260
+ <v-row dense>
261
+ <v-col cols="12"><v-textarea v-model="headerData.template" label="Item Template"></v-textarea></v-col>
262
+ </v-row>
252
263
  </v-container>
253
264
  </template>
254
265
  </form-table>
@@ -263,106 +274,71 @@ const ruleOptions = (inputType: string) => (value: any) => {
263
274
  <template #default="{data: optionData}">
264
275
  <v-row dense>
265
276
  <v-col cols="12">
266
- <form-table v-model="optionData.headers" :headers="formTableHeaders" title="Headers">
267
- <template #form="{data: headerData,rules}">
268
- <v-container fluid>
269
- <v-row dense>
270
- <v-col cols="6"><v-text-field v-model="headerData.title" label="Title"></v-text-field></v-col>
271
- <v-col cols="3"><v-text-field v-model="headerData.key" label="Key" :rules="[rules.require()]"></v-text-field></v-col>
272
- <v-col cols="3"><v-text-field v-model="headerData.width" label="Width"></v-text-field></v-col>
273
- </v-row>
274
- <v-row dense>
275
- <v-col cols="12">
276
- <template-builder title="Template" v-model="headerData.template"></template-builder>
277
- </v-col>
278
- </v-row>
279
- <v-row dense>
280
- <v-col cols="12">
281
- <template-builder title="Header Template" v-model="headerData.headerTemplate"></template-builder>
282
- </v-col>
283
- </v-row>
284
- <v-expansion-panels>
285
-
286
- <v-expansion-panel >
287
- <v-expansion-panel-title collapse-icon="mdi mdi-minus" expand-icon="mdi mdi-plus" class="font-weight-bold">Advanced settings</v-expansion-panel-title>
288
- <v-expansion-panel-text>
289
- <v-container fluid>
290
- <v-row dense>
291
- <v-col cols="12" md="6" v-if="!notRequireInputAttributes.includes(data.inputType)">
292
- <v-text-field
293
- v-model="headerData.inputAttributes"
294
- label="Input Attributes"
295
- />
296
- </v-col>
297
-
298
- <v-col cols="12" md="6">
299
- <v-text-field
300
- v-model="headerData.conditionalDisplay"
301
- label="Conditional Display"
302
- />
303
- </v-col>
304
- <v-col cols="12" md="6">
305
- <v-text-field
306
- v-model="headerData.computedValue"
307
- label="Computed Value"
308
- />
309
- </v-col>
310
- <v-col cols="12" md="6">
311
- <v-text-field
312
- v-model="headerData.retrievedValue"
313
- label="Retrieved Value"
314
- />
315
- </v-col>
316
- </v-row>
317
- </v-container>
318
- </v-expansion-panel-text>
319
- </v-expansion-panel>
320
- </v-expansion-panels>
321
- </v-container>
277
+ <tabs-group>
278
+ <template #tabs>
279
+ <v-tab value="itemTemplate">Item Template</v-tab>
280
+ <v-tab value="dataTemplate">Data Template</v-tab>
281
+ <v-tab value="header">Header</v-tab>
322
282
  </template>
323
- </form-table>
283
+ <template #items>
284
+ <v-tabs-window-item value="itemTemplate">
285
+ <document-template-builder v-model="optionData.itemTemplate" title="Item Template" disable-advance-mode></document-template-builder>
286
+ </v-tabs-window-item>
287
+ <v-tabs-window-item value="dataTemplate">
288
+ <document-template-builder v-model="optionData.dataTemplate" title="Data Template" disable-advance-mode></document-template-builder>
289
+ </v-tabs-window-item>
290
+ <v-tabs-window-item value="header">
291
+ <form-table v-model="optionData.headers" :headers="formTableHeaders" title="Headers">
292
+ <template #form="{data: headerData,rules}">
293
+ <v-container fluid>
294
+ <v-row dense>
295
+ <v-col cols="6"><v-text-field v-model="headerData.title" label="Title"></v-text-field></v-col>
296
+ <v-col cols="3"><v-text-field v-model="headerData.key" label="Key" :rules="[rules.require()]"></v-text-field></v-col>
297
+ <v-col cols="3"><v-text-field v-model="headerData.width" label="Width"></v-text-field></v-col>
298
+ </v-row>
299
+ <v-row dense>
300
+ <v-col cols="12"><v-text-field v-model="headerData.value" label="Value"></v-text-field></v-col>
301
+ <v-col cols="12"><v-textarea v-model="headerData.template" label="Item Template"></v-textarea></v-col>
302
+ </v-row>
303
+ </v-container>
304
+ </template>
305
+ </form-table>
306
+ </v-tabs-window-item>
307
+ </template>
308
+ </tabs-group>
324
309
  </v-col>
325
310
  <v-col cols="12">
326
- <form-table v-model="optionData.items"
327
- hide-default-footer
328
- :importable="false"
329
- :exportable="false"
330
- :searchable="false"
331
- :headers="optionData.headers" title="Data">
332
- <template #form="{data: headerData,rules}">
333
- <v-container fluid>
334
- <v-row dense>
335
- <v-col v-for="header of optionData.headers" cols="12">
336
- <v-text-field v-if="header.key != 'action' && !header.template"
337
- v-model="headerData[header.title]"
338
- :label="header.title">
339
-
340
- </v-text-field></v-col>
341
- </v-row>
342
- </v-container>
343
- </template>
344
-
345
- <template v-for="header of filter(optionData.headers,(item)=> item.template)" #[`item.${header.key}`]="{item}">
346
- <form-pad
347
- :template="header.template"
348
- v-model="item[header.key]">
349
- </form-pad>
350
-
311
+ <form-table v-model="optionData.items" :headers="(optionData.headers && optionData.headers.length>0) ? autoActionHeader(optionData.headers) : autoActionHeader(templateToHeader(optionData.itemTemplate))" title="Items">
312
+ <template #form="{data}">
313
+ <form-pad :model-value="data" :template="optionData.itemTemplate"></form-pad>
351
314
  </template>
352
- <template v-for="header of filter(optionData.headers,(item)=> item.headerTemplate)" #[`header.${header.key}`]="{item}">
353
- <form-pad
354
- :template="header.headerTemplate"
355
- >
356
- </form-pad>
357
-
358
- </template>
359
-
360
315
  </form-table>
361
-
316
+ </v-col>
317
+ <v-col cols="12">
318
+ <v-row dense>
319
+ <v-col cols="4">
320
+ <v-radio-group v-model="optionData.disableApplyToAll" inline>
321
+ <template #prepend>
322
+ <span class="opacity-60"> Disable Apply to All</span>
323
+ </template>
324
+ <v-radio label="No" value="false" hide-details> </v-radio>
325
+ <v-radio label="Yes" value="true" hide-details> </v-radio>
326
+ <v-radio label="Partial" value="partial" hide-details> </v-radio>
327
+ </v-radio-group>
328
+ </v-col>
329
+ <v-col cols="8">
330
+ <VTextField
331
+ variant="underlined"
332
+ v-model="optionData.disableApplyToAllPartial"
333
+ label="Disable Apply To All Column"
334
+ v-if="optionData.disableApplyToAll=='partial'"
335
+ >
336
+ </VTextField>
337
+ </v-col>
338
+ </v-row>
362
339
  </v-col>
363
340
  </v-row>
364
341
  </template>
365
-
366
342
  </form-pad>
367
343
  <form-pad v-model="data.inputOptions" v-if="data.inputType=='FormHidden'">
368
344
  <template #default="{data: optionData}">
@@ -371,7 +347,6 @@ const ruleOptions = (inputType: string) => (value: any) => {
371
347
  <v-text-field
372
348
  v-model="optionData.hook"
373
349
  label="Hook"
374
- :rules="[rules.require()]"
375
350
  />
376
351
  </v-col>
377
352
  <v-col cols="12">
@@ -473,37 +448,73 @@ const ruleOptions = (inputType: string) => (value: any) => {
473
448
  </v-container>
474
449
  </v-expansion-panel-text>
475
450
  </v-expansion-panel>
451
+ <v-expansion-panel v-if="!notRequireClassAndStyle.includes(data.inputType)">
452
+ <v-expansion-panel-title collapse-icon="mdi mdi-minus" expand-icon="mdi mdi-plus" class="font-weight-bold">Class and Styles</v-expansion-panel-title>
453
+ <v-expansion-panel-text>
454
+ <v-container fluid>
455
+ <v-row dense>
456
+ <v-col cols="12">
457
+ <v-text-field
458
+ v-model="data.customClass"
459
+ label="Custom Class"
460
+ />
461
+ </v-col>
462
+ <v-col cols="12">
463
+ <v-text-field
464
+ v-model="data.customStyle"
465
+ label="Custom Styles"
466
+ />
467
+ </v-col>
468
+ </v-row>
469
+ </v-container>
470
+ </v-expansion-panel-text>
471
+ </v-expansion-panel>
476
472
  </v-expansion-panels>
477
473
  </v-col>
478
474
  </v-row>
479
475
  </v-container>
480
476
  </template>
481
477
  <template #item.configuration="props">
482
- <template v-if="props.item.inputType=='CustomCode'">
483
- <b>Custom Code :</b><br>
484
- <span style="white-space: pre-line">{{ props.item.inputCustomCode }}</span>
485
- </template>
486
- <template v-if="props.item.validationRules">
487
- <b>Validation Rules :</b> {{ props.item.validationRules }}<br>
488
- </template>
489
- <template v-if="props.item.inputOptions">
490
- <b>Input Options :</b> {{ props.item.inputOptions }}<br>
491
- </template>
492
- <template v-if="props.item.inputAttributes">
493
- <b>Input Attributes :</b> {{ props.item.inputAttributes }}<br>
494
- </template>
495
- <template v-if="props.item.columnAttributes">
496
- <b>Column Attributes :</b> {{ props.item.columnAttributes }}<br>
497
- </template>
498
- <template v-if="props.item.conditionalDisplay">
499
- <b>Conditional Display :</b> {{ props.item.conditionalDisplay }}<br>
500
- </template>
501
- <template v-if="props.item.computedValue">
502
- <b>Computed Value :</b> {{ props.item.computedValue }}<br>
503
- </template>
504
- <template v-if="props.item.retrievedValue">
505
- <b>Retrieved Value :</b> {{ props.item.retrievedValue }}<br>
506
- </template>
478
+ <v-sheet
479
+ elevation="1"
480
+ max-height="250"
481
+ class="overflow-y-auto my-1 pa-2"
482
+ v-if="props.item.inputType=='CustomCode'
483
+ || props.item.validationRules
484
+ || props.item.inputOptions
485
+ || props.item.inputAttributes
486
+ || props.item.columnAttributes
487
+ || props.item.conditionalDisplay
488
+ || props.item.computedValue
489
+ || props.item.retrievedValue"
490
+ >
491
+ <template v-if="props.item.inputType=='CustomCode'">
492
+ <b>Custom Code :</b><br>
493
+ <span style="white-space: pre-line">{{ props.item.inputCustomCode }}</span>
494
+ </template>
495
+ <template v-if="props.item.validationRules">
496
+ <b>Validation Rules :</b> {{ props.item.validationRules }}<br>
497
+ </template>
498
+ <template v-if="props.item.inputOptions">
499
+ <b>Input Options :</b>
500
+ <vue-json-pretty :data="safeParseJSONDeep(props.item.inputOptions)" />
501
+ </template>
502
+ <template v-if="props.item.inputAttributes">
503
+ <b>Input Attributes :</b> {{ props.item.inputAttributes }}<br>
504
+ </template>
505
+ <template v-if="props.item.columnAttributes">
506
+ <b>Column Attributes :</b> {{ props.item.columnAttributes }}<br>
507
+ </template>
508
+ <template v-if="props.item.conditionalDisplay">
509
+ <b>Conditional Display :</b> {{ props.item.conditionalDisplay }}<br>
510
+ </template>
511
+ <template v-if="props.item.computedValue">
512
+ <b>Computed Value :</b> {{ props.item.computedValue }}<br>
513
+ </template>
514
+ <template v-if="props.item.retrievedValue">
515
+ <b>Retrieved Value :</b> {{ props.item.retrievedValue }}<br>
516
+ </template>
517
+ </v-sheet>
507
518
  </template>
508
519
  </FormTable>
509
520
  <FormCodeEditor
@@ -16,6 +16,7 @@ import {isObject} from 'lodash-es'
16
16
  import {watchDebounced} from '@vueuse/core'
17
17
  import {useRules} from '../../composables/utils/validation'
18
18
  import {useDocumentTemplate} from '../../composables/document/template'
19
+ import { isArray, isString, isPlainObject } from 'lodash-es'
19
20
  import FormPad from './Pad.vue'
20
21
 
21
22
  defineOptions({
@@ -78,7 +79,44 @@ const formInjected = ref()
78
79
 
79
80
  const formData = ref<any>({})
80
81
 
82
+ function isBlankString(v: unknown): v is string {
83
+ return isString(v) && v.length === 0
84
+ }
85
+
86
+ function sanitizeBlankStrings(val: any): boolean {
87
+ let changed = false
88
+
89
+ if (isArray(val)) {
90
+ for (let i = val.length - 1; i >= 0; i--) {
91
+ const item = val[i]
92
+ if (isBlankString(item)) {
93
+ val.splice(i, 1) // remove array element
94
+ changed = true
95
+ } else if (isPlainObject(item) || isArray(item)) {
96
+ if (sanitizeBlankStrings(item)) changed = true
97
+ }
98
+ }
99
+ return changed
100
+ }
101
+
102
+ if (isPlainObject(val)) {
103
+ for (const key of Object.keys(val)) {
104
+ const v = val[key]
105
+ if (isBlankString(v)) {
106
+ delete val[key] // remove property directly
107
+ changed = true
108
+ } else if (isPlainObject(v) || isArray(v)) {
109
+ if (sanitizeBlankStrings(v)) changed = true
110
+ }
111
+ }
112
+ return changed
113
+ }
114
+
115
+ return false
116
+ }
117
+
81
118
  watch(formData, (newValue) => {
119
+ sanitizeBlankStrings(newValue)
82
120
  emit('update:modelValue', newValue)
83
121
  }, { deep: true })
84
122
 
@@ -109,6 +147,7 @@ function buildFormComponent() {
109
147
  const formComponentData = ref<any>({})
110
148
  const formPadTemplate = ref<any>({})
111
149
  watch(formComponentData, (newValue) => {
150
+ sanitizeBlankStrings(newValue)
112
151
  ctx.emit('update:modelValue', newValue)
113
152
  }, { deep: true })
114
153
  watch(() => props.modelValue, (newValue) => {
@@ -1,30 +1,59 @@
1
- <script setup lang="ts">
1
+ <script lang="ts" setup>
2
2
  import {VDataTable} from 'vuetify/components/VDataTable'
3
- import {computed, onMounted, ref, useAttrs, watch} from 'vue'
4
- import {omit} from "lodash-es";
3
+ import {VInput} from 'vuetify/components/VInput'
4
+ import {computed, defineOptions,defineExpose, ref, useAttrs, watch, useTemplateRef} from 'vue'
5
+ import {cloneDeep, isEqual, omit, isArray, isString} from 'lodash-es'
6
+ import {templateItemToString} from "../../composables/document/template";
7
+ import {templateToHeader} from "../../composables/document/templateFormTable";
8
+
9
+ defineOptions({
10
+ inheritAttrs: false,
11
+ })
12
+
5
13
  interface Props extends /* @vue-ignore */ InstanceType<typeof VDataTable['$props']> {
6
14
  title: string
15
+ noDataText?: string
7
16
  modelValue?: Record<string, any>[]
8
17
  modelKey?: string
9
18
  toolbarColor?: string
10
- items : Record<string, any>
19
+ headers : Record<string, any>[]
20
+ itemsInitial : Record<string, any>[]
21
+ dataTemplate?: string | object
22
+ disableApplyToAll?: boolean | string | string[]
11
23
  }
24
+
12
25
  const props = withDefaults(defineProps<Props>(), {
26
+ noDataText: 'ไม่พบข้อมูล',
13
27
  modelKey: 'id',
28
+ toolbarColor: 'primary',
29
+ disableApplyToAll: false,
14
30
  })
15
31
 
16
32
  const emit = defineEmits(['update:modelValue'])
17
33
  const attrs = useAttrs()
18
34
  const plainAttrs = computed(() => {
19
- return omit(attrs, ['modelValue', 'onUpdate:modelValue'])
35
+ return omit(attrs, ['modelValue','items','onUpdate:modelValue','itemsInitial','dataTemplate'])
20
36
  })
21
- const modelValue = ref()
22
- const setDataItems = ()=>{
23
- modelValue.value = props.items
37
+
38
+ const inputRef = useTemplateRef<VInput>("inputRef")
39
+
40
+ const itemsInternal = ref<Record<string, any>[]>([])
41
+ const itemsApplyAll = ref<Record<string, any>>({})
42
+
43
+ const computedDisableApplyToAll = computed(()=>{
44
+ if (isString(props.disableApplyToAll)) {
45
+ return props.disableApplyToAll.split(',').map(i=>i.trim())
46
+ }
47
+ return props.disableApplyToAll
48
+ })
49
+ const canApplyAll = (variableName: string) => {
50
+ if (isArray(computedDisableApplyToAll.value)) return !computedDisableApplyToAll.value.includes(variableName)
51
+ return !computedDisableApplyToAll.value
24
52
  }
53
+
25
54
  watch(() => props.modelValue, (newValue) => {
26
55
  if (!Array.isArray(newValue) || !newValue.every(item => typeof item === 'object')) {
27
- modelValue.value = []
56
+ itemsInternal.value = cloneDeep(props.itemsInitial)
28
57
  }
29
58
  else {
30
59
  let maxKey = 0
@@ -36,47 +65,157 @@ watch(() => props.modelValue, (newValue) => {
36
65
  }
37
66
  })
38
67
 
39
- modelValue.value = newValue
68
+ itemsInternal.value = newValue
40
69
  }
41
70
  }, { immediate: true })
42
71
 
43
- watch(modelValue, (newValue) => {
44
- emit('update:modelValue', newValue)
72
+ watch(()=>props.itemsInitial, (newValue,oldValue)=>{
73
+ if (!isEqual(newValue,oldValue)) itemsInternal.value = cloneDeep(props.itemsInitial)
74
+ },{immediate:true,deep:true})
75
+
76
+ watch(itemsInternal, () => {
77
+ emit('update:modelValue', itemsInternal.value)
45
78
  }, { deep: true })
46
79
 
47
- onMounted(()=>{
48
- setDataItems()
80
+ watch(itemsApplyAll, () => {
81
+ itemsInternal.value = itemsInternal.value?.map((item) => {
82
+ return Object.assign(item, itemsApplyAll.value)
83
+ })
84
+ },{deep:true})
85
+
86
+ const computedHeaders = computed(()=>{
87
+ let dataHeaders = templateToHeader(props.dataTemplate)
88
+ let combinedHeaders = [...props.headers, ...dataHeaders]
89
+ return combinedHeaders.map((header: any) => {
90
+ return {...header,headerProps:
91
+ {
92
+ style: 'font-weight: 1000'
93
+ }
94
+ }
95
+ })
49
96
  })
50
97
 
51
- </script>
98
+ const computedDataTemplate = computed(()=>{
99
+ let template = cloneDeep(props.dataTemplate)
100
+ if (isString(props.dataTemplate)) {
101
+ try {
102
+ template = JSON.parse(props.dataTemplate)
103
+ } catch (e) {
104
+ void e
105
+ }
106
+ }
107
+ if (isArray(template)) {
108
+ return template.map((t: any) => {
109
+ const out = { ...t }
110
+ let s = out.inputAttributes?.trim() || ""
111
+ if (!/(^|\s)hide-details(\s|$)/.test(s) && !/(^|\s)hideDetails(\s|$)/.test(s)) {
112
+ s = `${s} hide-details`.trim()
113
+ }
114
+ out.inputAttributes = s
115
+ return out
116
+ })
117
+ }
118
+ return []
119
+ })
120
+
121
+ const reset = ()=>{
122
+ inputRef.value?.reset()
123
+ itemsApplyAll.value = {}
124
+ }
125
+
126
+ const isValid = computed(()=>{
127
+ return inputRef.value?.isValid
128
+ })
52
129
 
130
+ const errorMessages = computed(()=>{
131
+ return inputRef.value?.errorMessages
132
+ })
133
+
134
+ const operation = ref({reset})
135
+
136
+ defineExpose({
137
+ errorMessages,
138
+ isValid,
139
+ reset,
140
+ resetValidation : ()=>inputRef.value?.resetValidation(),
141
+ validate : ()=>inputRef.value?.validate(),
142
+ operation
143
+ })
144
+ </script>
53
145
 
54
146
  <template>
55
- <form-table
56
- v-bind="plainAttrs"
57
- v-model="modelValue"
58
- :title="props.title"
59
- :toolbarColor="props.toolbarColor"
60
- :model-key="props.modelKey"
61
- :insertable="false"
62
- :importable="false"
63
- :exportable="false"
64
- :searchable="false"
65
- hide-default-footer>
66
- <!-- @ts-ignore -->
67
- <template
68
- v-for="(_, name, index) in ($slots as {})"
69
- :key="index"
70
- #[name]="slotData"
71
- >
72
- <slot
73
- :name="name"
74
- v-bind="((slotData || {}) as object)"
75
- />
76
- </template>
77
- </form-table>
78
- </template>
147
+ <v-input v-model="itemsInternal" v-bind="plainAttrs" ref="inputRef">
148
+ <template #default="{isReadonly,isDisabled}">
149
+ <v-container fluid class="ma-0 pa-0">
150
+ <v-card>
151
+ <slot
152
+ name="header"
153
+ :items="itemsInternal"
154
+ :operation="operation"
155
+ >
156
+ <VToolbar :color="toolbarColor">
157
+ <v-row
158
+ justify="end"
159
+ class="ma-1"
160
+ dense
161
+ no-gutters
162
+ align="center"
163
+ >
164
+ <v-col>
165
+ <VToolbarTitle class="pl-3">
166
+ <slot name="title">
167
+ {{ title }}
168
+ </slot>
169
+ </VToolbarTitle>
170
+ </v-col>
171
+ </v-row>
172
+
173
+ <VToolbarItems>
174
+ <slot name="toolbarItems" :items="itemsInternal" :operation="operation"/>
175
+ <VBtn
176
+ :color="toolbarColor"
177
+ icon="mdi:mdi-restore"
178
+ variant="flat"
179
+ @click="reset()"
180
+ >
181
+ </VBtn>
182
+ </VToolbarItems>
183
+ </VToolbar>
184
+ </slot>
185
+ <v-data-table
186
+ v-bind="plainAttrs"
187
+ color="primary"
188
+ :items="itemsInternal"
189
+ :headers="computedHeaders"
190
+ disable-sort
191
+ hide-default-footer
192
+ >
193
+ <!-- @ts-ignore -->
194
+ <template
195
+ v-for="(_, name, index) in ($slots as {})"
196
+ :key="index"
197
+ #[name]="slotData"
198
+ >
199
+ <slot
200
+ :name="name"
201
+ v-bind="((slotData || {}) as object)"
202
+ :operation="operation"
203
+ :isReadonly="isReadonly"
204
+ :isDisabled="isDisabled"
205
+ />
206
+ </template>
79
207
 
80
- <style scoped>
208
+ <template v-for="template in computedDataTemplate" :key="template.variableName" #[`header.${template.variableName}`]="props">
209
+ <form-pad v-model="itemsApplyAll" :template="templateItemToString(template,[])" v-if="canApplyAll(template.variableName)"></form-pad>
210
+ <template v-else>{{props.column.title}}</template>
211
+ </template>
81
212
 
82
- </style>
213
+ <template v-for="template in computedDataTemplate" :key="template.variableName" #[`item.${template.variableName}`]="{index}">
214
+ <form-pad v-model="itemsInternal[index]" :template="templateItemToString(template,[])"></form-pad>
215
+ </template>
216
+ </v-data-table>
217
+ </v-card>
218
+ </v-container>
219
+ </template>
220
+ </v-input>
221
+ </template>