@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 12 kB
Source Map (JSON)
{"version":3,"file":"atoms.mjs","names":["ModalDescription: React.FC<ModalDescriptionProps>"],"sources":["../../../src/base-ui/Modal/atoms.tsx"],"sourcesContent":["'use client';\n\nimport { Dialog } from '@base-ui/react/dialog';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { cx } from 'antd-style';\nimport { X } from 'lucide-react';\nimport { AnimatePresence } from 'motion/react';\nimport type React from 'react';\nimport {\n cloneElement,\n createContext,\n isValidElement,\n use,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport { mergeRefs } from 'react-merge-refs';\n\nimport { useNativeButton } from '@/hooks/useNativeButton';\nimport { useMotionComponent } from '@/MotionProvider';\nimport { useAppElement } from '@/ThemeProvider';\n\nimport { backdropTransition, modalMotionConfig } from './constants';\nimport { styles } from './style';\n\nconst mergeStateClassName = <TState,>(\n base: string,\n className: string | ((state: TState) => string | undefined) | undefined,\n) => {\n if (typeof className === 'function') return (state: TState) => cx(base, className(state));\n return cx(base, className);\n};\n\n// --- Animation Contexts (granular to minimize re-renders) ---\n\n// State: open boolean, null = non-animated mode\nconst ModalOpenContext = createContext<boolean | null>(null);\n\n// Actions: stable callbacks, null = non-animated mode\ninterface ModalAnimationActions {\n onExitComplete: () => void;\n}\nconst ModalActionsContext = createContext<ModalAnimationActions | null>(null);\n\nexport const useModalOpen = () => use(ModalOpenContext);\nexport const useModalActions = () => use(ModalActionsContext);\n\n// --- Root ---\nexport type ModalRootProps = Dialog.Root.Props & {\n onExitComplete?: () => void;\n};\n\nconst AnimatedModalRoot = ({\n open,\n children,\n onExitComplete: onExitCompleteProp,\n ...rest\n}: Omit<ModalRootProps, 'open'> & { open: boolean }) => {\n const [isPresent, setIsPresent] = useState(!!open);\n\n useEffect(() => {\n if (open) setIsPresent(true);\n }, [open]);\n\n const handleExitComplete = useCallback(() => {\n setIsPresent(false);\n onExitCompleteProp?.();\n }, [onExitCompleteProp]);\n\n const actions = useMemo(() => ({ onExitComplete: handleExitComplete }), [handleExitComplete]);\n\n if (!isPresent) return null;\n\n return (\n <ModalOpenContext value={open}>\n <ModalActionsContext value={actions}>\n <Dialog.Root modal open {...rest}>\n {children}\n </Dialog.Root>\n </ModalActionsContext>\n </ModalOpenContext>\n );\n};\n\nexport const ModalRoot = ({ open, onExitComplete, ...rest }: ModalRootProps) => {\n if (open !== undefined) {\n return <AnimatedModalRoot open={open} onExitComplete={onExitComplete} {...rest} />;\n }\n return <Dialog.Root modal {...rest} />;\n};\n\n// --- Portal ---\nexport type ModalPortalProps = React.ComponentProps<typeof Dialog.Portal> & {\n container?: HTMLElement | null;\n};\nexport const ModalPortal = ({ container, ...rest }: ModalPortalProps) => {\n const appElement = useAppElement();\n return <Dialog.Portal container={container ?? appElement ?? undefined} {...rest} />;\n};\n\n// --- Viewport ---\nexport type ModalViewportProps = React.ComponentProps<typeof Dialog.Viewport>;\nexport const ModalViewport = ({ className, ...rest }: ModalViewportProps) => (\n <Dialog.Viewport\n {...rest}\n className={mergeStateClassName(styles.viewport, className as any) as any}\n />\n);\n\n// --- Backdrop ---\nexport type ModalBackdropProps = React.ComponentProps<typeof Dialog.Backdrop>;\nexport const ModalBackdrop = ({ className, style, ...rest }: ModalBackdropProps) => {\n const open = useModalOpen();\n const Motion = useMotionComponent();\n\n if (open !== null) {\n return (\n <Dialog.Backdrop\n {...rest}\n className={cx(styles.backdrop, className as string)}\n style={{ ...style, transition: 'none' }}\n render={\n <Motion.div\n animate={{ opacity: open ? 1 : 0 }}\n initial={{ opacity: 0 }}\n transition={backdropTransition}\n />\n }\n />\n );\n }\n\n return (\n <Dialog.Backdrop\n {...rest}\n className={mergeStateClassName(styles.backdrop, className as any) as any}\n style={style}\n />\n );\n};\n\n// --- Popup ---\nexport type ModalPopupProps = React.ComponentProps<typeof Dialog.Popup> & {\n motionProps?: Record<string, any>;\n panelClassName?: string;\n popupStyle?: React.CSSProperties;\n width?: number | string;\n};\nexport const ModalPopup = ({\n className,\n children,\n width,\n style,\n motionProps,\n panelClassName,\n popupStyle,\n ...rest\n}: ModalPopupProps) => {\n const open = useModalOpen();\n const actions = useModalActions();\n const Motion = useMotionComponent();\n\n if (open !== null && actions) {\n return (\n <Dialog.Popup {...rest} className={cx(styles.popup, className as string)} style={popupStyle}>\n <AnimatePresence onExitComplete={actions.onExitComplete}>\n {open ? (\n <Motion.div\n {...modalMotionConfig}\n {...motionProps}\n className={cx(styles.popupInner, panelClassName)}\n key=\"modal-popup-panel\"\n style={{ maxWidth: width ?? undefined, transition: 'none', ...style }}\n >\n {children}\n </Motion.div>\n ) : null}\n </AnimatePresence>\n </Dialog.Popup>\n );\n }\n\n return (\n <Dialog.Popup\n {...rest}\n className={mergeStateClassName(styles.popup, className as any) as any}\n style={popupStyle}\n >\n <div\n className={cx(styles.popupInner, panelClassName)}\n style={{ maxWidth: width ?? undefined, ...style }}\n >\n {children}\n </div>\n </Dialog.Popup>\n );\n};\n\n// --- Header ---\nexport type ModalHeaderProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\nexport const ModalHeader = ({ className, ...rest }: ModalHeaderProps) => (\n <div {...rest} className={cx(styles.header, className)} />\n);\n\n// --- Title ---\nexport type ModalTitleProps = React.ComponentProps<typeof Dialog.Title>;\nexport const ModalTitle = ({ className, ...rest }: ModalTitleProps) => (\n <Dialog.Title {...rest} className={mergeStateClassName(styles.title, className as any) as any} />\n);\n\n// --- Description ---\nexport type ModalDescriptionProps = React.ComponentProps<typeof Dialog.Description>;\nexport const ModalDescription: React.FC<ModalDescriptionProps> = Dialog.Description;\n\n// --- Content ---\nexport type ModalContentProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\nexport const ModalContent = ({ className, ...rest }: ModalContentProps) => (\n <div {...rest} className={cx(styles.content, className)} />\n);\n\n// --- Footer ---\nexport type ModalFooterProps = React.HTMLAttributes<HTMLDivElement> & {\n ref?: React.Ref<HTMLDivElement>;\n};\nexport const ModalFooter = ({ className, ...rest }: ModalFooterProps) => (\n <div {...rest} className={cx(styles.footer, className)} />\n);\n\n// --- Close ---\nexport type ModalCloseProps = React.ComponentProps<typeof Dialog.Close>;\nexport const ModalClose = ({ className, children, ...rest }: ModalCloseProps) => (\n <Dialog.Close {...rest} className={mergeStateClassName(styles.close, className as any) as any}>\n {children ?? <X size={18} />}\n </Dialog.Close>\n);\n\n// --- Trigger ---\nexport type ModalTriggerProps = Omit<\n React.ComponentPropsWithRef<typeof Dialog.Trigger>,\n 'children' | 'render'\n> & {\n children?: React.ReactNode;\n nativeButton?: boolean;\n};\n\nexport const ModalTrigger = ({\n children,\n className,\n nativeButton,\n ref: refProp,\n ...rest\n}: ModalTriggerProps) => {\n const { isNativeButtonTriggerElement, resolvedNativeButton } = useNativeButton({\n children,\n nativeButton,\n });\n\n const renderer = (props: any) => {\n const resolvedProps = (() => {\n if (isNativeButtonTriggerElement) return props as any;\n // eslint-disable-next-line unused-imports/no-unused-vars\n const { type, ...restProps } = props as any;\n return restProps;\n })();\n\n const mergedProps = mergeProps((children as any).props, resolvedProps);\n return cloneElement(children as any, {\n ...mergedProps,\n ref: mergeRefs([(children as any).ref, (props as any).ref, refProp]),\n });\n };\n\n if (isValidElement(children)) {\n return (\n <Dialog.Trigger\n {...rest}\n className={className}\n nativeButton={resolvedNativeButton}\n render={renderer as any}\n />\n );\n }\n\n return (\n <Dialog.Trigger\n {...rest}\n className={className}\n nativeButton={resolvedNativeButton}\n ref={refProp as any}\n >\n {children}\n </Dialog.Trigger>\n );\n};\n"],"mappings":"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"}