) {
+ toasts.value = toasts.value.map(toast => {
+ if (toast.id === id) {
+ return { ...toast, ...partial }
+ }
+ return toast;
+ });
+}
+
+export function removeToastFromStore(id: string) {
+ toasts.value = toasts.value.filter(toast => toast.id !== id);
+}
+//#endregion
+
export default {
showMessage,
showError,
diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css
index e395dbbb3..f2f554629 100644
--- a/apps/client/src/stylesheets/style.css
+++ b/apps/client/src/stylesheets/style.css
@@ -1135,61 +1135,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
margin: 0 12px;
}
-#toast-container {
- position: absolute;
- width: 100%;
- top: 20px;
- pointer-events: none;
-}
-
-.toast {
- --bs-toast-bg: var(--accented-background-color);
- --bs-toast-color: var(--main-text-color);
- z-index: 9999999999 !important;
- pointer-events: all;
- overflow: hidden;
-}
-
-.toast-header {
- background-color: var(--more-accented-background-color) !important;
- color: var(--main-text-color) !important;
-}
-
-.toast-body {
- white-space: preserve-breaks;
- overflow: hidden;
-}
-
-.toast.no-title {
- display: flex;
- flex-direction: row;
-}
-
-.toast.no-title .toast-icon {
- display: flex;
- align-items: center;
- padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);
-}
-
-.toast.no-title .toast-body {
- padding-inline-start: 0;
- padding-inline-end: 0;
-}
-
-.toast.no-title .toast-header {
- background-color: unset !important;
-}
-
-.toast .toast-progress {
- position: absolute;
- bottom: 0;
- inset-inline-start: 0;
- inset-inline-end: 0;
- background-color: var(--toast-text-color) !important;
- height: 4px;
- transition: width 0.1s linear;
-}
-
.ck-mentions .ck-button {
font-size: var(--detail-font-size) !important;
padding: 5px;
diff --git a/apps/client/src/widgets/Toast.css b/apps/client/src/widgets/Toast.css
new file mode 100644
index 000000000..457aadce2
--- /dev/null
+++ b/apps/client/src/widgets/Toast.css
@@ -0,0 +1,59 @@
+#toast-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ width: 100%;
+ top: 20px;
+ pointer-events: none;
+ contain: none;
+}
+
+.toast {
+ --bs-toast-bg: var(--accented-background-color);
+ --bs-toast-color: var(--main-text-color);
+ z-index: 9999999999 !important;
+ pointer-events: all;
+ overflow: hidden;
+}
+
+.toast-header {
+ background-color: var(--more-accented-background-color) !important;
+ color: var(--main-text-color) !important;
+}
+
+.toast-body {
+ white-space: preserve-breaks;
+ overflow: hidden;
+}
+
+.toast.no-title {
+ display: flex;
+ flex-direction: row;
+}
+
+.toast.no-title .toast-icon {
+ display: flex;
+ align-items: center;
+ padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);
+}
+
+.toast.no-title .toast-body {
+ padding-inline-start: 0;
+ padding-inline-end: 0;
+}
+
+.toast.no-title .toast-header {
+ background-color: unset !important;
+}
+
+.toast .toast-progress {
+ position: absolute;
+ bottom: 0;
+ inset-inline-start: 0;
+ inset-inline-end: 0;
+ background-color: var(--toast-text-color) !important;
+ height: 4px;
+ transition: width 0.1s linear;
+}
diff --git a/apps/client/src/widgets/Toast.tsx b/apps/client/src/widgets/Toast.tsx
new file mode 100644
index 000000000..62bfe78fd
--- /dev/null
+++ b/apps/client/src/widgets/Toast.tsx
@@ -0,0 +1,61 @@
+import "./Toast.css";
+
+import clsx from "clsx";
+import { useEffect } from "preact/hooks";
+
+import { removeToastFromStore, ToastOptionsWithRequiredId, toasts } from "../services/toast";
+import Icon from "./react/Icon";
+import { RawHtmlBlock } from "./react/RawHtml";
+
+export default function ToastContainer() {
+ return (
+
+ {toasts.value.map(toast => )}
+
+ )
+}
+
+function Toast({ id, title, timeout, progress, message, icon }: ToastOptionsWithRequiredId) {
+ // Autohide.
+ useEffect(() => {
+ if (!timeout || timeout <= 0) return;
+ const timerId = setTimeout(() => removeToastFromStore(id), timeout);
+ return () => clearTimeout(timerId);
+ }, [ id, timeout ]);
+
+ const closeButton = (
+