From 2f99d20516f8567aa8eab3f5c39ea442074846c5 Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Thu, 7 Dec 2023 11:50:57 +0100 Subject: [PATCH] Replace progress - element with sl-progress-bar Replace the default progress - element with the shoelace progress-bar - component, to have a better control over the styling. The progress - element does not allow animations in Chrome anymore. The server response can take some time. We need to show the user that there is still progress. The indeterminate - attribute is showing an animation, after the upload was finished. --- .../alchemy/_custom-properties.scss | 20 ++--- app/assets/stylesheets/alchemy/upload.scss | 86 ++++++++----------- app/javascript/alchemy_admin.js | 2 + .../components/uploader/file_upload.js | 17 ++-- .../components/uploader/progress.js | 19 +++- app/javascript/alchemy_admin/locales/en.js | 1 + config/importmap.rb | 1 + .../alchemy_admin/components/uploader.spec.js | 4 +- .../components/uploader/file_upload.spec.js | 2 +- .../components/uploader/progress.spec.js | 2 +- 10 files changed, 83 insertions(+), 71 deletions(-) diff --git a/app/assets/stylesheets/alchemy/_custom-properties.scss b/app/assets/stylesheets/alchemy/_custom-properties.scss index 0a1b61d4f1..ba28308cbd 100644 --- a/app/assets/stylesheets/alchemy/_custom-properties.scss +++ b/app/assets/stylesheets/alchemy/_custom-properties.scss @@ -67,15 +67,15 @@ --sl-input-label-color: var(--color-text); --file-upload_background-color: hsla(0deg, 0%, 70%, 0.8); - --file-upload_single-upload-background-color: var(--color-grey_medium); - --file-upload_progress-bar-color: var(--color-blue_very_light); - --file-upload_progress-value-color: var(--color-blue_dark); - - --file-upload_progress-value-color-canceled: hsla(0deg, 0%, 60%, 0.8); - --file-upload_progress-value-color-failed: var(--color-red_medium); - --file-upload_progress-value-color-invalid: var(--color-red_medium); - --file-upload_progress-value-color-successful: var(--color-green_medium); - --file-upload_progress-value-color-upload-finished: var( - --color-grey-blue_light + --file-upload_single-upload-background-color: var(--color-grey_light); + --file-upload_progress-track-color: var(--color-blue_very_light); + --file-upload_progress-indicator-color: var(--color-blue_dark); + + --file-upload_progress-indicator-color-canceled: hsla(0deg, 0%, 60%, 0.8); + --file-upload_progress-indicator-color-failed: var(--color-red_medium); + --file-upload_progress-indicator-color-invalid: var(--color-red_medium); + --file-upload_progress-indicator-color-successful: var(--color-green_medium); + --file-upload_progress-indicator-color-upload-finished: var( + --color-blue_dark ); } diff --git a/app/assets/stylesheets/alchemy/upload.scss b/app/assets/stylesheets/alchemy/upload.scss index 293969946f..b6969158e8 100644 --- a/app/assets/stylesheets/alchemy/upload.scss +++ b/app/assets/stylesheets/alchemy/upload.scss @@ -25,19 +25,21 @@ position: relative; &:after { - content: ""; + align-items: center; + background-color: rgba($dark-gray, 0.6); + color: rgba(255, 255, 255, 0.6); + content: $ri-upload-cloud-line; + display: flex; + font-family: "remixicon"; + font-size: 80px; + justify-content: center; + height: 100%; + left: 0; + pointer-events: none; position: absolute; + width: 100%; top: 0; - left: 0; z-index: 20; - width: 100%; - height: 100%; - background-color: rgba($dark-gray, 0.6); - background-image: url("data:image/svg+xml;utf8,%3Csvg%20style%3D%22enable-background%3Anew%200%200%2064%2046.9%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xml%3Aspace%3D%22preserve%22%20version%3D%221.1%22%20y%3D%220px%22%20x%3D%220px%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewBox%3D%220%200%2064%2046.9%22%3E%3Cpath%20fill%3D%22%23ededed%22%20d%3D%22m21.3%2024.5c0-0.3%200.1-0.6%200.3-0.8l11.8-11.7c0.2-0.2%200.5-0.3%200.8-0.3s0.6%200.1%200.8%200.3l11.7%2011.7c0.2%200.3%200.3%200.5%200.3%200.8s-0.1%200.6-0.3%200.8-0.5%200.3-0.8%200.3h-7.5v11.7c0%200.3-0.1%200.5-0.3%200.8-0.2%200.2-0.5%200.3-0.8%200.3h-6.4c-0.3%200-0.5-0.1-0.8-0.3s-0.3-0.5-0.3-0.8v-11.7h-7.5c-0.3%200-0.5-0.1-0.8-0.3s-0.2-0.5-0.2-0.8m-21.3%209.6c0%203.5%201.3%206.6%203.8%209%202.5%202.5%205.5%203.8%209%203.8h36.3c4.1%200%207.6-1.5%2010.6-4.4%202.8-2.9%204.3-6.4%204.3-10.5%200-2.9-0.8-5.6-2.3-8-1.6-2.4-3.6-4.3-6.3-5.5%200-0.7%200.1-1.1%200.1-1.4%200-4.7-1.7-8.7-5-12.1-3.3-3.3-7.4-5-12.1-5-3.5%200-6.6%201-9.5%202.9s-5%204.5-6.3%207.7c-1.6-1.4-3.4-2.1-5.5-2.1-2.4%200-4.4%200.8-6%202.5-1.7%201.7-2.5%203.7-2.5%206%200%201.7%200.5%203.2%201.4%204.6-2.9%200.7-5.3%202.2-7.1%204.5-2%202.4-2.9%205.1-2.9%208%22%2F%3E%3C%2Fsvg%3E"); - background-position: center; - background-size: 64px 47px; - background-repeat: no-repeat; - pointer-events: none; } } @@ -66,30 +68,6 @@ alchemy-upload-progress { bottom: 0; opacity: 1; } - progress { - appearance: none; - background-color: var(--file-upload_progress-bar-color); - border: none; - border-radius: var(--progress-border-radius, var(--border-radius)); - height: var(--progress-height, 20px); - margin: auto; - width: 100%; - - &::-webkit-progress-bar { - background-color: var(--file-upload_progress-bar-color); - border-radius: var(--progress-border-radius, var(--border-radius)); - } - - &::-moz-progress-bar { - background-color: var(--file-upload_progress-value-color); - } - - &::-webkit-progress-value { - background-color: var(--file-upload_progress-value-color); - background-size: 1rem 1rem; - border-radius: var(--progress-border-radius, var(--border-radius)); - } - } .value-text { color: white; @@ -103,7 +81,7 @@ alchemy-upload-progress { --padding: var(--spacing-2); --progress-border-radius: var(--border-radius_medium) var(--border-radius_medium) 0 0; - --progress-height: var(--spacing-3); + --progress-height: var(--spacing-1); display: grid; gap: var(--spacing-2); @@ -161,43 +139,51 @@ alchemy-upload-progress { } } - progress { + sl-progress-bar { + --height: var(--progress-height); left: 0; position: absolute; - top: calc(-1 * var(--progress-height)); + top: calc(-1 * var(--progress-height) / 2); + width: 100%; + } + } + + sl-progress-bar { + --indicator-color: var(--file-upload_progress-indicator-color); + --sl-border-radius-pill: var(--border-radius); + --track-color: var(--file-upload_progress-track-color); + &::part(base) { + top: calc(50% - var(--height) / 2); } } } .successful { - --file-upload_progress-value-color: var( - --file-upload_progress-value-color-successful + --file-upload_progress-indicator-color: var( + --file-upload_progress-indicator-color-successful ); } .failed { - --file-upload_progress-value-color: var( - --file-upload_progress-value-color-failed + --file-upload_progress-indicator-color: var( + --file-upload_progress-indicator-color-failed ); } .canceled { - --file-upload_progress-value-color: var( - --file-upload_progress-value-color-canceled + --file-upload_progress-indicator-color: var( + --file-upload_progress-indicator-color-canceled ); } .invalid { - --file-upload_progress-value-color: var( - --file-upload_progress-value-color-invalid - ); - --file-upload_progress-bar-color: var( - --file-upload_progress-value-color-invalid + --file-upload_progress-indicator-color: var( + --file-upload_progress-indicator-color-invalid ); } .upload-finished { - --file-upload_progress-value-color: var( - --file-upload_progress-value-color-upload-finished + --file-upload_progress-indicator-color: var( + --file-upload_progress-indicator-color-upload-finished ); } diff --git a/app/javascript/alchemy_admin.js b/app/javascript/alchemy_admin.js index acace8e9a6..e2d4242c4d 100644 --- a/app/javascript/alchemy_admin.js +++ b/app/javascript/alchemy_admin.js @@ -32,6 +32,7 @@ import "alchemy_admin/components/page_select" import "alchemy_admin/components/select" import "alchemy_admin/components/spinner" import "alchemy_admin/components/tinymce" +import "@shoelace/progress-bar" import "@shoelace/switch" import "@shoelace/tab" import "@shoelace/tab-group" @@ -61,6 +62,7 @@ setDefaultAnimation("tooltip.hide", { } }) + // Global Alchemy object if (typeof window.Alchemy === "undefined") { window.Alchemy = {} diff --git a/app/javascript/alchemy_admin/components/uploader/file_upload.js b/app/javascript/alchemy_admin/components/uploader/file_upload.js index 61fd460b2d..c0fe63cf5a 100644 --- a/app/javascript/alchemy_admin/components/uploader/file_upload.js +++ b/app/javascript/alchemy_admin/components/uploader/file_upload.js @@ -25,15 +25,17 @@ export class FileUpload extends AlchemyHTMLElement { render() { return ` - +
${this.file?.name} ${this.loadedSize} ${this.errorMessage}
- + + + ` } @@ -181,7 +183,7 @@ export class FileUpload extends AlchemyHTMLElement { * @returns {HTMLProgressElement|undefined} */ get progressElement() { - return this.querySelector("progress") + return this.querySelector("sl-progress-bar") } /** @@ -220,6 +222,11 @@ export class FileUpload extends AlchemyHTMLElement { set status(status) { this._status = status this.className = status + + this.progressElement?.toggleAttribute( + "indeterminate", + status === "upload-finished" + ) } /** diff --git a/app/javascript/alchemy_admin/components/uploader/progress.js b/app/javascript/alchemy_admin/components/uploader/progress.js index 4228ab4ca0..47fd9b4fa1 100644 --- a/app/javascript/alchemy_admin/components/uploader/progress.js +++ b/app/javascript/alchemy_admin/components/uploader/progress.js @@ -23,7 +23,7 @@ export class Progress extends AlchemyHTMLElement { render() { return ` - +
entry.finished) } + /** + * @returns {HTMLProgressElement|undefined} + */ + get progressElement() { + return this.querySelector("sl-progress-bar") + } + /** * get status of file progresses and accumulate the overall status * @returns {string} diff --git a/app/javascript/alchemy_admin/locales/en.js b/app/javascript/alchemy_admin/locales/en.js index 71b2da12f7..36432b1008 100644 --- a/app/javascript/alchemy_admin/locales/en.js +++ b/app/javascript/alchemy_admin/locales/en.js @@ -19,6 +19,7 @@ export const en = { "File type not allowed": "File type not allowed", "Maximum number of files exceeded": "Maximum number of files exceeded", "Uploaded bytes exceed file size": "Uploaded bytes exceed file size", + "Abort upload": "Abort upload", formats: { datetime: "Y-m-d H:i", date: "Y-m-d", diff --git a/config/importmap.rb b/config/importmap.rb index 6a731e956d..77496dbf67 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -10,6 +10,7 @@ pin "@shoelace/tab-group", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.12.0/cdn/components/tab-group/tab-group.js", preload: true pin "@shoelace/tab-panel", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.12.0/cdn/components/tab-panel/tab-panel.js", preload: true pin "@shoelace/tooltip", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.12.0/cdn/components/tooltip/tooltip.js", preload: true +pin "@shoelace/progress-bar", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.12.0/cdn/components/progress-bar/progress-bar.js", preload: true pin "@rails/ujs", to: "https://ga.jspm.io/npm:@rails/ujs@7.1.2/app/assets/javascripts/rails-ujs.esm.js" pin "alchemy_admin", to: "alchemy_admin.js", preload: true diff --git a/spec/javascript/alchemy_admin/components/uploader.spec.js b/spec/javascript/alchemy_admin/components/uploader.spec.js index 07242191ab..6d7f7ca618 100644 --- a/spec/javascript/alchemy_admin/components/uploader.spec.js +++ b/spec/javascript/alchemy_admin/components/uploader.spec.js @@ -108,7 +108,9 @@ describe("alchemy-uploader", () => { beforeEach(() => { component._uploadFiles([firstFile]) - progressBar = document.querySelector("alchemy-upload-progress progress") + progressBar = document.querySelector( + "alchemy-upload-progress sl-progress-bar" + ) }) it("shows upload component", () => { diff --git a/spec/javascript/alchemy_admin/components/uploader/file_upload.spec.js b/spec/javascript/alchemy_admin/components/uploader/file_upload.spec.js index 393d1ce503..bd62e7c7e3 100644 --- a/spec/javascript/alchemy_admin/components/uploader/file_upload.spec.js +++ b/spec/javascript/alchemy_admin/components/uploader/file_upload.spec.js @@ -39,7 +39,7 @@ describe("alchemy-file-upload", () => { document.body.innerHTML = "" // reset previous content to prevent raise conditions document.body.append(component) - progressBar = document.querySelector("progress") + progressBar = document.querySelector("sl-progress-bar") fileName = document.querySelector(".file-name") loadedSize = document.querySelector(".loaded-size") cancelButton = document.querySelector("button") diff --git a/spec/javascript/alchemy_admin/components/uploader/progress.spec.js b/spec/javascript/alchemy_admin/components/uploader/progress.spec.js index 492997be22..a98052218b 100644 --- a/spec/javascript/alchemy_admin/components/uploader/progress.spec.js +++ b/spec/javascript/alchemy_admin/components/uploader/progress.spec.js @@ -48,7 +48,7 @@ describe("alchemy-upload-progress", () => { document.body.append(component) - progressBar = document.querySelector("progress") + progressBar = document.querySelector("sl-progress-bar") overallProgressValue = document.querySelector(".overall-progress-value") overallUploadValue = document.querySelector(".overall-upload-value")