pukaad-ui-lib 1.39.0 → 1.41.0

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 (47) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/assets/json/address/Amphur.json +7346 -0
  3. package/dist/runtime/assets/json/address/Province.json +762 -0
  4. package/dist/runtime/assets/json/address/Tambon.json +64775 -0
  5. package/dist/runtime/assets/json/address/Zipcode.json +17208 -0
  6. package/dist/runtime/components/button.vue +1 -1
  7. package/dist/runtime/components/collapse/collapse.vue +1 -1
  8. package/dist/runtime/components/drawer/drawer.d.vue.ts +1 -1
  9. package/dist/runtime/components/drawer/drawer.vue +2 -2
  10. package/dist/runtime/components/drawer/drawer.vue.d.ts +1 -1
  11. package/dist/runtime/components/input/input-address.d.vue.ts +14 -32
  12. package/dist/runtime/components/input/input-address.vue +174 -360
  13. package/dist/runtime/components/input/input-address.vue.d.ts +14 -32
  14. package/dist/runtime/components/input/input-autocomplete.vue +2 -2
  15. package/dist/runtime/components/input/input-combobox.d.vue.ts +12 -5
  16. package/dist/runtime/components/input/input-combobox.vue +143 -121
  17. package/dist/runtime/components/input/input-combobox.vue.d.ts +12 -5
  18. package/dist/runtime/components/input/input-file.d.vue.ts +1 -1
  19. package/dist/runtime/components/input/input-file.vue.d.ts +1 -1
  20. package/dist/runtime/components/input/input-password.d.vue.ts +1 -1
  21. package/dist/runtime/components/input/input-password.vue.d.ts +1 -1
  22. package/dist/runtime/components/input/input-slider.d.vue.ts +1 -1
  23. package/dist/runtime/components/input/input-slider.vue.d.ts +1 -1
  24. package/dist/runtime/components/input/input-text-field.d.vue.ts +3 -1
  25. package/dist/runtime/components/input/input-text-field.vue +8 -2
  26. package/dist/runtime/components/input/input-text-field.vue.d.ts +3 -1
  27. package/dist/runtime/components/input/input-textarea.d.vue.ts +1 -1
  28. package/dist/runtime/components/input/input-textarea.vue.d.ts +1 -1
  29. package/dist/runtime/components/loading.d.vue.ts +2 -4
  30. package/dist/runtime/components/loading.vue +4 -8
  31. package/dist/runtime/components/loading.vue.d.ts +2 -4
  32. package/dist/runtime/components/modal/modal-share.vue +1 -1
  33. package/dist/runtime/components/modal/modal.d.vue.ts +2 -1
  34. package/dist/runtime/components/modal/modal.vue +2 -2
  35. package/dist/runtime/components/modal/modal.vue.d.ts +2 -1
  36. package/dist/runtime/components/ui/input-group/InputGroupButton.d.vue.ts +1 -1
  37. package/dist/runtime/components/ui/input-group/InputGroupButton.vue.d.ts +1 -1
  38. package/dist/runtime/components/ui/input-group/index.d.ts +1 -1
  39. package/dist/runtime/components/ui/native-select/NativeSelectOptGroup.d.vue.ts +2 -2
  40. package/dist/runtime/components/ui/native-select/NativeSelectOptGroup.vue.d.ts +2 -2
  41. package/dist/runtime/components/ui/native-select/NativeSelectOption.d.vue.ts +2 -2
  42. package/dist/runtime/components/ui/native-select/NativeSelectOption.vue.d.ts +2 -2
  43. package/dist/runtime/components/ui/tabs/TabsList.vue +6 -4
  44. package/dist/runtime/components/ui/tabs/TabsTrigger.vue +6 -4
  45. package/dist/runtime/composables/useThaiAddress.d.ts +45 -0
  46. package/dist/runtime/composables/useThaiAddress.js +81 -0
  47. package/package.json +1 -1
@@ -1,158 +1,126 @@
1
1
  <template>
2
- <div class="space-y-4">
2
+ <div :class="['flex flex-col', props.gap]">
3
3
  <!-- Address Selector (Province, District, Sub-district, Zip code) -->
4
- <ShadFormField
5
- ref="addressFieldRef"
4
+ <InputCombobox
5
+ ref="comboboxRef"
6
6
  :name="props.name"
7
+ :label="props.label"
8
+ :placeholder="props.placeholder"
9
+ :required="props.required"
7
10
  :rules="props.rules || defaultRules"
8
- v-slot="{ componentField }"
9
- v-model="addressValue"
11
+ v-model="internalValue"
12
+ @clear="handleClear"
10
13
  >
11
- <ShadFormItem>
12
- <ShadFormLabel v-if="props.label" class="w-full">
13
- <div class="flex-1">
14
- {{ props.label }}
15
- <span v-if="props.required" class="text-destructive">*</span>
16
- </div>
17
- </ShadFormLabel>
18
- <ShadPopover v-model:open="popoverOpen" v-bind="componentField">
19
- <ShadPopoverTrigger as-child>
20
- <ShadFormControl>
21
- <ShadButton
22
- variant="outline"
23
- role="combobox"
24
- class="w-full justify-between text-start"
14
+ <template #popover-content="{ close }">
15
+ <ShadCommand class="p-2">
16
+ <ShadTabs v-model="currentTab" class="w-full">
17
+ <ShadTabsList class="w-full grid grid-cols-4">
18
+ <ShadTabsTrigger value="province"> จังหวัด </ShadTabsTrigger>
19
+ <ShadTabsTrigger value="district" :disabled="!selectedProvince">
20
+ อำเภอ/เขต
21
+ </ShadTabsTrigger>
22
+ <ShadTabsTrigger
23
+ value="subDistrict"
24
+ :disabled="!selectedDistrict"
25
25
  >
26
- <span :class="{ 'text-cloud': !addressValue }">
27
- {{ addressValue || props.placeholder }}
28
- </span>
29
- <Icon name="lucide:chevron-down" class="h-4 w-4" />
30
- </ShadButton>
31
- </ShadFormControl>
32
- </ShadPopoverTrigger>
33
- <ShadPopoverContent class="w-[400px] p-0">
34
- <ShadCommand>
35
- <ShadCommandInput
36
- v-model="searchQuery"
37
- placeholder="ค้นหาที่อยู่"
38
- />
39
- <ShadTabs v-model="currentTab" class="w-full">
40
- <ShadTabsList class="w-full grid grid-cols-4 rounded-none">
41
- <ShadTabsTrigger value="province" class="rounded-none">
42
- จังหวัด
43
- </ShadTabsTrigger>
44
- <ShadTabsTrigger
45
- value="district"
46
- :disabled="!selectedProvince"
47
- class="rounded-none"
48
- >
49
- อำเภอ/เขต
50
- </ShadTabsTrigger>
51
- <ShadTabsTrigger
52
- value="subDistrict"
53
- :disabled="!selectedDistrict"
54
- class="rounded-none"
55
- >
56
- ตำบล/แขวง
57
- </ShadTabsTrigger>
58
- <ShadTabsTrigger
59
- value="zipCode"
60
- :disabled="!selectedSubDistrict"
61
- class="rounded-none"
62
- >
63
- รหัสไปรษณีย์
64
- </ShadTabsTrigger>
65
- </ShadTabsList>
26
+ ตำบล/แขวง
27
+ </ShadTabsTrigger>
28
+ <ShadTabsTrigger value="zipCode" :disabled="!selectedSubDistrict">
29
+ รหัสไปรษณีย์
30
+ </ShadTabsTrigger>
31
+ </ShadTabsList>
32
+ <InputTextField v-model="searchQuery" placeholder="ค้นหาที่อยู่">
33
+ <template #prepend>
34
+ <Icon name="lucide:search" class="h-4 w-4" />
35
+ </template>
36
+ </InputTextField>
66
37
 
67
- <!-- Province Tab -->
68
- <ShadTabsContent value="province" class="m-0">
69
- <ShadCommandList class="max-h-64">
70
- <ShadCommandGroup>
71
- <ShadCommandItem
72
- v-for="province in filteredProvinces"
73
- :key="province.code"
74
- :value="province.name_th"
75
- @select="onSelectProvince(province)"
76
- >
77
- {{ province.name_th }}
78
- <Icon
79
- v-if="selectedProvince?.code === province.code"
80
- name="lucide:check"
81
- class="ml-auto h-4 w-4"
82
- />
83
- </ShadCommandItem>
84
- </ShadCommandGroup>
85
- <ShadCommandEmpty>ไม่พบจังหวัด</ShadCommandEmpty>
86
- </ShadCommandList>
87
- </ShadTabsContent>
38
+ <!-- Province Tab -->
39
+ <ShadTabsContent value="province" class="m-0">
40
+ <ShadCommandList class="max-h-64">
41
+ <ShadCommandGroup>
42
+ <ShadCommandItem
43
+ v-for="province in provinces"
44
+ :key="province.id"
45
+ :value="province.name_th"
46
+ @select="onSelectProvince(province)"
47
+ >
48
+ {{ province.name_th }}
49
+ <Icon
50
+ v-if="selectedProvince?.id === province.id"
51
+ name="lucide:check"
52
+ class="ml-auto h-4 w-4"
53
+ />
54
+ </ShadCommandItem>
55
+ </ShadCommandGroup>
56
+ <ShadCommandEmpty :force-show="provinces.length === 0" />
57
+ </ShadCommandList>
58
+ </ShadTabsContent>
88
59
 
89
- <!-- District Tab -->
90
- <ShadTabsContent value="district" class="m-0">
91
- <ShadCommandList class="max-h-64">
92
- <ShadCommandGroup>
93
- <ShadCommandItem
94
- v-for="district in filteredDistricts"
95
- :key="district.code"
96
- :value="district.name_th"
97
- @select="onSelectDistrict(district)"
98
- >
99
- {{ district.name_th }}
100
- <Icon
101
- v-if="selectedDistrict?.code === district.code"
102
- name="lucide:check"
103
- class="ml-auto h-4 w-4"
104
- />
105
- </ShadCommandItem>
106
- </ShadCommandGroup>
107
- <ShadCommandEmpty>ไม่พบอำเภอ/เขต</ShadCommandEmpty>
108
- </ShadCommandList>
109
- </ShadTabsContent>
60
+ <!-- District Tab -->
61
+ <ShadTabsContent value="district" class="m-0">
62
+ <ShadCommandList class="max-h-64">
63
+ <ShadCommandGroup>
64
+ <ShadCommandItem
65
+ v-for="district in amphurs"
66
+ :key="district.id"
67
+ :value="district.name_th"
68
+ @select="onSelectDistrict(district)"
69
+ >
70
+ {{ district.name_th }}
71
+ <Icon
72
+ v-if="selectedDistrict?.id === district.id"
73
+ name="lucide:check"
74
+ class="ml-auto h-4 w-4"
75
+ />
76
+ </ShadCommandItem>
77
+ </ShadCommandGroup>
78
+ <ShadCommandEmpty :force-show="amphurs.length === 0" />
79
+ </ShadCommandList>
80
+ </ShadTabsContent>
110
81
 
111
- <!-- Sub-district Tab -->
112
- <ShadTabsContent value="subDistrict" class="m-0">
113
- <ShadCommandList class="max-h-64">
114
- <ShadCommandGroup>
115
- <ShadCommandItem
116
- v-for="subDistrict in filteredSubDistricts"
117
- :key="subDistrict.code"
118
- :value="subDistrict.name_th"
119
- @select="onSelectSubDistrict(subDistrict)"
120
- >
121
- {{ subDistrict.name_th }}
122
- <Icon
123
- v-if="selectedSubDistrict?.code === subDistrict.code"
124
- name="lucide:check"
125
- class="ml-auto h-4 w-4"
126
- />
127
- </ShadCommandItem>
128
- </ShadCommandGroup>
129
- <ShadCommandEmpty>ไม่พบตำบล/แขวง</ShadCommandEmpty>
130
- </ShadCommandList>
131
- </ShadTabsContent>
82
+ <!-- Sub-district Tab -->
83
+ <ShadTabsContent value="subDistrict" class="m-0">
84
+ <ShadCommandList class="max-h-64">
85
+ <ShadCommandGroup>
86
+ <ShadCommandItem
87
+ v-for="subDistrict in tambons"
88
+ :key="subDistrict.id"
89
+ :value="subDistrict.name_th"
90
+ @select="onSelectSubDistrict(subDistrict)"
91
+ >
92
+ {{ subDistrict.name_th }}
93
+ <Icon
94
+ v-if="selectedSubDistrict?.id === subDistrict.id"
95
+ name="lucide:check"
96
+ class="ml-auto h-4 w-4"
97
+ />
98
+ </ShadCommandItem>
99
+ </ShadCommandGroup>
100
+ <ShadCommandEmpty :force-show="tambons.length === 0" />
101
+ </ShadCommandList>
102
+ </ShadTabsContent>
132
103
 
133
- <!-- Zip Code Tab -->
134
- <ShadTabsContent value="zipCode" class="m-0">
135
- <ShadCommandList class="max-h-64">
136
- <ShadCommandGroup>
137
- <ShadCommandItem
138
- v-if="selectedSubDistrict"
139
- :value="selectedSubDistrict.zip_code.toString()"
140
- @select="onSelectZipCode"
141
- >
142
- {{ selectedSubDistrict.zip_code }}
143
- <Icon name="lucide:check" class="ml-auto h-4 w-4" />
144
- </ShadCommandItem>
145
- </ShadCommandGroup>
146
- <ShadCommandEmpty>ไม่พบรหัสไปรษณีย์</ShadCommandEmpty>
147
- </ShadCommandList>
148
- </ShadTabsContent>
149
- </ShadTabs>
150
- </ShadCommand>
151
- </ShadPopoverContent>
152
- </ShadPopover>
153
- <ShadFormMessage />
154
- </ShadFormItem>
155
- </ShadFormField>
104
+ <!-- Zip Code Tab -->
105
+ <ShadTabsContent value="zipCode" class="m-0">
106
+ <ShadCommandList class="max-h-64">
107
+ <ShadCommandGroup>
108
+ <ShadCommandItem
109
+ v-if="selectedSubDistrict"
110
+ :value="selectedSubDistrict.zipcode_code?.toString() || ''"
111
+ @select="() => onSelectZipCode(close)"
112
+ >
113
+ {{ selectedSubDistrict.zipcode_code }}
114
+ <Icon name="lucide:check" class="ml-auto h-4 w-4" />
115
+ </ShadCommandItem>
116
+ </ShadCommandGroup>
117
+ <ShadCommandEmpty :force-show="!selectedSubDistrict" />
118
+ </ShadCommandList>
119
+ </ShadTabsContent>
120
+ </ShadTabs>
121
+ </ShadCommand>
122
+ </template>
123
+ </InputCombobox>
156
124
 
157
125
  <!-- Address Detail (House number, Soi, Moo, Road) -->
158
126
  <InputTextField
@@ -169,7 +137,10 @@
169
137
 
170
138
  <script setup>
171
139
  import { ref, computed, watch } from "vue";
172
- import InputTextField from "./input-text-field.vue";
140
+ import {
141
+ useThaiAddress
142
+ } from "@/runtime/composables/useThaiAddress";
143
+ const { getProvinces, getAmphursByProvinceId, getTambonsByAmphurId } = useThaiAddress();
173
144
  const props = defineProps({
174
145
  name: { type: String, required: false, default: "address" },
175
146
  label: { type: String, required: false, default: "\u0E08\u0E31\u0E07\u0E2B\u0E27\u0E31\u0E14, \u0E2D\u0E33\u0E40\u0E20\u0E2D/\u0E40\u0E02\u0E15, \u0E15\u0E33\u0E1A\u0E25/\u0E41\u0E02\u0E27\u0E07, \u0E23\u0E2B\u0E31\u0E2A\u0E44\u0E1B\u0E23\u0E29\u0E13\u0E35\u0E22\u0E4C" },
@@ -179,227 +150,62 @@ const props = defineProps({
179
150
  required: { type: Boolean, required: false, default: false },
180
151
  requiredDetail: { type: Boolean, required: false, default: false },
181
152
  rules: { type: [Object, String, Function], required: false },
182
- provinces: { type: Array, required: false },
183
- districts: { type: Array, required: false },
184
- subDistricts: { type: Array, required: false }
153
+ gap: { type: String, required: false, default: "gap-4" }
185
154
  });
186
155
  const emits = defineEmits(["selectProvince", "selectDistrict", "selectSubDistrict", "selectZipCode", "change"]);
187
156
  const modelValue = defineModel({ type: Object, ...{
188
157
  default: () => ({})
189
158
  } });
190
- const popoverOpen = ref(false);
191
159
  const currentTab = ref("province");
192
160
  const searchQuery = ref("");
193
- const addressFieldRef = ref();
161
+ const comboboxRef = ref();
194
162
  const detailFieldRef = ref();
195
163
  const selectedProvince = ref(null);
196
164
  const selectedDistrict = ref(null);
197
165
  const selectedSubDistrict = ref(null);
198
- const addressValue = computed(() => {
199
- const parts = [];
200
- if (selectedProvince.value) parts.push(selectedProvince.value.name_th);
201
- if (selectedDistrict.value) parts.push(selectedDistrict.value.name_th);
202
- if (selectedSubDistrict.value) parts.push(selectedSubDistrict.value.name_th);
203
- if (selectedSubDistrict.value)
204
- parts.push(selectedSubDistrict.value.zip_code.toString());
205
- return parts.join(", ");
206
- });
207
- const addressDetailValue = ref("");
208
- const mockProvinces = props.provinces || [
209
- { code: 10, name_th: "\u0E01\u0E23\u0E38\u0E07\u0E40\u0E17\u0E1E\u0E21\u0E2B\u0E32\u0E19\u0E04\u0E23", name_en: "Bangkok" },
210
- { code: 11, name_th: "\u0E2A\u0E21\u0E38\u0E17\u0E23\u0E1B\u0E23\u0E32\u0E01\u0E32\u0E23", name_en: "Samut Prakan" },
211
- { code: 12, name_th: "\u0E19\u0E19\u0E17\u0E1A\u0E38\u0E23\u0E35", name_en: "Nonthaburi" },
212
- { code: 13, name_th: "\u0E1B\u0E17\u0E38\u0E21\u0E18\u0E32\u0E19\u0E35", name_en: "Pathum Thani" },
213
- { code: 14, name_th: "\u0E1E\u0E23\u0E30\u0E19\u0E04\u0E23\u0E28\u0E23\u0E35\u0E2D\u0E22\u0E38\u0E18\u0E22\u0E32", name_en: "Phra Nakhon Si Ayutthaya" },
214
- { code: 20, name_th: "\u0E0A\u0E25\u0E1A\u0E38\u0E23\u0E35", name_en: "Chon Buri" },
215
- { code: 50, name_th: "\u0E40\u0E0A\u0E35\u0E22\u0E07\u0E43\u0E2B\u0E21\u0E48", name_en: "Chiang Mai" }
216
- ];
217
- const mockDistricts = props.districts || [
218
- // กรุงเทพมหานคร
219
- {
220
- code: 1001,
221
- name_th: "\u0E40\u0E02\u0E15\u0E1E\u0E23\u0E30\u0E19\u0E04\u0E23",
222
- name_en: "Phra Nakhon",
223
- province_code: 10
224
- },
225
- { code: 1002, name_th: "\u0E40\u0E02\u0E15\u0E14\u0E38\u0E2A\u0E34\u0E15", name_en: "Dusit", province_code: 10 },
226
- {
227
- code: 1003,
228
- name_th: "\u0E40\u0E02\u0E15\u0E2B\u0E19\u0E2D\u0E07\u0E08\u0E2D\u0E01",
229
- name_en: "Nong Chok",
230
- province_code: 10
231
- },
232
- { code: 1004, name_th: "\u0E40\u0E02\u0E15\u0E1A\u0E32\u0E07\u0E23\u0E31\u0E01", name_en: "Bang Rak", province_code: 10 },
233
- { code: 1005, name_th: "\u0E40\u0E02\u0E15\u0E1A\u0E32\u0E07\u0E40\u0E02\u0E19", name_en: "Bang Khen", province_code: 10 },
234
- {
235
- code: 1006,
236
- name_th: "\u0E40\u0E02\u0E15\u0E1A\u0E32\u0E07\u0E01\u0E30\u0E1B\u0E34",
237
- name_en: "Bang Kapi",
238
- province_code: 10
239
- },
240
- {
241
- code: 1007,
242
- name_th: "\u0E40\u0E02\u0E15\u0E1B\u0E17\u0E38\u0E21\u0E27\u0E31\u0E19",
243
- name_en: "Pathum Wan",
244
- province_code: 10
245
- },
246
- // สมุทรปราการ
247
- {
248
- code: 1101,
249
- name_th: "\u0E2D\u0E33\u0E40\u0E20\u0E2D\u0E40\u0E21\u0E37\u0E2D\u0E07\u0E2A\u0E21\u0E38\u0E17\u0E23\u0E1B\u0E23\u0E32\u0E01\u0E32\u0E23",
250
- name_en: "Mueang Samut Prakan",
251
- province_code: 11
252
- },
253
- { code: 1102, name_th: "\u0E2D\u0E33\u0E40\u0E20\u0E2D\u0E1A\u0E32\u0E07\u0E1A\u0E48\u0E2D", name_en: "Bang Bo", province_code: 11 },
254
- // นนทบุรี
255
- {
256
- code: 1201,
257
- name_th: "\u0E2D\u0E33\u0E40\u0E20\u0E2D\u0E40\u0E21\u0E37\u0E2D\u0E07\u0E19\u0E19\u0E17\u0E1A\u0E38\u0E23\u0E35",
258
- name_en: "Mueang Nonthaburi",
259
- province_code: 12
260
- },
261
- {
262
- code: 1202,
263
- name_th: "\u0E2D\u0E33\u0E40\u0E20\u0E2D\u0E1A\u0E32\u0E07\u0E01\u0E23\u0E27\u0E22",
264
- name_en: "Bang Kruai",
265
- province_code: 12
266
- }
267
- ];
268
- const mockSubDistricts = props.subDistricts || [
269
- // เขตพระนคร
270
- {
271
- code: 100101,
272
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E1E\u0E23\u0E30\u0E1A\u0E23\u0E21\u0E21\u0E2B\u0E32\u0E23\u0E32\u0E0A\u0E27\u0E31\u0E07",
273
- name_en: "Phra Borom Maha Ratchawang",
274
- district_code: 1001,
275
- zip_code: 10200
276
- },
277
- {
278
- code: 100102,
279
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E27\u0E31\u0E07\u0E1A\u0E39\u0E23\u0E1E\u0E32\u0E20\u0E34\u0E23\u0E21\u0E22\u0E4C",
280
- name_en: "Wang Burapha Phirom",
281
- district_code: 1001,
282
- zip_code: 10200
283
- },
284
- {
285
- code: 100103,
286
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E27\u0E31\u0E14\u0E23\u0E32\u0E0A\u0E1A\u0E1E\u0E34\u0E18",
287
- name_en: "Wat Ratchabophit",
288
- district_code: 1001,
289
- zip_code: 10200
290
- },
291
- // เขตดุสิต
292
- {
293
- code: 100201,
294
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E14\u0E38\u0E2A\u0E34\u0E15",
295
- name_en: "Dusit",
296
- district_code: 1002,
297
- zip_code: 10300
298
- },
299
- {
300
- code: 100202,
301
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E27\u0E0A\u0E34\u0E23\u0E1E\u0E22\u0E32\u0E1A\u0E32\u0E25",
302
- name_en: "Wachiraphayaban",
303
- district_code: 1002,
304
- zip_code: 10300
305
- },
306
- // เขตบางรัก
307
- {
308
- code: 100401,
309
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E21\u0E2B\u0E32\u0E1E\u0E24\u0E12\u0E32\u0E23\u0E32\u0E21",
310
- name_en: "Maha Phruettharam",
311
- district_code: 1004,
312
- zip_code: 10500
313
- },
314
- {
315
- code: 100402,
316
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E2A\u0E35\u0E25\u0E21",
317
- name_en: "Si Lom",
318
- district_code: 1004,
319
- zip_code: 10500
320
- },
321
- // เขตปทุมวัน
322
- {
323
- code: 100701,
324
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E23\u0E2D\u0E07\u0E40\u0E21\u0E37\u0E2D\u0E07",
325
- name_en: "Rong Mueang",
326
- district_code: 1007,
327
- zip_code: 10330
328
- },
329
- {
330
- code: 100702,
331
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E27\u0E31\u0E07\u0E43\u0E2B\u0E21\u0E48",
332
- name_en: "Wang Mai",
333
- district_code: 1007,
334
- zip_code: 10330
335
- },
336
- {
337
- code: 100703,
338
- name_th: "\u0E41\u0E02\u0E27\u0E07\u0E1B\u0E17\u0E38\u0E21\u0E27\u0E31\u0E19",
339
- name_en: "Pathum Wan",
340
- district_code: 1007,
341
- zip_code: 10330
342
- },
343
- // อำเภอเมืองสมุทรปราการ
344
- {
345
- code: 110101,
346
- name_th: "\u0E15\u0E33\u0E1A\u0E25\u0E1B\u0E32\u0E01\u0E19\u0E49\u0E33",
347
- name_en: "Pak Nam",
348
- district_code: 1101,
349
- zip_code: 10270
350
- },
351
- {
352
- code: 110102,
353
- name_th: "\u0E15\u0E33\u0E1A\u0E25\u0E2A\u0E33\u0E42\u0E23\u0E07\u0E40\u0E2B\u0E19\u0E37\u0E2D",
354
- name_en: "Samrong Nuea",
355
- district_code: 1101,
356
- zip_code: 10270
357
- },
358
- // อำเภอเมืองนนทบุรี
359
- {
360
- code: 120101,
361
- name_th: "\u0E15\u0E33\u0E1A\u0E25\u0E2A\u0E27\u0E19\u0E43\u0E2B\u0E0D\u0E48",
362
- name_en: "Suan Yai",
363
- district_code: 1201,
364
- zip_code: 11e3
365
- },
366
- {
367
- code: 120102,
368
- name_th: "\u0E15\u0E33\u0E1A\u0E25\u0E15\u0E25\u0E32\u0E14\u0E02\u0E27\u0E31\u0E0D",
369
- name_en: "Talat Khwan",
370
- district_code: 1201,
371
- zip_code: 11e3
372
- }
373
- ];
374
- const filteredProvinces = computed(() => {
375
- if (!searchQuery.value) return mockProvinces;
166
+ const provinces = computed(() => {
167
+ const allProvinces = getProvinces();
168
+ if (!searchQuery.value || currentTab.value !== "province")
169
+ return allProvinces;
376
170
  const query = searchQuery.value.toLowerCase();
377
- return mockProvinces.filter(
171
+ return allProvinces.filter(
378
172
  (p) => p.name_th.toLowerCase().includes(query) || p.name_en.toLowerCase().includes(query)
379
173
  );
380
174
  });
381
- const filteredDistricts = computed(() => {
175
+ const amphurs = computed(() => {
382
176
  if (!selectedProvince.value) return [];
383
- const districts = mockDistricts.filter(
384
- (d) => d.province_code === selectedProvince.value?.code
385
- );
386
- if (!searchQuery.value) return districts;
177
+ const allAmphurs = getAmphursByProvinceId(selectedProvince.value.id);
178
+ if (!searchQuery.value || currentTab.value !== "district") return allAmphurs;
387
179
  const query = searchQuery.value.toLowerCase();
388
- return districts.filter(
389
- (d) => d.name_th.toLowerCase().includes(query) || d.name_en.toLowerCase().includes(query)
180
+ return allAmphurs.filter(
181
+ (a) => a.name_th.toLowerCase().includes(query) || a.name_en.toLowerCase().includes(query)
390
182
  );
391
183
  });
392
- const filteredSubDistricts = computed(() => {
184
+ const tambons = computed(() => {
393
185
  if (!selectedDistrict.value) return [];
394
- const subDistricts = mockSubDistricts.filter(
395
- (s) => s.district_code === selectedDistrict.value?.code
396
- );
397
- if (!searchQuery.value) return subDistricts;
186
+ const allTambons = getTambonsByAmphurId(selectedDistrict.value.id);
187
+ if (!searchQuery.value || currentTab.value !== "subDistrict")
188
+ return allTambons;
398
189
  const query = searchQuery.value.toLowerCase();
399
- return subDistricts.filter(
400
- (s) => s.name_th.toLowerCase().includes(query) || s.name_en.toLowerCase().includes(query)
190
+ return allTambons.filter(
191
+ (t) => t.name_th.toLowerCase().includes(query) || t.name_en.toLowerCase().includes(query)
401
192
  );
402
193
  });
194
+ const addressValue = computed(() => {
195
+ const parts = [];
196
+ if (selectedProvince.value) parts.push(selectedProvince.value.name_th);
197
+ if (selectedDistrict.value) parts.push(selectedDistrict.value.name_th);
198
+ if (selectedSubDistrict.value) parts.push(selectedSubDistrict.value.name_th);
199
+ if (selectedSubDistrict.value?.zipcode_code)
200
+ parts.push(selectedSubDistrict.value.zipcode_code.toString());
201
+ return parts.join(", ");
202
+ });
203
+ const internalValue = computed({
204
+ get: () => addressValue.value,
205
+ set: () => {
206
+ }
207
+ });
208
+ const addressDetailValue = ref("");
403
209
  const onSelectProvince = (province) => {
404
210
  selectedProvince.value = province;
405
211
  selectedDistrict.value = null;
@@ -424,22 +230,29 @@ const onSelectSubDistrict = (subDistrict) => {
424
230
  emits("selectSubDistrict", subDistrict);
425
231
  updateModelValue();
426
232
  };
427
- const onSelectZipCode = () => {
428
- if (selectedSubDistrict.value) {
429
- emits("selectZipCode", selectedSubDistrict.value.zip_code);
430
- popoverOpen.value = false;
233
+ const onSelectZipCode = (close) => {
234
+ if (selectedSubDistrict.value?.zipcode_code) {
235
+ emits("selectZipCode", selectedSubDistrict.value.zipcode_code);
236
+ close?.();
431
237
  currentTab.value = "province";
432
238
  updateModelValue();
433
239
  }
434
240
  };
241
+ const handleClear = () => {
242
+ selectedProvince.value = null;
243
+ selectedDistrict.value = null;
244
+ selectedSubDistrict.value = null;
245
+ searchQuery.value = "";
246
+ currentTab.value = "province";
247
+ };
435
248
  const updateModelValue = () => {
436
249
  const value = {
437
- province: selectedProvince.value || void 0,
438
- district: selectedDistrict.value || void 0,
439
- subDistrict: selectedSubDistrict.value || void 0,
440
- zipCode: selectedSubDistrict.value?.zip_code,
441
- detail: addressDetailValue.value || void 0,
442
- fullAddress: buildFullAddress()
250
+ province_id: selectedProvince.value?.id,
251
+ amphur_id: selectedDistrict.value?.id,
252
+ tambon_id: selectedSubDistrict.value?.id,
253
+ zipcode: selectedSubDistrict.value?.zipcode_code,
254
+ detail_address: addressDetailValue.value || void 0,
255
+ full_address: buildFullAddress()
443
256
  };
444
257
  modelValue.value = value;
445
258
  emits("change", value);
@@ -450,8 +263,8 @@ const buildFullAddress = () => {
450
263
  if (selectedSubDistrict.value) parts.push(selectedSubDistrict.value.name_th);
451
264
  if (selectedDistrict.value) parts.push(selectedDistrict.value.name_th);
452
265
  if (selectedProvince.value) parts.push(selectedProvince.value.name_th);
453
- if (selectedSubDistrict.value)
454
- parts.push(selectedSubDistrict.value.zip_code.toString());
266
+ if (selectedSubDistrict.value?.zipcode_code)
267
+ parts.push(selectedSubDistrict.value.zipcode_code.toString());
455
268
  return parts.join(" ");
456
269
  };
457
270
  watch(addressDetailValue, () => {
@@ -467,12 +280,12 @@ const defaultRules = (v) => {
467
280
  return true;
468
281
  };
469
282
  const validate = async () => {
470
- const addressValid = await addressFieldRef.value?.validate();
283
+ const addressValid = await comboboxRef.value?.validate?.();
471
284
  const detailValid = await detailFieldRef.value?.validate?.();
472
- return addressValid?.valid !== false && detailValid !== false;
285
+ return addressValid?.valid !== false && detailValid?.valid !== false;
473
286
  };
474
287
  const setErrors = (errMsg) => {
475
- addressFieldRef.value?.setErrors(errMsg);
288
+ comboboxRef.value?.setErrors(errMsg);
476
289
  };
477
290
  const reset = () => {
478
291
  selectedProvince.value = null;
@@ -480,13 +293,14 @@ const reset = () => {
480
293
  selectedSubDistrict.value = null;
481
294
  addressDetailValue.value = "";
482
295
  currentTab.value = "province";
296
+ searchQuery.value = "";
483
297
  updateModelValue();
484
298
  };
485
299
  defineExpose({
486
300
  validate,
487
301
  setErrors,
488
302
  reset,
489
- addressFieldRef,
303
+ comboboxRef,
490
304
  detailFieldRef
491
305
  });
492
306
  </script>