@salla.sa/twilight-components
Version:
Salla Web Component
326 lines (325 loc) • 12.7 kB
JavaScript
/*!
* Crafted with ❤ by Salla
*/
import { Host, h } from "@stencil/core";
export class SallaTelInput {
/**
* Lazy load intl-tel-input library
* This reduces initial bundle size by ~80-90KB
*/
async loadTelInput() {
if (this.TelInput)
return;
try {
const telInputModule = await import('intl-tel-input');
this.TelInput = telInputModule.default;
}
catch (error) {
console.error('Failed to load Tel Input:', error);
salla.notify?.error?.('Failed to load phone input. Please refresh the page.');
throw error;
}
}
constructor() {
/**
* input name
*/
this.name = 'phone';
/**
* input name
*/
this.disabled = false;
/**
* Current country_code
*/
this.countryCode = salla.config.get('user.country_code', 'SA') || 'SA';
this.countryCodeLabel = salla.lang.get('common.country_code');
this.mobileLabel = salla.lang.get('common.elements.mobile');
this.tooShort = salla.lang.get('common.errors.too_short', { attribute: this.mobileLabel });
this.tooLong = salla.lang.get('common.errors.too_long', { attribute: this.mobileLabel });
this.invalidCountryCode = salla.lang.get('common.errors.invalid_value', { attribute: this.countryCodeLabel });
this.invalidNumber = salla.lang.get('common.errors.invalid_value', { attribute: this.mobileLabel });
this.errorMap = [this.invalidNumber, this.invalidCountryCode, this.tooShort, this.tooLong, this.invalidNumber];
salla.lang.onLoaded(() => {
this.mobileLabel = salla.lang.get('common.elements.mobile');
this.countryCodeLabel = salla.lang.get('common.elements.country_code');
this.invalidNumber = salla.lang.get('common.errors.invalid_value', { attribute: this.mobileLabel });
this.invalidCountryCode = salla.lang.get('common.errors.invalid_value', { attribute: this.countryCodeLabel });
this.tooShort = salla.lang.get('common.errors.too_short', { attribute: this.mobileLabel });
this.tooLong = salla.lang.get('common.errors.too_long', { attribute: this.mobileLabel });
this.mobileRequired = salla.lang.get('common.errors.field_required', { attribute: this.mobileLabel });
this.errorMap = [this.invalidNumber, this.invalidCountryCode, this.tooShort, this.tooLong, this.invalidNumber];
});
}
/**
* Get current values
* @return {{mobile:number,countryCode:'SA'|string}}
*/
async getValues() {
return {
[this.name]: this.phone = this.phoneInput.value,
countryCode: this.countryCode = this.countryCodeInput.value,
countryKey: this.host.querySelector('.iti__selected-dial-code').innerText
};
}
/**
* Is current data valid or not
* @return {boolean}
*/
async isValid() {
this.reset();
if (this.iti.isValidNumber())
return true;
if (!this.phoneInput.value.trim()) {
this.phoneInput.classList.add("s-has-error");
this.errorMsg.innerText = this.mobileRequired || 'The mobile is required';
return;
}
this.phoneInput.classList.add("s-has-error");
let errorCode = this.iti.getValidationError();
this.errorMsg.innerText = this.errorMap[errorCode] || '';
salla.logger.info('Phone number (' + this.countryCode + ' - ' + this.phone + ') is not valid, error code ' + errorCode);
return false;
}
async initTelInput() {
// Load intl-tel-input before initializing
await this.loadTelInput();
salla.helpers.inputDigitsOnly(this.phoneInput);
this.iti = this.TelInput(this.phoneInput, {
initialCountry: this.countryCode?.toLowerCase() || 'sa',
preferredCountries: ['sa', 'ae', 'kw', 'bh', 'qa', 'iq', 'om', 'ye', 'eg', 'jo', 'ps', 'sd', 'lb', 'dz', 'tn', 'ma', 'ly'],
excludeCountries: ['sy', 'ir', 'cu', 'kp'],
formatOnDisplay: false,
separateDialCode: true,
autoPlaceholder: 'aggressive',
utilsScript: 'https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/21.2.7/js/utils.min.js',
dropdownContainer: this.host
});
this.autofocus && this.phoneInput.focus();
this.phoneInput.addEventListener("countrychange", () => {
let data = this.iti.getSelectedCountryData();
let value = data.iso2.toUpperCase();
this.countryCodeInput.value = value;
this.countryCode = value;
this.phoneEntered.emit({ number: this.phone, country_code: value });
});
// on blur: validate
// this.phoneInput.addEventListener('blur', () => this.isValid());
// on keyup / change flag: reset
this.phoneInput.addEventListener('input', (e) => {
salla.helpers.inputDigitsOnly(e.target);
this.reset();
this.phoneEntered.emit({ number: e.target.value, country_code: this.countryCode });
});
}
reset() {
this.phoneInput.classList.remove("s-has-error");
this.errorMsg.innerHTML = "";
}
;
handleCountryInput(event) {
if (!!this.phone) {
this.phoneEntered.emit({ number: event.target.value, country_code: this.countryCode });
}
}
render() {
return (h(Host, { key: '5b795b0844e986b7d0593b186eaf855ea1ad5823', class: "s-tel-input" }, h("input", { key: '699ff801a8b2effdadcbcf6f9389e20e621a9e91', type: "tel", disabled: this.disabled, name: this.name, value: this.phone, onChange: (event) => this.handleCountryInput(event), ref: el => this.phoneInput = el, enterkeyhint: "next", autocomplete: "tel", class: "s-tel-input-control tel-input s-ltr" }), h("span", { key: 'cdb37fbc3f0d914de7a70bf2cf6daddc0f46e739', class: "s-tel-input-error-msg", ref: el => this.errorMsg = el }), h("input", { key: '1a3a004ef9e663c040c7fb6b7c9fad55cfef67c3', type: "hidden", name: "country_code", value: this.countryCode, ref: el => this.countryCodeInput = el, class: "country_code" })));
}
componentDidLoad() {
this.initTelInput();
}
static get is() { return "salla-tel-input"; }
static get originalStyleUrls() {
return {
"$": ["salla-tel-input.scss"]
};
}
static get styleUrls() {
return {
"$": ["salla-tel-input.css"]
};
}
static get properties() {
return {
"phone": {
"type": "string",
"attribute": "phone",
"mutable": true,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Current mobile number"
},
"getter": false,
"setter": false,
"reflect": false
},
"autofocus": {
"type": "boolean",
"attribute": "autofocus",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Automatically focus telephone input"
},
"getter": false,
"setter": false,
"reflect": true
},
"name": {
"type": "string",
"attribute": "name",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "input name"
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "'phone'"
},
"disabled": {
"type": "boolean",
"attribute": "disabled",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "input name"
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "false"
},
"countryCode": {
"type": "string",
"attribute": "country-code",
"mutable": true,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Current country_code"
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "salla.config.get('user.country_code', 'SA') || 'SA'"
}
};
}
static get states() {
return {
"mobileRequired": {},
"countryCodeLabel": {},
"mobileLabel": {},
"tooShort": {},
"tooLong": {},
"invalidCountryCode": {},
"invalidNumber": {},
"errorMap": {}
};
}
static get events() {
return [{
"method": "phoneEntered",
"name": "phoneEntered",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Event emmitted when user enters a phone number."
},
"complexType": {
"original": "Phone",
"resolved": "Phone",
"references": {
"Phone": {
"location": "import",
"path": "./interfaces",
"id": "src/components/salla-tel-input/interfaces.ts::Phone"
}
}
}
}];
}
static get methods() {
return {
"getValues": {
"complexType": {
"signature": "() => Promise<{ [x: string]: any; countryCode: string; countryKey: any; }>",
"parameters": [],
"references": {
"Promise": {
"location": "global",
"id": "global::Promise"
}
},
"return": "Promise<{ [x: string]: any; countryCode: string; countryKey: any; }>"
},
"docs": {
"text": "Get current values",
"tags": [{
"name": "return",
"text": undefined
}]
}
},
"isValid": {
"complexType": {
"signature": "() => Promise<boolean>",
"parameters": [],
"references": {
"Promise": {
"location": "global",
"id": "global::Promise"
}
},
"return": "Promise<boolean>"
},
"docs": {
"text": "Is current data valid or not",
"tags": [{
"name": "return",
"text": undefined
}]
}
}
};
}
static get elementRef() { return "host"; }
}