@yamada-ui/notice
Version:
Yamada UI notice component
425 lines (422 loc) • 13.5 kB
JavaScript
"use client"
;
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