export class NavMenu extends HTMLElement { textByLang = { en: { pages: 'Pages', localStorage: 'Local storage', full: 'full', }, es: { pages: 'Páginas', localStorage: 'Almacenamiento local', full: 'lleno', }, } constructor() { super() this.attachShadow({mode: 'open'}) this.language = navigator.language this.pages = [] this.handleLinks = false } connectedCallback() { const style = document.createElement('style') style.textContent = ` :host { display: flex; flex-direction: column; color: #000; height: 100dvh; max-height: 100dvh; } .location-bar { padding: 5px 25px; display: flex; flex-direction: row; } .location-box { border: 2px solid #aaa; border-radius: 3px; flex-grow: 1; padding: 5px; font-size: 14px; } h1 { width: 100%; text-align: center; font-size: 18px; margin: 0; padding: 5px; } .page-list { flex-grow: 1; overflow-y: auto; padding-bottom: 50px; } a { display: block; margin: 8px 10px; } .footer { padding: 3px 5px; } ` this.shadowRoot.append(style) this.locationBar = document.createElement( 'div' ) this.locationBar.classList.add( 'location-bar' ) this.locationBox = document.createElement( 'div' ) this.locationBox.classList.add( 'location-box' ) this.locationBar.append(this.locationBox) this.locationBox.addEventListener( 'click', () => { this.dispatchEvent(new CustomEvent( 'click-location', {bubbles: true} )) } ) this.shadowRoot.append(this.locationBar) this.renderLocation() this.header = document.createElement('h1') this.header.innerText = this.text.pages this.shadowRoot.append(this.header) this.pageList = document.createElement('div') this.pageList.classList.add('page-list') this.shadowRoot.append(this.pageList) this.renderPageList() this.footer = document.createElement('div') this.footer.classList.add('footer') this.shadowRoot.append(this.footer) this.renderFooter() this.shadowRoot.addEventListener('click', e => { if (e.target.classList.contains('page')) { if (this.handleLinks) { e.preventDefault() } this.dispatchEvent(new CustomEvent( 'close-menu', {bubbles: true} )) if (this.handleLinks) { const hash = '#' + e.target.innerText location.hash = hash } } }) } get language() { return this._language } set language(language) { this._language = language this.text = this.textByLang[ this.langEs ? 'es' : 'en' ] } get langEs() { return /^es\b/.test(this.language) } set location(location) { this._location = location if (this.locationBox) { this.renderLocation() } } get location() { return this._location } renderLocation() { this.locationBox.innerText = this.location } set pages(pages) { this._pages = pages if (this.pageList) { this.renderPageList() } } get pages() { return this._pages } renderPageList() { const els = this.pages.map(page => { const el = document.createElement('a') el.href = `#${page.replace(/^#/, '%23')}` el.innerText = page el.classList.add('page') return el }) this.pageList.replaceChildren(...els) } set storageUse(n) { this._storageUse = n if (this.footer) { this.renderFooter() } } get storageUse() { return this._storageUse } renderFooter() { if (this.storageUse !== undefined) { const fmt = new Intl.NumberFormat( this.language, { style: 'percent', minimumFractionDigits: 2, maximumFractionDigits: 2, }, ) this.footer.innerText = ( this.text.localStorage + ' ' + fmt.format( this.storageUse ).replaceAll(/\s+/g, '') + ' ' + this.text.full ) } } }