You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pages/components/nav-menu.js

198 lines
4.2 KiB
JavaScript

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
)
}
}
}