import { customElement, property, html, DeesElement, type TemplateResult, cssManager, css, state, } from '@design.estate/dees-element'; import { type ISlashMenuItem } from './wysiwyg.types.js'; import { WysiwygShortcuts } from './wysiwyg.shortcuts.js'; declare global { interface HTMLElementTagNameMap { 'dees-slash-menu': DeesSlashMenu; } } @customElement('dees-slash-menu') export class DeesSlashMenu extends DeesElement { private static instance: DeesSlashMenu; public static getInstance(): DeesSlashMenu { if (!DeesSlashMenu.instance) { DeesSlashMenu.instance = new DeesSlashMenu(); document.body.appendChild(DeesSlashMenu.instance); } return DeesSlashMenu.instance; } @state() public visible: boolean = false; @state() private position: { x: number; y: number } = { x: 0, y: 0 }; @state() private filter: string = ''; @state() private selectedIndex: number = 0; private callback: ((type: string) => void) | null = null; public static styles = [ cssManager.defaultStyles, css` :host { position: fixed; z-index: 10000; pointer-events: none; } .slash-menu { position: absolute; background: ${cssManager.bdTheme('#ffffff', '#262626')}; border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#404040')}; border-radius: 8px; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); padding: 4px; min-width: 220px; max-height: 300px; overflow-y: auto; pointer-events: auto; user-select: none; animation: fadeInScale 0.15s ease-out; } @keyframes fadeInScale { from { opacity: 0; transform: scale(0.95) translateY(-10px); } to { opacity: 1; transform: scale(1) translateY(0); } } .slash-menu-item { padding: 10px 12px; cursor: pointer; transition: all 0.15s ease; display: flex; align-items: center; gap: 12px; border-radius: 4px; color: ${cssManager.bdTheme('#000000', '#e0e0e0')}; font-size: 14px; } .slash-menu-item:hover, .slash-menu-item.selected { background: ${cssManager.bdTheme('#f0f0f0', '#333333')}; color: ${cssManager.bdTheme('#000000', '#ffffff')}; } .slash-menu-item .icon { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 16px; color: ${cssManager.bdTheme('#666', '#999')}; font-weight: 600; } .slash-menu-item:hover .icon, .slash-menu-item.selected .icon { color: ${cssManager.bdTheme('#0066cc', '#4d94ff')}; } `, ]; render(): TemplateResult { if (!this.visible) return html``; const menuItems = this.getFilteredMenuItems(); return html`
`; } private getFilteredMenuItems(): ISlashMenuItem[] { const allItems = WysiwygShortcuts.getSlashMenuItems(); return allItems.filter(item => this.filter === '' || item.label.toLowerCase().includes(this.filter.toLowerCase()) ); } private selectItem(type: string): void { if (this.callback) { this.callback(type); } this.hide(); } public show(position: { x: number; y: number }, callback: (type: string) => void): void { this.position = position; this.callback = callback; this.filter = ''; this.selectedIndex = 0; this.visible = true; } public hide(): void { this.visible = false; this.callback = null; this.filter = ''; this.selectedIndex = 0; } public updateFilter(filter: string): void { this.filter = filter; this.selectedIndex = 0; } public navigate(direction: 'up' | 'down'): void { const items = this.getFilteredMenuItems(); if (direction === 'down') { this.selectedIndex = (this.selectedIndex + 1) % items.length; } else { this.selectedIndex = this.selectedIndex === 0 ? items.length - 1 : this.selectedIndex - 1; } } public selectCurrent(): void { const items = this.getFilteredMenuItems(); if (items[this.selectedIndex]) { this.selectItem(items[this.selectedIndex].type); } } }