UNPKG

@salla.sa/twilight-components

Version:
339 lines (338 loc) 14.9 kB
/*! * Crafted with ❤ by Salla */ import { Host, h } from "@stencil/core"; // Icons import ArrowDown from "../../assets/svg/keyboard_arrow_down.svg"; import BellRing from "../../assets/svg/bell-ring.svg"; import OrderIcon from "../../assets/svg/box-bankers.svg"; import PendingOrdersIcon from "../../assets/svg/cart.svg"; import WishListIcon from "../../assets/svg/star.svg"; import ProfileIcon from "../../assets/svg/user-circle.svg"; import LogoutIcon from "../../assets/svg/send-out.svg"; import Cancel from "../../assets/svg/cancel.svg"; import Rate from "../../assets/svg/star2.svg"; import UserCircle from "../../assets/svg/user-circle.svg"; import WalletIcon from "../../assets/svg/wallet.svg"; import loyaltyProgramIcon from "../../assets/svg/gift.svg"; /** * @slot trigger - Replaces trigger widget, has replaceable props `{avatar}`, `{hello}`, `{first_name}`, `{last_name}`, `{icon}`. * @slot login-btn - Replaces the login button, it must be used with `salla.event.dispatch('login::open')` to open the login modal. */ export class SallaUserMenu { constructor() { this.items = { notifications: BellRing, orders: OrderIcon, pending_orders: PendingOrdersIcon, wishlist: WishListIcon, profile: ProfileIcon, wallet: WalletIcon, loyalty_program: loyaltyProgramIcon, }; this.accountLoading = false; this.opened = false; this.notifications = salla.lang.get('common.titles.notifications'); this.orders = salla.lang.get('common.titles.orders'); this.pending_orders = salla.lang.get('common.titles.pending_orders'); this.wishlist = salla.lang.get('common.titles.wishlist'); this.profile = salla.lang.get('common.titles.profile'); this.rating = salla.lang.get('common.titles.rating'); this.wallet = salla.lang.get('common.titles.wallet'); this.loyalty_program = salla.lang.get('pages.loyalty_program.loyalty_points'); this.logout = salla.lang.get('blocks.header.logout'); this.hello = salla.lang.get('pages.checkout.hello'); this.first_name = salla.storage.get('user.first_name') || ''; this.last_name = salla.storage.get('user.last_name') || ''; this.avatar = salla.storage.get('user.avatar') || salla.url.cdn('images/avatar.png', 40, 40); this.badges = { notifications: salla.helpers.number(salla.storage.get('user.notifications') || 0), pending_orders: salla.helpers.number(salla.storage.get('user.pending_orders') || 0), }; this.hasBadges = Number(salla.storage.get('user.pending_orders')) > 0 || Number(salla.storage.get('user.notifications')) > 0; /** * To display only the list without the dropdown functionality */ this.inline = false; /** * To display the trigger as an avatar only */ this.avatarOnly = false; /** * To display the dropdown header in mobile sheet */ this.showHeader = false; /** * To Make the dropdown menu relative to parent element or not */ this.relativeDropdown = false; this.onClickOutside = () => { this.opened = false; }; this.OrderUpdate = 0; // salla.auth.event.onLoggedIn(() => { // this.is_loggedIn = true // }) salla.lang.onLoaded(() => { this.notifications = salla.lang.get('common.titles.notifications'); this.orders = salla.lang.get('common.titles.orders'); this.pending_orders = salla.lang.get('common.titles.pending_orders'); this.wishlist = salla.lang.get('common.titles.wishlist'); this.profile = salla.lang.get('common.titles.profile'); this.hello = salla.lang.get('pages.checkout.hello'); this.rating = salla.lang.get('common.titles.rating'); this.wallet = salla.lang.get('common.titles.wallet'); this.loyalty_program = salla.lang.get('pages.loyalty_program.loyalty_points'); this.logout = salla.lang.get('blocks.header.logout'); }); //we need it only in theme-y if (this.host.hasAttribute('with-rating')) { this.items.rating = Rate; } //we need it to be the last item this.items.logout = LogoutIcon; salla.onReady(() => { if (salla.config.isGuest()) { return; } this.is_loggedIn = true; /** * Get Fresh Notifications In These Cases: * - is notification page, if user already changed the status of his orders (to reset notification badge) * - is pending orders page, if user already changed the status of his orders (to reset orders badge) * - is profile page, in case user changed his name or avatar, we need to update it * - half hour is passed from the last user data fetched * * //todo:: update the data in the storage in customer pages * //todo:: cover two requests in customer pages * //todo:: make sure to run this only after token is set */ const shouldFetchProfile = !this.inline && (salla.url.is_page('customer.notifications') || salla.url.is_page('customer.orders.index.pending') || salla.url.is_page('customer.profile') || (Date.now() - (salla.storage.get('user.fetched_at') || 0)) / 1000 / 60 > 30); if (shouldFetchProfile) { this.fetchFreshProfile(); } else { salla.event.on('profile::info.fetched', res => { this.updateProfileState(res); }); } }); } componentWillLoad() { return new Promise(resolve => salla.onReady(resolve)).then(() => { let trigger = this.host.querySelector('[slot="trigger"]'); this.triggerSlot = '<div class="s-user-menu-trigger"><div class="s-user-menu-avatar-wrap"><img class="s-user-menu-trigger-avatar" src="{avatar}" alt="{first_name}{last_name}" /></div><div class="s-user-menu-trigger-content"><span class="s-user-menu-trigger-hello">{hello}</span><p class="s-user-menu-trigger-name">{first_name} {last_name}</p></div> <i class="s-user-menu-trigger-icon">{icon}</i></div>'; if (!trigger) { return; } this.triggerSlot = trigger.innerHTML; trigger.innerHTML = this.replaceParams(trigger.innerHTML); }); } fetchFreshProfile() { //don't request fetchFreshProfile unless token is injected into the api if (!salla.api.token) { salla.log('trying to fetchFreshProfile before injected the token!!'); return; } salla.profile.api.info().then(res => { this.updateProfileState(res); }); } updateProfileState(res) { this.badges = { notifications: salla.helpers.number(res.data.notifications || 0), pending_orders: salla.helpers.number(res.data.pending_orders || 0), }; this.hasBadges = Number(res.data.pending_orders) > 0 || Number(res.data.notifications) > 0; this.first_name = res.data.first_name; this.last_name = res.data.last_name; this.avatar = res.data.avatar || salla.url.cdn('images/avatar.png', 40, 40); } async open(e) { this.opened = !this.opened; e.stopPropagation(); if (this.opened) { window.addEventListener('click', this.onClickOutside); } } menuItemClicked(event, item) { if (item[0] !== 'logout') { return; } event.preventDefault(); salla.auth.logout('sall-user-menu'); } replaceParams(body) { return body .replace(/\{hello\}/g, this.hello) .replace(/\{first_name\}/g, this.first_name) .replace(/\{last_name\}/g, this.last_name) .replace(/\{avatar\}/g, this.avatar) .replace(/\{icon\}/g, ArrowDown); } getTheHeader() { return (h("div", { class: { 's-user-menu-trigger-slot': true, 's-user-menu-red-dot': this.hasBadges, 's-user-menu-trigger-avatar-only': this.avatarOnly, }, id: "trigger-slot", onClick: e => this.open(e), innerHTML: this.replaceParams(this.triggerSlot) })); } getMenuItem(item, i) { //todo:: enhancement support slot here if (item[0] === 'wallet' && !window.can_access_wallet) return; return (h("li", { class: { 's-user-menu-dropdown-item': true, 's-user-menu-dropdown-item-logout': i + 1 == Object.entries(this.items).length, } }, h("a", { href: salla.url.get(item[0]), onClick: event => this.menuItemClicked(event, item) }, h("i", { innerHTML: item[1] }, " "), h("span", { class: "s-user-menu-dropdown-item-title" }, this[item[0]]), !['٠', '0', undefined].includes(this.badges[item[0]]) ? (h("span", { class: "s-user-menu-dropdown-item-badge" }, this.badges[item[0]])) : ('')))); } componentShouldUpdate() { if (!this.opened) { window.removeEventListener('click', this.onClickOutside); } } render() { if (!this.is_loggedIn) { return (h(Host, null, h("slot", { name: "login-btn" }, h("button", { class: "s-user-menu-login-btn", onClick: () => salla.event.dispatch('login::open'), innerHTML: UserCircle })))); } if (this.inline) { return (h(Host, null, h("ul", { class: "s-user-menu-inline" }, Object.entries(this.items).map((item, i) => this.getMenuItem(item, i))))); } return (h(Host, null, h("div", { class: { 's-user-menu-wrapper': true, 's-user-menu-relative-dropdown': this.relativeDropdown, } }, this.getTheHeader(), h("div", { class: { 's-user-menu-toggler': true, opened: this.opened } }, h("div", { class: "s-user-menu-dropdown", onClick: e => e.stopPropagation() }, this.showHeader ? (h("div", { class: "s-user-menu-dropdown-header" }, h("img", { src: this.avatar, alt: `${this.first_name} ${this.last_name}` }), h("div", { class: "s-user-menu-dropdown-header-content" }, h("span", null, this.hello), h("p", null, this.first_name, " ", this.last_name)), h("button", { class: "s-user-menu-dropdown-header-close", innerHTML: Cancel, onClick: () => (this.opened = false) }))) : (''), h("ul", { class: "s-user-menu-dropdown-list" }, Object.entries(this.items).map((item, i) => this.getMenuItem(item, i)))))))); } componentDidLoad() { var _a; //make sure to load the avatar if it's lazy, we use it in Y (_a = document.lazyLoadInstance) === null || _a === void 0 ? void 0 : _a.update(this.host.querySelectorAll('.lazy')); } static get is() { return "salla-user-menu"; } static get originalStyleUrls() { return { "$": ["salla-user-menu.scss"] }; } static get styleUrls() { return { "$": ["salla-user-menu.css"] }; } static get properties() { return { "inline": { "type": "boolean", "attribute": "inline", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "To display only the list without the dropdown functionality" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "avatarOnly": { "type": "boolean", "attribute": "avatar-only", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "To display the trigger as an avatar only" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "showHeader": { "type": "boolean", "attribute": "show-header", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "To display the dropdown header in mobile sheet" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "relativeDropdown": { "type": "boolean", "attribute": "relative-dropdown", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "To Make the dropdown menu relative to parent element or not" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" } }; } static get states() { return { "accountLoading": {}, "opened": {}, "notifications": {}, "orders": {}, "pending_orders": {}, "wishlist": {}, "profile": {}, "rating": {}, "wallet": {}, "loyalty_program": {}, "logout": {}, "hello": {}, "first_name": {}, "last_name": {}, "avatar": {}, "is_loggedIn": {}, "badges": {}, "hasBadges": {}, "OrderUpdate": {} }; } static get elementRef() { return "host"; } } //# sourceMappingURL=salla-user-menu.js.map