Files
dees-catalog/ts_web/elements/00group-media/dees-tile-note/component.ts

211 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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`
<div class="note-content">
${this.language ? html`
<div class="tile-badge-topright note-language">${this.language}</div>
` : ''}
${this.title ? html`
<div class="note-header">
<div class="note-title">${this.title}</div>
</div>
` : ''}
<div class="note-body">
<pre class="note-text">${lines.join('\n')}</pre>
${!this.isHovering ? html`<div class="note-fade"></div>` : ''}
</div>
${this.isHovering && lines.length > 12 ? html`
<div class="tile-badge-corner">
Line ${this.getVisibleLineRange(lines.length)}
</div>
` : ''}
</div>
${this.clickable ? html`
<div class="tile-overlay">
<dees-icon icon="lucide:FileText"></dees-icon>
<span>Open Note</span>
</div>
` : ''}
`;
}
protected getTileClickDetail(): Record<string, unknown> {
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 `112 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}`;
}
}