@exegia/corpora-ui 0.17.0 → 0.19.0
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/dist-lib/components/blocks/nav/sidebar-block.d.ts +1 -1
- package/dist-lib/components/blocks/scaffold/__tests__/scaffold.test.d.ts +1 -0
- package/dist-lib/components/blocks/scaffold/constants.d.ts +23 -0
- package/dist-lib/components/blocks/scaffold/index.d.ts +27 -0
- package/dist-lib/components/blocks/scaffold/panel-close-button.d.ts +4 -0
- package/dist-lib/components/blocks/scaffold/panel-menu-button.d.ts +8 -0
- package/dist-lib/components/blocks/scaffold/scaffold-actions.d.ts +10 -0
- package/dist-lib/components/blocks/scaffold/scaffold-canvas.d.ts +7 -0
- package/dist-lib/components/blocks/scaffold/scaffold-context.d.ts +5 -0
- package/dist-lib/components/blocks/scaffold/scaffold-inspector.d.ts +9 -0
- package/dist-lib/components/blocks/scaffold/scaffold-main.d.ts +8 -0
- package/dist-lib/components/blocks/scaffold/scaffold-panel.d.ts +10 -0
- package/dist-lib/components/blocks/scaffold/scaffold-root.d.ts +8 -0
- package/dist-lib/components/blocks/scaffold/scaffold-sidebar.d.ts +7 -0
- package/dist-lib/components/blocks/scaffold/scaffold-sub-panel.d.ts +3 -0
- package/dist-lib/components/blocks/scaffold/scaffold-tab.d.ts +9 -0
- package/dist-lib/components/blocks/scaffold/type.d.ts +135 -0
- package/dist-lib/components/blocks/scaffold/use-scaffold.d.ts +8 -0
- package/dist-lib/components/blocks/scaffold/utils.d.ts +21 -0
- package/dist-lib/components/blocks/shell/animated-panel-inset.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-panel-provider.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-panel-trigger.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-panel.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-content.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-footer.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-group-content.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-group-label.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-group.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-header.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-menu-button.d.ts +3 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-menu-item.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub-button.d.ts +3 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub-item.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-sidebar-menu.d.ts +2 -0
- package/dist-lib/components/blocks/shell/index.d.ts +16 -0
- package/dist-lib/components/blocks/shell/type.d.ts +90 -4
- package/dist-lib/components/blocks/shell/utils.d.ts +37 -0
- package/dist-lib/components/motion/index.d.ts +1 -0
- package/dist-lib/components/motion/overflow-actions.d.ts +35 -0
- package/dist-lib/components/ui/button-group.d.ts +11 -0
- package/dist-lib/components/ui/glasscn/glass-button-group.d.ts +5 -0
- package/dist-lib/components/ui/glasscn/glass-container.d.ts +7 -0
- package/dist-lib/components/ui/glasscn/glass-separator.d.ts +5 -0
- package/dist-lib/components/ui/group.d.ts +17 -0
- package/dist-lib/components/ui/separator.d.ts +2 -3
- package/dist-lib/components/ui/sheet.d.ts +14 -0
- package/dist-lib/components/ui/tooltip.d.ts +6 -0
- package/dist-lib/index.d.ts +2 -1
- package/dist-lib/index.js +1631 -1101
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/ease.d.ts +3 -0
- package/dist-lib/lib/hooks/use-hover-capable.d.ts +6 -0
- package/package.json +2 -1
- package/src/components/beste/piece/browser-frame.tsx +2 -2
- package/src/components/blocks/nav/sidebar-block.tsx +5 -5
- package/src/components/blocks/nav/sidebar-nav-row.tsx +4 -5
- package/src/components/blocks/scaffold/__tests__/scaffold.test.tsx +286 -0
- package/src/components/blocks/scaffold/constants.ts +32 -0
- package/src/components/blocks/scaffold/index.ts +41 -0
- package/src/components/blocks/scaffold/panel-close-button.tsx +36 -0
- package/src/components/blocks/scaffold/panel-menu-button.tsx +218 -0
- package/src/components/blocks/scaffold/scaffold-actions.tsx +88 -0
- package/src/components/blocks/scaffold/scaffold-canvas.tsx +29 -0
- package/src/components/blocks/scaffold/scaffold-context.ts +18 -0
- package/src/components/blocks/scaffold/scaffold-inspector.tsx +80 -0
- package/src/components/blocks/scaffold/scaffold-main.tsx +28 -0
- package/src/components/blocks/scaffold/scaffold-panel.tsx +88 -0
- package/src/components/blocks/scaffold/scaffold-root.tsx +68 -0
- package/src/components/blocks/scaffold/scaffold-sidebar.tsx +31 -0
- package/src/components/blocks/scaffold/scaffold-sub-panel.tsx +51 -0
- package/src/components/blocks/scaffold/scaffold-tab.tsx +66 -0
- package/src/components/blocks/scaffold/type.ts +172 -0
- package/src/components/blocks/scaffold/use-scaffold.ts +48 -0
- package/src/components/blocks/scaffold/utils.ts +46 -0
- package/src/components/blocks/shell/animated-panel-inset.tsx +23 -0
- package/src/components/blocks/shell/animated-panel-provider.tsx +155 -0
- package/src/components/blocks/shell/animated-panel-trigger.tsx +42 -0
- package/src/components/blocks/shell/animated-panel.tsx +135 -0
- package/src/components/blocks/shell/animated-sidebar-content.tsx +19 -0
- package/src/components/blocks/shell/animated-sidebar-footer.tsx +19 -0
- package/src/components/blocks/shell/animated-sidebar-group-content.tsx +16 -0
- package/src/components/blocks/shell/animated-sidebar-group-label.tsx +29 -0
- package/src/components/blocks/shell/animated-sidebar-group.tsx +16 -0
- package/src/components/blocks/shell/animated-sidebar-header.tsx +16 -0
- package/src/components/blocks/shell/animated-sidebar-menu-button.tsx +151 -0
- package/src/components/blocks/shell/animated-sidebar-menu-item.tsx +20 -0
- package/src/components/blocks/shell/animated-sidebar-menu-sub-button.tsx +85 -0
- package/src/components/blocks/shell/animated-sidebar-menu-sub-item.tsx +19 -0
- package/src/components/blocks/shell/animated-sidebar-menu-sub.tsx +44 -0
- package/src/components/blocks/shell/animated-sidebar-menu.tsx +26 -0
- package/src/components/blocks/shell/index.ts +18 -0
- package/src/components/blocks/shell/shell-layout.tsx +28 -36
- package/src/components/blocks/shell/type.ts +112 -6
- package/src/components/blocks/shell/use-shell-panels.ts +1 -1
- package/src/components/blocks/shell/utils.ts +142 -1
- package/src/components/docs/demo-controls.tsx +1 -1
- package/src/components/motion/index.ts +0 -0
- package/src/components/motion/overflow-actions.tsx +376 -0
- package/src/components/ui/button-group.tsx +88 -0
- package/src/components/ui/glasscn/glass-button-group.tsx +48 -0
- package/src/components/ui/glasscn/glass-container.tsx +125 -0
- package/src/components/ui/glasscn/glass-separator.tsx +39 -0
- package/src/components/ui/glasscn/liquid-glass.tsx +250 -210
- package/src/components/ui/group.tsx +92 -0
- package/src/components/ui/separator.tsx +11 -11
- package/src/components/ui/sheet.tsx +135 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/index.css +1 -1
- package/src/index.ts +13 -1
- package/src/lib/ease.ts +26 -0
- package/src/lib/hooks/use-hover-capable.ts +23 -0
- package/dist-lib/components/motion/animated-sidebar.d.ts +0 -106
- package/src/components/motion/animated-sidebar.tsx +0 -1171
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
// beui.dev/components/motion/animated-sidebar
|
|
3
|
+
|
|
4
|
+
import { motion } from "motion/react"
|
|
5
|
+
import { EASE_OUT } from "@/lib/ease.ts"
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
import type { AnimatedSidebarProps } from "./type"
|
|
8
|
+
import {
|
|
9
|
+
AnimatedSidebarPanelContext,
|
|
10
|
+
SIDEBAR_MORPH_TRANSITION,
|
|
11
|
+
PANEL_TRANSITION,
|
|
12
|
+
REDUCED_TRANSITION,
|
|
13
|
+
useAnimatedSidebar
|
|
14
|
+
} from "./utils"
|
|
15
|
+
|
|
16
|
+
export function AnimatedPanel({
|
|
17
|
+
side = "left",
|
|
18
|
+
variant = "sidebar",
|
|
19
|
+
collapsible = "icon",
|
|
20
|
+
ariaLabel = "Sidebar",
|
|
21
|
+
children,
|
|
22
|
+
className,
|
|
23
|
+
panelClassName,
|
|
24
|
+
style,
|
|
25
|
+
ref,
|
|
26
|
+
...props
|
|
27
|
+
}: AnimatedSidebarProps) {
|
|
28
|
+
const context = useAnimatedSidebar()
|
|
29
|
+
const sideOpen = context.open[side]
|
|
30
|
+
const collapsed = collapsible !== "none" && !sideOpen
|
|
31
|
+
const offcanvas = collapsed && collapsible === "offcanvas"
|
|
32
|
+
const width = offcanvas
|
|
33
|
+
? "0px"
|
|
34
|
+
: collapsed
|
|
35
|
+
? "var(--sidebar-width-icon)"
|
|
36
|
+
: "var(--sidebar-width)"
|
|
37
|
+
|
|
38
|
+
if (context.isMobile) return <div>TODO: Create Mobile nav experience</div>
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<motion.aside
|
|
42
|
+
{...props}
|
|
43
|
+
ref={ref}
|
|
44
|
+
initial={false}
|
|
45
|
+
aria-label={ariaLabel}
|
|
46
|
+
data-slot="sidebar"
|
|
47
|
+
data-state={collapsed ? "collapsed" : "expanded"}
|
|
48
|
+
data-collapsible={collapsible}
|
|
49
|
+
data-variant={variant}
|
|
50
|
+
data-side={side}
|
|
51
|
+
animate={{
|
|
52
|
+
width,
|
|
53
|
+
// Swallow the shell's gap-x-2 column gap while off canvas so the
|
|
54
|
+
// shell edge keeps a constant spacing-2 inset whether the panel is
|
|
55
|
+
// open (gap + panel + margin) or collapsed (gap + this).
|
|
56
|
+
...(side === "right"
|
|
57
|
+
? { marginLeft: offcanvas ? "-0.5rem" : "0rem" }
|
|
58
|
+
: { marginRight: offcanvas ? "-0.5rem" : "0rem" }),
|
|
59
|
+
}}
|
|
60
|
+
transition={context.reduce ? { duration: 0 } : SIDEBAR_MORPH_TRANSITION}
|
|
61
|
+
style={style}
|
|
62
|
+
className={cn(
|
|
63
|
+
"group/sidebar relative hidden h-full shrink-0 will-change-[width] md:block",
|
|
64
|
+
"peer flex!",
|
|
65
|
+
side === "right" && "order-last",
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
{side === "right" && !collapsed && (
|
|
70
|
+
// The handle occupies the shell's gap-x-2 column gap. It must stay
|
|
71
|
+
// outside the clipping mask below or the gap would never receive
|
|
72
|
+
// hover — clipping cuts hit-testing, not just painting.
|
|
73
|
+
<motion.div
|
|
74
|
+
className="absolute inset-y-0 -left-2 z-10 w-2 cursor-col-resize"
|
|
75
|
+
initial="idle"
|
|
76
|
+
whileHover="hover"
|
|
77
|
+
>
|
|
78
|
+
<motion.div
|
|
79
|
+
variants={{ idle: { scaleY: 0 }, hover: { scaleY: 1 } }}
|
|
80
|
+
transition={
|
|
81
|
+
context.reduce
|
|
82
|
+
? { duration: 0 }
|
|
83
|
+
: { duration: 0.2, ease: EASE_OUT }
|
|
84
|
+
}
|
|
85
|
+
className="h-full w-full bg-radial/decreasing from-amber-300/70 to-amber-500/0 to-40%"
|
|
86
|
+
/>
|
|
87
|
+
</motion.div>
|
|
88
|
+
)}
|
|
89
|
+
{/* The off canvas panel keeps its full width and slides out of the
|
|
90
|
+
zero-width rail, so mask it to the aside's box here instead of
|
|
91
|
+
clipping the aside itself — the aside must stay unclipped for the
|
|
92
|
+
gap handle above to be hoverable. */}
|
|
93
|
+
<div
|
|
94
|
+
className={
|
|
95
|
+
collapsible === "offcanvas"
|
|
96
|
+
? "absolute inset-0 overflow-hidden rounded-[inherit]"
|
|
97
|
+
: "contents"
|
|
98
|
+
}
|
|
99
|
+
>
|
|
100
|
+
<motion.div
|
|
101
|
+
initial={false}
|
|
102
|
+
animate={{
|
|
103
|
+
opacity: offcanvas ? 0 : 1,
|
|
104
|
+
x: offcanvas ? (side === "left" ? "-100%" : "100%") : "0%",
|
|
105
|
+
}}
|
|
106
|
+
transition={context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION}
|
|
107
|
+
className={cn(
|
|
108
|
+
"sticky top-0 flex flex-col overflow-hidden",
|
|
109
|
+
// collapsible === "offcanvas" && "w-(--sidebar-width)",
|
|
110
|
+
// Pin the right panel to the aside's trailing edge so the width
|
|
111
|
+
// animation expands the panel out of it instead of revealing a
|
|
112
|
+
// detached strip.
|
|
113
|
+
side === "right" && "ml-auto",
|
|
114
|
+
|
|
115
|
+
// Panels fill the shell's container, not the viewport — the shell
|
|
116
|
+
// owns its own scrolling, so a bounded host (docs demo, split
|
|
117
|
+
// view) must bound the panels too. Full-page apps size the shell
|
|
118
|
+
// itself (e.g., h-svh). The bottom gap comes from the shell
|
|
119
|
+
// container's padding, so panels only carry their top offset.
|
|
120
|
+
variant === "floating" && "mx-2 mt-2 h-[calc(100%-0.5rem)]",
|
|
121
|
+
variant === "inset" && "mt-2 h-[calc(100%-0.5rem)]",
|
|
122
|
+
|
|
123
|
+
panelClassName
|
|
124
|
+
)}
|
|
125
|
+
>
|
|
126
|
+
<AnimatedSidebarPanelContext.Provider
|
|
127
|
+
value={{ collapsed, collapsible, side }}
|
|
128
|
+
>
|
|
129
|
+
{children}
|
|
130
|
+
</AnimatedSidebarPanelContext.Provider>
|
|
131
|
+
</motion.div>
|
|
132
|
+
</div>
|
|
133
|
+
</motion.aside>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export const AnimatedSidebarContent = forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(function AnimatedSidebarContent({ className, ...props }, forwardedRef) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
{...props}
|
|
11
|
+
ref={forwardedRef}
|
|
12
|
+
data-slot="sidebar-content"
|
|
13
|
+
className={cn(
|
|
14
|
+
"flex min-h-0 flex-1 flex-col gap-2 overflow-x-hidden overflow-y-auto overscroll-contain px-2 py-2",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export const AnimatedSidebarFooter = forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(function AnimatedSidebarFooter({ className, ...props }, forwardedRef) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
{...props}
|
|
11
|
+
ref={forwardedRef}
|
|
12
|
+
data-slot="sidebar-footer"
|
|
13
|
+
className={cn(
|
|
14
|
+
"flex shrink-0 flex-col gap-2 border-t border-border p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export const AnimatedSidebarGroupContent = forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(function AnimatedSidebarGroupContent({ className, ...props }, forwardedRef) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
{...props}
|
|
11
|
+
ref={forwardedRef}
|
|
12
|
+
data-slot="sidebar-group-content"
|
|
13
|
+
className={cn("w-full min-w-0", className)}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
import { useAnimatedSidebarPanel } from "./utils"
|
|
4
|
+
|
|
5
|
+
export const AnimatedSidebarGroupLabel = forwardRef<
|
|
6
|
+
HTMLDivElement,
|
|
7
|
+
HTMLAttributes<HTMLDivElement>
|
|
8
|
+
>(function AnimatedSidebarGroupLabel(
|
|
9
|
+
{ children, className, ...props },
|
|
10
|
+
forwardedRef
|
|
11
|
+
) {
|
|
12
|
+
const { collapsed } = useAnimatedSidebarPanel()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
{...props}
|
|
17
|
+
ref={forwardedRef}
|
|
18
|
+
aria-hidden={collapsed}
|
|
19
|
+
data-slot="sidebar-group-label"
|
|
20
|
+
className={cn(
|
|
21
|
+
"mb-1 h-7 overflow-hidden px-2 text-[10px] font-medium tracking-[0.14em] text-muted-foreground uppercase transition-opacity",
|
|
22
|
+
collapsed ? "opacity-0" : "opacity-100",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
{children}
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export const AnimatedSidebarGroup = forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(function AnimatedSidebarGroup({ className, ...props }, forwardedRef) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
{...props}
|
|
11
|
+
ref={forwardedRef}
|
|
12
|
+
data-slot="sidebar-group"
|
|
13
|
+
className={cn("flex w-full min-w-0 flex-col px-1 py-1.5", className)}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export const AnimatedSidebarHeader = forwardRef<
|
|
5
|
+
HTMLDivElement,
|
|
6
|
+
HTMLAttributes<HTMLDivElement>
|
|
7
|
+
>(function AnimatedSidebarHeader({ className, ...props }, forwardedRef) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
{...props}
|
|
11
|
+
ref={forwardedRef}
|
|
12
|
+
data-slot="sidebar-header"
|
|
13
|
+
className={cn("flex shrink-0 flex-col gap-2 p-3", className)}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
})
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type * as React from "react"
|
|
2
|
+
import { ChevronRight } from "lucide-react"
|
|
3
|
+
import { motion } from "motion/react"
|
|
4
|
+
import { SPRING_LAYOUT, SPRING_PRESS } from "@/lib/ease"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
import type { AnimatedSidebarMenuButtonProps } from "./type"
|
|
7
|
+
import {
|
|
8
|
+
LABEL_ENTER_TRANSITION,
|
|
9
|
+
LABEL_EXIT_TRANSITION,
|
|
10
|
+
REDUCED_TRANSITION,
|
|
11
|
+
useAnimatedSidebar,
|
|
12
|
+
useAnimatedSidebarPanel,
|
|
13
|
+
} from "./utils"
|
|
14
|
+
|
|
15
|
+
export function AnimatedSidebarMenuButton({
|
|
16
|
+
children,
|
|
17
|
+
icon,
|
|
18
|
+
badge,
|
|
19
|
+
href,
|
|
20
|
+
isActive = false,
|
|
21
|
+
ariaExpanded,
|
|
22
|
+
disabled = false,
|
|
23
|
+
closeOnSelect,
|
|
24
|
+
target,
|
|
25
|
+
rel,
|
|
26
|
+
onSelect,
|
|
27
|
+
className,
|
|
28
|
+
}: AnimatedSidebarMenuButtonProps) {
|
|
29
|
+
const context = useAnimatedSidebar()
|
|
30
|
+
const panel = useAnimatedSidebarPanel()
|
|
31
|
+
const textLabel = typeof children === "string" ? children : undefined
|
|
32
|
+
|
|
33
|
+
const select = (
|
|
34
|
+
event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>
|
|
35
|
+
) => {
|
|
36
|
+
if (disabled) {
|
|
37
|
+
event.preventDefault()
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
onSelect?.()
|
|
41
|
+
const shouldCloseOnSelect = closeOnSelect ?? ariaExpanded === undefined
|
|
42
|
+
if (context.isMobile && shouldCloseOnSelect) {
|
|
43
|
+
context.setOpenMobile(false, panel.side)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const content = (
|
|
48
|
+
<>
|
|
49
|
+
{isActive ? (
|
|
50
|
+
<motion.span
|
|
51
|
+
layoutId={context.layoutId}
|
|
52
|
+
transition={context.reduce ? { duration: 0 } : SPRING_LAYOUT}
|
|
53
|
+
className="absolute inset-0 rounded-xl bg-muted"
|
|
54
|
+
/>
|
|
55
|
+
) : null}
|
|
56
|
+
{icon ? (
|
|
57
|
+
<span
|
|
58
|
+
aria-hidden="true"
|
|
59
|
+
className="relative z-10 grid size-5 shrink-0 place-items-center"
|
|
60
|
+
>
|
|
61
|
+
{icon}
|
|
62
|
+
</span>
|
|
63
|
+
) : null}
|
|
64
|
+
<motion.span
|
|
65
|
+
initial={false}
|
|
66
|
+
animate={{
|
|
67
|
+
opacity: panel.collapsed ? 0 : 1,
|
|
68
|
+
x: panel.collapsed ? -4 : 0,
|
|
69
|
+
}}
|
|
70
|
+
transition={
|
|
71
|
+
context.reduce
|
|
72
|
+
? REDUCED_TRANSITION
|
|
73
|
+
: panel.collapsed
|
|
74
|
+
? LABEL_EXIT_TRANSITION
|
|
75
|
+
: LABEL_ENTER_TRANSITION
|
|
76
|
+
}
|
|
77
|
+
aria-hidden={panel.collapsed}
|
|
78
|
+
className={cn(
|
|
79
|
+
"relative z-10 min-w-0 flex-1 truncate",
|
|
80
|
+
panel.collapsed && "pointer-events-none"
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</motion.span>
|
|
85
|
+
{badge && !panel.collapsed ? (
|
|
86
|
+
<span className="relative z-10 shrink-0 text-xs text-muted-foreground">
|
|
87
|
+
{badge}
|
|
88
|
+
</span>
|
|
89
|
+
) : null}
|
|
90
|
+
{ariaExpanded !== undefined ? (
|
|
91
|
+
<motion.span
|
|
92
|
+
aria-hidden="true"
|
|
93
|
+
initial={false}
|
|
94
|
+
animate={{
|
|
95
|
+
opacity: panel.collapsed ? 0 : 1,
|
|
96
|
+
rotate: ariaExpanded ? 90 : 0,
|
|
97
|
+
x: panel.collapsed ? 4 : 0,
|
|
98
|
+
}}
|
|
99
|
+
transition={context.reduce ? { duration: 0 } : SPRING_LAYOUT}
|
|
100
|
+
className="relative z-10 grid size-4 shrink-0 place-items-center text-muted-foreground"
|
|
101
|
+
>
|
|
102
|
+
<ChevronRight className="size-3.5" />
|
|
103
|
+
</motion.span>
|
|
104
|
+
) : null}
|
|
105
|
+
</>
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const interactiveClassName = cn(
|
|
109
|
+
"relative flex min-h-9 w-full min-w-0 items-center gap-2.5 overflow-hidden rounded-xl px-3 text-left text-sm font-medium outline-none",
|
|
110
|
+
"text-muted-foreground transition-colors hover:text-foreground",
|
|
111
|
+
"focus-visible:bg-muted/70 focus-visible:ring-2 focus-visible:ring-ring",
|
|
112
|
+
isActive && "text-foreground",
|
|
113
|
+
disabled && "cursor-not-allowed opacity-40",
|
|
114
|
+
className
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return href ? (
|
|
118
|
+
<motion.a
|
|
119
|
+
href={href}
|
|
120
|
+
target={target}
|
|
121
|
+
rel={rel ?? (target === "_blank" ? "noreferrer noopener" : undefined)}
|
|
122
|
+
aria-current={isActive ? "page" : undefined}
|
|
123
|
+
aria-expanded={ariaExpanded}
|
|
124
|
+
aria-disabled={disabled || undefined}
|
|
125
|
+
aria-label={panel.collapsed ? textLabel : undefined}
|
|
126
|
+
title={panel.collapsed ? textLabel : undefined}
|
|
127
|
+
tabIndex={disabled ? -1 : undefined}
|
|
128
|
+
onClick={select}
|
|
129
|
+
whileTap={context.reduce || disabled ? undefined : { scale: 0.98 }}
|
|
130
|
+
transition={SPRING_PRESS}
|
|
131
|
+
className={interactiveClassName}
|
|
132
|
+
>
|
|
133
|
+
{content}
|
|
134
|
+
</motion.a>
|
|
135
|
+
) : (
|
|
136
|
+
<motion.button
|
|
137
|
+
type="button"
|
|
138
|
+
disabled={disabled}
|
|
139
|
+
aria-current={isActive ? "page" : undefined}
|
|
140
|
+
aria-expanded={ariaExpanded}
|
|
141
|
+
aria-label={panel.collapsed ? textLabel : undefined}
|
|
142
|
+
title={panel.collapsed ? textLabel : undefined}
|
|
143
|
+
onClick={select}
|
|
144
|
+
whileTap={context.reduce || disabled ? undefined : { scale: 0.98 }}
|
|
145
|
+
transition={SPRING_PRESS}
|
|
146
|
+
className={interactiveClassName}
|
|
147
|
+
>
|
|
148
|
+
{content}
|
|
149
|
+
</motion.button>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { forwardRef } from "react"
|
|
2
|
+
import { motion, type HTMLMotionProps } from "motion/react"
|
|
3
|
+
import { SPRING_LAYOUT } from "@/lib/ease"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
export const AnimatedSidebarMenuItem = forwardRef<
|
|
7
|
+
HTMLLIElement,
|
|
8
|
+
HTMLMotionProps<"li">
|
|
9
|
+
>(function AnimatedSidebarMenuItem({ className, ...props }, forwardedRef) {
|
|
10
|
+
return (
|
|
11
|
+
<motion.li
|
|
12
|
+
{...props}
|
|
13
|
+
ref={forwardedRef}
|
|
14
|
+
layout="position"
|
|
15
|
+
transition={SPRING_LAYOUT}
|
|
16
|
+
data-slot="sidebar-menu-item"
|
|
17
|
+
className={cn("relative", className)}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type * as React from "react"
|
|
2
|
+
import { motion } from "motion/react"
|
|
3
|
+
import { SPRING_PRESS } from "@/lib/ease"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import type { AnimatedSidebarMenuSubButtonProps } from "./type"
|
|
6
|
+
import { useAnimatedSidebar, useAnimatedSidebarPanel } from "./utils"
|
|
7
|
+
|
|
8
|
+
export function AnimatedSidebarMenuSubButton({
|
|
9
|
+
children,
|
|
10
|
+
icon,
|
|
11
|
+
href,
|
|
12
|
+
isActive = false,
|
|
13
|
+
disabled = false,
|
|
14
|
+
closeOnSelect = true,
|
|
15
|
+
target,
|
|
16
|
+
rel,
|
|
17
|
+
onSelect,
|
|
18
|
+
className,
|
|
19
|
+
}: AnimatedSidebarMenuSubButtonProps) {
|
|
20
|
+
const context = useAnimatedSidebar()
|
|
21
|
+
const panel = useAnimatedSidebarPanel()
|
|
22
|
+
|
|
23
|
+
const select = (
|
|
24
|
+
event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>
|
|
25
|
+
) => {
|
|
26
|
+
if (disabled) {
|
|
27
|
+
event.preventDefault()
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
onSelect?.()
|
|
31
|
+
if (context.isMobile && closeOnSelect) {
|
|
32
|
+
context.setOpenMobile(false, panel.side)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const content = (
|
|
37
|
+
<>
|
|
38
|
+
<span
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
className="grid size-4 shrink-0 place-items-center"
|
|
41
|
+
>
|
|
42
|
+
{icon ?? <span className="size-1 rounded-full bg-current" />}
|
|
43
|
+
</span>
|
|
44
|
+
<span className="min-w-0 flex-1 truncate">{children}</span>
|
|
45
|
+
</>
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const interactiveClassName = cn(
|
|
49
|
+
"flex min-h-8 w-full min-w-0 items-center gap-2 rounded-lg px-2 text-left text-xs outline-none",
|
|
50
|
+
"text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
|
|
51
|
+
"focus-visible:bg-muted/70 focus-visible:ring-2 focus-visible:ring-ring",
|
|
52
|
+
isActive && "bg-muted/70 text-foreground",
|
|
53
|
+
disabled && "cursor-not-allowed opacity-40",
|
|
54
|
+
className
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return href ? (
|
|
58
|
+
<motion.a
|
|
59
|
+
href={href}
|
|
60
|
+
target={target}
|
|
61
|
+
rel={rel ?? (target === "_blank" ? "noreferrer noopener" : undefined)}
|
|
62
|
+
aria-current={isActive ? "page" : undefined}
|
|
63
|
+
aria-disabled={disabled || undefined}
|
|
64
|
+
tabIndex={disabled ? -1 : undefined}
|
|
65
|
+
onClick={select}
|
|
66
|
+
whileTap={context.reduce || disabled ? undefined : { scale: 0.98 }}
|
|
67
|
+
transition={SPRING_PRESS}
|
|
68
|
+
className={interactiveClassName}
|
|
69
|
+
>
|
|
70
|
+
{content}
|
|
71
|
+
</motion.a>
|
|
72
|
+
) : (
|
|
73
|
+
<motion.button
|
|
74
|
+
type="button"
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
aria-current={isActive ? "page" : undefined}
|
|
77
|
+
onClick={select}
|
|
78
|
+
whileTap={context.reduce || disabled ? undefined : { scale: 0.98 }}
|
|
79
|
+
transition={SPRING_PRESS}
|
|
80
|
+
className={interactiveClassName}
|
|
81
|
+
>
|
|
82
|
+
{content}
|
|
83
|
+
</motion.button>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { forwardRef } from "react"
|
|
2
|
+
import { motion, type HTMLMotionProps } from "motion/react"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
import { SUBMENU_ITEM_VARIANTS } from "./utils"
|
|
5
|
+
|
|
6
|
+
export const AnimatedSidebarMenuSubItem = forwardRef<
|
|
7
|
+
HTMLLIElement,
|
|
8
|
+
HTMLMotionProps<"li">
|
|
9
|
+
>(function AnimatedSidebarMenuSubItem({ className, ...props }, forwardedRef) {
|
|
10
|
+
return (
|
|
11
|
+
<motion.li
|
|
12
|
+
{...props}
|
|
13
|
+
ref={forwardedRef}
|
|
14
|
+
variants={SUBMENU_ITEM_VARIANTS}
|
|
15
|
+
data-slot="sidebar-menu-sub-item"
|
|
16
|
+
className={cn("relative min-w-0", className)}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { forwardRef } from "react"
|
|
2
|
+
import { AnimatePresence, motion } from "motion/react"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
import type { AnimatedSidebarMenuSubProps } from "./type"
|
|
5
|
+
import {
|
|
6
|
+
SUBMENU_VARIANTS,
|
|
7
|
+
useAnimatedSidebar,
|
|
8
|
+
useAnimatedSidebarPanel,
|
|
9
|
+
} from "./utils"
|
|
10
|
+
|
|
11
|
+
export const AnimatedSidebarMenuSub = forwardRef<
|
|
12
|
+
HTMLUListElement,
|
|
13
|
+
AnimatedSidebarMenuSubProps
|
|
14
|
+
>(function AnimatedSidebarMenuSub(
|
|
15
|
+
{ open, children, className, ...props },
|
|
16
|
+
forwardedRef
|
|
17
|
+
) {
|
|
18
|
+
const context = useAnimatedSidebar()
|
|
19
|
+
const panel = useAnimatedSidebarPanel()
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<AnimatePresence initial={false} mode="popLayout">
|
|
23
|
+
{open && !panel.collapsed ? (
|
|
24
|
+
<motion.ul
|
|
25
|
+
{...props}
|
|
26
|
+
ref={forwardedRef}
|
|
27
|
+
key="sidebar-submenu"
|
|
28
|
+
variants={context.reduce ? undefined : SUBMENU_VARIANTS}
|
|
29
|
+
initial={context.reduce ? false : "closed"}
|
|
30
|
+
animate={context.reduce ? { opacity: 1 } : "open"}
|
|
31
|
+
exit={context.reduce ? { opacity: 0 } : "closed"}
|
|
32
|
+
transition={context.reduce ? { duration: 0.12 } : undefined}
|
|
33
|
+
data-slot="sidebar-menu-sub"
|
|
34
|
+
className={cn(
|
|
35
|
+
"relative mt-1 ml-5 flex min-w-0 flex-col gap-0.5 border-l border-border pl-3",
|
|
36
|
+
className
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</motion.ul>
|
|
41
|
+
) : null}
|
|
42
|
+
</AnimatePresence>
|
|
43
|
+
)
|
|
44
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes, type Ref } from "react"
|
|
2
|
+
import { SharedLayoutBg } from "@/components/motion/shared-layout-bg"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
export const AnimatedSidebarMenu = forwardRef<
|
|
6
|
+
HTMLUListElement,
|
|
7
|
+
HTMLAttributes<HTMLUListElement>
|
|
8
|
+
>(function AnimatedSidebarMenu(
|
|
9
|
+
{ children, className, ...props },
|
|
10
|
+
forwardedRef
|
|
11
|
+
) {
|
|
12
|
+
return (
|
|
13
|
+
<SharedLayoutBg
|
|
14
|
+
{...props}
|
|
15
|
+
ref={forwardedRef as Ref<HTMLElement>}
|
|
16
|
+
as="ul"
|
|
17
|
+
inset={0}
|
|
18
|
+
pillClassName="rounded-xl bg-muted/70"
|
|
19
|
+
pillContainerClassName="inset-y-auto top-0 h-9"
|
|
20
|
+
data-slot="sidebar-menu"
|
|
21
|
+
className={cn("flex w-full min-w-0 list-none flex-col gap-0.5", className)}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</SharedLayoutBg>
|
|
25
|
+
)
|
|
26
|
+
})
|
|
@@ -4,4 +4,22 @@ export type * from "./type"
|
|
|
4
4
|
export * from "./use-shell-panels"
|
|
5
5
|
export * from "./utils"
|
|
6
6
|
|
|
7
|
+
export * from "./animated-panel"
|
|
8
|
+
export * from "./animated-panel-inset"
|
|
9
|
+
export * from "./animated-panel-provider"
|
|
10
|
+
export * from "./animated-panel-trigger"
|
|
11
|
+
|
|
12
|
+
export * from "./animated-sidebar-header"
|
|
13
|
+
export * from "./animated-sidebar-content"
|
|
14
|
+
export * from "./animated-sidebar-footer"
|
|
15
|
+
export * from "./animated-sidebar-group"
|
|
16
|
+
export * from "./animated-sidebar-group-label"
|
|
17
|
+
export * from "./animated-sidebar-group-content"
|
|
18
|
+
export * from "./animated-sidebar-menu"
|
|
19
|
+
export * from "./animated-sidebar-menu-item"
|
|
20
|
+
export * from "./animated-sidebar-menu-sub"
|
|
21
|
+
export * from "./animated-sidebar-menu-sub-item"
|
|
22
|
+
export * from "./animated-sidebar-menu-sub-button"
|
|
23
|
+
export * from "./animated-sidebar-menu-button"
|
|
24
|
+
|
|
7
25
|
export { ShellLayout as default }
|