@srcroot/ui 0.0.44 → 0.0.45
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/package.json +1 -1
- package/src/registry/analytics/google-tag-manager.tsx +1 -1
- package/src/registry/themes/v4/blue.css +34 -36
- package/src/registry/themes/v4/gray.css +36 -38
- package/src/registry/themes/v4/green.css +34 -36
- package/src/registry/themes/v4/neutral.css +36 -38
- package/src/registry/themes/v4/orange.css +34 -36
- package/src/registry/themes/v4/rose.css +34 -36
- package/src/registry/themes/v4/slate.css +36 -38
- package/src/registry/themes/v4/stone.css +36 -38
- package/src/registry/themes/v4/violet.css +36 -38
- package/src/registry/themes/v4/zinc.css +34 -36
- package/src/registry/ui/chart.tsx +17 -15
- package/src/registry/ui/dropdown-menu.tsx +92 -15
- package/src/registry/ui/sidebar.tsx +159 -0
- package/src/registry/ui/theme-switcher.tsx +66 -0
|
@@ -126,54 +126,52 @@
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
:
|
|
131
|
-
|
|
132
|
-
--foreground: hsl(60 9.1% 97.8%);
|
|
129
|
+
.dark {
|
|
130
|
+
--background: hsl(24 9.8% 10%);
|
|
131
|
+
--foreground: hsl(60 9.1% 97.8%);
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
--card: hsl(24 9.8% 10%);
|
|
134
|
+
--card-foreground: hsl(60 9.1% 97.8%);
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
--popover: hsl(24 9.8% 10%);
|
|
137
|
+
--popover-foreground: hsl(60 9.1% 97.8%);
|
|
139
138
|
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
--primary: hsl(60 9.1% 97.8%);
|
|
140
|
+
--primary-foreground: hsl(24 9.8% 10%);
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
--secondary: hsl(12 6.5% 15.1%);
|
|
143
|
+
--secondary-foreground: hsl(60 9.1% 97.8%);
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
--muted: hsl(12 6.5% 15.1%);
|
|
146
|
+
--muted-foreground: hsl(24 5.4% 63.9%);
|
|
148
147
|
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
--accent: hsl(12 6.5% 15.1%);
|
|
149
|
+
--accent-foreground: hsl(60 9.1% 97.8%);
|
|
151
150
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
--success: hsl(142.1 70.6% 45.3%);
|
|
156
|
-
--success-foreground: hsl(24 9.8% 10%);
|
|
157
|
-
|
|
158
|
-
--warning: hsl(48 96.5% 53.1%);
|
|
159
|
-
--warning-foreground: hsl(24 9.8% 10%);
|
|
151
|
+
--destructive: hsl(0 62.8% 30.6%);
|
|
152
|
+
--destructive-foreground: hsl(60 9.1% 97.8%);
|
|
160
153
|
|
|
161
|
-
|
|
162
|
-
|
|
154
|
+
--success: hsl(142.1 70.6% 45.3%);
|
|
155
|
+
--success-foreground: hsl(24 9.8% 10%);
|
|
163
156
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
--ring: hsl(24 5.7% 82.9%);
|
|
157
|
+
--warning: hsl(48 96.5% 53.1%);
|
|
158
|
+
--warning-foreground: hsl(24 9.8% 10%);
|
|
167
159
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
--info: hsl(199.4 95.5% 53.8%);
|
|
161
|
+
--info-foreground: hsl(24 9.8% 10%);
|
|
162
|
+
|
|
163
|
+
--border: hsl(12 6.5% 15.1%);
|
|
164
|
+
--input: hsl(12 6.5% 15.1%);
|
|
165
|
+
--ring: hsl(24 5.7% 82.9%);
|
|
166
|
+
|
|
167
|
+
--sidebar-background: hsl(24 9.8% 14%);
|
|
168
|
+
--sidebar-foreground: hsl(60 9.1% 97.8%);
|
|
169
|
+
--sidebar-primary: hsl(60 9.1% 97.8%);
|
|
170
|
+
--sidebar-primary-foreground: hsl(24 9.8% 10%);
|
|
171
|
+
--sidebar-accent: hsl(12 6.5% 15.1%);
|
|
172
|
+
--sidebar-accent-foreground: hsl(60 9.1% 97.8%);
|
|
173
|
+
--sidebar-border: hsl(12 6.5% 15.1%);
|
|
174
|
+
--sidebar-ring: hsl(24 5.7% 82.9%);
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
body {
|
|
@@ -126,54 +126,52 @@
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
:
|
|
131
|
-
|
|
132
|
-
--foreground: hsl(0 0% 98%);
|
|
129
|
+
.dark {
|
|
130
|
+
--background: hsl(246 20% 6%);
|
|
131
|
+
--foreground: hsl(0 0% 98%);
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
--card: hsl(246 20% 8%);
|
|
134
|
+
--card-foreground: hsl(0 0% 98%);
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
--popover: hsl(246 20% 8%);
|
|
137
|
+
--popover-foreground: hsl(0 0% 98%);
|
|
139
138
|
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
--primary: hsl(246 80% 65%);
|
|
140
|
+
--primary-foreground: hsl(246 20% 6%);
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
--muted: hsl(246 20% 15%);
|
|
147
|
-
--muted-foreground: hsl(246 10% 60%);
|
|
148
|
-
|
|
149
|
-
--accent: hsl(320 85% 60%);
|
|
150
|
-
--accent-foreground: hsl(0 0% 100%);
|
|
142
|
+
--secondary: hsl(320 85% 60%);
|
|
143
|
+
--secondary-foreground: hsl(0 0% 100%);
|
|
151
144
|
|
|
152
|
-
|
|
153
|
-
|
|
145
|
+
--muted: hsl(246 20% 15%);
|
|
146
|
+
--muted-foreground: hsl(246 10% 60%);
|
|
154
147
|
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
--accent: hsl(320 85% 60%);
|
|
149
|
+
--accent-foreground: hsl(0 0% 100%);
|
|
157
150
|
|
|
158
|
-
|
|
159
|
-
|
|
151
|
+
--destructive: hsl(0 62.8% 30.6%);
|
|
152
|
+
--destructive-foreground: hsl(0 0% 98%);
|
|
160
153
|
|
|
161
|
-
|
|
162
|
-
|
|
154
|
+
--success: hsl(142.1 70.6% 45.3%);
|
|
155
|
+
--success-foreground: hsl(246 80% 60%);
|
|
163
156
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
--ring: hsl(246 80% 65%);
|
|
157
|
+
--warning: hsl(48 96.5% 53.1%);
|
|
158
|
+
--warning-foreground: hsl(246 80% 60%);
|
|
167
159
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
--info: hsl(199.4 95.5% 53.8%);
|
|
161
|
+
--info-foreground: hsl(246 80% 60%);
|
|
162
|
+
|
|
163
|
+
--border: hsl(246 20% 18%);
|
|
164
|
+
--input: hsl(246 20% 18%);
|
|
165
|
+
--ring: hsl(246 80% 65%);
|
|
166
|
+
|
|
167
|
+
--sidebar-background: hsl(246 20% 10%);
|
|
168
|
+
--sidebar-foreground: hsl(0 0% 98%);
|
|
169
|
+
--sidebar-primary: hsl(246 80% 65%);
|
|
170
|
+
--sidebar-primary-foreground: hsl(246 20% 6%);
|
|
171
|
+
--sidebar-accent: hsl(320 85% 60%);
|
|
172
|
+
--sidebar-accent-foreground: hsl(0 0% 100%);
|
|
173
|
+
--sidebar-border: hsl(246 20% 18%);
|
|
174
|
+
--sidebar-ring: hsl(246 80% 65%);
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
body {
|
|
@@ -126,54 +126,52 @@
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
:
|
|
131
|
-
|
|
132
|
-
--foreground: hsl(0 0% 98%);
|
|
129
|
+
.dark {
|
|
130
|
+
--background: hsl(240 10% 3.9%);
|
|
131
|
+
--foreground: hsl(0 0% 98%);
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
--card: hsl(240 10% 3.9%);
|
|
134
|
+
--card-foreground: hsl(0 0% 98%);
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
--popover: hsl(240 10% 3.9%);
|
|
137
|
+
--popover-foreground: hsl(0 0% 98%);
|
|
139
138
|
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
--primary: hsl(0 0% 98%);
|
|
140
|
+
--primary-foreground: hsl(240 5.9% 10%);
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
--secondary: hsl(240 3.7% 15.9%);
|
|
143
|
+
--secondary-foreground: hsl(0 0% 98%);
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
--muted: hsl(240 3.7% 15.9%);
|
|
146
|
+
--muted-foreground: hsl(240 5% 64.9%);
|
|
148
147
|
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
--accent: hsl(240 3.7% 15.9%);
|
|
149
|
+
--accent-foreground: hsl(0 0% 98%);
|
|
151
150
|
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
--destructive: hsl(0 62.8% 30.6%);
|
|
152
|
+
--destructive-foreground: hsl(0 0% 98%);
|
|
154
153
|
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
--success: hsl(142.1 70.6% 45.3%);
|
|
155
|
+
--success-foreground: hsl(240 5.9% 10%);
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
--warning: hsl(48 96.5% 53.1%);
|
|
158
|
+
--warning-foreground: hsl(240 5.9% 10%);
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
--info: hsl(199.4 95.5% 53.8%);
|
|
161
|
+
--info-foreground: hsl(240 5.9% 10%);
|
|
163
162
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
--border: hsl(240 3.7% 15.9%);
|
|
164
|
+
--input: hsl(240 3.7% 15.9%);
|
|
165
|
+
--ring: hsl(240 4.9% 83.9%);
|
|
167
166
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
167
|
+
--sidebar-background: hsl(240 5.9% 10%);
|
|
168
|
+
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
|
169
|
+
--sidebar-primary: hsl(224.3 76.3% 48%);
|
|
170
|
+
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
171
|
+
--sidebar-accent: hsl(240 3.7% 15.9%);
|
|
172
|
+
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
|
173
|
+
--sidebar-border: hsl(240 3.7% 15.9%);
|
|
174
|
+
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
body {
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Legend as ChartLegendPrimitive,
|
|
6
|
+
ResponsiveContainer,
|
|
7
|
+
Tooltip as ChartTooltipPrimitive,
|
|
8
|
+
} from "recharts"
|
|
5
9
|
|
|
6
10
|
import { cn } from "@/lib/utils"
|
|
7
11
|
|
|
@@ -102,13 +106,19 @@ const ChartTooltip = ChartTooltipPrimitive
|
|
|
102
106
|
|
|
103
107
|
const ChartTooltipContent = React.forwardRef<
|
|
104
108
|
HTMLDivElement,
|
|
105
|
-
React.ComponentProps<typeof ChartTooltipPrimitive> &
|
|
109
|
+
Omit<React.ComponentProps<typeof ChartTooltipPrimitive>, "payload"> &
|
|
106
110
|
React.ComponentProps<"div"> & {
|
|
107
111
|
hideLabel?: boolean
|
|
108
112
|
hideIndicator?: boolean
|
|
109
113
|
indicator?: "line" | "dot" | "dashed"
|
|
110
114
|
nameKey?: string
|
|
111
115
|
labelKey?: string
|
|
116
|
+
payload?: any[]
|
|
117
|
+
label?: any
|
|
118
|
+
labelFormatter?: any
|
|
119
|
+
labelClassName?: string
|
|
120
|
+
formatter?: any
|
|
121
|
+
color?: string
|
|
112
122
|
}
|
|
113
123
|
>(
|
|
114
124
|
(
|
|
@@ -181,7 +191,7 @@ const ChartTooltipContent = React.forwardRef<
|
|
|
181
191
|
>
|
|
182
192
|
{!nestLabel ? tooltipLabel : null}
|
|
183
193
|
<div className="grid gap-1.5">
|
|
184
|
-
{payload.map((item, index) => {
|
|
194
|
+
{payload.map((item: any, index: number) => {
|
|
185
195
|
const key = `${nameKey || item.name || item.dataKey || "value"}`
|
|
186
196
|
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
|
187
197
|
const indicatorColor = color || item.payload.fill || item.color
|
|
@@ -257,7 +267,9 @@ const ChartLegend = ChartLegendPrimitive
|
|
|
257
267
|
const ChartLegendContent = React.forwardRef<
|
|
258
268
|
HTMLDivElement,
|
|
259
269
|
React.ComponentProps<"div"> &
|
|
260
|
-
|
|
270
|
+
{
|
|
271
|
+
payload?: any[]
|
|
272
|
+
verticalAlign?: "top" | "middle" | "bottom"
|
|
261
273
|
hideIcon?: boolean
|
|
262
274
|
nameKey?: string
|
|
263
275
|
}
|
|
@@ -281,7 +293,7 @@ const ChartLegendContent = React.forwardRef<
|
|
|
281
293
|
className
|
|
282
294
|
)}
|
|
283
295
|
>
|
|
284
|
-
{payload.map((item) => {
|
|
296
|
+
{(payload as any[]).map((item) => {
|
|
285
297
|
const key = `${nameKey || item.dataKey || "value"}`
|
|
286
298
|
const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
|
287
299
|
|
|
@@ -351,16 +363,6 @@ function getPayloadConfigFromPayload(
|
|
|
351
363
|
: config[key as keyof typeof config]
|
|
352
364
|
}
|
|
353
365
|
|
|
354
|
-
// Recharts Primitive Imports - We import specific components to avoid large bundle reference but for now let's just use * and assume tree shaking or specific imports in consuming app.
|
|
355
|
-
// However, the above code uses RechartsPrimitive.Tooltip and Legend.
|
|
356
|
-
// Ideally we should import them from recharts.
|
|
357
|
-
|
|
358
|
-
import {
|
|
359
|
-
Legend as ChartLegendPrimitive,
|
|
360
|
-
Tooltip as ChartTooltipPrimitive,
|
|
361
|
-
ResponsiveContainer,
|
|
362
|
-
} from "recharts"
|
|
363
|
-
|
|
364
366
|
export {
|
|
365
367
|
ChartContainer,
|
|
366
368
|
ChartTooltip,
|
|
@@ -14,6 +14,7 @@ interface DropdownMenuProps {
|
|
|
14
14
|
open?: boolean
|
|
15
15
|
onOpenChange?: (open: boolean) => void
|
|
16
16
|
defaultOpen?: boolean
|
|
17
|
+
className?: string
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -32,7 +33,7 @@ interface DropdownMenuProps {
|
|
|
32
33
|
* </DropdownMenuContent>
|
|
33
34
|
* </DropdownMenu>
|
|
34
35
|
*/
|
|
35
|
-
function DropdownMenu({ children, open: controlledOpen, onOpenChange, defaultOpen = false }: DropdownMenuProps) {
|
|
36
|
+
function DropdownMenu({ children, open: controlledOpen, onOpenChange, defaultOpen = false, className }: DropdownMenuProps) {
|
|
36
37
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)
|
|
37
38
|
const triggerRef = React.useRef<HTMLButtonElement>(null)
|
|
38
39
|
|
|
@@ -41,13 +42,14 @@ function DropdownMenu({ children, open: controlledOpen, onOpenChange, defaultOpe
|
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
44
|
<DropdownMenuContext.Provider value={{ open, onOpenChange: setOpen, triggerRef }}>
|
|
44
|
-
<div className="relative inline-block text-left">
|
|
45
|
+
<div className={cn("relative inline-block text-left", className)}>
|
|
45
46
|
{children}
|
|
46
47
|
</div>
|
|
47
48
|
</DropdownMenuContext.Provider>
|
|
48
49
|
)
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
|
|
51
53
|
interface DropdownMenuTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
52
54
|
asChild?: boolean
|
|
53
55
|
}
|
|
@@ -107,6 +109,11 @@ const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContent
|
|
|
107
109
|
const context = React.useContext(DropdownMenuContext)
|
|
108
110
|
if (!context) throw new Error("DropdownMenuContent must be used within DropdownMenu")
|
|
109
111
|
const contentRef = React.useRef<HTMLDivElement>(null)
|
|
112
|
+
const [currentSide, setCurrentSide] = React.useState(side)
|
|
113
|
+
|
|
114
|
+
React.useEffect(() => {
|
|
115
|
+
setCurrentSide(side)
|
|
116
|
+
}, [side])
|
|
110
117
|
|
|
111
118
|
React.useEffect(() => {
|
|
112
119
|
const handleClickOutside = (e: MouseEvent) => {
|
|
@@ -129,17 +136,53 @@ const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContent
|
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
const checkPosition = () => {
|
|
140
|
+
if (context.open && contentRef.current && context.triggerRef.current) {
|
|
141
|
+
const triggerRect = context.triggerRef.current.getBoundingClientRect()
|
|
142
|
+
const contentRect = contentRef.current.getBoundingClientRect()
|
|
143
|
+
const viewportHeight = window.innerHeight
|
|
144
|
+
|
|
145
|
+
const spaceBelow = viewportHeight - triggerRect.bottom
|
|
146
|
+
const spaceAbove = triggerRect.top
|
|
147
|
+
const neededHeight = contentRect.height + sideOffset
|
|
148
|
+
|
|
149
|
+
if (side === 'bottom') {
|
|
150
|
+
if (spaceBelow < neededHeight && spaceAbove > neededHeight) {
|
|
151
|
+
setCurrentSide('top')
|
|
152
|
+
} else {
|
|
153
|
+
setCurrentSide('bottom')
|
|
154
|
+
}
|
|
155
|
+
} else if (side === 'top') {
|
|
156
|
+
if (spaceAbove < neededHeight && spaceBelow > neededHeight) {
|
|
157
|
+
setCurrentSide('bottom')
|
|
158
|
+
} else {
|
|
159
|
+
setCurrentSide('top')
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (context.open) {
|
|
166
|
+
// Check position immediately after render cycle (via timeout to allow ref to populate and layout to occur)
|
|
167
|
+
// Using requestAnimationFrame for better timing in layout cycle
|
|
168
|
+
requestAnimationFrame(checkPosition)
|
|
169
|
+
}
|
|
170
|
+
|
|
132
171
|
const timer = setTimeout(() => {
|
|
133
172
|
document.addEventListener("click", handleClickOutside)
|
|
134
173
|
}, 0)
|
|
135
174
|
document.addEventListener("keydown", handleEscape)
|
|
175
|
+
window.addEventListener("resize", checkPosition)
|
|
176
|
+
window.addEventListener("scroll", checkPosition, true) // Capture scroll to update pos
|
|
136
177
|
|
|
137
178
|
return () => {
|
|
138
179
|
clearTimeout(timer)
|
|
139
180
|
document.removeEventListener("click", handleClickOutside)
|
|
140
181
|
document.removeEventListener("keydown", handleEscape)
|
|
182
|
+
window.removeEventListener("resize", checkPosition)
|
|
183
|
+
window.removeEventListener("scroll", checkPosition, true)
|
|
141
184
|
}
|
|
142
|
-
}, [context.open, context])
|
|
185
|
+
}, [context.open, context, side, sideOffset])
|
|
143
186
|
|
|
144
187
|
if (!context.open) return null
|
|
145
188
|
|
|
@@ -150,12 +193,6 @@ const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContent
|
|
|
150
193
|
end: 'right-0',
|
|
151
194
|
}
|
|
152
195
|
|
|
153
|
-
// Calculate side classes
|
|
154
|
-
const sideClasses = {
|
|
155
|
-
bottom: `top-full mt-${sideOffset}`,
|
|
156
|
-
top: `bottom-full mb-${sideOffset}`,
|
|
157
|
-
}
|
|
158
|
-
|
|
159
196
|
return (
|
|
160
197
|
<div
|
|
161
198
|
ref={(node) => {
|
|
@@ -169,12 +206,12 @@ const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContent
|
|
|
169
206
|
"absolute z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
170
207
|
"animate-in fade-in-0 zoom-in-95",
|
|
171
208
|
alignmentClasses[align],
|
|
172
|
-
|
|
209
|
+
currentSide === 'bottom' ? 'top-full' : 'bottom-full',
|
|
173
210
|
className
|
|
174
211
|
)}
|
|
175
212
|
style={{
|
|
176
|
-
marginTop:
|
|
177
|
-
marginBottom:
|
|
213
|
+
marginTop: currentSide === 'bottom' ? sideOffset : undefined,
|
|
214
|
+
marginBottom: currentSide === 'top' ? sideOffset : undefined,
|
|
178
215
|
}}
|
|
179
216
|
{...props}
|
|
180
217
|
/>
|
|
@@ -183,16 +220,54 @@ const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContent
|
|
|
183
220
|
)
|
|
184
221
|
DropdownMenuContent.displayName = "DropdownMenuContent"
|
|
185
222
|
|
|
223
|
+
const DropdownMenuGroup = React.forwardRef<
|
|
224
|
+
HTMLDivElement,
|
|
225
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
226
|
+
>(({ className, ...props }, ref) => (
|
|
227
|
+
<div ref={ref} className={cn("", className)} {...props} />
|
|
228
|
+
))
|
|
229
|
+
DropdownMenuGroup.displayName = "DropdownMenuGroup"
|
|
230
|
+
|
|
231
|
+
const DropdownMenuPortal = ({ children }: { children: React.ReactNode }) => {
|
|
232
|
+
return <>{children}</>
|
|
233
|
+
}
|
|
234
|
+
DropdownMenuPortal.displayName = "DropdownMenuPortal"
|
|
235
|
+
|
|
186
236
|
const DropdownMenuItem = React.forwardRef<
|
|
187
237
|
HTMLDivElement,
|
|
188
|
-
React.HTMLAttributes<HTMLDivElement> & { inset?: boolean; disabled?: boolean }
|
|
189
|
-
>(({ className, inset, disabled, onClick, ...props }, ref) => {
|
|
238
|
+
React.HTMLAttributes<HTMLDivElement> & { inset?: boolean; disabled?: boolean; asChild?: boolean; closeOnSelect?: boolean }
|
|
239
|
+
>(({ className, inset, disabled, onClick, asChild = false, closeOnSelect = true, ...props }, ref) => {
|
|
190
240
|
const context = React.useContext(DropdownMenuContext)
|
|
191
241
|
|
|
192
242
|
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
193
243
|
if (disabled) return
|
|
194
244
|
onClick?.(e)
|
|
195
|
-
|
|
245
|
+
if (closeOnSelect) {
|
|
246
|
+
context?.onOpenChange(false)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (asChild) {
|
|
251
|
+
const child = React.Children.only(props.children) as React.ReactElement<any>
|
|
252
|
+
return React.cloneElement(child, {
|
|
253
|
+
ref,
|
|
254
|
+
className: cn(
|
|
255
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground",
|
|
256
|
+
inset && "pl-8",
|
|
257
|
+
disabled && "pointer-events-none opacity-50",
|
|
258
|
+
className,
|
|
259
|
+
child.props.className
|
|
260
|
+
),
|
|
261
|
+
onClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
|
262
|
+
handleClick(e)
|
|
263
|
+
child.props.onClick?.(e)
|
|
264
|
+
},
|
|
265
|
+
"data-disabled": disabled || undefined,
|
|
266
|
+
"data-inset": inset || undefined,
|
|
267
|
+
tabIndex: disabled ? -1 : 0,
|
|
268
|
+
...props,
|
|
269
|
+
children: child.props.children
|
|
270
|
+
})
|
|
196
271
|
}
|
|
197
272
|
|
|
198
273
|
return (
|
|
@@ -403,4 +478,6 @@ export {
|
|
|
403
478
|
DropdownMenuSub,
|
|
404
479
|
DropdownMenuSubTrigger,
|
|
405
480
|
DropdownMenuSubContent,
|
|
481
|
+
DropdownMenuGroup,
|
|
482
|
+
DropdownMenuPortal,
|
|
406
483
|
}
|