@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.
Files changed (111) hide show
  1. package/README.md +115 -96
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +1 -0
  4. package/dist/runtime/components/Alert.vue +58 -54
  5. package/dist/runtime/components/BarcodeReader.vue +130 -122
  6. package/dist/runtime/components/ExportCSV.vue +110 -102
  7. package/dist/runtime/components/FileBtn.vue +79 -67
  8. package/dist/runtime/components/ImportCSV.vue +151 -139
  9. package/dist/runtime/components/MrzReader.vue +168 -0
  10. package/dist/runtime/components/SplitterPanel.vue +67 -59
  11. package/dist/runtime/components/TabsGroup.vue +39 -31
  12. package/dist/runtime/components/TextBarcode.vue +66 -54
  13. package/dist/runtime/components/device/IdCardButton.vue +95 -83
  14. package/dist/runtime/components/device/IdCardWebSocket.vue +207 -195
  15. package/dist/runtime/components/device/Scanner.vue +350 -338
  16. package/dist/runtime/components/dialog/Confirm.vue +112 -100
  17. package/dist/runtime/components/dialog/Host.vue +88 -84
  18. package/dist/runtime/components/dialog/Index.vue +84 -72
  19. package/dist/runtime/components/dialog/Loading.vue +51 -39
  20. package/dist/runtime/components/dialog/default/Confirm.vue +112 -100
  21. package/dist/runtime/components/dialog/default/Loading.vue +60 -48
  22. package/dist/runtime/components/dialog/default/Notify.vue +82 -70
  23. package/dist/runtime/components/dialog/default/Printing.vue +46 -34
  24. package/dist/runtime/components/dialog/default/VerifyUser.vue +144 -132
  25. package/dist/runtime/components/document/Form.vue +50 -42
  26. package/dist/runtime/components/document/TemplateBuilder.vue +536 -524
  27. package/dist/runtime/components/form/ActionPad.vue +156 -144
  28. package/dist/runtime/components/form/Birthdate.vue +116 -104
  29. package/dist/runtime/components/form/CheckboxGroup.vue +99 -87
  30. package/dist/runtime/components/form/CodeEditor.vue +45 -37
  31. package/dist/runtime/components/form/Date.vue +270 -258
  32. package/dist/runtime/components/form/DateTime.vue +220 -208
  33. package/dist/runtime/components/form/Dialog.vue +178 -166
  34. package/dist/runtime/components/form/EditPad.vue +157 -145
  35. package/dist/runtime/components/form/File.vue +295 -283
  36. package/dist/runtime/components/form/Hidden.vue +44 -32
  37. package/dist/runtime/components/form/Iterator.vue +538 -526
  38. package/dist/runtime/components/form/Login.vue +143 -131
  39. package/dist/runtime/components/form/Pad.vue +399 -387
  40. package/dist/runtime/components/form/SignPad.vue +226 -218
  41. package/dist/runtime/components/form/System.vue +34 -26
  42. package/dist/runtime/components/form/Table.vue +391 -379
  43. package/dist/runtime/components/form/TableData.vue +236 -224
  44. package/dist/runtime/components/form/Time.vue +177 -165
  45. package/dist/runtime/components/form/images/Capture.vue +245 -237
  46. package/dist/runtime/components/form/images/Edit.vue +133 -121
  47. package/dist/runtime/components/form/images/Field.vue +331 -320
  48. package/dist/runtime/components/form/images/Pad.vue +54 -42
  49. package/dist/runtime/components/label/Date.vue +37 -29
  50. package/dist/runtime/components/label/DateAgo.vue +102 -94
  51. package/dist/runtime/components/label/DateCount.vue +152 -144
  52. package/dist/runtime/components/label/Field.vue +111 -103
  53. package/dist/runtime/components/label/FormatMoney.vue +37 -29
  54. package/dist/runtime/components/label/Mask.vue +46 -38
  55. package/dist/runtime/components/label/Object.vue +21 -13
  56. package/dist/runtime/components/master/Autocomplete.vue +89 -81
  57. package/dist/runtime/components/master/Combobox.vue +88 -80
  58. package/dist/runtime/components/master/RadioGroup.vue +90 -78
  59. package/dist/runtime/components/master/Select.vue +70 -62
  60. package/dist/runtime/components/master/label.vue +55 -47
  61. package/dist/runtime/components/model/Autocomplete.vue +91 -79
  62. package/dist/runtime/components/model/Combobox.vue +90 -78
  63. package/dist/runtime/components/model/Pad.vue +114 -102
  64. package/dist/runtime/components/model/Select.vue +78 -72
  65. package/dist/runtime/components/model/Table.vue +370 -358
  66. package/dist/runtime/components/model/iterator.vue +497 -489
  67. package/dist/runtime/components/model/label.vue +58 -50
  68. package/dist/runtime/components/pdf/Print.vue +75 -63
  69. package/dist/runtime/components/pdf/View.vue +146 -134
  70. package/dist/runtime/composables/alert.d.ts +4 -0
  71. package/dist/runtime/composables/api.d.ts +4 -0
  72. package/dist/runtime/composables/dialog.d.ts +1 -1
  73. package/dist/runtime/composables/document/templateFormHidden.d.ts +4 -0
  74. package/dist/runtime/composables/graphql.d.ts +1 -1
  75. package/dist/runtime/composables/graphqlModel.d.ts +9 -9
  76. package/dist/runtime/composables/graphqlModelItem.d.ts +7 -7
  77. package/dist/runtime/composables/graphqlModelOperation.d.ts +6 -6
  78. package/dist/runtime/composables/localStorageModel.d.ts +4 -0
  79. package/dist/runtime/composables/lookupList.d.ts +4 -0
  80. package/dist/runtime/composables/menu.d.ts +4 -0
  81. package/dist/runtime/composables/useMrzReader.d.ts +48 -0
  82. package/dist/runtime/composables/useMrzReader.js +423 -0
  83. package/dist/runtime/composables/useTesseract.d.ts +16 -0
  84. package/dist/runtime/composables/useTesseract.js +45 -0
  85. package/dist/runtime/composables/userPermission.d.ts +1 -1
  86. package/dist/runtime/labs/Calendar.vue +99 -99
  87. package/dist/runtime/labs/form/EditMobile.vue +152 -152
  88. package/dist/runtime/labs/form/TextFieldMask.vue +43 -43
  89. package/dist/runtime/plugins/clientConfig.d.ts +1 -1
  90. package/dist/runtime/plugins/default.d.ts +1 -1
  91. package/dist/runtime/plugins/dialogManager.d.ts +1 -1
  92. package/dist/runtime/plugins/permission.d.ts +1 -1
  93. package/dist/runtime/types/alert.d.ts +11 -11
  94. package/dist/runtime/types/clientConfig.d.ts +13 -13
  95. package/dist/runtime/types/dialogManager.d.ts +35 -35
  96. package/dist/runtime/types/formDialog.d.ts +5 -5
  97. package/dist/runtime/types/graphqlOperation.d.ts +23 -23
  98. package/dist/runtime/types/menu.d.ts +31 -31
  99. package/dist/runtime/types/modules.d.ts +7 -7
  100. package/dist/runtime/types/permission.d.ts +13 -13
  101. package/dist/runtime/utils/asset.d.ts +2 -0
  102. package/dist/runtime/utils/asset.js +49 -0
  103. package/package.json +131 -122
  104. package/scripts/enrich-vue-docs-from-ai.mjs +197 -0
  105. package/scripts/generate-ai-summary.mjs +321 -0
  106. package/scripts/generate-composables-md.mjs +129 -0
  107. package/scripts/postInstall.cjs +70 -70
  108. package/templates/.codegen/codegen.ts +32 -32
  109. package/templates/.codegen/plugin-schema-object.js +161 -161
  110. package/templates/public/tesseract/mrz.traineddata.gz +0 -0
  111. package/templates/public/tesseract/ocrb.traineddata.gz +0 -0
@@ -1,166 +1,178 @@
1
- <script lang="ts" setup>
2
- import {computed, defineModel, ref, watch, watchEffect} from 'vue'
3
- import {cloneDeep, isEqual} from 'lodash-es'
4
- import type {FormDialogCallback} from '../../types/formDialog'
5
-
6
- interface Props {
7
- fullscreen?: boolean
8
- title?: string
9
- initialData?: object
10
- formData?: object
11
- saveCaption?: string
12
- cancelCaption?: string
13
- closeCaption?: string
14
- saveAndStay?: boolean
15
- readonly?: boolean
16
- }
17
-
18
- const props = withDefaults(defineProps<Props>(), {
19
- saveCaption: 'บันทึก',
20
- cancelCaption: 'ยกเลิก',
21
- closeCaption: 'ปิด',
22
- saveAndStay: false,
23
- readonly: false,
24
- })
25
-
26
- const isShowing = defineModel<boolean>({ default: false })
27
- const isSaving = ref<boolean>(false)
28
- const isSavedAndStay = ref<boolean>(false)
29
- const formPadRef = ref()
30
- const formData = ref<object>({})
31
- const formDataOriginalValue = ref<object>()
32
-
33
- const emit = defineEmits(['create', 'update'])
34
-
35
- function save() {
36
- if (formPadRef.value.isValid) {
37
- isSaving.value = true
38
- emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value), (props.saveAndStay) ? stayCallback : callback)
39
- }
40
- }
41
-
42
- function cancel() {
43
- isShowing.value = false
44
- }
45
-
46
- const callback: FormDialogCallback = {
47
- done: function () {
48
- isSaving.value = false
49
- isShowing.value = false
50
- },
51
- error: function () {
52
- isSaving.value = false
53
- },
54
- }
55
-
56
- const stayCallback: FormDialogCallback = {
57
- done: function () {
58
- isSaving.value = false
59
- isSavedAndStay.value = true
60
- },
61
- error: function () {
62
- isSaving.value = false
63
- },
64
- setData: function (item: object) {
65
- formData.value = cloneDeep(item)
66
- formDataOriginalValue.value = cloneDeep(item)
67
- }
68
- }
69
-
70
- const isDataChange = computed(() => {
71
- return !((isCreating.value) ? isEqual(formData.value, createOriginalValue.value) : isEqual(formData.value, formDataOriginalValue.value))
72
- })
73
-
74
- const isCreating = computed(() => {
75
- return !props.formData && !isSavedAndStay.value
76
- })
77
-
78
- const createOriginalValue = computed(() => {
79
- return Object.assign({}, props.initialData)
80
- })
81
-
82
- const loadFormData = () => {
83
- if (props.formData) {
84
- formData.value = cloneDeep(props.formData)
85
- formDataOriginalValue.value = cloneDeep(props.formData)
86
- }
87
- else {
88
- formData.value = Object.assign({}, cloneDeep(props.initialData))
89
- }
90
- isSavedAndStay.value = false
91
- }
92
-
93
- const operation = ref({ isDataChange, isCreating, isSaving, save, cancel })
94
-
95
- watchEffect(loadFormData)
96
-
97
- watch(() => isShowing.value, (newValue) => {
98
- if (newValue) loadFormData()
99
- })
100
- </script>
101
-
102
- <template>
103
- <v-dialog
104
- v-model="isShowing"
105
- :fullscreen="fullscreen"
106
- persistent
107
- scrollable
108
- >
109
- <VCard>
110
- <VToolbar>
111
- <VToolbarTitle>
112
- <slot name="title" :operation="operation">
113
- {{ (isCreating) ? "New" : "Edit" }} {{ title }}
114
- </slot>
115
- </VToolbarTitle>
116
- <VSpacer />
117
- <VToolbarItems>
118
- <VBtn
119
- icon="mdi mdi-close"
120
- @click="cancel"
121
- />
122
- </VToolbarItems>
123
- </VToolbar>
124
- <VCardText>
125
- <form-pad
126
- ref="formPadRef"
127
- v-model="formData"
128
- :originalData="formDataOriginalValue"
129
- :readonly="readonly"
130
- isolated
131
- >
132
- <template #default="slotData">
133
- <slot
134
- v-bind="slotData"
135
- :is-creating="isCreating"
136
- :is-data-change="isDataChange"
137
- />
138
- </template>
139
- </form-pad>
140
- </VCardText>
141
- <VCardActions>
142
- <slot name="action" :operation="operation">
143
- <VSpacer />
144
- <VBtn
145
- color="primary"
146
- variant="flat"
147
- :loading="isSaving"
148
- :disabled="!isDataChange"
149
- @click="save"
150
- v-if="!readonly"
151
- >
152
- {{ saveCaption }}
153
- </VBtn>
154
- <VBtn
155
- color="error"
156
- variant="flat"
157
- :disabled="isSaving"
158
- @click="cancel"
159
- >
160
- {{ (!isDataChange) ? closeCaption : cancelCaption }}
161
- </VBtn>
162
- </slot>
163
- </VCardActions>
164
- </VCard>
165
- </v-dialog>
166
- </template>
1
+ <script lang="ts" setup>
2
+ /**
3
+ * FormDialog 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
+ import {computed, defineModel, ref, watch, watchEffect} from 'vue'
7
+ import {cloneDeep, isEqual} from 'lodash-es'
8
+ import type {FormDialogCallback} from '../../types/formDialog'
9
+
10
+ interface Props {
11
+ fullscreen?: boolean // Opens the dialog in fullscreen mode.
12
+ title?: string // Title text displayed in the component header or dialog.
13
+ initialData?: object // Initial form/object values used when creating a new record.
14
+ formData?: object // Configuration option used by Dialog.
15
+ saveCaption?: string // Label text for the save/confirm action button.
16
+ cancelCaption?: string // Label text for the cancel action button.
17
+ closeCaption?: string // Label text for the close action button.
18
+ saveAndStay?: boolean // Keeps editing context open after save for consecutive updates.
19
+ readonly?: boolean // renders as read-only while keeping value visible
20
+ }
21
+
22
+ /**
23
+ * Public props accepted by FormDialog.
24
+ * Document each prop field with intent, defaults, and side effects for clear generated docs.
25
+ */
26
+ const props = withDefaults(defineProps<Props>(), {
27
+ saveCaption: 'บันทึก',
28
+ cancelCaption: 'ยกเลิก',
29
+ closeCaption: 'ปิด',
30
+ saveAndStay: false,
31
+ readonly: false,
32
+ })
33
+
34
+ const isShowing = defineModel<boolean>({ default: false })
35
+ const isSaving = ref<boolean>(false)
36
+ const isSavedAndStay = ref<boolean>(false)
37
+ const formPadRef = ref()
38
+ const formData = ref<object>({})
39
+ const formDataOriginalValue = ref<object>()
40
+
41
+ /**
42
+ * Custom events emitted by FormDialog.
43
+ * Parents can listen to these events to react to user actions and internal state changes.
44
+ */
45
+ const emit = defineEmits(['create', 'update'])
46
+
47
+ function save() {
48
+ if (formPadRef.value.isValid) {
49
+ isSaving.value = true
50
+ emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value), (props.saveAndStay) ? stayCallback : callback)
51
+ }
52
+ }
53
+
54
+ function cancel() {
55
+ isShowing.value = false
56
+ }
57
+
58
+ const callback: FormDialogCallback = {
59
+ done: function () {
60
+ isSaving.value = false
61
+ isShowing.value = false
62
+ },
63
+ error: function () {
64
+ isSaving.value = false
65
+ },
66
+ }
67
+
68
+ const stayCallback: FormDialogCallback = {
69
+ done: function () {
70
+ isSaving.value = false
71
+ isSavedAndStay.value = true
72
+ },
73
+ error: function () {
74
+ isSaving.value = false
75
+ },
76
+ setData: function (item: object) {
77
+ formData.value = cloneDeep(item)
78
+ formDataOriginalValue.value = cloneDeep(item)
79
+ }
80
+ }
81
+
82
+ const isDataChange = computed(() => {
83
+ return !((isCreating.value) ? isEqual(formData.value, createOriginalValue.value) : isEqual(formData.value, formDataOriginalValue.value))
84
+ })
85
+
86
+ const isCreating = computed(() => {
87
+ return !props.formData && !isSavedAndStay.value
88
+ })
89
+
90
+ const createOriginalValue = computed(() => {
91
+ return Object.assign({}, props.initialData)
92
+ })
93
+
94
+ const loadFormData = () => {
95
+ if (props.formData) {
96
+ formData.value = cloneDeep(props.formData)
97
+ formDataOriginalValue.value = cloneDeep(props.formData)
98
+ }
99
+ else {
100
+ formData.value = Object.assign({}, cloneDeep(props.initialData))
101
+ }
102
+ isSavedAndStay.value = false
103
+ }
104
+
105
+ const operation = ref({ isDataChange, isCreating, isSaving, save, cancel })
106
+
107
+ watchEffect(loadFormData)
108
+
109
+ watch(() => isShowing.value, (newValue) => {
110
+ if (newValue) loadFormData()
111
+ })
112
+ </script>
113
+
114
+ <template>
115
+ <v-dialog
116
+ v-model="isShowing"
117
+ :fullscreen="fullscreen"
118
+ persistent
119
+ scrollable
120
+ >
121
+ <VCard>
122
+ <VToolbar>
123
+ <VToolbarTitle>
124
+ <slot name="title" :operation="operation">
125
+ {{ (isCreating) ? "New" : "Edit" }} {{ title }}
126
+ </slot>
127
+ </VToolbarTitle>
128
+ <VSpacer />
129
+ <VToolbarItems>
130
+ <VBtn
131
+ icon="mdi mdi-close"
132
+ @click="cancel"
133
+ />
134
+ </VToolbarItems>
135
+ </VToolbar>
136
+ <VCardText>
137
+ <form-pad
138
+ ref="formPadRef"
139
+ v-model="formData"
140
+ :originalData="formDataOriginalValue"
141
+ :readonly="readonly"
142
+ isolated
143
+ >
144
+ <template #default="slotData">
145
+ <slot
146
+ v-bind="slotData"
147
+ :is-creating="isCreating"
148
+ :is-data-change="isDataChange"
149
+ />
150
+ </template>
151
+ </form-pad>
152
+ </VCardText>
153
+ <VCardActions>
154
+ <slot name="action" :operation="operation">
155
+ <VSpacer />
156
+ <VBtn
157
+ color="primary"
158
+ variant="flat"
159
+ :loading="isSaving"
160
+ :disabled="!isDataChange"
161
+ @click="save"
162
+ v-if="!readonly"
163
+ >
164
+ {{ saveCaption }}
165
+ </VBtn>
166
+ <VBtn
167
+ color="error"
168
+ variant="flat"
169
+ :disabled="isSaving"
170
+ @click="cancel"
171
+ >
172
+ {{ (!isDataChange) ? closeCaption : cancelCaption }}
173
+ </VBtn>
174
+ </slot>
175
+ </VCardActions>
176
+ </VCard>
177
+ </v-dialog>
178
+ </template>
@@ -1,145 +1,157 @@
1
- <script lang="ts" setup>
2
- import {computed, defineExpose, ref, watchEffect} from 'vue'
3
- import {cloneDeep, isEqual} from 'lodash-es'
4
- import type {FormDialogCallback} from '../../types/formDialog'
5
- import FormPadComponent from './Pad.vue'
6
-
7
- interface Props extends /* @vue-ignore */ InstanceType<typeof FormPadComponent['$props']> {
8
- title?: string
9
- initialData?: object
10
- formData?: object
11
- saveCaption?: string
12
- cancelCaption?: string
13
- readonly?: boolean
14
- showTitle?: boolean
15
- skipValidation?:boolean
16
- }
17
-
18
- const props = withDefaults(defineProps<Props>(), {
19
- saveCaption: 'บันทึก',
20
- cancelCaption: 'ยกเลิก',
21
- readonly: false,
22
- showTitle: false,
23
- skipValidation:false
24
- })
25
-
26
- const isSaving = ref<boolean>(false)
27
- const formPadRef = ref()
28
- const formData = ref<object>({})
29
- const formDataOriginalValue = ref<object>()
30
-
31
- const emit = defineEmits(['create', 'update'])
32
-
33
- function save() {
34
- if (props.skipValidation || formPadRef.value?.isValid) {
35
- isSaving.value = true
36
- emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value), callback)
37
- }
38
- }
39
-
40
- function cancel() {
41
- reset()
42
- }
43
-
44
- function reset() {
45
- formDataOriginalValue.value = undefined
46
- formPadRef.value?.reset()
47
- loadFormData()
48
- }
49
-
50
- const callback: FormDialogCallback = {
51
- done: function () {
52
- isSaving.value = false
53
- },
54
- error: function () {
55
- isSaving.value = false
56
- },
57
- setData: function (item: object) {
58
- formData.value = cloneDeep(item)
59
- formDataOriginalValue.value = cloneDeep(item)
60
- }
61
- }
62
-
63
- const isDataChange = computed(() => {
64
- return !((isCreating.value) ? isEqual(formData.value, createOriginalValue.value) : isEqual(formData.value, formDataOriginalValue.value))
65
- })
66
-
67
- const isCreating = computed(() => {
68
- return !props.formData
69
- })
70
-
71
- const createOriginalValue = computed(() => {
72
- return Object.assign({}, props.initialData)
73
- })
74
-
75
- const loadFormData = () => {
76
- if (props.formData) {
77
- formData.value = cloneDeep(props.formData)
78
- formDataOriginalValue.value = cloneDeep(props.formData)
79
- }
80
- else {
81
- formData.value = Object.assign({}, cloneDeep(props.initialData))
82
- }
83
- }
84
-
85
- const operation = ref({ isDataChange, isCreating, isSaving, save, cancel })
86
-
87
- watchEffect(loadFormData)
88
-
89
- defineExpose({operation,formPad:formPadRef})
90
- </script>
91
-
92
- <template>
93
- <VCard flat>
94
- <VToolbar v-if="showTitle">
95
- <slot name="titleToolbar" :operation="operation">
96
- <VToolbarTitle>
97
- <slot name="title" :operation="operation">
98
- {{ (isCreating) ? "New" : "Edit" }} {{ title }}
99
- </slot>
100
- </VToolbarTitle>
101
- </slot>
102
- </VToolbar>
103
- <VCardText>
104
- <form-pad
105
- ref="formPadRef"
106
- v-model="formData"
107
- :originalData="formDataOriginalValue"
108
- :readonly="readonly"
109
- isolated
110
- v-bind="$attrs"
111
- >
112
- <template #default="slotData">
113
- <slot
114
- v-bind="slotData"
115
- :is-creating="isCreating"
116
- :is-data-change="isDataChange"
117
- />
118
- </template>
119
- </form-pad>
120
- </VCardText>
121
- <VCardActions>
122
- <slot name="action" :operation="operation">
123
- <VSpacer />
124
- <VBtn
125
- color="primary"
126
- variant="flat"
127
- :loading="isSaving"
128
- :disabled="!isDataChange"
129
- @click="save"
130
- v-if="!readonly"
131
- >
132
- {{ saveCaption }}
133
- </VBtn>
134
- <VBtn
135
- color="error"
136
- variant="flat"
137
- :disabled="isSaving"
138
- @click="cancel"
139
- >
140
- {{ cancelCaption }}
141
- </VBtn>
142
- </slot>
143
- </VCardActions>
144
- </VCard>
145
- </template>
1
+ <script lang="ts" setup>
2
+ /**
3
+ * FormEditPad 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
+ import {computed, defineExpose, ref, watchEffect} from 'vue'
7
+ import {cloneDeep, isEqual} from 'lodash-es'
8
+ import type {FormDialogCallback} from '../../types/formDialog'
9
+ import FormPadComponent from './Pad.vue'
10
+
11
+ interface Props extends /* @vue-ignore */ InstanceType<typeof FormPadComponent['$props']> {
12
+ title?: string // Title text displayed in the component header or dialog.
13
+ initialData?: object // Initial form/object values used when creating a new record.
14
+ formData?: object // Configuration option used by EditPad.
15
+ saveCaption?: string // Label text for the save/confirm action button.
16
+ cancelCaption?: string // Label text for the cancel action button.
17
+ readonly?: boolean // renders as read-only while keeping value visible
18
+ showTitle?: boolean // Shows or hides the component title/header area.
19
+ skipValidation?: boolean // Skips form validation before emitting save actions.
20
+ }
21
+
22
+ /**
23
+ * Public props accepted by FormEditPad.
24
+ * Document each prop field with intent, defaults, and side effects for clear generated docs.
25
+ */
26
+ const props = withDefaults(defineProps<Props>(), {
27
+ saveCaption: 'บันทึก',
28
+ cancelCaption: 'ยกเลิก',
29
+ readonly: false,
30
+ showTitle: false,
31
+ skipValidation:false
32
+ })
33
+
34
+ const isSaving = ref<boolean>(false)
35
+ const formPadRef = ref()
36
+ const formData = ref<object>({})
37
+ const formDataOriginalValue = ref<object>()
38
+
39
+ /**
40
+ * Custom events emitted by FormEditPad.
41
+ * Parents can listen to these events to react to user actions and internal state changes.
42
+ */
43
+ const emit = defineEmits(['create', 'update'])
44
+
45
+ function save() {
46
+ if (props.skipValidation || formPadRef.value?.isValid) {
47
+ isSaving.value = true
48
+ emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value), callback)
49
+ }
50
+ }
51
+
52
+ function cancel() {
53
+ reset()
54
+ }
55
+
56
+ function reset() {
57
+ formDataOriginalValue.value = undefined
58
+ formPadRef.value?.reset()
59
+ loadFormData()
60
+ }
61
+
62
+ const callback: FormDialogCallback = {
63
+ done: function () {
64
+ isSaving.value = false
65
+ },
66
+ error: function () {
67
+ isSaving.value = false
68
+ },
69
+ setData: function (item: object) {
70
+ formData.value = cloneDeep(item)
71
+ formDataOriginalValue.value = cloneDeep(item)
72
+ }
73
+ }
74
+
75
+ const isDataChange = computed(() => {
76
+ return !((isCreating.value) ? isEqual(formData.value, createOriginalValue.value) : isEqual(formData.value, formDataOriginalValue.value))
77
+ })
78
+
79
+ const isCreating = computed(() => {
80
+ return !props.formData
81
+ })
82
+
83
+ const createOriginalValue = computed(() => {
84
+ return Object.assign({}, props.initialData)
85
+ })
86
+
87
+ const loadFormData = () => {
88
+ if (props.formData) {
89
+ formData.value = cloneDeep(props.formData)
90
+ formDataOriginalValue.value = cloneDeep(props.formData)
91
+ }
92
+ else {
93
+ formData.value = Object.assign({}, cloneDeep(props.initialData))
94
+ }
95
+ }
96
+
97
+ const operation = ref({ isDataChange, isCreating, isSaving, save, cancel })
98
+
99
+ watchEffect(loadFormData)
100
+
101
+ defineExpose({operation,formPad:formPadRef})
102
+ </script>
103
+
104
+ <template>
105
+ <VCard flat>
106
+ <VToolbar v-if="showTitle">
107
+ <slot name="titleToolbar" :operation="operation">
108
+ <VToolbarTitle>
109
+ <slot name="title" :operation="operation">
110
+ {{ (isCreating) ? "New" : "Edit" }} {{ title }}
111
+ </slot>
112
+ </VToolbarTitle>
113
+ </slot>
114
+ </VToolbar>
115
+ <VCardText>
116
+ <form-pad
117
+ ref="formPadRef"
118
+ v-model="formData"
119
+ :originalData="formDataOriginalValue"
120
+ :readonly="readonly"
121
+ isolated
122
+ v-bind="$attrs"
123
+ >
124
+ <template #default="slotData">
125
+ <slot
126
+ v-bind="slotData"
127
+ :is-creating="isCreating"
128
+ :is-data-change="isDataChange"
129
+ />
130
+ </template>
131
+ </form-pad>
132
+ </VCardText>
133
+ <VCardActions>
134
+ <slot name="action" :operation="operation">
135
+ <VSpacer />
136
+ <VBtn
137
+ color="primary"
138
+ variant="flat"
139
+ :loading="isSaving"
140
+ :disabled="!isDataChange"
141
+ @click="save"
142
+ v-if="!readonly"
143
+ >
144
+ {{ saveCaption }}
145
+ </VBtn>
146
+ <VBtn
147
+ color="error"
148
+ variant="flat"
149
+ :disabled="isSaving"
150
+ @click="cancel"
151
+ >
152
+ {{ cancelCaption }}
153
+ </VBtn>
154
+ </slot>
155
+ </VCardActions>
156
+ </VCard>
157
+ </template>