@coze-arch/cli 0.0.1-alpha.de5a13 → 0.0.1-alpha.deaedf
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.
- package/README.md +1 -0
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +1 -1
- package/lib/__templates__/expo/client/components/Screen.tsx +2 -2
- package/lib/__templates__/expo/client/eslint.config.mjs +7 -0
- package/lib/__templates__/expo/client/metro.config.js +3 -0
- package/lib/__templates__/expo/client/scripts/install-missing-deps.js +10 -10
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/rule.js +112 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/tech.md +94 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/rule.js +120 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/tech.md +58 -0
- package/lib/__templates__/expo/pnpm-lock.yaml +8 -5
- package/lib/__templates__/expo/server/package.json +1 -1
- package/lib/__templates__/native-static/.coze +11 -0
- package/lib/__templates__/native-static/index.html +33 -0
- package/lib/__templates__/native-static/styles/main.css +136 -0
- package/lib/__templates__/native-static/template.config.js +22 -0
- package/lib/__templates__/nextjs/.babelrc +3 -0
- package/lib/__templates__/nextjs/README.md +5 -0
- package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
- package/lib/__templates__/nextjs/next.config.ts +1 -2
- package/lib/__templates__/nextjs/package.json +3 -6
- package/lib/__templates__/nextjs/pnpm-lock.yaml +1036 -10
- package/lib/__templates__/nextjs/scripts/build.sh +4 -1
- package/lib/__templates__/nextjs/scripts/dev.sh +8 -2
- package/lib/__templates__/nextjs/scripts/start.sh +7 -1
- package/lib/__templates__/nextjs/src/app/layout.tsx +1 -1
- package/lib/__templates__/nextjs/src/app/page.tsx +1 -2
- package/lib/__templates__/nextjs/src/server.ts +35 -0
- package/lib/__templates__/nextjs/tsconfig.json +1 -1
- package/lib/__templates__/nuxt-vue/.coze +12 -0
- package/lib/__templates__/nuxt-vue/README.md +73 -0
- package/lib/__templates__/nuxt-vue/_gitignore +24 -0
- package/lib/__templates__/nuxt-vue/_npmrc +23 -0
- package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
- package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
- package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
- package/lib/__templates__/nuxt-vue/nuxt.config.ts +116 -0
- package/lib/__templates__/nuxt-vue/package.json +35 -0
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
- package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
- package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
- package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
- package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
- package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
- package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
- package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
- package/lib/__templates__/nuxt-vue/template.config.js +87 -0
- package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +107 -37
- package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -1
- package/lib/__templates__/taro/README.md +138 -62
- package/lib/__templates__/taro/config/index.ts +105 -41
- package/lib/__templates__/taro/config/prod.ts +4 -5
- package/lib/__templates__/taro/eslint.config.mjs +82 -4
- package/lib/__templates__/taro/package.json +23 -7
- package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
- package/lib/__templates__/taro/pnpm-lock.yaml +1198 -214
- package/lib/__templates__/taro/server/package.json +3 -1
- package/lib/__templates__/taro/src/app.css +140 -47
- package/lib/__templates__/taro/src/app.tsx +9 -0
- package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
- package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
- package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
- package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
- package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
- package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
- package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
- package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
- package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
- package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
- package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
- package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
- package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
- package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
- package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
- package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
- package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
- package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
- package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
- package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
- package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
- package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
- package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
- package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
- package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
- package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
- package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
- package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
- package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
- package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
- package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
- package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
- package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
- package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
- package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
- package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
- package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
- package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
- package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
- package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
- package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
- package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
- package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
- package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
- package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
- package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
- package/lib/__templates__/taro/src/lib/measure.ts +115 -0
- package/lib/__templates__/taro/src/lib/platform.ts +12 -0
- package/lib/__templates__/taro/src/lib/utils.ts +6 -0
- package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +238 -0
- package/lib/__templates__/taro/src/presets/h5-styles.ts +220 -0
- package/lib/__templates__/taro/src/presets/index.tsx +18 -0
- package/lib/__templates__/templates.json +43 -0
- package/lib/__templates__/vite/README.md +190 -11
- package/lib/__templates__/vite/_gitignore +1 -0
- package/lib/__templates__/vite/eslint.config.mjs +6 -1
- package/lib/__templates__/vite/package.json +14 -5
- package/lib/__templates__/vite/pnpm-lock.yaml +768 -24
- package/lib/__templates__/vite/scripts/build.sh +4 -1
- package/lib/__templates__/vite/scripts/dev.sh +9 -2
- package/lib/__templates__/vite/scripts/start.sh +9 -3
- package/lib/__templates__/vite/server/routes/index.ts +31 -0
- package/lib/__templates__/vite/server/server.ts +65 -0
- package/lib/__templates__/vite/server/vite.ts +67 -0
- package/lib/__templates__/vite/tsconfig.json +4 -3
- package/lib/__templates__/vite/vite.config.ts +5 -0
- package/lib/cli.js +124 -103
- package/package.json +7 -3
- package/lib/__templates__/taro/src/app.ts +0 -14
- package/lib/__templates__/taro/src/utils/h5-styles.ts +0 -22
- package/lib/__templates__/taro/src/utils/wx-debug.ts +0 -23
|
@@ -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 }
|