feat(dees-input-fileupload): redesign the file upload dropzone with dees-tile integration and themed file list styling

This commit is contained in:
2026-04-05 00:00:35 +00:00
parent a7a710b320
commit 465f7585ac
5 changed files with 147 additions and 178 deletions

View File

@@ -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<DeesInputFileupload> {
.description=${this.description}
.required=${this.required}
></dees-label>
<div
class="dropzone ${this.state === 'dragOver' ? 'dropzone--active' : ''} ${this.disabled ? 'dropzone--disabled' : ''} ${this.value.length > 0 ? 'dropzone--has-files' : ''}"
role="button"
<dees-tile
class="${this.state === 'dragOver' ? 'dragover' : ''}"
@click=${this.handleDropzoneClick}
@keydown=${this.handleDropzoneKeydown}
tabindex=${this.disabled ? -1 : 0}
aria-disabled=${this.disabled}
aria-label=${`Select files${acceptedSummary ? ` (${acceptedSummary})` : ''}`}
@click=${this.handleDropzoneClick}
@keydown=${this.handleDropzoneKeydown}
>
<input
class="file-input"
@@ -94,32 +94,23 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
@change=${this.handleFileInputChange}
tabindex="-1"
/>
<div class="dropzone__body">
<div class="dropzone__icon">
${this.isLoading
? html`<span class="dropzone__loader" aria-hidden="true"></span>`
: html`<dees-icon icon="lucide:FolderOpen"></dees-icon>`}
</div>
<div class="dropzone__content">
<span class="dropzone__headline">${this.buttonText || 'Select files'}</span>
<span class="dropzone__subline">
Drag and drop files here or
<button
type="button"
class="dropzone__browse"
@click=${this.handleBrowseClick}
?disabled=${this.disabled}
>
browse
</button>
</span>
</div>
</div>
<div class="dropzone__meta">
${metaEntries.map((entry) => html`<span>${entry}</span>`)}
<div slot="header" class="dropzone-header">
${this.isLoading
? html`<span class="dropzone-loader" aria-hidden="true"></span>`
: html`<dees-icon icon="lucide:Upload"></dees-icon>`}
<span class="dropzone-title">Drop files here or</span>
<button
type="button"
class="dropzone-browse"
@click=${(e: MouseEvent) => { e.stopPropagation(); this.openFileSelector(); }}
?disabled=${this.disabled}
>browse</button>
</div>
${this.renderFileList()}
</div>
<div slot="footer" class="dropzone-footer">
${metaEntries.map((entry) => html`<span class="meta-chip">${entry}</span>`)}
</div>
</dees-tile>
${this.validationMessage
? html`<div class="validation-message" aria-live="polite">${this.validationMessage}</div>`
: html``}
@@ -129,20 +120,21 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
private renderFileList(): TemplateResult {
if (this.value.length === 0) {
return html``;
return html`
<div class="file-list-empty">
<dees-icon icon="lucide:FileStack"></dees-icon>
<span>No files selected</span>
</div>
`;
}
return html`
<div class="file-list">
<div class="file-list__header">
<div class="file-list-header">
<span>${this.value.length} file${this.value.length === 1 ? '' : 's'} selected</span>
${this.value.length > 0
? html`<button type="button" class="file-list__clear" @click=${this.handleClearAll}>Clear ${this.value.length > 1 ? 'all' : ''}</button>`
: html``}
</div>
<div class="file-list__items">
${this.value.map((file) => this.renderFileRow(file))}
<button type="button" class="file-list-clear" @click=${this.handleClearAll}>Clear ${this.value.length > 1 ? 'all' : ''}</button>
</div>
${this.value.map((file) => this.renderFileRow(file))}
</div>
`;
}
@@ -193,21 +185,14 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
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<DeesInputFileupload> {
}
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();