aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/components
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-08-31 01:50:40 +0800
committercrupest <crupest@outlook.com>2023-08-31 01:50:40 +0800
commitfb64a8f3d4a7dd4035f50ccf2601ae0a683bd1b8 (patch)
tree499e15e9567eae52ed043b8eaf99ddf33db05311 /FrontEnd/src/components
parent9c69024cf5961c3c71fb58e4237f09a513d195b1 (diff)
downloadtimeline-fb64a8f3d4a7dd4035f50ccf2601ae0a683bd1b8.tar.gz
timeline-fb64a8f3d4a7dd4035f50ccf2601ae0a683bd1b8.tar.bz2
timeline-fb64a8f3d4a7dd4035f50ccf2601ae0a683bd1b8.zip
...
Diffstat (limited to 'FrontEnd/src/components')
-rw-r--r--FrontEnd/src/components/alert/AlertHost.tsx126
-rw-r--r--FrontEnd/src/components/alert/AlertService.ts111
-rw-r--r--FrontEnd/src/components/alert/alert.css25
-rw-r--r--FrontEnd/src/components/alert/index.ts8
4 files changed, 172 insertions, 98 deletions
diff --git a/FrontEnd/src/components/alert/AlertHost.tsx b/FrontEnd/src/components/alert/AlertHost.tsx
index b234ac03..23f62472 100644
--- a/FrontEnd/src/components/alert/AlertHost.tsx
+++ b/FrontEnd/src/components/alert/AlertHost.tsx
@@ -1,95 +1,56 @@
-import * as React from "react";
-import without from "lodash/without";
-import { useTranslation } from "react-i18next";
+import { useEffect, useState } from "react";
import classNames from "classnames";
-import { alertService, AlertInfoEx, AlertInfo } from "~src/services/alert";
-import { convertI18nText } from "~src/common";
-
+import { ThemeColor, useC, Text } from "../common";
import IconButton from "../button/IconButton";
+import { alertService, AlertInfoWithId } from "./AlertService";
+
import "./alert.css";
interface AutoCloseAlertProps {
- alert: AlertInfo;
- close: () => void;
+ color: ThemeColor;
+ message: Text;
+ onDismiss?: () => void;
+ onIn?: () => void;
+ onOut?: () => void;
}
-export const AutoCloseAlert: React.FC<AutoCloseAlertProps> = (props) => {
- const { alert, close } = props;
- const { dismissTime } = alert;
-
- const { t } = useTranslation();
-
- const timerTag = React.useRef<number | null>(null);
- const closeHandler = React.useRef<(() => void) | null>(null);
-
- React.useEffect(() => {
- closeHandler.current = close;
- }, [close]);
-
- React.useEffect(() => {
- const tag =
- dismissTime === "never"
- ? null
- : typeof dismissTime === "number"
- ? window.setTimeout(() => closeHandler.current?.(), dismissTime)
- : window.setTimeout(() => closeHandler.current?.(), 5000);
- timerTag.current = tag;
- return () => {
- if (tag != null) {
- window.clearTimeout(tag);
- }
- };
- }, [dismissTime]);
-
- const cancelTimer = (): void => {
- const { current: tag } = timerTag;
- if (tag != null) {
- window.clearTimeout(tag);
- }
- };
+function Alert({
+ color,
+ message,
+ onDismiss,
+ onIn,
+ onOut,
+}: AutoCloseAlertProps) {
+ const c = useC();
return (
<div
- className={classNames(
- "m-3 cru-alert",
- "cru-" + (alert.type ?? "primary")
- )}
- onClick={cancelTimer}
+ className={classNames("cru-alert", `cru-theme-${color}`)}
+ onPointerEnter={onIn}
+ onPointerLeave={onOut}
>
- <div className="cru-alert-content">
- {(() => {
- const { message, customMessage } = alert;
- if (customMessage != null) {
- return customMessage;
- } else {
- return convertI18nText(message, t);
- }
- })()}
- </div>
- <div className="cru-alert-close-button-container">
- <IconButton
- icon="x"
- className="cru-alert-close-button"
- onClick={close}
- />
- </div>
+ <div className="cru-alert-message">{c(message)}</div>
+ <IconButton
+ icon="x"
+ color="danger"
+ className="cru-alert-close-button"
+ onClick={onDismiss}
+ />
</div>
);
-};
+}
-const AlertHost: React.FC = () => {
- const [alerts, setAlerts] = React.useState<AlertInfoEx[]>([]);
+export default function AlertHost() {
+ const [alerts, setAlerts] = useState<AlertInfoWithId[]>([]);
- React.useEffect(() => {
- const consume = (alert: AlertInfoEx): void => {
- setAlerts((old) => [...old, alert]);
- };
+ useEffect(() => {
+ alertService.registerListener(setAlerts);
- alertService.registerConsumer(consume);
return () => {
- alertService.unregisterConsumer(consume);
+ alertService.unregisterListener(setAlerts);
+ alert;
};
}, []);
@@ -97,17 +58,22 @@ const AlertHost: React.FC = () => {
<div className="alert-container">
{alerts.map((alert) => {
return (
- <AutoCloseAlert
+ <Alert
key={alert.id}
- alert={alert}
- close={() => {
- setAlerts((old) => without(old, alert));
+ message={alert.message}
+ color={alert.color ?? "primary"}
+ onIn={() => {
+ alertService.clearDismissTimer(alert.id);
+ }}
+ onOut={() => {
+ alertService.resetDismissTimer(alert.id);
+ }}
+ onDismiss={() => {
+ alertService.dismiss(alert.id);
}}
/>
);
})}
</div>
);
-};
-
-export default AlertHost;
+}
diff --git a/FrontEnd/src/components/alert/AlertService.ts b/FrontEnd/src/components/alert/AlertService.ts
new file mode 100644
index 00000000..b3565b8a
--- /dev/null
+++ b/FrontEnd/src/components/alert/AlertService.ts
@@ -0,0 +1,111 @@
+import { ThemeColor, Text } from "../common";
+
+export interface AlertInfo {
+ color?: ThemeColor;
+ message: Text;
+ dismissTime?: number | "never";
+}
+
+export interface AlertInfoWithId extends AlertInfo {
+ id: number;
+}
+
+interface AlertServiceAlert extends AlertInfoWithId {
+ timerId: number | null;
+}
+
+export type AlertsListener = (alerts: AlertInfoWithId[]) => void;
+
+export class AlertService {
+ private listeners: AlertsListener[] = [];
+ private alerts: AlertServiceAlert[] = [];
+ private currentId = 1;
+
+ getAlert(alertId?: number | null | undefined): AlertServiceAlert | null {
+ for (const alert of this.alerts) {
+ if (alert.id === alertId) return alert;
+ }
+ return null;
+ }
+
+ registerListener(listener: AlertsListener): void {
+ this.listeners.push(listener);
+ listener(this.alerts);
+ }
+
+ unregisterListener(listener: AlertsListener): void {
+ this.listeners = this.listeners.filter((l) => l !== listener);
+ }
+
+ notify() {
+ for (const listener of this.listeners) {
+ listener(this.alerts);
+ }
+ }
+
+ push(alert: AlertInfo): void {
+ const newAlert: AlertServiceAlert = {
+ ...alert,
+ id: this.currentId++,
+ timerId: null,
+ };
+
+ this.alerts.push(newAlert);
+ this._resetDismissTimer(newAlert);
+
+ this.notify();
+ }
+
+ private _dismiss(alert: AlertServiceAlert) {
+ if (alert.timerId != null) {
+ window.clearTimeout(alert.timerId);
+ }
+ this.alerts = this.alerts.filter((a) => a !== alert);
+ this.notify();
+ }
+
+ dismiss(alertId?: number | null | undefined) {
+ const alert = this.getAlert(alertId);
+ if (alert != null) {
+ this._dismiss(alert);
+ }
+ }
+
+ private _clearDismissTimer(alert: AlertServiceAlert) {
+ if (alert.timerId != null) {
+ window.clearTimeout(alert.timerId);
+ alert.timerId = null;
+ }
+ }
+
+ clearDismissTimer(alertId?: number | null | undefined) {
+ const alert = this.getAlert(alertId);
+ if (alert != null) {
+ this._clearDismissTimer(alert);
+ }
+ }
+
+ private _resetDismissTimer(
+ alert: AlertServiceAlert,
+ dismissTime?: number | null | undefined,
+ ) {
+ this._clearDismissTimer(alert);
+
+ const realDismissTime = dismissTime ?? alert.dismissTime;
+
+ if (typeof realDismissTime === "number") {
+ alert.timerId = window.setTimeout(() => {
+ this._dismiss(alert);
+ }, realDismissTime);
+ }
+ }
+
+ resetDismissTimer(alertId?: number | null | undefined) {
+ const alert = this.getAlert(alertId);
+ if (alert != null) {
+ this._resetDismissTimer(alert);
+ }
+ }
+}
+
+export const alertService = new AlertService();
diff --git a/FrontEnd/src/components/alert/alert.css b/FrontEnd/src/components/alert/alert.css
index 54c2b87f..063af933 100644
--- a/FrontEnd/src/components/alert/alert.css
+++ b/FrontEnd/src/components/alert/alert.css
@@ -5,29 +5,18 @@
.cru-alert {
border-radius: 5px;
- border: var(--cru-key-color) 1px solid;
- color: var(--cru-key-t-color);
- background-color: var(--cru-key-b1-color);
+ border: var(--cru-theme-color) 1px solid;
+ color: var(--cru-text-primary-color);
+ background-color: var(--cru-container-background-color);
- display: flex;
- overflow: hidden;
-}
-
-.cru-alert-content {
padding: 0.5em 2em;
-}
-.cru-alert-close-button-container {
- flex-shrink: 0;
- margin-left: auto;
- width: 2em;
- text-align: center;
display: flex;
align-items: center;
- justify-content: center;
- background-color: var(--cru-key-t-color);
}
+.cru-alert-message {}
+
.cru-alert-close-button {
- color: var(--cru-key-color);
-}
+ margin-left: auto;
+} \ No newline at end of file
diff --git a/FrontEnd/src/components/alert/index.ts b/FrontEnd/src/components/alert/index.ts
new file mode 100644
index 00000000..1be0c2ec
--- /dev/null
+++ b/FrontEnd/src/components/alert/index.ts
@@ -0,0 +1,8 @@
+import { alertService, AlertInfo } from "./AlertService";
+import { default as AlertHost } from "./AlertHost";
+
+export { alertService, AlertHost };
+
+export function pushAlert(alert: AlertInfo): void {
+ alertService.push(alert);
+}