pukaad-ui-lib 1.40.0 → 1.42.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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/assets/json/address/Amphur.json +7346 -0
- package/dist/runtime/assets/json/address/Province.json +762 -0
- package/dist/runtime/assets/json/address/Tambon.json +64775 -0
- package/dist/runtime/assets/json/address/Zipcode.json +17208 -0
- package/dist/runtime/components/button.vue +1 -1
- package/dist/runtime/components/collapse/collapse.d.vue.ts +2 -0
- package/dist/runtime/components/collapse/collapse.vue +13 -3
- package/dist/runtime/components/collapse/collapse.vue.d.ts +2 -0
- package/dist/runtime/components/input/input-address.d.vue.ts +56 -1
- package/dist/runtime/components/input/input-address.vue +293 -74
- package/dist/runtime/components/input/input-address.vue.d.ts +56 -1
- package/dist/runtime/components/input/input-autocomplete.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-autocomplete.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-combobox.d.vue.ts +10 -2
- package/dist/runtime/components/input/input-combobox.vue +44 -31
- package/dist/runtime/components/input/input-combobox.vue.d.ts +10 -2
- package/dist/runtime/components/input/input-text-field.d.vue.ts +2 -0
- package/dist/runtime/components/input/input-text-field.vue +8 -2
- package/dist/runtime/components/input/input-text-field.vue.d.ts +2 -0
- package/dist/runtime/components/ui/tabs/TabsList.vue +6 -4
- package/dist/runtime/components/ui/tabs/TabsTrigger.vue +6 -4
- package/dist/runtime/composables/useThaiAddress.d.ts +45 -0
- package/dist/runtime/composables/useThaiAddress.js +81 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ type __VLS_Props = {
|
|
|
2
2
|
title?: string;
|
|
3
3
|
width?: number | string;
|
|
4
4
|
fullWidth?: boolean;
|
|
5
|
+
expandAll?: boolean;
|
|
5
6
|
};
|
|
6
7
|
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_12: {};
|
|
7
8
|
type __VLS_Slots = {} & {
|
|
@@ -15,6 +16,7 @@ type __VLS_Slots = {} & {
|
|
|
15
16
|
};
|
|
16
17
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
17
18
|
fullWidth: boolean;
|
|
19
|
+
expandAll: boolean;
|
|
18
20
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
21
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
20
22
|
declare const _default: typeof __VLS_export;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
>
|
|
8
8
|
<slot name="title">
|
|
9
9
|
<div
|
|
10
|
-
class="flex justify-between items-center cursor-pointer"
|
|
10
|
+
class="flex justify-between items-center p-2 cursor-pointer"
|
|
11
11
|
@click="isOpen = !isOpen"
|
|
12
12
|
>
|
|
13
13
|
<slot name="title-text">
|
|
@@ -29,11 +29,21 @@
|
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<script setup>
|
|
32
|
-
import { ref } from "vue";
|
|
32
|
+
import { ref, watch } from "vue";
|
|
33
33
|
const props = defineProps({
|
|
34
34
|
title: { type: String, required: false },
|
|
35
35
|
width: { type: [Number, String], required: false },
|
|
36
|
-
fullWidth: { type: Boolean, required: false, default: false }
|
|
36
|
+
fullWidth: { type: Boolean, required: false, default: false },
|
|
37
|
+
expandAll: { type: Boolean, required: false, default: false }
|
|
37
38
|
});
|
|
38
39
|
const isOpen = ref(false);
|
|
40
|
+
watch(
|
|
41
|
+
() => props.expandAll,
|
|
42
|
+
(expandAll) => {
|
|
43
|
+
if (expandAll) {
|
|
44
|
+
isOpen.value = true;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{ immediate: true }
|
|
48
|
+
);
|
|
39
49
|
</script>
|
|
@@ -2,6 +2,7 @@ type __VLS_Props = {
|
|
|
2
2
|
title?: string;
|
|
3
3
|
width?: number | string;
|
|
4
4
|
fullWidth?: boolean;
|
|
5
|
+
expandAll?: boolean;
|
|
5
6
|
};
|
|
6
7
|
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_12: {};
|
|
7
8
|
type __VLS_Slots = {} & {
|
|
@@ -15,6 +16,7 @@ type __VLS_Slots = {} & {
|
|
|
15
16
|
};
|
|
16
17
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
17
18
|
fullWidth: boolean;
|
|
19
|
+
expandAll: boolean;
|
|
18
20
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
21
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
20
22
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,3 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
import { type Province, type Amphur, type Tambon } from "@/runtime/composables/useThaiAddress";
|
|
2
|
+
export interface InputAddressValue {
|
|
3
|
+
province_id?: number;
|
|
4
|
+
amphur_id?: number;
|
|
5
|
+
tambon_id?: number;
|
|
6
|
+
zipcode?: number;
|
|
7
|
+
detail_address?: string;
|
|
8
|
+
full_address?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface InputAddressProps {
|
|
11
|
+
name?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
labelDetail?: string;
|
|
15
|
+
placeholderDetail?: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
requiredDetail?: boolean;
|
|
18
|
+
rules?: object | string | Function;
|
|
19
|
+
gap?: string;
|
|
20
|
+
}
|
|
21
|
+
type __VLS_Props = InputAddressProps;
|
|
22
|
+
type __VLS_ModelProps = {
|
|
23
|
+
modelValue?: InputAddressValue;
|
|
24
|
+
};
|
|
25
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
26
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
27
|
+
validate: () => Promise<boolean>;
|
|
28
|
+
setErrors: (errMsg: string[]) => void;
|
|
29
|
+
reset: () => void;
|
|
30
|
+
comboboxRef: import("vue").Ref<any, any>;
|
|
31
|
+
detailFieldRef: import("vue").Ref<any, any>;
|
|
32
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
|
+
"update:modelValue": (value: InputAddressValue) => any;
|
|
34
|
+
} & {
|
|
35
|
+
change: (value: InputAddressValue) => any;
|
|
36
|
+
selectProvince: (province: Province) => any;
|
|
37
|
+
selectDistrict: (district: Amphur) => any;
|
|
38
|
+
selectSubDistrict: (subDistrict: Tambon) => any;
|
|
39
|
+
selectZipCode: (zipCode: number) => any;
|
|
40
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
41
|
+
onChange?: ((value: InputAddressValue) => any) | undefined;
|
|
42
|
+
"onUpdate:modelValue"?: ((value: InputAddressValue) => any) | undefined;
|
|
43
|
+
onSelectProvince?: ((province: Province) => any) | undefined;
|
|
44
|
+
onSelectDistrict?: ((district: Amphur) => any) | undefined;
|
|
45
|
+
onSelectSubDistrict?: ((subDistrict: Tambon) => any) | undefined;
|
|
46
|
+
onSelectZipCode?: ((zipCode: number) => any) | undefined;
|
|
47
|
+
}>, {
|
|
48
|
+
label: string;
|
|
49
|
+
required: boolean;
|
|
50
|
+
name: string;
|
|
51
|
+
placeholder: string;
|
|
52
|
+
gap: string;
|
|
53
|
+
labelDetail: string;
|
|
54
|
+
placeholderDetail: string;
|
|
55
|
+
requiredDetail: boolean;
|
|
56
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
2
57
|
declare const _default: typeof __VLS_export;
|
|
3
58
|
export default _default;
|
|
@@ -1,87 +1,306 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<template #content>
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
:key="i_pro"
|
|
26
|
-
class="p-2 border-b-[1px] border-mercury cursor-pointer"
|
|
27
|
-
@click="onSelectProvince(pro)"
|
|
2
|
+
<div :class="['flex flex-col', props.gap]">
|
|
3
|
+
<!-- Address Selector (Province, District, Sub-district, Zip code) -->
|
|
4
|
+
<InputCombobox
|
|
5
|
+
ref="comboboxRef"
|
|
6
|
+
:name="props.name"
|
|
7
|
+
:label="props.label"
|
|
8
|
+
:placeholder="props.placeholder"
|
|
9
|
+
:required="props.required"
|
|
10
|
+
:rules="props.rules || defaultRules"
|
|
11
|
+
v-model="internalValue"
|
|
12
|
+
@clear="handleClear"
|
|
13
|
+
>
|
|
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"
|
|
28
25
|
>
|
|
29
|
-
|
|
30
|
-
</
|
|
31
|
-
|
|
32
|
-
|
|
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>
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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>
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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>
|
|
61
81
|
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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>
|
|
103
|
+
|
|
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>
|
|
73
122
|
</template>
|
|
74
|
-
</
|
|
123
|
+
</InputCombobox>
|
|
124
|
+
|
|
125
|
+
<!-- Address Detail (House number, Soi, Moo, Road) -->
|
|
75
126
|
<InputTextField
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
:
|
|
79
|
-
:
|
|
127
|
+
ref="detailFieldRef"
|
|
128
|
+
:name="`${props.name}-detail`"
|
|
129
|
+
:label="props.labelDetail"
|
|
130
|
+
:placeholder="props.placeholderDetail"
|
|
131
|
+
:disabled="!addressValue"
|
|
132
|
+
:required="props.requiredDetail"
|
|
80
133
|
v-model="addressDetailValue"
|
|
81
134
|
/>
|
|
82
|
-
</div>
|
|
135
|
+
</div>
|
|
83
136
|
</template>
|
|
84
137
|
|
|
85
138
|
<script setup>
|
|
86
|
-
|
|
139
|
+
import { ref, computed, watch } from "vue";
|
|
140
|
+
import {
|
|
141
|
+
useThaiAddress
|
|
142
|
+
} from "@/runtime/composables/useThaiAddress";
|
|
143
|
+
const { getProvinces, getAmphursByProvinceId, getTambonsByAmphurId } = useThaiAddress();
|
|
144
|
+
const props = defineProps({
|
|
145
|
+
name: { type: String, required: false, default: "address" },
|
|
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" },
|
|
147
|
+
placeholder: { 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" },
|
|
148
|
+
labelDetail: { type: String, required: false, default: "\u0E1A\u0E49\u0E32\u0E19\u0E40\u0E25\u0E02\u0E17\u0E35\u0E48, \u0E0B\u0E2D\u0E22, \u0E2B\u0E21\u0E39\u0E48, \u0E16\u0E19\u0E19" },
|
|
149
|
+
placeholderDetail: { type: String, required: false, default: "\u0E1A\u0E49\u0E32\u0E19\u0E40\u0E25\u0E02\u0E17\u0E35\u0E48, \u0E0B\u0E2D\u0E22, \u0E2B\u0E21\u0E39\u0E48, \u0E16\u0E19\u0E19" },
|
|
150
|
+
required: { type: Boolean, required: false, default: false },
|
|
151
|
+
requiredDetail: { type: Boolean, required: false, default: false },
|
|
152
|
+
rules: { type: [Object, String, Function], required: false },
|
|
153
|
+
gap: { type: String, required: false, default: "gap-4" }
|
|
154
|
+
});
|
|
155
|
+
const emits = defineEmits(["selectProvince", "selectDistrict", "selectSubDistrict", "selectZipCode", "change"]);
|
|
156
|
+
const modelValue = defineModel({ type: Object, ...{
|
|
157
|
+
default: () => ({})
|
|
158
|
+
} });
|
|
159
|
+
const currentTab = ref("province");
|
|
160
|
+
const searchQuery = ref("");
|
|
161
|
+
const comboboxRef = ref();
|
|
162
|
+
const detailFieldRef = ref();
|
|
163
|
+
const selectedProvince = ref(null);
|
|
164
|
+
const selectedDistrict = ref(null);
|
|
165
|
+
const selectedSubDistrict = ref(null);
|
|
166
|
+
const provinces = computed(() => {
|
|
167
|
+
const allProvinces = getProvinces();
|
|
168
|
+
if (!searchQuery.value || currentTab.value !== "province")
|
|
169
|
+
return allProvinces;
|
|
170
|
+
const query = searchQuery.value.toLowerCase();
|
|
171
|
+
return allProvinces.filter(
|
|
172
|
+
(p) => p.name_th.toLowerCase().includes(query) || p.name_en.toLowerCase().includes(query)
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
const amphurs = computed(() => {
|
|
176
|
+
if (!selectedProvince.value) return [];
|
|
177
|
+
const allAmphurs = getAmphursByProvinceId(selectedProvince.value.id);
|
|
178
|
+
if (!searchQuery.value || currentTab.value !== "district") return allAmphurs;
|
|
179
|
+
const query = searchQuery.value.toLowerCase();
|
|
180
|
+
return allAmphurs.filter(
|
|
181
|
+
(a) => a.name_th.toLowerCase().includes(query) || a.name_en.toLowerCase().includes(query)
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
const tambons = computed(() => {
|
|
185
|
+
if (!selectedDistrict.value) return [];
|
|
186
|
+
const allTambons = getTambonsByAmphurId(selectedDistrict.value.id);
|
|
187
|
+
if (!searchQuery.value || currentTab.value !== "subDistrict")
|
|
188
|
+
return allTambons;
|
|
189
|
+
const query = searchQuery.value.toLowerCase();
|
|
190
|
+
return allTambons.filter(
|
|
191
|
+
(t) => t.name_th.toLowerCase().includes(query) || t.name_en.toLowerCase().includes(query)
|
|
192
|
+
);
|
|
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("");
|
|
209
|
+
const onSelectProvince = (province) => {
|
|
210
|
+
selectedProvince.value = province;
|
|
211
|
+
selectedDistrict.value = null;
|
|
212
|
+
selectedSubDistrict.value = null;
|
|
213
|
+
searchQuery.value = "";
|
|
214
|
+
currentTab.value = "district";
|
|
215
|
+
emits("selectProvince", province);
|
|
216
|
+
updateModelValue();
|
|
217
|
+
};
|
|
218
|
+
const onSelectDistrict = (district) => {
|
|
219
|
+
selectedDistrict.value = district;
|
|
220
|
+
selectedSubDistrict.value = null;
|
|
221
|
+
searchQuery.value = "";
|
|
222
|
+
currentTab.value = "subDistrict";
|
|
223
|
+
emits("selectDistrict", district);
|
|
224
|
+
updateModelValue();
|
|
225
|
+
};
|
|
226
|
+
const onSelectSubDistrict = (subDistrict) => {
|
|
227
|
+
selectedSubDistrict.value = subDistrict;
|
|
228
|
+
searchQuery.value = "";
|
|
229
|
+
currentTab.value = "zipCode";
|
|
230
|
+
emits("selectSubDistrict", subDistrict);
|
|
231
|
+
updateModelValue();
|
|
232
|
+
};
|
|
233
|
+
const onSelectZipCode = (close) => {
|
|
234
|
+
if (selectedSubDistrict.value?.zipcode_code) {
|
|
235
|
+
emits("selectZipCode", selectedSubDistrict.value.zipcode_code);
|
|
236
|
+
close?.();
|
|
237
|
+
currentTab.value = "province";
|
|
238
|
+
updateModelValue();
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const handleClear = () => {
|
|
242
|
+
selectedProvince.value = null;
|
|
243
|
+
selectedDistrict.value = null;
|
|
244
|
+
selectedSubDistrict.value = null;
|
|
245
|
+
searchQuery.value = "";
|
|
246
|
+
currentTab.value = "province";
|
|
247
|
+
};
|
|
248
|
+
const updateModelValue = () => {
|
|
249
|
+
const value = {
|
|
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()
|
|
256
|
+
};
|
|
257
|
+
modelValue.value = value;
|
|
258
|
+
emits("change", value);
|
|
259
|
+
};
|
|
260
|
+
const buildFullAddress = () => {
|
|
261
|
+
const parts = [];
|
|
262
|
+
if (addressDetailValue.value) parts.push(addressDetailValue.value);
|
|
263
|
+
if (selectedSubDistrict.value) parts.push(selectedSubDistrict.value.name_th);
|
|
264
|
+
if (selectedDistrict.value) parts.push(selectedDistrict.value.name_th);
|
|
265
|
+
if (selectedProvince.value) parts.push(selectedProvince.value.name_th);
|
|
266
|
+
if (selectedSubDistrict.value?.zipcode_code)
|
|
267
|
+
parts.push(selectedSubDistrict.value.zipcode_code.toString());
|
|
268
|
+
return parts.join(" ");
|
|
269
|
+
};
|
|
270
|
+
watch(addressDetailValue, () => {
|
|
271
|
+
updateModelValue();
|
|
272
|
+
});
|
|
273
|
+
const defaultRules = (v) => {
|
|
274
|
+
if (!v && props.required) {
|
|
275
|
+
return "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E17\u0E35\u0E48\u0E2D\u0E22\u0E39\u0E48";
|
|
276
|
+
}
|
|
277
|
+
if (v && v.split(", ").length < 4 && props.required) {
|
|
278
|
+
return "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E17\u0E35\u0E48\u0E2D\u0E22\u0E39\u0E48\u0E43\u0E2B\u0E49\u0E04\u0E23\u0E1A\u0E16\u0E49\u0E27\u0E19";
|
|
279
|
+
}
|
|
280
|
+
return true;
|
|
281
|
+
};
|
|
282
|
+
const validate = async () => {
|
|
283
|
+
const addressValid = await comboboxRef.value?.validate?.();
|
|
284
|
+
const detailValid = await detailFieldRef.value?.validate?.();
|
|
285
|
+
return addressValid?.valid !== false && detailValid?.valid !== false;
|
|
286
|
+
};
|
|
287
|
+
const setErrors = (errMsg) => {
|
|
288
|
+
comboboxRef.value?.setErrors(errMsg);
|
|
289
|
+
};
|
|
290
|
+
const reset = () => {
|
|
291
|
+
selectedProvince.value = null;
|
|
292
|
+
selectedDistrict.value = null;
|
|
293
|
+
selectedSubDistrict.value = null;
|
|
294
|
+
addressDetailValue.value = "";
|
|
295
|
+
currentTab.value = "province";
|
|
296
|
+
searchQuery.value = "";
|
|
297
|
+
updateModelValue();
|
|
298
|
+
};
|
|
299
|
+
defineExpose({
|
|
300
|
+
validate,
|
|
301
|
+
setErrors,
|
|
302
|
+
reset,
|
|
303
|
+
comboboxRef,
|
|
304
|
+
detailFieldRef
|
|
305
|
+
});
|
|
87
306
|
</script>
|
|
@@ -1,3 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
import { type Province, type Amphur, type Tambon } from "@/runtime/composables/useThaiAddress";
|
|
2
|
+
export interface InputAddressValue {
|
|
3
|
+
province_id?: number;
|
|
4
|
+
amphur_id?: number;
|
|
5
|
+
tambon_id?: number;
|
|
6
|
+
zipcode?: number;
|
|
7
|
+
detail_address?: string;
|
|
8
|
+
full_address?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface InputAddressProps {
|
|
11
|
+
name?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
labelDetail?: string;
|
|
15
|
+
placeholderDetail?: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
requiredDetail?: boolean;
|
|
18
|
+
rules?: object | string | Function;
|
|
19
|
+
gap?: string;
|
|
20
|
+
}
|
|
21
|
+
type __VLS_Props = InputAddressProps;
|
|
22
|
+
type __VLS_ModelProps = {
|
|
23
|
+
modelValue?: InputAddressValue;
|
|
24
|
+
};
|
|
25
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
26
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
27
|
+
validate: () => Promise<boolean>;
|
|
28
|
+
setErrors: (errMsg: string[]) => void;
|
|
29
|
+
reset: () => void;
|
|
30
|
+
comboboxRef: import("vue").Ref<any, any>;
|
|
31
|
+
detailFieldRef: import("vue").Ref<any, any>;
|
|
32
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
|
+
"update:modelValue": (value: InputAddressValue) => any;
|
|
34
|
+
} & {
|
|
35
|
+
change: (value: InputAddressValue) => any;
|
|
36
|
+
selectProvince: (province: Province) => any;
|
|
37
|
+
selectDistrict: (district: Amphur) => any;
|
|
38
|
+
selectSubDistrict: (subDistrict: Tambon) => any;
|
|
39
|
+
selectZipCode: (zipCode: number) => any;
|
|
40
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
41
|
+
onChange?: ((value: InputAddressValue) => any) | undefined;
|
|
42
|
+
"onUpdate:modelValue"?: ((value: InputAddressValue) => any) | undefined;
|
|
43
|
+
onSelectProvince?: ((province: Province) => any) | undefined;
|
|
44
|
+
onSelectDistrict?: ((district: Amphur) => any) | undefined;
|
|
45
|
+
onSelectSubDistrict?: ((subDistrict: Tambon) => any) | undefined;
|
|
46
|
+
onSelectZipCode?: ((zipCode: number) => any) | undefined;
|
|
47
|
+
}>, {
|
|
48
|
+
label: string;
|
|
49
|
+
required: boolean;
|
|
50
|
+
name: string;
|
|
51
|
+
placeholder: string;
|
|
52
|
+
gap: string;
|
|
53
|
+
labelDetail: string;
|
|
54
|
+
placeholderDetail: string;
|
|
55
|
+
requiredDetail: boolean;
|
|
56
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
2
57
|
declare const _default: typeof __VLS_export;
|
|
3
58
|
export default _default;
|
|
@@ -37,8 +37,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
37
37
|
name: string;
|
|
38
38
|
placeholder: string;
|
|
39
39
|
description: string;
|
|
40
|
-
options: AutocompleteOption[] | string[] | number[];
|
|
41
40
|
limit: number;
|
|
41
|
+
options: AutocompleteOption[] | string[] | number[];
|
|
42
42
|
disabledErrorMessage: boolean;
|
|
43
43
|
disabledBorder: boolean;
|
|
44
44
|
showCounter: boolean;
|
|
@@ -37,8 +37,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
37
37
|
name: string;
|
|
38
38
|
placeholder: string;
|
|
39
39
|
description: string;
|
|
40
|
-
options: AutocompleteOption[] | string[] | number[];
|
|
41
40
|
limit: number;
|
|
41
|
+
options: AutocompleteOption[] | string[] | number[];
|
|
42
42
|
disabledErrorMessage: boolean;
|
|
43
43
|
disabledBorder: boolean;
|
|
44
44
|
showCounter: boolean;
|
|
@@ -19,18 +19,26 @@ type __VLS_ModelProps = {
|
|
|
19
19
|
modelValue?: string | string[];
|
|
20
20
|
};
|
|
21
21
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
22
|
-
declare var __VLS_25: {},
|
|
22
|
+
declare var __VLS_25: {}, __VLS_44: {
|
|
23
|
+
close: () => boolean;
|
|
24
|
+
}, __VLS_69: {};
|
|
23
25
|
type __VLS_Slots = {} & {
|
|
24
26
|
label?: (props: typeof __VLS_25) => any;
|
|
25
27
|
} & {
|
|
26
|
-
|
|
28
|
+
'popover-content'?: (props: typeof __VLS_44) => any;
|
|
29
|
+
} & {
|
|
30
|
+
options?: (props: typeof __VLS_69) => any;
|
|
27
31
|
};
|
|
28
32
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
29
33
|
setErrors: (errMsg: string[]) => void;
|
|
34
|
+
validate: () => Promise<any>;
|
|
30
35
|
inputTextFieldRef: import("vue").Ref<any, any>;
|
|
31
36
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
32
37
|
"update:modelValue": (value: string | string[]) => any;
|
|
38
|
+
} & {
|
|
39
|
+
clear: () => any;
|
|
33
40
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
41
|
+
onClear?: (() => any) | undefined;
|
|
34
42
|
"onUpdate:modelValue"?: ((value: string | string[]) => any) | undefined;
|
|
35
43
|
}>, {
|
|
36
44
|
name: string;
|