@salla.sa/twilight-components
Version:
Salla Web Component
314 lines (308 loc) • 14.2 kB
JavaScript
/*!
* Crafted with ❤ by Salla
*/
import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client';
import { M as MailIcon } from './mail.js';
import { d as defineCustomElement$4 } from './salla-button2.js';
import { d as defineCustomElement$3 } from './salla-loading2.js';
import { d as defineCustomElement$2 } from './salla-modal2.js';
var AndroidPhoneIcon = `<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>android-phone</title>
<path d="M22.667 0h-13.333c-2.941 0-5.333 2.392-5.333 5.333v21.333c0 2.941 2.392 5.333 5.333 5.333h13.333c2.941 0 5.333-2.392 5.333-5.333v-21.333c0-2.941-2.392-5.333-5.333-5.333zM25.333 26.667c0 1.471-1.196 2.667-2.667 2.667h-13.333c-1.471 0-2.667-1.196-2.667-2.667v-2.667h18.667zM25.333 21.333h-18.667v-16c0-1.471 1.196-2.667 2.667-2.667h13.333c1.471 0 2.667 1.196 2.667 2.667zM13.333 28h5.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333h-5.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333z"></path>
</svg>
`;
const sallaVerifyCss = "salla-verify{display:block}.s-verify-input{}.s-verify-input::-webkit-outer-spin-button,.s-verify-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.s-verify-input[type=number]{-moz-appearance:textfield}";
const SallaVerify$1 = /*@__PURE__*/ proxyCustomElement(class SallaVerify extends HTMLElement {
constructor() {
super();
this.__registerHost();
this.verified = createEvent(this, "verified", 7);
this.translationLoaded = false;
/**
* Should render component without modal
*/
this.display = 'modal';
/**
* Verifying method
*/
this.type = 'mobile';
/**
* should auto reloading the page after success verification
*/
this.autoReload = true;
/**
* Once the api verify success, it will be login the customer in web pages
*/
this.supportWebAuth = true;
this.resendAfter = 30;
/**
* to use: `salla.api.auth.verify` or `salla.profile.verify`
*/
this.isProfileVerify = false;
salla.lang.onLoaded(() => {
var _a;
this.translationLoaded = true;
this.title = salla.lang.get('pages.profile.verify_title') + salla.lang.get('common.elements.' + this.type);
(_a = this.modal) === null || _a === void 0 ? void 0 : _a.setTitle(this.title);
});
if (this.display == 'inline') {
this.modal = { open: () => '', close: () => '', setTitle: () => '' };
return;
}
//todo:: change this way, now we fire the event from the backend, we should listen to salla.profile.event.onUpdated
salla.event.on('profile::verification', data => {
var _a;
let payload = Array.isArray(data) ? data[0] : data;
this.isProfileVerify = true;
this.open(payload);
this.title = salla.lang.get('pages.profile.verify_title') + salla.lang.get('common.elements.' + payload.type);
(_a = this.modal) === null || _a === void 0 ? void 0 : _a.setTitle(this.title);
});
salla.event.on('modalClosed', () => {
this.resendAfter = 0;
this.timer.innerHTML = '30 : 00';
});
}
splitNumber(e) {
this.resetError();
let data = e.data || e.target.value; // Chrome doesn't get the e.data, it's always empty, fallback to value then.
if (!data)
return; // Shouldn't happen, just in case.
if (data.length === 1)
return; // Here is a normal behavior, not a paste action.
this.modifyNext(e.target, data);
}
modifyNext(el, data) {
el.value = data[0]; // Apply first item to first input
data = data.substring(1); // remove the first char.
if (el.nextElementSibling && data.length) {
// Do the same with the next element and next data
this.modifyNext(el.nextElementSibling, data);
}
else if (!el.nextElementSibling && data.length === 0) {
el.focus();
}
else if (el.nextElementSibling && data.length === 0) {
el.nextElementSibling.focus();
}
}
checkAllInputs() {
let allFilled = true;
for (let i = 0; i < this.otpInputs.length; i++) {
if (this.otpInputs[i].value === '') {
allFilled = false;
}
}
return allFilled;
}
handleKeyUp(ev) {
var _a, _b, _c, _d;
this.resetError();
if (['Alt', 'Shift', 'Control', 'AltGraph', 'Ctrl'].includes(ev.key)) {
return;
}
let key = ev.keyCode || ev.charCode;
if (ev.target.value) {
(_a = ev.target.nextElementSibling) === null || _a === void 0 ? void 0 : _a.focus();
(_b = ev.target.nextElementSibling) === null || _b === void 0 ? void 0 : _b.select();
}
else if ([8, 46].includes(key)) {
(_c = ev.target.previousElementSibling) === null || _c === void 0 ? void 0 : _c.focus();
(_d = ev.target.previousElementSibling) === null || _d === void 0 ? void 0 : _d.select();
}
// If the target is populated to quickly, value length can be > 1
if (ev.target.value.length > 1) {
this.splitNumber(ev);
}
}
handlePaste(ev) {
this.resetError();
const clipboardText = salla.helpers.number(ev.clipboardData.getData('text')) || '';
let text = clipboardText.replace(/[^0-9]/g, '');
text = text.substring(0, this.otpInputs.length);
this.otpInputs.forEach(input => input.value = '');
this.modifyNext(this.otpInputs[0], text);
}
handleInput(ev) {
this.resetError();
salla.helpers.inputDigitsOnly(ev.target);
// check if all otpInputs has values then send the request
if (this.checkAllInputs()) {
setTimeout(() => {
this.toggleOTPSubmit();
}, 100);
}
}
resetError() {
this.hasError = false;
this.errorMessage = '';
}
handleFocus(ev) {
var _a;
// If the focus element is the first one, do nothing
if (ev.target === this.firstOtpInput)
return;
// If value of input 1 is empty, focus it.
if (((_a = this.firstOtpInput) === null || _a === void 0 ? void 0 : _a.value) == '') {
this.firstOtpInput.focus();
}
// If value of a previous input is empty, focus it.
// To remove if you don't wanna force user respecting the fields order.
if (ev.target.previousElementSibling.value == '') {
ev.target.previousElementSibling.focus();
}
}
/**
* Get current code
* @return {string}
*/
async getCode() {
return this.code.value;
}
/**
* Open verifying modal
* @param data
*/
async open(data) {
var _a, _b;
this.data = data;
this.data.type = this.data.type || this.type;
this.type = this.data.type;
this.resendTimer();
this.otpInputs = this.body.querySelectorAll('.s-verify-input');
this.firstOtpInput = this.body.querySelector('#otp-1');
this.reset();
this.resetError();
this.display == 'modal' && ((_a = this.modal) === null || _a === void 0 ? void 0 : _a.setTitle(this.title));
this.modal.open();
(_b = this.firstOtpInput) === null || _b === void 0 ? void 0 : _b.addEventListener('input', e => this.splitNumber(e));
// focus the first input after opening the modal
setTimeout(() => this.otpInputs[0].focus(), 100);
}
toggleOTPSubmit() {
let otp = [];
this.otpInputs.forEach(input => input.value && otp.push(input.value));
this.code.value = otp.join('');
if (otp.length === 4) {
this.btn.disable();
this.btn.click();
return;
}
this.btn.enable();
}
reset() {
this.otpInputs.forEach((input) => input.value = '');
this.code.value = '';
this.otpInputs[0].focus();
}
resendTimer() {
this.resendMessage.style.display = 'block';
this.resend.style.display = 'none';
this.resendAfter = 30;
let timerId = setInterval(() => {
if (this.resendAfter <= 0) {
clearInterval(timerId);
this.resend.style.display = 'block';
this.resendMessage.style.display = 'none';
}
else {
this.timer.innerHTML = `${this.resendAfter >= 10 ? this.resendAfter : '0' + this.resendAfter} : 00`;
this.resendAfter--;
}
}, 1000);
}
resendCode() {
return this.btn.stop()
.then(() => this.btn.disable())
.then(() => {
this.otpInputs.forEach(input => input.value = '');
this.otpInputs[0].focus();
})
.then(() => salla.api.auth.resend(this.data))
.finally(() => this.resendTimer());
}
submit() {
//if code not 4 digits, focus on the after filled input,
if (this.code.value.length < 4) {
this.otpInputs[this.code.value.length].focus();
salla.log('Trying to submit without 4 digits!');
return;
}
let data = Object.assign({ code: this.code.value }, this.data);
return this.btn.load()
.then(() => this.btn.disable())
.then(() => this.isProfileVerify ? salla.profile.verify(data) : salla.auth.verify(data, this.supportWebAuth))
.then(response => this.verified.emit(response))
.then(() => this.btn.stop() && this.btn.disable())
.then(() => this.modal.close())
.then(() => this.autoReload && window.location.reload())
.catch((error) => {
var _a, _b, _c;
this.hasError = true;
this.errorMessage = ((_c = (_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) || salla.lang.get('common.errors.error_occurred');
if (!error.response) {
console.log('Unexpected error', error);
}
else {
salla.logger.error(error);
}
this.btn.stop() && this.btn.enable() && this.reset();
});
}
render() {
return this.display == 'inline' ? h(Host, null, this.myBody()) :
h("salla-modal", { width: "xs", class: "s-verify", ref: modal => this.modal = modal, "modal-title": this.title }, h("span", { slot: 'icon', class: "s-verify-header-icon", innerHTML: this.type == "mobile" ? AndroidPhoneIcon : MailIcon }), this.myBody());
}
myBody() {
return (h("div", { class: "s-verify-body", ref: body => this.body = body }, h("div", { class: "s-verify-message", innerHTML: salla.lang.get('pages.profile.verify_message') }), h("slot", { name: "mobile" }), h("slot", { name: "email" }), h("input", { type: "hidden", name: "code", maxlength: "4", required: true, ref: code => this.code = code }), h("div", { class: { "s-verify-codes": true, "has-error": this.hasError }, dir: "ltr" }, [1, 2, 3, 4].map((i) => h("input", { type: "number", autocomplete: "one-time-code", pattern: "[0-9]*", inputmode: "numeric", maxlength: "1", value: "", id: `otp-${i}`, class: { "s-verify-input": true, "s-has-error": this.hasError }, onInput: e => this.handleInput(e), onPaste: e => this.handlePaste(e), onKeyUp: e => this.handleKeyUp(e), onFocus: e => this.handleFocus(e), required: true }))), this.hasError && this.errorMessage ? h("span", { class: "s-verify-error-message" }, this.errorMessage) : '', h("div", { slot: "footer", class: "s-verify-footer" }, h("salla-button", { class: "s-verify-submit", "loader-position": 'center', disabled: true, onClick: () => this.submit(), ref: b => this.btn = b }, salla.lang.get('pages.profile.verify')), h("p", { class: "s-verify-resend-message", ref: el => this.resendMessage = el }, salla.lang.get('blocks.header.resend_after'), h("b", { class: "s-verify-timer", ref: el => this.timer = el })), h("a", { href: "#", class: "s-verify-resend", onClick: () => this.resendCode(), ref: el => this.resend = el }, salla.lang.get('blocks.comments.submit'))), h("slot", { name: "after-footer" })));
}
get host() { return this; }
static get style() { return sallaVerifyCss; }
}, [4, "salla-verify", {
"display": [1],
"type": [1025],
"autoReload": [4, "auto-reload"],
"supportWebAuth": [4, "support-web-auth"],
"translationLoaded": [32],
"title": [32],
"resendAfter": [32],
"hasError": [32],
"errorMessage": [32],
"isProfileVerify": [32],
"getCode": [64],
"open": [64]
}]);
function defineCustomElement$1() {
if (typeof customElements === "undefined") {
return;
}
const components = ["salla-verify", "salla-button", "salla-loading", "salla-modal"];
components.forEach(tagName => { switch (tagName) {
case "salla-verify":
if (!customElements.get(tagName)) {
customElements.define(tagName, SallaVerify$1);
}
break;
case "salla-button":
if (!customElements.get(tagName)) {
defineCustomElement$4();
}
break;
case "salla-loading":
if (!customElements.get(tagName)) {
defineCustomElement$3();
}
break;
case "salla-modal":
if (!customElements.get(tagName)) {
defineCustomElement$2();
}
break;
} });
}
const SallaVerify = SallaVerify$1;
const defineCustomElement = defineCustomElement$1;
export { SallaVerify, defineCustomElement };
//# sourceMappingURL=salla-verify.js.map
//# sourceMappingURL=salla-verify.js.map