export class Header extends HTMLElement { icons = { menu: ` `, dot: ` `, edit: ` `, check: ` `, add: ` `, } textEn = { download: 'Download', rename: 'Move/Rename', delete: 'Delete', confirmDelete: f => ( `Are you sure you want to delete ${f}?` ), cancel: 'Cancel', alreadyExists: 'There is already a page with that name.', createPage: 'Create Page', } textEs = { download: 'Descargar', rename: 'Mover/Renombrar', delete: 'Borrar', confirmDelete: f => ( `¿Desea borrar ${f}?` ), cancel: 'Cancelar', alreadyExists: 'Ya existe una página con ese nombre.', createPage: 'Crear Página', } constructor() { super() this.language = navigator.language this.editing = false this.attachShadow({mode: 'open'}) this.addButton(this.icons.menu, 'nav', () => { this.menu.pages = this.getPages() this.menuPanel.classList.add('open') this.overlay.classList.add('open') }) this.addButton(this.icons.add, 'add', () => { this.addPage() }) this.addDivider() this.editBtn = this.addButton(this.editIcon, 'edit', () => { this.dispatchEvent(new CustomEvent( 'click-edit', {bubbles: true} )) }) this.addButton(this.icons.dot, 'page', () => { this.pageMenuPanel.classList.add('open') this.overlay.classList.add('open') }) this.addMenu() this.addPageMenu() this.dialogWrap = document.createElement('div') this.shadowRoot.appendChild(this.dialogWrap) } connectedCallback() { const style = document.createElement('style') style.textContent = ` :host { background: #111; color: #ddd; display: flex; flex-direction: row; padding: 0 4px; } button { border: none; background: inherit; color: inherit; font-size: 30px; width: 32px; } div.divider { flex-grow: 1; } div.menu { position: fixed; top: 0; left: -90vw; height: 100vh; width: 90vw; background-color: #fff; transition: left .25s ease-in-out; } div.menu.open { left: 0; } div.overlay { position: fixed; top: 0; left: -100vw; height: 100vh; width: 100vw; opacity: 0%; transition: opacity .25s ease-in; background: #777; } div.overlay.closing { left: 0; } div.overlay.open { left: 0; opacity: 15%; } button.page { position: relative; } div.page-menu { position: fixed; top: 34px; right: 0; display: flex; align-items: flex-end; margin-right: 0; display: none; } div.page-menu.open { display: block; } svg { width: 20px; height: 20px; } div.dialog-panel { position: fixed; top: 34px; left: 0; right: 0; bottom: 0; display: flex; align-items: flex-end; } div.page-menu.open { display: block; } m-dialog::part(footer) { padding-top: 15px; text-align: right; } ` this.shadowRoot.append(style) } encodePath(path) { return path } addButton(html, cls, onClick) { const b = document.createElement('button') b.innerHTML = html b.classList.add(cls) this.shadowRoot.appendChild(b) if (onClick) { b.addEventListener('click', onClick) } return b } addDivider() { const d = document.createElement('div') d.classList.add('divider') this.shadowRoot.appendChild(d) } addMenu() { this.overlay = document.createElement('div') this.overlay.classList.add('overlay') this.shadowRoot.appendChild(this.overlay) this.menuPanel = document.createElement('div') this.menuPanel.classList.add('menu') this.menu = document.createElement('m-nav-menu') this.menuPanel.appendChild(this.menu) this.shadowRoot.appendChild(this.menuPanel) this.overlay.addEventListener('click', () => { this.close() }) this.menu.addEventListener('close-menu', () => { this.close() }) } addPageMenu() { this.pageMenu = document.createElement('m-page-menu') this.pageMenu.add(this.text.download, () => { const text = localStorage.getItem(this.path) const sp = this.path.split('/') const filename = sp[sp.length - 1] const el = document.createElement('a') el.setAttribute( 'href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) ) el.setAttribute('download', filename) el.style.display = 'none' this.shadowRoot.appendChild(el) el.click() this.shadowRoot.removeChild(el) }) this.pageMenu.add(this.text.rename, () => { const dialog = document.createElement('m-dialog') this.dialogWrap.replaceChildren(dialog) const input = document.createElement('input') input.value = this.path input.style.minWidth = '300px' dialog.bodyEl.appendChild(input) const cancelBtn = document.createElement('button') cancelBtn.innerText = this.text.cancel cancelBtn.addEventListener('click', () => { dialog.close() }) const confirmBtn = document.createElement('button') confirmBtn.innerText = this.text.rename confirmBtn.style.marginLeft = '3px' let errorEl confirmBtn.addEventListener('click', () => { const newPath = input.value const v = localStorage.getItem(newPath) if (v !== null || newPath === this.path) { if (!errorEl) { errorEl = document.createElement('p') errorEl.style.color = 'red' errorEl.innerText = this.text.alreadyExists dialog.bodyEl.appendChild(errorEl) } return } localStorage.setItem( newPath, localStorage.getItem(this.path) ) localStorage.removeItem(this.path) dialog.close() location.hash = newPath }) dialog.footerEl.replaceChildren( cancelBtn, confirmBtn ) dialog.open() }) this.pageMenu.add(this.text.delete, () => { const dialog = document.createElement('m-dialog') this.dialogWrap.replaceChildren(dialog) const p = document.createElement('p') p.innerText = this.text.confirmDelete( JSON.stringify(this.path) ) dialog.bodyEl.appendChild(p) const cancelBtn = document.createElement('button') cancelBtn.innerText = this.text.cancel cancelBtn.addEventListener('click', () => { dialog.close() }) const confirmBtn = document.createElement('button') confirmBtn.innerText = this.text.delete confirmBtn.style.marginLeft = '3px' confirmBtn.addEventListener('click', () => { localStorage.removeItem(this.path) location.hash = '/' dialog.close() }) dialog.footerEl.replaceChildren( cancelBtn, confirmBtn ) dialog.open() }) this.pageMenu.addEventListener('close-menu', () => { this.close() }) this.pageMenuPanel = document.createElement('div') this.pageMenuPanel.appendChild(this.pageMenu) this.pageMenuPanel.classList.add('page-menu') this.shadowRoot.appendChild(this.pageMenuPanel) } close() { this.overlay.classList.add('closing') this.overlay.classList.remove('open') this.menuPanel.classList.remove('open') this.pageMenuPanel.classList.remove('open') setTimeout(() => { this.overlay.classList.remove('closing') }, 250) } getPages() { return Object.keys(localStorage).slice().sort() } set editing(value) { this._editing = value if (this.editBtn) { this.editBtn.innerHTML = this.editIcon } } get editing() { return this._editing } get editIcon() { return this.editing ? this.icons.check : this.icons.edit } addPage() { const dialog = document.createElement('m-dialog') this.dialogWrap.replaceChildren(dialog) const input = document.createElement('input') input.value = '/' input.style.minWidth = '300px' dialog.bodyEl.appendChild(input) const cancelBtn = document.createElement('button') cancelBtn.innerText = this.text.cancel cancelBtn.addEventListener('click', () => { dialog.close() }) const confirmBtn = document.createElement('button') confirmBtn.innerText = this.text.createPage confirmBtn.style.marginLeft = '3px' confirmBtn.addEventListener('click', () => { const newPath = this.encodePath(input.value) location.hash = newPath dialog.close() }) dialog.footerEl.replaceChildren( cancelBtn, confirmBtn ) dialog.open() } get language() { return this._language } set language(language) { this._language = language this.text = this.langEs ? this.textEs : this.textEn } get langEs() { return /^es\b/.test(this.language) } }