@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
240 lines (237 loc) • 7.28 kB
JavaScript
'use client';
import { useIsClient } from "../hooks/useIsClient.mjs";
import { LOBE_THEME_APP_ID } from "../ThemeProvider/constants.mjs";
import { registerDevSingleton } from "../utils/devSingleton.mjs";
import { ToastContext } from "./context.mjs";
import { viewportVariants } from "./style.mjs";
import Toast_default from "./Toast.mjs";
import { memo, useEffect, useState } from "react";
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
import { cx } from "antd-style";
import { createPortal } from "react-dom";
import { Toast } from "@base-ui/react/toast";
//#region src/Toast/imperative.tsx
const ALL_POSITIONS = [
"top",
"top-left",
"top-right",
"bottom",
"bottom-left",
"bottom-right"
];
let globalState = {
duration: 5e3,
limit: 5,
position: "bottom-right",
swipeDirection: ["down", "right"]
};
const toastManagers = {
"bottom": Toast.createToastManager(),
"bottom-left": Toast.createToastManager(),
"bottom-right": Toast.createToastManager(),
"top": Toast.createToastManager(),
"top-left": Toast.createToastManager(),
"top-right": Toast.createToastManager()
};
const activeToastIds = {
"bottom": /* @__PURE__ */ new Set(),
"bottom-left": /* @__PURE__ */ new Set(),
"bottom-right": /* @__PURE__ */ new Set(),
"top": /* @__PURE__ */ new Set(),
"top-left": /* @__PURE__ */ new Set(),
"top-right": /* @__PURE__ */ new Set()
};
const getManager = (position) => toastManagers[position];
const normalizeOptions = (optionsOrMessage, type) => {
if (typeof optionsOrMessage === "string") return {
description: optionsOrMessage,
type
};
return {
...optionsOrMessage,
type
};
};
const createToastInstance = (id, position) => ({
close: () => getManager(position).close(id),
id,
update: (options) => {
getManager(position).update(id, {
data: options,
description: options.description,
title: options.title
});
}
});
const addToast = (options) => {
const position = options.placement ?? globalState.position;
const manager = getManager(position);
const onRemove = options.onRemove;
const id = manager.add({
data: options,
description: options.description,
onClose: options.onClose,
onRemove: () => {
activeToastIds[position].delete(id);
onRemove?.();
},
timeout: options.duration ?? globalState.duration,
title: options.title
});
activeToastIds[position].add(id);
return createToastInstance(id, position);
};
const dismissToast = (id) => {
if (id) for (const [position, manager] of Object.entries(toastManagers)) {
activeToastIds[position].delete(id);
manager.close(id);
}
else for (const [position, manager] of Object.entries(toastManagers)) {
const ids = Array.from(activeToastIds[position]);
for (const toastId of ids) manager.close(toastId);
activeToastIds[position].clear();
}
};
const createSuccessToast = (optionsOrMessage) => {
return addToast(normalizeOptions(optionsOrMessage, "success"));
};
const createErrorToast = (optionsOrMessage) => {
return addToast(normalizeOptions(optionsOrMessage, "error"));
};
const createInfoToast = (optionsOrMessage) => {
return addToast(normalizeOptions(optionsOrMessage, "info"));
};
const createWarningToast = (optionsOrMessage) => {
return addToast(normalizeOptions(optionsOrMessage, "warning"));
};
const createLoadingToast = (optionsOrMessage) => {
return addToast({
duration: 0,
...normalizeOptions(optionsOrMessage, "loading")
});
};
async function promiseToast(promise, options) {
const loadingToast = addToast({
closable: false,
duration: 0,
type: "loading",
...typeof options.loading === "string" ? { description: options.loading } : options.loading
});
try {
const result = await promise;
loadingToast.close();
addToast({
type: "success",
...(() => {
if (typeof options.success === "string") return { description: options.success };
if (typeof options.success === "function") return { description: options.success(result) };
return options.success;
})()
});
return result;
} catch (error) {
loadingToast.close();
addToast({
type: "error",
...(() => {
if (typeof options.error === "string") return { description: options.error };
if (typeof options.error === "function") return { description: options.error(error) };
return options.error;
})()
});
throw error;
}
}
const baseToast = (options) => {
return addToast({
type: "default",
...options
});
};
const toast = Object.assign(baseToast, {
dismiss: dismissToast,
error: createErrorToast,
info: createInfoToast,
loading: createLoadingToast,
promise: promiseToast,
success: createSuccessToast,
warning: createWarningToast
});
const ToastList = memo(() => {
const { toasts } = Toast.useToastManager();
return toasts.map((t) => /* @__PURE__ */ jsx(Toast_default, { toast: t }, t.id));
});
ToastList.displayName = "ToastList";
const TOAST_PORTAL_ATTR = "data-lobe-ui-toast-portal";
const TOAST_CONTAINER_ATTR = "data-lobe-ui-toast-container";
const containerMap = /* @__PURE__ */ new WeakMap();
const getOrCreateContainer = (root) => {
const resolvedRoot = (() => {
if (typeof document === "undefined") return root;
if (typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) return root;
if (!(root === document.body)) return root;
const themeApp = document.querySelector(`#${LOBE_THEME_APP_ID}`);
if (themeApp) return themeApp;
const toastContainer = document.querySelector(`[${TOAST_CONTAINER_ATTR}="true"]`);
if (toastContainer) return toastContainer;
return root;
})();
const cached = containerMap.get(resolvedRoot);
if (cached && cached.isConnected) return cached;
const el = document.createElement("div");
el.setAttribute(TOAST_PORTAL_ATTR, "true");
resolvedRoot.append(el);
containerMap.set(resolvedRoot, el);
return el;
};
const resolveRoot = (root) => {
if (root) return root;
return document.body;
};
const ToastHost = memo(({ root, className, duration = 5e3, limit = 5, position = "bottom-right", swipeDirection = ["down", "right"] }) => {
const isClient = useIsClient();
const [container, setContainer] = useState(null);
useEffect(() => {
globalState = {
duration,
limit,
position,
swipeDirection
};
}, [
duration,
limit,
position,
swipeDirection
]);
useEffect(() => {
if (!isClient) return;
const resolved = resolveRoot(root);
if (resolved) setContainer(getOrCreateContainer(resolved));
return registerDevSingleton("ToastHost", root ?? document.body);
}, [isClient, root]);
if (!isClient || !container) return null;
return createPortal(/* @__PURE__ */ jsx(Fragment$1, { children: ALL_POSITIONS.map((pos) => /* @__PURE__ */ jsx(ToastContext.Provider, {
value: {
position: pos,
swipeDirection
},
children: /* @__PURE__ */ jsx(Toast.Provider, {
limit,
timeout: duration,
toastManager: getManager(pos),
children: /* @__PURE__ */ jsx(Toast.Portal, {
container,
children: /* @__PURE__ */ jsx(Toast.Viewport, {
className: cx(viewportVariants({ position: pos }), className),
children: /* @__PURE__ */ jsx(ToastList, {})
})
})
})
}, pos)) }), container);
});
ToastHost.displayName = "ToastHost";
const useToast = () => toast;
//#endregion
export { TOAST_CONTAINER_ATTR, ToastHost, toast, useToast };
//# sourceMappingURL=imperative.mjs.map