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

217 lines
5.3 KiB

  1. 'use client';
  2. import React from 'react';
  3. import { createMenu } from '@gluestack-ui/menu';
  4. import { tva } from '@gluestack-ui/nativewind-utils/tva';
  5. import { cssInterop } from 'nativewind';
  6. import { Pressable, Text, View, ViewStyle } from 'react-native';
  7. import {
  8. Motion,
  9. AnimatePresence,
  10. MotionComponentProps,
  11. } from '@legendapp/motion';
  12. import type { VariantProps } from '@gluestack-ui/nativewind-utils';
  13. type IMotionViewProps = React.ComponentProps<typeof View> &
  14. MotionComponentProps<typeof View, ViewStyle, unknown, unknown, unknown>;
  15. const MotionView = Motion.View as React.ComponentType<IMotionViewProps>;
  16. const menuStyle = tva({
  17. base: 'rounded-md bg-background-0 border border-outline-100 p-1 shadow-hard-5',
  18. });
  19. const menuItemStyle = tva({
  20. base: 'min-w-[200px] p-3 flex-row items-center rounded data-[hover=true]:bg-background-50 data-[active=true]:bg-background-100 data-[focus=true]:bg-background-50 data-[focus=true]:web:outline-none data-[focus=true]:web:outline-0 data-[disabled=true]:opacity-40 data-[disabled=true]:web:cursor-not-allowed data-[focus-visible=true]:web:outline-2 data-[focus-visible=true]:web:outline-primary-700 data-[focus-visible=true]:web:outline data-[focus-visible=true]:web:cursor-pointer data-[disabled=true]:data-[focus=true]:bg-transparent',
  21. });
  22. const menuBackdropStyle = tva({
  23. base: 'absolute top-0 bottom-0 left-0 right-0 web:cursor-default',
  24. // add this classnames if you want to give background color to backdrop
  25. // opacity-50 bg-background-500,
  26. });
  27. const menuSeparatorStyle = tva({
  28. base: 'bg-background-200 h-px w-full',
  29. });
  30. const menuItemLabelStyle = tva({
  31. base: 'text-typography-700 font-normal font-body',
  32. variants: {
  33. isTruncated: {
  34. true: 'web:truncate',
  35. },
  36. bold: {
  37. true: 'font-bold',
  38. },
  39. underline: {
  40. true: 'underline',
  41. },
  42. strikeThrough: {
  43. true: 'line-through',
  44. },
  45. size: {
  46. '2xs': 'text-2xs',
  47. 'xs': 'text-xs',
  48. 'sm': 'text-sm',
  49. 'md': 'text-base',
  50. 'lg': 'text-lg',
  51. 'xl': 'text-xl',
  52. '2xl': 'text-2xl',
  53. '3xl': 'text-3xl',
  54. '4xl': 'text-4xl',
  55. '5xl': 'text-5xl',
  56. '6xl': 'text-6xl',
  57. },
  58. sub: {
  59. true: 'text-xs',
  60. },
  61. italic: {
  62. true: 'italic',
  63. },
  64. highlight: {
  65. true: 'bg-yellow-500',
  66. },
  67. },
  68. });
  69. const BackdropPressable = React.forwardRef<
  70. React.ComponentRef<typeof Pressable>,
  71. React.ComponentPropsWithoutRef<typeof Pressable> &
  72. VariantProps<typeof menuBackdropStyle>
  73. >(function BackdropPressable({ className, ...props }, ref) {
  74. return (
  75. <Pressable
  76. ref={ref}
  77. className={menuBackdropStyle({
  78. class: className,
  79. })}
  80. {...props}
  81. />
  82. );
  83. });
  84. type IMenuItemProps = VariantProps<typeof menuItemStyle> & {
  85. className?: string;
  86. } & React.ComponentPropsWithoutRef<typeof Pressable>;
  87. const Item = React.forwardRef<
  88. React.ComponentRef<typeof Pressable>,
  89. IMenuItemProps
  90. >(function Item({ className, ...props }, ref) {
  91. return (
  92. <Pressable
  93. ref={ref}
  94. className={menuItemStyle({
  95. class: className,
  96. })}
  97. {...props}
  98. />
  99. );
  100. });
  101. const Separator = React.forwardRef<
  102. React.ComponentRef<typeof View>,
  103. React.ComponentPropsWithoutRef<typeof View> &
  104. VariantProps<typeof menuSeparatorStyle>
  105. >(function Separator({ className, ...props }, ref) {
  106. return (
  107. <View
  108. ref={ref}
  109. className={menuSeparatorStyle({ class: className })}
  110. {...props}
  111. />
  112. );
  113. });
  114. export const UIMenu = createMenu({
  115. Root: MotionView,
  116. Item: Item,
  117. Label: Text,
  118. Backdrop: BackdropPressable,
  119. AnimatePresence: AnimatePresence,
  120. Separator: Separator,
  121. });
  122. cssInterop(MotionView, { className: 'style' });
  123. type IMenuProps = React.ComponentProps<typeof UIMenu> &
  124. VariantProps<typeof menuStyle> & { className?: string };
  125. type IMenuItemLabelProps = React.ComponentProps<typeof UIMenu.ItemLabel> &
  126. VariantProps<typeof menuItemLabelStyle> & { className?: string };
  127. const Menu = React.forwardRef<React.ComponentRef<typeof UIMenu>, IMenuProps>(
  128. function Menu({ className, ...props }, ref) {
  129. return (
  130. <UIMenu
  131. ref={ref}
  132. initial={{
  133. opacity: 0,
  134. scale: 0.8,
  135. }}
  136. animate={{
  137. opacity: 1,
  138. scale: 1,
  139. }}
  140. exit={{
  141. opacity: 0,
  142. scale: 0.8,
  143. }}
  144. transition={{
  145. type: 'timing',
  146. duration: 100,
  147. }}
  148. className={menuStyle({
  149. class: className,
  150. })}
  151. {...props}
  152. />
  153. );
  154. }
  155. );
  156. const MenuItem = UIMenu.Item;
  157. const MenuItemLabel = React.forwardRef<
  158. React.ComponentRef<typeof UIMenu.ItemLabel>,
  159. IMenuItemLabelProps
  160. >(function MenuItemLabel(
  161. {
  162. className,
  163. isTruncated,
  164. bold,
  165. underline,
  166. strikeThrough,
  167. size = 'md',
  168. sub,
  169. italic,
  170. highlight,
  171. ...props
  172. },
  173. ref
  174. ) {
  175. return (
  176. <UIMenu.ItemLabel
  177. ref={ref}
  178. className={menuItemLabelStyle({
  179. isTruncated,
  180. bold,
  181. underline,
  182. strikeThrough,
  183. size,
  184. sub,
  185. italic,
  186. highlight,
  187. class: className,
  188. })}
  189. {...props}
  190. />
  191. );
  192. });
  193. const MenuSeparator = UIMenu.Separator;
  194. Menu.displayName = 'Menu';
  195. MenuItem.displayName = 'MenuItem';
  196. MenuItemLabel.displayName = 'MenuItemLabel';
  197. MenuSeparator.displayName = 'MenuSeparator';
  198. export { Menu, MenuItem, MenuItemLabel, MenuSeparator };