@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,114 @@
1
+ import * as React from "react"
2
+ import { View } from "@tarojs/components"
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const TabsContext = React.createContext<{
6
+ value?: string
7
+ onValueChange?: (value: string) => void
8
+ } | null>(null)
9
+
10
+ const Tabs = React.forwardRef<
11
+ React.ElementRef<typeof View>,
12
+ React.ComponentPropsWithoutRef<typeof View> & {
13
+ value?: string
14
+ defaultValue?: string
15
+ onValueChange?: (value: string) => void
16
+ }
17
+ >(({ className, value: valueProp, defaultValue, onValueChange, ...props }, ref) => {
18
+ const [valueState, setValueState] = React.useState<string | undefined>(defaultValue)
19
+ const value = valueProp !== undefined ? valueProp : valueState
20
+
21
+ const handleValueChange = (newValue: string) => {
22
+ if (valueProp === undefined) {
23
+ setValueState(newValue)
24
+ }
25
+ onValueChange?.(newValue)
26
+ }
27
+
28
+ return (
29
+ <TabsContext.Provider value={{ value, onValueChange: handleValueChange }}>
30
+ <View
31
+ ref={ref}
32
+ className={cn(className)}
33
+ {...props}
34
+ />
35
+ </TabsContext.Provider>
36
+ )
37
+ })
38
+ Tabs.displayName = "Tabs"
39
+
40
+ const TabsList = React.forwardRef<
41
+ React.ElementRef<typeof View>,
42
+ React.ComponentPropsWithoutRef<typeof View>
43
+ >(({ className, ...props }, ref) => (
44
+ <View
45
+ ref={ref}
46
+ className={cn(
47
+ "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ ))
53
+ TabsList.displayName = "TabsList"
54
+
55
+ const TabsTrigger = React.forwardRef<
56
+ React.ElementRef<typeof View>,
57
+ React.ComponentPropsWithoutRef<typeof View> & {
58
+ value: string
59
+ disabled?: boolean
60
+ }
61
+ >(({ className, value, onClick, disabled, ...props }, ref) => {
62
+ const context = React.useContext(TabsContext)
63
+ const isActive = context?.value === value
64
+
65
+ const handleClick = (e: any) => {
66
+ if (disabled) return
67
+ context?.onValueChange?.(value)
68
+ onClick?.(e)
69
+ }
70
+
71
+ return (
72
+ <View
73
+ ref={ref}
74
+ onClick={handleClick}
75
+ className={cn(
76
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1 text-sm font-medium ring-offset-background transition-all focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background disabled:pointer-events-none disabled:opacity-50",
77
+ isActive && "bg-background text-foreground shadow-sm",
78
+ disabled && "opacity-50 pointer-events-none",
79
+ className
80
+ )}
81
+ hoverClass={
82
+ disabled
83
+ ? undefined
84
+ : "border-ring ring-2 ring-ring ring-offset-2 ring-offset-background"
85
+ }
86
+ {...props}
87
+ />
88
+ )
89
+ })
90
+ TabsTrigger.displayName = "TabsTrigger"
91
+
92
+ const TabsContent = React.forwardRef<
93
+ React.ElementRef<typeof View>,
94
+ React.ComponentPropsWithoutRef<typeof View> & {
95
+ value: string
96
+ }
97
+ >(({ className, value, ...props }, ref) => {
98
+ const context = React.useContext(TabsContext)
99
+ if (context?.value !== value) return null
100
+
101
+ return (
102
+ <View
103
+ ref={ref}
104
+ className={cn(
105
+ "mt-2 ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background",
106
+ className
107
+ )}
108
+ {...props}
109
+ />
110
+ )
111
+ })
112
+ TabsContent.displayName = "TabsContent"
113
+
114
+ export { Tabs, TabsList, TabsTrigger, TabsContent }
@@ -0,0 +1,54 @@
1
+ import * as React from "react"
2
+ import { Textarea as TaroTextarea, View } from "@tarojs/components"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ export interface TextareaProps
7
+ extends React.ComponentPropsWithoutRef<typeof TaroTextarea> {
8
+ className?: string
9
+ autoFocus?: boolean
10
+ }
11
+
12
+ const Textarea = React.forwardRef<
13
+ React.ElementRef<typeof TaroTextarea>,
14
+ TextareaProps
15
+ >(({ className, autoFocus, focus, onFocus, onBlur, ...props }, ref) => {
16
+ const [isFocused, setIsFocused] = React.useState(false)
17
+ const disabled = !!(props as any).disabled
18
+
19
+ React.useEffect(() => {
20
+ if (autoFocus || focus) setIsFocused(true)
21
+ }, [autoFocus, focus])
22
+ return (
23
+ <View
24
+ className={cn(
25
+ "flex h-20 w-full rounded-md border border-input bg-background px-3 py-2 ring-offset-background focus-within:border-ring focus-within:ring-4 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background",
26
+ isFocused && "border-ring ring-4 ring-ring ring-offset-2 ring-offset-background",
27
+ className
28
+ )}
29
+ onTouchStart={() => {
30
+ if (disabled) return
31
+ setIsFocused(true)
32
+ }}
33
+ >
34
+ <TaroTextarea
35
+ className="flex-1 w-full h-full bg-transparent text-base text-foreground placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm selection:bg-selection selection:text-selection-foreground"
36
+ placeholderClass="text-muted-foreground"
37
+ ref={ref}
38
+ focus={autoFocus || focus}
39
+ onFocus={(e) => {
40
+ setIsFocused(true)
41
+ onFocus?.(e)
42
+ }}
43
+ onBlur={(e) => {
44
+ setIsFocused(false)
45
+ onBlur?.(e)
46
+ }}
47
+ {...props}
48
+ />
49
+ </View>
50
+ )
51
+ })
52
+ Textarea.displayName = "Textarea"
53
+
54
+ export { Textarea }
@@ -0,0 +1,517 @@
1
+ import * as React from "react"
2
+ import { Text, View } from "@tarojs/components"
3
+ import Taro from "@tarojs/taro"
4
+ import {
5
+ Check,
6
+ Info,
7
+ Loader,
8
+ TriangleAlert,
9
+ X,
10
+ CircleAlert
11
+ } from "lucide-react-taro"
12
+ import { cn } from "@/lib/utils"
13
+ import { Portal } from "@/components/ui/portal"
14
+
15
+ export type ToastPosition =
16
+ | "top-left"
17
+ | "top-right"
18
+ | "bottom-left"
19
+ | "bottom-right"
20
+ | "top-center"
21
+ | "bottom-center"
22
+
23
+ export type ToastType = "success" | "info" | "warning" | "error" | "loading" | "default"
24
+
25
+ export interface ToastAction {
26
+ label: string
27
+ onClick: () => void
28
+ }
29
+
30
+ export interface ToastData {
31
+ id?: string | number
32
+ title?: React.ReactNode
33
+ description?: React.ReactNode
34
+ type?: ToastType
35
+ duration?: number
36
+ position?: ToastPosition
37
+ invert?: boolean
38
+ dismissible?: boolean
39
+ descriptionClassName?: string
40
+ action?: ToastAction
41
+ cancel?: ToastAction
42
+ onDismiss?: (toast: Toast) => void
43
+ onAutoClose?: (toast: Toast) => void
44
+ richColors?: boolean
45
+ closeButton?: boolean
46
+ style?: React.CSSProperties
47
+ className?: string
48
+ jsx?: React.ReactNode | ((id: string | number) => React.ReactNode)
49
+ }
50
+
51
+ export interface Toast extends ToastData {
52
+ id: string | number
53
+ }
54
+
55
+ const listeners: Array<(toasts: Toast[]) => void> = []
56
+ let toasts: Toast[] = []
57
+
58
+ const notify = () => {
59
+ listeners.forEach((l) => l([...toasts]))
60
+ }
61
+
62
+ const createToast = (title: React.ReactNode, data: ToastData = {}) => {
63
+ const id = data.id || Date.now().toString() + Math.random().toString(36).substring(2, 9)
64
+
65
+ const existingToast = toasts.find((t) => t.id === id)
66
+
67
+ if (existingToast) {
68
+ toasts = toasts.map((t) =>
69
+ t.id === id ? { ...t, ...data, title: title || t.title } : t
70
+ )
71
+ } else {
72
+ const newToast: Toast = {
73
+ ...data,
74
+ id,
75
+ title,
76
+ type: data.type || "default",
77
+ dismissible: data.dismissible ?? true,
78
+ }
79
+ toasts = [...toasts, newToast]
80
+ }
81
+
82
+ notify()
83
+ return id
84
+ }
85
+
86
+ const dismiss = (id?: string | number) => {
87
+ if (!id) {
88
+ toasts = []
89
+ } else {
90
+ toasts = toasts.filter((t) => t.id !== id)
91
+ }
92
+ notify()
93
+ }
94
+
95
+ type ToastFunction = (title: string | React.ReactNode, data?: ToastData) => string | number
96
+
97
+ const toastFn: ToastFunction = (title, data) => createToast(title, data)
98
+
99
+ const toast = Object.assign(toastFn, {
100
+ success: (title: string | React.ReactNode, data?: ToastData) =>
101
+ createToast(title, { ...data, type: "success" }),
102
+ error: (title: string | React.ReactNode, data?: ToastData) =>
103
+ createToast(title, { ...data, type: "error" }),
104
+ warning: (title: string | React.ReactNode, data?: ToastData) =>
105
+ createToast(title, { ...data, type: "warning" }),
106
+ info: (title: string | React.ReactNode, data?: ToastData) =>
107
+ createToast(title, { ...data, type: "info" }),
108
+ loading: (title: string | React.ReactNode, data?: ToastData) =>
109
+ createToast(title, { ...data, type: "loading" }),
110
+ message: (title: string | React.ReactNode, data?: ToastData) =>
111
+ createToast(title, { ...data, type: "default" }),
112
+ custom: (jsx: (id: string | number) => React.ReactNode, data?: ToastData) => {
113
+ const id = data?.id || Date.now().toString()
114
+ return createToast(null, { ...data, id, jsx })
115
+ },
116
+ dismiss,
117
+ promise: <T,>(
118
+ promise: Promise<T> | (() => Promise<T>),
119
+ data: {
120
+ loading?: string | React.ReactNode
121
+ success?: string | React.ReactNode | ((data: T) => React.ReactNode)
122
+ error?: string | React.ReactNode | ((error: any) => React.ReactNode)
123
+ finally?: () => void
124
+ } & ToastData
125
+ ) => {
126
+ const id = toast.loading(data.loading, { ...data })
127
+
128
+ const p = typeof promise === "function" ? promise() : promise
129
+
130
+ p.then((res) => {
131
+ const successMessage = typeof data.success === "function" ? data.success(res) : data.success
132
+ toast.success(successMessage, { id, ...data })
133
+ })
134
+ .catch((err) => {
135
+ const errorMessage = typeof data.error === "function" ? data.error(err) : data.error
136
+ toast.error(errorMessage, { id, ...data })
137
+ })
138
+ .finally(() => {
139
+ data.finally?.()
140
+ })
141
+
142
+ return id
143
+ }
144
+ })
145
+
146
+ interface ToasterProps {
147
+ position?: ToastPosition
148
+ richColors?: boolean
149
+ expand?: boolean
150
+ closeButton?: boolean
151
+ offset?: number
152
+ dir?: "rtl" | "ltr" | "auto"
153
+ visibleToasts?: number
154
+ duration?: number
155
+ gap?: number
156
+ theme?: "light" | "dark" | "system"
157
+ className?: string
158
+ style?: React.CSSProperties
159
+ toastOptions?: Omit<ToastData, "id">
160
+ }
161
+
162
+ const Toaster = ({
163
+ position = "bottom-right",
164
+ richColors = false,
165
+ expand = false,
166
+ closeButton = false,
167
+ visibleToasts = 3,
168
+ duration = 4000,
169
+ gap = 14,
170
+ className,
171
+ style,
172
+ toastOptions
173
+ }: ToasterProps) => {
174
+ const [activeToasts, setActiveToasts] = React.useState<Toast[]>([])
175
+ const [closingIds, setClosingIds] = React.useState<Set<string | number>>(() => new Set())
176
+ const [frontHeight, setFrontHeight] = React.useState<number | null>(null)
177
+ const frontIdRef = React.useRef(`toaster-front-${Math.random().toString(36).slice(2, 9)}`)
178
+
179
+ React.useEffect(() => {
180
+ listeners.push(setActiveToasts)
181
+ return () => {
182
+ const index = listeners.indexOf(setActiveToasts)
183
+ if (index > -1) {
184
+ listeners.splice(index, 1)
185
+ }
186
+ }
187
+ }, [])
188
+
189
+ const getPositionStyle = (pos: ToastPosition) => {
190
+ switch (pos) {
191
+ case "top-left": return "top-0 left-0 right-0 justify-start"
192
+ case "top-right": return "top-0 left-0 right-0 justify-end"
193
+ case "bottom-left": return "bottom-0 left-0 right-0 justify-start"
194
+ case "bottom-right": return "bottom-0 left-0 right-0 justify-end"
195
+ case "top-center": return "top-0 left-0 right-0 justify-center"
196
+ case "bottom-center": return "bottom-0 left-0 right-0 justify-center"
197
+ default: return "bottom-0 left-0 right-0 justify-end"
198
+ }
199
+ }
200
+
201
+ const isTop = position.includes("top")
202
+
203
+ React.useEffect(() => {
204
+ if (expand) return
205
+ if (activeToasts.length <= visibleToasts) return
206
+
207
+ const overflow = activeToasts.slice(0, activeToasts.length - visibleToasts)
208
+ const nextToClose = overflow.find((t) => !closingIds.has(t.id))
209
+ if (!nextToClose) return
210
+
211
+ setClosingIds((prev) => {
212
+ if (prev.has(nextToClose.id)) return prev
213
+ const next = new Set(prev)
214
+ next.add(nextToClose.id)
215
+ return next
216
+ })
217
+ }, [activeToasts, closingIds, expand, visibleToasts])
218
+
219
+ React.useEffect(() => {
220
+ setClosingIds((prev) => {
221
+ if (prev.size === 0) return prev
222
+ const activeIds = new Set(activeToasts.map((t) => t.id))
223
+ const next = new Set<string | number>()
224
+ prev.forEach((id) => {
225
+ if (activeIds.has(id)) next.add(id)
226
+ })
227
+ return next.size === prev.size ? prev : next
228
+ })
229
+ }, [activeToasts])
230
+
231
+ const toastsToRender = React.useMemo(() => {
232
+ if (expand) return activeToasts
233
+ const keep = activeToasts.slice(-visibleToasts)
234
+ const overflowCount = Math.max(0, activeToasts.length - visibleToasts)
235
+ const overflowIds = new Set(activeToasts.slice(0, overflowCount).map((t) => t.id))
236
+ const closing = activeToasts.filter((t) => overflowIds.has(t.id) && closingIds.has(t.id))
237
+ return [...closing, ...keep]
238
+ }, [activeToasts, closingIds, expand, visibleToasts])
239
+
240
+ const listStyle = expand ? ({ gap } as React.CSSProperties) : undefined
241
+
242
+ React.useEffect(() => {
243
+ if (toastsToRender.length === 0) return
244
+ const timer = setTimeout(() => {
245
+ const query = Taro.createSelectorQuery()
246
+ query
247
+ .select(`#${frontIdRef.current}`)
248
+ .boundingClientRect((res) => {
249
+ const rect = Array.isArray(res) ? res[0] : res
250
+ if (rect?.height) setFrontHeight(rect.height)
251
+ })
252
+ .exec()
253
+ }, 50)
254
+ return () => clearTimeout(timer)
255
+ }, [toastsToRender.length])
256
+
257
+ return (
258
+ <Portal>
259
+ <View
260
+ className={cn(
261
+ "toaster fixed z-[100] flex p-4 w-full pointer-events-none",
262
+ getPositionStyle(position),
263
+ className
264
+ )}
265
+ data-position={position}
266
+ style={style}
267
+ >
268
+ <View
269
+ className={cn(
270
+ "toaster-list relative w-full flex",
271
+ isTop ? "flex-col-reverse" : "flex-col"
272
+ )}
273
+ style={listStyle}
274
+ >
275
+ {toastsToRender.map((t, index) => {
276
+ const isFront = index === toastsToRender.length - 1
277
+ const stackIndex = toastsToRender.length - 1 - index
278
+
279
+ return (
280
+ <ToastItem
281
+ key={t.id}
282
+ elementId={isFront ? frontIdRef.current : undefined}
283
+ item={t}
284
+ isExpanded={expand}
285
+ isFront={isFront}
286
+ stackIndex={stackIndex}
287
+ isTop={isTop}
288
+ gap={gap}
289
+ forceClose={!expand && closingIds.has(t.id)}
290
+ frontHeight={frontHeight}
291
+ duration={t.duration || duration}
292
+ richColors={t.richColors ?? richColors}
293
+ closeButton={t.closeButton ?? closeButton}
294
+ toastOptions={toastOptions}
295
+ />
296
+ )
297
+ })}
298
+ </View>
299
+ </View>
300
+ </Portal>
301
+ )
302
+ }
303
+
304
+ const ToastItem = ({
305
+ elementId,
306
+ item,
307
+ isExpanded,
308
+ isFront,
309
+ stackIndex,
310
+ isTop,
311
+ gap,
312
+ forceClose,
313
+ frontHeight,
314
+ duration,
315
+ richColors,
316
+ closeButton,
317
+ toastOptions
318
+ }: {
319
+ elementId?: string
320
+ item: Toast
321
+ isExpanded: boolean
322
+ isFront: boolean
323
+ stackIndex: number
324
+ isTop: boolean
325
+ gap: number
326
+ forceClose: boolean
327
+ frontHeight: number | null
328
+ duration: number
329
+ richColors: boolean
330
+ closeButton: boolean
331
+ toastOptions?: Omit<ToastData, "id">
332
+ }) => {
333
+ const [isVisible, setIsVisible] = React.useState(false)
334
+ const [isRemoved, setIsRemoved] = React.useState(false)
335
+ const timeOutRef = React.useRef<NodeJS.Timeout>()
336
+
337
+ React.useEffect(() => {
338
+ const timer = setTimeout(() => setIsVisible(true), 16)
339
+
340
+ if (item.duration === Infinity) return
341
+
342
+ const d = item.duration || duration
343
+ timeOutRef.current = setTimeout(() => {
344
+ handleDismiss()
345
+ }, d)
346
+
347
+ return () => {
348
+ clearTimeout(timer)
349
+ clearTimeout(timeOutRef.current)
350
+ }
351
+ }, [item.duration, duration])
352
+
353
+ const handleDismiss = () => {
354
+ setIsVisible(false)
355
+ setTimeout(() => {
356
+ setIsRemoved(true)
357
+ item.onDismiss?.(item)
358
+ dismiss(item.id)
359
+ }, 400)
360
+ }
361
+
362
+ React.useEffect(() => {
363
+ if (!forceClose) return
364
+ const t = setTimeout(() => handleDismiss(), 16)
365
+ return () => clearTimeout(t)
366
+ }, [forceClose])
367
+
368
+ const TypeIcon = {
369
+ success: Check,
370
+ error: CircleAlert,
371
+ warning: TriangleAlert,
372
+ info: Info,
373
+ loading: Loader,
374
+ default: null
375
+ }[item.type || "default"]
376
+
377
+ const palette = React.useMemo(() => {
378
+ if (!richColors) return null
379
+ const type = item.type || "default"
380
+ if (type === "success") {
381
+ return { backgroundColor: "hsl(143, 85%, 96%)", borderColor: "hsl(145, 92%, 87%)", color: "hsl(140, 100%, 27%)" }
382
+ }
383
+ if (type === "info") {
384
+ return { backgroundColor: "hsl(208, 100%, 97%)", borderColor: "hsl(221, 91%, 93%)", color: "hsl(210, 92%, 45%)" }
385
+ }
386
+ if (type === "warning") {
387
+ return { backgroundColor: "hsl(49, 100%, 97%)", borderColor: "hsl(49, 91%, 84%)", color: "hsl(31, 92%, 45%)" }
388
+ }
389
+ if (type === "error") {
390
+ return { backgroundColor: "hsl(359, 100%, 97%)", borderColor: "hsl(359, 100%, 94%)", color: "hsl(360, 100%, 45%)" }
391
+ }
392
+ return null
393
+ }, [item.type, richColors])
394
+
395
+ const iconColor = palette?.color
396
+
397
+ const isCollapsedStack = !isExpanded && !isFront
398
+ const lift = isTop ? 1 : -1
399
+ const enterOffset = (frontHeight ?? 80) * 1.5
400
+ const stackTranslate = lift * gap * stackIndex
401
+ const stackScale = 1 - stackIndex * 0.05
402
+ const transform = isCollapsedStack
403
+ ? `translateY(${stackTranslate}px) scale(${stackScale})`
404
+ : isVisible
405
+ ? "translateY(0px)"
406
+ : `translateY(${(-lift * enterOffset).toFixed(0)}px)`
407
+
408
+ const motionStyle: React.CSSProperties = {
409
+ transform,
410
+ opacity: isVisible ? 1 : 0,
411
+ transition: "transform 400ms ease, opacity 400ms ease, height 400ms ease, box-shadow 200ms ease",
412
+ transformOrigin: isCollapsedStack ? (isTop ? "bottom" : "top") : "center"
413
+ }
414
+
415
+ if (isRemoved) return null
416
+
417
+ const isCustom = typeof item.jsx !== "undefined"
418
+
419
+ const baseClasses = cn(
420
+ "toast pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-4 shadow-lg",
421
+ !palette && "bg-background border-border text-foreground",
422
+ item.className,
423
+ toastOptions?.className
424
+ )
425
+
426
+ const finalStyle: React.CSSProperties = {
427
+ ...(palette || {}),
428
+ ...item.style,
429
+ ...motionStyle,
430
+ position: isCollapsedStack ? "absolute" : "relative",
431
+ ...(isCollapsedStack ? (isTop ? { bottom: 0 } : { top: 0 }) : (isTop ? { top: 0 } : { bottom: 0 })),
432
+ left: 0,
433
+ right: 0,
434
+ zIndex: isFront ? 50 : 40 - stackIndex,
435
+ pointerEvents: isCollapsedStack ? "none" : "auto",
436
+ ...(isCollapsedStack && frontHeight ? { height: frontHeight } : {})
437
+ }
438
+
439
+ if (isCustom) {
440
+ const content = typeof item.jsx === "function" ? item.jsx(item.id) : item.jsx
441
+ return (
442
+ <View
443
+ className={cn(
444
+ "pointer-events-auto w-full"
445
+ )}
446
+ id={elementId}
447
+ style={finalStyle}
448
+ >
449
+ <View style={{ opacity: isCollapsedStack ? 0 : 1, transition: "opacity 400ms ease" }}>
450
+ {content}
451
+ </View>
452
+ </View>
453
+ )
454
+ }
455
+
456
+ return (
457
+ <View className={baseClasses} style={finalStyle} id={elementId}>
458
+ <View className="flex gap-3 items-center flex-1" style={{ opacity: isCollapsedStack ? 0 : 1, transition: "opacity 400ms ease" }}>
459
+ {TypeIcon && (
460
+ <TypeIcon className={cn("shrink-0", item.type === "loading" && "animate-spin")} color={iconColor} size={20} />
461
+ )}
462
+ <View className="flex flex-col gap-1 flex-1">
463
+ {item.title && <Text className="text-sm font-semibold leading-none" style={palette ? { color: palette.color } : undefined}>{item.title}</Text>}
464
+ {item.description && (
465
+ <Text className={cn("text-xs opacity-90 leading-normal", item.descriptionClassName)} style={palette ? { color: palette.color } : undefined}>
466
+ {item.description}
467
+ </Text>
468
+ )}
469
+ </View>
470
+ </View>
471
+
472
+ {(item.action || item.cancel) && (
473
+ <View className="flex flex-nowrap items-center gap-2 shrink-0" style={{ opacity: isCollapsedStack ? 0 : 1, transition: "opacity 400ms ease" }}>
474
+ {item.cancel && (
475
+ <View
476
+ className="text-xs font-medium opacity-70 active:opacity-100 whitespace-nowrap"
477
+ onClick={(e) => {
478
+ e.stopPropagation()
479
+ item.cancel?.onClick()
480
+ handleDismiss()
481
+ }}
482
+ >
483
+ {item.cancel.label}
484
+ </View>
485
+ )}
486
+ {item.action && (
487
+ <View
488
+ className="text-xs font-medium active:opacity-80 px-3 py-2 rounded-md bg-primary text-primary-foreground shadow hover:bg-primary hover:bg-opacity-90 whitespace-nowrap"
489
+ onClick={(e) => {
490
+ e.stopPropagation()
491
+ item.action?.onClick()
492
+ handleDismiss()
493
+ }}
494
+ >
495
+ {item.action.label}
496
+ </View>
497
+ )}
498
+ </View>
499
+ )}
500
+
501
+ {closeButton && (
502
+ <View
503
+ className="absolute right-2 top-2 rounded-md p-1 opacity-50 transition-opacity hover:opacity-100 focus:opacity-100 focus:outline-none focus:ring-2"
504
+ style={{ opacity: isCollapsedStack ? 0 : undefined, transition: "opacity 400ms ease" }}
505
+ onClick={(e) => {
506
+ e.stopPropagation()
507
+ handleDismiss()
508
+ }}
509
+ >
510
+ <X color={iconColor} size={16} />
511
+ </View>
512
+ )}
513
+ </View>
514
+ )
515
+ }
516
+
517
+ export { Toaster, toast }