feat(components): add large set of new UI components and demos, reorganize groups, and bump a few dependencies
This commit is contained in:
181
ts_web/elements/00group-media/dees-tile-folder/component.ts
Normal file
181
ts_web/elements/00group-media/dees-tile-folder/component.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import {
|
||||
property,
|
||||
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';
|
||||
|
||||
export interface ITileFolderItem {
|
||||
type: 'pdf' | 'image' | 'audio' | 'video' | 'note' | 'folder' | 'unknown';
|
||||
thumbnailSrc?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const TYPE_ICON_MAP: Record<string, string> = {
|
||||
pdf: 'lucide:FileText',
|
||||
image: 'lucide:Image',
|
||||
audio: 'lucide:Music',
|
||||
video: 'lucide:Video',
|
||||
note: 'lucide:FileCode',
|
||||
folder: 'lucide:Folder',
|
||||
unknown: 'lucide:File',
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dees-tile-folder': DeesTileFolder;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dees-tile-folder')
|
||||
export class DeesTileFolder extends DeesTileBase {
|
||||
public static demo = demo;
|
||||
public static demoGroups = ['Media'];
|
||||
public static styles = [
|
||||
...tileBaseStyles,
|
||||
css`
|
||||
.folder-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${cssManager.bdTheme('hsl(40 30% 97%)', 'hsl(215 20% 14%)')};
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.folder-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 14px 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.folder-icon {
|
||||
font-size: 18px;
|
||||
color: ${cssManager.bdTheme('hsl(40 80% 50%)', 'hsl(40 70% 60%)')};
|
||||
}
|
||||
|
||||
.folder-name {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: ${cssManager.bdTheme('hsl(215 20% 20%)', 'hsl(215 16% 80%)')};
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.preview-grid {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
gap: 4px;
|
||||
padding: 0 14px 14px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background: ${cssManager.bdTheme('hsl(215 20% 94%)', 'hsl(215 20% 18%)')};
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid-cell img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.grid-cell dees-icon {
|
||||
font-size: 20px;
|
||||
color: ${cssManager.bdTheme('hsl(215 16% 60%)', 'hsl(215 16% 55%)')};
|
||||
}
|
||||
|
||||
.grid-cell-empty {
|
||||
background: ${cssManager.bdTheme('hsl(215 15% 96%)', 'hsl(215 20% 16%)')};
|
||||
}
|
||||
|
||||
.item-count-badge {
|
||||
position: absolute;
|
||||
bottom: 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: 10;
|
||||
}
|
||||
`,
|
||||
] as any;
|
||||
|
||||
@property({ type: String })
|
||||
accessor name: string = '';
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor items: ITileFolderItem[] = [];
|
||||
|
||||
protected renderTileContent(): TemplateResult {
|
||||
const previewItems = this.items.slice(0, 4);
|
||||
const emptyCells = 4 - previewItems.length;
|
||||
|
||||
return html`
|
||||
<div class="folder-content">
|
||||
<div class="folder-header">
|
||||
<dees-icon class="folder-icon" icon="lucide:Folder"></dees-icon>
|
||||
<div class="folder-name">${this.name || 'Untitled Folder'}</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-grid">
|
||||
${previewItems.map((item) => html`
|
||||
<div class="grid-cell">
|
||||
${item.thumbnailSrc ? html`
|
||||
<img src="${item.thumbnailSrc}" alt="${item.name}" />
|
||||
` : html`
|
||||
<dees-icon icon="${TYPE_ICON_MAP[item.type] || TYPE_ICON_MAP.unknown}"></dees-icon>
|
||||
`}
|
||||
</div>
|
||||
`)}
|
||||
${Array.from({ length: emptyCells }).map(() => html`
|
||||
<div class="grid-cell grid-cell-empty"></div>
|
||||
`)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-count-badge">
|
||||
${this.items.length} item${this.items.length !== 1 ? 's' : ''}
|
||||
</div>
|
||||
|
||||
${this.clickable ? html`
|
||||
<div class="tile-overlay">
|
||||
<dees-icon icon="lucide:FolderOpen"></dees-icon>
|
||||
<span>Open Folder</span>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
protected getTileClickDetail(): Record<string, unknown> {
|
||||
return {
|
||||
name: this.name,
|
||||
itemCount: this.items.length,
|
||||
items: this.items,
|
||||
};
|
||||
}
|
||||
}
|
||||
122
ts_web/elements/00group-media/dees-tile-folder/demo.ts
Normal file
122
ts_web/elements/00group-media/dees-tile-folder/demo.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
import type { ITileFolderItem } from './component.js';
|
||||
|
||||
export const demo = () => {
|
||||
const photosFolder: ITileFolderItem[] = [
|
||||
{ type: 'image', name: 'sunset.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=1' },
|
||||
{ type: 'image', name: 'mountain.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=2' },
|
||||
{ type: 'image', name: 'ocean.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=3' },
|
||||
{ type: 'image', name: 'forest.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=4' },
|
||||
{ type: 'image', name: 'city.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=5' },
|
||||
{ type: 'image', name: 'desert.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=6' },
|
||||
];
|
||||
|
||||
const projectFolder: ITileFolderItem[] = [
|
||||
{ type: 'note', name: 'README.md' },
|
||||
{ type: 'note', name: 'package.json' },
|
||||
{ type: 'folder', name: 'src' },
|
||||
{ type: 'folder', name: 'test' },
|
||||
{ type: 'note', name: 'tsconfig.json' },
|
||||
{ type: 'pdf', name: 'docs.pdf' },
|
||||
{ type: 'image', name: 'logo.png', thumbnailSrc: 'https://picsum.photos/100/100?random=10' },
|
||||
];
|
||||
|
||||
const mediaFolder: ITileFolderItem[] = [
|
||||
{ type: 'video', name: 'intro.mp4' },
|
||||
{ type: 'audio', name: 'background.mp3' },
|
||||
{ type: 'image', name: 'thumbnail.jpg', thumbnailSrc: 'https://picsum.photos/200/200?random=20' },
|
||||
{ type: 'pdf', name: 'storyboard.pdf' },
|
||||
];
|
||||
|
||||
const emptyFolder: ITileFolderItem[] = [];
|
||||
|
||||
const singleItemFolder: ITileFolderItem[] = [
|
||||
{ type: 'pdf', name: 'report.pdf' },
|
||||
];
|
||||
|
||||
return 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>Folder Tiles</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-folder
|
||||
name="Photos"
|
||||
.items=${photosFolder}
|
||||
label="6 photos"
|
||||
@tile-click=${(e: CustomEvent) => console.log('Folder clicked:', e.detail)}
|
||||
></dees-tile-folder>
|
||||
|
||||
<dees-tile-folder
|
||||
name="my-project"
|
||||
.items=${projectFolder}
|
||||
label="Project files"
|
||||
></dees-tile-folder>
|
||||
|
||||
<dees-tile-folder
|
||||
name="Media Assets"
|
||||
.items=${mediaFolder}
|
||||
label="Mixed media"
|
||||
></dees-tile-folder>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Edge Cases</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-folder
|
||||
name="Empty Folder"
|
||||
.items=${emptyFolder}
|
||||
></dees-tile-folder>
|
||||
|
||||
<dees-tile-folder
|
||||
name="Single Item"
|
||||
.items=${singleItemFolder}
|
||||
></dees-tile-folder>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Size Variants</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-folder
|
||||
size="small"
|
||||
name="Small"
|
||||
.items=${photosFolder}
|
||||
></dees-tile-folder>
|
||||
|
||||
<dees-tile-folder
|
||||
name="Default"
|
||||
.items=${photosFolder}
|
||||
></dees-tile-folder>
|
||||
|
||||
<dees-tile-folder
|
||||
size="large"
|
||||
name="Large"
|
||||
.items=${photosFolder}
|
||||
></dees-tile-folder>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
1
ts_web/elements/00group-media/dees-tile-folder/index.ts
Normal file
1
ts_web/elements/00group-media/dees-tile-folder/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './component.js';
|
||||
Reference in New Issue
Block a user