UNPKG

media-show

Version:

Showcase your media player in style!

204 lines (173 loc) 5.38 kB
import '../node_modules/@shoelace-style/shoelace/dist/components/input/input.js'; import '../node_modules/@shoelace-style/shoelace/dist/components/dropdown/dropdown.js'; import '../node_modules/@shoelace-style/shoelace/dist/components/menu/menu.js'; import '../node_modules/@shoelace-style/shoelace/dist/components/menu-item/menu-item.js'; import '../node_modules/@shoelace-style/shoelace/dist/components/icon-button/icon-button.js'; import { registerIconLibrary } from '../node_modules/@shoelace-style/shoelace/dist/utilities/icon-library.js'; import { MsBaseElement } from './base-element.js'; import { html, css, render, unsafeHTML } from './html.js'; import { escapeRegExp } from './utils.js'; registerIconLibrary('default', { resolver: (name) => `../node_modules/@shoelace-style/shoelace/dist/assets/icons/${name}.svg`, }); const styles = () => css` :host { display: inline-block; } sl-dropdown { width: 100%; } `; const template = (props) => html` <style> ${styles(props)} </style> <sl-input placeholder="${props.placeholder}" label="${props.label}"> <slot name="suffix" slot="suffix"> <sl-icon-button name="chevron-down" label="Open menu"></sl-icon-button> </slot> </sl-input> <sl-dropdown open="${props.open}"> <sl-menu> ${props.results.length > 0 ? props.results.map( (item) => html` <sl-menu-item value="${JSON.stringify(item)}"> ${unsafeHTML(props.highlight(item.name))} </sl-menu-item> ` ) : [ html` <sl-menu-item disabled> Found 0 matching results for "${unsafeHTML(`<b>${props.query}</b>`)}" </sl-menu-item>`, ]} </sl-menu> </sl-dropdown> `; class MsInputList extends MsBaseElement { static observedAttributes = ['media-show']; constructor() { super(); this.attachShadow({ mode: 'open' }); this.open = false; this.data = []; } async connectedCallback() { this.render(); this.data = await (await fetch(this.getAttribute('src'))).json(); this.render(); this.addEventListener('sl-input', () => { this.open = true; this.render(); }); this.addEventListener('sl-focus', () => { this.open = true; this.render(); }); this.addEventListener('sl-select', ({ detail }) => { this.input.value = detail.item.innerText; this.dispatchEvent( new CustomEvent('ms-change', { detail: JSON.parse(detail.item.value), composed: true, bubbles: true, }) ); }); document.addEventListener('mousedown', (event) => { const path = event.composedPath(); if (path.includes(this.input)) { event.stopImmediatePropagation(); } }); this.shadowRoot .querySelector('sl-icon-button') .addEventListener('click', (event) => { event.stopImmediatePropagation(); if (this.input.value != '') { this.input.value = ''; } else { this.open = !this.dropdown.open; } this.render(); }); this.input.addEventListener('keydown', (event) => { if (['Enter'].includes(event.key)) { event.preventDefault(); this.dispatchEvent( new CustomEvent('ms-change', { detail: { src: this.input.value }, composed: true, bubbles: true, }) ); this.open = false; this.render(); return; } // Move the selection when pressing down or up if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) { const items = this.menu.getAllItems({ includeDisabled: false }); const activeItem = this.menu.getCurrentItem(); let index = activeItem ? items.indexOf(activeItem) : 0; if (items.length > 0) { event.preventDefault(); if (event.key === 'Home') { index = 0; } else if (event.key === 'End') { index = items.length - 1; } if (index < 0) { index = items.length - 1; } if (index > items.length - 1) { index = 0; } this.menu.setCurrentItem(items[index]); items[index].focus(); return; } } }); } get dropdown() { return this.shadowRoot.querySelector('sl-dropdown'); } get input() { return this.shadowRoot.querySelector('sl-input'); } get menu() { return this.shadowRoot.querySelector('sl-menu'); } get value() { return this.input.value; } render() { const pattern = new RegExp( `(${escapeRegExp(this.input?.value ?? '')})`, 'ig' ); const results = this.data.filter((attrs) => pattern.test(attrs.name)); render( template({ results, open: this.open, placeholder: this.getAttribute('placeholder') ?? '', label: this.getAttribute('label') ?? '', query: this.input?.value, highlight: (text) => { if (!this.input?.value) return text; return text.replace(pattern, `<mark>$1</mark>`); }, }), this.shadowRoot ); } } if (!customElements.get('ms-input-list')) { customElements.define('ms-input-list', MsInputList); } export { MsInputList };