UNPKG

@ribajs/shopify

Version:

Shopify extension for Riba.js

257 lines (227 loc) 6.74 kB
import { Component, ScopeBase } from "@ribajs/core"; import { EventDispatcher } from "@ribajs/events"; import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; import { Linklist, LinklistLink } from "../../interfaces/index.js"; export interface Scope extends ScopeBase { // Properties /** The linklist as a json string */ linklist?: Linklist; /** Sets the linklist by his name */ handle?: string; /** If the navigation should be displayed as pills */ pills: boolean; /** If the navigation should be displayed as vertically */ vertical: boolean; /** Set this option to true if toggleable links should be automatically collapse when a page changes */ collapseOnNewPage: boolean; /** Set this option to true if toggleable links should be automatically open when a child link is active */ showOnActiveChild: boolean; // Methods toggle: ShopifyLinklistComponent["toggle"]; collapse: ShopifyLinklistComponent["collapse"]; collapseAll: ShopifyLinklistComponent["collapseAll"]; show: ShopifyLinklistComponent["show"]; showAll: ShopifyLinklistComponent["showAll"]; } export interface State { url: string; namespace?: string; } /** * shopify-filter */ export class ShopifyLinklistComponent extends Component { public static tagName = "shopify-linklist"; protected mainDispatcher = new EventDispatcher("main"); protected autobind = true; static get observedAttributes(): string[] { return [ "linklist", "handle", "name", "pills", "vertical", "collapse-on-new-page", "show-on-child-url", ]; } public scope: Scope = { // properties pills: false, vertical: false, collapseOnNewPage: true, showOnActiveChild: true, // methods toggle: this.toggle, collapse: this.collapse, collapseAll: this.collapseAll, show: this.show, showAll: this.showAll, }; constructor() { super(); this.mainDispatcher.on("newPageReady", this.onNewPageReady, this); } protected connectedCallback() { super.connectedCallback(); this.init(ShopifyLinklistComponent.observedAttributes); } protected disconnectedCallback() { super.disconnectedCallback(); this.mainDispatcher.off("newPageReady", this.onNewPageReady, this); } public toggle(link: LinklistLink) { link.collapsed = !link.collapsed; } public collapse(link: LinklistLink) { link.collapsed = true; } public show(link: LinklistLink) { link.collapsed = false; } public showAll() { if (this.scope.linklist) { this.showAllByLinks(this.scope.linklist.links); } } public collapseAll() { if (this.scope.linklist && this.scope.linklist.links) { this.collapseAllByLinks(this.scope.linklist.links); } } /** * Show (uncollapse) link by child url */ public showByChildUrl(url: string) { if (this.scope.linklist && this.scope.linklist.links) { for (const link of this.scope.linklist.links) { for (const sublink of link.links) { if (sublink.url === url) { this.show(link); break; } for (const subsublink of sublink.links) { if (subsublink.url === url) { this.show(link); this.show(sublink); break; } } } } } } protected async attributeChangedCallback( name: string, oldValue: any, newValue: any, namespace: string | null, ) { // injects the changed attributes to scope super.attributeChangedCallback(name, oldValue, newValue, namespace); // set linklist by handle if (name === "handle" || name === "name") { if ( (window as any).model && (window as any).model.system && (window as any).model.system.linklists && (window as any).model.system.linklists[newValue] ) { this.scope.linklist = (window as any).model.system.linklists[newValue]; } else { throw new Error( `Linklist not found! \nNote: The linklist must be available under "window.model.system.linklists['${newValue}']" to set it using his handle.`, ); } } if (name === "linklist") { if (typeof newValue === "object") { // if object is in form of "main-menu": {...} if (Object.keys(newValue).length === 1) { newValue = newValue[Object.keys(newValue)[0]]; } } this.scope.linklist = newValue; } } protected collapseAllByLinks(links: LinklistLink[]) { if (this.scope.linklist && this.scope.linklist.links) { for (const link of links) { if (link.collapseable) { link.collapsed = true; } if (link.links) { this.collapseAllByLinks(link.links); } } } } protected showAllByLinks(links: LinklistLink[]) { if (this.scope.linklist && this.scope.linklist.links) { for (const link of links) { if (link.collapseable) { link.collapsed = false; } if (link.links) { this.collapseAllByLinks(link.links); } } } } protected onNewPageReady( viewId: string, currentStatus: State /*, prevStatus: State, container: HTMLElement, newPageRawHTML: string, dataset: any, isFirstPageLoad: boolean */, ) { const url = currentStatus.url; if (this.scope.collapseOnNewPage) { this.collapseAll(); } if (this.scope.showOnActiveChild) { this.showByChildUrl(url); } } protected async beforeBind(): Promise<any> { await super.beforeBind(); if (this.scope.linklist && this.scope.linklist.links) { this.transformLinks(this.scope.linklist.links); if (this.scope.collapseOnNewPage) { this.collapseAll(); } } if (this.scope.showOnActiveChild) { this.showByChildUrl(window.location.pathname); } } /** * Checks if link are collapseable and set initializes corresponding attributes */ protected transformLinks(links: LinklistLink[]) { for (const link of links) { if (link.url === "#collapse") { link.collapseable = true; link.collapsed = true; } else { link.collapseable = false; link.collapsed = false; } if (link.links) { this.transformLinks(link.links); } } } protected requiredAttributes(): string[] { return ["linklist"]; } /** * Only set the component template if there no childs already */ protected async template() { if (hasChildNodesTrim(this)) { return null; } else { const { default: template } = await import("./linklist.component.html?raw"); return template; } } }