廓形仪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

  1. 'use client';
  2. import React from 'react';
  3. import { createModal as createDrawer } from '@gluestack-ui/modal';
  4. import {
  5. Pressable,
  6. View,
  7. ScrollView,
  8. Dimensions,
  9. ViewStyle,
  10. } from 'react-native';
  11. import {
  12. Motion,
  13. AnimatePresence,
  14. createMotionAnimatedComponent,
  15. MotionComponentProps,
  16. } from '@legendapp/motion';
  17. import { tva } from '@gluestack-ui/nativewind-utils/tva';
  18. import {
  19. withStyleContext,
  20. useStyleContext,
  21. } from '@gluestack-ui/nativewind-utils/withStyleContext';
  22. import { cssInterop } from 'nativewind';
  23. import type { VariantProps } from '@gluestack-ui/nativewind-utils';
  24. type IAnimatedPressableProps = React.ComponentProps<typeof Pressable> &
  25. MotionComponentProps<typeof Pressable, ViewStyle, unknown, unknown, unknown>;
  26. const AnimatedPressable = createMotionAnimatedComponent(
  27. Pressable
  28. ) as React.ComponentType<IAnimatedPressableProps>;
  29. const SCOPE = 'MODAL';
  30. const screenWidth = Dimensions.get('window').width;
  31. const screenHeight = Dimensions.get('window').height;
  32. const sizes: { [key: string]: number } = {
  33. sm: 0.25,
  34. md: 0.5,
  35. lg: 0.75,
  36. full: 1,
  37. };
  38. type IMotionViewProps = React.ComponentProps<typeof View> &
  39. MotionComponentProps<typeof View, ViewStyle, unknown, unknown, unknown>;
  40. const MotionView = Motion.View as React.ComponentType<IMotionViewProps>;
  41. const UIDrawer = createDrawer({
  42. Root: withStyleContext(View, SCOPE),
  43. Backdrop: AnimatedPressable,
  44. Content: MotionView,
  45. Body: ScrollView,
  46. CloseButton: Pressable,
  47. Footer: View,
  48. Header: View,
  49. AnimatePresence: AnimatePresence,
  50. });
  51. cssInterop(AnimatedPressable, { className: 'style' });
  52. cssInterop(MotionView, { className: 'style' });
  53. const drawerStyle = tva({
  54. base: 'w-full h-full web:pointer-events-none relative',
  55. variants: {
  56. size: {
  57. sm: '',
  58. md: '',
  59. lg: '',
  60. full: '',
  61. },
  62. anchor: {
  63. left: 'items-start',
  64. right: 'items-end',
  65. top: 'justify-start',
  66. bottom: 'justify-end',
  67. },
  68. },
  69. });
  70. const drawerBackdropStyle = tva({
  71. base: 'absolute left-0 top-0 right-0 bottom-0 bg-background-dark web:cursor-default',
  72. });
  73. const drawerContentStyle = tva({
  74. base: 'bg-background-0 overflow-scroll border-outline-100 p-6 absolute',
  75. parentVariants: {
  76. size: {
  77. sm: 'w-1/4',
  78. md: 'w-1/2',
  79. lg: 'w-3/4',
  80. full: 'w-full',
  81. },
  82. anchor: {
  83. left: 'h-full border-r',
  84. right: 'h-full border-l',
  85. top: 'w-full border-b',
  86. bottom: 'w-full border-t',
  87. },
  88. },
  89. parentCompoundVariants: [
  90. {
  91. anchor: 'top',
  92. size: 'sm',
  93. class: 'h-1/4',
  94. },
  95. {
  96. anchor: 'top',
  97. size: 'md',
  98. class: 'h-1/2',
  99. },
  100. {
  101. anchor: 'top',
  102. size: 'lg',
  103. class: 'h-3/4',
  104. },
  105. {
  106. anchor: 'top',
  107. size: 'full',
  108. class: 'h-full',
  109. },
  110. {
  111. anchor: 'bottom',
  112. size: 'sm',
  113. class: 'h-1/4',
  114. },
  115. {
  116. anchor: 'bottom',
  117. size: 'md',
  118. class: 'h-1/2',
  119. },
  120. {
  121. anchor: 'bottom',
  122. size: 'lg',
  123. class: 'h-3/4',
  124. },
  125. {
  126. anchor: 'bottom',
  127. size: 'full',
  128. class: 'h-full',
  129. },
  130. ],
  131. });
  132. const drawerCloseButtonStyle = tva({
  133. base: 'z-10 rounded data-[focus-visible=true]:web:bg-background-100 web:outline-0 cursor-pointer',
  134. });
  135. const drawerHeaderStyle = tva({
  136. base: 'justify-between items-center flex-row',
  137. });
  138. const drawerBodyStyle = tva({
  139. base: 'mt-4 mb-6 shrink-0',
  140. });
  141. const drawerFooterStyle = tva({
  142. base: 'flex-row justify-end items-center',
  143. });
  144. type IDrawerProps = React.ComponentProps<typeof UIDrawer> &
  145. VariantProps<typeof drawerStyle> & { className?: string };
  146. type IDrawerBackdropProps = React.ComponentProps<typeof UIDrawer.Backdrop> &
  147. VariantProps<typeof drawerBackdropStyle> & { className?: string };
  148. type IDrawerContentProps = React.ComponentProps<typeof UIDrawer.Content> &
  149. VariantProps<typeof drawerContentStyle> & { className?: string };
  150. type IDrawerHeaderProps = React.ComponentProps<typeof UIDrawer.Header> &
  151. VariantProps<typeof drawerHeaderStyle> & { className?: string };
  152. type IDrawerBodyProps = React.ComponentProps<typeof UIDrawer.Body> &
  153. VariantProps<typeof drawerBodyStyle> & { className?: string };
  154. type IDrawerFooterProps = React.ComponentProps<typeof UIDrawer.Footer> &
  155. VariantProps<typeof drawerFooterStyle> & { className?: string };
  156. type IDrawerCloseButtonProps = React.ComponentProps<
  157. typeof UIDrawer.CloseButton
  158. > &
  159. VariantProps<typeof drawerCloseButtonStyle> & { className?: string };
  160. const Drawer = React.forwardRef<
  161. React.ComponentRef<typeof UIDrawer>,
  162. IDrawerProps
  163. >(function Drawer({ className, size = 'sm', anchor = 'left', ...props }, ref) {
  164. return (
  165. <UIDrawer
  166. ref={ref}
  167. {...props}
  168. pointerEvents="box-none"
  169. className={drawerStyle({ size, anchor, class: className })}
  170. context={{ size, anchor }}
  171. />
  172. );
  173. });
  174. const DrawerBackdrop = React.forwardRef<
  175. React.ComponentRef<typeof UIDrawer.Backdrop>,
  176. IDrawerBackdropProps
  177. >(function DrawerBackdrop({ className, ...props }, ref) {
  178. return (
  179. <UIDrawer.Backdrop
  180. ref={ref}
  181. initial={{
  182. opacity: 0,
  183. }}
  184. animate={{
  185. opacity: 0.5,
  186. }}
  187. exit={{
  188. opacity: 0,
  189. }}
  190. transition={{
  191. type: 'spring',
  192. damping: 18,
  193. stiffness: 250,
  194. opacity: {
  195. type: 'timing',
  196. duration: 250,
  197. },
  198. }}
  199. {...props}
  200. className={drawerBackdropStyle({
  201. class: className,
  202. })}
  203. />
  204. );
  205. });
  206. const DrawerContent = React.forwardRef<
  207. React.ComponentRef<typeof UIDrawer.Content>,
  208. IDrawerContentProps
  209. >(function DrawerContent({ className, ...props }, ref) {
  210. const { size: parentSize, anchor: parentAnchor } = useStyleContext(SCOPE);
  211. const drawerHeight = screenHeight * (sizes[parentSize] || sizes.md);
  212. const drawerWidth = screenWidth * (sizes[parentSize] || sizes.md);
  213. const isHorizontal = parentAnchor === 'left' || parentAnchor === 'right';
  214. const initialObj = isHorizontal
  215. ? { x: parentAnchor === 'left' ? -drawerWidth : drawerWidth }
  216. : { y: parentAnchor === 'top' ? -drawerHeight : drawerHeight };
  217. const animateObj = isHorizontal ? { x: 0 } : { y: 0 };
  218. const exitObj = isHorizontal
  219. ? { x: parentAnchor === 'left' ? -drawerWidth : drawerWidth }
  220. : { y: parentAnchor === 'top' ? -drawerHeight : drawerHeight };
  221. const customClass = isHorizontal
  222. ? `top-0 ${parentAnchor === 'left' ? 'left-0' : 'right-0'}`
  223. : `left-0 ${parentAnchor === 'top' ? 'top-0' : 'bottom-0'}`;
  224. return (
  225. <UIDrawer.Content
  226. ref={ref}
  227. initial={initialObj}
  228. animate={animateObj}
  229. exit={exitObj}
  230. transition={{
  231. type: 'timing',
  232. duration: 300,
  233. }}
  234. {...props}
  235. className={drawerContentStyle({
  236. parentVariants: {
  237. size: parentSize,
  238. anchor: parentAnchor,
  239. },
  240. class: `${className} ${customClass}`,
  241. })}
  242. pointerEvents="auto"
  243. />
  244. );
  245. });
  246. const DrawerHeader = React.forwardRef<
  247. React.ComponentRef<typeof UIDrawer.Header>,
  248. IDrawerHeaderProps
  249. >(function DrawerHeader({ className, ...props }, ref) {
  250. return (
  251. <UIDrawer.Header
  252. ref={ref}
  253. {...props}
  254. className={drawerHeaderStyle({
  255. class: className,
  256. })}
  257. />
  258. );
  259. });
  260. const DrawerBody = React.forwardRef<
  261. React.ComponentRef<typeof UIDrawer.Body>,
  262. IDrawerBodyProps
  263. >(function DrawerBody({ className, ...props }, ref) {
  264. return (
  265. <UIDrawer.Body
  266. ref={ref}
  267. {...props}
  268. className={drawerBodyStyle({
  269. class: className,
  270. })}
  271. />
  272. );
  273. });
  274. const DrawerFooter = React.forwardRef<
  275. React.ComponentRef<typeof UIDrawer.Footer>,
  276. IDrawerFooterProps
  277. >(function DrawerFooter({ className, ...props }, ref) {
  278. return (
  279. <UIDrawer.Footer
  280. ref={ref}
  281. {...props}
  282. className={drawerFooterStyle({
  283. class: className,
  284. })}
  285. />
  286. );
  287. });
  288. const DrawerCloseButton = React.forwardRef<
  289. React.ComponentRef<typeof UIDrawer.CloseButton>,
  290. IDrawerCloseButtonProps
  291. >(function DrawerCloseButton({ className, ...props }, ref) {
  292. return (
  293. <UIDrawer.CloseButton
  294. ref={ref}
  295. {...props}
  296. className={drawerCloseButtonStyle({
  297. class: className,
  298. })}
  299. />
  300. );
  301. });
  302. Drawer.displayName = 'Drawer';
  303. DrawerBackdrop.displayName = 'DrawerBackdrop';
  304. DrawerContent.displayName = 'DrawerContent';
  305. DrawerHeader.displayName = 'DrawerHeader';
  306. DrawerBody.displayName = 'DrawerBody';
  307. DrawerFooter.displayName = 'DrawerFooter';
  308. DrawerCloseButton.displayName = 'DrawerCloseButton';
  309. export {
  310. Drawer,
  311. DrawerBackdrop,
  312. DrawerContent,
  313. DrawerCloseButton,
  314. DrawerHeader,
  315. DrawerBody,
  316. DrawerFooter,
  317. };