}}
...attributes
>
+ {{#if @autoClose}}
+
+ {{/if}}
{{#if @data.icon}}
{{icon @data.icon}}
diff --git a/app/assets/javascripts/float-kit/addon/components/d-toast.gjs b/app/assets/javascripts/float-kit/addon/components/d-toast.gjs
new file mode 100644
index 00000000000..e402f015764
--- /dev/null
+++ b/app/assets/javascripts/float-kit/addon/components/d-toast.gjs
@@ -0,0 +1,135 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { registerDestructor } from "@ember/destroyable";
+import { action } from "@ember/object";
+import { cancel } from "@ember/runloop";
+import Modifier from "ember-modifier";
+import concatClass from "discourse/helpers/concat-class";
+import discourseLater from "discourse-common/lib/later";
+import { bind } from "discourse-common/utils/decorators";
+
+const CSS_TRANSITION_DELAY_MS = 300;
+const TRANSITION_CLASS = "-fade-out";
+
+class AutoCloseToast extends Modifier {
+ element;
+ close;
+ duration;
+ transitionLaterHandler;
+ closeLaterHandler;
+ progressBar;
+ progressAnimation;
+
+ constructor(owner, args) {
+ super(owner, args);
+
+ registerDestructor(this, (instance) => instance.cleanup());
+ }
+
+ modify(element, _, { close, duration, progressBar }) {
+ this.element = element;
+ this.close = close;
+ this.duration = duration;
+ this.timeRemaining = duration;
+ this.progressBar = progressBar;
+ this.element.addEventListener("mouseenter", this.stopTimer, {
+ passive: true,
+ });
+ this.element.addEventListener("mouseleave", this.startTimer, {
+ passive: true,
+ });
+ this.startTimer();
+ }
+
+ @bind
+ startTimer() {
+ this.startProgressAnimation();
+
+ this.transitionLaterHandler = discourseLater(() => {
+ this.element.classList.add(TRANSITION_CLASS);
+
+ this.closeLaterHandler = discourseLater(() => {
+ this.close();
+ }, CSS_TRANSITION_DELAY_MS);
+ }, this.timeRemaining);
+ }
+
+ @bind
+ stopTimer() {
+ this.pauseProgressAnimation();
+ cancel(this.transitionLaterHandler);
+ cancel(this.closeLaterHandler);
+ }
+
+ @bind
+ startProgressAnimation() {
+ if (!this.progressBar) {
+ return;
+ }
+
+ if (this.progressAnimation) {
+ this.progressAnimation.play();
+ this.progressBar.style.opacity = 1;
+ return;
+ }
+
+ this.progressAnimation = this.progressBar.animate(
+ { transform: `scaleX(0)` },
+ { duration: this.duration, fill: "forwards" }
+ );
+ }
+
+ @bind
+ pauseProgressAnimation() {
+ if (
+ !this.progressAnimation ||
+ this.progressAnimation.currentTime === this.duration
+ ) {
+ return;
+ }
+
+ this.progressAnimation.pause();
+ this.progressBar.style.opacity = 0.5;
+ this.timeRemaining = this.duration - this.progressAnimation.currentTime;
+ }
+
+ cleanup() {
+ this.stopTimer();
+ this.element.removeEventListener("mouseenter", this.stopTimer);
+ this.element.removeEventListener("mouseleave", this.startTimer);
+ this.progressBar = null;
+ }
+}
+
+export default class DToast extends Component {
+ @tracked progressBar;
+
+ @action
+ registerProgressBar(element) {
+ this.progressBar = element;
+ }
+
+
+
+
+}
diff --git a/app/assets/javascripts/float-kit/addon/components/d-toasts.gjs b/app/assets/javascripts/float-kit/addon/components/d-toasts.gjs
index 5c7adffb066..001ca095beb 100644
--- a/app/assets/javascripts/float-kit/addon/components/d-toasts.gjs
+++ b/app/assets/javascripts/float-kit/addon/components/d-toasts.gjs
@@ -1,64 +1,6 @@
import Component from "@glimmer/component";
-import { registerDestructor } from "@ember/destroyable";
-import { cancel } from "@ember/runloop";
import { service } from "@ember/service";
-import Modifier from "ember-modifier";
-import concatClass from "discourse/helpers/concat-class";
-import discourseLater from "discourse-common/lib/later";
-import { bind } from "discourse-common/utils/decorators";
-
-const CSS_TRANSITION_DELAY_MS = 300;
-const TRANSITION_CLASS = "-fade-out";
-
-class AutoCloseToast extends Modifier {
- element;
- close;
- duration;
- transitionLaterHandler;
- closeLaterHandler;
-
- constructor(owner, args) {
- super(owner, args);
-
- registerDestructor(this, (instance) => instance.cleanup());
- }
-
- modify(element, _, { close, duration }) {
- this.element = element;
- this.close = close;
- this.duration = duration;
- this.element.addEventListener("mouseenter", this.stopTimer, {
- passive: true,
- });
- this.element.addEventListener("mouseleave", this.startTimer, {
- passive: true,
- });
- this.startTimer();
- }
-
- @bind
- startTimer() {
- this.transitionLaterHandler = discourseLater(() => {
- this.element.classList.add(TRANSITION_CLASS);
-
- this.closeLaterHandler = discourseLater(() => {
- this.close();
- }, CSS_TRANSITION_DELAY_MS);
- }, this.duration);
- }
-
- @bind
- stopTimer() {
- cancel(this.transitionLaterHandler);
- cancel(this.closeLaterHandler);
- }
-
- cleanup() {
- this.stopTimer();
- this.element.removeEventListener("mouseenter", this.stopTimer);
- this.element.removeEventListener("mouseleave", this.startTimer);
- }
-}
+import DToast from "float-kit/components/d-toast";
export default class DToasts extends Component {
@service toasts;
@@ -66,22 +8,7 @@ export default class DToasts extends Component {
{{#each this.toasts.activeToasts as |toast|}}
-
+
{{/each}}
diff --git a/app/assets/stylesheets/common/float-kit/d-default-toast.scss b/app/assets/stylesheets/common/float-kit/d-default-toast.scss
index ec664ac730b..a9d29adeb57 100644
--- a/app/assets/stylesheets/common/float-kit/d-default-toast.scss
+++ b/app/assets/stylesheets/common/float-kit/d-default-toast.scss
@@ -70,6 +70,20 @@
min-height: 30px;
}
+ &__progress-bar {
+ width: 100%;
+ height: 5px;
+ top: 0;
+ left: 0;
+ position: absolute;
+ background-color: var(--success);
+ transform-origin: 0 0;
+ }
+
+ .fk-d-default-toast:has(&__progress-bar) {
+ padding-top: 15px;
+ }
+
&__texts {
min-height: 30px;
display: flex;
@@ -96,5 +110,7 @@
&__message {
display: flex;
+ margin-top: 0.5rem;
+ color: var(--primary-high);
}
}