import { VideoOverlay, LatLngBounds, LatLngBoundsExpression, LatLngExpression } from 'leaflet'; import { buildOptions, parseAttributeValue } from '../core/utils.js'; import { withProps } from '../core/with-props.js'; import { registerChildren, unregisterChildren } from '../core/register.js'; import type { PropDef } from '../types/props.js'; const PROPS = { url: { kind: 'str', attr: 'url', default: '' }, bounds: { kind: 'str', attr: 'bounds', default: '' }, opacity: { kind: 'num', attr: 'opacity', default: 1.0 }, alt: { kind: 'str', attr: 'alt', default: '' }, interactive: { kind: 'bool-on', attr: 'interactive' }, crossOrigin: { kind: 'str', attr: 'cross-origin', default: '' }, loop: { kind: 'bool-on', attr: 'loop' }, autoplay: { kind: 'bool-on', attr: 'autoplay' }, muted: { kind: 'bool-on', attr: 'muted' }, playsInline: { kind: 'bool-on', attr: 'playsinline' }, } satisfies Record; const PROP_BY_ATTR = new Map( Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), ); export class LeafletVideoOverlay extends withProps(HTMLElement, PROPS) { #obj?: VideoOverlay; connectedCallback() { const url = this.getAttribute('url') || ''; this.#obj = new VideoOverlay( url, this.#parsedBounds(), buildOptions(this, PROPS, ['url', 'bounds']), ); registerChildren(this, this.#obj); } disconnectedCallback() { unregisterChildren(this); this.#obj?.remove(); this.#obj = undefined; } attributeChangedCallback(name: string, _old: string | null, val: string | null) { if (!this.#obj) return; if (name === 'url') { if (val) this.#obj.setUrl(val); } else if (name === 'bounds') { this.#obj.setBounds(new LatLngBounds(this.#parsedBounds() as LatLngExpression[])); } else if ( name === 'loop' || name === 'autoplay' || name === 'muted' || name === 'playsinline' ) { const el = this.#obj.getElement(); if (el) { if (name === 'loop') el.loop = val !== null; else if (name === 'autoplay') el.autoplay = val !== null; else if (name === 'muted') el.muted = val !== null; else if (name === 'playsinline') el.playsInline = val !== null; } } else { const propName = PROP_BY_ATTR.get(name); if (!propName) return; const setter = `set${propName.charAt(0).toUpperCase()}${propName.slice(1)}` as keyof VideoOverlay; if (typeof this.#obj[setter] === 'function') { const spec = PROPS[propName as keyof typeof PROPS]; const value = spec.kind === 'bool-on' ? val !== null : parseAttributeValue(val); (this.#obj[setter] as (v: unknown) => void)(value); } } } #parsedBounds(): LatLngBoundsExpression { const raw = this.getAttribute('bounds'); return raw ? (JSON.parse(raw) as LatLngBoundsExpression) : []; } getElement(): HTMLVideoElement | undefined { return this.#obj?.getElement(); } } customElements.define('leaflet-video-overlay', LeafletVideoOverlay);