fix(icons): update icon usage across components

- Replace .iconName property with .icon for dees-icon component
- Fix incorrect lucide icon names to use proper prefix and kebab-case
- Replace deprecated .iconFA property with .icon
- Add loading animation to dees-input-fileupload button
- Maintain compatibility with external interfaces expecting iconName
This commit is contained in:
Juergen Kunz
2025-06-30 12:57:13 +00:00
parent 1db74177b3
commit 956edf0d63
9 changed files with 101 additions and 23 deletions

View File

@ -168,7 +168,7 @@ export class DeesChips extends DeesElement {
event.stopPropagation(); // prevent the selectChip event from being triggered event.stopPropagation(); // prevent the selectChip event from being triggered
this.removeChip(chip); this.removeChip(chip);
}} }}
.iconFA=${'xmark'} .icon=${'fa:xmark'}
></dees-icon> ></dees-icon>
` `
: html``} : html``}

View File

@ -175,21 +175,21 @@ export class DeesDataviewStatusobject extends DeesElement {
DeesContextmenu.openContextMenuWithOptions(event, [ DeesContextmenu.openContextMenuWithOptions(event, [
{ {
name: 'Copy Value', name: 'Copy Value',
iconName: 'lucideCopy', iconName: 'lucide:copy',
action: async () => { action: async () => {
await this.copyToClipboard(detailArg.value, 'Value'); await this.copyToClipboard(detailArg.value, 'Value');
}, },
}, },
{ {
name: 'Copy Key', name: 'Copy Key',
iconName: 'lucideKey', iconName: 'lucide:key',
action: async () => { action: async () => {
await this.copyToClipboard(detailArg.name, 'Key'); await this.copyToClipboard(detailArg.name, 'Key');
}, },
}, },
{ {
name: 'Copy Key:Value', name: 'Copy Key:Value',
iconName: 'lucideCopyPlus', iconName: 'lucide:copy-plus',
action: async () => { action: async () => {
await this.copyToClipboard(`${detailArg.name}: ${detailArg.value}`, 'Key:Value'); await this.copyToClipboard(`${detailArg.name}: ${detailArg.value}`, 'Key:Value');
}, },

View File

@ -37,6 +37,9 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
@property() @property()
public state: 'idle' | 'dragOver' | 'dropped' | 'uploading' | 'completed' = 'idle'; public state: 'idle' | 'dragOver' | 'dropped' | 'uploading' | 'completed' = 'idle';
@property({ type: Boolean })
private isLoading: boolean = false;
@property({ @property({
type: String, type: String,
}) })
@ -317,6 +320,53 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
margin-top: 6px; margin-top: 6px;
line-height: 1.5; line-height: 1.5;
} }
/* Loading state styles */
.uploadButton.loading {
pointer-events: none;
opacity: 0.8;
}
.uploadButton .button-content {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid ${cssManager.bdTheme('rgba(0, 0, 0, 0.1)', 'rgba(255, 255, 255, 0.1)')};
border-top-color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.02);
opacity: 0.9;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.uploadButton.loading {
animation: pulse 1s ease-in-out infinite;
}
`, `,
]; ];
@ -353,7 +403,7 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
${isImage && this.canShowPreview(fileArg) ? html` ${isImage && this.canShowPreview(fileArg) ? html`
<img class="image-preview" src="${URL.createObjectURL(fileArg)}" alt="${fileArg.name}"> <img class="image-preview" src="${URL.createObjectURL(fileArg)}" alt="${fileArg.name}">
` : html` ` : html`
<dees-icon .iconName=${this.getFileIcon(fileArg)}></dees-icon> <dees-icon .icon=${this.getFileIcon(fileArg)}></dees-icon>
`} `}
</div> </div>
<div class="info"> <div class="info">
@ -366,7 +416,7 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
@click=${() => this.removeFile(fileArg)} @click=${() => this.removeFile(fileArg)}
title="Remove file" title="Remove file"
> >
<dees-icon .iconName=${'lucide:x'}></dees-icon> <dees-icon .icon=${'lucide:x'}></dees-icon>
</button> </button>
</div> </div>
</div> </div>
@ -375,13 +425,20 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
</div> </div>
` : html` ` : html`
<div class="drop-hint"> <div class="drop-hint">
<dees-icon .iconName=${'lucide:cloud-upload'}></dees-icon> <dees-icon .icon=${'lucide:cloud-upload'}></dees-icon>
<div>Drag files here or click to browse</div> <div>Drag files here or click to browse</div>
</div> </div>
`} `}
<div class="uploadButton" @click=${this.openFileSelector}> <div class="uploadButton ${this.isLoading ? 'loading' : ''}" @click=${this.openFileSelector}>
<dees-icon .iconName=${'lucide:upload'}></dees-icon> <div class="button-content">
${this.isLoading ? html`
<div class="loading-spinner"></div>
<span>Opening...</span>
` : html`
<dees-icon .icon=${'lucide:upload'}></dees-icon>
${this.buttonText} ${this.buttonText}
`}
</div>
</div> </div>
</div> </div>
${this.description ? html` ${this.description ? html`
@ -482,8 +539,25 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
} }
public async openFileSelector() { public async openFileSelector() {
if (this.disabled) return; if (this.disabled || this.isLoading) return;
// Set loading state
this.isLoading = true;
const inputFile: HTMLInputElement = this.shadowRoot.querySelector('input[type="file"]'); const inputFile: HTMLInputElement = this.shadowRoot.querySelector('input[type="file"]');
// Set up a focus handler to detect when the dialog is closed without selection
const handleFocus = () => {
setTimeout(() => {
// Check if no file was selected
if (!inputFile.files || inputFile.files.length === 0) {
this.isLoading = false;
}
window.removeEventListener('focus', handleFocus);
}, 300);
};
window.addEventListener('focus', handleFocus);
inputFile.click(); inputFile.click();
} }
@ -516,6 +590,10 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
inputFile.addEventListener('change', async (event: Event) => { inputFile.addEventListener('change', async (event: Event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
const newFiles = Array.from(target.files); const newFiles = Array.from(target.files);
// Always reset loading state when file dialog interaction completes
this.isLoading = false;
await this.addFiles(newFiles); await this.addFiles(newFiles);
// Reset the input value to allow selecting the same file again if needed // Reset the input value to allow selecting the same file again if needed
target.value = ''; target.value = '';

View File

@ -231,7 +231,7 @@ export class DeesInputText extends DeesInputBase {
${this.isPasswordBool ${this.isPasswordBool
? html` ? html`
<div class="showPassword" @click=${this.togglePasswordView}> <div class="showPassword" @click=${this.togglePasswordView}>
<dees-icon .iconName=${this.showPasswordBool ? 'lucideEye' : 'lucideEyeOff'}></dees-icon> <dees-icon .icon=${this.showPasswordBool ? 'lucide:eye' : 'lucide:eye-off'}></dees-icon>
</div> </div>
` `
: html``} : html``}

View File

@ -82,7 +82,7 @@ export class DeesLabel extends DeesElement {
${this.required ? html`<span class="required">*</span>` : ''} ${this.required ? html`<span class="required">*</span>` : ''}
${this.description ${this.description
? html` ? html`
<dees-icon .iconName=${'lucideInfo'}></dees-icon> <dees-icon .icon=${'lucide:info'}></dees-icon>
<dees-speechbubble .text=${this.description}></dees-speechbubble> <dees-speechbubble .text=${this.description}></dees-speechbubble>
` `
: html``} : html``}

View File

@ -252,7 +252,7 @@ export class DeesShoppingProductcard extends DeesElement {
${imageUrl ? html` ${imageUrl ? html`
<img src="${imageUrl}" alt="${name}"> <img src="${imageUrl}" alt="${name}">
` : html` ` : html`
<dees-icon .iconName=${iconName}></dees-icon> <dees-icon .icon=${iconName}></dees-icon>
`} `}
${this.selectable ? html` ${this.selectable ? html`
<div <div
@ -262,7 +262,7 @@ export class DeesShoppingProductcard extends DeesElement {
this.handleSelectionToggle(); this.handleSelectionToggle();
}} }}
> >
<dees-icon .iconName=${'lucide:check'}></dees-icon> <dees-icon .icon=${'lucide:check'}></dees-icon>
</div> </div>
` : ''} ` : ''}
</div> </div>
@ -275,7 +275,7 @@ export class DeesShoppingProductcard extends DeesElement {
<div class="product-description">${description}</div> <div class="product-description">${description}</div>
` : ''} ` : ''}
<div class="stock-status ${inStock ? 'in-stock' : 'out-of-stock'}"> <div class="stock-status ${inStock ? 'in-stock' : 'out-of-stock'}">
<dees-icon .iconName=${inStock ? 'lucide:check-circle' : 'lucide:x-circle'}></dees-icon> <dees-icon .icon=${inStock ? 'lucide:check-circle' : 'lucide:x-circle'}></dees-icon>
${stockText} ${stockText}
</div> </div>
<div class="product-footer"> <div class="product-footer">

View File

@ -120,9 +120,9 @@ export class DeesSpinner extends DeesElement {
<div class="${this.status}" id="loading"> <div class="${this.status}" id="loading">
${(() => { ${(() => {
if (this.status === 'success') { if (this.status === 'success') {
return html`<dees-icon style="transform: translateX(1%) translateY(3%);" .iconFA=${'circleCheck' as any}></dees-icon>`; return html`<dees-icon style="transform: translateX(1%) translateY(3%);" .icon=${'fa:circle-check'}></dees-icon>`;
} else if (this.status === 'error') { } else if (this.status === 'error') {
return html`<dees-icon .iconFA=${'circleXmark' as any}></dees-icon>`; return html`<dees-icon .icon=${'fa:circle-xmark'}></dees-icon>`;
} }
})()} })()}
</div> </div>

View File

@ -391,7 +391,7 @@ export class DeesStatsGrid extends DeesElement {
type="outline" type="outline"
size="sm" size="sm"
> >
${action.iconName ? html`<dees-icon .iconFA=${action.iconName} size="small"></dees-icon>` : ''} ${action.iconName ? html`<dees-icon .icon=${action.iconName} size="small"></dees-icon>` : ''}
${action.name} ${action.name}
</dees-button> </dees-button>
`)} `)}
@ -427,7 +427,7 @@ export class DeesStatsGrid extends DeesElement {
<div class="tile-header"> <div class="tile-header">
<h3 class="tile-title">${tile.title}</h3> <h3 class="tile-title">${tile.title}</h3>
${tile.icon ? html` ${tile.icon ? html`
<dees-icon class="tile-icon" .iconFA=${tile.icon} size="small"></dees-icon> <dees-icon class="tile-icon" .icon=${tile.icon} size="small"></dees-icon>
` : ''} ` : ''}
</div> </div>

View File

@ -568,7 +568,7 @@ export class DeesTable<T> extends DeesElement {
}} }}
> >
${action.iconName ${action.iconName
? html`<dees-icon .iconSize=${14} .iconFA=${action.iconName}></dees-icon> ? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
${action.name}` ${action.name}`
: action.name} : action.name}
</div>` </div>`
@ -743,7 +743,7 @@ export class DeesTable<T> extends DeesElement {
${actionArg.iconName ${actionArg.iconName
? html` ? html`
<dees-icon <dees-icon
.iconFA=${actionArg.iconName} .icon=${actionArg.iconName}
></dees-icon> ></dees-icon>
` `
: actionArg.name} : actionArg.name}
@ -785,7 +785,7 @@ export class DeesTable<T> extends DeesElement {
}} }}
> >
${action.iconName ${action.iconName
? html`<dees-icon .iconSize=${14} .iconFA=${action.iconName}></dees-icon> ? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
${action.name}` ${action.name}`
: action.name} : action.name}
</div>` </div>`