@coze-arch/cli 0.0.1-alpha.db1c06 → 0.0.1-alpha.dcc485
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/_npmrc +1 -0
- 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/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__/nextjs/AGENTS.md +54 -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 +2 -5
- package/lib/__templates__/nextjs/pnpm-lock.yaml +1028 -5
- 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/AGENTS.md +42 -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/README.md +57 -45
- package/lib/__templates__/taro/config/index.ts +106 -41
- package/lib/__templates__/taro/config/prod.ts +4 -5
- package/lib/__templates__/taro/eslint.config.mjs +62 -6
- package/lib/__templates__/taro/package.json +19 -4
- package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
- package/lib/__templates__/taro/pnpm-lock.yaml +912 -206
- package/lib/__templates__/taro/src/app.css +140 -36
- 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 +97 -30
- package/lib/__templates__/taro/src/presets/h5-styles.ts +192 -5
- package/lib/__templates__/taro/src/presets/index.tsx +4 -4
- package/lib/__templates__/templates.json +32 -0
- package/lib/__templates__/vite/AGENTS.md +41 -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 +10 -3
- package/lib/__templates__/vite/pnpm-lock.yaml +755 -15
- 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 +4 -0
- package/lib/cli.js +99 -92
- package/package.json +6 -3
- package/lib/__templates__/taro/src/presets/wx-debug.ts +0 -23
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { View, ScrollView } from "@tarojs/components"
|
|
3
|
+
import { Check, ChevronRight } from "lucide-react-taro"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import { isH5 } from "@/lib/platform"
|
|
6
|
+
import { computePosition, getRectById } from "@/lib/measure"
|
|
7
|
+
import { Portal } from "@/components/ui/portal"
|
|
8
|
+
|
|
9
|
+
const MenubarContext = React.createContext<{
|
|
10
|
+
openMenu?: string
|
|
11
|
+
setOpenMenu?: (id: string | undefined) => void
|
|
12
|
+
} | null>(null)
|
|
13
|
+
|
|
14
|
+
const Menubar = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof View>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
17
|
+
>(({ className, ...props }, ref) => {
|
|
18
|
+
const [openMenu, setOpenMenu] = React.useState<string | undefined>()
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<MenubarContext.Provider value={{ openMenu, setOpenMenu }}>
|
|
22
|
+
<View
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn(
|
|
25
|
+
"flex h-10 items-center space-x-1 rounded-md border border-border bg-background p-1",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
</MenubarContext.Provider>
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
Menubar.displayName = "Menubar"
|
|
34
|
+
|
|
35
|
+
const MenubarMenuContext = React.createContext<{
|
|
36
|
+
id: string
|
|
37
|
+
open: boolean
|
|
38
|
+
onOpenChange: (open: boolean) => void
|
|
39
|
+
triggerId: string
|
|
40
|
+
} | null>(null)
|
|
41
|
+
|
|
42
|
+
let menubarMenuIdCounter = 0
|
|
43
|
+
|
|
44
|
+
const MenubarMenu = ({ children }: { children: React.ReactNode }) => {
|
|
45
|
+
const id = React.useMemo(() => `menubar-menu-${menubarMenuIdCounter++}`, [])
|
|
46
|
+
const triggerId = React.useMemo(() => `${id}-trigger`, [id])
|
|
47
|
+
const context = React.useContext(MenubarContext)
|
|
48
|
+
|
|
49
|
+
const open = context?.openMenu === id
|
|
50
|
+
const onOpenChange = (isOpen: boolean) => {
|
|
51
|
+
if (isOpen) {
|
|
52
|
+
context?.setOpenMenu?.(id)
|
|
53
|
+
} else {
|
|
54
|
+
context?.setOpenMenu?.(undefined)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<MenubarMenuContext.Provider value={{ id, open, onOpenChange, triggerId }}>
|
|
60
|
+
{children}
|
|
61
|
+
</MenubarMenuContext.Provider>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const MenubarTrigger = React.forwardRef<
|
|
66
|
+
React.ElementRef<typeof View>,
|
|
67
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
68
|
+
>(({ className, ...props }, ref) => {
|
|
69
|
+
const context = React.useContext(MenubarMenuContext)
|
|
70
|
+
return (
|
|
71
|
+
<View
|
|
72
|
+
ref={ref}
|
|
73
|
+
id={context?.triggerId}
|
|
74
|
+
data-slot="menubar-trigger"
|
|
75
|
+
className={cn(
|
|
76
|
+
"flex cursor-default select-none items-center rounded-sm px-3 py-2 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
onClick={() => context?.onOpenChange(!context.open)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
MenubarTrigger.displayName = "MenubarTrigger"
|
|
85
|
+
|
|
86
|
+
const MenubarPortal = ({ children }: { children: React.ReactNode }) => {
|
|
87
|
+
return <>{children}</>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const MenubarContent = React.forwardRef<
|
|
91
|
+
React.ElementRef<typeof View>,
|
|
92
|
+
React.ComponentPropsWithoutRef<typeof View> & {
|
|
93
|
+
align?: "start" | "center" | "end"
|
|
94
|
+
side?: "top" | "bottom" | "left" | "right"
|
|
95
|
+
sideOffset?: number
|
|
96
|
+
}
|
|
97
|
+
>(({ className, align = "start", side = "bottom", sideOffset = 4, children, ...props }, ref) => {
|
|
98
|
+
const context = React.useContext(MenubarMenuContext)
|
|
99
|
+
const contentId = React.useRef(`menubar-content-${Math.random().toString(36).slice(2, 10)}`)
|
|
100
|
+
const [position, setPosition] = React.useState<{ left: number; top: number } | null>(null)
|
|
101
|
+
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
if (!context?.open) {
|
|
104
|
+
setPosition(null)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let cancelled = false
|
|
109
|
+
|
|
110
|
+
const compute = async () => {
|
|
111
|
+
if (!context?.triggerId) return
|
|
112
|
+
const [triggerRect, contentRect] = await Promise.all([
|
|
113
|
+
getRectById(context.triggerId),
|
|
114
|
+
getRectById(contentId.current),
|
|
115
|
+
])
|
|
116
|
+
|
|
117
|
+
if (cancelled) return
|
|
118
|
+
if (!triggerRect?.width || !contentRect?.width) return
|
|
119
|
+
|
|
120
|
+
setPosition(
|
|
121
|
+
computePosition({
|
|
122
|
+
triggerRect,
|
|
123
|
+
contentRect,
|
|
124
|
+
align,
|
|
125
|
+
side,
|
|
126
|
+
sideOffset,
|
|
127
|
+
})
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const raf = (() => {
|
|
132
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
133
|
+
return requestAnimationFrame(() => compute())
|
|
134
|
+
}
|
|
135
|
+
return setTimeout(() => compute(), 0) as unknown as number
|
|
136
|
+
})()
|
|
137
|
+
|
|
138
|
+
return () => {
|
|
139
|
+
cancelled = true
|
|
140
|
+
if (typeof cancelAnimationFrame !== "undefined") {
|
|
141
|
+
cancelAnimationFrame(raf)
|
|
142
|
+
} else {
|
|
143
|
+
clearTimeout(raf)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}, [align, context?.open, context?.triggerId, side, sideOffset])
|
|
147
|
+
|
|
148
|
+
React.useEffect(() => {
|
|
149
|
+
if (!context?.open) return
|
|
150
|
+
if (!isH5() || typeof document === "undefined") return
|
|
151
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
152
|
+
if (e.key === "Escape") {
|
|
153
|
+
context?.onOpenChange(false)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
document.addEventListener("keydown", onKeyDown)
|
|
157
|
+
return () => document.removeEventListener("keydown", onKeyDown)
|
|
158
|
+
}, [context?.open])
|
|
159
|
+
|
|
160
|
+
if (!context?.open) return null
|
|
161
|
+
|
|
162
|
+
const baseClassName =
|
|
163
|
+
"fixed z-50 min-w-32 overflow-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground ring-opacity-10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
|
|
164
|
+
|
|
165
|
+
const contentStyle = position
|
|
166
|
+
? ({ left: position.left, top: position.top } as React.CSSProperties)
|
|
167
|
+
: ({
|
|
168
|
+
left: 0,
|
|
169
|
+
top: 0,
|
|
170
|
+
opacity: 0,
|
|
171
|
+
pointerEvents: "none",
|
|
172
|
+
} as React.CSSProperties)
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<Portal>
|
|
176
|
+
<View
|
|
177
|
+
className="fixed inset-0 z-50 bg-transparent"
|
|
178
|
+
onClick={() => context.onOpenChange(false)}
|
|
179
|
+
/>
|
|
180
|
+
<View
|
|
181
|
+
ref={ref}
|
|
182
|
+
id={contentId.current}
|
|
183
|
+
data-slot="menubar-content"
|
|
184
|
+
data-state="open"
|
|
185
|
+
data-side={side}
|
|
186
|
+
className={cn(
|
|
187
|
+
baseClassName,
|
|
188
|
+
className
|
|
189
|
+
)}
|
|
190
|
+
style={contentStyle}
|
|
191
|
+
onClick={(e) => e.stopPropagation()}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
<ScrollView scrollY className="max-h-[50vh] overflow-x-hidden overflow-y-auto">
|
|
195
|
+
{children}
|
|
196
|
+
</ScrollView>
|
|
197
|
+
</View>
|
|
198
|
+
</Portal>
|
|
199
|
+
)
|
|
200
|
+
})
|
|
201
|
+
MenubarContent.displayName = "MenubarContent"
|
|
202
|
+
|
|
203
|
+
const MenubarItem = React.forwardRef<
|
|
204
|
+
React.ElementRef<typeof View>,
|
|
205
|
+
React.ComponentPropsWithoutRef<typeof View> & {
|
|
206
|
+
inset?: boolean
|
|
207
|
+
variant?: "default" | "destructive"
|
|
208
|
+
disabled?: boolean
|
|
209
|
+
closeOnSelect?: boolean
|
|
210
|
+
}
|
|
211
|
+
>(({ className, inset, variant = "default", disabled, closeOnSelect = true, children, onClick, ...props }, ref) => {
|
|
212
|
+
const context = React.useContext(MenubarMenuContext)
|
|
213
|
+
return (
|
|
214
|
+
<View
|
|
215
|
+
ref={ref}
|
|
216
|
+
data-slot="menubar-item"
|
|
217
|
+
data-inset={inset ? "" : undefined}
|
|
218
|
+
data-variant={variant}
|
|
219
|
+
data-disabled={disabled ? "" : undefined}
|
|
220
|
+
className={cn(
|
|
221
|
+
"relative flex cursor-default select-none items-center gap-1.5 rounded-md px-2 py-1 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
222
|
+
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive data-[variant=destructive]:focus:bg-opacity-10 data-[variant=destructive]:focus:text-destructive",
|
|
223
|
+
inset && "pl-7",
|
|
224
|
+
disabled && "pointer-events-none opacity-50",
|
|
225
|
+
className
|
|
226
|
+
)}
|
|
227
|
+
onClick={(e) => {
|
|
228
|
+
if (disabled) return
|
|
229
|
+
onClick?.(e)
|
|
230
|
+
if (closeOnSelect) context?.onOpenChange(false)
|
|
231
|
+
}}
|
|
232
|
+
{...props}
|
|
233
|
+
>
|
|
234
|
+
{children}
|
|
235
|
+
</View>
|
|
236
|
+
)
|
|
237
|
+
})
|
|
238
|
+
MenubarItem.displayName = "MenubarItem"
|
|
239
|
+
|
|
240
|
+
const MenubarCheckboxItem = React.forwardRef<
|
|
241
|
+
React.ElementRef<typeof View>,
|
|
242
|
+
React.ComponentPropsWithoutRef<typeof View> & {
|
|
243
|
+
checked?: boolean
|
|
244
|
+
inset?: boolean
|
|
245
|
+
disabled?: boolean
|
|
246
|
+
closeOnSelect?: boolean
|
|
247
|
+
}
|
|
248
|
+
>(({ className, children, checked, inset, disabled, closeOnSelect = false, onClick, ...props }, ref) => {
|
|
249
|
+
const context = React.useContext(MenubarMenuContext)
|
|
250
|
+
return (
|
|
251
|
+
<View
|
|
252
|
+
ref={ref}
|
|
253
|
+
data-slot="menubar-checkbox-item"
|
|
254
|
+
data-inset={inset ? "" : undefined}
|
|
255
|
+
data-disabled={disabled ? "" : undefined}
|
|
256
|
+
data-state={checked ? "checked" : "unchecked"}
|
|
257
|
+
className={cn(
|
|
258
|
+
"relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-8 pl-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
259
|
+
inset && "pl-7",
|
|
260
|
+
disabled && "pointer-events-none opacity-50",
|
|
261
|
+
className
|
|
262
|
+
)}
|
|
263
|
+
onClick={(e) => {
|
|
264
|
+
if (disabled) return
|
|
265
|
+
onClick?.(e)
|
|
266
|
+
if (closeOnSelect) context?.onOpenChange(false)
|
|
267
|
+
}}
|
|
268
|
+
{...props}
|
|
269
|
+
>
|
|
270
|
+
<View className="pointer-events-none absolute right-2 flex items-center justify-center">
|
|
271
|
+
{checked && <Check size={16} />}
|
|
272
|
+
</View>
|
|
273
|
+
{children}
|
|
274
|
+
</View>
|
|
275
|
+
)
|
|
276
|
+
})
|
|
277
|
+
MenubarCheckboxItem.displayName = "MenubarCheckboxItem"
|
|
278
|
+
|
|
279
|
+
const MenubarRadioGroupContext = React.createContext<{
|
|
280
|
+
value?: string
|
|
281
|
+
onValueChange?: (value: string) => void
|
|
282
|
+
} | null>(null)
|
|
283
|
+
|
|
284
|
+
interface MenubarRadioGroupProps extends React.ComponentPropsWithoutRef<typeof View> {
|
|
285
|
+
value?: string
|
|
286
|
+
defaultValue?: string
|
|
287
|
+
onValueChange?: (value: string) => void
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const MenubarRadioGroup = React.forwardRef<
|
|
291
|
+
React.ElementRef<typeof View>,
|
|
292
|
+
MenubarRadioGroupProps
|
|
293
|
+
>(({ value: valueProp, defaultValue, onValueChange, ...props }, ref) => {
|
|
294
|
+
const [valueState, setValueState] = React.useState<string | undefined>(defaultValue)
|
|
295
|
+
const value = valueProp !== undefined ? valueProp : valueState
|
|
296
|
+
|
|
297
|
+
const handleValueChange = (next: string) => {
|
|
298
|
+
if (valueProp === undefined) {
|
|
299
|
+
setValueState(next)
|
|
300
|
+
}
|
|
301
|
+
onValueChange?.(next)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<MenubarRadioGroupContext.Provider value={{ value, onValueChange: handleValueChange }}>
|
|
306
|
+
<View ref={ref} {...props} />
|
|
307
|
+
</MenubarRadioGroupContext.Provider>
|
|
308
|
+
)
|
|
309
|
+
})
|
|
310
|
+
MenubarRadioGroup.displayName = "MenubarRadioGroup"
|
|
311
|
+
|
|
312
|
+
const MenubarRadioItem = React.forwardRef<
|
|
313
|
+
React.ElementRef<typeof View>,
|
|
314
|
+
React.ComponentPropsWithoutRef<typeof View> & {
|
|
315
|
+
value: string
|
|
316
|
+
checked?: boolean
|
|
317
|
+
inset?: boolean
|
|
318
|
+
disabled?: boolean
|
|
319
|
+
closeOnSelect?: boolean
|
|
320
|
+
}
|
|
321
|
+
>(({ className, children, value, checked: checkedProp, inset, disabled, closeOnSelect = false, onClick, ...props }, ref) => {
|
|
322
|
+
const context = React.useContext(MenubarMenuContext)
|
|
323
|
+
const group = React.useContext(MenubarRadioGroupContext)
|
|
324
|
+
const checked = checkedProp !== undefined ? checkedProp : group?.value === value
|
|
325
|
+
return (
|
|
326
|
+
<View
|
|
327
|
+
ref={ref}
|
|
328
|
+
data-slot="menubar-radio-item"
|
|
329
|
+
data-inset={inset ? "" : undefined}
|
|
330
|
+
data-disabled={disabled ? "" : undefined}
|
|
331
|
+
data-state={checked ? "checked" : "unchecked"}
|
|
332
|
+
className={cn(
|
|
333
|
+
"relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-8 pl-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
334
|
+
inset && "pl-7",
|
|
335
|
+
disabled && "pointer-events-none opacity-50",
|
|
336
|
+
className
|
|
337
|
+
)}
|
|
338
|
+
onClick={(e) => {
|
|
339
|
+
if (disabled) return
|
|
340
|
+
group?.onValueChange?.(value)
|
|
341
|
+
onClick?.(e)
|
|
342
|
+
if (closeOnSelect) context?.onOpenChange(false)
|
|
343
|
+
}}
|
|
344
|
+
{...props}
|
|
345
|
+
>
|
|
346
|
+
<View className="pointer-events-none absolute right-2 flex items-center justify-center">
|
|
347
|
+
{checked && <Check size={16} />}
|
|
348
|
+
</View>
|
|
349
|
+
{children}
|
|
350
|
+
</View>
|
|
351
|
+
)
|
|
352
|
+
})
|
|
353
|
+
MenubarRadioItem.displayName = "MenubarRadioItem"
|
|
354
|
+
|
|
355
|
+
const MenubarLabel = React.forwardRef<
|
|
356
|
+
React.ElementRef<typeof View>,
|
|
357
|
+
React.ComponentPropsWithoutRef<typeof View> & {
|
|
358
|
+
inset?: boolean
|
|
359
|
+
}
|
|
360
|
+
>(({ className, inset, ...props }, ref) => (
|
|
361
|
+
<View
|
|
362
|
+
ref={ref}
|
|
363
|
+
data-slot="menubar-label"
|
|
364
|
+
data-inset={inset ? "" : undefined}
|
|
365
|
+
className={cn(
|
|
366
|
+
"px-2 py-1 text-xs font-medium text-muted-foreground",
|
|
367
|
+
inset && "pl-7",
|
|
368
|
+
className
|
|
369
|
+
)}
|
|
370
|
+
{...props}
|
|
371
|
+
/>
|
|
372
|
+
))
|
|
373
|
+
MenubarLabel.displayName = "MenubarLabel"
|
|
374
|
+
|
|
375
|
+
const MenubarSeparator = React.forwardRef<
|
|
376
|
+
React.ElementRef<typeof View>,
|
|
377
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
378
|
+
>(({ className, ...props }, ref) => (
|
|
379
|
+
<View
|
|
380
|
+
ref={ref}
|
|
381
|
+
data-slot="menubar-separator"
|
|
382
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
383
|
+
{...props}
|
|
384
|
+
/>
|
|
385
|
+
))
|
|
386
|
+
MenubarSeparator.displayName = "MenubarSeparator"
|
|
387
|
+
|
|
388
|
+
const MenubarShortcut = ({
|
|
389
|
+
className,
|
|
390
|
+
...props
|
|
391
|
+
}: React.ComponentPropsWithoutRef<typeof View>) => {
|
|
392
|
+
return (
|
|
393
|
+
<View
|
|
394
|
+
data-slot="menubar-shortcut"
|
|
395
|
+
className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
|
|
396
|
+
{...props}
|
|
397
|
+
/>
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
MenubarShortcut.displayName = "MenubarShortcut"
|
|
401
|
+
|
|
402
|
+
const MenubarGroup = React.forwardRef<
|
|
403
|
+
React.ElementRef<typeof View>,
|
|
404
|
+
React.ComponentPropsWithoutRef<typeof View>
|
|
405
|
+
>(({ className, ...props }, ref) => (
|
|
406
|
+
<View ref={ref} data-slot="menubar-group" className={className} {...props} />
|
|
407
|
+
))
|
|
408
|
+
MenubarGroup.displayName = "MenubarGroup"
|
|
409
|
+
|
|
410
|
+
const MenubarSubContext = React.createContext<{
|
|
411
|
+
open?: boolean
|
|
412
|
+
onOpenChange?: (open: boolean) => void
|
|
413
|
+
triggerId: string
|
|
414
|
+
} | null>(null)
|
|
415
|
+
|
|
416
|
+
interface MenubarSubProps {
|
|
417
|
+
children: React.ReactNode
|
|
418
|
+
open?: boolean
|
|
419
|
+
defaultOpen?: boolean
|
|
420
|
+
onOpenChange?: (open: boolean) => void
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const MenubarSub = ({ open: openProp, defaultOpen, onOpenChange, children }: MenubarSubProps) => {
|
|
424
|
+
const parent = React.useContext(MenubarMenuContext)
|
|
425
|
+
const baseIdRef = React.useRef(`menubar-sub-${Math.random().toString(36).slice(2, 10)}`)
|
|
426
|
+
const [openState, setOpenState] = React.useState(defaultOpen || false)
|
|
427
|
+
const open = openProp !== undefined ? openProp : openState
|
|
428
|
+
|
|
429
|
+
const handleOpenChange = (newOpen: boolean) => {
|
|
430
|
+
if (openProp === undefined) {
|
|
431
|
+
setOpenState(newOpen)
|
|
432
|
+
}
|
|
433
|
+
onOpenChange?.(newOpen)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
React.useEffect(() => {
|
|
437
|
+
if (parent?.open === false && open) {
|
|
438
|
+
handleOpenChange(false)
|
|
439
|
+
}
|
|
440
|
+
}, [open, parent?.open])
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<MenubarSubContext.Provider value={{ open, onOpenChange: handleOpenChange, triggerId: baseIdRef.current }}>
|
|
444
|
+
{children}
|
|
445
|
+
</MenubarSubContext.Provider>
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const MenubarSubTrigger = React.forwardRef<
|
|
450
|
+
React.ElementRef<typeof View>,
|
|
451
|
+
React.ComponentPropsWithoutRef<typeof View> & { inset?: boolean; disabled?: boolean }
|
|
452
|
+
>(({ className, inset, disabled, children, onClick, ...props }, ref) => {
|
|
453
|
+
const subContext = React.useContext(MenubarSubContext)
|
|
454
|
+
return (
|
|
455
|
+
<View
|
|
456
|
+
{...props}
|
|
457
|
+
ref={ref}
|
|
458
|
+
id={subContext?.triggerId}
|
|
459
|
+
data-slot="menubar-sub-trigger"
|
|
460
|
+
data-inset={inset ? "" : undefined}
|
|
461
|
+
data-disabled={disabled ? "" : undefined}
|
|
462
|
+
data-state={subContext?.open ? "open" : "closed"}
|
|
463
|
+
className={cn(
|
|
464
|
+
"relative flex cursor-default select-none items-center gap-1.5 rounded-md px-2 py-1 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
465
|
+
inset && "pl-7",
|
|
466
|
+
disabled && "pointer-events-none opacity-50",
|
|
467
|
+
className
|
|
468
|
+
)}
|
|
469
|
+
onClick={(e) => {
|
|
470
|
+
e.stopPropagation()
|
|
471
|
+
if (disabled) return
|
|
472
|
+
subContext?.onOpenChange?.(!subContext.open)
|
|
473
|
+
onClick?.(e)
|
|
474
|
+
}}
|
|
475
|
+
>
|
|
476
|
+
{children}
|
|
477
|
+
<ChevronRight className="ml-auto opacity-50" size={16} />
|
|
478
|
+
</View>
|
|
479
|
+
)
|
|
480
|
+
})
|
|
481
|
+
MenubarSubTrigger.displayName = "MenubarSubTrigger"
|
|
482
|
+
|
|
483
|
+
const MenubarSubContent = React.forwardRef<
|
|
484
|
+
React.ElementRef<typeof View>,
|
|
485
|
+
React.ComponentPropsWithoutRef<typeof View> & {
|
|
486
|
+
align?: "start" | "center" | "end"
|
|
487
|
+
side?: "top" | "bottom" | "left" | "right"
|
|
488
|
+
sideOffset?: number
|
|
489
|
+
}
|
|
490
|
+
>(({ className, align = "start", side = "right", sideOffset = 4, children, ...props }, ref) => {
|
|
491
|
+
const parent = React.useContext(MenubarMenuContext)
|
|
492
|
+
const subContext = React.useContext(MenubarSubContext)
|
|
493
|
+
const contentId = React.useRef(`menubar-sub-content-${Math.random().toString(36).slice(2, 10)}`)
|
|
494
|
+
const [position, setPosition] = React.useState<{ left: number; top: number } | null>(null)
|
|
495
|
+
|
|
496
|
+
React.useEffect(() => {
|
|
497
|
+
if (!parent?.open || !subContext?.open) {
|
|
498
|
+
setPosition(null)
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
let cancelled = false
|
|
503
|
+
|
|
504
|
+
const compute = async () => {
|
|
505
|
+
if (!subContext?.triggerId) return
|
|
506
|
+
const [triggerRect, contentRect] = await Promise.all([
|
|
507
|
+
getRectById(subContext.triggerId),
|
|
508
|
+
getRectById(contentId.current),
|
|
509
|
+
])
|
|
510
|
+
|
|
511
|
+
if (cancelled) return
|
|
512
|
+
if (!triggerRect?.width || !contentRect?.width) return
|
|
513
|
+
|
|
514
|
+
setPosition(
|
|
515
|
+
computePosition({
|
|
516
|
+
triggerRect,
|
|
517
|
+
contentRect,
|
|
518
|
+
align,
|
|
519
|
+
side,
|
|
520
|
+
sideOffset,
|
|
521
|
+
})
|
|
522
|
+
)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const raf = (() => {
|
|
526
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
527
|
+
return requestAnimationFrame(() => compute())
|
|
528
|
+
}
|
|
529
|
+
return setTimeout(() => compute(), 0) as unknown as number
|
|
530
|
+
})()
|
|
531
|
+
|
|
532
|
+
return () => {
|
|
533
|
+
cancelled = true
|
|
534
|
+
if (typeof cancelAnimationFrame !== "undefined") {
|
|
535
|
+
cancelAnimationFrame(raf)
|
|
536
|
+
} else {
|
|
537
|
+
clearTimeout(raf)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}, [align, parent?.open, side, sideOffset, subContext?.open, subContext?.triggerId])
|
|
541
|
+
|
|
542
|
+
if (!parent?.open || !subContext?.open) return null
|
|
543
|
+
|
|
544
|
+
const baseClassName =
|
|
545
|
+
"fixed z-50 min-w-[96px] overflow-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground ring-opacity-10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
|
|
546
|
+
|
|
547
|
+
const contentStyle = position
|
|
548
|
+
? ({ left: position.left, top: position.top } as React.CSSProperties)
|
|
549
|
+
: ({
|
|
550
|
+
left: 0,
|
|
551
|
+
top: 0,
|
|
552
|
+
opacity: 0,
|
|
553
|
+
pointerEvents: "none",
|
|
554
|
+
} as React.CSSProperties)
|
|
555
|
+
|
|
556
|
+
return (
|
|
557
|
+
<Portal>
|
|
558
|
+
<View
|
|
559
|
+
{...props}
|
|
560
|
+
ref={ref}
|
|
561
|
+
id={contentId.current}
|
|
562
|
+
data-slot="menubar-sub-content"
|
|
563
|
+
data-state="open"
|
|
564
|
+
data-side={side}
|
|
565
|
+
className={cn(baseClassName, className)}
|
|
566
|
+
style={contentStyle}
|
|
567
|
+
onClick={(e) => e.stopPropagation()}
|
|
568
|
+
>
|
|
569
|
+
<ScrollView scrollY className="max-h-[50vh] overflow-x-hidden overflow-y-auto">
|
|
570
|
+
{children}
|
|
571
|
+
</ScrollView>
|
|
572
|
+
</View>
|
|
573
|
+
</Portal>
|
|
574
|
+
)
|
|
575
|
+
})
|
|
576
|
+
MenubarSubContent.displayName = "MenubarSubContent"
|
|
577
|
+
|
|
578
|
+
export {
|
|
579
|
+
Menubar,
|
|
580
|
+
MenubarMenu,
|
|
581
|
+
MenubarTrigger,
|
|
582
|
+
MenubarContent,
|
|
583
|
+
MenubarItem,
|
|
584
|
+
MenubarSeparator,
|
|
585
|
+
MenubarLabel,
|
|
586
|
+
MenubarCheckboxItem,
|
|
587
|
+
MenubarRadioGroup,
|
|
588
|
+
MenubarRadioItem,
|
|
589
|
+
MenubarPortal,
|
|
590
|
+
MenubarSubContent,
|
|
591
|
+
MenubarSubTrigger,
|
|
592
|
+
MenubarGroup,
|
|
593
|
+
MenubarSub,
|
|
594
|
+
MenubarShortcut,
|
|
595
|
+
}
|