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

161 lines
3.9 KiB
TypeScript
Raw Normal View History

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-image': DeesTileImage;
}
}
@customElement('dees-tile-image')
export class DeesTileImage extends DeesTileBase {
public static demo = demo;
public static demoGroups = ['Media'];
public static styles = [
...tileBaseStyles,
css`
.image-wrapper {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: ${cssManager.bdTheme(
'repeating-conic-gradient(#e8e8e8 0% 25%, white 0% 50%) 50% / 16px 16px',
'repeating-conic-gradient(hsl(215 20% 18%) 0% 25%, hsl(215 20% 14%) 0% 50%) 50% / 16px 16px'
)};
}
.image-wrapper img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: opacity 0.3s ease;
}
.image-wrapper img.loaded {
opacity: 1;
}
.image-wrapper img.loading {
opacity: 0;
}
.tile-badge-topright.dimension-badge {
opacity: 0;
transition: opacity 0.2s ease;
}
.tile-container.clickable:hover .tile-badge-topright.dimension-badge {
opacity: 1;
}
`,
] as any;
@property({ type: String })
accessor src: string = '';
@property({ type: String })
accessor alt: string = '';
@state()
accessor imageLoaded: boolean = false;
@state()
accessor imageWidth: number = 0;
@state()
accessor imageHeight: number = 0;
private hasStartedLoading: boolean = false;
protected renderTileContent(): TemplateResult {
return html`
<div class="image-wrapper">
${this.hasStartedLoading ? html`
<img
class="${this.imageLoaded ? 'loaded' : 'loading'}"
src="${this.src}"
alt="${this.alt}"
@load=${this.handleImageLoad}
@error=${this.handleImageError}
/>
` : ''}
</div>
${this.imageWidth > 0 && this.imageHeight > 0 ? html`
<div class="tile-badge-topright dimension-badge">
${this.imageWidth} × ${this.imageHeight}
</div>
` : ''}
${this.imageLoaded ? html`
<div class="tile-info">
<dees-icon icon="lucide:Image"></dees-icon>
<span class="tile-info-text">${this.imageWidth} × ${this.imageHeight}</span>
</div>
` : ''}
${this.clickable ? html`
<div class="tile-overlay">
<dees-icon icon="lucide:Eye"></dees-icon>
<span>View Image</span>
</div>
` : ''}
`;
}
protected getTileClickDetail(): Record<string, unknown> {
return {
src: this.src,
alt: this.alt,
width: this.imageWidth,
height: this.imageHeight,
};
}
protected onBecameVisible(): void {
if (!this.hasStartedLoading && this.src) {
this.hasStartedLoading = true;
this.loading = true;
this.requestUpdate();
}
}
private handleImageLoad(e: Event): void {
const img = e.target as HTMLImageElement;
this.imageWidth = img.naturalWidth;
this.imageHeight = img.naturalHeight;
this.imageLoaded = true;
this.loading = false;
}
private handleImageError(): void {
this.error = true;
this.loading = false;
}
public async updated(changedProperties: Map<PropertyKey, unknown>): Promise<void> {
super.updated(changedProperties);
if (changedProperties.has('src') && this.src && this.isVisible) {
this.hasStartedLoading = true;
this.imageLoaded = false;
this.loading = true;
}
}
}