@coze-arch/cli 0.0.1-beta.6 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +1 -0
  2. package/lib/__templates__/expo/.coze +7 -2
  3. package/lib/__templates__/expo/.cozeproj/scripts/dev_build.sh +46 -0
  4. package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +229 -0
  5. package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +47 -0
  6. package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +34 -0
  7. package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +46 -0
  8. package/lib/__templates__/expo/README.md +68 -7
  9. package/lib/__templates__/expo/_gitignore +2 -1
  10. package/lib/__templates__/expo/_npmrc +4 -5
  11. package/lib/__templates__/expo/client/app/+not-found.tsx +15 -64
  12. package/lib/__templates__/expo/client/app/_layout.tsx +15 -12
  13. package/lib/__templates__/expo/client/app/index.tsx +1 -0
  14. package/lib/__templates__/expo/client/app.config.ts +76 -0
  15. package/lib/__templates__/expo/client/components/Screen.tsx +3 -19
  16. package/lib/__templates__/expo/client/components/ThemedText.tsx +33 -0
  17. package/lib/__templates__/expo/client/components/ThemedView.tsx +37 -0
  18. package/lib/__templates__/expo/client/constants/theme.ts +117 -58
  19. package/lib/__templates__/expo/client/contexts/AuthContext.tsx +14 -107
  20. package/lib/__templates__/expo/client/declarations.d.ts +5 -0
  21. package/lib/__templates__/expo/{eslint.config.mjs → client/eslint.config.mjs} +40 -10
  22. package/lib/__templates__/expo/client/hooks/useColorScheme.tsx +48 -0
  23. package/lib/__templates__/expo/client/hooks/useSafeRouter.ts +152 -0
  24. package/lib/__templates__/expo/client/hooks/useTheme.ts +26 -6
  25. package/lib/__templates__/expo/client/metro.config.js +124 -0
  26. package/lib/__templates__/expo/client/package.json +95 -0
  27. package/lib/__templates__/expo/client/screens/demo/index.tsx +25 -0
  28. package/lib/__templates__/expo/client/screens/demo/styles.ts +28 -0
  29. package/lib/__templates__/expo/client/scripts/install-missing-deps.js +11 -10
  30. package/lib/__templates__/expo/client/tsconfig.json +24 -0
  31. package/lib/__templates__/expo/client/utils/index.ts +23 -2
  32. package/lib/__templates__/expo/eslint-plugins/fontawesome6/index.js +9 -0
  33. package/lib/__templates__/expo/eslint-plugins/fontawesome6/names.js +1889 -0
  34. package/lib/__templates__/expo/eslint-plugins/fontawesome6/rule.js +174 -0
  35. package/lib/__templates__/expo/eslint-plugins/fontawesome6/v5-only-names.js +388 -0
  36. package/lib/__templates__/expo/eslint-plugins/forbid-emoji/index.js +9 -0
  37. package/lib/__templates__/expo/eslint-plugins/forbid-emoji/rule.js +112 -0
  38. package/lib/__templates__/expo/eslint-plugins/forbid-emoji/tech.md +94 -0
  39. package/lib/__templates__/expo/eslint-plugins/react-native/index.js +9 -0
  40. package/lib/__templates__/expo/eslint-plugins/react-native/rule.js +64 -0
  41. package/lib/__templates__/expo/eslint-plugins/reanimated/index.js +9 -0
  42. package/lib/__templates__/expo/eslint-plugins/reanimated/rule.js +88 -0
  43. package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/index.js +9 -0
  44. package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/rule.js +120 -0
  45. package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/tech.md +58 -0
  46. package/lib/__templates__/expo/package.json +16 -101
  47. package/lib/__templates__/expo/patches/expo@54.0.33.patch +45 -0
  48. package/lib/__templates__/expo/pnpm-lock.yaml +1622 -3274
  49. package/lib/__templates__/expo/pnpm-workspace.yaml +3 -0
  50. package/lib/__templates__/expo/server/build.js +21 -0
  51. package/lib/__templates__/expo/server/package.json +34 -0
  52. package/lib/__templates__/expo/server/src/index.ts +20 -0
  53. package/lib/__templates__/expo/server/tsconfig.json +24 -0
  54. package/lib/__templates__/expo/template.config.js +58 -1
  55. package/lib/__templates__/expo/tsconfig.json +1 -24
  56. package/lib/__templates__/native-static/.coze +11 -0
  57. package/lib/__templates__/native-static/index.html +33 -0
  58. package/lib/__templates__/native-static/styles/main.css +136 -0
  59. package/lib/__templates__/native-static/template.config.js +22 -0
  60. package/lib/__templates__/nextjs/.coze +4 -3
  61. package/lib/__templates__/nextjs/README.md +5 -0
  62. package/lib/__templates__/nextjs/_gitignore +1 -0
  63. package/lib/__templates__/nextjs/_npmrc +2 -1
  64. package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
  65. package/lib/__templates__/nextjs/next.config.ts +11 -0
  66. package/lib/__templates__/nextjs/package.json +15 -1
  67. package/lib/__templates__/nextjs/pnpm-lock.yaml +7694 -4394
  68. package/lib/__templates__/nextjs/scripts/build.sh +4 -1
  69. package/lib/__templates__/nextjs/scripts/dev.sh +15 -28
  70. package/lib/__templates__/nextjs/scripts/prepare.sh +9 -0
  71. package/lib/__templates__/nextjs/scripts/start.sh +7 -1
  72. package/lib/__templates__/nextjs/src/app/globals.css +109 -89
  73. package/lib/__templates__/nextjs/src/app/layout.tsx +20 -33
  74. package/lib/__templates__/nextjs/src/app/page.tsx +18 -49
  75. package/lib/__templates__/nextjs/src/components/ui/resizable.tsx +29 -22
  76. package/lib/__templates__/nextjs/src/components/ui/sidebar.tsx +228 -230
  77. package/lib/__templates__/nextjs/src/server.ts +35 -0
  78. package/lib/__templates__/nextjs/template.config.js +68 -3
  79. package/lib/__templates__/nextjs/tsconfig.json +1 -1
  80. package/lib/__templates__/nuxt-vue/.coze +12 -0
  81. package/lib/__templates__/nuxt-vue/README.md +73 -0
  82. package/lib/__templates__/nuxt-vue/_gitignore +25 -0
  83. package/lib/__templates__/nuxt-vue/_npmrc +23 -0
  84. package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
  85. package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
  86. package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
  87. package/lib/__templates__/nuxt-vue/nuxt.config.ts +126 -0
  88. package/lib/__templates__/nuxt-vue/package.json +35 -0
  89. package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
  90. package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
  91. package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
  92. package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
  93. package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
  94. package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
  95. package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
  96. package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
  97. package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
  98. package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
  99. package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
  100. package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
  101. package/lib/__templates__/nuxt-vue/template.config.js +87 -0
  102. package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
  103. package/lib/__templates__/taro/.coze +14 -0
  104. package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +19 -0
  105. package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +14 -0
  106. package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +2 -0
  107. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +151 -0
  108. package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +5 -0
  109. package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -0
  110. package/lib/__templates__/taro/README.md +763 -0
  111. package/lib/__templates__/taro/_gitignore +41 -0
  112. package/lib/__templates__/taro/_npmrc +18 -0
  113. package/lib/__templates__/taro/babel.config.js +12 -0
  114. package/lib/__templates__/taro/config/dev.ts +9 -0
  115. package/lib/__templates__/taro/config/index.ts +238 -0
  116. package/lib/__templates__/taro/config/prod.ts +34 -0
  117. package/lib/__templates__/taro/eslint.config.mjs +135 -0
  118. package/lib/__templates__/taro/key/private.appid.key +0 -0
  119. package/lib/__templates__/taro/package.json +112 -0
  120. package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
  121. package/lib/__templates__/taro/pnpm-lock.yaml +23412 -0
  122. package/lib/__templates__/taro/pnpm-workspace.yaml +2 -0
  123. package/lib/__templates__/taro/project.config.json +15 -0
  124. package/lib/__templates__/taro/server/nest-cli.json +10 -0
  125. package/lib/__templates__/taro/server/package.json +40 -0
  126. package/lib/__templates__/taro/server/src/app.controller.ts +23 -0
  127. package/lib/__templates__/taro/server/src/app.module.ts +10 -0
  128. package/lib/__templates__/taro/server/src/app.service.ts +8 -0
  129. package/lib/__templates__/taro/server/src/interceptors/http-status.interceptor.ts +23 -0
  130. package/lib/__templates__/taro/server/src/main.ts +49 -0
  131. package/lib/__templates__/taro/server/tsconfig.json +24 -0
  132. package/lib/__templates__/taro/src/app.config.ts +11 -0
  133. package/lib/__templates__/taro/src/app.css +156 -0
  134. package/lib/__templates__/taro/src/app.tsx +9 -0
  135. package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
  136. package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
  137. package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
  138. package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
  139. package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
  140. package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
  141. package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
  142. package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
  143. package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
  144. package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
  145. package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
  146. package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
  147. package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
  148. package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
  149. package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
  150. package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
  151. package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
  152. package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
  153. package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
  154. package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
  155. package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
  156. package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
  157. package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
  158. package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
  159. package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
  160. package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
  161. package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
  162. package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
  163. package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
  164. package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
  165. package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
  166. package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
  167. package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
  168. package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
  169. package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
  170. package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
  171. package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
  172. package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
  173. package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
  174. package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
  175. package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
  176. package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
  177. package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
  178. package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
  179. package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
  180. package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
  181. package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
  182. package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
  183. package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
  184. package/lib/__templates__/taro/src/index.html +39 -0
  185. package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
  186. package/lib/__templates__/taro/src/lib/measure.ts +115 -0
  187. package/lib/__templates__/taro/src/lib/platform.ts +12 -0
  188. package/lib/__templates__/taro/src/lib/utils.ts +6 -0
  189. package/lib/__templates__/taro/src/network.ts +39 -0
  190. package/lib/__templates__/taro/src/pages/index/index.config.ts +3 -0
  191. package/lib/__templates__/taro/src/pages/index/index.css +1 -0
  192. package/lib/__templates__/taro/src/pages/index/index.tsx +33 -0
  193. package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
  194. package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
  195. package/lib/__templates__/taro/src/presets/h5-navbar.tsx +238 -0
  196. package/lib/__templates__/taro/src/presets/h5-styles.ts +220 -0
  197. package/lib/__templates__/taro/src/presets/index.tsx +18 -0
  198. package/lib/__templates__/taro/stylelint.config.mjs +4 -0
  199. package/lib/__templates__/taro/template.config.js +68 -0
  200. package/lib/__templates__/taro/tsconfig.json +29 -0
  201. package/lib/__templates__/taro/types/global.d.ts +32 -0
  202. package/lib/__templates__/templates.json +173 -36
  203. package/lib/__templates__/vite/.coze +4 -3
  204. package/lib/__templates__/vite/README.md +383 -26
  205. package/lib/__templates__/vite/_gitignore +2 -0
  206. package/lib/__templates__/vite/_npmrc +2 -1
  207. package/lib/__templates__/vite/eslint.config.mjs +14 -0
  208. package/lib/__templates__/vite/package.json +23 -3
  209. package/lib/__templates__/vite/pnpm-lock.yaml +2509 -293
  210. package/lib/__templates__/vite/scripts/build.sh +4 -1
  211. package/lib/__templates__/vite/scripts/dev.sh +16 -28
  212. package/lib/__templates__/vite/scripts/prepare.sh +9 -0
  213. package/lib/__templates__/vite/scripts/start.sh +9 -3
  214. package/lib/__templates__/vite/server/routes/index.ts +31 -0
  215. package/lib/__templates__/vite/server/server.ts +65 -0
  216. package/lib/__templates__/vite/server/vite.ts +67 -0
  217. package/lib/__templates__/vite/src/main.ts +17 -48
  218. package/lib/__templates__/vite/template.config.js +77 -7
  219. package/lib/__templates__/vite/tsconfig.json +4 -3
  220. package/lib/__templates__/vite/vite.config.ts +8 -3
  221. package/lib/cli.js +1545 -526
  222. package/package.json +20 -7
  223. package/lib/__templates__/expo/.cozeproj/scripts/deploy_build.sh +0 -109
  224. package/lib/__templates__/expo/.cozeproj/scripts/deploy_run.sh +0 -257
  225. package/lib/__templates__/expo/app.json +0 -63
  226. package/lib/__templates__/expo/babel.config.js +0 -9
  227. package/lib/__templates__/expo/client/app/(tabs)/_layout.tsx +0 -43
  228. package/lib/__templates__/expo/client/app/(tabs)/home.tsx +0 -1
  229. package/lib/__templates__/expo/client/app/(tabs)/index.tsx +0 -7
  230. package/lib/__templates__/expo/client/hooks/useColorScheme.ts +0 -1
  231. package/lib/__templates__/expo/client/index.js +0 -12
  232. package/lib/__templates__/expo/client/screens/home/index.tsx +0 -54
  233. package/lib/__templates__/expo/client/screens/home/styles.ts +0 -332
  234. package/lib/__templates__/expo/metro.config.js +0 -53
  235. package/lib/__templates__/expo/src/index.ts +0 -12
  236. package/lib/__templates__/nextjs/.vscode/settings.json +0 -121
  237. package/lib/__templates__/nextjs/server.mjs +0 -50
  238. package/lib/__templates__/vite/.vscode/settings.json +0 -7
  239. /package/lib/__templates__/expo/{eslint-formatter-simple.mjs → client/eslint-formatter-simple.mjs} +0 -0
@@ -0,0 +1,561 @@
1
+ import * as React from "react"
2
+ import { ScrollView, View } from "@tarojs/components"
3
+ import { Check, ChevronRight } from "lucide-react-taro"
4
+ import { cn } from "@/lib/utils"
5
+ import { isH5 } from "@/lib/platform"
6
+ import { computePosition, getRectById } from "@/lib/measure"
7
+ import { Portal } from "@/components/ui/portal"
8
+
9
+ const DropdownMenuContext = React.createContext<{
10
+ open?: boolean
11
+ onOpenChange?: (open: boolean) => void
12
+ triggerId: string
13
+ } | null>(null)
14
+
15
+ interface DropdownMenuProps {
16
+ children: React.ReactNode
17
+ open?: boolean
18
+ defaultOpen?: boolean
19
+ onOpenChange?: (open: boolean) => void
20
+ }
21
+
22
+ const DropdownMenu = ({ open: openProp, defaultOpen, onOpenChange, children }: DropdownMenuProps) => {
23
+ const baseIdRef = React.useRef(`dropdown-menu-${Math.random().toString(36).slice(2, 10)}`)
24
+ const [openState, setOpenState] = React.useState(defaultOpen || false)
25
+ const open = openProp !== undefined ? openProp : openState
26
+
27
+ const handleOpenChange = (newOpen: boolean) => {
28
+ if (openProp === undefined) {
29
+ setOpenState(newOpen)
30
+ }
31
+ onOpenChange?.(newOpen)
32
+ }
33
+
34
+ return (
35
+ <DropdownMenuContext.Provider
36
+ value={{ open, onOpenChange: handleOpenChange, triggerId: baseIdRef.current }}
37
+ >
38
+ {children}
39
+ </DropdownMenuContext.Provider>
40
+ )
41
+ }
42
+
43
+ const DropdownMenuTrigger = React.forwardRef<
44
+ React.ElementRef<typeof View>,
45
+ React.ComponentPropsWithoutRef<typeof View>
46
+ >(({ className, children, onClick, ...props }, ref) => {
47
+ const context = React.useContext(DropdownMenuContext)
48
+ return (
49
+ <View
50
+ {...props}
51
+ ref={ref}
52
+ id={context?.triggerId}
53
+ data-slot="dropdown-menu-trigger"
54
+ className={className}
55
+ onClick={(e) => {
56
+ e.stopPropagation()
57
+ context?.onOpenChange?.(!context.open)
58
+ onClick?.(e)
59
+ }}
60
+ >
61
+ {children}
62
+ </View>
63
+ )
64
+ })
65
+ DropdownMenuTrigger.displayName = "DropdownMenuTrigger"
66
+
67
+ const DropdownMenuGroup = React.forwardRef<
68
+ React.ElementRef<typeof View>,
69
+ React.ComponentPropsWithoutRef<typeof View>
70
+ >(({ className, ...props }, ref) => (
71
+ <View ref={ref} data-slot="dropdown-menu-group" className={className} {...props} />
72
+ ))
73
+ DropdownMenuGroup.displayName = "DropdownMenuGroup"
74
+
75
+ const DropdownMenuPortal = ({ children }: { children: React.ReactNode }) => {
76
+ return <>{children}</>
77
+ }
78
+
79
+ const DropdownMenuRadioGroupContext = React.createContext<{
80
+ value?: string
81
+ onValueChange?: (value: string) => void
82
+ } | null>(null)
83
+
84
+ interface DropdownMenuRadioGroupProps extends React.ComponentPropsWithoutRef<typeof View> {
85
+ value?: string
86
+ defaultValue?: string
87
+ onValueChange?: (value: string) => void
88
+ }
89
+
90
+ const DropdownMenuRadioGroup = React.forwardRef<
91
+ React.ElementRef<typeof View>,
92
+ DropdownMenuRadioGroupProps
93
+ >(({ value: valueProp, defaultValue, onValueChange, ...props }, ref) => {
94
+ const [valueState, setValueState] = React.useState<string | undefined>(defaultValue)
95
+ const value = valueProp !== undefined ? valueProp : valueState
96
+
97
+ const handleValueChange = (next: string) => {
98
+ if (valueProp === undefined) {
99
+ setValueState(next)
100
+ }
101
+ onValueChange?.(next)
102
+ }
103
+
104
+ return (
105
+ <DropdownMenuRadioGroupContext.Provider value={{ value, onValueChange: handleValueChange }}>
106
+ <View ref={ref} {...props} />
107
+ </DropdownMenuRadioGroupContext.Provider>
108
+ )
109
+ })
110
+ DropdownMenuRadioGroup.displayName = "DropdownMenuRadioGroup"
111
+
112
+ const DropdownMenuSubContext = React.createContext<{
113
+ open?: boolean
114
+ onOpenChange?: (open: boolean) => void
115
+ triggerId: string
116
+ } | null>(null)
117
+
118
+ interface DropdownMenuSubProps {
119
+ children: React.ReactNode
120
+ open?: boolean
121
+ defaultOpen?: boolean
122
+ onOpenChange?: (open: boolean) => void
123
+ }
124
+
125
+ const DropdownMenuSub = ({ open: openProp, defaultOpen, onOpenChange, children }: DropdownMenuSubProps) => {
126
+ const parent = React.useContext(DropdownMenuContext)
127
+ const baseIdRef = React.useRef(`dropdown-menu-sub-${Math.random().toString(36).slice(2, 10)}`)
128
+ const [openState, setOpenState] = React.useState(defaultOpen || false)
129
+ const open = openProp !== undefined ? openProp : openState
130
+
131
+ const handleOpenChange = (newOpen: boolean) => {
132
+ if (openProp === undefined) {
133
+ setOpenState(newOpen)
134
+ }
135
+ onOpenChange?.(newOpen)
136
+ }
137
+
138
+ React.useEffect(() => {
139
+ if (parent?.open === false && open) {
140
+ handleOpenChange(false)
141
+ }
142
+ }, [open, parent?.open])
143
+
144
+ return (
145
+ <DropdownMenuSubContext.Provider
146
+ value={{ open, onOpenChange: handleOpenChange, triggerId: baseIdRef.current }}
147
+ >
148
+ {children}
149
+ </DropdownMenuSubContext.Provider>
150
+ )
151
+ }
152
+
153
+ interface DropdownMenuContentProps extends React.ComponentPropsWithoutRef<typeof View> {
154
+ align?: "start" | "center" | "end"
155
+ side?: "top" | "bottom" | "left" | "right"
156
+ sideOffset?: number
157
+ }
158
+
159
+ const DropdownMenuContent = React.forwardRef<
160
+ React.ElementRef<typeof View>,
161
+ DropdownMenuContentProps
162
+ >(({ className, align = "start", side = "bottom", sideOffset = 4, children, ...props }, ref) => {
163
+ const context = React.useContext(DropdownMenuContext)
164
+ const contentId = React.useRef(`dropdown-menu-content-${Math.random().toString(36).slice(2, 10)}`)
165
+ const [position, setPosition] = React.useState<{ left: number; top: number } | null>(null)
166
+
167
+ React.useEffect(() => {
168
+ if (!context?.open) {
169
+ setPosition(null)
170
+ return
171
+ }
172
+
173
+ let cancelled = false
174
+
175
+ const compute = async () => {
176
+ if (!context?.triggerId) return
177
+ const [triggerRect, contentRect] = await Promise.all([
178
+ getRectById(context.triggerId),
179
+ getRectById(contentId.current),
180
+ ])
181
+
182
+ if (cancelled) return
183
+ if (!triggerRect?.width || !contentRect?.width) return
184
+
185
+ setPosition(
186
+ computePosition({
187
+ triggerRect,
188
+ contentRect,
189
+ align,
190
+ side,
191
+ sideOffset,
192
+ })
193
+ )
194
+ }
195
+
196
+ const raf = (() => {
197
+ if (typeof requestAnimationFrame !== "undefined") {
198
+ return requestAnimationFrame(() => compute())
199
+ }
200
+ return setTimeout(() => compute(), 0) as unknown as number
201
+ })()
202
+
203
+ return () => {
204
+ cancelled = true
205
+ if (typeof cancelAnimationFrame !== "undefined") {
206
+ cancelAnimationFrame(raf)
207
+ } else {
208
+ clearTimeout(raf)
209
+ }
210
+ }
211
+ }, [align, context?.open, context?.triggerId, side, sideOffset])
212
+
213
+ React.useEffect(() => {
214
+ if (!context?.open) return
215
+ if (!isH5() || typeof document === "undefined") return
216
+ const onKeyDown = (e: KeyboardEvent) => {
217
+ if (e.key === "Escape") {
218
+ context?.onOpenChange?.(false)
219
+ }
220
+ }
221
+ document.addEventListener("keydown", onKeyDown)
222
+ return () => document.removeEventListener("keydown", onKeyDown)
223
+ }, [context?.open])
224
+
225
+ if (!context?.open) return null
226
+
227
+ const baseClassName =
228
+ "fixed z-50 min-w-32 overflow-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground ring-opacity-10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
229
+
230
+ const contentStyle = position
231
+ ? ({ left: position.left, top: position.top } as React.CSSProperties)
232
+ : ({
233
+ left: 0,
234
+ top: 0,
235
+ opacity: 0,
236
+ pointerEvents: "none",
237
+ } as React.CSSProperties)
238
+
239
+ return (
240
+ <Portal>
241
+ <View className="fixed inset-0 z-50 bg-transparent" onClick={() => context.onOpenChange?.(false)} />
242
+ <View
243
+ {...props}
244
+ ref={ref}
245
+ id={contentId.current}
246
+ data-slot="dropdown-menu-content"
247
+ data-state="open"
248
+ data-side={side}
249
+ className={cn(baseClassName, className)}
250
+ style={contentStyle}
251
+ onClick={(e) => e.stopPropagation()}
252
+ >
253
+ <ScrollView scrollY className="max-h-[50vh] overflow-x-hidden overflow-y-auto">
254
+ {children}
255
+ </ScrollView>
256
+ </View>
257
+ </Portal>
258
+ )
259
+ })
260
+ DropdownMenuContent.displayName = "DropdownMenuContent"
261
+
262
+ const DropdownMenuItem = React.forwardRef<
263
+ React.ElementRef<typeof View>,
264
+ React.ComponentPropsWithoutRef<typeof View> & {
265
+ inset?: boolean
266
+ variant?: "default" | "destructive"
267
+ disabled?: boolean
268
+ closeOnSelect?: boolean
269
+ }
270
+ >(({ className, inset, variant = "default", disabled, closeOnSelect = true, onClick, ...props }, ref) => {
271
+ const context = React.useContext(DropdownMenuContext)
272
+ return (
273
+ <View
274
+ {...props}
275
+ ref={ref}
276
+ data-slot="dropdown-menu-item"
277
+ data-inset={inset ? "" : undefined}
278
+ data-variant={variant}
279
+ data-disabled={disabled ? "" : undefined}
280
+ className={cn(
281
+ "relative flex cursor-default select-none items-center gap-1.5 rounded-md px-2 py-1 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
282
+ "data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive data-[variant=destructive]:focus:bg-opacity-10 data-[variant=destructive]:focus:text-destructive",
283
+ inset && "pl-7",
284
+ disabled && "pointer-events-none opacity-50",
285
+ className
286
+ )}
287
+ onClick={(e) => {
288
+ if (disabled) return
289
+ onClick?.(e)
290
+ if (closeOnSelect) context?.onOpenChange?.(false)
291
+ }}
292
+ />
293
+ )
294
+ })
295
+ DropdownMenuItem.displayName = "DropdownMenuItem"
296
+
297
+ const DropdownMenuCheckboxItem = React.forwardRef<
298
+ React.ElementRef<typeof View>,
299
+ React.ComponentPropsWithoutRef<typeof View> & {
300
+ checked?: boolean
301
+ inset?: boolean
302
+ disabled?: boolean
303
+ closeOnSelect?: boolean
304
+ }
305
+ >(({ className, children, checked, inset, disabled, closeOnSelect = false, onClick, ...props }, ref) => {
306
+ const context = React.useContext(DropdownMenuContext)
307
+ return (
308
+ <View
309
+ {...props}
310
+ ref={ref}
311
+ data-slot="dropdown-menu-checkbox-item"
312
+ data-inset={inset ? "" : undefined}
313
+ data-disabled={disabled ? "" : undefined}
314
+ data-state={checked ? "checked" : "unchecked"}
315
+ className={cn(
316
+ "relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-8 pl-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
317
+ inset && "pl-7",
318
+ className
319
+ )}
320
+ onClick={(e) => {
321
+ if (disabled) return
322
+ onClick?.(e)
323
+ if (closeOnSelect) context?.onOpenChange?.(false)
324
+ }}
325
+ >
326
+ <View className="pointer-events-none absolute right-2 flex items-center justify-center">
327
+ {checked && <Check size={16} />}
328
+ </View>
329
+ {children}
330
+ </View>
331
+ )
332
+ })
333
+ DropdownMenuCheckboxItem.displayName = "DropdownMenuCheckboxItem"
334
+
335
+ const DropdownMenuRadioItem = React.forwardRef<
336
+ React.ElementRef<typeof View>,
337
+ React.ComponentPropsWithoutRef<typeof View> & {
338
+ value: string
339
+ checked?: boolean
340
+ inset?: boolean
341
+ disabled?: boolean
342
+ closeOnSelect?: boolean
343
+ }
344
+ >(({ className, children, value, checked: checkedProp, inset, disabled, closeOnSelect = false, onClick, ...props }, ref) => {
345
+ const context = React.useContext(DropdownMenuContext)
346
+ const group = React.useContext(DropdownMenuRadioGroupContext)
347
+ const checked = checkedProp !== undefined ? checkedProp : group?.value === value
348
+ return (
349
+ <View
350
+ {...props}
351
+ ref={ref}
352
+ data-slot="dropdown-menu-radio-item"
353
+ data-inset={inset ? "" : undefined}
354
+ data-disabled={disabled ? "" : undefined}
355
+ data-state={checked ? "checked" : "unchecked"}
356
+ className={cn(
357
+ "relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-8 pl-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
358
+ inset && "pl-7",
359
+ className
360
+ )}
361
+ onClick={(e) => {
362
+ if (disabled) return
363
+ group?.onValueChange?.(value)
364
+ onClick?.(e)
365
+ if (closeOnSelect) context?.onOpenChange?.(false)
366
+ }}
367
+ >
368
+ <View className="pointer-events-none absolute right-2 flex items-center justify-center">
369
+ {checked && <Check size={16} />}
370
+ </View>
371
+ {children}
372
+ </View>
373
+ )
374
+ })
375
+ DropdownMenuRadioItem.displayName = "DropdownMenuRadioItem"
376
+
377
+ const DropdownMenuLabel = React.forwardRef<
378
+ React.ElementRef<typeof View>,
379
+ React.ComponentPropsWithoutRef<typeof View> & {
380
+ inset?: boolean
381
+ }
382
+ >(({ className, inset, ...props }, ref) => (
383
+ <View
384
+ ref={ref}
385
+ data-slot="dropdown-menu-label"
386
+ data-inset={inset ? "" : undefined}
387
+ className={cn("px-2 py-1 text-xs font-medium text-muted-foreground", inset && "pl-7", className)}
388
+ {...props}
389
+ />
390
+ ))
391
+ DropdownMenuLabel.displayName = "DropdownMenuLabel"
392
+
393
+ const DropdownMenuSeparator = React.forwardRef<
394
+ React.ElementRef<typeof View>,
395
+ React.ComponentPropsWithoutRef<typeof View>
396
+ >(({ className, ...props }, ref) => (
397
+ <View
398
+ ref={ref}
399
+ data-slot="dropdown-menu-separator"
400
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
401
+ {...props}
402
+ />
403
+ ))
404
+ DropdownMenuSeparator.displayName = "DropdownMenuSeparator"
405
+
406
+ const DropdownMenuShortcut = ({ className, ...props }: React.ComponentPropsWithoutRef<typeof View>) => {
407
+ return (
408
+ <View
409
+ data-slot="dropdown-menu-shortcut"
410
+ className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
411
+ {...props}
412
+ />
413
+ )
414
+ }
415
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
416
+
417
+ const DropdownMenuSubTrigger = React.forwardRef<
418
+ React.ElementRef<typeof View>,
419
+ React.ComponentPropsWithoutRef<typeof View> & {
420
+ inset?: boolean
421
+ disabled?: boolean
422
+ }
423
+ >(({ className, inset, disabled, children, onClick, ...props }, ref) => {
424
+ const subContext = React.useContext(DropdownMenuSubContext)
425
+ return (
426
+ <View
427
+ {...props}
428
+ ref={ref}
429
+ id={subContext?.triggerId}
430
+ data-slot="dropdown-menu-sub-trigger"
431
+ data-inset={inset ? "" : undefined}
432
+ data-disabled={disabled ? "" : undefined}
433
+ data-state={subContext?.open ? "open" : "closed"}
434
+ className={cn(
435
+ "relative flex cursor-default select-none items-center gap-1.5 rounded-md px-2 py-1 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
436
+ inset && "pl-7",
437
+ disabled && "pointer-events-none opacity-50",
438
+ className
439
+ )}
440
+ onClick={(e) => {
441
+ e.stopPropagation()
442
+ if (disabled) return
443
+ subContext?.onOpenChange?.(!subContext.open)
444
+ onClick?.(e)
445
+ }}
446
+ >
447
+ {children}
448
+ <ChevronRight className="ml-auto opacity-50" size={16} />
449
+ </View>
450
+ )
451
+ })
452
+ DropdownMenuSubTrigger.displayName = "DropdownMenuSubTrigger"
453
+
454
+ const DropdownMenuSubContent = React.forwardRef<
455
+ React.ElementRef<typeof View>,
456
+ DropdownMenuContentProps
457
+ >(({ className, align = "start", side = "right", sideOffset = 4, children, ...props }, ref) => {
458
+ const parent = React.useContext(DropdownMenuContext)
459
+ const subContext = React.useContext(DropdownMenuSubContext)
460
+ const contentId = React.useRef(`dropdown-menu-sub-content-${Math.random().toString(36).slice(2, 10)}`)
461
+ const [position, setPosition] = React.useState<{ left: number; top: number } | null>(null)
462
+
463
+ React.useEffect(() => {
464
+ if (!parent?.open || !subContext?.open) {
465
+ setPosition(null)
466
+ return
467
+ }
468
+
469
+ let cancelled = false
470
+
471
+ const compute = async () => {
472
+ if (!subContext?.triggerId) return
473
+ const [triggerRect, contentRect] = await Promise.all([
474
+ getRectById(subContext.triggerId),
475
+ getRectById(contentId.current),
476
+ ])
477
+
478
+ if (cancelled) return
479
+ if (!triggerRect?.width || !contentRect?.width) return
480
+
481
+ setPosition(
482
+ computePosition({
483
+ triggerRect,
484
+ contentRect,
485
+ align,
486
+ side,
487
+ sideOffset,
488
+ })
489
+ )
490
+ }
491
+
492
+ const raf = (() => {
493
+ if (typeof requestAnimationFrame !== "undefined") {
494
+ return requestAnimationFrame(() => compute())
495
+ }
496
+ return setTimeout(() => compute(), 0) as unknown as number
497
+ })()
498
+
499
+ return () => {
500
+ cancelled = true
501
+ if (typeof cancelAnimationFrame !== "undefined") {
502
+ cancelAnimationFrame(raf)
503
+ } else {
504
+ clearTimeout(raf)
505
+ }
506
+ }
507
+ }, [align, parent?.open, side, sideOffset, subContext?.open, subContext?.triggerId])
508
+
509
+ if (!parent?.open || !subContext?.open) return null
510
+
511
+ const baseClassName =
512
+ "fixed z-50 min-w-[96px] overflow-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground ring-opacity-10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
513
+
514
+ const contentStyle = position
515
+ ? ({ left: position.left, top: position.top } as React.CSSProperties)
516
+ : ({
517
+ left: 0,
518
+ top: 0,
519
+ opacity: 0,
520
+ pointerEvents: "none",
521
+ } as React.CSSProperties)
522
+
523
+ return (
524
+ <Portal>
525
+ <View
526
+ {...props}
527
+ ref={ref}
528
+ id={contentId.current}
529
+ data-slot="dropdown-menu-sub-content"
530
+ data-state="open"
531
+ data-side={side}
532
+ className={cn(baseClassName, className)}
533
+ style={contentStyle}
534
+ onClick={(e) => e.stopPropagation()}
535
+ >
536
+ <ScrollView scrollY className="max-h-[50vh] overflow-x-hidden overflow-y-auto">
537
+ {children}
538
+ </ScrollView>
539
+ </View>
540
+ </Portal>
541
+ )
542
+ })
543
+ DropdownMenuSubContent.displayName = "DropdownMenuSubContent"
544
+
545
+ export {
546
+ DropdownMenu,
547
+ DropdownMenuTrigger,
548
+ DropdownMenuContent,
549
+ DropdownMenuItem,
550
+ DropdownMenuCheckboxItem,
551
+ DropdownMenuRadioItem,
552
+ DropdownMenuLabel,
553
+ DropdownMenuSeparator,
554
+ DropdownMenuShortcut,
555
+ DropdownMenuGroup,
556
+ DropdownMenuPortal,
557
+ DropdownMenuSub,
558
+ DropdownMenuSubContent,
559
+ DropdownMenuSubTrigger,
560
+ DropdownMenuRadioGroup,
561
+ }