feat(components): add large set of new UI components and demos, reorganize groups, and bump a few dependencies
This commit is contained in:
172
ts_web/elements/00group-media/dees-tile-image/component.ts
Normal file
172
ts_web/elements/00group-media/dees-tile-image/component.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
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;
|
||||
}
|
||||
|
||||
.dimension-badge {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
padding: 3px 8px;
|
||||
background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.6)', 'hsl(0 0% 100% / 0.85)')};
|
||||
color: ${cssManager.bdTheme('white', 'hsl(215 20% 12%)')};
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
backdrop-filter: blur(8px);
|
||||
z-index: 15;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.tile-container.clickable:hover .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="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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user