diff --git a/ts_web/elements/dees-chips.ts b/ts_web/elements/dees-chips.ts index cc0f37a..5e92203 100644 --- a/ts_web/elements/dees-chips.ts +++ b/ts_web/elements/dees-chips.ts @@ -168,7 +168,7 @@ export class DeesChips extends DeesElement { event.stopPropagation(); // prevent the selectChip event from being triggered this.removeChip(chip); }} - .iconFA=${'xmark'} + .icon=${'fa:xmark'} > ` : html``} diff --git a/ts_web/elements/dees-dataview-statusobject.ts b/ts_web/elements/dees-dataview-statusobject.ts index e41c06d..395e9a8 100644 --- a/ts_web/elements/dees-dataview-statusobject.ts +++ b/ts_web/elements/dees-dataview-statusobject.ts @@ -175,21 +175,21 @@ export class DeesDataviewStatusobject extends DeesElement { DeesContextmenu.openContextMenuWithOptions(event, [ { name: 'Copy Value', - iconName: 'lucideCopy', + iconName: 'lucide:copy', action: async () => { await this.copyToClipboard(detailArg.value, 'Value'); }, }, { name: 'Copy Key', - iconName: 'lucideKey', + iconName: 'lucide:key', action: async () => { await this.copyToClipboard(detailArg.name, 'Key'); }, }, { name: 'Copy Key:Value', - iconName: 'lucideCopyPlus', + iconName: 'lucide:copy-plus', action: async () => { await this.copyToClipboard(`${detailArg.name}: ${detailArg.value}`, 'Key:Value'); }, diff --git a/ts_web/elements/dees-input-fileupload.ts b/ts_web/elements/dees-input-fileupload.ts index 7f6471e..a713459 100644 --- a/ts_web/elements/dees-input-fileupload.ts +++ b/ts_web/elements/dees-input-fileupload.ts @@ -37,6 +37,9 @@ export class DeesInputFileupload extends DeesInputBase { @property() public state: 'idle' | 'dragOver' | 'dropped' | 'uploading' | 'completed' = 'idle'; + @property({ type: Boolean }) + private isLoading: boolean = false; + @property({ type: String, }) @@ -317,6 +320,53 @@ export class DeesInputFileupload extends DeesInputBase { margin-top: 6px; 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 { ${isImage && this.canShowPreview(fileArg) ? html` ${fileArg.name} ` : html` - + `}
@@ -366,7 +416,7 @@ export class DeesInputFileupload extends DeesInputBase { @click=${() => this.removeFile(fileArg)} title="Remove file" > - +
@@ -375,13 +425,20 @@ export class DeesInputFileupload extends DeesInputBase { ` : html`
- +
Drag files here or click to browse
`} -
- - ${this.buttonText} +
+
+ ${this.isLoading ? html` +
+ Opening... + ` : html` + + ${this.buttonText} + `} +
${this.description ? html` @@ -482,8 +539,25 @@ export class DeesInputFileupload extends DeesInputBase { } 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"]'); + + // 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(); } @@ -516,6 +590,10 @@ export class DeesInputFileupload extends DeesInputBase { inputFile.addEventListener('change', async (event: Event) => { const target = event.target as HTMLInputElement; const newFiles = Array.from(target.files); + + // Always reset loading state when file dialog interaction completes + this.isLoading = false; + await this.addFiles(newFiles); // Reset the input value to allow selecting the same file again if needed target.value = ''; diff --git a/ts_web/elements/dees-input-text.ts b/ts_web/elements/dees-input-text.ts index e4ba326..a311234 100644 --- a/ts_web/elements/dees-input-text.ts +++ b/ts_web/elements/dees-input-text.ts @@ -231,7 +231,7 @@ export class DeesInputText extends DeesInputBase { ${this.isPasswordBool ? html`
- +
` : html``} diff --git a/ts_web/elements/dees-label.ts b/ts_web/elements/dees-label.ts index f5b7b97..659e340 100644 --- a/ts_web/elements/dees-label.ts +++ b/ts_web/elements/dees-label.ts @@ -82,7 +82,7 @@ export class DeesLabel extends DeesElement { ${this.required ? html`*` : ''} ${this.description ? html` - + ` : html``} diff --git a/ts_web/elements/dees-shopping-productcard.ts b/ts_web/elements/dees-shopping-productcard.ts index df7a5d3..6c27a34 100644 --- a/ts_web/elements/dees-shopping-productcard.ts +++ b/ts_web/elements/dees-shopping-productcard.ts @@ -252,7 +252,7 @@ export class DeesShoppingProductcard extends DeesElement { ${imageUrl ? html` ${name} ` : html` - + `} ${this.selectable ? html`
- +
` : ''} @@ -275,7 +275,7 @@ export class DeesShoppingProductcard extends DeesElement {
${description}
` : ''}
- + ${stockText}
` @@ -743,7 +743,7 @@ export class DeesTable extends DeesElement { ${actionArg.iconName ? html` ` : actionArg.name} @@ -785,7 +785,7 @@ export class DeesTable extends DeesElement { }} > ${action.iconName - ? html` + ? html` ${action.name}` : action.name} `