maz-ui
Version:
A standalone components library for Vue.Js 3 & Nuxt.Js 3
413 lines (412 loc) • 18.3 kB
JavaScript
import { ref, defineComponent, defineAsyncComponent, computed, onBeforeMount, onMounted, watch, provide, createElementBlock, openBlock, normalizeStyle, normalizeClass, unref, createBlock, createCommentVNode, createVNode, mergeProps, withCtx, createElementVNode, toDisplayString, renderSlot, nextTick } from "vue";
import { useTranslations } from "@maz-ui/translations";
import { useInstanceUniqId } from "../composables/useInstanceUniqId.js";
import { getCountryCallingCode, getCountries, isSupportedCountry, getExampleNumber, parsePhoneNumberFromString, AsYouType } from "libphonenumber-js";
import { _ as _export_sfc } from "./_plugin-vue_export-helper.B--vMWp3.js";
import '../assets/MazInputPhoneNumber.CKOOaZiA.css';const examples = ref();
function isCountryAvailable(locale) {
try {
const response = isSupportedCountry(locale);
return response || (console.error(`[maz-ui](MazInputPhoneNumber) The code country "${locale}" is not available`), !1);
} catch (error) {
return console.error(`[maz-ui](MazInputPhoneNumber) ${error}`), !1;
}
}
function getPhoneNumberResults({
phoneNumber,
countryCode,
checkCountryCode = !1
}) {
try {
if (!phoneNumber)
return {
isValid: !1,
countryCode
};
const parsedNumber = parsePhoneNumberFromString(phoneNumber, countryCode ?? void 0), isValid = parsedNumber?.isValid() ?? !1, isSameCountryCode = countryCode && checkCountryCode ? parsedNumber?.country && countryCode === parsedNumber.country : !0;
return {
isValid: isValid && !!isSameCountryCode,
countryCode,
parsedCountryCode: parsedNumber?.country,
isPossible: parsedNumber?.isPossible(),
countryCallingCode: parsedNumber?.countryCallingCode,
nationalNumber: parsedNumber?.nationalNumber,
type: parsedNumber?.getType(),
formatInternational: parsedNumber?.formatInternational(),
formatNational: parsedNumber?.formatNational(),
uri: parsedNumber?.getURI(),
e164: parsedNumber?.format("E.164"),
rfc3966: parsedNumber?.format("RFC3966"),
possibleCountries: parsedNumber?.getPossibleCountries(),
phoneNumber
};
} catch (error) {
throw new Error(`[MazInputPhoneNumber](getResultsFromPhoneNumber) ${error}`);
}
}
async function getPhoneNumberExamplesFile() {
const { default: data } = await import("./examples.mobile.json.CZXHnLzv.js");
return data;
}
function getPhoneNumberExample(countryCode) {
try {
return examples.value && countryCode ? getExampleNumber(countryCode, examples.value)?.formatNational() : void 0;
} catch (error) {
console.error(`[maz-ui](MazInputPhoneNumber) ${error}`);
}
}
function getAsYouTypeFormat(countryCode, phoneNumber) {
try {
return !phoneNumber || !countryCode || typeof countryCode != "string" || countryCode.length !== 2 ? phoneNumber : new AsYouType(countryCode).input(phoneNumber);
} catch (error) {
return console.error(`[MazInputPhoneNumber](getAsYouTypeFormat) Error with countryCode: "${countryCode}", phoneNumber: "${phoneNumber}"`, error), phoneNumber;
}
}
function isSameCountryCallingCode(countryCode, countryCode2) {
return getCountryCallingCode(countryCode) === getCountryCallingCode(countryCode2);
}
async function loadExamples() {
try {
if (examples.value)
return;
examples.value = await getPhoneNumberExamplesFile();
} catch (error) {
console.error("[maz-ui](MazInputPhoneNumber) while loading phone number examples file", error);
}
}
function useLibphonenumber() {
return {
examples,
getAsYouTypeFormat,
getPhoneNumberResults,
getPhoneNumberExamplesFile,
getPhoneNumberExample,
isSameCountryCallingCode,
isCountryAvailable,
getCountries,
getCountryCallingCode,
loadExamples
};
}
async function fetchLocaleIp() {
try {
const reponse = await fetch("https://ipwho.is"), { country_code } = await reponse.json();
return country_code;
} catch (error) {
console.error(`[maz-ui](fetchLocaleIp) ${error}`);
return;
}
}
function getBrowserLocale() {
if (typeof globalThis.window > "u")
return;
const browserLocale = globalThis.navigator.language;
if (!browserLocale)
return;
const parts = browserLocale.split("-");
let locale = parts.length > 1 ? parts[1].slice(0, 2).toUpperCase() : parts[0].slice(0, 2).toUpperCase();
return locale === "EN" && (locale = "US"), locale === "JA" && (locale = "JP"), {
locale,
browserLocale
};
}
let displayNamesInstance, displayNamesLocale;
function getCountryName(locale, code, customCountriesNameListByIsoCode) {
return customCountriesNameListByIsoCode?.[code] ? customCountriesNameListByIsoCode[code] : ((displayNamesLocale !== locale || !displayNamesInstance) && (displayNamesLocale = locale, displayNamesInstance = new Intl.DisplayNames([locale], { type: "region" })), displayNamesInstance.of(code));
}
function getCountryList(locale, customCountriesNameListByIsoCode) {
const countriesList = [], isoList = getCountries();
locale = locale ?? getBrowserLocale()?.browserLocale ?? "en-US";
for (const code of isoList) {
const name = getCountryName(locale, code, customCountriesNameListByIsoCode);
if (name)
try {
const dialCode = getCountryCallingCode(code);
countriesList.push({
code,
dialCode,
name
});
} catch (error) {
console.error(`[MazInputPhoneNumber](getCountryCallingCode) ${error}`);
}
}
return countriesList;
}
async function fetchCountryCode() {
try {
const countryCode = await fetchLocaleIp();
return countryCode ? {
data: countryCode,
error: void 0
} : {
data: void 0,
error: new Error("[MazInputPhoneNumber](fetchCountryCode) No country code found")
};
} catch (error) {
return {
data: void 0,
error: new Error(`[MazInputPhoneNumber](fetchCountryCode) ${error}`)
};
}
}
function sanitizePhoneNumber(input) {
if (!input)
return "";
const regex = new RegExp(/[^\d ()+-]/g);
return input.replaceAll(regex, "").trim();
}
function useMazInputPhoneNumber() {
return {
fetchCountryCode,
getBrowserLocale,
getCountryList,
sanitizePhoneNumber
};
}
const _hoisted_1 = ["id"], _hoisted_2 = { class: "m-input-phone-number__country-list-code" }, _sfc_main = /* @__PURE__ */ defineComponent({
name: "MazInputPhoneNumber",
inheritAttrs: !1,
__name: "MazInputPhoneNumber",
props: {
style: {},
class: {},
modelValue: {},
countryCode: {},
id: {},
placeholder: {},
label: {},
preferredCountries: {},
ignoredCountries: {},
onlyCountries: {},
translations: {},
listPosition: { default: "bottom-start" },
color: { default: "primary" },
size: { default: "md" },
hideFlags: { type: Boolean, default: !1 },
disabled: { type: Boolean, default: !1 },
example: { type: Boolean, default: !0 },
search: { type: Boolean, default: !0 },
searchThreshold: { default: 0.75 },
useBrowserLocale: { type: Boolean, default: !0 },
fetchCountry: { type: Boolean },
hideCountrySelect: { type: Boolean, default: !1 },
showCodeInList: { type: Boolean, default: !1 },
customCountriesList: { default: void 0 },
autoFormat: { type: [String, Boolean], default: "blur" },
countryLocale: { default: void 0 },
validationError: { type: Boolean, default: !0 },
validationSuccess: { type: Boolean, default: !0 },
success: { type: Boolean },
error: { type: Boolean },
displayCountryName: { type: Boolean, default: !1 },
block: { type: Boolean },
orientation: { default: "responsive" },
countrySelectAttributes: { default: () => ({
name: "country",
autocomplete: "off",
style: {
width: "14rem"
}
}) },
phoneInputAttributes: { default: () => ({
name: "phone",
autocomplete: "tel",
inputmode: "tel"
}) }
},
emits: ["update:model-value", "country-code", "update:country-code", "data"],
setup(__props, { emit: __emit }) {
const props = __props, emits = __emit, MazSelectCountry = defineAsyncComponent(() => import("../components/MazSelectCountry.js")), PhoneInput = defineAsyncComponent(() => import("./PhoneInput.BK3DUfxF.js")), { fetchCountryCode: fetchCountryCode2, getBrowserLocale: getBrowserLocale2, getCountryList: getCountryList2 } = useMazInputPhoneNumber(), { isCountryAvailable: isCountryAvailable2, getPhoneNumberResults: getPhoneNumberResults2 } = useLibphonenumber(), instanceId = useInstanceUniqId({
componentName: "MazInputPhoneNumber",
providedId: props.id
}), phoneNumber = ref(), selectedCountry = ref(), { t } = useTranslations(), messages = computed(() => ({
countrySelect: {
error: props.translations?.countrySelect?.error || t("inputPhoneNumber.countrySelect.error"),
placeholder: props.translations?.countrySelect?.placeholder || t("inputPhoneNumber.countrySelect.placeholder"),
searchPlaceholder: props.translations?.countrySelect?.searchPlaceholder || t("inputPhoneNumber.countrySelect.searchPlaceholder")
},
phoneInput: {
placeholder: props.translations?.phoneInput?.placeholder || t("inputPhoneNumber.phoneInput.placeholder"),
example: props.translations?.phoneInput?.example
}
})), isPhoneNumberInternalUpdate = ref(!1), isCountryInternalUpdate = ref(!1), results = ref({
isValid: !1,
countryCode: props.countryCode,
phoneNumber: props.modelValue
}), PhoneInputRef = ref();
onBeforeMount(async () => {
if (props.countryCode && !selectedCountry.value && onCountryChanged({ countryCode: props.countryCode }), props.fetchCountry && !selectedCountry.value) {
const { data: countryCode, error } = await fetchCountryCode2();
if (error) {
console.error(`[MazInputPhoneNumber](onBeforeMount) Error while fetching country code - ${error.message}`);
return;
}
onCountryChanged({ countryCode });
}
}), onMounted(() => {
if (!selectedCountry.value && props.useBrowserLocale) {
const countryCode = getBrowserLocale2()?.locale;
countryCode && isCountryAvailable2(countryCode) && onCountryChanged({ countryCode });
}
});
function updateTheResults({
phone = phoneNumber.value || props.modelValue,
countryCode = selectedCountry.value,
checkCountryCode = !1
}) {
results.value = getPhoneNumberResults2({
phoneNumber: phone,
countryCode,
checkCountryCode
});
}
async function selectPhoneNumberInput() {
await nextTick(), setTimeout(() => {
PhoneInputRef.value?.focus();
}, 100);
}
function setSelectedCountry(countryCode) {
if (countryCode) {
if (!isCountryAvailable2(countryCode)) {
console.warn(`[MazInputPhoneNumber] Country code not available: "${countryCode}"`), selectedCountry.value = void 0;
return;
}
selectedCountry.value = countryCode;
}
}
function onPhoneNumberChanged({
newPhoneNumber
}) {
updateTheResults({ phone: newPhoneNumber }), results.value.parsedCountryCode && results.value.parsedCountryCode !== selectedCountry.value && onCountryChanged({
countryCode: results.value.parsedCountryCode,
updateResults: !1
}), results.value.isValid && (props.autoFormat === "blur" || props.autoFormat === "typing") ? phoneNumber.value = results.value.formatNational?.trim().replace(new RegExp(/\D/g), "") : phoneNumber.value = newPhoneNumber, isPhoneNumberInternalUpdate.value = !0, results.value.e164 ? emits("update:model-value", results.value.e164) : emits("update:model-value", results.value.phoneNumber), setTimeout(() => {
isPhoneNumberInternalUpdate.value = !1;
}, 0);
}
function onCountryChanged({
countryCode,
updateResults = !0,
selectPhoneNumber = !1
}) {
if (!countryCode) {
selectedCountry.value = void 0;
return;
}
isCountryInternalUpdate.value = !0, countryCode !== selectedCountry.value && setSelectedCountry(countryCode), updateResults && updateTheResults({
countryCode: selectedCountry.value,
checkCountryCode: !0
});
const code = selectedCountry.value;
emits("country-code", code), emits("update:country-code", code), selectPhoneNumber && !results.value.isValid && selectPhoneNumberInput(), setTimeout(() => {
isCountryInternalUpdate.value = !1;
}, 0);
}
const countriesList = computed(() => {
let list = getCountryList2(props.countryLocale, props.customCountriesList);
return props.onlyCountries && (list = list?.filter((country) => props.onlyCountries?.includes(country.code))), props.ignoredCountries && (list = list?.filter((country) => !props.ignoredCountries?.includes(country.code))), props.preferredCountries && (list = list?.sort((a, b) => {
const indexA = props.preferredCountries?.indexOf(a.code) ?? -1, indexB = props.preferredCountries?.indexOf(b.code) ?? -1;
return indexA >= 0 && indexB >= 0 ? indexA - indexB : indexA >= 0 ? -1 : indexB >= 0 ? 1 : 0;
})), list ?? [];
});
return watch(
() => props.modelValue,
(value, oldValue) => {
!isPhoneNumberInternalUpdate.value && value !== oldValue && value !== phoneNumber.value && onPhoneNumberChanged({ newPhoneNumber: value });
},
{ immediate: !0 }
), watch(
() => props.countryCode,
(value, oldValue) => {
!isCountryInternalUpdate.value && value && value !== oldValue && value !== selectedCountry.value && onCountryChanged({ countryCode: value });
},
{ immediate: !0 }
), watch(
results,
(value) => emits("data", value),
{ immediate: !0 }
), provide("mazInputPhoneNumberData", {
selectedCountry,
phoneNumber,
results
}), (_ctx, _cache) => (openBlock(), createElementBlock("div", {
id: unref(instanceId),
class: normalizeClass(["m-input-phone-number m-reset-css", [props.class, { "--block": __props.block }, __props.orientation ? `--${__props.orientation}` : void 0]]),
style: normalizeStyle(__props.style)
}, [
__props.hideCountrySelect ? createCommentVNode("", !0) : (openBlock(), createBlock(unref(MazSelectCountry), mergeProps({
key: 0,
id: `${unref(instanceId)}-country`,
class: "m-input-phone-number__country-select"
}, __props.countrySelectAttributes, {
"model-value": selectedCountry.value,
"option-input-value-key": __props.displayCountryName ? "name" : "dialCode",
color: __props.color,
size: __props.size,
locale: __props.countryLocale,
"countries-list": __props.customCountriesList,
"list-position": __props.listPosition,
"hide-flags": __props.hideFlags,
search: __props.search,
block: __props.block,
error: __props.error || (__props.validationError ? !!phoneNumber.value && !selectedCountry.value : !1),
success: __props.success || (__props.validationSuccess ? results.value?.isValid : !1),
translations: messages.value.countrySelect,
hint: phoneNumber.value && !selectedCountry.value ? messages.value.countrySelect.error : void 0,
options: countriesList.value,
disabled: __props.disabled,
"search-threshold": __props.searchThreshold,
"format-input-value": __props.displayCountryName || !selectedCountry.value ? void 0 : (value) => `+${value}`,
"show-code-in-list": __props.showCodeInList,
label: messages.value.countrySelect.placeholder,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => onCountryChanged({ countryCode: $event, selectPhoneNumber: !0 }))
}), {
"no-results": withCtx(() => [
renderSlot(_ctx.$slots, "no-results", {}, void 0, !0)
]),
"selector-flag": withCtx(({ countryCode: codeCountry }) => [
renderSlot(_ctx.$slots, "selector-flag", { countryCode: codeCountry }, void 0, !0)
]),
"country-list-flag": withCtx(({ isSelected, option }) => [
renderSlot(_ctx.$slots, "country-list-flag", {
countryCode: option.code,
option,
isSelected
}, void 0, !0)
]),
"country-list-code": withCtx(({ option }) => [
createElementVNode("span", _hoisted_2, " +" + toDisplayString(option.dialCode), 1)
]),
_: 3
}, 16, ["id", "model-value", "option-input-value-key", "color", "size", "locale", "countries-list", "list-position", "hide-flags", "search", "block", "error", "success", "translations", "hint", "options", "disabled", "search-threshold", "format-input-value", "show-code-in-list", "label"])),
createVNode(unref(PhoneInput), mergeProps({
id: `${unref(instanceId)}-phone`,
ref_key: "PhoneInputRef",
ref: PhoneInputRef,
class: "m-input-phone-number__phone-input",
"model-value": phoneNumber.value
}, { ..._ctx.$attrs, ...__props.phoneInputAttributes }, {
color: __props.color,
size: __props.size,
"auto-format": __props.autoFormat,
example: __props.example,
block: "",
disabled: __props.disabled,
name: __props.phoneInputAttributes.name,
"has-radius": !__props.hideCountrySelect,
success: __props.success || (__props.validationSuccess ? results.value.isValid : !1),
error: __props.error || (__props.validationError ? !!phoneNumber.value && !results.value.isValid : !1),
locales: messages.value.phoneInput,
label: __props.label,
placeholder: __props.placeholder,
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => onPhoneNumberChanged({ newPhoneNumber: $event }))
}), null, 16, ["id", "model-value", "color", "size", "auto-format", "example", "disabled", "name", "has-radius", "success", "error", "locales", "label", "placeholder"])
], 14, _hoisted_1));
}
}), MazInputPhoneNumber = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-2b9b89e5"]]);
export {
MazInputPhoneNumber as M,
useMazInputPhoneNumber as a,
useLibphonenumber as u
};