UNPKG

@components-1812/offcanvas

Version:

A web component for custom offcanvas, lateral panel

161 lines (126 loc) 5.42 kB
class OffcanvasBase extends HTMLElement { static VERSION = '0.0.3'; static DEFAULT_TAG_NAME = 'custom-offcanvas'; static DEFAULT_ICONS = { 'close-button': /*svg*/`<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/> </svg>`, 'handle-button': /*svg*/`<svg width="20" height="20" fill="currentColor" viewBox="0 0 16 16" data-rotate-icon> <path fill-rule="evenodd" d="M6 8a.5.5 0 0 0 .5.5h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L12.293 7.5H6.5A.5.5 0 0 0 6 8m-2.5 7a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5"/> </svg>` }; /** * Define the custom element and add stylesheets to it if not already defined. * * @param {string} [tagName=Offcanvas.DEFAULT_TAG_NAME] - The tag name to define the custom element. * @param {{links:string[], adopted:CSSStyleSheet[], raw:string[]}} [stylesSheets={}] - An object with stylesheets to add to the element. It contains three properties: `links`, `adopted`, and `raw`. * @returns {void} */ static define(tagName, stylesSheets = {}){ tagName ??= this.DEFAULT_TAG_NAME; if(!window.customElements.get(tagName)){ //Add new styles for(const key of ['links', 'adopted', 'raw']) { if(Array.isArray(stylesSheets[key])){ this.stylesSheets[key].push(...stylesSheets[key]); } } window.customElements.define(tagName, this); } else { console.warn(`Custom element with tag name "${tagName}" is already defined.`); } } //MARK: Styles /** * @type {{links:string[], adopted:CSSStyleSheet[], raw:string[]}} Stylesheets to be applied to the component */ static stylesSheets = { links: [], adopted: [], raw: [], }; /** * Applies the given stylesheets to the component. * @param {{links:string[], adopted:CSSStyleSheet[], raw:string[]}} [stylesSheets=this.constructor.stylesSheets] * - An object with stylesheets to be applied to the component. It contains three properties: `links`, `adopted`, and `raw`. * @returns {void} * @fires ready-links */ applyStylesSheets(stylesSheets = {}){ //Add new styles for(const key of ['links', 'adopted', 'raw']) { if(Array.isArray(stylesSheets[key])){ this.constructor.stylesSheets[key].push(...stylesSheets[key]); } } //Get styles const {links, adopted, raw} = this.constructor.stylesSheets; const $styles = document.createElement('div'); $styles.classList.add('styles'); $styles.style.display = 'none'; //Links const linksPromises = links.map((styleSheet) => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = styleSheet; const { promise, resolve, reject } = Promise.withResolvers(); //If it's already loaded (rare in shadow DOM, but possible) if(link.sheet){ resolve({ link, href: styleSheet, status: 'loaded' }); } else { link.addEventListener('load', () => resolve({ link, href: styleSheet, status: 'loaded' })); link.addEventListener('error', () => reject({ link, href: styleSheet, status: 'error' })); } $styles.append(link); return promise; }); this.removeAttribute('ready-links'); Promise.allSettled(linksPromises).then((results) => { this.dispatchEvent( new CustomEvent('ready-links', { detail: { results: results.map((r) => r.value || r.reason) }, }) ); this.setAttribute('ready-links', ''); }); //Raw css raw.forEach((style) => { const styleElement = document.createElement('style'); styleElement.textContent = style; $styles.append(styleElement); }); //Clear previous styles this.shadowRoot.querySelector('.styles')?.remove(); //Add new styles this.shadowRoot.prepend($styles); //Adopted this.shadowRoot.adoptedStyleSheets = adopted; }; constructor() { super(); this.attachShadow({ mode: 'open' }); } //MARK: Getters and Setters set variant(value) { value ? this.setAttribute('variant', value) : this.removeAttribute('variant'); } get variant() { return this.getAttribute('variant'); } set open(value) { this.toggleAttribute('open', value); } get open() { return this.hasAttribute('open'); } set handleButton(value){ this.toggleAttribute('handle-button', value); } get handleButton(){ return this.hasAttribute('handle-button'); } } export {OffcanvasBase}; export default OffcanvasBase;