161 lines
3.9 KiB
TypeScript
161 lines
3.9 KiB
TypeScript
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;
|
||
}
|
||
}
|
||
}
|