@srcroot/ui 0.0.55 → 0.0.58
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 +151 -151
- package/dist/index.d.ts +0 -0
- package/dist/index.js +120 -93
- package/package.json +7 -2
- package/src/registry/analytics/google-analytics.tsx +36 -39
- package/src/registry/analytics/google-tag-manager.tsx +62 -65
- package/src/registry/analytics/meta-pixel.tsx +44 -47
- package/src/registry/analytics/microsoft-clarity.tsx +31 -34
- package/src/registry/analytics/tiktok-pixel.tsx +34 -37
- package/src/registry/lib/utils.ts +0 -0
- package/src/registry/themes/v3/blue.css +157 -157
- package/src/registry/themes/v3/glass.css +153 -153
- package/src/registry/themes/v3/gray.css +157 -157
- package/src/registry/themes/v3/green.css +157 -157
- package/src/registry/themes/v3/neutral.css +157 -157
- package/src/registry/themes/v3/orange.css +157 -157
- package/src/registry/themes/v3/rose.css +157 -157
- package/src/registry/themes/v3/slate.css +157 -157
- package/src/registry/themes/v3/stone.css +157 -157
- package/src/registry/themes/v3/violet.css +186 -186
- package/src/registry/themes/v3/zinc.css +157 -157
- package/src/registry/themes/v4/blue.css +184 -184
- package/src/registry/themes/v4/glass.css +180 -180
- package/src/registry/themes/v4/gray.css +184 -184
- package/src/registry/themes/v4/green.css +184 -184
- package/src/registry/themes/v4/neutral.css +184 -184
- package/src/registry/themes/v4/orange.css +184 -184
- package/src/registry/themes/v4/rose.css +184 -184
- package/src/registry/themes/v4/slate.css +184 -184
- package/src/registry/themes/v4/stone.css +184 -184
- package/src/registry/themes/v4/violet.css +184 -184
- package/src/registry/themes/v4/zinc.css +184 -184
- package/src/registry/ui/accordion.tsx +164 -165
- package/src/registry/ui/alert-dialog.tsx +213 -214
- package/src/registry/ui/alert.tsx +73 -76
- package/src/registry/ui/aspect-ratio.tsx +44 -47
- package/src/registry/ui/avatar.tsx +96 -97
- package/src/registry/ui/badge.tsx +52 -55
- package/src/registry/ui/breadcrumb.tsx +147 -150
- package/src/registry/ui/button-group.tsx +64 -67
- package/src/registry/ui/button.tsx +71 -72
- package/src/registry/ui/calendar.tsx +514 -515
- package/src/registry/ui/card.tsx +88 -91
- package/src/registry/ui/carousel.tsx +214 -214
- package/src/registry/ui/chart.tsx +373 -373
- package/src/registry/ui/chatbot.tsx +86 -13
- package/src/registry/ui/checkbox.tsx +93 -94
- package/src/registry/ui/collapsible.tsx +107 -108
- package/src/registry/ui/combobox.tsx +171 -171
- package/src/registry/ui/command.tsx +300 -300
- package/src/registry/ui/container.tsx +44 -47
- package/src/registry/ui/context-menu.tsx +221 -221
- package/src/registry/ui/date-picker.tsx +228 -228
- package/src/registry/ui/dialog.tsx +269 -270
- package/src/registry/ui/drawer.tsx +10 -4
- package/src/registry/ui/dropdown-menu.tsx +529 -530
- package/src/registry/ui/empty-state.tsx +0 -2
- package/src/registry/ui/file-upload.tsx +0 -0
- package/src/registry/ui/floating-dock.tsx +0 -0
- package/src/registry/ui/form-field.tsx +91 -94
- package/src/registry/ui/google-analytics.tsx +38 -0
- package/src/registry/ui/google-tag-manager.tsx +64 -0
- package/src/registry/ui/hover-card.tsx +223 -223
- package/src/registry/ui/image.tsx +144 -147
- package/src/registry/ui/input-group.tsx +82 -85
- package/src/registry/ui/input.tsx +125 -125
- package/src/registry/ui/kbd.tsx +60 -63
- package/src/registry/ui/label.tsx +36 -37
- package/src/registry/ui/loading-spinner.tsx +108 -111
- package/src/registry/ui/map.tsx +0 -0
- package/src/registry/ui/marquee.tsx +2 -0
- package/src/registry/ui/menubar.tsx +246 -246
- package/src/registry/ui/meta-pixel.tsx +46 -0
- package/src/registry/ui/microsoft-clarity.tsx +33 -0
- package/src/registry/ui/native-select.tsx +49 -52
- package/src/registry/ui/otp-input.tsx +163 -155
- package/src/registry/ui/pagination.tsx +149 -152
- package/src/registry/ui/patterns.tsx +28 -0
- package/src/registry/ui/popover.tsx +226 -227
- package/src/registry/ui/progress.tsx +51 -52
- package/src/registry/ui/radio.tsx +99 -102
- package/src/registry/ui/resizable.tsx +314 -314
- package/src/registry/ui/scroll-animation.tsx +45 -0
- package/src/registry/ui/scroll-area.tsx +121 -122
- package/src/registry/ui/scroll-to-top.tsx +0 -0
- package/src/registry/ui/search.tsx +162 -150
- package/src/registry/ui/select.tsx +292 -293
- package/src/registry/ui/separator.tsx +46 -47
- package/src/registry/ui/sheet.tsx +6 -3
- package/src/registry/ui/sidebar.tsx +628 -628
- package/src/registry/ui/skeleton.tsx +26 -29
- package/src/registry/ui/slider.tsx +196 -197
- package/src/registry/ui/slot.tsx +69 -72
- package/src/registry/ui/star-rating.tsx +146 -134
- package/src/registry/ui/switch.tsx +72 -73
- package/src/registry/ui/table-of-contents.tsx +96 -96
- package/src/registry/ui/table.tsx +138 -139
- package/src/registry/ui/tabs.tsx +124 -125
- package/src/registry/ui/text.tsx +61 -64
- package/src/registry/ui/textarea.tsx +41 -42
- package/src/registry/ui/theme-switcher.tsx +66 -66
- package/src/registry/ui/tiktok-pixel.tsx +36 -0
- package/src/registry/ui/toast.tsx +97 -98
- package/src/registry/ui/toggle-group.tsx +129 -129
- package/src/registry/ui/toggle.tsx +72 -72
- package/src/registry/ui/tooltip.tsx +143 -144
- package/src/registry/ui/whatsapp.tsx +0 -0
|
@@ -1,246 +1,246 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
// Menubar Context
|
|
7
|
-
interface MenubarContextValue {
|
|
8
|
-
activeMenu: string | null
|
|
9
|
-
setActiveMenu: (menu: string | null) => void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const MenubarContext = React.createContext<MenubarContextValue | null>(null)
|
|
13
|
-
|
|
14
|
-
function useMenubar() {
|
|
15
|
-
const context = React.useContext(MenubarContext)
|
|
16
|
-
if (!context) {
|
|
17
|
-
throw new Error("useMenubar must be used within a Menubar")
|
|
18
|
-
}
|
|
19
|
-
return context
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// MenubarMenu Context
|
|
23
|
-
interface MenubarMenuContextValue {
|
|
24
|
-
menuId: string
|
|
25
|
-
triggerRef: React.RefObject<HTMLButtonElement | null>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const MenubarMenuContext = React.createContext<MenubarMenuContextValue | null>(null)
|
|
29
|
-
|
|
30
|
-
function useMenubarMenu() {
|
|
31
|
-
const context = React.useContext(MenubarMenuContext)
|
|
32
|
-
if (!context) {
|
|
33
|
-
throw new Error("useMenubarMenu must be used within a MenubarMenu")
|
|
34
|
-
}
|
|
35
|
-
return context
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Menubar Root
|
|
39
|
-
const Menubar = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
40
|
-
({ className, children, ...props }, ref) => {
|
|
41
|
-
const [activeMenu, setActiveMenu] = React.useState<string | null>(null)
|
|
42
|
-
|
|
43
|
-
// Close on outside click
|
|
44
|
-
React.useEffect(() => {
|
|
45
|
-
if (!activeMenu) return
|
|
46
|
-
const handleClick = (e: MouseEvent) => {
|
|
47
|
-
const target = e.target as Element
|
|
48
|
-
if (!target.closest('[data-menubar]')) {
|
|
49
|
-
setActiveMenu(null)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
document.addEventListener("click", handleClick)
|
|
53
|
-
return () => document.removeEventListener("click", handleClick)
|
|
54
|
-
}, [activeMenu])
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<MenubarContext.Provider value={{ activeMenu, setActiveMenu }}>
|
|
58
|
-
<div
|
|
59
|
-
ref={ref}
|
|
60
|
-
data-menubar
|
|
61
|
-
className={cn(
|
|
62
|
-
"flex h-10 items-center space-x-1 rounded-md border bg-background p-1",
|
|
63
|
-
className
|
|
64
|
-
)}
|
|
65
|
-
{...props}
|
|
66
|
-
>
|
|
67
|
-
{children}
|
|
68
|
-
</div>
|
|
69
|
-
</MenubarContext.Provider>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
)
|
|
73
|
-
Menubar.displayName = "Menubar"
|
|
74
|
-
|
|
75
|
-
// MenubarMenu
|
|
76
|
-
interface MenubarMenuProps {
|
|
77
|
-
children: React.ReactNode
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const MenubarMenu = ({ children }: MenubarMenuProps) => {
|
|
81
|
-
const menuId = React.useId()
|
|
82
|
-
const triggerRef = React.useRef<HTMLButtonElement>(null)
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<MenubarMenuContext.Provider value={{ menuId, triggerRef }}>
|
|
86
|
-
<div className="relative">
|
|
87
|
-
{children}
|
|
88
|
-
</div>
|
|
89
|
-
</MenubarMenuContext.Provider>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
MenubarMenu.displayName = "MenubarMenu"
|
|
93
|
-
|
|
94
|
-
// MenubarTrigger
|
|
95
|
-
const MenubarTrigger = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
|
|
96
|
-
({ className, children, ...props }, ref) => {
|
|
97
|
-
const { activeMenu, setActiveMenu } = useMenubar()
|
|
98
|
-
const { menuId, triggerRef } = useMenubarMenu()
|
|
99
|
-
const isOpen = activeMenu === menuId
|
|
100
|
-
|
|
101
|
-
const handleClick = () => {
|
|
102
|
-
setActiveMenu(isOpen ? null : menuId)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const handleMouseEnter = () => {
|
|
106
|
-
if (activeMenu && activeMenu !== menuId) {
|
|
107
|
-
setActiveMenu(menuId)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<button
|
|
113
|
-
ref={triggerRef}
|
|
114
|
-
className={cn(
|
|
115
|
-
"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none",
|
|
116
|
-
"focus:bg-accent focus:text-accent-foreground",
|
|
117
|
-
isOpen && "bg-accent text-accent-foreground",
|
|
118
|
-
className
|
|
119
|
-
)}
|
|
120
|
-
onClick={handleClick}
|
|
121
|
-
onMouseEnter={handleMouseEnter}
|
|
122
|
-
{...props}
|
|
123
|
-
>
|
|
124
|
-
{children}
|
|
125
|
-
</button>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
)
|
|
129
|
-
MenubarTrigger.displayName = "MenubarTrigger"
|
|
130
|
-
|
|
131
|
-
// MenubarContent
|
|
132
|
-
const MenubarContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
133
|
-
({ className, children, ...props }, ref) => {
|
|
134
|
-
const { activeMenu, setActiveMenu } = useMenubar()
|
|
135
|
-
const { menuId, triggerRef } = useMenubarMenu()
|
|
136
|
-
const isOpen = activeMenu === menuId
|
|
137
|
-
const [position, setPosition] = React.useState({ top: 0, left: 0 })
|
|
138
|
-
const contentRef = React.useRef<HTMLDivElement>(null)
|
|
139
|
-
|
|
140
|
-
React.useEffect(() => {
|
|
141
|
-
if (!isOpen || !triggerRef.current) return
|
|
142
|
-
const rect = triggerRef.current.getBoundingClientRect()
|
|
143
|
-
setPosition({
|
|
144
|
-
top: rect.bottom + 4,
|
|
145
|
-
left: rect.left,
|
|
146
|
-
})
|
|
147
|
-
}, [isOpen, triggerRef])
|
|
148
|
-
|
|
149
|
-
// Close on Escape
|
|
150
|
-
React.useEffect(() => {
|
|
151
|
-
if (!isOpen) return
|
|
152
|
-
const handleEscape = (e: KeyboardEvent) => {
|
|
153
|
-
if (e.key === "Escape") setActiveMenu(null)
|
|
154
|
-
}
|
|
155
|
-
document.addEventListener("keydown", handleEscape)
|
|
156
|
-
return () => document.removeEventListener("keydown", handleEscape)
|
|
157
|
-
}, [isOpen, setActiveMenu])
|
|
158
|
-
|
|
159
|
-
if (!isOpen) return null
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<div
|
|
163
|
-
ref={contentRef}
|
|
164
|
-
className={cn(
|
|
165
|
-
"fixed z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
166
|
-
"animate-in fade-in-0 slide-in-from-top-2",
|
|
167
|
-
className
|
|
168
|
-
)}
|
|
169
|
-
style={{
|
|
170
|
-
top: position.top,
|
|
171
|
-
left: position.left,
|
|
172
|
-
}}
|
|
173
|
-
{...props}
|
|
174
|
-
>
|
|
175
|
-
{children}
|
|
176
|
-
</div>
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
)
|
|
180
|
-
MenubarContent.displayName = "MenubarContent"
|
|
181
|
-
|
|
182
|
-
// MenubarItem
|
|
183
|
-
interface MenubarItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
184
|
-
inset?: boolean
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const MenubarItem = React.forwardRef<HTMLButtonElement, MenubarItemProps>(
|
|
188
|
-
({ className, inset, children, ...props }, ref) => {
|
|
189
|
-
const { setActiveMenu } = useMenubar()
|
|
190
|
-
|
|
191
|
-
return (
|
|
192
|
-
<button
|
|
193
|
-
ref={ref}
|
|
194
|
-
className={cn(
|
|
195
|
-
"relative flex w-full cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
196
|
-
"focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground",
|
|
197
|
-
"disabled:pointer-events-none disabled:opacity-50",
|
|
198
|
-
inset && "pl-8",
|
|
199
|
-
className
|
|
200
|
-
)}
|
|
201
|
-
onClick={() => setActiveMenu(null)}
|
|
202
|
-
{...props}
|
|
203
|
-
>
|
|
204
|
-
{children}
|
|
205
|
-
</button>
|
|
206
|
-
)
|
|
207
|
-
}
|
|
208
|
-
)
|
|
209
|
-
MenubarItem.displayName = "MenubarItem"
|
|
210
|
-
|
|
211
|
-
// MenubarSeparator
|
|
212
|
-
const MenubarSeparator = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
213
|
-
({ className, ...props }, ref) => (
|
|
214
|
-
<div ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
|
215
|
-
)
|
|
216
|
-
)
|
|
217
|
-
MenubarSeparator.displayName = "MenubarSeparator"
|
|
218
|
-
|
|
219
|
-
// MenubarLabel
|
|
220
|
-
const MenubarLabel = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & { inset?: boolean }>(
|
|
221
|
-
({ className, inset, ...props }, ref) => (
|
|
222
|
-
<div
|
|
223
|
-
ref={ref}
|
|
224
|
-
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
|
225
|
-
{...props}
|
|
226
|
-
/>
|
|
227
|
-
)
|
|
228
|
-
)
|
|
229
|
-
MenubarLabel.displayName = "MenubarLabel"
|
|
230
|
-
|
|
231
|
-
// MenubarShortcut
|
|
232
|
-
const MenubarShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
|
|
233
|
-
<span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />
|
|
234
|
-
)
|
|
235
|
-
MenubarShortcut.displayName = "MenubarShortcut"
|
|
236
|
-
|
|
237
|
-
export {
|
|
238
|
-
Menubar,
|
|
239
|
-
MenubarMenu,
|
|
240
|
-
MenubarTrigger,
|
|
241
|
-
MenubarContent,
|
|
242
|
-
MenubarItem,
|
|
243
|
-
MenubarSeparator,
|
|
244
|
-
MenubarLabel,
|
|
245
|
-
MenubarShortcut,
|
|
246
|
-
}
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
// Menubar Context
|
|
7
|
+
interface MenubarContextValue {
|
|
8
|
+
activeMenu: string | null
|
|
9
|
+
setActiveMenu: (menu: string | null) => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const MenubarContext = React.createContext<MenubarContextValue | null>(null)
|
|
13
|
+
|
|
14
|
+
function useMenubar() {
|
|
15
|
+
const context = React.useContext(MenubarContext)
|
|
16
|
+
if (!context) {
|
|
17
|
+
throw new Error("useMenubar must be used within a Menubar")
|
|
18
|
+
}
|
|
19
|
+
return context
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// MenubarMenu Context
|
|
23
|
+
interface MenubarMenuContextValue {
|
|
24
|
+
menuId: string
|
|
25
|
+
triggerRef: React.RefObject<HTMLButtonElement | null>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const MenubarMenuContext = React.createContext<MenubarMenuContextValue | null>(null)
|
|
29
|
+
|
|
30
|
+
function useMenubarMenu() {
|
|
31
|
+
const context = React.useContext(MenubarMenuContext)
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error("useMenubarMenu must be used within a MenubarMenu")
|
|
34
|
+
}
|
|
35
|
+
return context
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Menubar Root
|
|
39
|
+
const Menubar = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
40
|
+
({ className, children, ...props }, ref) => {
|
|
41
|
+
const [activeMenu, setActiveMenu] = React.useState<string | null>(null)
|
|
42
|
+
|
|
43
|
+
// Close on outside click
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
if (!activeMenu) return
|
|
46
|
+
const handleClick = (e: MouseEvent) => {
|
|
47
|
+
const target = e.target as Element
|
|
48
|
+
if (!target.closest('[data-menubar]')) {
|
|
49
|
+
setActiveMenu(null)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
document.addEventListener("click", handleClick)
|
|
53
|
+
return () => document.removeEventListener("click", handleClick)
|
|
54
|
+
}, [activeMenu])
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<MenubarContext.Provider value={{ activeMenu, setActiveMenu }}>
|
|
58
|
+
<div
|
|
59
|
+
ref={ref}
|
|
60
|
+
data-menubar
|
|
61
|
+
className={cn(
|
|
62
|
+
"flex h-10 items-center space-x-1 rounded-md border bg-background p-1",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</div>
|
|
69
|
+
</MenubarContext.Provider>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
Menubar.displayName = "Menubar"
|
|
74
|
+
|
|
75
|
+
// MenubarMenu
|
|
76
|
+
interface MenubarMenuProps {
|
|
77
|
+
children: React.ReactNode
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const MenubarMenu = ({ children }: MenubarMenuProps) => {
|
|
81
|
+
const menuId = React.useId()
|
|
82
|
+
const triggerRef = React.useRef<HTMLButtonElement>(null)
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<MenubarMenuContext.Provider value={{ menuId, triggerRef }}>
|
|
86
|
+
<div className="relative">
|
|
87
|
+
{children}
|
|
88
|
+
</div>
|
|
89
|
+
</MenubarMenuContext.Provider>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
MenubarMenu.displayName = "MenubarMenu"
|
|
93
|
+
|
|
94
|
+
// MenubarTrigger
|
|
95
|
+
const MenubarTrigger = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
|
|
96
|
+
({ className, children, ...props }, ref) => {
|
|
97
|
+
const { activeMenu, setActiveMenu } = useMenubar()
|
|
98
|
+
const { menuId, triggerRef } = useMenubarMenu()
|
|
99
|
+
const isOpen = activeMenu === menuId
|
|
100
|
+
|
|
101
|
+
const handleClick = () => {
|
|
102
|
+
setActiveMenu(isOpen ? null : menuId)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const handleMouseEnter = () => {
|
|
106
|
+
if (activeMenu && activeMenu !== menuId) {
|
|
107
|
+
setActiveMenu(menuId)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<button
|
|
113
|
+
ref={triggerRef}
|
|
114
|
+
className={cn(
|
|
115
|
+
"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none",
|
|
116
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
117
|
+
isOpen && "bg-accent text-accent-foreground",
|
|
118
|
+
className
|
|
119
|
+
)}
|
|
120
|
+
onClick={handleClick}
|
|
121
|
+
onMouseEnter={handleMouseEnter}
|
|
122
|
+
{...props}
|
|
123
|
+
>
|
|
124
|
+
{children}
|
|
125
|
+
</button>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
MenubarTrigger.displayName = "MenubarTrigger"
|
|
130
|
+
|
|
131
|
+
// MenubarContent
|
|
132
|
+
const MenubarContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
133
|
+
({ className, children, ...props }, ref) => {
|
|
134
|
+
const { activeMenu, setActiveMenu } = useMenubar()
|
|
135
|
+
const { menuId, triggerRef } = useMenubarMenu()
|
|
136
|
+
const isOpen = activeMenu === menuId
|
|
137
|
+
const [position, setPosition] = React.useState({ top: 0, left: 0 })
|
|
138
|
+
const contentRef = React.useRef<HTMLDivElement>(null)
|
|
139
|
+
|
|
140
|
+
React.useEffect(() => {
|
|
141
|
+
if (!isOpen || !triggerRef.current) return
|
|
142
|
+
const rect = triggerRef.current.getBoundingClientRect()
|
|
143
|
+
setPosition({
|
|
144
|
+
top: rect.bottom + 4,
|
|
145
|
+
left: rect.left,
|
|
146
|
+
})
|
|
147
|
+
}, [isOpen, triggerRef])
|
|
148
|
+
|
|
149
|
+
// Close on Escape
|
|
150
|
+
React.useEffect(() => {
|
|
151
|
+
if (!isOpen) return
|
|
152
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
153
|
+
if (e.key === "Escape") setActiveMenu(null)
|
|
154
|
+
}
|
|
155
|
+
document.addEventListener("keydown", handleEscape)
|
|
156
|
+
return () => document.removeEventListener("keydown", handleEscape)
|
|
157
|
+
}, [isOpen, setActiveMenu])
|
|
158
|
+
|
|
159
|
+
if (!isOpen) return null
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div
|
|
163
|
+
ref={contentRef}
|
|
164
|
+
className={cn(
|
|
165
|
+
"fixed z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
166
|
+
"animate-in fade-in-0 slide-in-from-top-2",
|
|
167
|
+
className
|
|
168
|
+
)}
|
|
169
|
+
style={{
|
|
170
|
+
top: position.top,
|
|
171
|
+
left: position.left,
|
|
172
|
+
}}
|
|
173
|
+
{...props}
|
|
174
|
+
>
|
|
175
|
+
{children}
|
|
176
|
+
</div>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
MenubarContent.displayName = "MenubarContent"
|
|
181
|
+
|
|
182
|
+
// MenubarItem
|
|
183
|
+
interface MenubarItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
184
|
+
inset?: boolean
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const MenubarItem = React.forwardRef<HTMLButtonElement, MenubarItemProps>(
|
|
188
|
+
({ className, inset, children, ...props }, ref) => {
|
|
189
|
+
const { setActiveMenu } = useMenubar()
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<button
|
|
193
|
+
ref={ref}
|
|
194
|
+
className={cn(
|
|
195
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
196
|
+
"focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground",
|
|
197
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
198
|
+
inset && "pl-8",
|
|
199
|
+
className
|
|
200
|
+
)}
|
|
201
|
+
onClick={() => setActiveMenu(null)}
|
|
202
|
+
{...props}
|
|
203
|
+
>
|
|
204
|
+
{children}
|
|
205
|
+
</button>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
MenubarItem.displayName = "MenubarItem"
|
|
210
|
+
|
|
211
|
+
// MenubarSeparator
|
|
212
|
+
const MenubarSeparator = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
213
|
+
({ className, ...props }, ref) => (
|
|
214
|
+
<div ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
MenubarSeparator.displayName = "MenubarSeparator"
|
|
218
|
+
|
|
219
|
+
// MenubarLabel
|
|
220
|
+
const MenubarLabel = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & { inset?: boolean }>(
|
|
221
|
+
({ className, inset, ...props }, ref) => (
|
|
222
|
+
<div
|
|
223
|
+
ref={ref}
|
|
224
|
+
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
|
225
|
+
{...props}
|
|
226
|
+
/>
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
MenubarLabel.displayName = "MenubarLabel"
|
|
230
|
+
|
|
231
|
+
// MenubarShortcut
|
|
232
|
+
const MenubarShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
|
|
233
|
+
<span className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} {...props} />
|
|
234
|
+
)
|
|
235
|
+
MenubarShortcut.displayName = "MenubarShortcut"
|
|
236
|
+
|
|
237
|
+
export {
|
|
238
|
+
Menubar,
|
|
239
|
+
MenubarMenu,
|
|
240
|
+
MenubarTrigger,
|
|
241
|
+
MenubarContent,
|
|
242
|
+
MenubarItem,
|
|
243
|
+
MenubarSeparator,
|
|
244
|
+
MenubarLabel,
|
|
245
|
+
MenubarShortcut,
|
|
246
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Script from 'next/script';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
|
|
6
|
+
interface MetaPixelProps {
|
|
7
|
+
pixelIds: string[]; // ← array now
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const MetaPixel: FC<MetaPixelProps> = ({ pixelIds }) => {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<Script
|
|
14
|
+
id="fb-script-multi"
|
|
15
|
+
strategy="afterInteractive"
|
|
16
|
+
dangerouslySetInnerHTML={{
|
|
17
|
+
__html: `
|
|
18
|
+
!function(f,b,e,v,n,t,s)
|
|
19
|
+
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
|
20
|
+
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
|
21
|
+
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
|
22
|
+
n.queue=[];t=b.createElement(e);t.async=!0;
|
|
23
|
+
t.src=v;s=b.getElementsByTagName(e)[0];
|
|
24
|
+
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
|
25
|
+
'https://connect.facebook.net/en_US/fbevents.js');
|
|
26
|
+
${pixelIds.map(id => `fbq('init', '${id}');`).join('\n')}
|
|
27
|
+
fbq('track', 'PageView');
|
|
28
|
+
`,
|
|
29
|
+
}}
|
|
30
|
+
/>
|
|
31
|
+
|
|
32
|
+
{/* One noscript tag per pixel */}
|
|
33
|
+
{pixelIds.map(id => (
|
|
34
|
+
<noscript key={id}>
|
|
35
|
+
<img
|
|
36
|
+
height="1" width="1" style={{ display: 'none' }}
|
|
37
|
+
src={`https://www.facebook.com/tr?id=${id}&ev=PageView&noscript=1`}
|
|
38
|
+
alt=""
|
|
39
|
+
/>
|
|
40
|
+
</noscript>
|
|
41
|
+
))}
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default MetaPixel;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Script from 'next/script';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
|
|
6
|
+
interface MicrosoftClarityProps {
|
|
7
|
+
clarityIds: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const MicrosoftClarity: FC<MicrosoftClarityProps> = ({ clarityIds }) => {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
{clarityIds.map((id) => (
|
|
14
|
+
<Script
|
|
15
|
+
key={id}
|
|
16
|
+
id={`microsoft-clarity-init-${id}`}
|
|
17
|
+
strategy="afterInteractive"
|
|
18
|
+
dangerouslySetInnerHTML={{
|
|
19
|
+
__html: `
|
|
20
|
+
(function(c,l,a,r,i,t,y){
|
|
21
|
+
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
|
22
|
+
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
|
23
|
+
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
|
24
|
+
})(window, document, "clarity", "script", "${id}");
|
|
25
|
+
`,
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
))}
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default MicrosoftClarity;
|