@exegia/corpora-ui 0.21.0 → 0.23.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.
Files changed (94) hide show
  1. package/dist-lib/components/blocks/auth/auth-flow-block.d.ts +123 -0
  2. package/dist-lib/components/blocks/layout.d.ts +4 -1
  3. package/dist-lib/components/blocks/profile/profile-card-block.d.ts +1 -1
  4. package/dist-lib/components/blocks/scaffold/__tests__/scaffold-atom.test.d.ts +1 -0
  5. package/dist-lib/components/blocks/scaffold/index.d.ts +2 -0
  6. package/dist-lib/components/blocks/scaffold/scaffold-atom.d.ts +97 -0
  7. package/dist-lib/components/blocks/scaffold/scaffold-context.d.ts +3 -1
  8. package/dist-lib/components/blocks/scaffold/scaffold-root.d.ts +12 -3
  9. package/dist-lib/components/blocks/scaffold/type.d.ts +58 -18
  10. package/dist-lib/components/blocks/scaffold/use-scaffold-state.d.ts +26 -0
  11. package/dist-lib/components/blocks/scaffold/use-scaffold.d.ts +7 -1
  12. package/dist-lib/components/blocks/scaffold/utils.d.ts +11 -1
  13. package/dist-lib/components/blocks/shell/index.d.ts +0 -12
  14. package/dist-lib/components/composed/__tests__/logo.test.d.ts +1 -0
  15. package/dist-lib/components/composed/logo.d.ts +34 -0
  16. package/dist-lib/components/ui/switch.d.ts +4 -0
  17. package/dist-lib/components/user-avatar/type.d.ts +10 -2
  18. package/dist-lib/components/user-avatar/use-user-avatar.d.ts +5 -0
  19. package/dist-lib/components/user-avatar/user-avatar-atom.d.ts +9 -0
  20. package/dist-lib/components/user-avatar/utils.d.ts +22 -0
  21. package/dist-lib/index.d.ts +2 -1
  22. package/dist-lib/index.js +3889 -3848
  23. package/dist-lib/index.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/components/beste/piece/browser-frame.tsx +1 -1
  26. package/src/components/beste/piece/utils.ts +4 -4
  27. package/src/components/blocks/auth/__tests__/auth-flow-block.test.tsx +183 -0
  28. package/src/components/blocks/auth/auth-flow-block.tsx +296 -0
  29. package/src/components/blocks/layout.ts +30 -2
  30. package/src/components/blocks/profile/profile-card-block.tsx +1 -1
  31. package/src/components/blocks/scaffold/__tests__/scaffold-atom.test.tsx +210 -0
  32. package/src/components/blocks/scaffold/index.ts +15 -0
  33. package/src/components/blocks/scaffold/scaffold-actions.tsx +5 -1
  34. package/src/components/blocks/scaffold/scaffold-atom.ts +383 -0
  35. package/src/components/blocks/scaffold/scaffold-canvas.tsx +12 -4
  36. package/src/components/blocks/scaffold/scaffold-context.ts +3 -1
  37. package/src/components/blocks/scaffold/scaffold-inspector.tsx +9 -2
  38. package/src/components/blocks/scaffold/scaffold-panel.tsx +9 -5
  39. package/src/components/blocks/scaffold/scaffold-root.tsx +64 -32
  40. package/src/components/blocks/scaffold/scaffold-tab.tsx +13 -9
  41. package/src/components/blocks/scaffold/type.ts +64 -18
  42. package/src/components/blocks/scaffold/use-scaffold-state.ts +58 -0
  43. package/src/components/blocks/scaffold/use-scaffold.ts +30 -19
  44. package/src/components/blocks/scaffold/utils.ts +45 -1
  45. package/src/components/blocks/shell/index.ts +0 -13
  46. package/src/components/composed/__tests__/logo.test.tsx +60 -0
  47. package/src/components/composed/logo.tsx +159 -0
  48. package/src/components/docs/component-preview.tsx +7 -2
  49. package/src/components/docs/demo-controls.tsx +32 -23
  50. package/src/components/ui/switch.tsx +30 -0
  51. package/src/components/user-avatar/__tests__/user-avatar.test.tsx +41 -0
  52. package/src/components/user-avatar/component.tsx +38 -8
  53. package/src/components/user-avatar/fallback.tsx +3 -1
  54. package/src/components/user-avatar/type.ts +10 -2
  55. package/src/components/user-avatar/use-user-avatar.ts +38 -2
  56. package/src/components/user-avatar/user-avatar-atom.ts +20 -0
  57. package/src/components/user-avatar/utils.ts +94 -0
  58. package/src/index.ts +2 -1
  59. package/dist-lib/components/blocks/nav/sidebar-block.d.ts +0 -14
  60. package/dist-lib/components/blocks/nav/sidebar-nav-row.d.ts +0 -3
  61. package/dist-lib/components/blocks/nav/types.d.ts +0 -60
  62. package/dist-lib/components/blocks/nav/utils.d.ts +0 -1
  63. package/dist-lib/components/blocks/scaffold/use-panel-visibility.d.ts +0 -19
  64. package/dist-lib/components/blocks/shell/animated-sidebar-content.d.ts +0 -2
  65. package/dist-lib/components/blocks/shell/animated-sidebar-footer.d.ts +0 -2
  66. package/dist-lib/components/blocks/shell/animated-sidebar-group-content.d.ts +0 -2
  67. package/dist-lib/components/blocks/shell/animated-sidebar-group-label.d.ts +0 -2
  68. package/dist-lib/components/blocks/shell/animated-sidebar-group.d.ts +0 -2
  69. package/dist-lib/components/blocks/shell/animated-sidebar-header.d.ts +0 -2
  70. package/dist-lib/components/blocks/shell/animated-sidebar-menu-button.d.ts +0 -3
  71. package/dist-lib/components/blocks/shell/animated-sidebar-menu-item.d.ts +0 -2
  72. package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub-button.d.ts +0 -3
  73. package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub-item.d.ts +0 -2
  74. package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub.d.ts +0 -2
  75. package/dist-lib/components/blocks/shell/animated-sidebar-menu.d.ts +0 -2
  76. package/src/components/blocks/nav/__tests__/sidebar-block.test.tsx +0 -125
  77. package/src/components/blocks/nav/sidebar-block.tsx +0 -142
  78. package/src/components/blocks/nav/sidebar-nav-row.tsx +0 -78
  79. package/src/components/blocks/nav/types.ts +0 -65
  80. package/src/components/blocks/nav/utils.ts +0 -4
  81. package/src/components/blocks/scaffold/use-panel-visibility.ts +0 -180
  82. package/src/components/blocks/shell/animated-sidebar-content.tsx +0 -19
  83. package/src/components/blocks/shell/animated-sidebar-footer.tsx +0 -19
  84. package/src/components/blocks/shell/animated-sidebar-group-content.tsx +0 -16
  85. package/src/components/blocks/shell/animated-sidebar-group-label.tsx +0 -29
  86. package/src/components/blocks/shell/animated-sidebar-group.tsx +0 -16
  87. package/src/components/blocks/shell/animated-sidebar-header.tsx +0 -16
  88. package/src/components/blocks/shell/animated-sidebar-menu-button.tsx +0 -151
  89. package/src/components/blocks/shell/animated-sidebar-menu-item.tsx +0 -20
  90. package/src/components/blocks/shell/animated-sidebar-menu-sub-button.tsx +0 -85
  91. package/src/components/blocks/shell/animated-sidebar-menu-sub-item.tsx +0 -19
  92. package/src/components/blocks/shell/animated-sidebar-menu-sub.tsx +0 -44
  93. package/src/components/blocks/shell/animated-sidebar-menu.tsx +0 -26
  94. /package/dist-lib/components/blocks/{nav/__tests__/sidebar-block.test.d.ts → auth/__tests__/auth-flow-block.test.d.ts} +0 -0
@@ -1,65 +0,0 @@
1
-
2
- /** A nested link, one level under a top-level entry. */
3
- export interface ISidebarNavSubItem {
4
- id: string
5
- label: string
6
- icon?: React.ReactNode
7
- /** Renders an anchor. Without one the row is a button. */
8
- href?: string
9
- disabled?: boolean
10
- target?: "_blank" | "_self" | "_parent" | "_top"
11
- onSelect?: () => void
12
- }
13
-
14
- /** A top-level entry. With `items` it expands instead of navigating. */
15
- export interface ISidebarNavItem extends ISidebarNavSubItem {
16
- /** Trailing hint — a count, a "New" pill. Hidden while collapsed. */
17
- badge?: React.ReactNode
18
- items?: ISidebarNavSubItem[]
19
- /** Start expanded. Defaults to expanded when one of its children is active. */
20
- defaultOpen?: boolean
21
- }
22
-
23
- /** A titled run of entries. The title hides while the rail is collapsed. */
24
- export interface ISidebarNavSection {
25
- id: string
26
- label?: string
27
- items: ISidebarNavItem[]
28
- }
29
-
30
- export interface ISidebarBlockProps {
31
- sections: ISidebarNavSection[]
32
- /** `id` of the current entry — matches top-level and nested ids alike. */
33
- activeId?: string
34
- /** Fires for every selection, after the entry's own `onSelect`. */
35
- onNavigate?: (item: ISidebarNavItem | ISidebarNavSubItem) => void
36
- /** Brand row above the navigation. */
37
- header?: React.ReactNode
38
- /** Pinned below the navigation — an account card, a version note. */
39
- footer?: React.ReactNode
40
- /** Collapse toggle in the header row. */
41
- showTrigger?: boolean
42
- side?: "left" | "right"
43
- variant?: "sidebar" | "floating" | "inset"
44
- /** How the rail collapses: to icons, off-canvas, or not at all. */
45
- collapsible?: "offcanvas" | "icon" | "none"
46
- open?: boolean
47
- defaultOpen?: boolean
48
- onOpenChange?: (open: boolean) => void
49
- /** Under 768px the panel is a drawer — drive it from your own header. */
50
- openMobile?: boolean
51
- defaultOpenMobile?: boolean
52
- onOpenMobileChange?: (open: boolean) => void
53
- /** Rail widths. CSS lengths, e.g. "16rem". */
54
- width?: string
55
- iconWidth?: string
56
- mobileWidth?: string
57
- ariaLabel?: string
58
- className?: string
59
- }
60
-
61
- export interface IRowProps {
62
- item: ISidebarNavItem
63
- activeId?: string
64
- onNavigate?: (item: ISidebarNavItem | ISidebarNavSubItem) => void
65
- }
@@ -1,4 +0,0 @@
1
-
2
- export function isActive(id: string, activeId?: string): boolean {
3
- return activeId !== undefined && id === activeId
4
- }
@@ -1,180 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
-
5
- import { SCAFFOLD_PANEL_CAPACITY } from "./constants"
6
- import { getPanelCapacity } from "./utils"
7
-
8
- /**
9
- * Internal bookkeeping for responsive panel hiding.
10
- *
11
- * - `visibleOrder`: visible panel ids, least-recently-activated first — the
12
- * front is what capacity pressure evicts next.
13
- * - `autoHidden`: panels the scaffold hid because the canvas shrank, most
14
- * recent last. They return on their own when room comes back.
15
- * - `userHidden`: panels hidden by a tab press. They stay hidden until the
16
- * tab is pressed again, however wide the canvas grows.
17
- */
18
- interface PanelVisibilityState {
19
- visibleOrder: string[]
20
- autoHidden: string[]
21
- userHidden: string[]
22
- }
23
-
24
- const EMPTY_STATE: PanelVisibilityState = {
25
- visibleOrder: [],
26
- autoHidden: [],
27
- userHidden: [],
28
- }
29
-
30
- /** Fold the registered ids and current capacity into a settled state:
31
- * prune departed panels, admit new ones, evict the least-recently-activated
32
- * visible panels past capacity, and restore auto-hidden ones when room
33
- * returns. Returns `prev` untouched when nothing changes. */
34
- function reconcile(
35
- prev: PanelVisibilityState,
36
- ids: readonly string[],
37
- capacity: number
38
- ): PanelVisibilityState {
39
- const known = new Set(ids)
40
- const visibleOrder = prev.visibleOrder.filter((id) => known.has(id))
41
- const autoHidden = prev.autoHidden.filter((id) => known.has(id))
42
- const userHidden = prev.userHidden.filter((id) => known.has(id))
43
-
44
- const tracked = new Set([...visibleOrder, ...autoHidden, ...userHidden])
45
- for (const id of ids) if (!tracked.has(id)) visibleOrder.push(id)
46
-
47
- while (visibleOrder.length > capacity && visibleOrder.length > 1) {
48
- autoHidden.push(visibleOrder.shift() as string)
49
- }
50
- while (visibleOrder.length < capacity && autoHidden.length > 0) {
51
- visibleOrder.push(autoHidden.pop() as string)
52
- }
53
-
54
- const unchanged =
55
- visibleOrder.join(" ") === prev.visibleOrder.join(" ") &&
56
- autoHidden.join(" ") === prev.autoHidden.join(" ") &&
57
- userHidden.join(" ") === prev.userHidden.join(" ")
58
- return unchanged ? prev : { visibleOrder, autoHidden, userHidden }
59
- }
60
-
61
- export interface PanelVisibility {
62
- /** How many panels currently fit side by side (1..capacity cap). */
63
- panelCapacity: number
64
- /** Ids of panels currently hidden (auto + user), for tabs to reflect. */
65
- hiddenPanelIds: readonly string[]
66
- isPanelHidden: (id: string) => boolean
67
- togglePanelVisibility: (id: string) => void
68
- /** Id of the panel whose tab is hovered — the canvas spotlights it. */
69
- hoveredPanelId: string | null
70
- /** @internal Tabs report pointer enter/leave on their label. */
71
- setPanelHovered: (id: string, hovered: boolean) => void
72
- /** @internal Canvas reports its ordered panel ids each render. */
73
- registerPanelIds: (ids: readonly string[]) => void
74
- /** @internal Canvas reports its measured width. */
75
- setCanvasWidth: (width: number) => void
76
- }
77
-
78
- /** Owns which panels are hidden as the canvas gains and loses room.
79
- * Lives in `Scaffold.Root`; Canvas and Tab reach it through context. */
80
- export function usePanelVisibility(): PanelVisibility {
81
- const [panelIds, setPanelIds] = React.useState<readonly string[]>([])
82
- const [measuredCapacity, setMeasuredCapacity] = React.useState<number | null>(
83
- null
84
- )
85
- const [state, setState] = React.useState<PanelVisibilityState>(EMPTY_STATE)
86
- const [hoveredPanelId, setHoveredPanelId] = React.useState<string | null>(
87
- null
88
- )
89
-
90
- const panelCapacity = measuredCapacity ?? SCAFFOLD_PANEL_CAPACITY
91
-
92
- const registerPanelIds = React.useCallback((ids: readonly string[]) => {
93
- setPanelIds((prev) =>
94
- prev.length === ids.length && prev.every((id, i) => id === ids[i])
95
- ? prev
96
- : [...ids]
97
- )
98
- }, [])
99
-
100
- // Store the derived capacity, not the raw width — resize storms only
101
- // re-render the tree when a panel actually gains or loses room.
102
- const setCanvasWidth = React.useCallback((width: number) => {
103
- setMeasuredCapacity(getPanelCapacity(width))
104
- }, [])
105
-
106
- // Settle during render (the derive-state-while-rendering pattern): ids
107
- // and capacity arrive as state, so an effect here would only add a
108
- // cascading render after each change.
109
- const settled = reconcile(state, panelIds, panelCapacity)
110
- if (settled !== state) setState(settled)
111
-
112
- const togglePanelVisibility = React.useCallback(
113
- (id: string) => {
114
- setState((prev) => {
115
- if (!panelIds.includes(id)) return prev
116
- const hidden =
117
- prev.autoHidden.includes(id) || prev.userHidden.includes(id)
118
- if (!hidden && prev.visibleOrder.length <= 1) return prev
119
-
120
- const next: PanelVisibilityState = hidden
121
- ? {
122
- // Showing: newest activation — reconcile evicts the oldest
123
- // visible panel if the canvas can't fit one more.
124
- visibleOrder: [...prev.visibleOrder, id],
125
- autoHidden: prev.autoHidden.filter((other) => other !== id),
126
- userHidden: prev.userHidden.filter((other) => other !== id),
127
- }
128
- : {
129
- visibleOrder: prev.visibleOrder.filter((other) => other !== id),
130
- autoHidden: prev.autoHidden,
131
- userHidden: [...prev.userHidden, id],
132
- }
133
- return reconcile(next, panelIds, panelCapacity)
134
- })
135
- },
136
- [panelIds, panelCapacity]
137
- )
138
-
139
- // Leaving clears only its own id — a tab that unmounts mid-hover must
140
- // not wipe out a hover the pointer has already moved on to.
141
- const setPanelHovered = React.useCallback((id: string, hovered: boolean) => {
142
- setHoveredPanelId((prev) => {
143
- if (hovered) return id
144
- return prev === id ? null : prev
145
- })
146
- }, [])
147
-
148
- const hiddenPanelIds = React.useMemo(
149
- () => [...settled.autoHidden, ...settled.userHidden],
150
- [settled]
151
- )
152
-
153
- const isPanelHidden = React.useCallback(
154
- (id: string) => hiddenPanelIds.includes(id),
155
- [hiddenPanelIds]
156
- )
157
-
158
- return React.useMemo(
159
- () => ({
160
- panelCapacity,
161
- hiddenPanelIds,
162
- isPanelHidden,
163
- togglePanelVisibility,
164
- hoveredPanelId,
165
- setPanelHovered,
166
- registerPanelIds,
167
- setCanvasWidth,
168
- }),
169
- [
170
- panelCapacity,
171
- hiddenPanelIds,
172
- isPanelHidden,
173
- togglePanelVisibility,
174
- hoveredPanelId,
175
- setPanelHovered,
176
- registerPanelIds,
177
- setCanvasWidth,
178
- ]
179
- )
180
- }
@@ -1,19 +0,0 @@
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
- })
@@ -1,19 +0,0 @@
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
- })
@@ -1,16 +0,0 @@
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
- })
@@ -1,29 +0,0 @@
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
- })
@@ -1,16 +0,0 @@
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
- })
@@ -1,16 +0,0 @@
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
- })
@@ -1,151 +0,0 @@
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
- }
@@ -1,20 +0,0 @@
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
- })
@@ -1,85 +0,0 @@
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
- }
@@ -1,19 +0,0 @@
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
- })