@bagelink/vue 0.0.466 → 0.0.471
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/components/Accordion.vue.d.ts.map +1 -1
- package/dist/components/AccordionItem.vue.d.ts.map +1 -1
- package/dist/components/Alert.vue.d.ts +2 -2
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/Avatar.vue.d.ts.map +1 -1
- package/dist/components/Badge.vue.d.ts +2 -2
- package/dist/components/Badge.vue.d.ts.map +1 -1
- package/dist/components/BglVideo.vue.d.ts +2 -2
- package/dist/components/BglVideo.vue.d.ts.map +1 -1
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/Carousel.vue.d.ts +4 -2
- package/dist/components/Carousel.vue.d.ts.map +1 -1
- package/dist/components/DataPreview.vue.d.ts.map +1 -1
- package/dist/components/Drop.vue.d.ts +34 -0
- package/dist/components/Drop.vue.d.ts.map +1 -0
- package/dist/components/FileUploader.vue.d.ts +60 -0
- package/dist/components/FileUploader.vue.d.ts.map +1 -0
- package/dist/components/Flag.vue.d.ts +2 -2
- package/dist/components/Flag.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/ListView.vue.d.ts.map +1 -1
- package/dist/components/MapEmbed.vue.d.ts +2 -2
- package/dist/components/MapEmbed.vue.d.ts.map +1 -1
- package/dist/components/MaterialIcon.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts +2 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/ModalConfirm.vue.d.ts.map +1 -1
- package/dist/components/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/NavBar.vue.d.ts.map +1 -1
- package/dist/components/PageTitle.vue.d.ts.map +1 -1
- package/dist/components/TableSchema.vue.d.ts.map +1 -1
- package/dist/components/Title.vue.d.ts.map +1 -1
- package/dist/components/dashboard/Lineart.vue.d.ts.map +1 -1
- package/dist/components/dashboard/index.d.ts.map +1 -1
- package/dist/components/form/BglField.vue.d.ts.map +1 -1
- package/dist/components/form/BglForm.vue.d.ts +4 -2
- package/dist/components/form/BglForm.vue.d.ts.map +1 -1
- package/dist/components/form/ItemRef.vue.d.ts +1 -0
- package/dist/components/form/ItemRef.vue.d.ts.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/inputs/CheckInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Checkbox.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ColorPicker.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/FileUpload.vue.d.ts +2 -1
- package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/JSONInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioPillsInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectField.vue.d.ts +1 -4
- package/dist/components/form/inputs/SelectField.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SignaturePad.vue.d.ts +56 -0
- package/dist/components/form/inputs/SignaturePad.vue.d.ts.map +1 -0
- package/dist/components/form/inputs/TableField.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ToggleInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/index.d.ts +1 -0
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/BottomMenu.vue.d.ts.map +1 -1
- package/dist/components/layout/Layout.vue.d.ts +2 -2
- package/dist/components/layout/Layout.vue.d.ts.map +1 -1
- package/dist/components/layout/SidebarMenu.vue.d.ts.map +1 -1
- package/dist/components/layout/TabbedLayout.vue.d.ts.map +1 -1
- package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsBody.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/index.cjs +17616 -20818
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +17616 -20818
- package/dist/plugins/bagel.d.ts +1 -1
- package/dist/plugins/bagel.d.ts.map +1 -1
- package/dist/plugins/modal.d.ts +14 -14
- package/dist/plugins/modal.d.ts.map +1 -1
- package/dist/style.css +330 -309
- package/dist/types/BagelForm.d.ts +16 -16
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/BtnOptions.d.ts +1 -1
- package/dist/types/BtnOptions.d.ts.map +1 -1
- package/dist/types/NavLink.d.ts +2 -2
- package/dist/types/NavLink.d.ts.map +1 -1
- package/dist/types/file.d.ts +2 -2
- package/dist/types/file.d.ts.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/materialIcons.d.ts.map +1 -1
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/dist/utils/allCountries.d.ts +2 -2
- package/dist/utils/allCountries.d.ts.map +1 -1
- package/dist/utils/clickOutside.d.ts.map +1 -1
- package/dist/utils/index.d.ts +5 -5
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +19 -17
- package/src/components/Accordion.vue +10 -10
- package/src/components/AccordionItem.vue +47 -47
- package/src/components/Alert.vue +21 -21
- package/src/components/Avatar.vue +15 -15
- package/src/components/Badge.vue +17 -16
- package/src/components/BglVideo.vue +43 -43
- package/src/components/Btn.vue +64 -65
- package/src/components/Card.vue +26 -26
- package/src/components/Carousel.vue +130 -134
- package/src/components/DataPreview.vue +30 -30
- package/src/components/Flag.vue +26 -26
- package/src/components/ListItem.vue +13 -13
- package/src/components/ListView.vue +2 -2
- package/src/components/MapEmbed.vue +33 -33
- package/src/components/MaterialIcon.vue +12 -12
- package/src/components/Modal.vue +73 -66
- package/src/components/ModalConfirm.vue +18 -18
- package/src/components/ModalForm.vue +79 -76
- package/src/components/NavBar.vue +33 -30
- package/src/components/PageTitle.vue +10 -8
- package/src/components/TableSchema.vue +123 -113
- package/src/components/Title.vue +8 -8
- package/src/components/dashboard/Lineart.vue +103 -96
- package/src/components/dashboard/index.ts +1 -1
- package/src/components/form/BglField.vue +61 -61
- package/src/components/form/BglFieldSet.vue +9 -8
- package/src/components/form/BglForm.vue +71 -70
- package/src/components/form/index.ts +4 -4
- package/src/components/form/inputs/CheckInput.vue +25 -23
- package/src/components/form/inputs/Checkbox.vue +10 -10
- package/src/components/form/inputs/ColorPicker.vue +26 -24
- package/src/components/form/inputs/DateInput.vue +39 -39
- package/src/components/form/inputs/DatePicker.vue +37 -36
- package/src/components/form/inputs/FileUpload.vue +165 -161
- package/src/components/form/inputs/FileUploadURL.vue +134 -132
- package/src/components/form/inputs/JSONInput.vue +29 -29
- package/src/components/form/inputs/RadioPillsInput.vue +49 -49
- package/src/components/form/inputs/RichText.vue +62 -63
- package/src/components/form/inputs/SelectInput.vue +173 -167
- package/src/components/form/inputs/SignaturePad.vue +150 -0
- package/src/components/form/inputs/TableField.vue +77 -76
- package/src/components/form/inputs/TelInput.vue +280 -285
- package/src/components/form/inputs/TextInput.vue +81 -76
- package/src/components/form/inputs/ToggleInput.vue +13 -13
- package/src/components/form/inputs/index.ts +17 -15
- package/src/components/formkit/FileUploader.vue +162 -158
- package/src/components/formkit/MiscFields.vue +20 -20
- package/src/components/formkit/Toggle.vue +13 -13
- package/src/components/index.ts +28 -28
- package/src/components/layout/BottomMenu.vue +25 -21
- package/src/components/layout/Layout.vue +29 -29
- package/src/components/layout/SidebarMenu.vue +31 -27
- package/src/components/layout/TabbedLayout.vue +47 -45
- package/src/components/layout/Tabs.vue +32 -32
- package/src/components/layout/TabsBody.vue +10 -10
- package/src/components/layout/TabsNav.vue +44 -44
- package/src/components/layout/index.ts +7 -7
- package/src/components/layout/tabsManager.ts +6 -6
- package/src/index.ts +13 -8
- package/src/plugins/bagel.ts +37 -37
- package/src/plugins/modal.ts +66 -67
- package/src/styles/layout.css +5 -3
- package/src/styles/mobilLayout.css +7 -0
- package/src/types/BagelForm.ts +27 -27
- package/src/types/BtnOptions.ts +13 -13
- package/src/types/NavLink.ts +8 -8
- package/src/types/file.ts +11 -11
- package/src/types/index.ts +16 -12
- package/src/types/materialIcons.ts +3249 -3249
- package/src/utils/BagelFormUtils.ts +23 -23
- package/src/utils/allCountries.ts +11 -12
- package/src/utils/clickOutside.ts +6 -6
- package/src/utils/index.ts +39 -41
- package/src/utils/lang.ts +13 -13
- package/src/utils/strings.ts +9 -9
- package/tsconfig.json +0 -1
- package/vite.config.ts +8 -8
- package/dist/components/Popover.vue.d.ts +0 -10
- package/dist/components/Popover.vue.d.ts.map +0 -1
- package/dist/types/materialIcon.d.ts +0 -2
- package/dist/types/materialIcon.d.ts.map +0 -1
|
@@ -1,183 +1,20 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="bagel-input"
|
|
4
|
-
:class="{ disabled: disabled }"
|
|
5
|
-
>
|
|
6
|
-
<label>
|
|
7
|
-
{{ label }}
|
|
8
|
-
<div
|
|
9
|
-
dir="ltr"
|
|
10
|
-
class="flex gap-05 tel-input"
|
|
11
|
-
tabindex="-1"
|
|
12
|
-
aria-label="Country Code Selector"
|
|
13
|
-
aria-haspopup="listbox"
|
|
14
|
-
:aria-expanded="open"
|
|
15
|
-
@keydown.esc="reset"
|
|
16
|
-
@keydown.tab="reset"
|
|
17
|
-
>
|
|
18
|
-
<Dropdown
|
|
19
|
-
:placement="'bottom'"
|
|
20
|
-
@hide="open = false"
|
|
21
|
-
v-if="!computedDropDownOptions.hide"
|
|
22
|
-
:disabled="computedDropDownOptions.disabled"
|
|
23
|
-
>
|
|
24
|
-
<span
|
|
25
|
-
class="flex gap-05"
|
|
26
|
-
@click="open = true"
|
|
27
|
-
>
|
|
28
|
-
<Icon :icon="open ? 'collapse_all' : 'expand_all'" />
|
|
29
|
-
<Flag
|
|
30
|
-
v-if="computedDropDownOptions.showFlags && activeCountryCode"
|
|
31
|
-
:country="activeCountryCode"
|
|
32
|
-
/>
|
|
33
|
-
</span>
|
|
34
|
-
<template #popper="{ hide }">
|
|
35
|
-
<div class="p-075 tel-countryp-dropdown">
|
|
36
|
-
<TextInput
|
|
37
|
-
v-if="searchable"
|
|
38
|
-
aria-label="Search by country name or country code"
|
|
39
|
-
placeholder="Search"
|
|
40
|
-
icon="search"
|
|
41
|
-
v-model="searchQuery"
|
|
42
|
-
/>
|
|
43
|
-
|
|
44
|
-
<ul
|
|
45
|
-
ref="listEl"
|
|
46
|
-
class="overflow-y p-0"
|
|
47
|
-
:style="{ 'max-height': '400px' }"
|
|
48
|
-
:class="dropdownOpenDirection"
|
|
49
|
-
role="listbox"
|
|
50
|
-
>
|
|
51
|
-
<li
|
|
52
|
-
v-for="(pb, index) in sortedCountries"
|
|
53
|
-
role="option"
|
|
54
|
-
class="flex gap-075"
|
|
55
|
-
:key="pb.iso2 + isPreferred(pb)"
|
|
56
|
-
tabindex="-1"
|
|
57
|
-
@click="
|
|
58
|
-
chooseCountry(pb.iso2);
|
|
59
|
-
hide();
|
|
60
|
-
"
|
|
61
|
-
@mousemove="selectedIndex = index"
|
|
62
|
-
:aria-selected="activeCountryCode === pb.iso2 && !isPreferred(pb)
|
|
63
|
-
"
|
|
64
|
-
>
|
|
65
|
-
<Flag
|
|
66
|
-
v-if="computedDropDownOptions.showFlags"
|
|
67
|
-
:country="pb.iso2"
|
|
68
|
-
/>
|
|
69
|
-
<p class="tel-country">{{ pb.name }}</p>
|
|
70
|
-
<span v-if="computedDropDownOptions.showDialCodeInList">
|
|
71
|
-
+{{ pb.dialCode }}
|
|
72
|
-
</span>
|
|
73
|
-
</li>
|
|
74
|
-
</ul>
|
|
75
|
-
</div>
|
|
76
|
-
</template>
|
|
77
|
-
</Dropdown>
|
|
78
|
-
|
|
79
|
-
<input
|
|
80
|
-
:required="required"
|
|
81
|
-
:placeholder="computedInputOptions.placeholder"
|
|
82
|
-
:disabled="disabled"
|
|
83
|
-
v-model="phone"
|
|
84
|
-
type="tel"
|
|
85
|
-
ref="refInput"
|
|
86
|
-
:autocomplete="autocomplete"
|
|
87
|
-
:class="['vti__input']"
|
|
88
|
-
:id="id"
|
|
89
|
-
:pattern="computedInputOptions.pattern"
|
|
90
|
-
:minlength="computedInputOptions.minlength"
|
|
91
|
-
:maxlength="computedInputOptions.maxlength"
|
|
92
|
-
:name="computedInputOptions.name"
|
|
93
|
-
:readonly="computedInputOptions.readonly"
|
|
94
|
-
:tabindex="computedInputOptions.tabindex"
|
|
95
|
-
:aria-describedby="computedInputOptions['aria-describedby']"
|
|
96
|
-
@blur="onBlur"
|
|
97
|
-
@focus="onFocus"
|
|
98
|
-
@keyup.enter="onEnter"
|
|
99
|
-
@keyup.space="onSpace"
|
|
100
|
-
@keydown="handleInput"
|
|
101
|
-
:style="computedInputOptions.style"
|
|
102
|
-
/>
|
|
103
|
-
</div>
|
|
104
|
-
</label>
|
|
105
|
-
</div>
|
|
106
|
-
</template>
|
|
107
|
-
|
|
108
1
|
<script lang="ts" setup>
|
|
109
2
|
import {
|
|
110
|
-
|
|
3
|
+
type Country,
|
|
111
4
|
Dropdown,
|
|
112
|
-
TextInput,
|
|
113
5
|
Flag,
|
|
6
|
+
Icon,
|
|
7
|
+
TextInput,
|
|
114
8
|
allCountries,
|
|
115
|
-
type Country,
|
|
116
9
|
debounce,
|
|
117
|
-
} from '@bagelink/vue'
|
|
118
|
-
import { type Raw, type StyleValue, onMounted, watch } from 'vue'
|
|
10
|
+
} from '@bagelink/vue'
|
|
11
|
+
import { type Raw, type StyleValue, onMounted, watch } from 'vue'
|
|
119
12
|
import {
|
|
120
|
-
parsePhoneNumberFromString,
|
|
121
|
-
type NumberFormat,
|
|
122
13
|
type CountryCode,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const apiData = sessionStorage?.getItem('ipapi');
|
|
128
|
-
if (apiData) return JSON.parse(apiData);
|
|
129
|
-
const { data } = await axios.get('https://ipapi.co/json/');
|
|
130
|
-
sessionStorage.setItem('ipapi', JSON.stringify(data));
|
|
131
|
-
return data;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const defaultDropdownOptions = {
|
|
135
|
-
hide: false,
|
|
136
|
-
disabled: false,
|
|
137
|
-
showFlags: true,
|
|
138
|
-
showDialCodeInSelection: true,
|
|
139
|
-
showDialCodeInList: true,
|
|
140
|
-
searchable: true,
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const defaultInputOptions = {
|
|
144
|
-
showDialCode: true,
|
|
145
|
-
autofocus: false,
|
|
146
|
-
'aria-describedby': '',
|
|
147
|
-
id: '',
|
|
148
|
-
maxlength: 25,
|
|
149
|
-
minlength: 9,
|
|
150
|
-
pattern: '',
|
|
151
|
-
name: '',
|
|
152
|
-
readonly: false,
|
|
153
|
-
tabindex: 0,
|
|
154
|
-
style: '' as Raw<StyleValue>,
|
|
155
|
-
placeholder: 'Enter a phone number',
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
type Props = {
|
|
159
|
-
label?: string;
|
|
160
|
-
id?: string;
|
|
161
|
-
autocomplete?: 'on' | 'off' | 'tel';
|
|
162
|
-
placeholder?: string;
|
|
163
|
-
required?: boolean;
|
|
164
|
-
modelValue: string;
|
|
165
|
-
allCountries?: Country[];
|
|
166
|
-
autoFormat?: boolean;
|
|
167
|
-
customValidate?: boolean | RegExp;
|
|
168
|
-
defaultCountry?: string | number;
|
|
169
|
-
disabled?: boolean;
|
|
170
|
-
searchable?: boolean;
|
|
171
|
-
autoDefaultCountry?: boolean;
|
|
172
|
-
inputOptions?: Partial<typeof defaultInputOptions>;
|
|
173
|
-
dropdownOptions?: Partial<typeof defaultDropdownOptions>;
|
|
174
|
-
excludeCountries?: string[];
|
|
175
|
-
mode?: NumberFormat;
|
|
176
|
-
onlyCountries?: string[];
|
|
177
|
-
preferredCountries?: string[];
|
|
178
|
-
parseArg?: { extract?: boolean };
|
|
179
|
-
debounceDelay?: number;
|
|
180
|
-
};
|
|
14
|
+
type NumberFormat,
|
|
15
|
+
parsePhoneNumberFromString,
|
|
16
|
+
} from 'libphonenumber-js'
|
|
17
|
+
import axios from 'axios'
|
|
181
18
|
|
|
182
19
|
const props = withDefaults(defineProps<Props>(), {
|
|
183
20
|
modelValue: '',
|
|
@@ -197,25 +34,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
197
34
|
preferredCountries: () => [],
|
|
198
35
|
showDropdown: true,
|
|
199
36
|
debounceDelay: 300,
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
const computedDropDownOptions = $computed(() => ({
|
|
203
|
-
...defaultDropdownOptions,
|
|
204
|
-
...props.dropdownOptions,
|
|
205
|
-
}));
|
|
206
|
-
|
|
207
|
-
const computedInputOptions = $computed(() => ({
|
|
208
|
-
...defaultInputOptions,
|
|
209
|
-
...props.inputOptions,
|
|
210
|
-
}));
|
|
211
|
-
|
|
212
|
-
let activeCountryCode = $ref<CountryCode>();
|
|
213
|
-
let open = $ref(false);
|
|
214
|
-
let selectedIndex = $ref<number>();
|
|
215
|
-
const dropdownOpenDirection = $ref('below');
|
|
216
|
-
const searchQuery = $ref('');
|
|
217
|
-
|
|
218
|
-
const refInput = $ref<HTMLInputElement | null>(null);
|
|
37
|
+
})
|
|
219
38
|
|
|
220
39
|
const emit = defineEmits([
|
|
221
40
|
'update:modelValue',
|
|
@@ -227,78 +46,147 @@ const emit = defineEmits([
|
|
|
227
46
|
'enter',
|
|
228
47
|
'space',
|
|
229
48
|
'debounce',
|
|
230
|
-
])
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
async function getIp() {
|
|
52
|
+
const apiData = sessionStorage.getItem('ipapi')
|
|
53
|
+
if (apiData) return JSON.parse(apiData)
|
|
54
|
+
const { data } = await axios.get('https://ipapi.co/json/')
|
|
55
|
+
sessionStorage.setItem('ipapi', JSON.stringify(data))
|
|
56
|
+
return data
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const defaultDropdownOptions = {
|
|
60
|
+
hide: false,
|
|
61
|
+
disabled: false,
|
|
62
|
+
showFlags: true,
|
|
63
|
+
showDialCodeInSelection: true,
|
|
64
|
+
showDialCodeInList: true,
|
|
65
|
+
searchable: true,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const defaultInputOptions = {
|
|
69
|
+
'showDialCode': true,
|
|
70
|
+
'autofocus': false,
|
|
71
|
+
'aria-describedby': '',
|
|
72
|
+
'id': '',
|
|
73
|
+
'maxlength': 25,
|
|
74
|
+
'minlength': 9,
|
|
75
|
+
'pattern': '',
|
|
76
|
+
'name': '',
|
|
77
|
+
'readonly': false,
|
|
78
|
+
'tabindex': 0,
|
|
79
|
+
'style': '' as Raw<StyleValue>,
|
|
80
|
+
'placeholder': 'Enter a phone number',
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface Props {
|
|
84
|
+
label?: string
|
|
85
|
+
id?: string
|
|
86
|
+
autocomplete?: 'on' | 'off' | 'tel'
|
|
87
|
+
placeholder?: string
|
|
88
|
+
required?: boolean
|
|
89
|
+
modelValue: string
|
|
90
|
+
allCountries?: Country[]
|
|
91
|
+
autoFormat?: boolean
|
|
92
|
+
customValidate?: boolean | RegExp
|
|
93
|
+
defaultCountry?: string | number
|
|
94
|
+
disabled?: boolean
|
|
95
|
+
searchable?: boolean
|
|
96
|
+
autoDefaultCountry?: boolean
|
|
97
|
+
inputOptions?: Partial<typeof defaultInputOptions>
|
|
98
|
+
dropdownOptions?: Partial<typeof defaultDropdownOptions>
|
|
99
|
+
excludeCountries?: string[]
|
|
100
|
+
mode?: NumberFormat
|
|
101
|
+
onlyCountries?: string[]
|
|
102
|
+
preferredCountries?: string[]
|
|
103
|
+
parseArg?: { extract?: boolean }
|
|
104
|
+
debounceDelay?: number
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const computedDropDownOptions = $computed(() => ({
|
|
108
|
+
...defaultDropdownOptions,
|
|
109
|
+
...props.dropdownOptions,
|
|
110
|
+
}))
|
|
111
|
+
|
|
112
|
+
const computedInputOptions = $computed(() => ({
|
|
113
|
+
...defaultInputOptions,
|
|
114
|
+
...props.inputOptions,
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
let activeCountryCode = $ref<CountryCode>()
|
|
118
|
+
let open = $ref(false)
|
|
119
|
+
let selectedIndex = $ref<number>()
|
|
120
|
+
const dropdownOpenDirection = $ref('below')
|
|
121
|
+
const searchQuery = $ref('')
|
|
122
|
+
|
|
123
|
+
const refInput = $ref<HTMLInputElement | null>(null)
|
|
231
124
|
|
|
232
125
|
// Computed: activeCountry
|
|
233
|
-
const activeCountry = $computed(() =>
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
);
|
|
126
|
+
const activeCountry = $computed(() => props.allCountries.find(
|
|
127
|
+
country => country.iso2 === activeCountryCode?.toUpperCase(),
|
|
128
|
+
),
|
|
129
|
+
)
|
|
238
130
|
|
|
239
|
-
const isPreferred = (country?: Country) =>
|
|
240
|
-
props.preferredCountries.includes(country?.iso2 as CountryCode);
|
|
131
|
+
const isPreferred = (country?: Country) => props.preferredCountries.includes(country?.iso2 as CountryCode)
|
|
241
132
|
|
|
242
133
|
const filteredCountries = $computed(() => {
|
|
243
134
|
if (props.onlyCountries.length) {
|
|
244
|
-
return props.allCountries.filter(({ iso2 }) =>
|
|
245
|
-
|
|
246
|
-
);
|
|
135
|
+
return props.allCountries.filter(({ iso2 }) => props.onlyCountries.some(c => c.toUpperCase() === iso2),
|
|
136
|
+
)
|
|
247
137
|
}
|
|
248
138
|
if (props.excludeCountries.length) {
|
|
249
139
|
return props.allCountries.filter(
|
|
250
|
-
({ iso2 }) =>
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
);
|
|
140
|
+
({ iso2 }) => !props.excludeCountries.includes(iso2.toUpperCase())
|
|
141
|
+
&& !props.excludeCountries.includes(iso2.toLowerCase()),
|
|
142
|
+
)
|
|
254
143
|
}
|
|
255
|
-
return props.allCountries
|
|
256
|
-
})
|
|
144
|
+
return props.allCountries
|
|
145
|
+
})
|
|
257
146
|
|
|
258
147
|
const sortedCountries = $computed(() => {
|
|
259
|
-
const preferredCountries = getCountries(props.preferredCountries)
|
|
148
|
+
const preferredCountries = getCountries(props.preferredCountries)
|
|
260
149
|
|
|
261
|
-
const countriesList = [...preferredCountries, ...filteredCountries]
|
|
150
|
+
const countriesList = [...preferredCountries, ...filteredCountries]
|
|
262
151
|
const cleanInput = searchQuery.replace(
|
|
263
|
-
/[
|
|
152
|
+
/[~`!#$%&*()+={};:'"<>.,/?-_]/g,
|
|
264
153
|
'',
|
|
265
|
-
)
|
|
154
|
+
)
|
|
266
155
|
return countriesList
|
|
267
156
|
.filter(
|
|
268
|
-
(c)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
new RegExp(cleanInput, 'i').test(c?.dialCode || ''),
|
|
157
|
+
c => new RegExp(cleanInput, 'i').test(c.name || '')
|
|
158
|
+
|| new RegExp(cleanInput, 'i').test(c.iso2 || '')
|
|
159
|
+
|| new RegExp(cleanInput, 'i').test(c.dialCode || ''),
|
|
272
160
|
)
|
|
273
|
-
.filter(Boolean)
|
|
274
|
-
})
|
|
161
|
+
.filter(Boolean)
|
|
162
|
+
})
|
|
275
163
|
|
|
276
164
|
const parseArgs = $computed(() => ({
|
|
277
|
-
...props
|
|
165
|
+
...props.parseArg,
|
|
278
166
|
defaultCountry: activeCountryCode,
|
|
279
|
-
}))
|
|
167
|
+
}))
|
|
280
168
|
|
|
281
169
|
const phone = defineModel<string>('modelValue', {
|
|
282
170
|
default: '',
|
|
283
171
|
set: (value) => {
|
|
284
|
-
const formatted = formatPhone(`${value}`)
|
|
285
|
-
if (!formatted) return ''
|
|
172
|
+
const formatted = formatPhone(`${value}`)
|
|
173
|
+
if (!formatted) return ''
|
|
286
174
|
|
|
287
|
-
emit('update:modelValue', formatted)
|
|
288
|
-
debounce(() => emit('debounce', formatted), props.debounceDelay)
|
|
289
|
-
return formatted
|
|
175
|
+
emit('update:modelValue', formatted)
|
|
176
|
+
debounce(() => { emit('debounce', formatted) }, props.debounceDelay)
|
|
177
|
+
return formatted
|
|
290
178
|
},
|
|
291
|
-
})
|
|
179
|
+
})
|
|
292
180
|
|
|
293
181
|
function formatPhone(val: string): string {
|
|
294
|
-
const phoneNumber = parsePhoneNumberFromString(val, parseArgs)
|
|
182
|
+
const phoneNumber = parsePhoneNumberFromString(val, parseArgs)
|
|
295
183
|
if (!phoneNumber) {
|
|
296
|
-
const dialCode
|
|
297
|
-
sortedCountries.find(
|
|
298
|
-
if (props.mode === 'INTERNATIONAL') return `+${dialCode}
|
|
299
|
-
return dialCode
|
|
184
|
+
const dialCode
|
|
185
|
+
= sortedCountries.find(c => c.iso2 === activeCountryCode)?.dialCode || ''
|
|
186
|
+
if (props.mode === 'INTERNATIONAL') return `+${dialCode}`
|
|
187
|
+
return dialCode
|
|
300
188
|
}
|
|
301
|
-
return phoneNumber.format(props.mode)
|
|
189
|
+
return phoneNumber.format(props.mode).replaceAll(' ', '') || ''
|
|
302
190
|
}
|
|
303
191
|
|
|
304
192
|
// Watchers
|
|
@@ -306,109 +194,216 @@ watch(
|
|
|
306
194
|
() => activeCountry,
|
|
307
195
|
(value, oldValue) => {
|
|
308
196
|
if (!value && oldValue?.iso2) {
|
|
309
|
-
activeCountryCode = oldValue.iso2
|
|
310
|
-
return
|
|
197
|
+
activeCountryCode = oldValue.iso2
|
|
198
|
+
return
|
|
311
199
|
}
|
|
312
|
-
if (value?.iso2) emit('country-changed', value)
|
|
200
|
+
if (value?.iso2) emit('country-changed', value)
|
|
313
201
|
},
|
|
314
|
-
)
|
|
202
|
+
)
|
|
315
203
|
|
|
316
|
-
|
|
204
|
+
async function initializeCountry() {
|
|
317
205
|
// if (phone?.[0] === '+') return;
|
|
318
206
|
// 2. Use default country if passed from parent
|
|
319
207
|
if (props.defaultCountry) {
|
|
320
208
|
if (typeof props.defaultCountry === 'string') {
|
|
321
|
-
chooseCountry(props.defaultCountry)
|
|
322
|
-
return
|
|
209
|
+
chooseCountry(props.defaultCountry)
|
|
210
|
+
return
|
|
323
211
|
}
|
|
324
212
|
if (typeof props.defaultCountry === 'number') {
|
|
325
|
-
const country = findCountryByDialCode(props.defaultCountry)
|
|
213
|
+
const country = findCountryByDialCode(props.defaultCountry)
|
|
326
214
|
if (country) {
|
|
327
|
-
chooseCountry(country.iso2)
|
|
328
|
-
return
|
|
215
|
+
chooseCountry(country.iso2)
|
|
216
|
+
return
|
|
329
217
|
}
|
|
330
218
|
}
|
|
331
219
|
}
|
|
332
220
|
|
|
333
|
-
const fallbackCountry = sortedCountries[0]
|
|
221
|
+
const fallbackCountry = sortedCountries[0]
|
|
334
222
|
|
|
335
223
|
if (props.autoDefaultCountry) {
|
|
336
224
|
try {
|
|
337
|
-
const res = (await getIp()).country
|
|
338
|
-
chooseCountry(res || activeCountryCode)
|
|
225
|
+
const res = (await getIp()).country
|
|
226
|
+
chooseCountry(res || activeCountryCode)
|
|
339
227
|
} catch (error) {
|
|
340
|
-
console.warn(error)
|
|
341
|
-
chooseCountry(fallbackCountry
|
|
228
|
+
console.warn(error)
|
|
229
|
+
chooseCountry(fallbackCountry.iso2)
|
|
342
230
|
}
|
|
343
231
|
} else {
|
|
344
232
|
// 4. Use the first country from preferred list (if available) or all countries list
|
|
345
|
-
chooseCountry(fallbackCountry
|
|
233
|
+
chooseCountry(fallbackCountry.iso2)
|
|
346
234
|
}
|
|
347
|
-
}
|
|
235
|
+
}
|
|
348
236
|
|
|
349
|
-
onMounted(initializeCountry)
|
|
237
|
+
onMounted(initializeCountry)
|
|
350
238
|
|
|
351
|
-
const findCountry = (iso: string) =>
|
|
352
|
-
filteredCountries.find((country) => country.iso2 === iso?.toUpperCase());
|
|
239
|
+
const findCountry = (iso: string) => filteredCountries.find(country => country.iso2 === iso.toUpperCase())
|
|
353
240
|
|
|
354
|
-
|
|
355
|
-
const countryList: Country[] = []
|
|
241
|
+
function getCountries(list: string[]): Country[] {
|
|
242
|
+
const countryList: Country[] = []
|
|
356
243
|
list.forEach((countryCode) => {
|
|
357
|
-
const country = findCountry(countryCode)
|
|
358
|
-
if (country) countryList.push(country)
|
|
359
|
-
})
|
|
360
|
-
return countryList
|
|
361
|
-
}
|
|
244
|
+
const country = findCountry(countryCode)
|
|
245
|
+
if (country) countryList.push(country)
|
|
246
|
+
})
|
|
247
|
+
return countryList
|
|
248
|
+
}
|
|
362
249
|
|
|
363
|
-
|
|
364
|
-
filteredCountries.find(
|
|
250
|
+
function findCountryByDialCode(dialCode: number) {
|
|
251
|
+
return filteredCountries.find(country => Number(country.dialCode) === dialCode)
|
|
252
|
+
}
|
|
365
253
|
|
|
366
|
-
|
|
367
|
-
if (!country) return
|
|
368
|
-
const parsedCountry = findCountry(country)
|
|
369
|
-
if (!parsedCountry) return
|
|
370
|
-
activeCountryCode = parsedCountry.iso2
|
|
254
|
+
function chooseCountry(country?: string) {
|
|
255
|
+
if (!country) return
|
|
256
|
+
const parsedCountry = findCountry(country)
|
|
257
|
+
if (!parsedCountry) return
|
|
258
|
+
activeCountryCode = parsedCountry.iso2
|
|
371
259
|
|
|
372
260
|
if (props.inputOptions?.showDialCode && parsedCountry) {
|
|
373
|
-
phone.value = `+ ${parsedCountry.dialCode}
|
|
374
|
-
activeCountryCode = parsedCountry.iso2 || ''
|
|
375
|
-
return
|
|
261
|
+
phone.value = `+ ${parsedCountry.dialCode}`
|
|
262
|
+
activeCountryCode = parsedCountry.iso2 || ''
|
|
263
|
+
return
|
|
376
264
|
}
|
|
377
265
|
|
|
378
|
-
activeCountryCode = parsedCountry.iso2 || ''
|
|
266
|
+
activeCountryCode = parsedCountry.iso2 || ''
|
|
379
267
|
// emitInput(phone);
|
|
380
|
-
}
|
|
268
|
+
}
|
|
381
269
|
|
|
382
|
-
|
|
270
|
+
function onBlur() { emit('blur') }
|
|
383
271
|
|
|
384
|
-
|
|
272
|
+
function onFocus() { emit('focus') }
|
|
385
273
|
|
|
386
|
-
|
|
274
|
+
function onEnter(e: KeyboardEvent) { emit('enter') }
|
|
387
275
|
|
|
388
|
-
|
|
276
|
+
function onSpace() { emit('space') }
|
|
389
277
|
|
|
390
278
|
// const focus = () => refInput?.focus();
|
|
391
279
|
|
|
392
280
|
// Method: reset
|
|
393
|
-
|
|
281
|
+
function reset() {
|
|
394
282
|
selectedIndex = sortedCountries.findIndex(
|
|
395
|
-
|
|
396
|
-
)
|
|
397
|
-
open = false
|
|
398
|
-
}
|
|
283
|
+
c => c.iso2 === activeCountryCode,
|
|
284
|
+
)
|
|
285
|
+
open = false
|
|
286
|
+
}
|
|
399
287
|
|
|
400
288
|
function handleInput(e: KeyboardEvent) {
|
|
401
|
-
const keyVal = e.key
|
|
402
|
-
if (keyVal
|
|
289
|
+
const keyVal = e.key
|
|
290
|
+
if (keyVal.length > 1 || e.metaKey) return
|
|
403
291
|
|
|
404
|
-
const hasBadChars = RegExp(/[^+\d]/).test(keyVal)
|
|
292
|
+
const hasBadChars = RegExp(/[^+\d]/).test(keyVal)
|
|
405
293
|
|
|
406
|
-
if (!hasBadChars) return
|
|
294
|
+
if (!hasBadChars) return
|
|
407
295
|
|
|
408
|
-
e.preventDefault()
|
|
296
|
+
e.preventDefault()
|
|
409
297
|
}
|
|
410
298
|
</script>
|
|
411
299
|
|
|
300
|
+
<template>
|
|
301
|
+
<div
|
|
302
|
+
class="bagel-input"
|
|
303
|
+
:class="{ disabled }"
|
|
304
|
+
>
|
|
305
|
+
<label>
|
|
306
|
+
{{ label }}
|
|
307
|
+
<div
|
|
308
|
+
dir="ltr"
|
|
309
|
+
class="flex gap-05 tel-input"
|
|
310
|
+
tabindex="-1"
|
|
311
|
+
aria-label="Country Code Selector"
|
|
312
|
+
aria-haspopup="listbox"
|
|
313
|
+
:aria-expanded="open"
|
|
314
|
+
@keydown.esc="reset"
|
|
315
|
+
@keydown.tab="reset"
|
|
316
|
+
>
|
|
317
|
+
<Dropdown
|
|
318
|
+
v-if="!computedDropDownOptions.hide"
|
|
319
|
+
placement="bottom"
|
|
320
|
+
:disabled="computedDropDownOptions.disabled"
|
|
321
|
+
@hide="open = false"
|
|
322
|
+
>
|
|
323
|
+
<span
|
|
324
|
+
class="flex gap-05"
|
|
325
|
+
@click="open = true"
|
|
326
|
+
>
|
|
327
|
+
<Icon :icon="open ? 'collapse_all' : 'expand_all'" />
|
|
328
|
+
<Flag
|
|
329
|
+
v-if="computedDropDownOptions.showFlags && activeCountryCode"
|
|
330
|
+
:country="activeCountryCode"
|
|
331
|
+
/>
|
|
332
|
+
</span>
|
|
333
|
+
<template #popper="{ hide }">
|
|
334
|
+
<div class="p-075 tel-countryp-dropdown">
|
|
335
|
+
<TextInput
|
|
336
|
+
v-if="searchable"
|
|
337
|
+
v-model="searchQuery"
|
|
338
|
+
aria-label="Search by country name or country code"
|
|
339
|
+
placeholder="Search"
|
|
340
|
+
icon="search"
|
|
341
|
+
/>
|
|
342
|
+
|
|
343
|
+
<ul
|
|
344
|
+
ref="listEl"
|
|
345
|
+
class="overflow-y p-0"
|
|
346
|
+
:style="{ 'max-height': '400px' }"
|
|
347
|
+
:class="dropdownOpenDirection"
|
|
348
|
+
role="listbox"
|
|
349
|
+
>
|
|
350
|
+
<li
|
|
351
|
+
v-for="(pb, index) in sortedCountries"
|
|
352
|
+
:key="pb.iso2 + isPreferred(pb)"
|
|
353
|
+
role="option"
|
|
354
|
+
class="flex gap-075"
|
|
355
|
+
tabindex="-1"
|
|
356
|
+
:aria-selected="activeCountryCode === pb.iso2 && !isPreferred(pb)
|
|
357
|
+
"
|
|
358
|
+
@click="
|
|
359
|
+
chooseCountry(pb.iso2);
|
|
360
|
+
hide();
|
|
361
|
+
"
|
|
362
|
+
@mousemove="selectedIndex = index"
|
|
363
|
+
>
|
|
364
|
+
<Flag
|
|
365
|
+
v-if="computedDropDownOptions.showFlags"
|
|
366
|
+
:country="pb.iso2"
|
|
367
|
+
/>
|
|
368
|
+
<p class="tel-country">{{ pb.name }}</p>
|
|
369
|
+
<span v-if="computedDropDownOptions.showDialCodeInList">
|
|
370
|
+
+{{ pb.dialCode }}
|
|
371
|
+
</span>
|
|
372
|
+
</li>
|
|
373
|
+
</ul>
|
|
374
|
+
</div>
|
|
375
|
+
</template>
|
|
376
|
+
</Dropdown>
|
|
377
|
+
|
|
378
|
+
<input
|
|
379
|
+
:id="id"
|
|
380
|
+
ref="refInput"
|
|
381
|
+
v-model="phone"
|
|
382
|
+
:required="required"
|
|
383
|
+
:placeholder="computedInputOptions.placeholder"
|
|
384
|
+
:disabled="disabled"
|
|
385
|
+
type="tel"
|
|
386
|
+
:autocomplete="autocomplete"
|
|
387
|
+
class="vti__input"
|
|
388
|
+
:pattern="computedInputOptions.pattern"
|
|
389
|
+
:minlength="computedInputOptions.minlength"
|
|
390
|
+
:maxlength="computedInputOptions.maxlength"
|
|
391
|
+
:name="computedInputOptions.name"
|
|
392
|
+
:readonly="computedInputOptions.readonly"
|
|
393
|
+
:tabindex="computedInputOptions.tabindex"
|
|
394
|
+
:aria-describedby="computedInputOptions['aria-describedby']"
|
|
395
|
+
:style="computedInputOptions.style"
|
|
396
|
+
@blur="onBlur"
|
|
397
|
+
@focus="onFocus"
|
|
398
|
+
@keyup.enter="onEnter"
|
|
399
|
+
@keyup.space="onSpace"
|
|
400
|
+
@keydown="handleInput"
|
|
401
|
+
>
|
|
402
|
+
</div>
|
|
403
|
+
</label>
|
|
404
|
+
</div>
|
|
405
|
+
</template>
|
|
406
|
+
|
|
412
407
|
<style scoped>
|
|
413
408
|
.tel-input {
|
|
414
409
|
direction: ltr;
|