@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,264 @@
1
+ import * as React from "react"
2
+ import { View, ScrollView } from "@tarojs/components"
3
+ import { ChevronDown } from "lucide-react-taro"
4
+ import { cva } from "class-variance-authority"
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 NavigationMenuContext = React.createContext<{
11
+ value?: string
12
+ onValueChange?: (value: string | undefined) => void
13
+ } | null>(null)
14
+
15
+ const NavigationMenu = React.forwardRef<
16
+ React.ElementRef<typeof View>,
17
+ React.ComponentPropsWithoutRef<typeof View> & {
18
+ value?: string
19
+ onValueChange?: (value: string | undefined) => void
20
+ }
21
+ >(({ className, children, value: valueProp, onValueChange, ...props }, ref) => {
22
+ const [valueState, setValueState] = React.useState<string | undefined>()
23
+ const value = valueProp !== undefined ? valueProp : valueState
24
+
25
+ const handleValueChange = (newValue: string | undefined) => {
26
+ if (valueProp === undefined) {
27
+ setValueState(newValue)
28
+ }
29
+ onValueChange?.(newValue)
30
+ }
31
+
32
+ return (
33
+ <NavigationMenuContext.Provider value={{ value, onValueChange: handleValueChange }}>
34
+ <View
35
+ ref={ref}
36
+ className={cn(
37
+ "relative z-10 flex max-w-max flex-1 items-center justify-center",
38
+ className
39
+ )}
40
+ {...props}
41
+ >
42
+ {children}
43
+ </View>
44
+ </NavigationMenuContext.Provider>
45
+ )
46
+ })
47
+ NavigationMenu.displayName = "NavigationMenu"
48
+
49
+ const NavigationMenuList = React.forwardRef<
50
+ React.ElementRef<typeof View>,
51
+ React.ComponentPropsWithoutRef<typeof View>
52
+ >(({ className, ...props }, ref) => (
53
+ <View
54
+ ref={ref}
55
+ className={cn(
56
+ "group flex flex-1 list-none items-center justify-center space-x-1",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ ))
62
+ NavigationMenuList.displayName = "NavigationMenuList"
63
+
64
+ const NavigationMenuItemContext = React.createContext<{ value: string; triggerId: string } | null>(null)
65
+
66
+ const NavigationMenuItem = React.forwardRef<
67
+ React.ElementRef<typeof View>,
68
+ React.ComponentPropsWithoutRef<typeof View> & { value?: string }
69
+ >(({ children, value: valueProp, ...props }, ref) => {
70
+ const id = React.useId()
71
+ const value = valueProp || id
72
+ const triggerIdRef = React.useRef(`navigation-menu-trigger-${Math.random().toString(36).slice(2, 10)}`)
73
+ return (
74
+ <NavigationMenuItemContext.Provider value={{ value, triggerId: triggerIdRef.current }}>
75
+ <View ref={ref} {...props}>
76
+ {children}
77
+ </View>
78
+ </NavigationMenuItemContext.Provider>
79
+ )
80
+ })
81
+ NavigationMenuItem.displayName = "NavigationMenuItem"
82
+
83
+ const navigationMenuTriggerStyle = cva(
84
+ "group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:bg-opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent"
85
+ )
86
+
87
+ const NavigationMenuTrigger = React.forwardRef<
88
+ React.ElementRef<typeof View>,
89
+ React.ComponentPropsWithoutRef<typeof View>
90
+ >(({ className, children, ...props }, ref) => {
91
+ const context = React.useContext(NavigationMenuContext)
92
+ const item = React.useContext(NavigationMenuItemContext)
93
+ const isOpen = context?.value === item?.value
94
+
95
+ return (
96
+ <View
97
+ ref={ref}
98
+ id={item?.triggerId}
99
+ data-slot="navigation-menu-trigger"
100
+ data-state={isOpen ? "open" : "closed"}
101
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
102
+ onClick={() => context?.onValueChange?.(isOpen ? undefined : item?.value)}
103
+ {...props}
104
+ >
105
+ {children}{" "}
106
+ <ChevronDown
107
+ className={cn(
108
+ "relative top-[1px] ml-1 h-3 w-3 transition duration-200",
109
+ isOpen && "rotate-180"
110
+ )}
111
+ aria-hidden="true"
112
+ />
113
+ </View>
114
+ )
115
+ })
116
+ NavigationMenuTrigger.displayName = "NavigationMenuTrigger"
117
+
118
+ const NavigationMenuContent = React.forwardRef<
119
+ React.ElementRef<typeof View>,
120
+ React.ComponentPropsWithoutRef<typeof View> & {
121
+ align?: "start" | "center" | "end"
122
+ side?: "top" | "bottom" | "left" | "right"
123
+ sideOffset?: number
124
+ }
125
+ >(({ className, align = "start", side = "bottom", sideOffset = 4, children, ...props }, ref) => {
126
+ const context = React.useContext(NavigationMenuContext)
127
+ const item = React.useContext(NavigationMenuItemContext)
128
+ const isOpen = context?.value === item?.value
129
+ const contentId = React.useRef(`navigation-menu-content-${Math.random().toString(36).slice(2, 10)}`)
130
+ const [position, setPosition] = React.useState<{ left: number; top: number } | null>(null)
131
+ const [contentWidth, setContentWidth] = React.useState<number | null>(null)
132
+
133
+ React.useEffect(() => {
134
+ if (!isOpen) {
135
+ setPosition(null)
136
+ setContentWidth(null)
137
+ return
138
+ }
139
+
140
+ let cancelled = false
141
+
142
+ const compute = async () => {
143
+ const triggerId = item?.triggerId
144
+ if (!triggerId) return
145
+ const [triggerRect, contentRect] = await Promise.all([
146
+ getRectById(triggerId),
147
+ getRectById(contentId.current),
148
+ ])
149
+
150
+ if (cancelled) return
151
+ if (!triggerRect?.width || !contentRect?.width) return
152
+
153
+ setContentWidth(contentRect.width)
154
+ setPosition(
155
+ computePosition({
156
+ triggerRect,
157
+ contentRect,
158
+ align,
159
+ side,
160
+ sideOffset,
161
+ })
162
+ )
163
+ }
164
+
165
+ const raf = (() => {
166
+ if (typeof requestAnimationFrame !== "undefined") {
167
+ return requestAnimationFrame(() => compute())
168
+ }
169
+ return setTimeout(() => compute(), 0) as unknown as number
170
+ })()
171
+
172
+ return () => {
173
+ cancelled = true
174
+ if (typeof cancelAnimationFrame !== "undefined") {
175
+ cancelAnimationFrame(raf)
176
+ } else {
177
+ clearTimeout(raf)
178
+ }
179
+ }
180
+ }, [align, isOpen, item?.triggerId, side, sideOffset])
181
+
182
+ React.useEffect(() => {
183
+ if (!isOpen) return
184
+ if (!isH5() || typeof document === "undefined") return
185
+ const onKeyDown = (e: KeyboardEvent) => {
186
+ if (e.key === "Escape") {
187
+ context?.onValueChange?.(undefined)
188
+ }
189
+ }
190
+ document.addEventListener("keydown", onKeyDown)
191
+ return () => document.removeEventListener("keydown", onKeyDown)
192
+ }, [context, isOpen])
193
+
194
+ if (!isOpen) return null
195
+
196
+ const baseClassName =
197
+ "fixed z-50 min-w-32 w-max max-w-[95vw] 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"
198
+
199
+ const vw = getViewport().width
200
+ const isMobileLayout = vw < 640
201
+ const shouldFullWidth = isMobileLayout && contentWidth !== null && contentWidth > vw - 16
202
+
203
+ const contentStyle = position
204
+ ? (shouldFullWidth
205
+ ? ({ left: 8, right: 8, top: position.top } as React.CSSProperties)
206
+ : ({ left: position.left, top: position.top } as React.CSSProperties))
207
+ : ({
208
+ left: shouldFullWidth ? 8 : 0,
209
+ right: shouldFullWidth ? 8 : undefined,
210
+ top: 0,
211
+ opacity: 0,
212
+ pointerEvents: "none",
213
+ } as React.CSSProperties)
214
+
215
+ return (
216
+ <Portal>
217
+ <View
218
+ className="fixed inset-0 z-50 bg-transparent"
219
+ onClick={() => context?.onValueChange?.(undefined)}
220
+ />
221
+ <View
222
+ ref={ref}
223
+ id={contentId.current}
224
+ data-slot="navigation-menu-content"
225
+ data-state="open"
226
+ data-side={side}
227
+ className={cn(
228
+ baseClassName,
229
+ className
230
+ )}
231
+ style={contentStyle}
232
+ onClick={(e) => e.stopPropagation()}
233
+ {...props}
234
+ >
235
+ <ScrollView scrollY className="max-h-[70vh] overflow-x-hidden overflow-y-auto">
236
+ {children}
237
+ </ScrollView>
238
+ </View>
239
+ </Portal>
240
+ )
241
+ })
242
+ NavigationMenuContent.displayName = "NavigationMenuContent"
243
+
244
+ const NavigationMenuLink = ({ children, className, ...props }: any) => (
245
+ <View className={cn("block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", className)} {...props}>
246
+ {children}
247
+ </View>
248
+ )
249
+
250
+ const NavigationMenuViewport = () => null
251
+
252
+ const NavigationMenuIndicator = () => null
253
+
254
+ export {
255
+ navigationMenuTriggerStyle,
256
+ NavigationMenu,
257
+ NavigationMenuList,
258
+ NavigationMenuItem,
259
+ NavigationMenuContent,
260
+ NavigationMenuTrigger,
261
+ NavigationMenuLink,
262
+ NavigationMenuIndicator,
263
+ NavigationMenuViewport,
264
+ }
@@ -0,0 +1,118 @@
1
+ import * as React from "react"
2
+ import { View, Text } from "@tarojs/components"
3
+ import { ChevronLeft, ChevronRight, Ellipsis } from "lucide-react-taro"
4
+
5
+ import { cn } from "@/lib/utils"
6
+ import { ButtonProps, buttonVariants } from "@/components/ui/button"
7
+
8
+ const Pagination = ({ className, ...props }: React.ComponentPropsWithoutRef<typeof View>) => (
9
+ <View
10
+ role="navigation"
11
+ aria-label="pagination"
12
+ className={cn("mx-auto flex w-full justify-center", className)}
13
+ {...props}
14
+ />
15
+ )
16
+ Pagination.displayName = "Pagination"
17
+
18
+ const PaginationContent = React.forwardRef<
19
+ React.ElementRef<typeof View>,
20
+ React.ComponentPropsWithoutRef<typeof View>
21
+ >(({ className, ...props }, ref) => (
22
+ <View
23
+ ref={ref}
24
+ className={cn("flex flex-row items-center gap-1", className)}
25
+ {...props}
26
+ />
27
+ ))
28
+ PaginationContent.displayName = "PaginationContent"
29
+
30
+ const PaginationItem = React.forwardRef<
31
+ React.ElementRef<typeof View>,
32
+ React.ComponentPropsWithoutRef<typeof View>
33
+ >(({ className, ...props }, ref) => (
34
+ <View ref={ref} className={cn("", className)} {...props} />
35
+ ))
36
+ PaginationItem.displayName = "PaginationItem"
37
+
38
+ type PaginationLinkProps = {
39
+ isActive?: boolean
40
+ } & Pick<ButtonProps, "size"> &
41
+ React.ComponentPropsWithoutRef<typeof View>
42
+
43
+ const PaginationLink = ({
44
+ className,
45
+ isActive,
46
+ size = "icon",
47
+ ...props
48
+ }: PaginationLinkProps) => (
49
+ <View
50
+ aria-current={isActive ? "page" : undefined}
51
+ className={cn(
52
+ buttonVariants({
53
+ variant: isActive ? "outline" : "ghost",
54
+ size,
55
+ }),
56
+ className
57
+ )}
58
+ {...props}
59
+ />
60
+ )
61
+ PaginationLink.displayName = "PaginationLink"
62
+
63
+ const PaginationPrevious = ({
64
+ className,
65
+ ...props
66
+ }: React.ComponentProps<typeof PaginationLink>) => (
67
+ <PaginationLink
68
+ aria-label="Go to previous page"
69
+ size="default"
70
+ className={cn("gap-1 pl-3", className)}
71
+ {...props}
72
+ >
73
+ <ChevronLeft size={16} />
74
+ <Text>上一页</Text>
75
+ </PaginationLink>
76
+ )
77
+ PaginationPrevious.displayName = "PaginationPrevious"
78
+
79
+ const PaginationNext = ({
80
+ className,
81
+ ...props
82
+ }: React.ComponentProps<typeof PaginationLink>) => (
83
+ <PaginationLink
84
+ aria-label="Go to next page"
85
+ size="default"
86
+ className={cn("gap-1 pr-3", className)}
87
+ {...props}
88
+ >
89
+ <Text>下一页</Text>
90
+ <ChevronRight size={16} />
91
+ </PaginationLink>
92
+ )
93
+ PaginationNext.displayName = "PaginationNext"
94
+
95
+ const PaginationEllipsis = ({
96
+ className,
97
+ ...props
98
+ }: React.ComponentPropsWithoutRef<typeof View>) => (
99
+ <View
100
+ aria-hidden
101
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
102
+ {...props}
103
+ >
104
+ <Ellipsis size={16} />
105
+ <View className="sr-only">More pages</View>
106
+ </View>
107
+ )
108
+ PaginationEllipsis.displayName = "PaginationEllipsis"
109
+
110
+ export {
111
+ Pagination,
112
+ PaginationContent,
113
+ PaginationEllipsis,
114
+ PaginationItem,
115
+ PaginationLink,
116
+ PaginationNext,
117
+ PaginationPrevious,
118
+ }
@@ -0,0 +1,291 @@
1
+ import * as React from "react"
2
+ import { View } from "@tarojs/components"
3
+ import Taro from "@tarojs/taro"
4
+ import { cn } from "@/lib/utils"
5
+ import { Portal } from "@/components/ui/portal"
6
+ import { useKeyboardOffset } from "@/lib/hooks/use-keyboard-offset"
7
+ import { isH5 } from "@/lib/platform"
8
+
9
+ const PopoverContext = React.createContext<{
10
+ open?: boolean
11
+ onOpenChange?: (open: boolean) => void
12
+ triggerId: string
13
+ } | null>(null)
14
+
15
+ interface PopoverProps {
16
+ children: React.ReactNode
17
+ open?: boolean
18
+ defaultOpen?: boolean
19
+ onOpenChange?: (open: boolean) => void
20
+ }
21
+
22
+ const Popover = ({ open: openProp, defaultOpen, onOpenChange, children }: PopoverProps) => {
23
+ const baseIdRef = React.useRef(
24
+ `popover-${Math.random().toString(36).slice(2, 10)}`
25
+ )
26
+ const [openState, setOpenState] = React.useState(defaultOpen || false)
27
+ const open = openProp !== undefined ? openProp : openState
28
+
29
+ const handleOpenChange = (newOpen: boolean) => {
30
+ if (openProp === undefined) {
31
+ setOpenState(newOpen)
32
+ }
33
+ onOpenChange?.(newOpen)
34
+ }
35
+
36
+ React.useEffect(() => {
37
+ if (!isH5()) return
38
+ if (!open) return
39
+
40
+ const onKeyDown = (e: KeyboardEvent) => {
41
+ if (e.key !== "Escape") return
42
+ e.stopPropagation()
43
+ handleOpenChange(false)
44
+ }
45
+
46
+ window.addEventListener("keydown", onKeyDown, true)
47
+ return () => {
48
+ window.removeEventListener("keydown", onKeyDown, true)
49
+ }
50
+ }, [open])
51
+
52
+ return (
53
+ <PopoverContext.Provider value={{ open, onOpenChange: handleOpenChange, triggerId: baseIdRef.current }}>
54
+ {children}
55
+ </PopoverContext.Provider>
56
+ )
57
+ }
58
+
59
+ const PopoverTrigger = React.forwardRef<
60
+ React.ElementRef<typeof View>,
61
+ React.ComponentPropsWithoutRef<typeof View> & { asChild?: boolean }
62
+ >(({ className, children, asChild, ...props }, ref) => {
63
+ const context = React.useContext(PopoverContext)
64
+ return (
65
+ <View
66
+ ref={ref}
67
+ id={context?.triggerId}
68
+ className={cn("w-fit", className)}
69
+ onClick={(e) => {
70
+ e.stopPropagation()
71
+ context?.onOpenChange?.(!context.open)
72
+ }}
73
+ {...props}
74
+ >
75
+ {children}
76
+ </View>
77
+ )
78
+ })
79
+ PopoverTrigger.displayName = "PopoverTrigger"
80
+
81
+ interface PopoverContentProps extends React.ComponentPropsWithoutRef<typeof View> {
82
+ align?: "center" | "start" | "end"
83
+ side?: "top" | "bottom" | "left" | "right"
84
+ position?: "top" | "bottom" | "left" | "right"
85
+ sideOffset?: number
86
+ }
87
+
88
+ const PopoverContent = React.forwardRef<
89
+ React.ElementRef<typeof View>,
90
+ PopoverContentProps
91
+ >(({ className, align = "center", side, position: positionProp, sideOffset = 4, style, ...props }, ref) => {
92
+ const context = React.useContext(PopoverContext)
93
+ const offset = useKeyboardOffset()
94
+ const resolvedSide = positionProp ?? side ?? "bottom"
95
+ const contentId = React.useRef(
96
+ `popover-content-${Math.random().toString(36).slice(2, 10)}`
97
+ )
98
+ const [contentPosition, setContentPosition] = React.useState<
99
+ | {
100
+ left: number
101
+ top: number
102
+ }
103
+ | null
104
+ >(null)
105
+
106
+ React.useEffect(() => {
107
+ if (!context?.open) {
108
+ setContentPosition(null)
109
+ return
110
+ }
111
+
112
+ let cancelled = false
113
+
114
+ const getViewport = () => {
115
+ if (isH5() && typeof window !== "undefined") {
116
+ return { width: window.innerWidth, height: window.innerHeight }
117
+ }
118
+ try {
119
+ const info = Taro.getSystemInfoSync()
120
+ return { width: info.windowWidth, height: info.windowHeight }
121
+ } catch {
122
+ return { width: 375, height: 667 }
123
+ }
124
+ }
125
+
126
+ const getRectH5 = (id: string) => {
127
+ if (!isH5() || typeof document === "undefined") return null
128
+ const el = document.getElementById(id)
129
+ if (!el) return null
130
+ const r = el.getBoundingClientRect()
131
+ return { left: r.left, top: r.top, width: r.width, height: r.height }
132
+ }
133
+
134
+ const getRect = (id: string) => {
135
+ const h5Rect = getRectH5(id)
136
+ if (h5Rect) return Promise.resolve(h5Rect)
137
+ return new Promise<any>((resolve) => {
138
+ const query = Taro.createSelectorQuery()
139
+ query
140
+ .select(`#${id}`)
141
+ .boundingClientRect((res) => {
142
+ const rect = Array.isArray(res) ? res[0] : res
143
+ resolve(rect || null)
144
+ })
145
+ .exec()
146
+ })
147
+ }
148
+
149
+ const compute = async () => {
150
+ const [triggerRect, contentRect] = await Promise.all([
151
+ getRect(context.triggerId),
152
+ getRect(contentId.current),
153
+ ])
154
+
155
+ if (cancelled) return
156
+ if (!triggerRect?.width || !contentRect?.width) return
157
+
158
+ const viewport = getViewport()
159
+ const vw = viewport.width
160
+ const vh = Math.max(0, viewport.height - (isH5() ? 0 : offset || 0))
161
+ const margin = 8
162
+
163
+ const crossStart =
164
+ resolvedSide === "left" || resolvedSide === "right" ? triggerRect.top : triggerRect.left
165
+ const crossSize =
166
+ resolvedSide === "left" || resolvedSide === "right" ? triggerRect.height : triggerRect.width
167
+ const contentCrossSize =
168
+ resolvedSide === "left" || resolvedSide === "right" ? contentRect.height : contentRect.width
169
+
170
+ const cross = (() => {
171
+ if (align === "start") return crossStart
172
+ if (align === "end") return crossStart + crossSize - contentCrossSize
173
+ return crossStart + crossSize / 2 - contentCrossSize / 2
174
+ })()
175
+
176
+ let left = 0
177
+ let top = 0
178
+
179
+ if (resolvedSide === "bottom" || resolvedSide === "top") {
180
+ left = cross
181
+ top =
182
+ resolvedSide === "bottom"
183
+ ? triggerRect.top + triggerRect.height + sideOffset
184
+ : triggerRect.top - contentRect.height - sideOffset
185
+ } else {
186
+ top = cross
187
+ left =
188
+ resolvedSide === "right"
189
+ ? triggerRect.left + triggerRect.width + sideOffset
190
+ : triggerRect.left - contentRect.width - sideOffset
191
+ }
192
+
193
+ left = Math.min(Math.max(left, margin), vw - contentRect.width - margin)
194
+ top = Math.min(Math.max(top, margin), vh - contentRect.height - margin)
195
+
196
+ setContentPosition({ left, top })
197
+ }
198
+
199
+ const raf = (() => {
200
+ if (typeof requestAnimationFrame !== "undefined") {
201
+ return requestAnimationFrame(() => compute())
202
+ }
203
+ return setTimeout(() => compute(), 0) as unknown as number
204
+ })()
205
+
206
+ return () => {
207
+ cancelled = true
208
+ if (typeof cancelAnimationFrame !== "undefined") {
209
+ cancelAnimationFrame(raf)
210
+ } else {
211
+ clearTimeout(raf)
212
+ }
213
+ }
214
+ }, [align, context?.open, context?.triggerId, offset, resolvedSide, sideOffset])
215
+
216
+ if (!context?.open) return null
217
+
218
+ const baseClassName =
219
+ "fixed z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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"
220
+
221
+ const contentStyle = contentPosition
222
+ ? ({
223
+ ...(style as object),
224
+ left: contentPosition.left,
225
+ top: contentPosition.top,
226
+ } as React.CSSProperties)
227
+ : ({
228
+ ...(style as object),
229
+ left: 0,
230
+ top: 0,
231
+ opacity: 0,
232
+ pointerEvents: "none",
233
+ } as React.CSSProperties)
234
+
235
+ return (
236
+ <Portal>
237
+ <View
238
+ className="fixed inset-0 z-50 bg-transparent"
239
+ onClick={() => context.onOpenChange?.(false)}
240
+ />
241
+ <View
242
+ ref={ref}
243
+ className={cn(
244
+ baseClassName,
245
+ className
246
+ )}
247
+ id={contentId.current}
248
+ style={contentStyle}
249
+ {...props}
250
+ />
251
+ </Portal>
252
+ )
253
+ })
254
+ PopoverContent.displayName = "PopoverContent"
255
+
256
+ const PopoverHeader = React.forwardRef<
257
+ React.ElementRef<typeof View>,
258
+ React.ComponentPropsWithoutRef<typeof View>
259
+ >(({ className, ...props }, ref) => (
260
+ <View ref={ref} className={cn("grid gap-1.5", className)} {...props} />
261
+ ))
262
+ PopoverHeader.displayName = "PopoverHeader"
263
+
264
+ const PopoverTitle = React.forwardRef<
265
+ React.ElementRef<typeof View>,
266
+ React.ComponentPropsWithoutRef<typeof View>
267
+ >(({ className, ...props }, ref) => (
268
+ <View ref={ref} className={cn("font-medium leading-none", className)} {...props} />
269
+ ))
270
+ PopoverTitle.displayName = "PopoverTitle"
271
+
272
+ const PopoverDescription = React.forwardRef<
273
+ React.ElementRef<typeof View>,
274
+ React.ComponentPropsWithoutRef<typeof View>
275
+ >(({ className, ...props }, ref) => (
276
+ <View
277
+ ref={ref}
278
+ className={cn("text-sm text-muted-foreground", className)}
279
+ {...props}
280
+ />
281
+ ))
282
+ PopoverDescription.displayName = "PopoverDescription"
283
+
284
+ export {
285
+ Popover,
286
+ PopoverTrigger,
287
+ PopoverContent,
288
+ PopoverHeader,
289
+ PopoverTitle,
290
+ PopoverDescription,
291
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+ import * as TaroComponents from "@tarojs/components"
3
+ import { createPortal } from "react-dom"
4
+ import { isH5 } from "@/lib/platform"
5
+
6
+ const RootPortal = (TaroComponents as any).RootPortal
7
+
8
+ const Portal = ({ children }: { children: React.ReactNode }) => {
9
+ if (isH5()) {
10
+ if (typeof document === "undefined") return <>{children}</>
11
+ return createPortal(children, document.body)
12
+ }
13
+ if (!RootPortal) {
14
+ return <>{children}</>
15
+ }
16
+ return <RootPortal>{children}</RootPortal>
17
+ }
18
+
19
+ export { Portal }