import { LitElement, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; @customElement('nile-rte-color') export class NileRteColor extends LitElement { protected createRenderRoot() { return this; } @property({ type: String, attribute: true, reflect: true }) mode: 'foreColor' | 'backColor' = 'foreColor'; @property({ type: String, attribute: true, reflect: true }) label = 'Text Color'; @property({ type: String, attribute: true, reflect: true }) value = '#000000'; @property({ type: Array, attribute: true, reflect: true }) palette: string[] = [ '#000000','#444444','#666666','#999999','#cccccc','#eeeeee','#ffffff', '#d32f2f','#f44336','#ff9800','#ffeb3b','#4caf50','#2196f3','#3f51b5','#9c27b0', '#03a9f4','#00bcd4','#009688','#8bc34a','#cddc39','#ffc107','#ff5722' ]; @state() private recentColors: string[] = []; private inputEl!: HTMLInputElement; connectedCallback() { super.connectedCallback(); this.injectCss(` nile-button.rte-color-trigger::part(base){ width:32px; height:32px; padding:0px 6px; } .glyph-stack { display:grid; align-items:center; justify-items:center; line-height:1; } .glyph { font-size:14px; margin-bottom:2px; } .underline { width:18px; height:3px; border-radius:2px; } .swatch-box { width:18px; height:16px; border-radius:4px; border:1px solid rgba(0,0,0,0.35); } nile-popover::part(popover) { min-width: 225px; height: 250px; padding: 0px; gap: 0px; } .swatch-dropdown { height: 245px; width: 275px; padding: 12px; background: #fff; border-radius: 8px; box-sizing: border-box; overflow-y: auto; } .section { margin-bottom: 12px; } .title { font-size: 13px; font-weight: 600; margin-bottom: 8px; color:#111; } .swatches { display: grid; grid-template-columns: repeat(auto-fill, 24px); gap: 8px; } .swatch { width: 24px; height: 24px; border-radius: 50%; border: 1px solid rgba(0,0,0,0.25); cursor: pointer; position: relative; background-clip: content-box; } .swatch.add { display:flex; align-items:center; justify-content:center; font-weight:bold; font-size:16px; background:#fff; } .checkmark { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); font-size: 13px; color: white; text-shadow: 0 0 2px rgba(0,0,0,0.6); } `); this.inputEl = document.createElement('input'); this.inputEl.type = 'color'; this.inputEl.style.position = 'absolute'; this.inputEl.style.opacity = '0'; this.inputEl.style.pointerEvents = 'none'; this.inputEl.value = this.value; this.appendChild(this.inputEl); this.inputEl.addEventListener('input', () => this.setColor(this.inputEl.value)); } private injectCss(cssText: string) { if (this.querySelector('style[data-rte-color-style]')) return; const style = document.createElement('style'); style.setAttribute('data-rte-color-style', 'true'); style.textContent = cssText; this.insertBefore(style, this.firstChild); } private setColor(color: string) { this.value = color; this.recentColors = [color, ...this.recentColors.filter(c => c !== color)].slice(0, 8); this.dispatchEvent(new CustomEvent('change', { detail: { mode: this.mode, value: color }, bubbles: true, composed: true })); } private pickCustom = () => this.inputEl.click(); render() { return html` ${this.mode === 'backColor' ? html`` : html` A `} Default ${this.palette.map(c => html` this.setColor(c)} title=${c}> ${this.value === c ? html`✓` : ''} `)} ${this.recentColors.length > 0 ? html` Recently Used ${this.recentColors.map(c => html` this.setColor(c)} title=${c}> `)} ` : ''} Custom + `; } } declare global { interface HTMLElementTagNameMap { 'nile-rte-color': NileRteColor; } }