From af97e6634dccffeb9ee15b01eea2383691698e00 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Tue, 9 Sep 2025 11:01:06 +0330 Subject: [PATCH 1/4] feat(file-input): improve loading and error style when the input has value --- .../src/file-input/file-input.style.ts | 46 +++++++++++++++++-- .../src/file-input/file-input.ts | 35 ++++++++++---- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/packages/web-components/src/file-input/file-input.style.ts b/packages/web-components/src/file-input/file-input.style.ts index 5e839562..c82cd3ac 100644 --- a/packages/web-components/src/file-input/file-input.style.ts +++ b/packages/web-components/src/file-input/file-input.style.ts @@ -18,6 +18,13 @@ const styles: CSSResult = css` --input-support-color: var(--tapsi-color-content-secondary); --input-control-color: var(--tapsi-color-content-on-inverse); --input-label-color: var(--tapsi-color-content-primary); + --input-loading-state-text-color: var(--tapsi-color-content-secondary); + --input-loading-state-progress-foreground-color: var( + --tapsi-color-content-accent + ); + --input-loading-state-progress-background-color: var( + --tapsi-color-surface-tertiary + ); display: inline-block; vertical-align: middle; @@ -140,15 +147,42 @@ const styles: CSSResult = css` flex-direction: column; align-items: center; justify-content: center; - color: var(--tapsi-color-content-accent); + + color: var(--input-loading-state-progress-foreground-color); + + position: absolute; + } + + .loading-state.on-overlay { + --input-loading-state-text-color: var(--tapsi-color-content-on-inverse); + --input-loading-state-progress-foreground-color: var( + --tapsi-color-content-on-inverse + ); + --input-loading-state-progress-background-color: transparent; } .preview { + position: absolute; + width: calc(100% - var(--tapsi-spacing-6)); height: calc(100% - var(--tapsi-spacing-6)); object-fit: contain; } + .overlay { + position: absolute; + bottom: var(--tapsi-spacing-4); + left: var(--tapsi-spacing-4); + top: var(--tapsi-spacing-4); + right: var(--tapsi-spacing-4); + + border-radius: var(--tapsi-spacing-3); + + background-color: var(--tapsi-color-surface-overlay-dark); + + backdrop-filter: blur(2px); + } + .clear-button { position: absolute; bottom: var(--tapsi-spacing-6); @@ -160,6 +194,8 @@ const styles: CSSResult = css` align-items: center; justify-content: center; flex-direction: column; + + position: absolute; } .error-state .icon { @@ -199,10 +235,10 @@ const styles: CSSResult = css` transform-origin: center; } .background-circle { - stroke: var(--tapsi-color-surface-tertiary); + stroke: var(--input-loading-state-progress-background-color); } .foreground-circle { - stroke: var(--tapsi-color-content-accent); + stroke: var(--input-loading-state-progress-foreground-color); transition: stroke-dashoffset 0.3s ease; } @@ -231,7 +267,7 @@ const styles: CSSResult = css` } .loading-state .text { - color: var(--tapsi-color-content-secondary); + color: var(--input-loading-state-text-color); margin-top: var(--tapsi-spacing-4); @@ -244,6 +280,8 @@ const styles: CSSResult = css` .loading-state .spinner { height: 2rem; width: 2rem; + + color: inherit; } .sr-only { diff --git a/packages/web-components/src/file-input/file-input.ts b/packages/web-components/src/file-input/file-input.ts index b3d50ea1..ae3fb7f3 100644 --- a/packages/web-components/src/file-input/file-input.ts +++ b/packages/web-components/src/file-input/file-input.ts @@ -575,6 +575,10 @@ export class FileInput extends BaseClass { return !this.disabled && !this._isLoading; } + private get _shouldShowOverlay(): boolean { + return !!this.value && (this._hasError() || this._isLoading); + } + private _handleClick() { if (!this._isInteractable) return; @@ -644,7 +648,10 @@ export class FileInput extends BaseClass { private _renderPreview() { const files = this.files; - if (!files) return null; + if (!files || files.length === 0 || !this.value) { + if (this._hasError() || this._isLoading) return null; + else return this._renderEmptyState(); + } if (files.length === 1) { const file = files[0]!; @@ -729,8 +736,13 @@ export class FileInput extends BaseClass { `; } + const loadingClasses = classMap({ + ["loading-state"]: true, + ["on-overlay"]: this._shouldShowOverlay, + }); + return html`
${icon} @@ -809,18 +821,25 @@ export class FileInput extends BaseClass { e.stopPropagation(); } - private _renderFileInputContent() { + private _renderOverlay() { + if (!this._shouldShowOverlay) return null; + + return html`
`; + } + + private _renderOverlayContent() { if (this._hasError()) return this._renderErrorState(); if (this._isLoading) return this._renderLoadingState(); - if (this.value) return this._renderPreview(); - - return this._renderEmptyState(); + return null; } private _renderClearIcon() { - if (!this.value) return null; + if (!this.value || this._shouldShowOverlay) return null; return html` - ${this._renderFileInputContent()}${this._renderClearIcon()} + ${this._renderPreview()}${this._renderOverlay()}${this._renderOverlayContent()}${this._renderClearIcon()}
${this._renderHelperText()} From ff963b91dce698eae711b81f52a28dbe4f0416ad Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Tue, 9 Sep 2025 15:28:04 +0330 Subject: [PATCH 2/4] feat(file-input): save the file instance as a state --- packages/web-components/src/file-input/file-input.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/web-components/src/file-input/file-input.ts b/packages/web-components/src/file-input/file-input.ts index ae3fb7f3..0ee8d4d0 100644 --- a/packages/web-components/src/file-input/file-input.ts +++ b/packages/web-components/src/file-input/file-input.ts @@ -297,7 +297,7 @@ export class FileInput extends BaseClass { * The list of selected files. */ public get files(): FileList | null { - return this._input?.files ?? null; + return this._files; } @state() @@ -317,6 +317,9 @@ export class FileInput extends BaseClass { @state() private _nativeErrorText = ""; + @state() + private _files: FileList | null = null; + @queryAssignedNodes({ slot: Slots.PLACEHOLDER_ICON }) private _placeholderIconSlotNodes!: Node[]; @@ -401,7 +404,8 @@ export class FileInput extends BaseClass { private _handleInput() { if (!this._isInteractable) return; - this.requestUpdate(); + // this.requestUpdate(); + this._files = this._input?.files ?? null; } private _handleChange(event: Event) { @@ -433,6 +437,7 @@ export class FileInput extends BaseClass { if (!input) return; input.files = files; + this._files = files; this.dispatchEvent(new Event("change", { bubbles: true })); this.dispatchEvent(new Event("input", { bubbles: true, composed: true })); From cc684f366a281a5710c4b72569501fe38db3f9f6 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Tue, 9 Sep 2025 15:31:22 +0330 Subject: [PATCH 3/4] docs: update changeset --- .changeset/honest-keys-push.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/honest-keys-push.md diff --git a/.changeset/honest-keys-push.md b/.changeset/honest-keys-push.md new file mode 100644 index 00000000..67614dad --- /dev/null +++ b/.changeset/honest-keys-push.md @@ -0,0 +1,6 @@ +--- +"@tapsioss/web-components": patch +--- + +Make the file input's preview remain visible on loading and error state. + \ No newline at end of file From 266aaaa1ba8fb0f7c40213cc3ce733658f316503 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Sun, 14 Sep 2025 19:32:46 +0330 Subject: [PATCH 4/4] fix(web-components/file-input): fix progress rotation degree --- packages/web-components/src/file-input/file-input.style.ts | 6 ++---- packages/web-components/src/file-input/file-input.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/web-components/src/file-input/file-input.style.ts b/packages/web-components/src/file-input/file-input.style.ts index c82cd3ac..998dfe68 100644 --- a/packages/web-components/src/file-input/file-input.style.ts +++ b/packages/web-components/src/file-input/file-input.style.ts @@ -230,10 +230,6 @@ const styles: CSSResult = css` font-weight: var(--tapsi-typography-body-sm-weight); } - .progress-circle { - transform: rotate(-90deg); - transform-origin: center; - } .background-circle { stroke: var(--input-loading-state-progress-background-color); } @@ -255,6 +251,8 @@ const styles: CSSResult = css` .progress-wrapper .progress { width: 100%; height: 100%; + + transform: rotate(-90deg); } .progress-wrapper .percentage { diff --git a/packages/web-components/src/file-input/file-input.ts b/packages/web-components/src/file-input/file-input.ts index 0ee8d4d0..116a93da 100644 --- a/packages/web-components/src/file-input/file-input.ts +++ b/packages/web-components/src/file-input/file-input.ts @@ -844,7 +844,7 @@ export class FileInput extends BaseClass { } private _renderClearIcon() { - if (!this.value || this._shouldShowOverlay) return null; + if (this._shouldShowOverlay || !this.value) return null; return html`