UNPKG

@yamada-ui/notice

Version:

Yamada UI notice component

425 lines (422 loc) • 13.5 kB
"use client" "use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { NoticeProvider: () => NoticeProvider, noticeStore: () => noticeStore, useNotice: () => useNotice }); module.exports = __toCommonJS(index_exports); // src/notice.tsx var import_alert = require("@yamada-ui/alert"); var import_close_button = require("@yamada-ui/close-button"); var import_core = require("@yamada-ui/core"); var import_utils = require("@yamada-ui/utils"); var import_react = require("react"); var import_jsx_runtime = require("react/jsx-runtime"); var findId = (options, id) => options.find((notice) => notice.id === id); var findNotice = (state, id) => { const placement = getNoticePlacement(state, id); const index = placement ? state[placement].findIndex((notice) => notice.id === id) : -1; return { index, placement }; }; var getNoticePlacement = (state, id) => { for (const [placement, values] of Object.entries(state)) { if (findId(values, id)) return placement; } }; var counter = 0; var createNotice = (message, { id, style, duration, placement = "top", status, onCloseComplete }) => { counter += 1; id != null ? id : id = counter; return { id, style, duration, isDelete: false, message, placement, status, onCloseComplete, onDelete: () => noticeStore.remove(String(id), placement) }; }; var createRender = (options) => { const { component } = options; const Render = (props) => { if (typeof component === "function") { return component({ ...props, ...options }); } else { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Notice, { ...props, ...options }); } }; return Render; }; var createNoticeFunc = (defaultOptions, theme) => { var _a, _b, _c; const themeOptions = (_c = (_b = (_a = theme.__config) == null ? void 0 : _a.notice) == null ? void 0 : _b.options) != null ? _c : {}; const computedOptions = (options) => (0, import_utils.merge)(themeOptions, (0, import_utils.merge)(defaultOptions, options)); const notice = (options = {}) => { options = computedOptions(options); const message = createRender(options); return noticeStore.create(message, options); }; notice.update = (id, options) => { options = computedOptions(options); noticeStore.update(id, options); }; notice.closeAll = noticeStore.closeAll; notice.close = noticeStore.close; notice.isActive = noticeStore.isActive; return notice; }; var useNotice = (defaultOptions) => { const { theme } = (0, import_core.useTheme)(); return (0, import_react.useMemo)( () => createNoticeFunc(defaultOptions != null ? defaultOptions : {}, theme), [defaultOptions, theme] ); }; var initialState = { bottom: [], "bottom-left": [], "bottom-right": [], top: [], "top-left": [], "top-right": [] }; var createNoticeStore = (initialState2) => { let state = initialState2; const storeChangeCache = /* @__PURE__ */ new Set(); const setState = (setStateFunc) => { state = setStateFunc(state); storeChangeCache.forEach((onStoreChange) => onStoreChange()); }; return { close: (id) => { setState((prev) => { const placement = getNoticePlacement(prev, id); if (!placement) return prev; return { ...prev, [placement]: prev[placement].map( (notice) => notice.id == id ? { ...notice, isDelete: true } : notice ) }; }); }, closeAll: ({ placement } = {}) => { setState((prev) => { let placements = [ "bottom", "bottom-right", "bottom-left", "top", "top-left", "top-right" ]; if (placement) placements = placement; return placements.reduce( (acc, placement2) => { acc[placement2] = prev[placement2].map((notice) => ({ ...notice, isDelete: true })); return acc; }, { ...prev } ); }); }, create: (message, options) => { const limit = options.limit; const notice = createNotice(message, options); const { id, placement } = notice; setState((prev) => { let prevNotices = prev[placement]; if (limit !== void 0 && limit > 0 && prevNotices.length > limit - 1) { const n = prevNotices.length - (limit - 1); const notices2 = placement.includes("top") ? prevNotices.slice(n * -1) : prevNotices.slice(0, n); const ids = notices2.map(({ id: id2 }) => id2); prevNotices = prevNotices.map( (notice2) => ids.includes(notice2.id) ? { ...notice2, isDelete: true } : notice2 ); } const notices = placement.includes("top") ? [notice, ...prevNotices] : [...prevNotices, notice]; return { ...prev, [placement]: notices }; }); return id; }, getSnapshot: () => state, isActive: (id) => Boolean(findNotice(noticeStore.getSnapshot(), id).placement), remove: (id, placement) => { setState((prevState) => ({ ...prevState, [placement]: prevState[placement].filter((notice) => notice.id != id) })); }, subscribe: (onStoreChange) => { storeChangeCache.add(onStoreChange); return () => { setState(() => initialState2); storeChangeCache.delete(onStoreChange); }; }, update: (id, options) => { setState((prev) => { const next = { ...prev }; const { index, placement } = findNotice(next, id); if (placement && index !== -1 && next[placement][index]) { next[placement][index] = { ...next[placement][index], ...options, message: createRender(options) }; } return next; }); } }; }; var noticeStore = createNoticeStore(initialState); var Notice = ({ className, colorScheme, variant = "basic", closeStrategy = "button", description, icon, isClosable, status, title, onClose }) => { const isButtonClosable = isClosable && (closeStrategy === "button" || closeStrategy === "both"); const isElementClosable = isClosable && (closeStrategy === "element" || closeStrategy === "both"); return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( import_alert.Alert, { className: (0, import_utils.cx)("ui-notice", className), colorScheme, variant, alignItems: "start", boxShadow: "fallback(lg, 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05))", pe: isButtonClosable ? 8 : void 0, status, onClick: isElementClosable ? onClose : void 0, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_alert.AlertIcon, { className: "ui-notice__icon", variant: icon == null ? void 0 : icon.variant, ...(icon == null ? void 0 : icon.color) ? { color: icon.color } : {}, children: icon == null ? void 0 : icon.children } ), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_core.ui.div, { flex: "1", children: [ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_alert.AlertTitle, { className: "ui-notice__title", lineClamp: 1, children: title }) : null, description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_alert.AlertDescription, { className: "ui-notice__desc", lineClamp: 3, children: description }) : null ] }), isButtonClosable ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_close_button.CloseButton, { className: "ui-notice__close-button", size: "sm", position: "absolute", right: 2, top: 2, onClick: (ev) => { ev.stopPropagation(); onClose == null ? void 0 : onClose(); } } ) : null ] } ); }; Notice.displayName = "Notice"; Notice.__ui__ = "Notice"; // src/notice-provider.tsx var import_core2 = require("@yamada-ui/core"); var import_motion = require("@yamada-ui/motion"); var import_portal = require("@yamada-ui/portal"); var import_use_timeout = require("@yamada-ui/use-timeout"); var import_utils2 = require("@yamada-ui/utils"); var import_react2 = require("react"); var import_jsx_runtime2 = require("react/jsx-runtime"); var NoticeProvider = ({ appendToParentPortal, containerRef, gap = "fallback(4, 1rem)", variants, itemProps, listProps }) => { const state = (0, import_react2.useSyncExternalStore)( noticeStore.subscribe, noticeStore.getSnapshot, noticeStore.getSnapshot ); const components = Object.entries(state).map(([placement, notices]) => { const top = placement.includes("top") ? "env(safe-area-inset-top, 0px)" : void 0; const bottom = placement.includes("bottom") ? "env(safe-area-inset-bottom, 0px)" : void 0; const right = !placement.includes("left") ? "env(safe-area-inset-right, 0px)" : void 0; const left = !placement.includes("right") ? "env(safe-area-inset-left, 0px)" : void 0; const css = { bottom, display: "flex", flexDirection: "column", gap, left, margin: gap, pointerEvents: "none", position: "fixed", right, top, zIndex: "fallback(zarbon, 160)" }; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_core2.ui.ul, { className: (0, import_utils2.cx)("ui-notice__list", `ui-notice__list--${placement}`), __css: css, ...listProps, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_motion.AnimatePresence, { initial: false, children: notices.map((notice) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( NoticeComponent, { variants, itemProps, ...notice }, notice.id )) }) }, placement ); }); return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_portal.Portal, { appendToParentPortal, containerRef, children: components } ); }; var defaultVariants = { animate: { opacity: 1, scale: 1, transition: { duration: 0.4, ease: [0.4, 0, 0.2, 1] }, x: 0, y: 0 }, exit: { opacity: 0, scale: 0.95, transition: { duration: 0.2, ease: [0.4, 0, 1, 1] } }, initial: ({ placement }) => ({ [["bottom", "top"].includes(placement) ? "y" : "x"]: (placement === "bottom" ? 1 : placement.includes("right") ? 1 : -1) * 24, opacity: 0 }) }; var NoticeComponent = (0, import_core2.memo)( ({ style, duration = 5e3, isDelete = false, message, placement, variants = defaultVariants, itemProps, onCloseComplete, onDelete }) => { const [delay, setDelay] = (0, import_react2.useState)(duration); const isPresent = (0, import_motion.useIsPresent)(); (0, import_utils2.useUpdateEffect)(() => { if (!isPresent) onCloseComplete == null ? void 0 : onCloseComplete(); }, [isPresent]); (0, import_utils2.useUpdateEffect)(() => { setDelay(duration); }, [duration]); const onMouseEnter = () => setDelay(null); const onMouseLeave = () => setDelay(duration); const onClose = () => { if (isPresent) onDelete(); }; (0, import_react2.useEffect)(() => { if (isPresent && isDelete) onDelete(); }, [isPresent, isDelete, onDelete]); (0, import_use_timeout.useTimeout)(onClose, delay); const css = { maxW: "36rem", minW: "20rem", pointerEvents: "auto", ...style }; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_motion.motion.li, { className: "ui-notice__list__item", style: { display: "flex", justifyContent: placement.includes("left") ? "flex-start" : placement.includes("right") ? "flex-end" : "center" }, animate: "animate", custom: { placement }, exit: "exit", initial: "initial", layout: true, variants, onHoverEnd: onMouseLeave, onHoverStart: onMouseEnter, ...itemProps, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core2.ui.div, { className: "ui-notice__list__item__inner", __css: css, children: (0, import_utils2.runIfFunc)(message, { onClose }) }) } ); } ); NoticeComponent.displayName = "NoticeComponent"; NoticeComponent.__ui__ = "NoticeComponent"; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { NoticeProvider, noticeStore, useNotice }); //# sourceMappingURL=index.js.map