|
|
'use client'; import React from 'react'; import { createModal as createDrawer } from '@gluestack-ui/modal'; import { Pressable, View, ScrollView, Dimensions, ViewStyle, } from 'react-native'; import { Motion, AnimatePresence, createMotionAnimatedComponent, MotionComponentProps, } from '@legendapp/motion'; import { tva } from '@gluestack-ui/nativewind-utils/tva'; import { withStyleContext, useStyleContext, } from '@gluestack-ui/nativewind-utils/withStyleContext'; import { cssInterop } from 'nativewind'; import type { VariantProps } from '@gluestack-ui/nativewind-utils';
type IAnimatedPressableProps = React.ComponentProps<typeof Pressable> & MotionComponentProps<typeof Pressable, ViewStyle, unknown, unknown, unknown>;
const AnimatedPressable = createMotionAnimatedComponent( Pressable ) as React.ComponentType<IAnimatedPressableProps>;
const SCOPE = 'MODAL'; const screenWidth = Dimensions.get('window').width; const screenHeight = Dimensions.get('window').height; const sizes: { [key: string]: number } = { sm: 0.25, md: 0.5, lg: 0.75, full: 1, };
type IMotionViewProps = React.ComponentProps<typeof View> & MotionComponentProps<typeof View, ViewStyle, unknown, unknown, unknown>;
const MotionView = Motion.View as React.ComponentType<IMotionViewProps>;
const UIDrawer = createDrawer({ Root: withStyleContext(View, SCOPE), Backdrop: AnimatedPressable, Content: MotionView, Body: ScrollView, CloseButton: Pressable, Footer: View, Header: View, AnimatePresence: AnimatePresence, });
cssInterop(AnimatedPressable, { className: 'style' }); cssInterop(MotionView, { className: 'style' });
const drawerStyle = tva({ base: 'w-full h-full web:pointer-events-none relative', variants: { size: { sm: '', md: '', lg: '', full: '', }, anchor: { left: 'items-start', right: 'items-end', top: 'justify-start', bottom: 'justify-end', }, }, });
const drawerBackdropStyle = tva({ base: 'absolute left-0 top-0 right-0 bottom-0 bg-background-dark web:cursor-default', });
const drawerContentStyle = tva({ base: 'bg-background-0 overflow-scroll border-outline-100 p-6 absolute', parentVariants: { size: { sm: 'w-1/4', md: 'w-1/2', lg: 'w-3/4', full: 'w-full', }, anchor: { left: 'h-full border-r', right: 'h-full border-l', top: 'w-full border-b', bottom: 'w-full border-t', }, }, parentCompoundVariants: [ { anchor: 'top', size: 'sm', class: 'h-1/4', }, { anchor: 'top', size: 'md', class: 'h-1/2', }, { anchor: 'top', size: 'lg', class: 'h-3/4', }, { anchor: 'top', size: 'full', class: 'h-full', }, { anchor: 'bottom', size: 'sm', class: 'h-1/4', }, { anchor: 'bottom', size: 'md', class: 'h-1/2', }, { anchor: 'bottom', size: 'lg', class: 'h-3/4', }, { anchor: 'bottom', size: 'full', class: 'h-full', }, ], });
const drawerCloseButtonStyle = tva({ base: 'z-10 rounded data-[focus-visible=true]:web:bg-background-100 web:outline-0 cursor-pointer', });
const drawerHeaderStyle = tva({ base: 'justify-between items-center flex-row', });
const drawerBodyStyle = tva({ base: 'mt-4 mb-6 shrink-0', });
const drawerFooterStyle = tva({ base: 'flex-row justify-end items-center', });
type IDrawerProps = React.ComponentProps<typeof UIDrawer> & VariantProps<typeof drawerStyle> & { className?: string };
type IDrawerBackdropProps = React.ComponentProps<typeof UIDrawer.Backdrop> & VariantProps<typeof drawerBackdropStyle> & { className?: string };
type IDrawerContentProps = React.ComponentProps<typeof UIDrawer.Content> & VariantProps<typeof drawerContentStyle> & { className?: string };
type IDrawerHeaderProps = React.ComponentProps<typeof UIDrawer.Header> & VariantProps<typeof drawerHeaderStyle> & { className?: string };
type IDrawerBodyProps = React.ComponentProps<typeof UIDrawer.Body> & VariantProps<typeof drawerBodyStyle> & { className?: string };
type IDrawerFooterProps = React.ComponentProps<typeof UIDrawer.Footer> & VariantProps<typeof drawerFooterStyle> & { className?: string };
type IDrawerCloseButtonProps = React.ComponentProps< typeof UIDrawer.CloseButton > & VariantProps<typeof drawerCloseButtonStyle> & { className?: string };
const Drawer = React.forwardRef< React.ComponentRef<typeof UIDrawer>, IDrawerProps >(function Drawer({ className, size = 'sm', anchor = 'left', ...props }, ref) { return ( <UIDrawer ref={ref} {...props} pointerEvents="box-none" className={drawerStyle({ size, anchor, class: className })} context={{ size, anchor }} /> ); });
const DrawerBackdrop = React.forwardRef< React.ComponentRef<typeof UIDrawer.Backdrop>, IDrawerBackdropProps >(function DrawerBackdrop({ className, ...props }, ref) { return ( <UIDrawer.Backdrop ref={ref} initial={{ opacity: 0, }} animate={{ opacity: 0.5, }} exit={{ opacity: 0, }} transition={{ type: 'spring', damping: 18, stiffness: 250, opacity: { type: 'timing', duration: 250, }, }} {...props} className={drawerBackdropStyle({ class: className, })} /> ); });
const DrawerContent = React.forwardRef< React.ComponentRef<typeof UIDrawer.Content>, IDrawerContentProps >(function DrawerContent({ className, ...props }, ref) { const { size: parentSize, anchor: parentAnchor } = useStyleContext(SCOPE);
const drawerHeight = screenHeight * (sizes[parentSize] || sizes.md); const drawerWidth = screenWidth * (sizes[parentSize] || sizes.md);
const isHorizontal = parentAnchor === 'left' || parentAnchor === 'right';
const initialObj = isHorizontal ? { x: parentAnchor === 'left' ? -drawerWidth : drawerWidth } : { y: parentAnchor === 'top' ? -drawerHeight : drawerHeight };
const animateObj = isHorizontal ? { x: 0 } : { y: 0 };
const exitObj = isHorizontal ? { x: parentAnchor === 'left' ? -drawerWidth : drawerWidth } : { y: parentAnchor === 'top' ? -drawerHeight : drawerHeight };
const customClass = isHorizontal ? `top-0 ${parentAnchor === 'left' ? 'left-0' : 'right-0'}` : `left-0 ${parentAnchor === 'top' ? 'top-0' : 'bottom-0'}`;
return ( <UIDrawer.Content ref={ref} initial={initialObj} animate={animateObj} exit={exitObj} transition={{ type: 'timing', duration: 300, }} {...props} className={drawerContentStyle({ parentVariants: { size: parentSize, anchor: parentAnchor, }, class: `${className} ${customClass}`, })} pointerEvents="auto" /> ); });
const DrawerHeader = React.forwardRef< React.ComponentRef<typeof UIDrawer.Header>, IDrawerHeaderProps >(function DrawerHeader({ className, ...props }, ref) { return ( <UIDrawer.Header ref={ref} {...props} className={drawerHeaderStyle({ class: className, })} /> ); });
const DrawerBody = React.forwardRef< React.ComponentRef<typeof UIDrawer.Body>, IDrawerBodyProps >(function DrawerBody({ className, ...props }, ref) { return ( <UIDrawer.Body ref={ref} {...props} className={drawerBodyStyle({ class: className, })} /> ); });
const DrawerFooter = React.forwardRef< React.ComponentRef<typeof UIDrawer.Footer>, IDrawerFooterProps >(function DrawerFooter({ className, ...props }, ref) { return ( <UIDrawer.Footer ref={ref} {...props} className={drawerFooterStyle({ class: className, })} /> ); });
const DrawerCloseButton = React.forwardRef< React.ComponentRef<typeof UIDrawer.CloseButton>, IDrawerCloseButtonProps >(function DrawerCloseButton({ className, ...props }, ref) { return ( <UIDrawer.CloseButton ref={ref} {...props} className={drawerCloseButtonStyle({ class: className, })} /> ); });
Drawer.displayName = 'Drawer'; DrawerBackdrop.displayName = 'DrawerBackdrop'; DrawerContent.displayName = 'DrawerContent'; DrawerHeader.displayName = 'DrawerHeader'; DrawerBody.displayName = 'DrawerBody'; DrawerFooter.displayName = 'DrawerFooter'; DrawerCloseButton.displayName = 'DrawerCloseButton';
export { Drawer, DrawerBackdrop, DrawerContent, DrawerCloseButton, DrawerHeader, DrawerBody, DrawerFooter, };
|