forked from zhongyidian/food-add-edu
feat: 引入 yuxiang-kit 课程组件源码与运行时
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
77742d5b02
commit
3995656175
41 files changed
+2752
No files matched your search
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import type {
|
||||
ToastContextValue,
|
||||
ToastItem,
|
||||
ToastOptions,
|
||||
ToastProviderProps,
|
||||
ToastType,
|
||||
} from "./Toast.types";
|
||||
|
||||
const DEFAULT_DURATION = 2400;
|
||||
|
||||
const ToastContext = createContext<ToastContextValue | null>(null);
|
||||
|
||||
function createToastId() {
|
||||
return `toast-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
|
||||
function getDefaultTitle(type: ToastType) {
|
||||
const titleMap: Record<ToastType, string> = {
|
||||
info: "提示",
|
||||
success: "成功",
|
||||
warning: "注意",
|
||||
error: "错误",
|
||||
};
|
||||
|
||||
return titleMap[type];
|
||||
}
|
||||
|
||||
export function ToastProvider({ children }: ToastProviderProps) {
|
||||
const [toasts, setToasts] = useState<ToastItem[]>([]);
|
||||
const timersRef = useRef<Map<string, number>>(new Map());
|
||||
|
||||
const remove = useCallback((id: string) => {
|
||||
const timer = timersRef.current.get(id);
|
||||
if (timer) {
|
||||
window.clearTimeout(timer);
|
||||
timersRef.current.delete(id);
|
||||
}
|
||||
|
||||
setToasts((current) => current.filter((toast) => toast.id !== id));
|
||||
}, []);
|
||||
|
||||
const clear = useCallback(() => {
|
||||
timersRef.current.forEach((timer) => {
|
||||
window.clearTimeout(timer);
|
||||
});
|
||||
timersRef.current.clear();
|
||||
setToasts([]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
timersRef.current.forEach((timer) => {
|
||||
window.clearTimeout(timer);
|
||||
});
|
||||
timersRef.current.clear();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const show = useCallback(
|
||||
(options: ToastOptions | string) => {
|
||||
const normalizedOptions: ToastOptions =
|
||||
typeof options === "string" ? { message: options } : options;
|
||||
const type = normalizedOptions.type ?? "info";
|
||||
const id = createToastId();
|
||||
const toast: ToastItem = {
|
||||
id,
|
||||
type,
|
||||
title: normalizedOptions.title ?? getDefaultTitle(type),
|
||||
message: normalizedOptions.message,
|
||||
duration: normalizedOptions.duration ?? DEFAULT_DURATION,
|
||||
className: normalizedOptions.className,
|
||||
data: normalizedOptions.data,
|
||||
};
|
||||
|
||||
setToasts((current) => [...current, toast]);
|
||||
|
||||
if (toast.duration > 0) {
|
||||
const timer = window.setTimeout(() => remove(id), toast.duration);
|
||||
timersRef.current.set(id, timer);
|
||||
}
|
||||
|
||||
return id;
|
||||
},
|
||||
[remove],
|
||||
);
|
||||
|
||||
const contextValue = useMemo<ToastContextValue>(
|
||||
() => ({
|
||||
toasts,
|
||||
show,
|
||||
success: (message, options) =>
|
||||
show({ ...options, message, type: "success" }),
|
||||
error: (message, options) => show({ ...options, message, type: "error" }),
|
||||
warning: (message, options) =>
|
||||
show({ ...options, message, type: "warning" }),
|
||||
info: (message, options) => show({ ...options, message, type: "info" }),
|
||||
remove,
|
||||
clear,
|
||||
}),
|
||||
[clear, remove, show, toasts],
|
||||
);
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
const context = useContext(ToastContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useToast must be used within ToastProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
Reference in new issue
Block a user