@tradalab/lyra 1.0.0 → 1.3.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/{breadcrumb-CGzHbSCd.d.ts → breadcrumb-CgEcSonR.d.ts} +42 -1
- package/dist/{chunk-E2QCV5QI.js → chunk-5UAFV75V.js} +415 -16
- package/dist/chunk-5UAFV75V.js.map +1 -0
- package/dist/chunk-CV3UXQBT.js +110 -0
- package/dist/chunk-CV3UXQBT.js.map +1 -0
- package/dist/{chunk-X2BCIKOJ.js → chunk-VEWKPOMS.js} +3 -105
- package/dist/chunk-VEWKPOMS.js.map +1 -0
- package/dist/data-table/index.js +2 -1
- package/dist/data-table/index.js.map +1 -1
- package/dist/shell/index.d.ts +171 -2
- package/dist/shell/index.js +359 -6
- package/dist/shell/index.js.map +1 -1
- package/dist/ui/index.d.ts +3 -42
- package/dist/ui/index.js +13 -411
- package/dist/ui/index.js.map +1 -1
- package/package.json +7 -3
- package/styles/globals.css +3 -3
- package/dist/chunk-E2QCV5QI.js.map +0 -1
- package/dist/chunk-X2BCIKOJ.js.map +0 -1
package/dist/shell/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ComponentProps, ReactNode } from 'react';
|
|
3
|
-
import { S as SidebarProvider, a as SidebarInset, B as Breadcrumb } from '../breadcrumb-
|
|
3
|
+
import { S as SidebarProvider, a as SidebarInset, b as Sidebar, A as AccountMenuUser, c as AccountMenuLabels, B as Breadcrumb } from '../breadcrumb-CgEcSonR.js';
|
|
4
4
|
import 'class-variance-authority/types';
|
|
5
5
|
import 'class-variance-authority';
|
|
6
6
|
import 'radix-ui';
|
|
@@ -39,6 +39,47 @@ declare function AppShellHeader({ className, children, ...rest }: ComponentProps
|
|
|
39
39
|
/** Scroll body inside `<AppShellMain>`. Sibling of `<AppShellHeader>`. */
|
|
40
40
|
declare function AppShellBody({ className, children, ...rest }: ComponentProps<"div">): React.JSX.Element;
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Dual / stacked sidebar wrapper — an icon rail plus one or more content
|
|
44
|
+
* panels rendered side-by-side inside a single `<Sidebar>`. This is the
|
|
45
|
+
* shadcn "sidebar-07" pattern.
|
|
46
|
+
*
|
|
47
|
+
* The `*:data-[sidebar=sidebar]:flex-row` trick lays the inner
|
|
48
|
+
* `[data-sidebar=sidebar]` wrappers of the nested {@link SidebarDock} /
|
|
49
|
+
* {@link SidebarPanel} columns out horizontally. `variant`/`collapsible`
|
|
50
|
+
* default to `"sidebar"`/`"icon"` but can be overridden.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* import { AppShell, AppShellMain, StackedSidebar, SidebarDock, SidebarPanel } from "@tradalab/lyra/shell"
|
|
54
|
+
* import { SidebarHeader, SidebarContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton } from "@tradalab/lyra/ui"
|
|
55
|
+
*
|
|
56
|
+
* <AppShell>
|
|
57
|
+
* <StackedSidebar>
|
|
58
|
+
* <SidebarDock>
|
|
59
|
+
* <SidebarHeader>{logo}</SidebarHeader>
|
|
60
|
+
* <SidebarContent>{iconButtons}</SidebarContent>
|
|
61
|
+
* </SidebarDock>
|
|
62
|
+
* {tab === "files" ? <SidebarPanel>{tree}</SidebarPanel> : null}
|
|
63
|
+
* </StackedSidebar>
|
|
64
|
+
* <AppShellMain>{children}</AppShellMain>
|
|
65
|
+
* </AppShell>
|
|
66
|
+
*/
|
|
67
|
+
declare function StackedSidebar({ className, ...rest }: ComponentProps<typeof Sidebar>): React.JSX.Element;
|
|
68
|
+
/**
|
|
69
|
+
* Narrow icon-rail column inside a {@link StackedSidebar}. Fixed to the icon
|
|
70
|
+
* width, never collapses on its own (the parent `StackedSidebar` owns the
|
|
71
|
+
* icon-mode toggle).
|
|
72
|
+
*
|
|
73
|
+
* Named `SidebarDock` — not `SidebarRail` — because `SidebarRail` already
|
|
74
|
+
* exists in `@tradalab/lyra/ui` as shadcn's drag-to-resize edge handle.
|
|
75
|
+
*/
|
|
76
|
+
declare function SidebarDock({ className, ...rest }: ComponentProps<typeof Sidebar>): React.JSX.Element;
|
|
77
|
+
/**
|
|
78
|
+
* Wider content column inside a {@link StackedSidebar} (tree, list, …).
|
|
79
|
+
* Fills remaining width; render conditionally to show/hide per app state.
|
|
80
|
+
*/
|
|
81
|
+
declare function SidebarPanel({ className, ...rest }: ComponentProps<typeof Sidebar>): React.JSX.Element;
|
|
82
|
+
|
|
42
83
|
interface NavGroupProps {
|
|
43
84
|
label?: ReactNode;
|
|
44
85
|
children: ReactNode;
|
|
@@ -72,6 +113,134 @@ interface NavItemProps {
|
|
|
72
113
|
/** Sidebar nav row. Must be rendered inside a {@link NavGroup}. */
|
|
73
114
|
declare function NavItem({ href, label, icon, tooltip, matchMode, }: NavItemProps): React.JSX.Element;
|
|
74
115
|
|
|
116
|
+
interface NavSectionProps {
|
|
117
|
+
/** Optional group heading. */
|
|
118
|
+
label?: ReactNode;
|
|
119
|
+
/** Nav rows — each rendered as a {@link NavItem}. */
|
|
120
|
+
items: NavItemProps[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Config-driven convenience over {@link NavGroup} + {@link NavItem}: pass a
|
|
124
|
+
* data array instead of hand-mapping JSX. For dynamic or conditional rows,
|
|
125
|
+
* compose `NavGroup`/`NavItem` directly.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const NAV = [
|
|
129
|
+
* { href: "/files", label: "Files", icon: <Folder /> },
|
|
130
|
+
* { href: "/trash", label: "Trash", icon: <Trash2 /> },
|
|
131
|
+
* ]
|
|
132
|
+
* <NavSection label="Workspace" items={NAV} />
|
|
133
|
+
*/
|
|
134
|
+
declare function NavSection({ label, items }: NavSectionProps): React.JSX.Element;
|
|
135
|
+
|
|
136
|
+
interface NavUserThemeLabels {
|
|
137
|
+
theme: string;
|
|
138
|
+
light: string;
|
|
139
|
+
dark: string;
|
|
140
|
+
system: string;
|
|
141
|
+
}
|
|
142
|
+
/** Default English theme-submenu labels — spread + override for i18n. */
|
|
143
|
+
declare const defaultNavUserThemeLabels: NavUserThemeLabels;
|
|
144
|
+
interface NavUserProps {
|
|
145
|
+
/** Signed-in user. Renders nothing when `null`/`undefined`. */
|
|
146
|
+
user?: AccountMenuUser | null;
|
|
147
|
+
/** Account-action labels — reuses lyra's {@link AccountMenuLabels}. */
|
|
148
|
+
labels?: Partial<AccountMenuLabels>;
|
|
149
|
+
/** Switch-account handler. Item hidden if omitted. */
|
|
150
|
+
onSwitchAccount?: () => void;
|
|
151
|
+
/** Sign-out handler. Item hidden if omitted. */
|
|
152
|
+
onSignOut?: () => void;
|
|
153
|
+
/** Render the light/dark/system theme submenu. Default `true`. */
|
|
154
|
+
showTheme?: boolean;
|
|
155
|
+
/** Theme-submenu labels. */
|
|
156
|
+
themeLabels?: Partial<NavUserThemeLabels>;
|
|
157
|
+
/** Extra menu content inserted below the theme submenu. */
|
|
158
|
+
children?: ReactNode;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Sidebar-footer account menu — the standardized NavUser for app shells.
|
|
162
|
+
* Avatar trigger (size `lg`) + name/email opening a dropdown with an optional
|
|
163
|
+
* theme switcher and switch-account / sign-out actions. Auth flows stay in the
|
|
164
|
+
* app (callbacks); labels are props for i18n.
|
|
165
|
+
*
|
|
166
|
+
* Differs from {@link AccountMenu} in `@tradalab/lyra/ui`: that one is a
|
|
167
|
+
* generic header dropdown; this is styled for the collapsible sidebar footer
|
|
168
|
+
* and follows `useSidebar()` for mobile placement.
|
|
169
|
+
*
|
|
170
|
+
* Must render inside an `AppShell`/SidebarProvider, and below a
|
|
171
|
+
* `TooltipProvider` (the avatar button's collapsed-mode tooltip requires one —
|
|
172
|
+
* app root providers normally mount it). The theme submenu needs a
|
|
173
|
+
* `next-themes` provider mounted (it no-ops without one); pass
|
|
174
|
+
* `showTheme={false}` for apps with a fixed theme.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* import { NavUser } from "@tradalab/lyra/shell"
|
|
178
|
+
* import { SidebarFooter } from "@tradalab/lyra/ui"
|
|
179
|
+
*
|
|
180
|
+
* <SidebarFooter>
|
|
181
|
+
* <NavUser
|
|
182
|
+
* user={session.user}
|
|
183
|
+
* onSwitchAccount={() => signIn("zitadel", { redirectTo: "/" }, { prompt: "select_account" })}
|
|
184
|
+
* onSignOut={() => signOut({ redirectTo: "/" })}
|
|
185
|
+
* />
|
|
186
|
+
* </SidebarFooter>
|
|
187
|
+
*/
|
|
188
|
+
declare function NavUser({ user, labels, onSwitchAccount, onSignOut, showTheme, themeLabels, children, }: NavUserProps): React.JSX.Element | null;
|
|
189
|
+
|
|
190
|
+
interface TabItem {
|
|
191
|
+
id: string;
|
|
192
|
+
title: string;
|
|
193
|
+
/** Secondary line under the title (e.g. connection/db, file path). */
|
|
194
|
+
subtitle?: ReactNode;
|
|
195
|
+
/** Leading icon element — the app maps its own tab type → icon. */
|
|
196
|
+
icon?: ReactNode;
|
|
197
|
+
pinned?: boolean;
|
|
198
|
+
}
|
|
199
|
+
interface TabBarLabels {
|
|
200
|
+
close: string;
|
|
201
|
+
pinTab: string;
|
|
202
|
+
unpinTab: string;
|
|
203
|
+
closeOthers: string;
|
|
204
|
+
closeAll: string;
|
|
205
|
+
}
|
|
206
|
+
/** Default English labels — spread + override for i18n. */
|
|
207
|
+
declare const defaultTabBarLabels: TabBarLabels;
|
|
208
|
+
interface TabBarProps {
|
|
209
|
+
tabs: TabItem[];
|
|
210
|
+
activeId: string | null;
|
|
211
|
+
onSelect: (id: string) => void;
|
|
212
|
+
onClose: (id: string) => void;
|
|
213
|
+
/** Pin row in the context menu is hidden when omitted. */
|
|
214
|
+
onTogglePin?: (id: string) => void;
|
|
215
|
+
/** "Close others" row is hidden when omitted. */
|
|
216
|
+
onCloseOthers?: (id: string) => void;
|
|
217
|
+
/** "Close all" row is hidden when omitted. */
|
|
218
|
+
onCloseAll?: () => void;
|
|
219
|
+
labels?: Partial<TabBarLabels>;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Horizontal tab strip for the content area of stacked-sidebar apps
|
|
223
|
+
* (redishub, loom). Presentational + store-agnostic: the app owns tab state
|
|
224
|
+
* and passes `tabs` + callbacks; `icon`/`subtitle` are per-tab so apps map
|
|
225
|
+
* their own tab types, and labels are props for i18n. Renders nothing when
|
|
226
|
+
* `tabs` is empty.
|
|
227
|
+
*
|
|
228
|
+
* Sizes rows from the `--tab-height` CSS var — set it in your globals.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* const s = useTabStore()
|
|
232
|
+
* <TabBar
|
|
233
|
+
* tabs={s.tabs.map(t => ({ id: t.id, title: t.title, pinned: t.pinned, icon: <Icon t={t.type}/>, subtitle: fmt(t) }))}
|
|
234
|
+
* activeId={s.activeTabId}
|
|
235
|
+
* onSelect={s.setActiveTabId}
|
|
236
|
+
* onClose={s.removeTab}
|
|
237
|
+
* onTogglePin={s.togglePin}
|
|
238
|
+
* onCloseOthers={s.closeOthers}
|
|
239
|
+
* onCloseAll={s.closeAll}
|
|
240
|
+
* />
|
|
241
|
+
*/
|
|
242
|
+
declare function TabBar({ tabs, activeId, onSelect, onClose, onTogglePin, onCloseOthers, onCloseAll, labels, }: TabBarProps): React.JSX.Element | null;
|
|
243
|
+
|
|
75
244
|
interface BreadcrumbsProps extends ComponentProps<typeof Breadcrumb> {
|
|
76
245
|
/** Separator content. Falls back to shadcn's chevron when omitted. */
|
|
77
246
|
separator?: ReactNode;
|
|
@@ -81,4 +250,4 @@ interface BreadcrumbsProps extends ComponentProps<typeof Breadcrumb> {
|
|
|
81
250
|
/** Pathname-derived breadcrumbs. Renders nothing on the root route. */
|
|
82
251
|
declare function Breadcrumbs({ separator, labels, ...rest }: BreadcrumbsProps): React.JSX.Element | null;
|
|
83
252
|
|
|
84
|
-
export { AppShell, AppShellBody, AppShellHeader, AppShellMain, type AppShellProps, Breadcrumbs, type BreadcrumbsProps, type MatchMode, NavGroup, type NavGroupProps, type NavIcon, NavItem, type NavItemProps, type UseActiveRoute, useActiveRoute };
|
|
253
|
+
export { AppShell, AppShellBody, AppShellHeader, AppShellMain, type AppShellProps, Breadcrumbs, type BreadcrumbsProps, type MatchMode, NavGroup, type NavGroupProps, type NavIcon, NavItem, type NavItemProps, NavSection, type NavSectionProps, NavUser, type NavUserProps, type NavUserThemeLabels, SidebarDock, SidebarPanel, StackedSidebar, TabBar, type TabBarLabels, type TabBarProps, type TabItem, type UseActiveRoute, defaultNavUserThemeLabels, defaultTabBarLabels, useActiveRoute };
|
package/dist/shell/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { SidebarInset, SidebarProvider, SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, Breadcrumb, BreadcrumbList, BreadcrumbSeparator, BreadcrumbItem, BreadcrumbPage, BreadcrumbLink } from '../chunk-
|
|
2
|
+
import { SidebarInset, SidebarProvider, Sidebar, SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, useSidebar, defaultAccountMenuLabels, Avatar, AvatarImage, AvatarFallback, initialsOf, ScrollArea, ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ScrollBar, Breadcrumb, BreadcrumbList, BreadcrumbSeparator, BreadcrumbItem, BreadcrumbPage, BreadcrumbLink } from '../chunk-5UAFV75V.js';
|
|
3
3
|
import '../chunk-QYA2HGKW.js';
|
|
4
|
-
import '../chunk-
|
|
4
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuItem } from '../chunk-VEWKPOMS.js';
|
|
5
|
+
import { Button } from '../chunk-KFQOYZ64.js';
|
|
5
6
|
import { cn } from '../chunk-XH55BHUU.js';
|
|
6
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
8
|
import Link from 'next/link';
|
|
8
9
|
import { usePathname } from 'next/navigation';
|
|
9
|
-
import {
|
|
10
|
+
import { ChevronsUpDown, Palette, Sun, Moon, Monitor, UserRoundCog, LogOut, Pin, X, PinOff, ListX, Trash2, ChevronDown } from 'lucide-react';
|
|
11
|
+
import { useTheme } from 'next-themes';
|
|
12
|
+
import { useRef, useEffect, Fragment as Fragment$1 } from 'react';
|
|
10
13
|
|
|
11
14
|
function AppShell({ children, ...rest }) {
|
|
12
15
|
return /* @__PURE__ */ jsx(SidebarProvider, { ...rest, children });
|
|
@@ -36,6 +39,52 @@ function AppShellBody({
|
|
|
36
39
|
}) {
|
|
37
40
|
return /* @__PURE__ */ jsx("div", { className: cn("flex-1 overflow-auto", className), ...rest, children });
|
|
38
41
|
}
|
|
42
|
+
function StackedSidebar({
|
|
43
|
+
className,
|
|
44
|
+
...rest
|
|
45
|
+
}) {
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
Sidebar,
|
|
48
|
+
{
|
|
49
|
+
variant: "sidebar",
|
|
50
|
+
collapsible: "icon",
|
|
51
|
+
...rest,
|
|
52
|
+
className: cn(
|
|
53
|
+
"overflow-hidden *:data-[sidebar=sidebar]:flex-row",
|
|
54
|
+
className
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function SidebarDock({
|
|
60
|
+
className,
|
|
61
|
+
...rest
|
|
62
|
+
}) {
|
|
63
|
+
return /* @__PURE__ */ jsx(
|
|
64
|
+
Sidebar,
|
|
65
|
+
{
|
|
66
|
+
...rest,
|
|
67
|
+
collapsible: "none",
|
|
68
|
+
className: cn(
|
|
69
|
+
"w-[calc(var(--sidebar-width-icon)+1px)]! overflow-hidden border-r",
|
|
70
|
+
className
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function SidebarPanel({
|
|
76
|
+
className,
|
|
77
|
+
...rest
|
|
78
|
+
}) {
|
|
79
|
+
return /* @__PURE__ */ jsx(
|
|
80
|
+
Sidebar,
|
|
81
|
+
{
|
|
82
|
+
...rest,
|
|
83
|
+
collapsible: "none",
|
|
84
|
+
className: cn("flex-1 overflow-hidden", className)
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
39
88
|
function NavGroup({ label, children }) {
|
|
40
89
|
return /* @__PURE__ */ jsxs(SidebarGroup, { children: [
|
|
41
90
|
label != null ? /* @__PURE__ */ jsx(SidebarGroupLabel, { children: label }) : null,
|
|
@@ -73,6 +122,310 @@ function NavItem({
|
|
|
73
122
|
}
|
|
74
123
|
) });
|
|
75
124
|
}
|
|
125
|
+
function NavSection({ label, items }) {
|
|
126
|
+
return /* @__PURE__ */ jsx(NavGroup, { label, children: items.map((item) => /* @__PURE__ */ jsx(NavItem, { ...item }, item.href)) });
|
|
127
|
+
}
|
|
128
|
+
var defaultNavUserThemeLabels = {
|
|
129
|
+
theme: "Theme",
|
|
130
|
+
light: "Light",
|
|
131
|
+
dark: "Dark",
|
|
132
|
+
system: "System"
|
|
133
|
+
};
|
|
134
|
+
var THEMES = [
|
|
135
|
+
{ value: "light", key: "light", icon: Sun },
|
|
136
|
+
{ value: "dark", key: "dark", icon: Moon },
|
|
137
|
+
{ value: "system", key: "system", icon: Monitor }
|
|
138
|
+
];
|
|
139
|
+
function NavUser({
|
|
140
|
+
user,
|
|
141
|
+
labels,
|
|
142
|
+
onSwitchAccount,
|
|
143
|
+
onSignOut,
|
|
144
|
+
showTheme = true,
|
|
145
|
+
themeLabels,
|
|
146
|
+
children
|
|
147
|
+
}) {
|
|
148
|
+
const { isMobile } = useSidebar();
|
|
149
|
+
const { theme, setTheme } = useTheme();
|
|
150
|
+
const l = { ...defaultAccountMenuLabels, ...labels };
|
|
151
|
+
const t = { ...defaultNavUserThemeLabels, ...themeLabels };
|
|
152
|
+
if (!user) return null;
|
|
153
|
+
const name = user.name ?? user.email ?? l.account;
|
|
154
|
+
const email = user.email ?? "";
|
|
155
|
+
return /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
156
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
157
|
+
SidebarMenuButton,
|
|
158
|
+
{
|
|
159
|
+
size: "lg",
|
|
160
|
+
tooltip: name,
|
|
161
|
+
className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-8 rounded-lg", children: [
|
|
164
|
+
user.image ? /* @__PURE__ */ jsx(AvatarImage, { src: user.image, alt: name }) : null,
|
|
165
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "rounded-lg", children: initialsOf(name) })
|
|
166
|
+
] }),
|
|
167
|
+
/* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
|
|
168
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: name }),
|
|
169
|
+
email ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-xs", children: email }) : null
|
|
170
|
+
] }),
|
|
171
|
+
/* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-auto size-4" })
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
) }),
|
|
175
|
+
/* @__PURE__ */ jsxs(
|
|
176
|
+
DropdownMenuContent,
|
|
177
|
+
{
|
|
178
|
+
className: "w-(--radix-dropdown-menu-trigger-width) min-w-56",
|
|
179
|
+
side: isMobile ? "bottom" : "right",
|
|
180
|
+
align: "end",
|
|
181
|
+
sideOffset: 4,
|
|
182
|
+
children: [
|
|
183
|
+
/* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex flex-col gap-0.5", children: [
|
|
184
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium leading-tight", children: name }),
|
|
185
|
+
email ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-xs font-normal leading-tight", children: email }) : null
|
|
186
|
+
] }),
|
|
187
|
+
showTheme ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
188
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
189
|
+
/* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
|
|
190
|
+
/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [
|
|
191
|
+
/* @__PURE__ */ jsx(Palette, { className: "mr-2 size-4" }),
|
|
192
|
+
t.theme
|
|
193
|
+
] }),
|
|
194
|
+
/* @__PURE__ */ jsx(DropdownMenuSubContent, { children: /* @__PURE__ */ jsx(
|
|
195
|
+
DropdownMenuRadioGroup,
|
|
196
|
+
{
|
|
197
|
+
value: theme ?? "system",
|
|
198
|
+
onValueChange: setTheme,
|
|
199
|
+
children: THEMES.map(({ value, key, icon: Icon }) => /* @__PURE__ */ jsxs(DropdownMenuRadioItem, { value, children: [
|
|
200
|
+
/* @__PURE__ */ jsx(Icon, { className: "mr-2 size-4" }),
|
|
201
|
+
t[key]
|
|
202
|
+
] }, value))
|
|
203
|
+
}
|
|
204
|
+
) })
|
|
205
|
+
] })
|
|
206
|
+
] }) : null,
|
|
207
|
+
children ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
208
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
209
|
+
children
|
|
210
|
+
] }) : null,
|
|
211
|
+
onSwitchAccount || onSignOut ? /* @__PURE__ */ jsx(DropdownMenuSeparator, {}) : null,
|
|
212
|
+
onSwitchAccount ? /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: onSwitchAccount, children: [
|
|
213
|
+
/* @__PURE__ */ jsx(UserRoundCog, { className: "mr-2 size-4" }),
|
|
214
|
+
l.switchAccount
|
|
215
|
+
] }) : null,
|
|
216
|
+
onSignOut ? /* @__PURE__ */ jsxs(DropdownMenuItem, { variant: "destructive", onSelect: onSignOut, children: [
|
|
217
|
+
/* @__PURE__ */ jsx(LogOut, { className: "mr-2 size-4" }),
|
|
218
|
+
l.signOut
|
|
219
|
+
] }) : null
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
] }) }) });
|
|
224
|
+
}
|
|
225
|
+
var defaultTabBarLabels = {
|
|
226
|
+
close: "Close",
|
|
227
|
+
pinTab: "Pin tab",
|
|
228
|
+
unpinTab: "Unpin tab",
|
|
229
|
+
closeOthers: "Close others",
|
|
230
|
+
closeAll: "Close all"
|
|
231
|
+
};
|
|
232
|
+
function TabBar({
|
|
233
|
+
tabs,
|
|
234
|
+
activeId,
|
|
235
|
+
onSelect,
|
|
236
|
+
onClose,
|
|
237
|
+
onTogglePin,
|
|
238
|
+
onCloseOthers,
|
|
239
|
+
onCloseAll,
|
|
240
|
+
labels
|
|
241
|
+
}) {
|
|
242
|
+
const l = { ...defaultTabBarLabels, ...labels };
|
|
243
|
+
const activeTabRef = useRef(null);
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
if (activeId && activeTabRef.current) {
|
|
246
|
+
activeTabRef.current.scrollIntoView({
|
|
247
|
+
behavior: "smooth",
|
|
248
|
+
block: "nearest",
|
|
249
|
+
inline: "nearest"
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}, [activeId]);
|
|
253
|
+
const handleWheel = (e) => {
|
|
254
|
+
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
|
|
255
|
+
const viewport = e.currentTarget.querySelector(
|
|
256
|
+
'[data-slot="scroll-area-viewport"]'
|
|
257
|
+
);
|
|
258
|
+
if (viewport) viewport.scrollLeft += e.deltaY * 0.5;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
if (tabs.length === 0) return null;
|
|
262
|
+
const hasContextActions = Boolean(onTogglePin || onCloseOthers || onCloseAll);
|
|
263
|
+
return /* @__PURE__ */ jsxs(
|
|
264
|
+
"div",
|
|
265
|
+
{
|
|
266
|
+
className: "group/tabbar flex w-full shrink-0 items-center overflow-hidden border-b bg-muted/30",
|
|
267
|
+
style: { height: "var(--tab-height)" },
|
|
268
|
+
children: [
|
|
269
|
+
/* @__PURE__ */ jsxs(
|
|
270
|
+
ScrollArea,
|
|
271
|
+
{
|
|
272
|
+
type: "hover",
|
|
273
|
+
className: "min-w-0 flex-1 overflow-hidden [&_[data-orientation=vertical]]:hidden",
|
|
274
|
+
onWheel: handleWheel,
|
|
275
|
+
children: [
|
|
276
|
+
/* @__PURE__ */ jsx(
|
|
277
|
+
"div",
|
|
278
|
+
{
|
|
279
|
+
className: "flex w-max items-center gap-0",
|
|
280
|
+
style: { height: "var(--tab-height)" },
|
|
281
|
+
children: tabs.map((tab) => {
|
|
282
|
+
const isActive = activeId === tab.id;
|
|
283
|
+
return /* @__PURE__ */ jsxs(ContextMenu, { children: [
|
|
284
|
+
/* @__PURE__ */ jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
ref: isActive ? activeTabRef : null,
|
|
288
|
+
onClick: () => onSelect(tab.id),
|
|
289
|
+
title: tab.title,
|
|
290
|
+
className: cn(
|
|
291
|
+
"group relative flex min-w-[140px] max-w-[240px] shrink-0 cursor-pointer select-none items-center gap-2 border-r px-3 transition-colors",
|
|
292
|
+
isActive ? "border-t-2 border-t-primary bg-background" : "border-t-2 border-t-transparent bg-transparent text-muted-foreground hover:bg-muted/50",
|
|
293
|
+
tab.pinned && "min-w-[100px] bg-primary/5"
|
|
294
|
+
),
|
|
295
|
+
style: { height: "var(--tab-height)" },
|
|
296
|
+
children: [
|
|
297
|
+
tab.icon ? /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", children: [
|
|
298
|
+
/* @__PURE__ */ jsx(
|
|
299
|
+
"span",
|
|
300
|
+
{
|
|
301
|
+
className: cn(
|
|
302
|
+
"flex [&_svg]:size-3.5",
|
|
303
|
+
isActive && "text-primary"
|
|
304
|
+
),
|
|
305
|
+
children: tab.icon
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
tab.pinned ? /* @__PURE__ */ jsx("div", { className: "absolute -right-1.5 -top-1.5 rounded-full bg-background p-0.5 shadow-sm", children: /* @__PURE__ */ jsx(Pin, { className: "size-2 fill-primary text-primary" }) }) : null
|
|
309
|
+
] }) : null,
|
|
310
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-start justify-center overflow-hidden py-1", children: [
|
|
311
|
+
/* @__PURE__ */ jsx(
|
|
312
|
+
"span",
|
|
313
|
+
{
|
|
314
|
+
className: cn(
|
|
315
|
+
"w-full truncate text-xs leading-tight",
|
|
316
|
+
isActive && "font-medium text-foreground"
|
|
317
|
+
),
|
|
318
|
+
children: tab.title
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
tab.subtitle ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 w-full truncate text-[10px] leading-none text-muted-foreground/70", children: tab.subtitle }) : null
|
|
322
|
+
] }),
|
|
323
|
+
/* @__PURE__ */ jsx(
|
|
324
|
+
Button,
|
|
325
|
+
{
|
|
326
|
+
variant: "ghost",
|
|
327
|
+
size: "icon",
|
|
328
|
+
title: l.close,
|
|
329
|
+
className: cn(
|
|
330
|
+
"size-5 shrink-0 rounded-md opacity-0 transition-opacity hover:bg-muted-foreground/20 group-hover:opacity-100",
|
|
331
|
+
isActive && "opacity-100"
|
|
332
|
+
),
|
|
333
|
+
onClick: (e) => {
|
|
334
|
+
e.stopPropagation();
|
|
335
|
+
onClose(tab.id);
|
|
336
|
+
},
|
|
337
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
338
|
+
}
|
|
339
|
+
),
|
|
340
|
+
isActive ? /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 z-10 h-[2px] bg-background" }) : null
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
) }),
|
|
344
|
+
hasContextActions ? /* @__PURE__ */ jsxs(ContextMenuContent, { className: "w-48", children: [
|
|
345
|
+
onTogglePin ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
346
|
+
/* @__PURE__ */ jsx(ContextMenuItem, { onClick: () => onTogglePin(tab.id), children: tab.pinned ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
347
|
+
/* @__PURE__ */ jsx(PinOff, { className: "mr-2 size-4" }),
|
|
348
|
+
/* @__PURE__ */ jsx("span", { children: l.unpinTab })
|
|
349
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
350
|
+
/* @__PURE__ */ jsx(Pin, { className: "mr-2 size-4" }),
|
|
351
|
+
/* @__PURE__ */ jsx("span", { children: l.pinTab })
|
|
352
|
+
] }) }),
|
|
353
|
+
/* @__PURE__ */ jsx(ContextMenuSeparator, {})
|
|
354
|
+
] }) : null,
|
|
355
|
+
/* @__PURE__ */ jsxs(ContextMenuItem, { onClick: () => onClose(tab.id), children: [
|
|
356
|
+
/* @__PURE__ */ jsx(X, { className: "mr-2 size-4" }),
|
|
357
|
+
/* @__PURE__ */ jsx("span", { children: l.close })
|
|
358
|
+
] }),
|
|
359
|
+
onCloseOthers ? /* @__PURE__ */ jsxs(ContextMenuItem, { onClick: () => onCloseOthers(tab.id), children: [
|
|
360
|
+
/* @__PURE__ */ jsx(ListX, { className: "mr-2 size-4" }),
|
|
361
|
+
/* @__PURE__ */ jsx("span", { children: l.closeOthers })
|
|
362
|
+
] }) : null,
|
|
363
|
+
onCloseAll ? /* @__PURE__ */ jsxs(ContextMenuItem, { onClick: () => onCloseAll(), children: [
|
|
364
|
+
/* @__PURE__ */ jsx(Trash2, { className: "mr-2 size-4" }),
|
|
365
|
+
/* @__PURE__ */ jsx("span", { children: l.closeAll })
|
|
366
|
+
] }) : null
|
|
367
|
+
] }) : null
|
|
368
|
+
] }, tab.id);
|
|
369
|
+
})
|
|
370
|
+
}
|
|
371
|
+
),
|
|
372
|
+
/* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal", className: "z-20 h-1.5 hover:h-2" })
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
),
|
|
376
|
+
/* @__PURE__ */ jsx(
|
|
377
|
+
"div",
|
|
378
|
+
{
|
|
379
|
+
className: "flex shrink-0 items-center border-l bg-muted/30 px-1",
|
|
380
|
+
style: { height: "var(--tab-height)" },
|
|
381
|
+
children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
382
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "size-8 shrink-0 rounded-sm", children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4" }) }) }),
|
|
383
|
+
/* @__PURE__ */ jsx(
|
|
384
|
+
DropdownMenuContent,
|
|
385
|
+
{
|
|
386
|
+
align: "end",
|
|
387
|
+
className: "max-h-[400px] w-[260px] overflow-y-auto",
|
|
388
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxs(
|
|
389
|
+
DropdownMenuItem,
|
|
390
|
+
{
|
|
391
|
+
onClick: () => onSelect(tab.id),
|
|
392
|
+
title: tab.title,
|
|
393
|
+
className: cn(
|
|
394
|
+
"cursor-pointer gap-2 py-2",
|
|
395
|
+
activeId === tab.id && "bg-accent"
|
|
396
|
+
),
|
|
397
|
+
children: [
|
|
398
|
+
tab.icon ? /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", children: [
|
|
399
|
+
/* @__PURE__ */ jsx("span", { className: "flex text-muted-foreground [&_svg]:size-4", children: tab.icon }),
|
|
400
|
+
tab.pinned ? /* @__PURE__ */ jsx(Pin, { className: "absolute -right-1 -top-1 size-2 fill-primary text-primary" }) : null
|
|
401
|
+
] }) : null,
|
|
402
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-start overflow-hidden leading-tight", children: [
|
|
403
|
+
/* @__PURE__ */ jsx("span", { className: "w-full flex-1 truncate text-sm", children: tab.title }),
|
|
404
|
+
tab.subtitle ? /* @__PURE__ */ jsx("span", { className: "w-full truncate text-[10px] text-muted-foreground", children: tab.subtitle }) : null
|
|
405
|
+
] }),
|
|
406
|
+
/* @__PURE__ */ jsx(
|
|
407
|
+
X,
|
|
408
|
+
{
|
|
409
|
+
className: "size-4 shrink-0 rounded-sm p-0.5 hover:bg-red-50 hover:text-red-500",
|
|
410
|
+
onClick: (e) => {
|
|
411
|
+
e.stopPropagation();
|
|
412
|
+
onClose(tab.id);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
)
|
|
416
|
+
]
|
|
417
|
+
},
|
|
418
|
+
tab.id
|
|
419
|
+
))
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
] })
|
|
423
|
+
}
|
|
424
|
+
)
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
}
|
|
76
429
|
function Breadcrumbs({ separator, labels, ...rest }) {
|
|
77
430
|
const { pathname } = useActiveRoute();
|
|
78
431
|
const segments = pathname.split("/").filter(Boolean);
|
|
@@ -81,13 +434,13 @@ function Breadcrumbs({ separator, labels, ...rest }) {
|
|
|
81
434
|
return /* @__PURE__ */ jsx(Breadcrumb, { ...rest, children: /* @__PURE__ */ jsx(BreadcrumbList, { children: segments.map((seg, i) => {
|
|
82
435
|
const href = "/" + segments.slice(0, i + 1).join("/");
|
|
83
436
|
const label = labels?.[seg] ?? seg;
|
|
84
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
437
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
85
438
|
i > 0 ? /* @__PURE__ */ jsx(BreadcrumbSeparator, { children: separator }) : null,
|
|
86
439
|
/* @__PURE__ */ jsx(BreadcrumbItem, { children: i === last ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: label }) : /* @__PURE__ */ jsx(BreadcrumbLink, { asChild: true, children: /* @__PURE__ */ jsx(Link, { href, children: label }) }) })
|
|
87
440
|
] }, href);
|
|
88
441
|
}) }) });
|
|
89
442
|
}
|
|
90
443
|
|
|
91
|
-
export { AppShell, AppShellBody, AppShellHeader, AppShellMain, Breadcrumbs, NavGroup, NavItem, useActiveRoute };
|
|
444
|
+
export { AppShell, AppShellBody, AppShellHeader, AppShellMain, Breadcrumbs, NavGroup, NavItem, NavSection, NavUser, SidebarDock, SidebarPanel, StackedSidebar, TabBar, defaultNavUserThemeLabels, defaultTabBarLabels, useActiveRoute };
|
|
92
445
|
//# sourceMappingURL=index.js.map
|
|
93
446
|
//# sourceMappingURL=index.js.map
|