@easy-shadcn/react
Version:
Use shadcn/ui easy&enhance like component library
721 lines (714 loc) • 22.2 kB
JavaScript
import { Dialog, DialogTrigger, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from './chunk-525HUKLS.mjs';
import { useLocale_default } from './chunk-35JJS2VW.mjs';
import { Button } from './chunk-P4KSIALF.mjs';
import { buttonVariants } from './chunk-KWT7SCRZ.mjs';
import * as React2 from 'react';
import { createContext, useState, useEffect, useReducer, useContext, useCallback, useMemo } from 'react';
import { cn } from '@easy-shadcn/utils';
import * as AlertDialogPrimitive2 from '@radix-ui/react-alert-dialog';
import { AnimatePresence, motion } from 'motion/react';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { Cross2Icon } from '@radix-ui/react-icons';
var AlertDialogAnimateContent = React2.forwardRef(({ className, children, open, onExitComplete, ...props }, ref) => /* @__PURE__ */ jsx(AnimatePresence, { onExitComplete, children: open && /* @__PURE__ */ jsxs(AlertDialogPrimitive2.Portal, { forceMount: true, children: [
/* @__PURE__ */ jsx(AlertDialogPrimitive2.Overlay, { forceMount: true, asChild: true, children: /* @__PURE__ */ jsx(
motion.div,
{
className: cn("fixed inset-0 z-50 bg-black/80"),
exit: { opacity: 0 },
animate: { opacity: 1 },
initial: { opacity: 0 }
}
) }),
/* @__PURE__ */ jsx(AlertDialogPrimitive2.Content, { ref, forceMount: true, asChild: true, ...props, children: /* @__PURE__ */ jsx(
motion.div,
{
className: cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg sm:rounded-lg",
className
),
initial: { opacity: 0, scale: 0.5, translateX: "-50%", translateY: "-50%" },
animate: { opacity: 1, scale: 1, translateX: "-50%", translateY: "-50%" },
exit: { opacity: 0, scale: 0.5, translateX: "-50%", translateY: "-50%" },
transition: { type: "spring", duration: 0.5, bounce: 0.3 },
children
}
) })
] }) }));
AlertDialogAnimateContent.displayName = AlertDialogPrimitive2.Content.displayName;
var AlertDialog = AlertDialogPrimitive2.Root;
var AlertDialogTrigger = AlertDialogPrimitive2.Trigger;
var AlertDialogPortal = AlertDialogPrimitive2.Portal;
var AlertDialogOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
AlertDialogPrimitive2.Overlay,
{
className: cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
),
...props,
ref
}
));
AlertDialogOverlay.displayName = AlertDialogPrimitive2.Overlay.displayName;
var AlertDialogContent = React2.forwardRef(({ className, overlayProps, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
/* @__PURE__ */ jsx(AlertDialogOverlay, { ...overlayProps }),
/* @__PURE__ */ jsx(
AlertDialogPrimitive2.Content,
{
ref,
className: cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
),
...props
}
)
] }));
AlertDialogContent.displayName = AlertDialogPrimitive2.Content.displayName;
var AlertDialogHeader = ({
className,
...props
}) => /* @__PURE__ */ jsx(
"div",
{
className: cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
),
...props
}
);
AlertDialogHeader.displayName = "AlertDialogHeader";
var AlertDialogFooter = ({
className,
...props
}) => /* @__PURE__ */ jsx(
"div",
{
className: cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
),
...props
}
);
AlertDialogFooter.displayName = "AlertDialogFooter";
var AlertDialogTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
AlertDialogPrimitive2.Title,
{
ref,
className: cn("text-lg font-semibold", className),
...props
}
));
AlertDialogTitle.displayName = AlertDialogPrimitive2.Title.displayName;
var AlertDialogDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
AlertDialogPrimitive2.Description,
{
ref,
className: cn("text-sm text-muted-foreground", className),
...props
}
));
AlertDialogDescription.displayName = AlertDialogPrimitive2.Description.displayName;
var AlertDialogAction = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
AlertDialogPrimitive2.Action,
{
ref,
className: cn(buttonVariants(), className),
...props
}
));
AlertDialogAction.displayName = AlertDialogPrimitive2.Action.displayName;
var AlertDialogCancel = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
AlertDialogPrimitive2.Cancel,
{
ref,
className: cn(
buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0",
className
),
...props
}
));
AlertDialogCancel.displayName = AlertDialogPrimitive2.Cancel.displayName;
var AlertModal = ({
title,
content,
contentProps,
children,
cancelText,
onCancel,
cancelProps,
confirmText,
onConfirm,
confirmProps,
onOpenChange,
afterClose,
open,
...props
}) => {
const [innerOpen, setInnerOpen] = useState(open || false);
const [locale] = useLocale_default("AlertModal");
useEffect(() => {
setInnerOpen(open || false);
}, [open]);
const handleClose = () => {
setInnerOpen(false);
onOpenChange?.(false);
};
return /* @__PURE__ */ jsxs(
AlertDialog,
{
onOpenChange: (op) => {
setInnerOpen(op);
onOpenChange?.(op);
},
open: innerOpen,
...props,
children: [
children && /* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children }),
/* @__PURE__ */ jsxs(AlertDialogAnimateContent, { open: innerOpen, onExitComplete: afterClose, ...contentProps, children: [
(title || content) && /* @__PURE__ */ jsxs(AlertDialogHeader, { className: "w-full overflow-auto whitespace-break-spaces", children: [
title && /* @__PURE__ */ jsx(AlertDialogTitle, { children: title }),
content && /* @__PURE__ */ jsx(AlertDialogDescription, { children: content })
] }),
/* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
/* @__PURE__ */ jsx(
Button,
{
variant: "outline",
onClick: async () => {
await onCancel?.();
handleClose();
},
className: cn("mt-2 sm:mt-0", cancelProps?.className),
...cancelProps,
children: cancelText || locale.cancelText
}
),
/* @__PURE__ */ jsx(
Button,
{
variant: "default",
onClick: async () => {
await onConfirm?.();
handleClose();
},
...confirmProps,
children: confirmText || locale.okText
}
)
] })
] })
]
}
);
};
var DialogAnimateContent = React2.forwardRef(({ className, children, open, onExitComplete, ...props }, ref) => {
return /* @__PURE__ */ jsx(AnimatePresence, { onExitComplete, children: open && /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { forceMount: true, children: [
/* @__PURE__ */ jsx(DialogPrimitive.Overlay, { forceMount: true, asChild: true, ...props, children: /* @__PURE__ */ jsx(
motion.div,
{
className: cn("fixed inset-0 z-50 bg-black/80"),
exit: { opacity: 0 },
animate: { opacity: 1 },
initial: { opacity: 0 }
}
) }),
/* @__PURE__ */ jsx(DialogPrimitive.Content, { ref, forceMount: true, asChild: true, ...props, children: /* @__PURE__ */ jsxs(
motion.div,
{
className: cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg sm:rounded-lg",
className
),
initial: { opacity: 0, scale: 0.5, translateX: "-50%", translateY: "-50%" },
animate: { opacity: 1, scale: 1, translateX: "-50%", translateY: "-50%" },
exit: { opacity: 0, scale: 0.5, translateX: "-50%", translateY: "-50%" },
transition: { type: "spring", duration: 0.5, bounce: 0.3 },
children: [
children,
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { tabIndex: -1, className: "absolute right-4 top-4 flex size-7 items-center justify-center rounded-sm text-muted-foreground opacity-70 ring-offset-background transition-opacity hover:bg-accent hover:opacity-100 disabled:pointer-events-none", children: [
/* @__PURE__ */ jsx(Cross2Icon, { className: "size-4" }),
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
] })
]
}
) })
] }) });
});
DialogAnimateContent.displayName = DialogPrimitive.Content.displayName;
var Modal = ({
title,
titleProps,
description,
descriptionProps,
footer,
footerProps,
content,
contentProps,
children,
open,
afterClose,
onOpenChange,
...restProps
}) => {
const [innerOpen, setInnerOpen] = useState(open || false);
useEffect(() => {
setInnerOpen(open || false);
}, [open]);
return /* @__PURE__ */ jsxs(
Dialog,
{
open: innerOpen,
onOpenChange: (op) => {
setInnerOpen(op);
onOpenChange?.(op);
},
...restProps,
children: [
/* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children }),
/* @__PURE__ */ jsxs(
DialogAnimateContent,
{
...contentProps,
open: innerOpen,
className: cn(
"max-h-screen grid-rows-[auto_1fr_auto] gap-0 py-3 px-0",
contentProps?.className
),
onExitComplete: afterClose,
children: [
/* @__PURE__ */ jsxs(DialogHeader, { className: "px-6 py-3", children: [
title && /* @__PURE__ */ jsx(DialogTitle, { ...titleProps, children: title }),
description && /* @__PURE__ */ jsx(DialogDescription, { ...descriptionProps, children: description })
] }),
/* @__PURE__ */ jsx("div", { className: "overflow-auto px-6 py-3", children: content }),
footer && /* @__PURE__ */ jsx(
DialogFooter,
{
...footerProps,
className: cn("px-6 py-3 space-x-2", footerProps?.className),
children: footer
}
)
]
}
)
]
}
);
};
// src/modal/modal-helper/symbol.ts
var symModalId = Symbol("ModalHelperId");
// src/modal/modal-helper/constants.ts
var MODAL_REGISTRY = {};
var ALREADY_MOUNTED = {};
var uidSeed = 0;
var getUid = () => `_nice_modal_${uidSeed++}`;
var modalCallbacks = {};
var hideModalCallbacks = {};
function getModalId(modal) {
if (typeof modal === "string") return modal;
if (!modal[symModalId]) {
modal[symModalId] = getUid();
}
return modal[symModalId];
}
var reducer = (state = initialState, action) => {
switch (action.type) {
case "modal-helper/show" /* showModal */: {
const { modalId, args } = action.payload;
return {
...state,
[modalId]: {
...state[modalId],
id: modalId,
args,
// If modal is not mounted, mount it first then make it visible.
// There is logic inside HOC wrapper to make it visible after its first mount.
// This mechanism ensures the entering transition.
visible: !!ALREADY_MOUNTED[modalId],
delayVisible: !ALREADY_MOUNTED[modalId]
}
};
}
case "modal-helper/hide" /* hideModal */: {
const { modalId } = action.payload;
if (!state[modalId]) return state;
return {
...state,
[modalId]: {
...state[modalId],
visible: false
}
};
}
case "modal-helper/remove" /* removeModal */: {
const { modalId } = action.payload;
const newState = { ...state };
delete newState[modalId];
return newState;
}
case "modal-helper/set-flags" /* setModalFlags */: {
const { modalId, flags } = action.payload;
return {
...state,
[modalId]: {
...state[modalId],
...flags
}
};
}
default:
return state;
}
};
var reducerDispatch = () => {
throw new Error("No dispatch method detected, did you embed your app with ModalHelper.Provider?");
};
var reducerActions = {
// action creator to show a modal
showModal: (modalId, args) => {
reducerDispatch({
type: "modal-helper/show" /* showModal */,
payload: {
modalId,
args
}
});
},
// action creator to set flags of a modal
setModalFlags: (modalId, flags) => {
reducerDispatch({
type: "modal-helper/set-flags" /* setModalFlags */,
payload: {
modalId,
flags
}
});
},
// action creator to hide a modal
hideModal: (modalId) => {
reducerDispatch({
type: "modal-helper/hide" /* hideModal */,
payload: {
modalId
}
});
},
// action creator to remove a modal
removeModal: (modalId) => {
reducerDispatch({
type: "modal-helper/remove" /* removeModal */,
payload: {
modalId
}
});
}
};
var initialState = {};
var ModalHelperContext = createContext(initialState);
var ModalHelperIdContext = createContext(null);
var ModalHelperPlaceholder = () => {
const modals = useContext(ModalHelperContext);
const visibleModalIds = Object.keys(modals).filter((id) => !!modals[id]);
visibleModalIds.forEach((id) => {
if (!MODAL_REGISTRY[id] && !ALREADY_MOUNTED[id]) {
console.warn(
`No modal found for id: ${id}. Please check the id or if it is registered or declared via JSX.`
);
return;
}
});
const toRender = visibleModalIds.filter((id) => MODAL_REGISTRY[id]).map((id) => ({
id,
...MODAL_REGISTRY[id]
}));
return /* @__PURE__ */ jsx(Fragment, { children: toRender.map((t) => /* @__PURE__ */ jsx(t.comp, { id: t.id, ...t.props }, t.id)) });
};
var Provider = ({ children }) => {
const [modals, dispatch] = useReducer(reducer, initialState);
reducerDispatch = dispatch;
return /* @__PURE__ */ jsxs(ModalHelperContext.Provider, { value: modals, children: [
children,
/* @__PURE__ */ jsx(ModalHelperPlaceholder, {})
] });
};
function ModalHolder({
modal,
handler,
...restProps
}) {
const mid = useMemo(() => getUid(), []);
const ModalComp = typeof modal === "string" ? MODAL_REGISTRY[modal]?.comp : modal;
if (!ModalComp && typeof modal === "string") {
throw new Error(`No modal found for id: ${modal} in ModalHelper.ModalHolder.`);
}
handler.show = useCallback((args) => show(mid, args), [mid]);
handler.hide = useCallback(() => hide(mid), [mid]);
return /* @__PURE__ */ jsx(ModalComp, { id: mid, ...restProps });
}
function useModal(modal, args) {
const modals = useContext(ModalHelperContext);
const contextModalId = useContext(ModalHelperIdContext);
let modalId = null;
const isUseComponent = modal && typeof modal !== "string";
if (!modal) {
modalId = contextModalId;
} else {
modalId = getModalId(modal);
}
if (!modalId) throw new Error("No modal id found in ModalHelper.useModal.");
const mid = modalId;
useEffect(() => {
if (isUseComponent && !MODAL_REGISTRY[mid]) {
register(mid, modal, args);
}
}, [isUseComponent, mid, modal, args]);
const modalInfo = modals[mid];
const showCallback = useCallback((args2) => show(mid, args2), [mid]);
const hideCallback = useCallback(() => hide(mid), [mid]);
const removeCallback = useCallback(() => remove(mid), [mid]);
const resolveCallback = useCallback(
(args2) => {
modalCallbacks[mid]?.resolve(args2);
delete modalCallbacks[mid];
},
[mid]
);
const rejectCallback = useCallback(
(args2) => {
modalCallbacks[mid]?.reject(args2);
delete modalCallbacks[mid];
},
[mid]
);
const resolveHide = useCallback(
(args2) => {
hideModalCallbacks[mid]?.resolve(args2);
delete hideModalCallbacks[mid];
},
[mid]
);
return useMemo(
() => ({
id: mid,
args: modalInfo?.args,
visible: !!modalInfo?.visible,
keepMounted: !!modalInfo?.keepMounted,
show: showCallback,
hide: hideCallback,
remove: removeCallback,
resolve: resolveCallback,
reject: rejectCallback,
resolveHide,
modalProps: {
open: modalInfo?.visible,
onOpenChange: (open) => {
if (open) {
void showCallback();
} else {
void hideCallback();
}
},
afterClose: () => {
resolveHide();
if (!modalInfo?.keepMounted) removeCallback();
}
}
}),
[
mid,
modalInfo?.args,
modalInfo?.visible,
modalInfo?.keepMounted,
showCallback,
hideCallback,
removeCallback,
resolveCallback,
rejectCallback,
resolveHide
]
);
}
function useModalHolder(modal) {
const handler = useMemo(() => ({}), []);
const ModalHolderCallback = useCallback(
(props) => /* @__PURE__ */ jsx(ModalHolder, { modal, ...props, handler }),
[modal, handler]
);
return [handler, ModalHolderCallback];
}
function show(modal, args) {
const modalId = getModalId(modal);
if (typeof modal !== "string" && !MODAL_REGISTRY[modalId]) {
register(modalId, modal);
}
reducerActions.showModal(modalId, args);
if (!modalCallbacks[modalId]) {
let theResolve;
let theReject;
const promise = new Promise((resolve, reject) => {
theResolve = resolve;
theReject = reject;
});
modalCallbacks[modalId] = {
resolve: theResolve,
reject: theReject,
promise
};
}
return modalCallbacks[modalId].promise;
}
function hide(modal) {
const modalId = getModalId(modal);
reducerActions.hideModal(modalId);
delete modalCallbacks[modalId];
if (!hideModalCallbacks[modalId]) {
let theResolve;
let theReject;
const promise = new Promise((resolve, reject) => {
theResolve = resolve;
theReject = reject;
});
hideModalCallbacks[modalId] = {
resolve: theResolve,
reject: theReject,
promise
};
}
return hideModalCallbacks[modalId].promise;
}
var remove = (modal) => {
const modalId = getModalId(modal);
reducerActions.removeModal(modalId);
delete modalCallbacks[modalId];
delete hideModalCallbacks[modalId];
};
var create = (Comp) => {
const HocComp = ({ defaultVisible, keepMounted, id, ...props }) => {
const { args, show: show2 } = useModal(id);
const modals = useContext(ModalHelperContext);
const shouldMount = !!modals[id];
useEffect(() => {
if (defaultVisible) {
void show2();
}
ALREADY_MOUNTED[id] = true;
return () => {
delete ALREADY_MOUNTED[id];
};
}, [id, show2, defaultVisible]);
useEffect(() => {
if (keepMounted) {
reducerActions.setModalFlags(id, { keepMounted: true });
}
}, [id, keepMounted]);
const delayVisible = modals[id]?.delayVisible;
useEffect(() => {
if (delayVisible) {
void show2(args);
}
}, [delayVisible, args, show2]);
if (!shouldMount) return null;
return /* @__PURE__ */ jsx(ModalHelperIdContext.Provider, { value: id, children: /* @__PURE__ */ jsx(Comp, { ...props, ...args }) });
};
return HocComp;
};
var register = (id, comp, props) => {
if (!MODAL_REGISTRY[id]) {
MODAL_REGISTRY[id] = { comp, props };
} else {
MODAL_REGISTRY[id].props = props;
}
};
// src/modal/modal-helper/index.tsx
var modalProps = (modal) => {
return {
open: modal.visible,
onOpenChange: (open) => {
if (open) {
void modal.show();
} else {
void modal.hide();
}
},
afterClose: () => {
modal.resolveHide();
if (!modal.keepMounted) modal.remove();
}
};
};
var ModalHelper = {
Provider,
ModalHelperContext,
create,
register,
show,
hide,
remove,
useModal,
useModalHolder,
reducer,
modalProps
};
var modal_helper_default = ModalHelper;
var alert = (props) => {
const AlertModalHelper = modal_helper_default.create(() => {
const { modalProps: modalProps2, resolve } = modal_helper_default.useModal();
return /* @__PURE__ */ jsx(
AlertModal,
{
...props,
...modalProps2,
cancelProps: {
className: "hidden"
},
onConfirm: async () => {
await props.onConfirm?.();
resolve();
}
}
);
});
return modal_helper_default.show(AlertModalHelper);
};
var confirm = (props) => {
const AlertModalHelper = modal_helper_default.create(() => {
const { modalProps: modalProps2, resolve, reject } = modal_helper_default.useModal();
return /* @__PURE__ */ jsx(
AlertModal,
{
...props,
...modalProps2,
onConfirm: async () => {
await props.onConfirm?.();
resolve();
},
onCancel: async () => {
await props.onCancel?.();
reject();
}
}
);
});
return modal_helper_default.show(AlertModalHelper);
};
var alert_modal_helper_default = {
alert,
confirm
};
// src/modal/index.tsx
var AlertModal2 = Object.assign(
AlertModal,
alert_modal_helper_default
);
var Modal2 = Object.assign(
Modal,
modal_helper_default
);
export { AlertModal2 as AlertModal, Modal2 as Modal };