From 465f7585acd816716548307ba1bdef661f70c0a2 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Sun, 5 Apr 2026 00:00:35 +0000 Subject: [PATCH] feat(dees-input-fileupload): redesign the file upload dropzone with dees-tile integration and themed file list styling --- changelog.md | 7 + ts_web/00_commitinfo_data.ts | 2 +- .../dees-input-fileupload/component.ts | 81 +++--- .../dees-input-fileupload/styles.ts | 234 ++++++++---------- ts_web/elements/00theme.ts | 1 + 5 files changed, 147 insertions(+), 178 deletions(-) diff --git a/changelog.md b/changelog.md index cd70293..d544ee3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-04-05 - 3.57.0 - feat(dees-input-fileupload) +redesign the file upload dropzone with dees-tile integration and themed file list styling + +- Replace the custom dropzone container with dees-tile and move actions and metadata into header and footer slots +- Add an explicit empty state for the file list and simplify file list layout and interaction handling +- Adopt shared theme tokens in the file upload styles and introduce a reusable row hover color token + ## 2026-04-04 - 3.56.1 - fix(dees-input-dropdown) improve dropdown popup lifecycle with window layer overlay and animated visibility transitions diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index fae9816..f7d8cae 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@design.estate/dees-catalog', - version: '3.56.1', + version: '3.57.0', description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' } diff --git a/ts_web/elements/00group-input/dees-input-fileupload/component.ts b/ts_web/elements/00group-input/dees-input-fileupload/component.ts index 7f72811..6a0732b 100644 --- a/ts_web/elements/00group-input/dees-input-fileupload/component.ts +++ b/ts_web/elements/00group-input/dees-input-fileupload/component.ts @@ -3,6 +3,7 @@ import { demoFunc } from './demo.js'; import { fileuploadStyles } from './styles.js'; import '../../00group-utility/dees-icon/dees-icon.js'; import '../../00group-layout/dees-label/dees-label.js'; +import '../../00group-layout/dees-tile/dees-tile.js'; import { customElement, @@ -75,14 +76,13 @@ export class DeesInputFileupload extends DeesInputBase { .description=${this.description} .required=${this.required} > -
{ @change=${this.handleFileInputChange} tabindex="-1" /> -
-
- ${this.isLoading - ? html`` - : html``} -
-
- ${this.buttonText || 'Select files'} - - Drag and drop files here or - - -
-
-
- ${metaEntries.map((entry) => html`${entry}`)} +
+ ${this.isLoading + ? html`` + : html``} + Drop files here or +
${this.renderFileList()} -
+ + ${this.validationMessage ? html`
${this.validationMessage}
` : html``} @@ -129,20 +120,21 @@ export class DeesInputFileupload extends DeesInputBase { private renderFileList(): TemplateResult { if (this.value.length === 0) { - return html``; + return html` +
+ + No files selected +
+ `; } return html`
-
+
${this.value.length} file${this.value.length === 1 ? '' : 's'} selected - ${this.value.length > 0 - ? html`` - : html``} -
-
- ${this.value.map((file) => this.renderFileRow(file))} +
+ ${this.value.map((file) => this.renderFileRow(file))}
`; } @@ -193,21 +185,14 @@ export class DeesInputFileupload extends DeesInputBase { if (this.disabled) { return; } - // Don't open file selector if clicking on the browse button or file list - if ((event.target as HTMLElement).closest('.dropzone__browse, .file-list')) { + // Don't open file selector when clicking file list items or actions + const target = event.target as HTMLElement; + if (target.closest('.file-list, .dropzone-header, .dropzone-footer')) { return; } this.openFileSelector(); }; - private handleBrowseClick = (event: MouseEvent) => { - if (this.disabled) { - return; - } - event.stopPropagation(); // Stop propagation to prevent double trigger - this.openFileSelector(); - }; - private handleDropzoneKeydown = (event: KeyboardEvent) => { if (this.disabled) { return; @@ -280,7 +265,7 @@ export class DeesInputFileupload extends DeesInputBase { } private rebindInteractiveElements(): void { - const newDropArea = this.shadowRoot?.querySelector('.dropzone') as HTMLElement | null; + const newDropArea = this.shadowRoot?.querySelector('dees-tile') as HTMLElement | null; if (newDropArea !== this.dropArea) { this.detachDropListeners(); diff --git a/ts_web/elements/00group-input/dees-input-fileupload/styles.ts b/ts_web/elements/00group-input/dees-input-fileupload/styles.ts index 4652a3e..98f85f3 100644 --- a/ts_web/elements/00group-input/dees-input-fileupload/styles.ts +++ b/ts_web/elements/00group-input/dees-input-fileupload/styles.ts @@ -1,201 +1,158 @@ import { css, cssManager } from '@design.estate/dees-element'; import { DeesInputBase } from '../dees-input-base/dees-input-base.js'; +import { themeDefaultStyles } from '../../00theme.js'; export const fileuploadStyles = [ - cssManager.defaultStyles, + themeDefaultStyles, ...DeesInputBase.baseStyles, + cssManager.defaultStyles, css` :host { position: relative; display: block; } - .input-wrapper { display: flex; flex-direction: column; gap: 12px; } - .dropzone { - position: relative; - padding: 20px; - border-radius: 12px; - border: 1.5px dashed ${cssManager.bdTheme('hsl(215 16% 80%)', 'hsl(217 20% 25%)')}; - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20% 12%)')}; - transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease; - cursor: pointer; - outline: none; + /* ── Tile integration ── */ + dees-tile { + cursor: default; } - .dropzone:focus-visible { - box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20% 12%)')}, - 0 0 0 4px ${cssManager.bdTheme('hsl(217 91% 60% / 0.5)', 'hsl(213 93% 68% / 0.4)')}; - border-color: ${cssManager.bdTheme('hsl(217 91% 60%)', 'hsl(213 93% 68%)')}; + dees-tile:hover::part(outer) { + border-color: var(--dees-color-border-strong); } - .dropzone--active { - border-color: ${cssManager.bdTheme('hsl(217 91% 60%)', 'hsl(213 93% 68%)')}; - box-shadow: 0 12px 32px ${cssManager.bdTheme('rgba(15, 23, 42, 0.12)', 'rgba(0, 0, 0, 0.35)')}; - background: ${cssManager.bdTheme('hsl(217 91% 60% / 0.06)', 'hsl(213 93% 68% / 0.12)')}; + dees-tile.dragover::part(outer) { + border-color: var(--dees-color-accent-primary); + box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(217 91% 60% / 0.15)', 'hsl(213 93% 68% / 0.15)')}; } - .dropzone--has-files { - background: ${cssManager.bdTheme('hsl(0 0% 99%)', 'hsl(215 20% 11%)')}; - } - - .dropzone--disabled { + :host([disabled]) dees-tile { opacity: 0.6; - pointer-events: none; cursor: not-allowed; + pointer-events: none; } - .dropzone__body { + /* ── Header slot: sleek toolbar ── */ + .dropzone-header { display: flex; align-items: center; - gap: 16px; + gap: 8px; + height: 32px; + padding: 0 12px; } - .dropzone__icon { - width: 48px; - height: 48px; - border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - color: ${cssManager.bdTheme('hsl(217 91% 60%)', 'hsl(213 93% 68%)')}; - background: ${cssManager.bdTheme('hsl(217 91% 60% / 0.12)', 'hsl(213 93% 68% / 0.12)')}; - position: relative; - flex-shrink: 0; + .dropzone-header dees-icon { + width: 14px; + height: 14px; + color: var(--dees-color-text-muted); } - .dropzone__icon dees-icon { - font-size: 22px; - } - - .dropzone__loader { - width: 20px; - height: 20px; - border-radius: 999px; + .dropzone-loader { + width: 14px; + height: 14px; + border-radius: var(--dees-radius-full); border: 2px solid ${cssManager.bdTheme('rgba(15, 23, 42, 0.15)', 'rgba(255, 255, 255, 0.15)')}; - border-top-color: ${cssManager.bdTheme('hsl(217 91% 60%)', 'hsl(213 93% 68%)')}; + border-top-color: var(--dees-color-accent-primary); animation: loader-spin 0.6s linear infinite; } - .dropzone__content { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0; - } - - .dropzone__headline { - font-size: 15px; - font-weight: 600; - color: ${cssManager.bdTheme('hsl(222 47% 11%)', 'hsl(210 20% 96%)')}; - } - - .dropzone__subline { + .dropzone-title { font-size: 13px; - color: ${cssManager.bdTheme('hsl(215 16% 46%)', 'hsl(215 16% 70%)')}; + color: var(--dees-color-text-muted); } - .dropzone__browse { + .dropzone-browse { appearance: none; border: none; background: none; padding: 0; - margin-left: 4px; - color: ${cssManager.bdTheme('hsl(217 91% 60%)', 'hsl(213 93% 68%)')}; + font-size: 13px; + font-family: inherit; font-weight: 600; + color: var(--dees-color-accent-primary); cursor: pointer; - text-decoration: none; } - .dropzone__browse:hover { + .dropzone-browse:hover { text-decoration: underline; } - .dropzone__browse:disabled { + .dropzone-browse:disabled { cursor: not-allowed; - opacity: 0.6; + opacity: 0.5; } - .dropzone__meta { - margin-top: 14px; + /* ── Content slot: file list in rounded inset ── */ + .file-list-empty { display: flex; - flex-wrap: wrap; + flex-direction: column; + align-items: center; + justify-content: center; gap: 8px; - font-size: 12px; - color: ${cssManager.bdTheme('hsl(215 16% 50%)', 'hsl(215 16% 72%)')}; + padding: 24px 16px; + color: var(--dees-color-text-muted); + font-size: 13px; } - .dropzone__meta span { - padding: 4px 10px; - border-radius: 999px; - background: ${cssManager.bdTheme('hsl(217 91% 95%)', 'hsl(213 93% 18%)')}; - border: 1px solid ${cssManager.bdTheme('hsl(217 91% 90%)', 'hsl(213 93% 24%)')}; + .file-list-empty dees-icon { + font-size: 24px; + opacity: 0.4; } .file-list { display: flex; flex-direction: column; - gap: 12px; - margin-top: 20px; - padding-top: 20px; - border-top: 1px solid ${cssManager.bdTheme('hsl(217 91% 90%)', 'hsl(213 93% 24%)')}; } - .file-list__header { + .file-list-header { display: flex; align-items: center; justify-content: space-between; - font-size: 13px; + padding: 8px 12px; + font-size: 12px; font-weight: 500; - color: ${cssManager.bdTheme('hsl(215 16% 45%)', 'hsl(215 16% 68%)')}; + color: var(--dees-color-text-muted); } - .file-list__clear { + .file-list-clear { appearance: none; border: none; background: none; - color: ${cssManager.bdTheme('hsl(217 91% 60%)', 'hsl(213 93% 68%)')}; + color: var(--dees-color-accent-primary); cursor: pointer; font-weight: 500; - font-size: 13px; + font-size: 12px; padding: 0; + font-family: inherit; } - .file-list__clear:hover { + .file-list-clear:hover { text-decoration: underline; } - .file-list__items { - display: flex; - flex-direction: column; - gap: 12px; - } - .file-row { display: flex; align-items: center; gap: 12px; - padding: 10px 12px; - background: ${cssManager.bdTheme('hsl(0 0% 100% / 0.5)', 'hsl(215 20% 16% / 0.5)')}; - border: 1px solid ${cssManager.bdTheme('hsl(213 27% 92%)', 'hsl(217 25% 26%)')}; - border-radius: 8px; - transition: background 0.15s ease; + padding: 6px 12px; + transition: background var(--dees-transition-fast) ease; } .file-row:hover { - background: ${cssManager.bdTheme('hsl(0 0% 100% / 0.8)', 'hsl(215 20% 16% / 0.8)')}; + background: var(--dees-color-row-hover); } .file-thumb { - width: 36px; - height: 36px; - border-radius: 8px; - background: ${cssManager.bdTheme('hsl(214 31% 92%)', 'hsl(217 32% 18%)')}; + width: 32px; + height: 32px; + border-radius: var(--dees-radius-sm); + background: var(--dees-color-bg-tertiary); display: flex; align-items: center; justify-content: center; @@ -204,16 +161,15 @@ export const fileuploadStyles = [ } .file-thumb dees-icon { - font-size: 18px; - color: ${cssManager.bdTheme('hsl(215 16% 45%)', 'hsl(215 16% 70%)')}; + font-size: 16px; + color: var(--dees-color-text-muted); display: block; - width: 18px; - height: 18px; + width: 16px; + height: 16px; line-height: 1; flex-shrink: 0; } - .thumb-image { width: 100%; height: 100%; @@ -223,14 +179,14 @@ export const fileuploadStyles = [ .file-meta { display: flex; flex-direction: column; - gap: 4px; + gap: 2px; min-width: 0; } .file-name { - font-weight: 600; - font-size: 14px; - color: ${cssManager.bdTheme('hsl(222 47% 11%)', 'hsl(210 20% 96%)')}; + font-weight: 500; + font-size: 13px; + color: var(--dees-color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -241,8 +197,8 @@ export const fileuploadStyles = [ align-items: center; gap: 8px; flex-wrap: wrap; - font-size: 12px; - color: ${cssManager.bdTheme('hsl(215 16% 46%)', 'hsl(215 16% 70%)')}; + font-size: 11px; + color: var(--dees-color-text-muted); } .file-size { @@ -250,39 +206,40 @@ export const fileuploadStyles = [ } .file-type { - padding: 2px 8px; - border-radius: 999px; - border: 1px solid ${cssManager.bdTheme('hsl(214 31% 86%)', 'hsl(217 32% 28%)')}; - color: ${cssManager.bdTheme('hsl(215 16% 46%)', 'hsl(215 16% 70%)')}; + padding: 1px 6px; + border-radius: var(--dees-radius-full); + border: 1px solid var(--dees-color-border-default); + color: var(--dees-color-text-muted); text-transform: uppercase; letter-spacing: 0.08em; line-height: 1; + font-size: 10px; } .file-actions { display: flex; align-items: center; - gap: 8px; margin-left: auto; } .remove-button { - width: 28px; - height: 28px; - border-radius: 6px; + width: 24px; + height: 24px; + border-radius: var(--dees-radius-xs); background: transparent; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; - transition: background 0.15s ease, transform 0.15s ease, color 0.15s ease; - color: ${cssManager.bdTheme('hsl(215 16% 52%)', 'hsl(215 16% 68%)')}; + transition: background var(--dees-transition-fast) ease, + color var(--dees-transition-fast) ease; + color: var(--dees-color-text-muted); } .remove-button:hover { background: ${cssManager.bdTheme('hsl(0 72% 50% / 0.08)', 'hsl(0 62% 32% / 0.15)')}; - color: ${cssManager.bdTheme('hsl(0 72% 46%)', 'hsl(0 70% 70%)')}; + color: var(--dees-color-accent-error); } .remove-button:active { @@ -298,9 +255,28 @@ export const fileuploadStyles = [ flex-shrink: 0; } + /* ── Footer slot: meta chips ── */ + .dropzone-footer { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 6px 12px; + align-items: center; + } + + .meta-chip { + font-size: 11px; + padding: 2px 8px; + border-radius: var(--dees-radius-full); + color: var(--dees-color-text-muted); + background: var(--dees-color-bg-tertiary); + border: 1px solid var(--dees-color-border-subtle); + } + + /* ── Validation ── */ .validation-message { font-size: 13px; - color: ${cssManager.bdTheme('hsl(0 72% 40%)', 'hsl(0 70% 68%)')}; + color: var(--dees-color-accent-error); line-height: 1.5; } diff --git a/ts_web/elements/00theme.ts b/ts_web/elements/00theme.ts index 146ccd1..c2832e1 100644 --- a/ts_web/elements/00theme.ts +++ b/ts_web/elements/00theme.ts @@ -253,6 +253,7 @@ export const themeDefaultStyles: CSSResult = css` --dees-color-hover: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.06)')}; --dees-color-active: ${cssManager.bdTheme('rgba(0, 0, 0, 0.06)', 'rgba(255, 255, 255, 0.08)')}; --dees-color-pressed: ${cssManager.bdTheme('rgba(0, 0, 0, 0.08)', 'rgba(255, 255, 255, 0.12)')}; + --dees-color-row-hover: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.06)', 'hsl(217.2 91.2% 59.8% / 0.08)')}; /* ======================================== * Colors — Focus Ring