import { property, state, html, customElement, css, cssManager, type TemplateResult, type CSSResult, } from '@design.estate/dees-element'; import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js'; import { tileBaseStyles } from '../dees-tile-shared/styles.js'; import { demo } from './demo.js'; declare global { interface HTMLElementTagNameMap { 'dees-tile-note': DeesTileNote; } } @customElement('dees-tile-note') export class DeesTileNote extends DeesTileBase { public static demo = demo; public static demoGroups = ['Media']; public static styles = [ ...tileBaseStyles, css` .note-content { position: relative; width: 100%; height: 100%; display: flex; flex-direction: column; background: ${cssManager.bdTheme('#ffffff', 'hsl(60 5% 96%)')}; overflow: hidden; } .note-header { padding: 12px 14px 8px; flex-shrink: 0; } .note-title { font-size: 12px; font-weight: 700; color: ${cssManager.bdTheme('hsl(215 20% 20%)', 'hsl(215 20% 20%)')}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.3; } .note-body { flex: 1; padding: 0 14px 14px; position: relative; overflow: hidden; } .note-text { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 10px; line-height: 1.5; color: ${cssManager.bdTheme('hsl(215 10% 40%)', 'hsl(215 10% 35%)')}; white-space: pre-wrap; word-wrap: break-word; overflow: hidden; margin: 0; } .note-fade { position: absolute; bottom: 0; left: 0; right: 0; height: 60px; background: linear-gradient( transparent, ${cssManager.bdTheme('#ffffff', 'hsl(60 5% 96%)')} ); pointer-events: none; } .tile-badge-topright.note-language { background: ${cssManager.bdTheme('hsl(215 20% 92%)', 'hsl(215 20% 88%)')}; color: ${cssManager.bdTheme('hsl(215 16% 50%)', 'hsl(215 16% 40%)')}; font-size: 9px; text-transform: uppercase; z-index: 5; } .note-lines { position: absolute; top: 0; left: 0; bottom: 0; width: 34px; border-right: 1px solid ${cssManager.bdTheme('hsl(0 70% 85%)', 'hsl(0 50% 80%)')}; display: flex; flex-direction: column; padding-top: 12px; } .line-number { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 9px; line-height: 15px; /* matches 10px * 1.5 line-height */ color: ${cssManager.bdTheme('hsl(215 10% 75%)', 'hsl(215 10% 70%)')}; text-align: right; padding-right: 6px; } `, ] as any; @property({ type: String }) accessor title: string = ''; @property({ type: String }) accessor content: string = ''; @property({ type: String }) accessor language: string = ''; @state() accessor isHovering: boolean = false; private noteBodyElement: HTMLElement | null = null; protected renderTileContent(): TemplateResult { const lines = this.content.split('\n'); return html`
${this.language ? html`
${this.language}
` : ''} ${this.title ? html`
${this.title}
` : ''}
${lines.join('\n')}
${!this.isHovering ? html`
` : ''}
${this.isHovering && lines.length > 12 ? html`
Line ${this.getVisibleLineRange(lines.length)}
` : ''}
${this.clickable ? html`
Open Note
` : ''} `; } protected getTileClickDetail(): Record { return { title: this.title, content: this.content, language: this.language, }; } protected onTileMouseEnter(): void { this.isHovering = true; if (!this.noteBodyElement) { this.noteBodyElement = this.shadowRoot?.querySelector('.note-body') as HTMLElement; } } protected onTileMouseLeave(): void { this.isHovering = false; if (this.noteBodyElement) { this.noteBodyElement.scrollTop = 0; } } protected onTileMouseMove(e: MouseEvent): void { if (!this.isHovering || !this.noteBodyElement) return; const totalLines = this.content.split('\n').length; if (totalLines <= 12) return; const rect = this.getBoundingClientRect(); const x = e.clientX - rect.left; const percentage = Math.max(0, Math.min(1, x / rect.width)); const maxScroll = this.noteBodyElement.scrollHeight - this.noteBodyElement.clientHeight; this.noteBodyElement.scrollTop = percentage * maxScroll; } private getVisibleLineRange(totalLines: number): string { if (!this.noteBodyElement) return `1–12 of ${totalLines}`; const lineHeight = 15; // 10px font × 1.5 line-height const firstLine = Math.floor(this.noteBodyElement.scrollTop / lineHeight) + 1; const visibleCount = Math.floor(this.noteBodyElement.clientHeight / lineHeight); const lastLine = Math.min(firstLine + visibleCount - 1, totalLines); return `${firstLine}–${lastLine} of ${totalLines}`; } }