廓形仪rn版本-技术调研
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

354 lines
8.5 KiB

'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,
};