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

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 (238) 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 +1 -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/_npmrc +2 -1
  63. package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
  64. package/lib/__templates__/nextjs/next.config.ts +11 -0
  65. package/lib/__templates__/nextjs/package.json +15 -1
  66. package/lib/__templates__/nextjs/pnpm-lock.yaml +7694 -4394
  67. package/lib/__templates__/nextjs/scripts/build.sh +4 -1
  68. package/lib/__templates__/nextjs/scripts/dev.sh +15 -28
  69. package/lib/__templates__/nextjs/scripts/prepare.sh +9 -0
  70. package/lib/__templates__/nextjs/scripts/start.sh +7 -1
  71. package/lib/__templates__/nextjs/src/app/globals.css +109 -89
  72. package/lib/__templates__/nextjs/src/app/layout.tsx +20 -33
  73. package/lib/__templates__/nextjs/src/app/page.tsx +18 -49
  74. package/lib/__templates__/nextjs/src/components/ui/resizable.tsx +29 -22
  75. package/lib/__templates__/nextjs/src/components/ui/sidebar.tsx +228 -230
  76. package/lib/__templates__/nextjs/src/server.ts +35 -0
  77. package/lib/__templates__/nextjs/template.config.js +68 -3
  78. package/lib/__templates__/nextjs/tsconfig.json +1 -1
  79. package/lib/__templates__/nuxt-vue/.coze +12 -0
  80. package/lib/__templates__/nuxt-vue/README.md +73 -0
  81. package/lib/__templates__/nuxt-vue/_gitignore +24 -0
  82. package/lib/__templates__/nuxt-vue/_npmrc +23 -0
  83. package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
  84. package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
  85. package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
  86. package/lib/__templates__/nuxt-vue/nuxt.config.ts +116 -0
  87. package/lib/__templates__/nuxt-vue/package.json +35 -0
  88. package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
  89. package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
  90. package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
  91. package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
  92. package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
  93. package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
  94. package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
  95. package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
  96. package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
  97. package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
  98. package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
  99. package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
  100. package/lib/__templates__/nuxt-vue/template.config.js +87 -0
  101. package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
  102. package/lib/__templates__/taro/.coze +14 -0
  103. package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +19 -0
  104. package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +14 -0
  105. package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +2 -0
  106. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +151 -0
  107. package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +5 -0
  108. package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -0
  109. package/lib/__templates__/taro/README.md +763 -0
  110. package/lib/__templates__/taro/_gitignore +40 -0
  111. package/lib/__templates__/taro/_npmrc +18 -0
  112. package/lib/__templates__/taro/babel.config.js +12 -0
  113. package/lib/__templates__/taro/config/dev.ts +9 -0
  114. package/lib/__templates__/taro/config/index.ts +238 -0
  115. package/lib/__templates__/taro/config/prod.ts +34 -0
  116. package/lib/__templates__/taro/eslint.config.mjs +135 -0
  117. package/lib/__templates__/taro/key/private.appid.key +0 -0
  118. package/lib/__templates__/taro/package.json +112 -0
  119. package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
  120. package/lib/__templates__/taro/pnpm-lock.yaml +23412 -0
  121. package/lib/__templates__/taro/pnpm-workspace.yaml +2 -0
  122. package/lib/__templates__/taro/project.config.json +15 -0
  123. package/lib/__templates__/taro/server/nest-cli.json +10 -0
  124. package/lib/__templates__/taro/server/package.json +40 -0
  125. package/lib/__templates__/taro/server/src/app.controller.ts +23 -0
  126. package/lib/__templates__/taro/server/src/app.module.ts +10 -0
  127. package/lib/__templates__/taro/server/src/app.service.ts +8 -0
  128. package/lib/__templates__/taro/server/src/interceptors/http-status.interceptor.ts +23 -0
  129. package/lib/__templates__/taro/server/src/main.ts +49 -0
  130. package/lib/__templates__/taro/server/tsconfig.json +24 -0
  131. package/lib/__templates__/taro/src/app.config.ts +11 -0
  132. package/lib/__templates__/taro/src/app.css +156 -0
  133. package/lib/__templates__/taro/src/app.tsx +9 -0
  134. package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
  135. package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
  136. package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
  137. package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
  138. package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
  139. package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
  140. package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
  141. package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
  142. package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
  143. package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
  144. package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
  145. package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
  146. package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
  147. package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
  148. package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
  149. package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
  150. package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
  151. package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
  152. package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
  153. package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
  154. package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
  155. package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
  156. package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
  157. package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
  158. package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
  159. package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
  160. package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
  161. package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
  162. package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
  163. package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
  164. package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
  165. package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
  166. package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
  167. package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
  168. package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
  169. package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
  170. package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
  171. package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
  172. package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
  173. package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
  174. package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
  175. package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
  176. package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
  177. package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
  178. package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
  179. package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
  180. package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
  181. package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
  182. package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
  183. package/lib/__templates__/taro/src/index.html +39 -0
  184. package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
  185. package/lib/__templates__/taro/src/lib/measure.ts +115 -0
  186. package/lib/__templates__/taro/src/lib/platform.ts +12 -0
  187. package/lib/__templates__/taro/src/lib/utils.ts +6 -0
  188. package/lib/__templates__/taro/src/network.ts +39 -0
  189. package/lib/__templates__/taro/src/pages/index/index.config.ts +3 -0
  190. package/lib/__templates__/taro/src/pages/index/index.css +1 -0
  191. package/lib/__templates__/taro/src/pages/index/index.tsx +33 -0
  192. package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
  193. package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
  194. package/lib/__templates__/taro/src/presets/h5-navbar.tsx +238 -0
  195. package/lib/__templates__/taro/src/presets/h5-styles.ts +220 -0
  196. package/lib/__templates__/taro/src/presets/index.tsx +18 -0
  197. package/lib/__templates__/taro/stylelint.config.mjs +4 -0
  198. package/lib/__templates__/taro/template.config.js +68 -0
  199. package/lib/__templates__/taro/tsconfig.json +29 -0
  200. package/lib/__templates__/taro/types/global.d.ts +32 -0
  201. package/lib/__templates__/templates.json +136 -36
  202. package/lib/__templates__/vite/.coze +4 -3
  203. package/lib/__templates__/vite/README.md +383 -26
  204. package/lib/__templates__/vite/_gitignore +1 -0
  205. package/lib/__templates__/vite/_npmrc +2 -1
  206. package/lib/__templates__/vite/eslint.config.mjs +14 -0
  207. package/lib/__templates__/vite/package.json +23 -3
  208. package/lib/__templates__/vite/pnpm-lock.yaml +2509 -293
  209. package/lib/__templates__/vite/scripts/build.sh +4 -1
  210. package/lib/__templates__/vite/scripts/dev.sh +16 -28
  211. package/lib/__templates__/vite/scripts/prepare.sh +9 -0
  212. package/lib/__templates__/vite/scripts/start.sh +9 -3
  213. package/lib/__templates__/vite/server/routes/index.ts +31 -0
  214. package/lib/__templates__/vite/server/server.ts +65 -0
  215. package/lib/__templates__/vite/server/vite.ts +67 -0
  216. package/lib/__templates__/vite/src/main.ts +17 -48
  217. package/lib/__templates__/vite/template.config.js +77 -7
  218. package/lib/__templates__/vite/tsconfig.json +4 -3
  219. package/lib/__templates__/vite/vite.config.ts +8 -3
  220. package/lib/cli.js +1545 -526
  221. package/package.json +17 -6
  222. package/lib/__templates__/expo/.cozeproj/scripts/deploy_build.sh +0 -109
  223. package/lib/__templates__/expo/.cozeproj/scripts/deploy_run.sh +0 -257
  224. package/lib/__templates__/expo/app.json +0 -63
  225. package/lib/__templates__/expo/babel.config.js +0 -9
  226. package/lib/__templates__/expo/client/app/(tabs)/_layout.tsx +0 -43
  227. package/lib/__templates__/expo/client/app/(tabs)/home.tsx +0 -1
  228. package/lib/__templates__/expo/client/app/(tabs)/index.tsx +0 -7
  229. package/lib/__templates__/expo/client/hooks/useColorScheme.ts +0 -1
  230. package/lib/__templates__/expo/client/index.js +0 -12
  231. package/lib/__templates__/expo/client/screens/home/index.tsx +0 -54
  232. package/lib/__templates__/expo/client/screens/home/styles.ts +0 -332
  233. package/lib/__templates__/expo/metro.config.js +0 -53
  234. package/lib/__templates__/expo/src/index.ts +0 -12
  235. package/lib/__templates__/nextjs/.vscode/settings.json +0 -121
  236. package/lib/__templates__/nextjs/server.mjs +0 -50
  237. package/lib/__templates__/vite/.vscode/settings.json +0 -7
  238. /package/lib/__templates__/expo/{eslint-formatter-simple.mjs → client/eslint-formatter-simple.mjs} +0 -0
@@ -0,0 +1,614 @@
1
+ import * as React from "react"
2
+ import { View, ScrollView } from "@tarojs/components"
3
+ import Taro from "@tarojs/taro"
4
+ import { Check, ChevronRight, Circle } from "lucide-react-taro"
5
+ import { cn } from "@/lib/utils"
6
+ import { isH5 } from "@/lib/platform"
7
+ import { computePosition, getRectById, getViewport } from "@/lib/measure"
8
+ import { Portal } from "@/components/ui/portal"
9
+
10
+ const ContextMenuContext = React.createContext<{
11
+ open?: boolean
12
+ onOpenChange?: (open: boolean) => void
13
+ position: { x: number; y: number }
14
+ setPosition: (pos: { x: number; y: number }) => void
15
+ activeSubId?: string | null
16
+ setActiveSubId: (id: string | null) => void
17
+ } | null>(null)
18
+
19
+ interface ContextMenuProps {
20
+ children: React.ReactNode
21
+ onOpenChange?: (open: boolean) => void
22
+ }
23
+
24
+ const ContextMenu = ({ children, onOpenChange }: ContextMenuProps) => {
25
+ const [open, setOpen] = React.useState(false)
26
+ const [position, setPosition] = React.useState({ x: 0, y: 0 })
27
+ const [activeSubId, setActiveSubId] = React.useState<string | null>(null)
28
+
29
+ const handleOpenChange = (newOpen: boolean) => {
30
+ setOpen(newOpen)
31
+ if (!newOpen) setActiveSubId(null)
32
+ onOpenChange?.(newOpen)
33
+ }
34
+
35
+ return (
36
+ <ContextMenuContext.Provider
37
+ value={{ open, onOpenChange: handleOpenChange, position, setPosition, activeSubId, setActiveSubId }}
38
+ >
39
+ {children}
40
+ </ContextMenuContext.Provider>
41
+ )
42
+ }
43
+
44
+ const ContextMenuTrigger = React.forwardRef<
45
+ any,
46
+ React.ComponentPropsWithoutRef<typeof View> & { disabled?: boolean }
47
+ >(({ className, children, disabled, ...props }, ref) => {
48
+ const context = React.useContext(ContextMenuContext)
49
+ const touchPos = React.useRef({ x: 0, y: 0 })
50
+
51
+ const handleTrigger = (x: number, y: number) => {
52
+ if (disabled) return
53
+ context?.setPosition({ x, y })
54
+ context?.onOpenChange?.(true)
55
+ }
56
+
57
+ if (isH5()) {
58
+ const { onLongPress: _onLongPress, onTouchStart: _onTouchStart, ...rest } = props as any
59
+ return (
60
+ <div
61
+ ref={ref}
62
+ className={className}
63
+ onContextMenu={(e) => {
64
+ e.preventDefault()
65
+ e.stopPropagation()
66
+ handleTrigger(e.clientX, e.clientY)
67
+ }}
68
+ {...rest}
69
+ >
70
+ {children}
71
+ </div>
72
+ )
73
+ }
74
+
75
+ return (
76
+ <View
77
+ ref={ref}
78
+ className={className}
79
+ onTouchStart={(e) => {
80
+ const touch = (e as unknown as { touches?: Array<{ pageX: number; pageY: number }> }).touches?.[0]
81
+ if (!touch) return
82
+ touchPos.current = { x: touch.pageX, y: touch.pageY }
83
+ }}
84
+ onLongPress={(e) => {
85
+ e.stopPropagation()
86
+ handleTrigger(touchPos.current.x, touchPos.current.y)
87
+ }}
88
+ {...props}
89
+ >
90
+ {children}
91
+ </View>
92
+ )
93
+ })
94
+ ContextMenuTrigger.displayName = "ContextMenuTrigger"
95
+
96
+ const ContextMenuGroup = React.forwardRef<
97
+ React.ElementRef<typeof View>,
98
+ React.ComponentPropsWithoutRef<typeof View>
99
+ >(({ className, ...props }, ref) => (
100
+ <View ref={ref} className={className} {...props} />
101
+ ))
102
+ ContextMenuGroup.displayName = "ContextMenuGroup"
103
+
104
+ const ContextMenuPortal = ({ children }: { children: React.ReactNode }) => {
105
+ return <>{children}</>
106
+ }
107
+
108
+ const ContextMenuContent = React.forwardRef<
109
+ React.ElementRef<typeof View>,
110
+ React.ComponentPropsWithoutRef<typeof View>
111
+ >(({ className, children, ...props }, ref) => {
112
+ const context = React.useContext(ContextMenuContext)
113
+ const contentId = React.useRef(`context-menu-${Math.random().toString(36).slice(2, 10)}`)
114
+ const [adjustedPos, setAdjustedPos] = React.useState<{ x: number; y: number } | null>(null)
115
+
116
+ React.useEffect(() => {
117
+ if (!context?.open) {
118
+ setAdjustedPos(null)
119
+ return
120
+ }
121
+
122
+ let cancelled = false
123
+
124
+ const compute = async () => {
125
+ const { width: vw, height: vh } = getViewport()
126
+ let { x, y } = context.position
127
+
128
+ if (isH5() && typeof document !== "undefined") {
129
+ const el = document.getElementById(contentId.current)
130
+ const rect = el?.getBoundingClientRect()
131
+ if (rect) {
132
+ if (x + rect.width > vw) x = vw - rect.width - 8
133
+ if (y + rect.height > vh) y = vh - rect.height - 8
134
+ }
135
+ if (!cancelled) setAdjustedPos({ x, y })
136
+ return
137
+ }
138
+
139
+ const query = Taro.createSelectorQuery()
140
+ query
141
+ .select(`#${contentId.current}`)
142
+ .boundingClientRect((res) => {
143
+ if (cancelled) return
144
+ const rect = Array.isArray(res) ? res[0] : res
145
+ if (rect?.width) {
146
+ if (x + rect.width > vw) x = vw - rect.width - 8
147
+ if (y + rect.height > vh) y = vh - rect.height - 8
148
+ }
149
+ setAdjustedPos({ x, y })
150
+ })
151
+ .exec()
152
+ }
153
+
154
+ const raf = (() => {
155
+ if (typeof requestAnimationFrame !== "undefined") {
156
+ return requestAnimationFrame(() => compute())
157
+ }
158
+ return setTimeout(() => compute(), 0) as unknown as number
159
+ })()
160
+
161
+ return () => {
162
+ cancelled = true
163
+ if (typeof cancelAnimationFrame !== "undefined") {
164
+ cancelAnimationFrame(raf)
165
+ } else {
166
+ clearTimeout(raf)
167
+ }
168
+ }
169
+ }, [context?.open, context?.position])
170
+
171
+ if (!context?.open) return null
172
+
173
+ const contentStyle: React.CSSProperties = adjustedPos
174
+ ? { left: adjustedPos.x, top: adjustedPos.y }
175
+ : { left: context.position.x, top: context.position.y }
176
+
177
+ return (
178
+ <Portal>
179
+ <View
180
+ className="fixed inset-0 z-50 bg-transparent"
181
+ onClick={() => context.onOpenChange?.(false)}
182
+ // @ts-ignore
183
+ onContextMenu={(e) => {
184
+ e.preventDefault()
185
+ context.onOpenChange?.(false)
186
+ }}
187
+ />
188
+ <View
189
+ ref={ref}
190
+ id={contentId.current}
191
+ data-slot="context-menu-content"
192
+ data-state="open"
193
+ className={cn(
194
+ "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",
195
+ className
196
+ )}
197
+ style={contentStyle}
198
+ onClick={(e) => e.stopPropagation()}
199
+ {...props}
200
+ >
201
+ <ScrollView scrollY className="max-h-[50vh] overflow-x-hidden overflow-y-auto">
202
+ {children}
203
+ </ScrollView>
204
+ </View>
205
+ </Portal>
206
+ )
207
+ })
208
+ ContextMenuContent.displayName = "ContextMenuContent"
209
+
210
+ const ContextMenuItem = React.forwardRef<
211
+ React.ElementRef<typeof View>,
212
+ React.ComponentPropsWithoutRef<typeof View> & {
213
+ inset?: boolean
214
+ disabled?: boolean
215
+ closeOnSelect?: boolean
216
+ }
217
+ >(({ className, inset, disabled, closeOnSelect = true, children, onClick, ...props }, ref) => {
218
+ const context = React.useContext(ContextMenuContext)
219
+ return (
220
+ <View
221
+ ref={ref}
222
+ data-slot="context-menu-item"
223
+ data-inset={inset ? "" : undefined}
224
+ data-disabled={disabled ? "" : undefined}
225
+ className={cn(
226
+ "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",
227
+ inset && "pl-7",
228
+ disabled && "pointer-events-none opacity-50",
229
+ className
230
+ )}
231
+ onClick={(e) => {
232
+ if (disabled) return
233
+ e.stopPropagation()
234
+ onClick?.(e)
235
+ if (closeOnSelect) context?.onOpenChange?.(false)
236
+ }}
237
+ {...props}
238
+ >
239
+ {children}
240
+ </View>
241
+ )
242
+ })
243
+ ContextMenuItem.displayName = "ContextMenuItem"
244
+
245
+ const ContextMenuRadioGroupContext = React.createContext<{
246
+ value?: string
247
+ onValueChange?: (value: string) => void
248
+ } | null>(null)
249
+
250
+ interface ContextMenuRadioGroupProps extends React.ComponentPropsWithoutRef<typeof View> {
251
+ value?: string
252
+ defaultValue?: string
253
+ onValueChange?: (value: string) => void
254
+ }
255
+
256
+ const ContextMenuRadioGroup = React.forwardRef<
257
+ React.ElementRef<typeof View>,
258
+ ContextMenuRadioGroupProps
259
+ >(({ value: valueProp, defaultValue, onValueChange, ...props }, ref) => {
260
+ const [valueState, setValueState] = React.useState<string | undefined>(defaultValue)
261
+ const value = valueProp !== undefined ? valueProp : valueState
262
+
263
+ const handleValueChange = (next: string) => {
264
+ if (valueProp === undefined) {
265
+ setValueState(next)
266
+ }
267
+ onValueChange?.(next)
268
+ }
269
+
270
+ return (
271
+ <ContextMenuRadioGroupContext.Provider value={{ value, onValueChange: handleValueChange }}>
272
+ <View ref={ref} {...props} />
273
+ </ContextMenuRadioGroupContext.Provider>
274
+ )
275
+ })
276
+ ContextMenuRadioGroup.displayName = "ContextMenuRadioGroup"
277
+
278
+ const ContextMenuCheckboxItem = React.forwardRef<
279
+ React.ElementRef<typeof View>,
280
+ React.ComponentPropsWithoutRef<typeof View> & {
281
+ checked?: boolean
282
+ inset?: boolean
283
+ disabled?: boolean
284
+ closeOnSelect?: boolean
285
+ }
286
+ >(({ className, children, checked, inset, disabled, closeOnSelect = false, onClick, ...props }, ref) => {
287
+ const context = React.useContext(ContextMenuContext)
288
+ return (
289
+ <View
290
+ ref={ref}
291
+ data-slot="context-menu-checkbox-item"
292
+ data-inset={inset ? "" : undefined}
293
+ data-disabled={disabled ? "" : undefined}
294
+ data-state={checked ? "checked" : "unchecked"}
295
+ className={cn(
296
+ "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",
297
+ inset && "pl-7",
298
+ disabled && "pointer-events-none opacity-50",
299
+ className
300
+ )}
301
+ onClick={(e) => {
302
+ if (disabled) return
303
+ e.stopPropagation()
304
+ onClick?.(e)
305
+ if (closeOnSelect) context?.onOpenChange?.(false)
306
+ }}
307
+ {...props}
308
+ >
309
+ <View className="pointer-events-none absolute right-2 flex items-center justify-center">
310
+ {checked && <Check size={16} />}
311
+ </View>
312
+ {children}
313
+ </View>
314
+ )
315
+ })
316
+ ContextMenuCheckboxItem.displayName = "ContextMenuCheckboxItem"
317
+
318
+ const ContextMenuRadioItem = React.forwardRef<
319
+ React.ElementRef<typeof View>,
320
+ React.ComponentPropsWithoutRef<typeof View> & {
321
+ value: string
322
+ checked?: boolean
323
+ inset?: boolean
324
+ disabled?: boolean
325
+ closeOnSelect?: boolean
326
+ }
327
+ >(({ className, children, value, checked: checkedProp, inset, disabled, closeOnSelect = false, onClick, ...props }, ref) => {
328
+ const context = React.useContext(ContextMenuContext)
329
+ const group = React.useContext(ContextMenuRadioGroupContext)
330
+ const checked = checkedProp !== undefined ? checkedProp : group?.value === value
331
+ return (
332
+ <View
333
+ ref={ref}
334
+ data-slot="context-menu-radio-item"
335
+ data-inset={inset ? "" : undefined}
336
+ data-disabled={disabled ? "" : undefined}
337
+ data-state={checked ? "checked" : "unchecked"}
338
+ className={cn(
339
+ "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",
340
+ inset && "pl-7",
341
+ disabled && "pointer-events-none opacity-50",
342
+ className
343
+ )}
344
+ onClick={(e) => {
345
+ if (disabled) return
346
+ e.stopPropagation()
347
+ group?.onValueChange?.(value)
348
+ onClick?.(e)
349
+ if (closeOnSelect) context?.onOpenChange?.(false)
350
+ }}
351
+ {...props}
352
+ >
353
+ <View className="pointer-events-none absolute right-2 flex items-center justify-center">
354
+ {checked && <Circle className="fill-current" size={8} />}
355
+ </View>
356
+ {children}
357
+ </View>
358
+ )
359
+ })
360
+ ContextMenuRadioItem.displayName = "ContextMenuRadioItem"
361
+
362
+ const ContextMenuLabel = React.forwardRef<
363
+ React.ElementRef<typeof View>,
364
+ React.ComponentPropsWithoutRef<typeof View> & {
365
+ inset?: boolean
366
+ }
367
+ >(({ className, inset, ...props }, ref) => (
368
+ <View
369
+ ref={ref}
370
+ className={cn(
371
+ "px-2 py-1 text-xs font-medium text-muted-foreground",
372
+ inset && "pl-7",
373
+ className
374
+ )}
375
+ {...props}
376
+ />
377
+ ))
378
+ ContextMenuLabel.displayName = "ContextMenuLabel"
379
+
380
+ const ContextMenuSeparator = React.forwardRef<
381
+ React.ElementRef<typeof View>,
382
+ React.ComponentPropsWithoutRef<typeof View>
383
+ >(({ className, ...props }, ref) => (
384
+ <View
385
+ ref={ref}
386
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
387
+ {...props}
388
+ />
389
+ ))
390
+ ContextMenuSeparator.displayName = "ContextMenuSeparator"
391
+
392
+ const ContextMenuShortcut = ({
393
+ className,
394
+ ...props
395
+ }: React.ComponentPropsWithoutRef<typeof View>) => {
396
+ return (
397
+ <View
398
+ className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
399
+ {...props}
400
+ />
401
+ )
402
+ }
403
+ ContextMenuShortcut.displayName = "ContextMenuShortcut"
404
+
405
+ const ContextMenuSubContext = React.createContext<{
406
+ open?: boolean
407
+ onOpenChange?: (open: boolean) => void
408
+ triggerId: string
409
+ } | null>(null)
410
+
411
+ interface ContextMenuSubProps {
412
+ children: React.ReactNode
413
+ open?: boolean
414
+ defaultOpen?: boolean
415
+ onOpenChange?: (open: boolean) => void
416
+ }
417
+
418
+ const ContextMenuSub = ({ open: openProp, defaultOpen, onOpenChange, children }: ContextMenuSubProps) => {
419
+ const parent = React.useContext(ContextMenuContext)
420
+ const baseIdRef = React.useRef(`context-menu-sub-${Math.random().toString(36).slice(2, 10)}`)
421
+ const [openState, setOpenState] = React.useState(defaultOpen || false)
422
+ const isActive = parent?.activeSubId === baseIdRef.current
423
+ const open = openProp !== undefined ? openProp : openState && isActive
424
+
425
+ const handleOpenChange = React.useCallback(
426
+ (nextOpen: boolean) => {
427
+ if (openProp === undefined) {
428
+ setOpenState(nextOpen)
429
+ if (nextOpen) {
430
+ parent?.setActiveSubId(baseIdRef.current)
431
+ } else if (parent?.activeSubId === baseIdRef.current) {
432
+ parent?.setActiveSubId(null)
433
+ }
434
+ } else {
435
+ if (nextOpen) {
436
+ parent?.setActiveSubId(baseIdRef.current)
437
+ } else if (parent?.activeSubId === baseIdRef.current) {
438
+ parent?.setActiveSubId(null)
439
+ }
440
+ }
441
+ onOpenChange?.(nextOpen)
442
+ },
443
+ [onOpenChange, openProp, parent]
444
+ )
445
+
446
+ React.useEffect(() => {
447
+ if (defaultOpen) {
448
+ setOpenState(true)
449
+ parent?.setActiveSubId(baseIdRef.current)
450
+ }
451
+ }, [])
452
+
453
+ React.useEffect(() => {
454
+ if (parent?.open === false && open) {
455
+ handleOpenChange(false)
456
+ }
457
+ }, [handleOpenChange, open, parent?.open])
458
+
459
+ return (
460
+ <ContextMenuSubContext.Provider value={{ open, onOpenChange: handleOpenChange, triggerId: baseIdRef.current }}>
461
+ {children}
462
+ </ContextMenuSubContext.Provider>
463
+ )
464
+ }
465
+
466
+ const ContextMenuSubTrigger = React.forwardRef<
467
+ React.ElementRef<typeof View>,
468
+ React.ComponentPropsWithoutRef<typeof View> & {
469
+ inset?: boolean
470
+ disabled?: boolean
471
+ }
472
+ >(({ className, inset, disabled, children, onClick, ...props }, ref) => {
473
+ const subContext = React.useContext(ContextMenuSubContext)
474
+ return (
475
+ <View
476
+ {...props}
477
+ ref={ref}
478
+ id={subContext?.triggerId}
479
+ data-slot="context-menu-sub-trigger"
480
+ data-inset={inset ? "" : undefined}
481
+ data-disabled={disabled ? "" : undefined}
482
+ data-state={subContext?.open ? "open" : "closed"}
483
+ className={cn(
484
+ "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",
485
+ inset && "pl-7",
486
+ disabled && "pointer-events-none opacity-50",
487
+ className
488
+ )}
489
+ onClick={(e) => {
490
+ e.stopPropagation()
491
+ if (disabled) return
492
+ subContext?.onOpenChange?.(!subContext.open)
493
+ onClick?.(e)
494
+ }}
495
+ >
496
+ {children}
497
+ <ChevronRight className="ml-auto opacity-50" size={16} />
498
+ </View>
499
+ )
500
+ })
501
+
502
+ interface ContextMenuSubContentProps extends React.ComponentPropsWithoutRef<typeof View> {
503
+ align?: "start" | "center" | "end"
504
+ side?: "top" | "bottom" | "left" | "right"
505
+ sideOffset?: number
506
+ }
507
+
508
+ const ContextMenuSubContent = React.forwardRef<
509
+ React.ElementRef<typeof View>,
510
+ ContextMenuSubContentProps
511
+ >(({ className, align = "start", side = "right", sideOffset = 4, children, ...props }, ref) => {
512
+ const parent = React.useContext(ContextMenuContext)
513
+ const subContext = React.useContext(ContextMenuSubContext)
514
+ const contentId = React.useRef(`context-menu-sub-content-${Math.random().toString(36).slice(2, 10)}`)
515
+ const [position, setPosition] = React.useState<{ left: number; top: number } | null>(null)
516
+
517
+ React.useEffect(() => {
518
+ if (!parent?.open || !subContext?.open) {
519
+ setPosition(null)
520
+ return
521
+ }
522
+
523
+ let cancelled = false
524
+
525
+ const compute = async () => {
526
+ if (!subContext?.triggerId) return
527
+ const [triggerRect, contentRect] = await Promise.all([
528
+ getRectById(subContext.triggerId),
529
+ getRectById(contentId.current),
530
+ ])
531
+
532
+ if (cancelled) return
533
+ if (!triggerRect?.width || !contentRect?.width) return
534
+
535
+ setPosition(
536
+ computePosition({
537
+ triggerRect,
538
+ contentRect,
539
+ align,
540
+ side,
541
+ sideOffset,
542
+ })
543
+ )
544
+ }
545
+
546
+ const raf = (() => {
547
+ if (typeof requestAnimationFrame !== "undefined") {
548
+ return requestAnimationFrame(() => compute())
549
+ }
550
+ return setTimeout(() => compute(), 0) as unknown as number
551
+ })()
552
+
553
+ return () => {
554
+ cancelled = true
555
+ if (typeof cancelAnimationFrame !== "undefined") {
556
+ cancelAnimationFrame(raf)
557
+ } else {
558
+ clearTimeout(raf)
559
+ }
560
+ }
561
+ }, [align, parent?.open, side, sideOffset, subContext?.open, subContext?.triggerId])
562
+
563
+ if (!parent?.open || !subContext?.open) return null
564
+
565
+ const baseClassName =
566
+ "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"
567
+
568
+ const contentStyle = position
569
+ ? ({ left: position.left, top: position.top } as React.CSSProperties)
570
+ : ({
571
+ left: 0,
572
+ top: 0,
573
+ opacity: 0,
574
+ pointerEvents: "none",
575
+ } as React.CSSProperties)
576
+
577
+ return (
578
+ <Portal>
579
+ <View
580
+ {...props}
581
+ ref={ref}
582
+ id={contentId.current}
583
+ data-slot="context-menu-sub-content"
584
+ data-state="open"
585
+ data-side={side}
586
+ className={cn(baseClassName, className)}
587
+ style={contentStyle}
588
+ onClick={(e) => e.stopPropagation()}
589
+ >
590
+ <ScrollView scrollY className="max-h-[50vh] overflow-x-hidden overflow-y-auto">
591
+ {children}
592
+ </ScrollView>
593
+ </View>
594
+ </Portal>
595
+ )
596
+ })
597
+
598
+ export {
599
+ ContextMenu,
600
+ ContextMenuTrigger,
601
+ ContextMenuContent,
602
+ ContextMenuItem,
603
+ ContextMenuCheckboxItem,
604
+ ContextMenuRadioItem,
605
+ ContextMenuLabel,
606
+ ContextMenuSeparator,
607
+ ContextMenuShortcut,
608
+ ContextMenuGroup,
609
+ ContextMenuPortal,
610
+ ContextMenuSub,
611
+ ContextMenuSubContent,
612
+ ContextMenuSubTrigger,
613
+ ContextMenuRadioGroup,
614
+ }