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

345 lines
9.6 KiB

  1. 'use client';
  2. import React from 'react';
  3. import { View, Pressable, ScrollView, ViewStyle } from 'react-native';
  4. import {
  5. Motion,
  6. createMotionAnimatedComponent,
  7. AnimatePresence,
  8. MotionComponentProps,
  9. } from '@legendapp/motion';
  10. import { createPopover } from '@gluestack-ui/popover';
  11. import { tva } from '@gluestack-ui/nativewind-utils/tva';
  12. import {
  13. withStyleContext,
  14. useStyleContext,
  15. } from '@gluestack-ui/nativewind-utils/withStyleContext';
  16. import { cssInterop } from 'nativewind';
  17. import type { VariantProps } from '@gluestack-ui/nativewind-utils';
  18. type IAnimatedPressableProps = React.ComponentProps<typeof Pressable> &
  19. MotionComponentProps<typeof Pressable, ViewStyle, unknown, unknown, unknown>;
  20. const AnimatedPressable = createMotionAnimatedComponent(
  21. Pressable
  22. ) as React.ComponentType<IAnimatedPressableProps>;
  23. const SCOPE = 'POPOVER';
  24. type IMotionViewProps = React.ComponentProps<typeof View> &
  25. MotionComponentProps<typeof View, ViewStyle, unknown, unknown, unknown>;
  26. const MotionView = Motion.View as React.ComponentType<IMotionViewProps>;
  27. const UIPopover = createPopover({
  28. Root: withStyleContext(View, SCOPE),
  29. Arrow: MotionView,
  30. Backdrop: AnimatedPressable,
  31. Body: ScrollView,
  32. CloseButton: Pressable,
  33. Content: MotionView,
  34. Footer: View,
  35. Header: View,
  36. AnimatePresence: AnimatePresence,
  37. });
  38. cssInterop(MotionView, { className: 'style' });
  39. cssInterop(AnimatedPressable, { className: 'style' });
  40. const popoverStyle = tva({
  41. base: 'group/popover w-full h-full justify-center items-center web:pointer-events-none',
  42. variants: {
  43. size: {
  44. xs: '',
  45. sm: '',
  46. md: '',
  47. lg: '',
  48. full: '',
  49. },
  50. },
  51. });
  52. const popoverArrowStyle = tva({
  53. base: 'bg-background-0 z-[1] border absolute overflow-hidden h-3.5 w-3.5 border-outline-100',
  54. variants: {
  55. placement: {
  56. 'top left':
  57. 'data-[flip=false]:border-t-0 data-[flip=false]:border-l-0 data-[flip=true]:border-b-0 data-[flip=true]:border-r-0',
  58. 'top':
  59. 'data-[flip=false]:border-t-0 data-[flip=false]:border-l-0 data-[flip=true]:border-b-0 data-[flip=true]:border-r-0',
  60. 'top right':
  61. 'data-[flip=false]:border-t-0 data-[flip=false]:border-l-0 data-[flip=true]:border-b-0 data-[flip=true]:border-r-0',
  62. 'bottom':
  63. 'data-[flip=false]:border-b-0 data-[flip=false]:border-r-0 data-[flip=true]:border-t-0 data-[flip=true]:border-l-0',
  64. 'bottom left':
  65. 'data-[flip=false]:border-b-0 data-[flip=false]:border-r-0 data-[flip=true]:border-t-0 data-[flip=true]:border-l-0',
  66. 'bottom right':
  67. 'data-[flip=false]:border-b-0 data-[flip=false]:border-r-0 data-[flip=true]:border-t-0 data-[flip=true]:border-l-0',
  68. 'left':
  69. 'data-[flip=false]:border-l-0 data-[flip=false]:border-b-0 data-[flip=true]:border-r-0 data-[flip=true]:border-t-0',
  70. 'left top':
  71. 'data-[flip=false]:border-l-0 data-[flip=false]:border-b-0 data-[flip=true]:border-r-0 data-[flip=true]:border-t-0',
  72. 'left bottom':
  73. 'data-[flip=false]:border-l-0 data-[flip=false]:border-b-0 data-[flip=true]:border-r-0 data-[flip=true]:border-t-0',
  74. 'right':
  75. 'data-[flip=false]:border-r-0 data-[flip=false]:border-t-0 data-[flip=true]:border-l-0 data-[flip=true]:border-b-0',
  76. 'right top':
  77. 'data-[flip=false]:border-r-0 data-[flip=false]:border-t-0 data-[flip=true]:border-l-0 data-[flip=true]:border-b-0',
  78. 'right bottom':
  79. 'data-[flip=false]:border-r-0 data-[flip=false]:border-t-0 data-[flip=true]:border-l-0 data-[flip=true]:border-b-0',
  80. },
  81. },
  82. });
  83. const popoverBackdropStyle = tva({
  84. base: 'absolute left-0 top-0 right-0 bottom-0 web:cursor-default',
  85. });
  86. const popoverCloseButtonStyle = tva({
  87. base: 'group/popover-close-button z-[1] rounded-sm data-[focus-visible=true]:web:bg-background-100 web:outline-0 web:cursor-pointer',
  88. });
  89. const popoverContentStyle = tva({
  90. base: 'bg-background-0 rounded-lg overflow-hidden border border-outline-100 w-full',
  91. parentVariants: {
  92. size: {
  93. xs: 'max-w-[360px] p-3.5',
  94. sm: 'max-w-[420px] p-4',
  95. md: 'max-w-[510px] p-[18px]',
  96. lg: 'max-w-[640px] p-5',
  97. full: 'p-6',
  98. },
  99. },
  100. });
  101. const popoverHeaderStyle = tva({
  102. base: 'flex-row justify-between items-center',
  103. });
  104. const popoverBodyStyle = tva({
  105. base: '',
  106. });
  107. const popoverFooterStyle = tva({
  108. base: 'flex-row justify-between items-center',
  109. });
  110. type IPopoverProps = React.ComponentProps<typeof UIPopover> &
  111. VariantProps<typeof popoverStyle> & { className?: string };
  112. type IPopoverArrowProps = React.ComponentProps<typeof UIPopover.Arrow> &
  113. VariantProps<typeof popoverArrowStyle> & { className?: string };
  114. type IPopoverContentProps = React.ComponentProps<typeof UIPopover.Content> &
  115. VariantProps<typeof popoverContentStyle> & { className?: string };
  116. type IPopoverHeaderProps = React.ComponentProps<typeof UIPopover.Header> &
  117. VariantProps<typeof popoverHeaderStyle> & { className?: string };
  118. type IPopoverFooterProps = React.ComponentProps<typeof UIPopover.Footer> &
  119. VariantProps<typeof popoverFooterStyle> & { className?: string };
  120. type IPopoverBodyProps = React.ComponentProps<typeof UIPopover.Body> &
  121. VariantProps<typeof popoverBodyStyle> & { className?: string };
  122. type IPopoverBackdropProps = React.ComponentProps<typeof UIPopover.Backdrop> &
  123. VariantProps<typeof popoverBackdropStyle> & { className?: string };
  124. type IPopoverCloseButtonProps = React.ComponentProps<
  125. typeof UIPopover.CloseButton
  126. > &
  127. VariantProps<typeof popoverCloseButtonStyle> & { className?: string };
  128. const Popover = React.forwardRef<
  129. React.ComponentRef<typeof UIPopover>,
  130. IPopoverProps
  131. >(function Popover(
  132. { className, size = 'md', placement = 'bottom', ...props },
  133. ref
  134. ) {
  135. return (
  136. <UIPopover
  137. ref={ref}
  138. placement={placement}
  139. {...props}
  140. className={popoverStyle({ size, class: className })}
  141. context={{ size, placement }}
  142. pointerEvents="box-none"
  143. />
  144. );
  145. });
  146. const PopoverContent = React.forwardRef<
  147. React.ComponentRef<typeof UIPopover.Content>,
  148. IPopoverContentProps
  149. >(function PopoverContent({ className, size, ...props }, ref) {
  150. const { size: parentSize } = useStyleContext(SCOPE);
  151. return (
  152. <UIPopover.Content
  153. ref={ref}
  154. transition={{
  155. type: 'spring',
  156. damping: 18,
  157. stiffness: 250,
  158. mass: 0.9,
  159. opacity: {
  160. type: 'timing',
  161. duration: 50,
  162. delay: 50,
  163. },
  164. }}
  165. {...props}
  166. className={popoverContentStyle({
  167. parentVariants: {
  168. size: parentSize,
  169. },
  170. size,
  171. class: className,
  172. })}
  173. pointerEvents="auto"
  174. />
  175. );
  176. });
  177. const PopoverArrow = React.forwardRef<
  178. React.ComponentRef<typeof UIPopover.Arrow>,
  179. IPopoverArrowProps
  180. >(function PopoverArrow({ className, ...props }, ref) {
  181. const { placement } = useStyleContext(SCOPE);
  182. return (
  183. <UIPopover.Arrow
  184. ref={ref}
  185. transition={{
  186. type: 'spring',
  187. damping: 18,
  188. stiffness: 250,
  189. mass: 0.9,
  190. opacity: {
  191. type: 'timing',
  192. duration: 50,
  193. delay: 50,
  194. },
  195. }}
  196. {...props}
  197. className={popoverArrowStyle({
  198. class: className,
  199. placement,
  200. })}
  201. />
  202. );
  203. });
  204. const PopoverBackdrop = React.forwardRef<
  205. React.ComponentRef<typeof UIPopover.Backdrop>,
  206. IPopoverBackdropProps
  207. >(function PopoverBackdrop({ className, ...props }, ref) {
  208. return (
  209. <UIPopover.Backdrop
  210. ref={ref}
  211. {...props}
  212. initial={{
  213. opacity: 0,
  214. }}
  215. animate={{
  216. opacity: 0.1,
  217. }}
  218. exit={{
  219. opacity: 0,
  220. }}
  221. transition={{
  222. type: 'spring',
  223. damping: 18,
  224. stiffness: 450,
  225. mass: 0.9,
  226. opacity: {
  227. type: 'timing',
  228. duration: 50,
  229. delay: 50,
  230. },
  231. }}
  232. className={popoverBackdropStyle({
  233. class: className,
  234. })}
  235. />
  236. );
  237. });
  238. const PopoverBody = React.forwardRef<
  239. React.ComponentRef<typeof UIPopover.Body>,
  240. IPopoverBodyProps
  241. >(function PopoverBody({ className, ...props }, ref) {
  242. return (
  243. <UIPopover.Body
  244. ref={ref}
  245. {...props}
  246. className={popoverBodyStyle({
  247. class: className,
  248. })}
  249. />
  250. );
  251. });
  252. const PopoverCloseButton = React.forwardRef<
  253. React.ComponentRef<typeof UIPopover.CloseButton>,
  254. IPopoverCloseButtonProps
  255. >(function PopoverCloseButton({ className, ...props }, ref) {
  256. return (
  257. <UIPopover.CloseButton
  258. ref={ref}
  259. {...props}
  260. className={popoverCloseButtonStyle({
  261. class: className,
  262. })}
  263. />
  264. );
  265. });
  266. const PopoverFooter = React.forwardRef<
  267. React.ComponentRef<typeof UIPopover.Footer>,
  268. IPopoverFooterProps
  269. >(function PopoverFooter({ className, ...props }, ref) {
  270. return (
  271. <UIPopover.Footer
  272. ref={ref}
  273. {...props}
  274. className={popoverFooterStyle({
  275. class: className,
  276. })}
  277. />
  278. );
  279. });
  280. const PopoverHeader = React.forwardRef<
  281. React.ComponentRef<typeof UIPopover.Header>,
  282. IPopoverHeaderProps
  283. >(function PopoverHeader({ className, ...props }, ref) {
  284. return (
  285. <UIPopover.Header
  286. ref={ref}
  287. {...props}
  288. className={popoverHeaderStyle({
  289. class: className,
  290. })}
  291. />
  292. );
  293. });
  294. Popover.displayName = 'Popover';
  295. PopoverArrow.displayName = 'PopoverArrow';
  296. PopoverBackdrop.displayName = 'PopoverBackdrop';
  297. PopoverContent.displayName = 'PopoverContent';
  298. PopoverHeader.displayName = 'PopoverHeader';
  299. PopoverFooter.displayName = 'PopoverFooter';
  300. PopoverBody.displayName = 'PopoverBody';
  301. PopoverCloseButton.displayName = 'PopoverCloseButton';
  302. export {
  303. Popover,
  304. PopoverBackdrop,
  305. PopoverArrow,
  306. PopoverCloseButton,
  307. PopoverFooter,
  308. PopoverHeader,
  309. PopoverBody,
  310. PopoverContent,
  311. };