@ozdao/prometheus-framework 0.2.35 → 0.2.36
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/main.css +1 -1
- package/dist/prometheus-framework.cjs.js +18 -19
- package/dist/prometheus-framework.es.js +803 -1028
- package/package.json +1 -1
- package/src/components/Button/Button.vue +26 -19
- package/src/components/FieldBig/FieldBig.vue +1 -1
- package/src/components/FieldPhone/FieldPhone.vue +55 -304
- package/src/components/FieldPhone/FieldPhone2.vue +671 -0
- package/src/components/FieldPhone/click-outside.js +16 -11
- package/src/components/Footer/Footer.vue +4 -4
- package/src/components/Header/Header.vue +2 -1
- package/src/components/Select/Select.vue +2 -1
- package/src/modules/chats/components/sections/ChatWindow.vue +3 -1
- package/src/modules/chats/store/chat.store.js +1 -3
- package/src/modules/events/components/pages/EditEvent.vue +2 -1
- package/src/modules/events/components/pages/Event.vue +8 -2
- package/src/modules/events/components/sections/SectionMainGuest.vue +1 -1
- package/src/modules/events/components/sections/SectionSpecialGuests.vue +0 -1
- package/src/modules/events/store/events.js +23 -3
- package/src/modules/globals/mixins/mixins.js +33 -2
- package/src/modules/globals/store/globals.js +1 -0
- package/src/modules/landing/components/sections/HeroGovernance.vue +156 -0
- package/src/modules/marketplace/components/sections/Filters.vue +3 -0
- package/src/modules/marketplace/router/marketplace.js +1 -0
- package/src/modules/orders/components/blocks/StatusHistory.vue +8 -3
- package/src/modules/orders/components/pages/EditOrder.vue +9 -11
- package/src/modules/orders/components/pages/FormOrder.vue +58 -46
- package/src/modules/orders/components/pages/Order.vue +63 -42
- package/src/modules/orders/components/sections/FormOrderDetails.vue +50 -18
- package/src/modules/orders/controllers/orders.controller.js +10 -1
- package/src/modules/orders/models/order.model.js +1 -0
- package/src/modules/orders/store/orders.js +21 -27
- package/src/modules/orders/store/shopcart.js +14 -2
- package/src/modules/organizations/components/sections/DetailsTab.vue +2 -2
- package/src/modules/products/components/sections/FilterProducts.vue +3 -4
- package/src/modules/products/components/sections/HeroRecommendation.vue +29 -2
- package/src/modules/products/components/sections/SectionProduct.vue +14 -9
- package/src/modules/products/controllers/products.controller.js +26 -17
- package/src/modules/products/routes/products.routes.js +6 -6
- package/src/modules/products/store/products.js +10 -10
- package/src/styles/config.scss +10 -3
@@ -0,0 +1,671 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
:class="[
|
4
|
+
rootClass,
|
5
|
+
classes,
|
6
|
+
$attrs.class,
|
7
|
+
{ 'bg-fourth-nano': validation },
|
8
|
+
{ disabled: disabled }
|
9
|
+
]"
|
10
|
+
:style="[
|
11
|
+
rootStyle,
|
12
|
+
styles
|
13
|
+
]"
|
14
|
+
ref="rootElement"
|
15
|
+
class="vue3-reactive-tel-input"
|
16
|
+
>
|
17
|
+
<div
|
18
|
+
v-click-outside="clickedOutside"
|
19
|
+
:class="['vti__dropdown', { open: open }]"
|
20
|
+
:style="[dropdownStyle]"
|
21
|
+
:tabindex="dropdownOptions.tabindex"
|
22
|
+
@keydown="keyboardNav"
|
23
|
+
@click="toggleDropdown"
|
24
|
+
@keydown.esc="reset"
|
25
|
+
class="br-solid br-black-transp br-1px radius-medium"
|
26
|
+
>
|
27
|
+
<span class="flex-nowrap flex flex-v-center p-medium vti__selection">
|
28
|
+
<span v-if="dropdownOptions.showFlags" v-html="activeCountryFlag" class="mn-r-thin" />
|
29
|
+
<span v-if="dropdownOptions.showDialCodeInSelection" class="vti__country-code">
|
30
|
+
+{{ activeCountry && activeCountry.dialCode }}
|
31
|
+
</span>
|
32
|
+
<!-- <slot name="arrow-icon" :open="open"> -->
|
33
|
+
<!-- <span class="vti__dropdown-arrow">{{ open ? "▲" : "▼" }}</span> -->
|
34
|
+
<!-- </slot> -->
|
35
|
+
</span>
|
36
|
+
<ul
|
37
|
+
ref="list"
|
38
|
+
class="bs-black radius-small vti__dropdown-list"
|
39
|
+
v-show="open"
|
40
|
+
:class="dropdownOpenDirection"
|
41
|
+
:style="[listStyle]"
|
42
|
+
|
43
|
+
>
|
44
|
+
<li
|
45
|
+
v-for="(pb, index) in sortedCountries"
|
46
|
+
class="flex-nowrap flex w-max"
|
47
|
+
:class="['vti__dropdown-item', getItemClass(index, pb.iso2)]"
|
48
|
+
:key="pb.iso2 + (pb.preferred ? '-preferred' : '')"
|
49
|
+
@click="choose(pb)"
|
50
|
+
@mousemove="selectedIndex = index"
|
51
|
+
>
|
52
|
+
<span v-if="dropdownOptions.showFlags" v-html="pb.flag" class="mn-r-thin"/>
|
53
|
+
<strong>{{ pb.name }}</strong>
|
54
|
+
<span v-if="dropdownOptions.showDialCodeInList"> +{{ pb.dialCode }} </span>
|
55
|
+
</li>
|
56
|
+
</ul>
|
57
|
+
</div>
|
58
|
+
<!-- <div :class="[divInputClass]" > -->
|
59
|
+
<input
|
60
|
+
v-model="phone"
|
61
|
+
inputmode="numeric"
|
62
|
+
pattern="\d*"
|
63
|
+
ref="input"
|
64
|
+
:type="inputOptions.type"
|
65
|
+
:autocomplete="inputOptions.autocomplete"
|
66
|
+
:autofocus="inputOptions.autofocus"
|
67
|
+
:class="['vti__input', inputOptions.styleClasses, inputClass, Iclasses]"
|
68
|
+
:style="[inputStyle, Istyles]"
|
69
|
+
:disabled="disabled"
|
70
|
+
:id="inputId !== ''? inputId : inputOptions.id"
|
71
|
+
:maxlength="inputOptions.maxlength"
|
72
|
+
:name="inputId !== ''? inputId : inputOptions.name"
|
73
|
+
:placeholder="parsedPlaceholder"
|
74
|
+
:readonly="inputOptions.readonly"
|
75
|
+
:required="inputOptions.required"
|
76
|
+
:tabindex="inputOptions.tabindex"
|
77
|
+
@blur="onBlur"
|
78
|
+
@focus="onFocus"
|
79
|
+
@input="onInput"
|
80
|
+
@keyup.enter="onEnter"
|
81
|
+
@keyup.space="onSpace"
|
82
|
+
/>
|
83
|
+
<slot></slot> <!-- slot outlet -->
|
84
|
+
<!-- </div> -->
|
85
|
+
|
86
|
+
</div>
|
87
|
+
<!-- Validation -->
|
88
|
+
<transition name="fade">
|
89
|
+
<div v-if="validation" class="mn-t-thin mn-b-thin invalid-feedback">
|
90
|
+
* {{validation.message}}
|
91
|
+
</div>
|
92
|
+
</transition>
|
93
|
+
</template>
|
94
|
+
|
95
|
+
|
96
|
+
<script setup>
|
97
|
+
import { ref, reactive, watch, computed, onMounted, nextTick } from 'vue';
|
98
|
+
import { parsePhoneNumberFromString } from 'libphonenumber-js';
|
99
|
+
|
100
|
+
import clickOutside from './click-outside';
|
101
|
+
import allCountries from './all-countries';
|
102
|
+
|
103
|
+
let vClickOutside = clickOutside
|
104
|
+
|
105
|
+
const props = defineProps({
|
106
|
+
modelValue: { type: String, default: '',},
|
107
|
+
badClass: { type: [String, Array, Object], default: '', },
|
108
|
+
goodClass: { type: [String, Array, Object], default: '', },
|
109
|
+
badStyle: { type: [String, Array, Object], default: '', },
|
110
|
+
goodStyle: { type: [String, Array, Object], default: '', },
|
111
|
+
badInputClass: { type: [String, Array, Object], default: '', },
|
112
|
+
goodInputClass: { type: [String, Array, Object], default: '', },
|
113
|
+
badInputStyle: { type: [String, Array, Object], default: '', },
|
114
|
+
goodInputStyle: { type: [String, Array, Object], default: '', },
|
115
|
+
allCountries: { type: Array, default: () => allCountries, },
|
116
|
+
autoFormat: { type: Boolean, default: () => true, },
|
117
|
+
validation: { type: [Boolean, Object], default: false, },
|
118
|
+
customValidate: { type: [Boolean, RegExp], default: () => false, },
|
119
|
+
defaultCountry: { type: String, default: () => '', },
|
120
|
+
disabled: { type: Boolean, default: () => false, },
|
121
|
+
autoDefaultCountry: { type: Boolean, default: () => true },
|
122
|
+
ignoredCountries: { type: Array, default: () => [], },
|
123
|
+
invalidMsg: { type: String, default: () => '', },
|
124
|
+
mode: { type: String, default: () => 'auto', },
|
125
|
+
onlyCountries: { type: Array, default: () => [], },
|
126
|
+
preferredCountries: { type: Array, default: () => [], },
|
127
|
+
validCharactersOnly: { type: Boolean, default: () => false, },
|
128
|
+
rootClass: { type: [String, Array, Object], default: () => '', },
|
129
|
+
inputClass: { type: [String, Array, Object], default: () => {return ''}, },
|
130
|
+
divInputClass: { type: [String, Array, Object], default: () => {return ''}, },
|
131
|
+
rootStyle: { type: [String, Array, Object], default: () => {return ''}, },
|
132
|
+
dropdownStyle: { type: [String, Array, Object], default: () => {return ''}, },
|
133
|
+
listStyle: { type: [String, Array, Object], default: () => {return ''}, },
|
134
|
+
inputStyle: { type: [String, Array, Object], default: () => {return ''}, },
|
135
|
+
inputId: { type: [String, Array, Object], default: () => {return ''}, },
|
136
|
+
Placeholder: { type: [String, Object, Function], default: () => {return ''}, },
|
137
|
+
dropdownOptions: {
|
138
|
+
type: Object,
|
139
|
+
default: () => {
|
140
|
+
return {
|
141
|
+
showDialCodeInList: true,
|
142
|
+
showDialCodeInSelection: false,
|
143
|
+
showFlags: true,
|
144
|
+
tabindex: 0
|
145
|
+
}
|
146
|
+
}
|
147
|
+
},
|
148
|
+
inputOptions: {
|
149
|
+
type: Object,
|
150
|
+
default: () => {
|
151
|
+
return {
|
152
|
+
autocomplete: 'on',
|
153
|
+
autofocus: false,
|
154
|
+
id: '',
|
155
|
+
maxlength: 25,
|
156
|
+
name: 'telephone',
|
157
|
+
placeholder: 'Enter a phone number',
|
158
|
+
readonly: false,
|
159
|
+
required: false,
|
160
|
+
tabindex: 0,
|
161
|
+
type: 'tel',
|
162
|
+
styleClasses: ''
|
163
|
+
}
|
164
|
+
}
|
165
|
+
},
|
166
|
+
});
|
167
|
+
|
168
|
+
const emits = defineEmits([
|
169
|
+
'update:modelValue',
|
170
|
+
'validate',
|
171
|
+
'country-changed',
|
172
|
+
'open',
|
173
|
+
'close',
|
174
|
+
'blur',
|
175
|
+
'focus',
|
176
|
+
'enter',
|
177
|
+
'space'
|
178
|
+
]);
|
179
|
+
|
180
|
+
const counter = ref(5);
|
181
|
+
const initCounter = ref(5);
|
182
|
+
const message = reactive({
|
183
|
+
action: null,
|
184
|
+
amount: null,
|
185
|
+
});
|
186
|
+
const phone = ref(props.modelValue ? props.modelValue : '');
|
187
|
+
const activeCountryCode = ref('');
|
188
|
+
const open = ref(false);
|
189
|
+
const finishMounted = ref(false);
|
190
|
+
const selectedIndex = ref(null);
|
191
|
+
const typeToFindInput = ref('');
|
192
|
+
const typeToFindTimer = ref(null);
|
193
|
+
const dropdownOpenDirection = ref('below');
|
194
|
+
const parsedPlaceholder = ref(props.Placeholder !== '' ? props.Placeholder : props.inputOptions.placeholder);
|
195
|
+
|
196
|
+
const rootElement = ref(null)
|
197
|
+
const list = ref(null)
|
198
|
+
const input = ref(null)
|
199
|
+
|
200
|
+
// Lifecycle hooks
|
201
|
+
onMounted(async () => {
|
202
|
+
if (props.modelValue) {
|
203
|
+
phone.value = props.modelValue;
|
204
|
+
}
|
205
|
+
|
206
|
+
cleanInvalidCharacters();
|
207
|
+
|
208
|
+
initializeCountry()
|
209
|
+
.then(() => {
|
210
|
+
if (!phone.value
|
211
|
+
&& props.inputOptions?.showDialCode
|
212
|
+
&& activeCountryCode.value) {
|
213
|
+
phone.value = `+${activeCountryCode.value}`;
|
214
|
+
}
|
215
|
+
emits('validate', phoneObject.value);
|
216
|
+
})
|
217
|
+
.catch(console.error)
|
218
|
+
.then(() => {
|
219
|
+
finishMounted.value = true;
|
220
|
+
});
|
221
|
+
});
|
222
|
+
|
223
|
+
// Computed properties
|
224
|
+
const activeCountry = computed(() => {
|
225
|
+
return findCountry(activeCountryCode.value);
|
226
|
+
});
|
227
|
+
|
228
|
+
const activeCountryFlag = computed(() => {
|
229
|
+
return activeCountry.value ? activeCountry.value.flag : null;
|
230
|
+
});
|
231
|
+
|
232
|
+
const parsedMode = computed(() => {
|
233
|
+
if (props.mode === 'auto') {
|
234
|
+
if (!phone.value || phone.value[0] !== '+') {
|
235
|
+
return 'national';
|
236
|
+
}
|
237
|
+
return 'international';
|
238
|
+
}
|
239
|
+
if (!['international', 'national'].includes(props.mode)) {
|
240
|
+
console.error('Invalid value of prop "mode"');
|
241
|
+
return 'international';
|
242
|
+
}
|
243
|
+
return props.mode;
|
244
|
+
});
|
245
|
+
|
246
|
+
const filteredCountries = computed(() => {
|
247
|
+
if (props.onlyCountries && props.onlyCountries.length) {
|
248
|
+
return props.allCountries.filter(({ iso2 }) => props.onlyCountries.includes(iso2.toUpperCase()));
|
249
|
+
}
|
250
|
+
|
251
|
+
if (props.ignoredCountries && props.ignoredCountries.length) {
|
252
|
+
return props.allCountries.filter(({ iso2 }) =>
|
253
|
+
!props.ignoredCountries.includes(iso2.toUpperCase()) && !props.ignoredCountries.includes(iso2.toLowerCase()),
|
254
|
+
);
|
255
|
+
}
|
256
|
+
|
257
|
+
return props.allCountries;
|
258
|
+
});
|
259
|
+
const sortedCountries = computed(() => {
|
260
|
+
const preferredCountries = getCountries(props.preferredCountries)
|
261
|
+
.map(country => ({ ...country, preferred: true }));
|
262
|
+
|
263
|
+
return [...preferredCountries, ...filteredCountries.value];
|
264
|
+
});
|
265
|
+
|
266
|
+
const phoneObject = computed(() => {
|
267
|
+
let result = {};
|
268
|
+
|
269
|
+
if (phone.value?.[0] === '+') {
|
270
|
+
result = parsePhoneNumberFromString(phone.value) || {};
|
271
|
+
} else {
|
272
|
+
result = parsePhoneNumberFromString(phone.value, activeCountryCode.value) || {};
|
273
|
+
}
|
274
|
+
|
275
|
+
const { metadata, ...phoneDetails } = result;
|
276
|
+
|
277
|
+
let valid = result.isValid?.();
|
278
|
+
let formatted = phone.value;
|
279
|
+
|
280
|
+
if (valid) {
|
281
|
+
formatted = result.format?.(parsedMode.value.toUpperCase(), { nationalPrefix: false });
|
282
|
+
}
|
283
|
+
|
284
|
+
if (result.country && (props.ignoredCountries.length || props.onlyCountries.length)) {
|
285
|
+
if (!findCountry(result.country)) {
|
286
|
+
valid = false;
|
287
|
+
result = { ...result, country: null };
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
return {
|
292
|
+
...phoneDetails,
|
293
|
+
countryCode: result.country,
|
294
|
+
valid,
|
295
|
+
country: activeCountry.value,
|
296
|
+
formatted,
|
297
|
+
};
|
298
|
+
});
|
299
|
+
|
300
|
+
const classes = computed(() => {
|
301
|
+
return phoneObject.value.valid === true ? props.goodClass :
|
302
|
+
phoneObject.value.valid === false ? props.badClass : undefined;
|
303
|
+
});
|
304
|
+
|
305
|
+
const styles = computed(() => {
|
306
|
+
return phoneObject.value.valid === true ? props.goodStyle :
|
307
|
+
phoneObject.value.valid === false ? props.badStyle : undefined;
|
308
|
+
});
|
309
|
+
|
310
|
+
const Iclasses = computed(() => {
|
311
|
+
return phoneObject.value.valid === true ? props.goodInputClass :
|
312
|
+
phoneObject.value.valid === false ? props.badInputClass : undefined;
|
313
|
+
});
|
314
|
+
|
315
|
+
const Istyles = computed(() => {
|
316
|
+
return phoneObject.value.valid === true ? props.goodInputStyle :
|
317
|
+
phoneObject.value.valid === false ? props.badInputStyle : undefined;
|
318
|
+
});
|
319
|
+
// ///////////
|
320
|
+
// Watchers
|
321
|
+
// ///////////
|
322
|
+
watch(activeCountry, (newValue, oldValue) => {
|
323
|
+
if (!newValue && oldValue?.iso2) {
|
324
|
+
activeCountryCode.value = oldValue.iso2;
|
325
|
+
return;
|
326
|
+
}
|
327
|
+
if (newValue?.iso2) {
|
328
|
+
emits('country-changed', newValue);
|
329
|
+
resetPlaceholder();
|
330
|
+
}
|
331
|
+
});
|
332
|
+
|
333
|
+
// Следим за изменениями countryCode в phoneObject
|
334
|
+
watch(() => phoneObject.value.countryCode, (newValue) => {
|
335
|
+
activeCountryCode.value = newValue;
|
336
|
+
});
|
337
|
+
|
338
|
+
// Следим за изменениями валидности phoneObject
|
339
|
+
watch(() => phoneObject.value.valid, () => {
|
340
|
+
emits('validate', phoneObject.value);
|
341
|
+
});
|
342
|
+
|
343
|
+
// Следим за изменениями отформатированного номера в phoneObject
|
344
|
+
watch(() => phoneObject.value.formatted, (newValue) => {
|
345
|
+
if (!props.autoFormat || props.customValidate) {
|
346
|
+
return;
|
347
|
+
}
|
348
|
+
emitInput(newValue);
|
349
|
+
|
350
|
+
nextTick(() => {
|
351
|
+
if (newValue && !props.modelValue) {
|
352
|
+
phone.value = newValue;
|
353
|
+
}
|
354
|
+
});
|
355
|
+
});
|
356
|
+
|
357
|
+
// Следим за изменениями placeholder в inputOptions
|
358
|
+
watch(() => props.inputOptions.placeholder, () => {
|
359
|
+
resetPlaceholder();
|
360
|
+
});
|
361
|
+
|
362
|
+
// Следим за внешними изменениями значения (например, через v-model)
|
363
|
+
watch(() => props.modelValue, (newValue, oldValue) => {
|
364
|
+
if (!testCharacters()) {
|
365
|
+
nextTick(() => {
|
366
|
+
phone.value = oldValue;
|
367
|
+
onInput();
|
368
|
+
});
|
369
|
+
} else {
|
370
|
+
phone.value = newValue;
|
371
|
+
}
|
372
|
+
});
|
373
|
+
|
374
|
+
// Следим за состоянием открытия/закрытия dropdown
|
375
|
+
watch(open, (isDropdownOpened) => {
|
376
|
+
if (isDropdownOpened) {
|
377
|
+
setDropdownPosition();
|
378
|
+
emits('open');
|
379
|
+
} else {
|
380
|
+
emits('close');
|
381
|
+
}
|
382
|
+
});
|
383
|
+
|
384
|
+
// Methods
|
385
|
+
function getCountry() {
|
386
|
+
return fetch('https://ip2c.org/s')
|
387
|
+
.then((response) => response.text())
|
388
|
+
.then((response) => {
|
389
|
+
const result = (response || '').toString();
|
390
|
+
|
391
|
+
if (!result || result[0] !== '1') {
|
392
|
+
throw new Error('unable to fetch the country');
|
393
|
+
}
|
394
|
+
|
395
|
+
return result.substr(2, 2);
|
396
|
+
});
|
397
|
+
}
|
398
|
+
|
399
|
+
function setCaretPosition(ctrl, pos) {
|
400
|
+
// Modern browsers
|
401
|
+
if (ctrl.setSelectionRange) {
|
402
|
+
ctrl.focus();
|
403
|
+
ctrl.setSelectionRange(pos, pos);
|
404
|
+
|
405
|
+
// IE8 and below
|
406
|
+
} else if (ctrl.createTextRange) {
|
407
|
+
const range = ctrl.createTextRange();
|
408
|
+
range.collapse(true);
|
409
|
+
range.moveEnd('character', pos);
|
410
|
+
range.moveStart('character', pos);
|
411
|
+
range.select();
|
412
|
+
}
|
413
|
+
}
|
414
|
+
|
415
|
+
function resetPlaceholder() {
|
416
|
+
parsedPlaceholder.value = props.inputOptions.placeholder;
|
417
|
+
}
|
418
|
+
function initializeCountry() {
|
419
|
+
return new Promise((resolve) => {
|
420
|
+
if (phone.value?.[0] === '+') { resolve(); return; }
|
421
|
+
if (props.defaultCountry) { choose(props.defaultCountry); resolve(); return; }
|
422
|
+
const fallbackCountry = props.preferredCountries[0] || filteredCountries.value[0];
|
423
|
+
if (props.autoDefaultCountry) {
|
424
|
+
getCountry()
|
425
|
+
.then((res) => { choose(res || activeCountryCode.value); })
|
426
|
+
.catch((error) => { console.warn(error); choose(fallbackCountry); })
|
427
|
+
.finally(() => { resolve(); });
|
428
|
+
} else {
|
429
|
+
choose(fallbackCountry);
|
430
|
+
resolve();
|
431
|
+
}
|
432
|
+
});
|
433
|
+
}
|
434
|
+
|
435
|
+
function getCountries(list = []) {
|
436
|
+
return list.map((countryCode) => findCountry(countryCode)).filter(Boolean);
|
437
|
+
}
|
438
|
+
|
439
|
+
function findCountry(iso = '') {
|
440
|
+
return filteredCountries.value.find((country) => country.iso2 === iso.toUpperCase());
|
441
|
+
}
|
442
|
+
|
443
|
+
function getItemClass(index, iso2) {
|
444
|
+
const highlighted = selectedIndex.value === index;
|
445
|
+
const lastPreferred = index === props.preferredCountries.length - 1;
|
446
|
+
const preferred = props.preferredCountries.some((c) => c.toUpperCase() === iso2);
|
447
|
+
return { highlighted, 'last-preferred': lastPreferred, preferred };
|
448
|
+
}
|
449
|
+
|
450
|
+
function choose(country) {
|
451
|
+
let parsedCountry = typeof country === 'string' ? findCountry(country) : country;
|
452
|
+
if (!parsedCountry) return;
|
453
|
+
|
454
|
+
if (phone.value?.[0] === '+' && parsedCountry.iso2 && phoneObject.value.nationalNumber) {
|
455
|
+
activeCountryCode.value = parsedCountry.iso2;
|
456
|
+
phone.value = parsePhoneNumberFromString(phoneObject.value.nationalNumber, parsedCountry.iso2).formatInternational();
|
457
|
+
return;
|
458
|
+
}
|
459
|
+
|
460
|
+
if (props.inputOptions?.showDialCode && parsedCountry) {
|
461
|
+
phone.value = `+${parsedCountry.dialCode}`;
|
462
|
+
return;
|
463
|
+
}
|
464
|
+
|
465
|
+
activeCountryCode.value = parsedCountry.iso2;
|
466
|
+
emitInput(phone.value);
|
467
|
+
}
|
468
|
+
|
469
|
+
function cleanInvalidCharacters() {
|
470
|
+
const currentPhone = phone.value;
|
471
|
+
|
472
|
+
if (props.validCharactersOnly) {
|
473
|
+
const results = phone.value.match(/[()\-+0-9\s]*/g);
|
474
|
+
phone.value = results.join('');
|
475
|
+
}
|
476
|
+
|
477
|
+
if (props.customValidate && props.customValidate instanceof RegExp) {
|
478
|
+
const results = phone.value.match(props.customValidate);
|
479
|
+
phone.value = results ? results.join('') : '';
|
480
|
+
}
|
481
|
+
|
482
|
+
if (currentPhone !== phone.value) {
|
483
|
+
emitInput(phone.value);
|
484
|
+
}
|
485
|
+
}
|
486
|
+
|
487
|
+
function testCharacters() {
|
488
|
+
if (props.validCharactersOnly) {
|
489
|
+
const result = /^[()\-+0-9\s]*$/.test(phone.value);
|
490
|
+
if (!result) return false;
|
491
|
+
}
|
492
|
+
if (props.customValidate) {
|
493
|
+
return testCustomValidate();
|
494
|
+
}
|
495
|
+
return true;
|
496
|
+
}
|
497
|
+
|
498
|
+
function testCustomValidate() {
|
499
|
+
return props.customValidate instanceof RegExp ? props.customValidate.test(phone.value) : false;
|
500
|
+
}
|
501
|
+
|
502
|
+
function onInput() {
|
503
|
+
input.value.setCustomValidity(phoneObject.valid ? '' : props.invalidMsg);
|
504
|
+
|
505
|
+
emitInput(phone.value);
|
506
|
+
}
|
507
|
+
|
508
|
+
function emitInput(value) {
|
509
|
+
emits('update:modelValue', value);
|
510
|
+
|
511
|
+
if (phoneObject.value.valid) {
|
512
|
+
emits('country', phoneObject.value.number);
|
513
|
+
} else {
|
514
|
+
emits('country', null);
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
function onBlur() {
|
519
|
+
emits('blur');
|
520
|
+
}
|
521
|
+
|
522
|
+
function onFocus() {
|
523
|
+
setCaretPosition(input.value, phone.value.length);
|
524
|
+
emits('focus');
|
525
|
+
}
|
526
|
+
|
527
|
+
function onEnter() {
|
528
|
+
emits('enter');
|
529
|
+
}
|
530
|
+
|
531
|
+
function onSpace() {
|
532
|
+
emits('space');
|
533
|
+
}
|
534
|
+
|
535
|
+
function focus() {
|
536
|
+
input.value.focus();
|
537
|
+
}
|
538
|
+
|
539
|
+
function toggleDropdown() {
|
540
|
+
if (props.disabled) return;
|
541
|
+
open.value = !open.value;
|
542
|
+
}
|
543
|
+
|
544
|
+
function clickedOutside() {
|
545
|
+
open.value = false;
|
546
|
+
}
|
547
|
+
|
548
|
+
function keyboardNav(e) {
|
549
|
+
if (e.keyCode === 40) { // down arrow
|
550
|
+
e.preventDefault();
|
551
|
+
open.value = true;
|
552
|
+
selectedIndex.value = selectedIndex.value === null ? 0 : Math.min(sortedCountries.value.length - 1, selectedIndex.value + 1);
|
553
|
+
nextTick(() => {
|
554
|
+
const selEle = list.value.children[selectedIndex.value];
|
555
|
+
if (selEle.offsetTop + selEle.clientHeight > list.value.scrollTop + list.value.clientHeight) {
|
556
|
+
list.value.scrollTop = selEle.offsetTop - list.value.clientHeight + selEle.clientHeight;
|
557
|
+
}
|
558
|
+
});
|
559
|
+
} else if (e.keyCode === 38) { // up arrow
|
560
|
+
e.preventDefault();
|
561
|
+
open.value = true;
|
562
|
+
selectedIndex.value = selectedIndex.value === null ? sortedCountries.value.length - 1 : Math.max(0, selectedIndex.value - 1);
|
563
|
+
nextTick(() => {
|
564
|
+
const selEle = list.value.children[selectedIndex.value];
|
565
|
+
if (selEle.offsetTop < list.value.scrollTop) {
|
566
|
+
list.value.scrollTop = selEle.offsetTop;
|
567
|
+
}
|
568
|
+
});
|
569
|
+
} else if (e.keyCode === 13) { // enter key
|
570
|
+
e.preventDefault();
|
571
|
+
if (selectedIndex.value !== null) {
|
572
|
+
choose(sortedCountries.value[selectedIndex.value]);
|
573
|
+
}
|
574
|
+
open.value = !open.value;
|
575
|
+
} else { // typing a country's name
|
576
|
+
typeToFindInput.value += e.key;
|
577
|
+
clearTimeout(typeToFindTimer);
|
578
|
+
typeToFindTimer = setTimeout(() => {
|
579
|
+
typeToFindInput.value = '';
|
580
|
+
}, 700);
|
581
|
+
nextTick(() => {
|
582
|
+
const typedCountryI = sortedCountries.value.slice(props.preferredCountries.length).findIndex((c) => c.name.toLowerCase().startsWith(typeToFindInput.value));
|
583
|
+
if (typedCountryI >= 0) {
|
584
|
+
selectedIndex.value = props.preferredCountries.length + typedCountryI;
|
585
|
+
const selEle = list.value.children[selectedIndex.value];
|
586
|
+
const needToScroll = selEle.offsetTop < list.value.scrollTop || selEle.offsetTop + selEle.clientHeight > list.value.scrollTop + list.value.clientHeight;
|
587
|
+
if (needToScroll) {
|
588
|
+
list.value.scrollTop = selEle.offsetTop - list.value.clientHeight / 2;
|
589
|
+
}
|
590
|
+
}
|
591
|
+
});
|
592
|
+
}
|
593
|
+
}
|
594
|
+
|
595
|
+
function reset() {
|
596
|
+
selectedIndex.value = sortedCountries.value.map(c => c.iso2).indexOf(activeCountryCode.value);
|
597
|
+
open.value = false;
|
598
|
+
}
|
599
|
+
|
600
|
+
function setDropdownPosition() {
|
601
|
+
const spaceBelow = window.innerHeight - rootElement.value.getBoundingClientRect().bottom;
|
602
|
+
const hasEnoughSpaceBelow = spaceBelow > 200;
|
603
|
+
dropdownOpenDirection.value = hasEnoughSpaceBelow ? 'below' : 'above';
|
604
|
+
}
|
605
|
+
</script>
|
606
|
+
|
607
|
+
<style scoped>
|
608
|
+
.vue3-reactive-tel-input{
|
609
|
+
display:flex
|
610
|
+
}
|
611
|
+
.vue3-reactive-tel-input.disabled .dropdown,.vue3-reactive-tel-input.disabled .selection,.vue3-reactive-tel-input.disabled input{
|
612
|
+
cursor:no-drop
|
613
|
+
}
|
614
|
+
.vti__dropdown{
|
615
|
+
display:flex;
|
616
|
+
flex-direction:column;
|
617
|
+
align-content:center;
|
618
|
+
justify-content:center;
|
619
|
+
position:relative;
|
620
|
+
padding:7px;
|
621
|
+
cursor:pointer
|
622
|
+
}
|
623
|
+
.vti__dropdown.show{
|
624
|
+
max-height:300px;
|
625
|
+
overflow:scroll
|
626
|
+
}
|
627
|
+
.vti__dropdown-list{
|
628
|
+
z-index:1;
|
629
|
+
padding:0;
|
630
|
+
margin:0;
|
631
|
+
text-align:left;
|
632
|
+
list-style:none;
|
633
|
+
max-height:200px;
|
634
|
+
overflow-y:scroll;
|
635
|
+
position:absolute;
|
636
|
+
left:-1px;
|
637
|
+
background-color:#fff;
|
638
|
+
width:fit-content
|
639
|
+
}
|
640
|
+
.vti__dropdown-list.below{
|
641
|
+
top:33px
|
642
|
+
}
|
643
|
+
.vti__dropdown-list.above{
|
644
|
+
top:auto;
|
645
|
+
bottom:100%
|
646
|
+
}
|
647
|
+
.vti__dropdown-arrow{
|
648
|
+
transform:scaleY(.5);
|
649
|
+
display:inline-block;
|
650
|
+
color:#666
|
651
|
+
}
|
652
|
+
.vti__dropdown-item{
|
653
|
+
cursor:pointer;
|
654
|
+
padding:4px 15px
|
655
|
+
}
|
656
|
+
.vti__dropdown-item.last-preferred{
|
657
|
+
border-bottom:1px solid #cacaca
|
658
|
+
}
|
659
|
+
.vti__dropdown-item .vti__flag{
|
660
|
+
display:inline-block;
|
661
|
+
margin-right:5px
|
662
|
+
}
|
663
|
+
.vti__input{
|
664
|
+
color: inherit;
|
665
|
+
border:none;
|
666
|
+
border-radius:0 2px 2px 0;
|
667
|
+
width:100%;
|
668
|
+
outline:0;
|
669
|
+
padding-left:7px
|
670
|
+
}
|
671
|
+
</style>
|
@@ -1,23 +1,28 @@
|
|
1
1
|
export default {
|
2
|
-
beforeMount(el, binding, vNode) {
|
3
|
-
// Provided expression must evaluate to a function.
|
2
|
+
beforeMount(el, binding, vNode) {
|
4
3
|
if (typeof binding.value !== 'function') {
|
5
|
-
|
6
|
-
|
7
|
-
is not a function, but has to be`;
|
4
|
+
let warn = `[Vue-click-outside:] provided expression ${binding.expression} is not a function, but has to be`;
|
5
|
+
const compName = vNode.component?.name;
|
8
6
|
if (compName) {
|
9
|
-
warn += `Found in component ${compName}`;
|
7
|
+
warn += ` Found in component ${compName}`;
|
10
8
|
}
|
11
9
|
console.warn(warn);
|
12
10
|
}
|
13
|
-
|
11
|
+
|
12
|
+
el.clickOutsideEvent = function(event) {
|
13
|
+
// Проверка, что клик был сделан за пределами элемента и не на самом элементе
|
14
14
|
if (!(el === event.target || el.contains(event.target))) {
|
15
|
-
|
15
|
+
// Вызов переданной функции, если условие выполняется
|
16
|
+
binding.value(event);
|
16
17
|
}
|
17
18
|
};
|
18
|
-
|
19
|
+
|
20
|
+
// Использование document вместо document.body может помочь в некоторых случаях,
|
21
|
+
// особенно если событие stopPropagation используется в дочерних элементах
|
22
|
+
document.addEventListener('click', el.clickOutsideEvent, true); // true для использования в фазе перехвата
|
19
23
|
},
|
20
24
|
unmounted(el) {
|
21
|
-
|
25
|
+
// Убедитесь, что используете тот же флаг при удалении обработчика
|
26
|
+
document.removeEventListener('click', el.clickOutsideEvent, true);
|
22
27
|
},
|
23
|
-
};
|
28
|
+
};
|