@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,259 +1,271 @@
1
- <script lang="ts" setup>
2
- import { ref, watch, watchEffect, nextTick, defineExpose, computed} from 'vue'
3
- import { VTextField } from 'vuetify/components/VTextField'
4
- import Datepicker from '@vuepic/vue-datepicker'
5
- import '@vuepic/vue-datepicker/dist/main.css'
6
- import { isArray, isString } from "lodash-es";
7
- import { type dateFormat, Datetime } from '../../utils/datetime'
8
- import { useRules } from "../../composables/utils/validation";
9
- import { th } from 'date-fns/locale';
10
-
11
- interface Props extends /* @vue-ignore */ InstanceType<typeof VTextField['$props']> {
12
- locale?: 'TH' | 'EN'
13
- format?: dateFormat | string
14
- modelValue?: string | null
15
- holiday?: object[] | undefined
16
- minDate?: Date | string
17
- maxDate?: Date | string
18
- pickerOnly?: boolean
19
- flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[]
20
- rules?: typeof VTextField['rules']
21
-
22
- defaultDate?: boolean | string
23
- }
24
-
25
- const props = withDefaults(defineProps<Props>(), {
26
- locale: 'TH',
27
- format: 'shortDate',
28
- pickerOnly: false,
29
- flow: () => [],
30
-
31
- defaultDate: false,
32
- })
33
-
34
- const emit = defineEmits(['update:modelValue'])
35
-
36
- const computedRules = computed(() => {
37
- if (props.rules && isArray(props.rules)) {
38
-
39
- const ruleConfig = {
40
- singleModifier: ['DateFuture', 'DateHappen'],
41
- twoPartModifier: ['DateAfter', 'DateBefore', 'DateEqual']
42
- };
43
-
44
- const ruleMapping : Record<string,Function> = {
45
- 'DateFuture' : useRules().DateFuture,
46
- 'DateHappen' : useRules().DateHappen,
47
- 'DateAfter' : useRules().DateAfter,
48
- 'DateBefore' : useRules().DatetimeFuture,
49
- 'DateEqual' : useRules().DateEqual,
50
- }
51
-
52
- const applyRule = (ruleName: string, modifier: any) => {
53
- if (props.readonly) return true
54
- const ruleFunc = ruleMapping[ruleName](...Object.values(modifier))
55
- return ruleFunc(selectedDate.value)
56
- };
57
-
58
- return props.rules.map((rule) => {
59
- if (isString(rule)) {
60
- const ruleParts = rule.split('#')
61
- const baseRule = ruleParts[0]
62
-
63
- if (ruleConfig.singleModifier.includes(baseRule)) {
64
- const modifiers = ruleParts.slice(1).join('#').trim() || undefined
65
- return applyRule(baseRule, { modifiers });
66
- }
67
-
68
- if (ruleConfig.twoPartModifier.includes(baseRule)) {
69
- const [modifier1, ...modifierRest] = ruleParts.slice(1)
70
- const combinedModifier = modifierRest.join('#') || undefined
71
- return applyRule(baseRule, { modifier1, combinedModifier })
72
- }
73
- }
74
- return rule
75
- })
76
- } else {
77
- return props.rules
78
- }
79
- })
80
-
81
- const selectedDate = ref<string | null>(null)
82
- const displayedDate = ref<string | null>(null)
83
-
84
- const isMenuOpen = ref(false)
85
- const isTextFieldFocused = ref(false)
86
- const hasTextFieldInput = ref(false)
87
-
88
- function handleTextFieldFocus(event: Event) {
89
- isTextFieldFocused.value = true
90
- nextTick(() => {
91
- (event.target as HTMLInputElement).select()
92
- })
93
- }
94
-
95
- function handleTextFieldInput() {
96
- if (!hasTextFieldInput.value) {
97
- hasTextFieldInput.value = true
98
- isMenuOpen.value = false
99
- }
100
- }
101
-
102
- function handleTextFieldBlur() {
103
- if (hasTextFieldInput.value) {
104
- updateDate(displayedDate.value)
105
- hasTextFieldInput.value = false
106
- }
107
- isTextFieldFocused.value = false
108
- }
109
-
110
- function handleTextFieldEnterKey() {
111
- handleTextFieldBlur()
112
- }
113
-
114
- function updateDatePicker(dateString: string | null) {
115
- isMenuOpen.value = false
116
- updateDate(dateString)
117
- }
118
-
119
- function updateDate(dateString: string | null) {
120
- const dateTime = Datetime().fromString(dateString, undefined, props.locale)
121
- if (!dateTime.luxonDateTime.isValid) {
122
- displayedDate.value = null
123
- selectedDate.value = null
124
- }
125
- else {
126
- selectedDate.value = dateTime.toFormat('yyyy-MM-dd', 'EN')
127
- displayedDate.value = selectedDate.value
128
- }
129
-
130
- if (!isTextFieldFocused.value) displayedDate.value = formatDate(displayedDate.value)
131
- }
132
-
133
- function formatDate(dateString: string | null) {
134
- if (!dateString) return null
135
-
136
- const dateTime = Datetime().fromString(dateString, undefined, props.locale)
137
- return dateTime.toFormat(props.format, props.locale)
138
- }
139
-
140
- function handleTextFieldClear() {
141
- resetDatePicker()
142
- }
143
-
144
- function resetDatePicker() {
145
- selectedDate.value = null
146
- displayedDate.value = null
147
- }
148
-
149
- watchEffect(() => {
150
- if (!isTextFieldFocused.value && selectedDate.value) {
151
- const dateTime = Datetime().fromString(selectedDate.value, undefined, props.locale)
152
- displayedDate.value = dateTime.toFormat(props.format, props.locale)
153
- }
154
- else {
155
- displayedDate.value = selectedDate.value
156
- }
157
- })
158
-
159
- watch(selectedDate, (newValue) => {
160
- emit('update:modelValue', newValue)
161
- })
162
-
163
- watch(() => props.modelValue, (newValue,oldValue) => {
164
- if (!oldValue && !newValue && props.defaultDate) {
165
- if (!oldValue && !newValue && props.defaultDate) {
166
- let defaultDate: string | null = null;
167
-
168
- if (props.defaultDate === true) {
169
- defaultDate = Datetime().now().toFormat('yyyy-MM-dd', 'EN');
170
- } else if (typeof props.defaultDate === 'string' &&/^[+-]?\d+$/.test(props.defaultDate.trim())) {
171
- defaultDate = Datetime().now().luxonDateTime
172
- .plus({ days: Number(props.defaultDate) })
173
- .toFormat('yyyy-MM-dd');
174
- } else {
175
- const dateTime = Datetime().fromString(props.defaultDate)
176
- if (dateTime.luxonDateTime.isValid) defaultDate = dateTime.toFormat('yyyy-MM-dd')
177
- }
178
-
179
- updateDate(defaultDate);
180
- } else {
181
- updateDate(newValue ?? null);
182
- }
183
- } else {
184
- updateDate(props.modelValue || null)
185
- }
186
- }, { immediate: true })
187
-
188
- function toggleMenuOpen(trigger: string) {
189
- if ((trigger === 'textField' && props.pickerOnly) || (trigger === 'icon' && !props.pickerOnly)) {
190
- if (!props.readonly) isMenuOpen.value = true
191
- }
192
- }
193
-
194
- const textFieldRef = ref<VTextField>()
195
- function validate() {
196
- textFieldRef.value?.validate()
197
- }
198
- function resetValidation() {
199
- textFieldRef.value?.resetValidation()
200
- }
201
-
202
- defineExpose({
203
- reset : resetDatePicker,validate,resetValidation, isValid: textFieldRef.value?.isValid
204
- })
205
- </script>
206
-
207
- <template>
208
- <v-menu
209
- v-model="isMenuOpen"
210
- :open-on-click="false"
211
- >
212
- <template #activator="{ props: activatorProps }">
213
- <v-text-field
214
- ref="textFieldRef"
215
- v-model="displayedDate"
216
- :rules="computedRules"
217
- v-bind="$attrs"
218
- @focus="handleTextFieldFocus"
219
- @blur="handleTextFieldBlur"
220
- @keydown="handleTextFieldInput"
221
- @keyup.enter="handleTextFieldEnterKey"
222
- @click:clear="handleTextFieldClear"
223
- @click="toggleMenuOpen('textField')"
224
- >
225
- <template #append="appendProps">
226
- <slot name="append" v-bind="appendProps" :toggleMenuOpen="toggleMenuOpen" :activatorProps="activatorProps">
227
- <v-icon
228
- :disabled="appendProps.isReadonly?.value || appendProps.isDisabled?.value"
229
- v-bind="activatorProps"
230
- @click="toggleMenuOpen('icon')"
231
- >
232
- fa:fa-regular fa-calendar
233
- </v-icon>
234
- </slot>
235
- </template>
236
- </v-text-field>
237
- </template>
238
- <Datepicker
239
- v-model="selectedDate"
240
- model-type="yyyy-MM-dd"
241
- :enable-time-picker="false"
242
- :flow="flow"
243
- :min-date="props.minDate"
244
- :max-date="props.maxDate"
245
- auto-apply
246
- inline
247
- :locale="locale"
248
- :format-locale="(locale=='TH') ? th : undefined"
249
- @update:model-value="updateDatePicker"
250
- >
251
- <template #year="{value}" v-if="locale=='TH'">
252
- {{value+543}}
253
- </template>
254
- <template #year-overlay-value="{value}" v-if="locale=='TH'">
255
- {{value+543}}
256
- </template>
257
- </Datepicker>
258
- </v-menu>
1
+ <script lang="ts" setup>
2
+ /**
3
+ * FormDate 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 { ref, watch, watchEffect, nextTick, defineExpose, computed} from 'vue'
7
+ import { VTextField } from 'vuetify/components/VTextField'
8
+ import Datepicker from '@vuepic/vue-datepicker'
9
+ import '@vuepic/vue-datepicker/dist/main.css'
10
+ import { isArray, isString } from "lodash-es";
11
+ import { type dateFormat, Datetime } from '../../utils/datetime'
12
+ import { useRules } from "../../composables/utils/validation";
13
+ import { th } from 'date-fns/locale';
14
+
15
+ interface Props extends /* @vue-ignore */ InstanceType<typeof VTextField['$props']> {
16
+ locale?: 'TH' | 'EN' // Locale used for date/time formatting and localized labels.
17
+ format?: dateFormat | string // Output format used when converting the value for display or emit.
18
+ modelValue?: string | null // Bound value for v-model synchronization with the parent component.
19
+ holiday?: object[] | undefined // Holiday date list used to decorate or block specific dates.
20
+ minDate?: Date | string // earliest selectable date allowed by business rules
21
+ maxDate?: Date | string // latest selectable date allowed by business rules
22
+ pickerOnly?: boolean // forces value selection through picker interaction only
23
+ flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[] // picker navigation flow order shown to users
24
+ rules?: typeof VTextField['rules'] // validation rules applied before accepting input
25
+
26
+ defaultDate?: boolean | string // default date behavior/value when model is initially empty
27
+ }
28
+
29
+ /**
30
+ * Public props accepted by FormDate.
31
+ * Document each prop field with intent, defaults, and side effects for clear generated docs.
32
+ */
33
+ const props = withDefaults(defineProps<Props>(), {
34
+ locale: 'TH',
35
+ format: 'shortDate',
36
+ pickerOnly: false,
37
+ flow: () => [],
38
+
39
+ defaultDate: false,
40
+ })
41
+
42
+ /**
43
+ * Custom events emitted by FormDate.
44
+ * Parents can listen to these events to react to user actions and internal state changes.
45
+ */
46
+ const emit = defineEmits(['update:modelValue'])
47
+
48
+ const computedRules = computed(() => {
49
+ if (props.rules && isArray(props.rules)) {
50
+
51
+ const ruleConfig = {
52
+ singleModifier: ['DateFuture', 'DateHappen'],
53
+ twoPartModifier: ['DateAfter', 'DateBefore', 'DateEqual']
54
+ };
55
+
56
+ const ruleMapping : Record<string,Function> = {
57
+ 'DateFuture' : useRules().DateFuture,
58
+ 'DateHappen' : useRules().DateHappen,
59
+ 'DateAfter' : useRules().DateAfter,
60
+ 'DateBefore' : useRules().DatetimeFuture,
61
+ 'DateEqual' : useRules().DateEqual,
62
+ }
63
+
64
+ const applyRule = (ruleName: string, modifier: any) => {
65
+ if (props.readonly) return true
66
+ const ruleFunc = ruleMapping[ruleName](...Object.values(modifier))
67
+ return ruleFunc(selectedDate.value)
68
+ };
69
+
70
+ return props.rules.map((rule) => {
71
+ if (isString(rule)) {
72
+ const ruleParts = rule.split('#')
73
+ const baseRule = ruleParts[0]
74
+
75
+ if (ruleConfig.singleModifier.includes(baseRule)) {
76
+ const modifiers = ruleParts.slice(1).join('#').trim() || undefined
77
+ return applyRule(baseRule, { modifiers });
78
+ }
79
+
80
+ if (ruleConfig.twoPartModifier.includes(baseRule)) {
81
+ const [modifier1, ...modifierRest] = ruleParts.slice(1)
82
+ const combinedModifier = modifierRest.join('#') || undefined
83
+ return applyRule(baseRule, { modifier1, combinedModifier })
84
+ }
85
+ }
86
+ return rule
87
+ })
88
+ } else {
89
+ return props.rules
90
+ }
91
+ })
92
+
93
+ const selectedDate = ref<string | null>(null)
94
+ const displayedDate = ref<string | null>(null)
95
+
96
+ const isMenuOpen = ref(false)
97
+ const isTextFieldFocused = ref(false)
98
+ const hasTextFieldInput = ref(false)
99
+
100
+ function handleTextFieldFocus(event: Event) {
101
+ isTextFieldFocused.value = true
102
+ nextTick(() => {
103
+ (event.target as HTMLInputElement).select()
104
+ })
105
+ }
106
+
107
+ function handleTextFieldInput() {
108
+ if (!hasTextFieldInput.value) {
109
+ hasTextFieldInput.value = true
110
+ isMenuOpen.value = false
111
+ }
112
+ }
113
+
114
+ function handleTextFieldBlur() {
115
+ if (hasTextFieldInput.value) {
116
+ updateDate(displayedDate.value)
117
+ hasTextFieldInput.value = false
118
+ }
119
+ isTextFieldFocused.value = false
120
+ }
121
+
122
+ function handleTextFieldEnterKey() {
123
+ handleTextFieldBlur()
124
+ }
125
+
126
+ function updateDatePicker(dateString: string | null) {
127
+ isMenuOpen.value = false
128
+ updateDate(dateString)
129
+ }
130
+
131
+ function updateDate(dateString: string | null) {
132
+ const dateTime = Datetime().fromString(dateString, undefined, props.locale)
133
+ if (!dateTime.luxonDateTime.isValid) {
134
+ displayedDate.value = null
135
+ selectedDate.value = null
136
+ }
137
+ else {
138
+ selectedDate.value = dateTime.toFormat('yyyy-MM-dd', 'EN')
139
+ displayedDate.value = selectedDate.value
140
+ }
141
+
142
+ if (!isTextFieldFocused.value) displayedDate.value = formatDate(displayedDate.value)
143
+ }
144
+
145
+ function formatDate(dateString: string | null) {
146
+ if (!dateString) return null
147
+
148
+ const dateTime = Datetime().fromString(dateString, undefined, props.locale)
149
+ return dateTime.toFormat(props.format, props.locale)
150
+ }
151
+
152
+ function handleTextFieldClear() {
153
+ resetDatePicker()
154
+ }
155
+
156
+ function resetDatePicker() {
157
+ selectedDate.value = null
158
+ displayedDate.value = null
159
+ }
160
+
161
+ watchEffect(() => {
162
+ if (!isTextFieldFocused.value && selectedDate.value) {
163
+ const dateTime = Datetime().fromString(selectedDate.value, undefined, props.locale)
164
+ displayedDate.value = dateTime.toFormat(props.format, props.locale)
165
+ }
166
+ else {
167
+ displayedDate.value = selectedDate.value
168
+ }
169
+ })
170
+
171
+ watch(selectedDate, (newValue) => {
172
+ emit('update:modelValue', newValue)
173
+ })
174
+
175
+ watch(() => props.modelValue, (newValue,oldValue) => {
176
+ if (!oldValue && !newValue && props.defaultDate) {
177
+ if (!oldValue && !newValue && props.defaultDate) {
178
+ let defaultDate: string | null = null;
179
+
180
+ if (props.defaultDate === true) {
181
+ defaultDate = Datetime().now().toFormat('yyyy-MM-dd', 'EN');
182
+ } else if (typeof props.defaultDate === 'string' &&/^[+-]?\d+$/.test(props.defaultDate.trim())) {
183
+ defaultDate = Datetime().now().luxonDateTime
184
+ .plus({ days: Number(props.defaultDate) })
185
+ .toFormat('yyyy-MM-dd');
186
+ } else {
187
+ const dateTime = Datetime().fromString(props.defaultDate)
188
+ if (dateTime.luxonDateTime.isValid) defaultDate = dateTime.toFormat('yyyy-MM-dd')
189
+ }
190
+
191
+ updateDate(defaultDate);
192
+ } else {
193
+ updateDate(newValue ?? null);
194
+ }
195
+ } else {
196
+ updateDate(props.modelValue || null)
197
+ }
198
+ }, { immediate: true })
199
+
200
+ function toggleMenuOpen(trigger: string) {
201
+ if ((trigger === 'textField' && props.pickerOnly) || (trigger === 'icon' && !props.pickerOnly)) {
202
+ if (!props.readonly) isMenuOpen.value = true
203
+ }
204
+ }
205
+
206
+ const textFieldRef = ref<VTextField>()
207
+ function validate() {
208
+ textFieldRef.value?.validate()
209
+ }
210
+ function resetValidation() {
211
+ textFieldRef.value?.resetValidation()
212
+ }
213
+
214
+ defineExpose({
215
+ reset : resetDatePicker,validate,resetValidation, isValid: textFieldRef.value?.isValid
216
+ })
217
+ </script>
218
+
219
+ <template>
220
+ <v-menu
221
+ v-model="isMenuOpen"
222
+ :open-on-click="false"
223
+ >
224
+ <template #activator="{ props: activatorProps }">
225
+ <v-text-field
226
+ ref="textFieldRef"
227
+ v-model="displayedDate"
228
+ :rules="computedRules"
229
+ v-bind="$attrs"
230
+ @focus="handleTextFieldFocus"
231
+ @blur="handleTextFieldBlur"
232
+ @keydown="handleTextFieldInput"
233
+ @keyup.enter="handleTextFieldEnterKey"
234
+ @click:clear="handleTextFieldClear"
235
+ @click="toggleMenuOpen('textField')"
236
+ >
237
+ <template #append="appendProps">
238
+ <slot name="append" v-bind="appendProps" :toggleMenuOpen="toggleMenuOpen" :activatorProps="activatorProps">
239
+ <v-icon
240
+ :disabled="appendProps.isReadonly?.value || appendProps.isDisabled?.value"
241
+ v-bind="activatorProps"
242
+ @click="toggleMenuOpen('icon')"
243
+ >
244
+ fa:fa-regular fa-calendar
245
+ </v-icon>
246
+ </slot>
247
+ </template>
248
+ </v-text-field>
249
+ </template>
250
+ <Datepicker
251
+ v-model="selectedDate"
252
+ model-type="yyyy-MM-dd"
253
+ :enable-time-picker="false"
254
+ :flow="flow"
255
+ :min-date="props.minDate"
256
+ :max-date="props.maxDate"
257
+ auto-apply
258
+ inline
259
+ :locale="locale"
260
+ :format-locale="(locale=='TH') ? th : undefined"
261
+ @update:model-value="updateDatePicker"
262
+ >
263
+ <template #year="{value}" v-if="locale=='TH'">
264
+ {{value+543}}
265
+ </template>
266
+ <template #year-overlay-value="{value}" v-if="locale=='TH'">
267
+ {{value+543}}
268
+ </template>
269
+ </Datepicker>
270
+ </v-menu>
259
271
  </template>