import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit'; import {property, state} from 'lit/decorators.js'; import ZincElement from "../../../../internal/zinc-element"; import styles from './context-menu.scss'; export interface ResultItem { icon: string; label: string; format?: string; key?: string; value?: string | boolean; order?: number; } export default class ContextMenuComponent extends ZincElement { static styles: CSSResultGroup = unsafeCSS(styles); @property({type: Boolean, reflect: true}) open = false; @property({type: String}) query = ''; @property({type: Array}) results: ResultItem[] = []; @state() private _activeIndex = -1; public show() { this.open = true; } public hide() { this.open = false; this._activeIndex = -1; } setPosition(left: number, top: number) { this.style.left = `${Math.max(0, left)}px`; this.style.top = `${top}px`; } setActiveIndex(index: number) { const len = this.results?.length ?? 0; if (!len) { this._activeIndex = -1; return; } if (index < 0) { index = len - 1; } if (index >= len) { index = 0; } this._activeIndex = index; this.requestUpdate(); requestAnimationFrame(() => { const items = Array.from(this.renderRoot.querySelectorAll('[data-toolbar-option]')); const active = items[this._activeIndex]; active?.scrollIntoView?.({block: 'nearest'}); }); } getActiveIndex() { return this._activeIndex; } private _onClickItem = (e: MouseEvent) => { const target = e.currentTarget as HTMLElement | null; if (!target) return; const idx = parseInt(target.dataset.index || '-1', 10); if (Number.isNaN(idx)) return; this.setActiveIndex(idx); const item = this.results?.[idx]; if (!item) return; this.dispatchEvent(new CustomEvent('zn-format-select', { bubbles: true, composed: true, detail: {icon: item.icon, label: item.label, format: item.format, value: item.value, key: item.key} })); } protected willUpdate(changed: PropertyValues) { if (changed.has('results')) { // Reset active index when results change this._activeIndex = this.results?.length ? 0 : -1; } } render() { return html`
${this.query ? `Search: ${this.query}` : 'Options'}
${Array.isArray(this.results) && this.results.length > 0 ? ( this.results.slice(0, 20).map((res, i) => html` `) ) : html`
${this.query ? 'No results' : 'Type something'}
`} `; } } ContextMenuComponent.define('zn-context-menu');