import { DeesElement, html, property, type TemplateResult, type CSSResult, } from '@design.estate/dees-element'; import { tileBaseStyles } from './styles.js'; import '../../00group-utility/dees-icon/dees-icon.js'; export abstract class DeesTileBase extends DeesElement { public static styles: CSSResult[] = tileBaseStyles as any; @property({ type: Boolean }) accessor clickable: boolean = true; @property({ type: Boolean }) accessor loading: boolean = false; @property({ type: Boolean }) accessor error: boolean = false; @property({ type: String, reflect: true }) accessor size: 'small' | 'default' | 'large' = 'default'; @property({ type: String }) accessor label: string = ''; private observer: IntersectionObserver | undefined; private _visible: boolean = false; /** Whether this tile is currently visible in the viewport */ protected get isVisible(): boolean { return this._visible; } public render(): TemplateResult { return html`
${this.loading ? html`
Loading...
` : ''} ${this.error ? html`
Failed to load
` : ''} ${!this.loading && !this.error ? this.renderTileContent() : ''} ${this.label ? html`
${this.label}
` : ''}
`; } /** Subclasses implement this to render their specific content */ protected abstract renderTileContent(): TemplateResult; public async connectedCallback(): Promise { await super.connectedCallback(); this.setupIntersectionObserver(); } public async disconnectedCallback(): Promise { await super.disconnectedCallback(); if (this.observer) { this.observer.disconnect(); this.observer = undefined; } } private setupIntersectionObserver(): void { this.observer = new IntersectionObserver( (entries) => { for (const entry of entries) { const wasVisible = this._visible; this._visible = entry.isIntersecting; if (this._visible && !wasVisible) { this.onBecameVisible(); } } }, { root: null, rootMargin: '200px', threshold: 0.01 } ); this.observer.observe(this); } /** Called when the tile first enters the viewport. Override for lazy loading. */ protected onBecameVisible(): void { // Subclasses can override } /** Called when mouse enters the tile container. Override in subclasses. */ protected onTileMouseEnter(): void {} /** Called when mouse leaves the tile container. Override in subclasses. */ protected onTileMouseLeave(): void {} /** Called when mouse moves over the tile container. Override in subclasses. */ protected onTileMouseMove(_e: MouseEvent): void {} protected handleTileClick(): void { if (!this.clickable) return; this.dispatchEvent( new CustomEvent('tile-click', { detail: this.getTileClickDetail(), bubbles: true, composed: true, }) ); } /** Return the detail object for tile-click events. Override in subclasses. */ protected getTileClickDetail(): Record { return {}; } }