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;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
ts_web/elements/00group-media/dees-tile-image/demo.ts
Normal file
84
ts_web/elements/00group-media/dees-tile-image/demo.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
|
||||
export const demo = () => html`
|
||||
<style>
|
||||
.demo-container {
|
||||
padding: 40px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.demo-section {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tile-row {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="demo-container">
|
||||
<div class="demo-section">
|
||||
<h3>Image Tiles</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-image
|
||||
src="https://picsum.photos/800/600"
|
||||
alt="Landscape photo"
|
||||
label="landscape.jpg"
|
||||
@tile-click=${(e: CustomEvent) => console.log('Image clicked:', e.detail)}
|
||||
></dees-tile-image>
|
||||
|
||||
<dees-tile-image
|
||||
src="https://picsum.photos/400/400"
|
||||
alt="Square photo"
|
||||
label="square.png"
|
||||
></dees-tile-image>
|
||||
|
||||
<dees-tile-image
|
||||
src="https://picsum.photos/300/900"
|
||||
alt="Portrait photo"
|
||||
label="portrait.webp"
|
||||
></dees-tile-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Size Variants</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-image
|
||||
size="small"
|
||||
src="https://picsum.photos/200/200"
|
||||
alt="Small"
|
||||
label="small.jpg"
|
||||
></dees-tile-image>
|
||||
|
||||
<dees-tile-image
|
||||
src="https://picsum.photos/600/400"
|
||||
alt="Default"
|
||||
label="default.jpg"
|
||||
></dees-tile-image>
|
||||
|
||||
<dees-tile-image
|
||||
size="large"
|
||||
src="https://picsum.photos/1200/800"
|
||||
alt="Large"
|
||||
label="large.jpg"
|
||||
></dees-tile-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Error State (broken URL)</h3>
|
||||
<dees-tile-image
|
||||
src="https://invalid-url-that-does-not-exist.example/image.png"
|
||||
alt="Broken"
|
||||
label="broken.png"
|
||||
></dees-tile-image>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
1
ts_web/elements/00group-media/dees-tile-image/index.ts
Normal file
1
ts_web/elements/00group-media/dees-tile-image/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './component.js';
|
||||
Reference in New Issue
Block a user