UNPKG

@easy-shadcn/react

Version:

Use shadcn/ui easy&enhance like component library

721 lines (714 loc) 22.2 kB
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 };