@medalsocial/meda 1.0.0 → 1.1.1
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/chat/latency-badge.js +1 -1
- package/dist/chat/latency-breakdown.js +1 -1
- package/dist/chat/turn-card.js +1 -1
- package/dist/shell/app-shell-auth.d.ts +5 -0
- package/dist/shell/app-shell-auth.js +50 -0
- package/dist/shell/app-shell-chat.d.ts +6 -0
- package/dist/shell/app-shell-chat.js +9 -0
- package/dist/shell/app-shell-workspace.d.ts +10 -0
- package/dist/shell/app-shell-workspace.js +55 -0
- package/dist/shell/app-shell.d.ts +18 -2
- package/dist/shell/app-shell.js +17 -2
- package/dist/shell/context-rail.d.ts +1 -15
- package/dist/shell/context-rail.js +61 -15
- package/dist/shell/icon-rail.js +1 -1
- package/dist/shell/index.d.ts +1 -6
- package/dist/shell/index.js +0 -5
- package/dist/shell/{mobile → internal}/mobile-bottom-nav.d.ts +9 -1
- package/dist/shell/{mobile → internal}/mobile-bottom-nav.js +5 -2
- package/dist/shell/{mobile → internal}/mobile-drawers.d.ts +7 -1
- package/dist/shell/{mobile → internal}/mobile-drawers.js +18 -4
- package/dist/shell/internal/mobile-header.js +22 -0
- package/dist/shell/layout-state.js +4 -1
- package/dist/shell/types.d.ts +35 -0
- package/dist/timeline/event-card.js +1 -1
- package/dist/timeline/live-indicator.js +1 -1
- package/dist/timeline/scrub-bar.js +1 -1
- package/package.json +5 -1
- package/dist/shell/mobile/mobile-header.js +0 -22
- package/dist/shell/navigation-area.d.ts +0 -4
- package/dist/shell/navigation-area.js +0 -4
- package/dist/shell/public.d.ts +0 -16
- package/dist/shell/public.js +0 -20
- package/dist/shell/shell-app-rail.d.ts +0 -18
- package/dist/shell/shell-app-rail.js +0 -27
- package/dist/shell/shell-auth-frame.d.ts +0 -22
- package/dist/shell/shell-auth-frame.js +0 -60
- package/dist/shell/shell-desktop-panel-dock.d.ts +0 -14
- package/dist/shell/shell-desktop-panel-dock.js +0 -43
- package/dist/shell/shell-frame.d.ts +0 -13
- package/dist/shell/shell-frame.js +0 -4
- package/dist/shell/shell-module-nav.d.ts +0 -29
- package/dist/shell/shell-module-nav.js +0 -16
- package/dist/shell/shell-panel-rail.d.ts +0 -9
- package/dist/shell/shell-panel-rail.js +0 -17
- package/dist/shell/shell-scrollable-content.d.ts +0 -9
- package/dist/shell/shell-scrollable-content.js +0 -7
- package/dist/shell/shell-state.d.ts +0 -39
- package/dist/shell/shell-state.js +0 -135
- package/dist/shell/shell-tab-bar.d.ts +0 -18
- package/dist/shell/shell-tab-bar.js +0 -27
- package/dist/shell/workbench-layout.d.ts +0 -10
- package/dist/shell/workbench-layout.js +0 -29
- package/dist/styles.css +0 -26
- /package/dist/shell/{mobile → internal}/mobile-header.d.ts +0 -0
package/dist/shell/types.d.ts
CHANGED
|
@@ -62,3 +62,38 @@ export interface ThemeAdapter {
|
|
|
62
62
|
setTheme: (t: 'light' | 'dark' | 'system') => void;
|
|
63
63
|
resolvedTheme: 'light' | 'dark';
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* AppShell renders one of three shell modes:
|
|
67
|
+
* - `auth` — split-screen marketing + form (signed-out)
|
|
68
|
+
* - `workspace` — header + rails + main + panel (signed-in work surface;
|
|
69
|
+
* collapses to mobile header + bottom nav + drawers on mobile)
|
|
70
|
+
* - `chat` — transcript-dominant layout (conversational AI)
|
|
71
|
+
*/
|
|
72
|
+
export type AppShellVariant = 'auth' | 'workspace' | 'chat';
|
|
73
|
+
/** IconRail configuration for `<AppShell variant="workspace">`. */
|
|
74
|
+
export interface AppShellIconRailConfig {
|
|
75
|
+
mainItems: import('./icon-rail.js').IconRailItem[];
|
|
76
|
+
utilityItems?: import('./icon-rail.js').IconRailItem[];
|
|
77
|
+
footer?: ReactNode;
|
|
78
|
+
activeId?: string;
|
|
79
|
+
}
|
|
80
|
+
/** ContextRail configuration for `<AppShell variant="workspace">`. */
|
|
81
|
+
export interface AppShellContextRailConfig {
|
|
82
|
+
appId: string;
|
|
83
|
+
module: ContextModule;
|
|
84
|
+
activeItemId?: string;
|
|
85
|
+
}
|
|
86
|
+
/** RightPanel configuration for `<AppShell variant="workspace">`. */
|
|
87
|
+
export interface AppShellRightPanelConfig {
|
|
88
|
+
panelViews: PanelView[];
|
|
89
|
+
defaultView?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface AppShellAuthConfig {
|
|
92
|
+
title: ReactNode;
|
|
93
|
+
description?: ReactNode;
|
|
94
|
+
brandName?: ReactNode;
|
|
95
|
+
brandMark?: ReactNode;
|
|
96
|
+
eyebrow?: ReactNode;
|
|
97
|
+
preview?: ReactNode;
|
|
98
|
+
actions?: ReactNode;
|
|
99
|
+
}
|
|
@@ -14,5 +14,5 @@ export function EventCard({ event, size = 'default', onSelect, className }) {
|
|
|
14
14
|
return (_jsxs("button", { type: "button", "data-live": String(isLive), "data-selected": String(isSelected), "data-kind": event.kind, onClick: () => onSelect?.(event), className: cn('relative w-full cursor-pointer rounded-md border border-border bg-card text-left transition-[border-color,transform] duration-100', 'hover:translate-x-px hover:border-primary/40', sizeClasses, liveClasses, errorClasses, selectedClasses, className), children: [_jsx("span", { "aria-hidden": "true", className: "absolute -left-2.5 top-1/2 h-px w-2.5 bg-border" }), _jsxs("span", { className: "flex items-center gap-1.5 font-medium", children: [isLive && _jsx(LiveDot, {}), _jsx("span", { className: "min-w-0 flex-1 truncate", children: event.primary })] }), size === 'default' && event.secondary != null && (_jsx("span", { className: "mt-0.5 block text-[10px] text-muted-foreground tabular-nums", children: event.secondary }))] }));
|
|
15
15
|
}
|
|
16
16
|
function LiveDot() {
|
|
17
|
-
return (_jsx("span", { "aria-hidden": "true", className: "inline-block size-1.5 shrink-0 animate-pulse rounded-full bg-success shadow-[0_0_0_3px_rgba(16,185,129,0.20)]" }));
|
|
17
|
+
return (_jsx("span", { "aria-hidden": "true", "data-chromatic": "ignore", className: "inline-block size-1.5 shrink-0 animate-pulse rounded-full bg-success shadow-[0_0_0_3px_rgba(16,185,129,0.20)]" }));
|
|
18
18
|
}
|
|
@@ -3,5 +3,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { formatClock } from '../lib/format-time.js';
|
|
4
4
|
import { cn } from '../lib/utils.js';
|
|
5
5
|
export function LiveIndicator({ now, tz, className }) {
|
|
6
|
-
return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn('relative h-0 pointer-events-none', className), children: [_jsx("span", { className: "pointer-events-auto absolute left-0 top-0 inline-flex items-center rounded-md bg-success-700 px-2.5 py-1 text-[11px] font-bold uppercase tracking-wider text-white shadow-[0_6px_18px_rgba(16,185,129,0.35)]", children: "LIVE" }), _jsx("span", { "aria-hidden": "true", className: "absolute right-0 top-1/2 left-[60px] h-px bg-gradient-to-r from-success via-success/30 to-transparent" }), _jsx("span", { className: "absolute right-1.5 top-[-2px] text-[10px] font-semibold tabular-nums text-success-700", children: formatClock(now, { tz }) })] }));
|
|
6
|
+
return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn('relative h-0 pointer-events-none', className), children: [_jsx("span", { className: "pointer-events-auto absolute left-0 top-0 inline-flex items-center rounded-md bg-success-700 px-2.5 py-1 text-[11px] font-bold uppercase tracking-wider text-white shadow-[0_6px_18px_rgba(16,185,129,0.35)]", children: "LIVE" }), _jsx("span", { "aria-hidden": "true", className: "absolute right-0 top-1/2 left-[60px] h-px bg-gradient-to-r from-success via-success/30 to-transparent" }), _jsx("span", { "data-chromatic": "ignore", className: "absolute right-1.5 top-[-2px] text-[10px] font-semibold tabular-nums text-success-700", children: formatClock(now, { tz }) })] }));
|
|
7
7
|
}
|
|
@@ -34,7 +34,7 @@ export function ScrubBar({ durationMs, positionMs, isLive = false, marks, onSeek
|
|
|
34
34
|
onSeek(Math.min(durationMs, positionMs + step));
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
return (_jsxs("div", { className: cn('rounded-2xl border border-border bg-card p-3.5', className), children: [_jsxs("div", { className: "mb-2.5 flex items-center justify-between", children: [_jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: "Timeline \u00B7 this call" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("span", { className: "px-1.5 text-[11px] tabular-nums text-muted-foreground", children: [formatDuration(positionMs), isLive ? ' / live' : ` / ${formatDuration(durationMs)}`] }), onSkipBack && (_jsx(TransportBtn, { label: "Skip back", onClick: onSkipBack, children: _jsx(SkipBack, { className: "size-3.5" }) })), showPlayPause && (_jsx(TransportBtn, { label: isPlayingValue ? 'Pause' : 'Play', variant: "primary", onClick: onPlayPause ?? (() => { }), children: isPlayingValue ? _jsx(Pause, { className: "size-3.5" }) : _jsx(Play, { className: "size-3.5" }) })), onSkipForward && (_jsx(TransportBtn, { label: "Skip forward", onClick: onSkipForward, children: _jsx(SkipForward, { className: "size-3.5" }) }))] })] }), _jsxs("div", { ref: trackRef, role: "slider", "aria-label": "Conversation position", "aria-valuemin": 0, "aria-valuemax": durationMs, "aria-valuenow": positionMs, onClick: handleSeek, onKeyDown: handleKeyDown, tabIndex: 0, className: "relative h-8 cursor-pointer overflow-hidden rounded-md bg-muted focus:outline-none focus:ring-2 focus:ring-primary/50", children: [_jsx("span", { "aria-hidden": "true", className: "absolute inset-y-0 left-0 bg-primary/10", style: { width: `${fillPct}%` } }), marks.map((m) => (_jsx("span", { "data-mark-kind": m.kind, title: m.label ?? m.kind, className: cn('absolute inset-y-1 w-0.5 rounded-sm', KIND_COLOR[m.kind]), style: { left: `${m.positionPct}%` } }, m.id))), _jsx("span", { "aria-hidden": "true", className: "absolute -inset-y-0.5 w-0.5 bg-foreground", style: { left: `${fillPct}%` } })] }), _jsxs("div", { className: "flex justify-between pt-2 text-[10px] tabular-nums text-muted-foreground", children: [_jsx("span", { children: "0:00" }), _jsx("span", { children: isLive ? 'now' : formatDuration(durationMs) })] })] }));
|
|
37
|
+
return (_jsxs("div", { className: cn('rounded-2xl border border-border bg-card p-3.5', className), children: [_jsxs("div", { className: "mb-2.5 flex items-center justify-between", children: [_jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: "Timeline \u00B7 this call" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("span", { className: "px-1.5 text-[11px] tabular-nums text-muted-foreground", children: [formatDuration(positionMs), isLive ? ' / live' : ` / ${formatDuration(durationMs)}`] }), onSkipBack && (_jsx(TransportBtn, { label: "Skip back", onClick: onSkipBack, children: _jsx(SkipBack, { className: "size-3.5" }) })), showPlayPause && (_jsx(TransportBtn, { label: isPlayingValue ? 'Pause' : 'Play', variant: "primary", onClick: onPlayPause ?? (() => { }), children: isPlayingValue ? _jsx(Pause, { className: "size-3.5" }) : _jsx(Play, { className: "size-3.5" }) })), onSkipForward && (_jsx(TransportBtn, { label: "Skip forward", onClick: onSkipForward, children: _jsx(SkipForward, { className: "size-3.5" }) }))] })] }), _jsxs("div", { ref: trackRef, role: "slider", "aria-label": "Conversation position", "aria-valuemin": 0, "aria-valuemax": durationMs, "aria-valuenow": positionMs, onClick: handleSeek, onKeyDown: handleKeyDown, tabIndex: 0, className: "relative h-8 cursor-pointer overflow-hidden rounded-md bg-muted focus:outline-none focus:ring-2 focus:ring-primary/50", children: [_jsx("span", { "aria-hidden": "true", className: "absolute inset-y-0 left-0 bg-primary/10", style: { width: `${fillPct}%` } }), marks.map((m) => (_jsx("span", { "data-mark-kind": m.kind, title: m.label ?? m.kind, className: cn('absolute inset-y-1 w-0.5 rounded-sm', KIND_COLOR[m.kind]), style: { left: `${m.positionPct}%` } }, m.id))), _jsx("span", { "aria-hidden": "true", "data-chromatic": "ignore", className: "absolute -inset-y-0.5 w-0.5 bg-foreground", style: { left: `${fillPct}%` } })] }), _jsxs("div", { className: "flex justify-between pt-2 text-[10px] tabular-nums text-muted-foreground", children: [_jsx("span", { children: "0:00" }), _jsx("span", { children: isLive ? 'now' : formatDuration(durationMs) })] })] }));
|
|
38
38
|
}
|
|
39
39
|
function TransportBtn({ label, onClick, variant, children, }) {
|
|
40
40
|
return (_jsx("button", { type: "button", "aria-label": label, onClick: onClick, className: cn('inline-flex size-6 items-center justify-center rounded', variant === 'primary'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@medalsocial/meda",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Shared Meda UI shell and runtime package.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"type": "module",
|
|
15
15
|
"main": "./dist/index.js",
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22"
|
|
19
|
+
},
|
|
17
20
|
"files": [
|
|
18
21
|
"README.md",
|
|
19
22
|
"components.json",
|
|
@@ -158,6 +161,7 @@
|
|
|
158
161
|
"release": "pnpm build && changeset publish",
|
|
159
162
|
"registry:validate": "node ./registry/scripts/validate-registry.mjs",
|
|
160
163
|
"lint:tokens": "node scripts/lint-tokens.mjs",
|
|
164
|
+
"check:stories": "node scripts/check-stories.mjs",
|
|
161
165
|
"quality": "pnpm lint && pnpm typecheck && pnpm lint:tokens && pnpm test"
|
|
162
166
|
}
|
|
163
167
|
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { ChevronLeft } from 'lucide-react';
|
|
4
|
-
import { cn } from '../../lib/utils.js';
|
|
5
|
-
import { useMedaShell } from '../shell-provider.js';
|
|
6
|
-
import { useShellViewport } from '../use-shell-viewport.js';
|
|
7
|
-
/**
|
|
8
|
-
* Mobile-only header.
|
|
9
|
-
*
|
|
10
|
-
* Root mode: workspace name + optional globalActions.
|
|
11
|
-
* Nested mode (when parentLabel is set): ← parentLabel · pageTitle back button.
|
|
12
|
-
*
|
|
13
|
-
* Renders nothing on non-mobile viewports.
|
|
14
|
-
*/
|
|
15
|
-
export function MobileHeader({ parentLabel, parentTo: _parentTo, title, onBack, globalActions, className, }) {
|
|
16
|
-
const ctx = useMedaShell();
|
|
17
|
-
const band = useShellViewport();
|
|
18
|
-
const isNested = !!parentLabel;
|
|
19
|
-
if (band !== 'mobile')
|
|
20
|
-
return null;
|
|
21
|
-
return (_jsx("header", { "data-meda-mobile-header": isNested ? 'nested' : 'root', className: cn('flex h-[var(--shell-header-height)] items-center justify-between border-b border-border bg-card px-3', className), children: isNested ? (_jsxs("button", { type: "button", onClick: onBack, "aria-label": "Go back", className: "flex items-center gap-1.5 rounded-md px-2 py-1.5 text-sm hover:bg-accent", children: [_jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "text-muted-foreground", children: parentLabel }), title && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground/40", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "text-foreground", children: title })] }))] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [_jsx("span", { "aria-hidden": "true", children: ctx.workspace.icon }), _jsx("span", { children: ctx.workspace.name })] }), globalActions && _jsx("div", { className: "flex items-center gap-1", children: globalActions })] })) }));
|
|
22
|
-
}
|
package/dist/shell/public.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export { ShellDesktopPanelDock } from './extras/shell-desktop-panel-dock.js';
|
|
2
|
-
export { ShellScrollableContent } from './extras/shell-scrollable-content.js';
|
|
3
|
-
export { ShellTabBar } from './extras/shell-tab-bar.js';
|
|
4
|
-
export { WorkbenchLayout } from './extras/workbench-layout.js';
|
|
5
|
-
export { NavigationArea } from './navigation-area.js';
|
|
6
|
-
export { ShellAppRail } from './shell-app-rail.js';
|
|
7
|
-
export { ShellFrame } from './shell-frame.js';
|
|
8
|
-
export { ShellHeaderFrame, ShellPanelToggle } from './shell-header.js';
|
|
9
|
-
export { getResolvedShellPanelWidth, getShellContentMaxWidth, } from './shell-layout-utils.js';
|
|
10
|
-
export { ShellModuleNav } from './shell-module-nav.js';
|
|
11
|
-
export { ShellPanelRail } from './shell-panel-rail.js';
|
|
12
|
-
export { getShellActionsFromMatches, getShellContentLayoutFromMatches, getShellPanelViewsFromMatches, getShellTabsFromMatches, } from './shell-route-utils.js';
|
|
13
|
-
export type { ShellSearchParamsAdapter, ShellStateContextValue, } from './shell-state.js';
|
|
14
|
-
export { ShellStateProvider, useShellState } from './shell-state.js';
|
|
15
|
-
export type { ShellCommandDefinition, ShellContentLayout, ShellHostAdapter, ShellModuleDefinition, ShellNavItem, ShellPanelDefinition, ShellPanelRenderContext, ShellRailItem, ShellRouteContext, ShellTab, ShellViewDefinition, ShellViewportBand, } from './types.js';
|
|
16
|
-
export { buildShellSectionCommands, buildShellShortcutMap, getDefaultShellPanelView, getShellPanelCollection, getShellPanelView, isShellPanelViewAvailable, resolveShellPanelView, } from './utils.js';
|
package/dist/shell/public.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// Legacy public surface — imports from source files directly since index.ts now re-exports v2 only.
|
|
2
|
-
// Phase 17 deletes this file along with the legacy component files.
|
|
3
|
-
// Extras (moved to extras/ during Phase 15)
|
|
4
|
-
export { ShellDesktopPanelDock } from './extras/shell-desktop-panel-dock.js';
|
|
5
|
-
export { ShellScrollableContent } from './extras/shell-scrollable-content.js';
|
|
6
|
-
export { ShellTabBar } from './extras/shell-tab-bar.js';
|
|
7
|
-
export { WorkbenchLayout } from './extras/workbench-layout.js';
|
|
8
|
-
// Legacy components
|
|
9
|
-
export { NavigationArea } from './navigation-area.js';
|
|
10
|
-
export { ShellAppRail } from './shell-app-rail.js';
|
|
11
|
-
export { ShellFrame } from './shell-frame.js';
|
|
12
|
-
// Legacy compat shims inside shell-header.tsx
|
|
13
|
-
export { ShellHeaderFrame, ShellPanelToggle } from './shell-header.js';
|
|
14
|
-
export { getResolvedShellPanelWidth, getShellContentMaxWidth, } from './shell-layout-utils.js';
|
|
15
|
-
export { ShellModuleNav } from './shell-module-nav.js';
|
|
16
|
-
export { ShellPanelRail } from './shell-panel-rail.js';
|
|
17
|
-
export { getShellActionsFromMatches, getShellContentLayoutFromMatches, getShellPanelViewsFromMatches, getShellTabsFromMatches, } from './shell-route-utils.js';
|
|
18
|
-
export { ShellStateProvider, useShellState } from './shell-state.js';
|
|
19
|
-
// Utility functions from their actual source locations
|
|
20
|
-
export { buildShellSectionCommands, buildShellShortcutMap, getDefaultShellPanelView, getShellPanelCollection, getShellPanelView, isShellPanelViewAvailable, resolveShellPanelView, } from './utils.js';
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { ShellRailItem } from './types.js';
|
|
3
|
-
interface ShellAppRailRenderArgs {
|
|
4
|
-
children: ReactNode;
|
|
5
|
-
className: string;
|
|
6
|
-
isActive: boolean;
|
|
7
|
-
item: ShellRailItem;
|
|
8
|
-
}
|
|
9
|
-
export interface ShellAppRailProps {
|
|
10
|
-
mainItems: ShellRailItem[];
|
|
11
|
-
utilityItems: ShellRailItem[];
|
|
12
|
-
className?: string;
|
|
13
|
-
footer?: ReactNode;
|
|
14
|
-
isItemActive: (item: ShellRailItem) => boolean;
|
|
15
|
-
renderLink: (args: ShellAppRailRenderArgs) => ReactNode;
|
|
16
|
-
}
|
|
17
|
-
export declare function ShellAppRail({ mainItems, utilityItems, className, footer, isItemActive, renderLink, }: ShellAppRailProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
-
export {};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
function joinClasses(...values) {
|
|
5
|
-
return values.filter(Boolean).join(' ');
|
|
6
|
-
}
|
|
7
|
-
function RailDivider({ pinned, onToggle }) {
|
|
8
|
-
return (_jsx("div", { className: "group flex w-full items-center justify-center py-4", children: _jsxs("button", { type: "button", "aria-label": pinned ? 'Push items down' : 'Push items up', onClick: onToggle, className: "flex cursor-pointer flex-col items-center gap-0.5", children: [_jsx("span", { className: "hidden h-3.5 w-6 items-center justify-center text-[var(--app-rail-foreground)] transition-colors group-hover:flex", children: pinned ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronUp, { size: 14 }) }), _jsx("span", { className: "block h-0.5 w-6 rounded-full bg-[var(--app-rail-foreground)]/30 transition-colors group-hover:bg-[var(--primary)]" })] }) }));
|
|
9
|
-
}
|
|
10
|
-
function RailSection({ items, isItemActive, renderLink, ariaLabel, className, }) {
|
|
11
|
-
return (_jsx("nav", { "aria-label": ariaLabel, className: className, children: items.map((item) => {
|
|
12
|
-
const isActive = isItemActive(item);
|
|
13
|
-
const Icon = item.icon;
|
|
14
|
-
return renderLink({
|
|
15
|
-
item,
|
|
16
|
-
isActive,
|
|
17
|
-
className: joinClasses('flex w-full flex-col items-center justify-center gap-1.5 rounded-2xl px-2 py-2.5 text-center transition-colors', isActive
|
|
18
|
-
? 'bg-[var(--app-rail-item-active-bg)] text-[var(--foreground)]'
|
|
19
|
-
: 'text-[var(--app-rail-foreground)] hover:bg-[var(--surface-highlight)] hover:text-[var(--foreground)]'),
|
|
20
|
-
children: (_jsxs(_Fragment, { children: [_jsx(Icon, { size: 22 }), _jsx("span", { className: "text-[10px] font-medium leading-none", children: item.label })] })),
|
|
21
|
-
});
|
|
22
|
-
}) }));
|
|
23
|
-
}
|
|
24
|
-
export function ShellAppRail({ mainItems, utilityItems, className, footer, isItemActive, renderLink, }) {
|
|
25
|
-
const [pinned, setPinned] = useState(false);
|
|
26
|
-
return (_jsxs("div", { className: joinClasses('flex w-[var(--app-rail-width)] shrink-0 flex-col items-center bg-[var(--app-rail)] px-2 py-4', className), children: [_jsx(RailSection, { items: mainItems, isItemActive: isItemActive, renderLink: renderLink, ariaLabel: "Main navigation", className: "flex flex-col items-center gap-1.5" }), _jsx("div", { className: joinClasses('transition-[flex-grow] duration-300 ease-in-out', pinned ? 'grow-0' : 'grow') }), _jsx(RailDivider, { pinned: pinned, onToggle: () => setPinned(!pinned) }), _jsx(RailSection, { items: utilityItems, isItemActive: isItemActive, renderLink: renderLink, ariaLabel: "Utility navigation", className: "mb-2 flex flex-col items-center gap-1" }), _jsx("div", { className: joinClasses('transition-[flex-grow] duration-300 ease-in-out', pinned ? 'grow' : 'grow-0') }), footer] }));
|
|
27
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
export type ShellAuthTheme = 'light' | 'dark';
|
|
3
|
-
export interface ShellAuthFrameProps {
|
|
4
|
-
children: ReactNode;
|
|
5
|
-
title: ReactNode;
|
|
6
|
-
description?: ReactNode;
|
|
7
|
-
brandName?: ReactNode;
|
|
8
|
-
brandMark?: ReactNode;
|
|
9
|
-
eyebrow?: ReactNode;
|
|
10
|
-
preview?: ReactNode;
|
|
11
|
-
actions?: ReactNode;
|
|
12
|
-
className?: string;
|
|
13
|
-
}
|
|
14
|
-
export interface ShellAuthThemeToggleProps {
|
|
15
|
-
value: ShellAuthTheme;
|
|
16
|
-
onValueChange: (value: ShellAuthTheme) => void;
|
|
17
|
-
className?: string;
|
|
18
|
-
lightLabel?: string;
|
|
19
|
-
darkLabel?: string;
|
|
20
|
-
}
|
|
21
|
-
export declare function ShellAuthFrame({ children, title, description, brandName, brandMark, eyebrow, preview, actions, className, }: ShellAuthFrameProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
-
export declare function ShellAuthThemeToggle({ value, onValueChange, className, lightLabel, darkLabel, }: ShellAuthThemeToggleProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { BarChart3, CheckCircle2, Mail, Moon, PanelRightOpen, Search, Send, Sparkles, Sun, UsersRound, } from 'lucide-react';
|
|
4
|
-
function cx(...values) {
|
|
5
|
-
return values.filter(Boolean).join(' ');
|
|
6
|
-
}
|
|
7
|
-
function DefaultBrandMark() {
|
|
8
|
-
return (_jsx("span", { className: "inline-flex size-10 items-center justify-center rounded-lg bg-white/14 text-white ring-1 ring-white/20 shadow-lg shadow-black/20", children: _jsx(Sparkles, { className: "size-5", "aria-hidden": true }) }));
|
|
9
|
-
}
|
|
10
|
-
export function ShellAuthFrame({ children, title, description, brandName = 'Meda', brandMark, eyebrow, preview, actions, className, }) {
|
|
11
|
-
const resolvedBrandMark = brandMark ?? _jsx(DefaultBrandMark, {});
|
|
12
|
-
return (_jsxs("section", { "data-testid": "shell-auth-frame", className: cx('grid min-h-screen overflow-hidden bg-background text-foreground lg:grid-cols-[minmax(0,1.08fr)_minmax(420px,0.92fr)]', className), children: [_jsxs("div", { className: "relative flex min-h-[24rem] flex-col overflow-hidden bg-[radial-gradient(circle_at_24%_18%,var(--color-info-500)_0,transparent_25%),radial-gradient(circle_at_84%_24%,var(--color-brand-400)_0,transparent_28%),linear-gradient(135deg,var(--color-brand-800),var(--color-brand-700)_42%,var(--color-brand-500))] px-6 py-6 text-white sm:min-h-[30rem] sm:px-8 lg:min-h-screen lg:px-10 lg:py-8", children: [_jsx("div", { className: "pointer-events-none absolute inset-0 opacity-[0.07]", style: {
|
|
13
|
-
backgroundImage: "url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E\")",
|
|
14
|
-
} }), _jsxs("div", { className: "relative z-10 flex items-center gap-3", children: [resolvedBrandMark, _jsx("span", { className: "text-lg font-semibold tracking-normal", children: brandName })] }), _jsxs("div", { className: "relative z-10 flex flex-1 flex-col justify-center gap-8 py-10 lg:py-12", children: [_jsxs("div", { className: "max-w-2xl", children: [eyebrow && (_jsx("p", { className: "mb-4 text-sm font-semibold uppercase tracking-[0.12em] text-white/70", children: eyebrow })), _jsx("h1", { className: "text-4xl font-bold leading-tight tracking-normal text-white sm:text-5xl", children: title }), description && (_jsx("p", { className: "mt-4 max-w-xl text-base leading-7 text-white/72 sm:text-lg", children: description }))] }), _jsx("div", { className: "w-full max-w-3xl", children: preview ?? _jsx(DefaultAuthPreview, {}) })] })] }), _jsxs("div", { className: "relative flex min-h-screen flex-col bg-background px-6 py-6 sm:px-8 lg:px-12", children: [actions && _jsx("div", { className: "flex justify-end", children: actions }), _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-1 flex-col justify-center py-10", children: [_jsx("div", { className: "mb-8 flex justify-center lg:hidden", children: resolvedBrandMark }), children] })] })] }));
|
|
15
|
-
}
|
|
16
|
-
export function ShellAuthThemeToggle({ value, onValueChange, className, lightLabel = 'Light theme', darkLabel = 'Dark theme', }) {
|
|
17
|
-
return (_jsxs("fieldset", { className: cx('inline-flex items-center gap-1 rounded-md border border-border bg-card p-1 text-card-foreground shadow-sm', className), children: [_jsx("legend", { className: "sr-only", children: "Theme" }), _jsx("button", { type: "button", "aria-label": lightLabel, "aria-pressed": value === 'light', title: lightLabel, onClick: () => onValueChange('light'), className: themeButtonClass(value === 'light'), children: _jsx(Sun, { className: "size-4", "aria-hidden": true }) }), _jsx("button", { type: "button", "aria-label": darkLabel, "aria-pressed": value === 'dark', title: darkLabel, onClick: () => onValueChange('dark'), className: themeButtonClass(value === 'dark'), children: _jsx(Moon, { className: "size-4", "aria-hidden": true }) })] }));
|
|
18
|
-
}
|
|
19
|
-
function themeButtonClass(active) {
|
|
20
|
-
return cx('inline-flex size-8 items-center justify-center rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', active
|
|
21
|
-
? 'bg-primary text-primary-foreground shadow-sm'
|
|
22
|
-
: 'text-muted-foreground hover:bg-muted hover:text-foreground');
|
|
23
|
-
}
|
|
24
|
-
function DefaultAuthPreview() {
|
|
25
|
-
return (_jsx("div", { "data-testid": "shell-auth-default-preview", className: "relative overflow-hidden rounded-[1.5rem] border border-white/16 bg-[color-mix(in_srgb,var(--background)_88%,transparent)] p-2 text-foreground shadow-2xl shadow-black/30 backdrop-blur", "aria-hidden": true, children: _jsxs("div", { className: "overflow-hidden rounded-[1.125rem] border border-border/70 bg-background shadow-sm", children: [_jsxs("div", { className: "flex h-9 items-center justify-between bg-card/90 px-3", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "size-2 rounded-full bg-rose-400" }), _jsx("span", { className: "size-2 rounded-full bg-amber-400" }), _jsx("span", { className: "size-2 rounded-full bg-emerald-400" })] }), _jsx("span", { className: "hidden rounded-full bg-muted px-2 py-0.5 text-[9px] font-medium text-muted-foreground sm:inline-flex", children: "app.medal.social" })] }), _jsxs("div", { "data-testid": "shell-auth-preview-shell-header", className: "flex h-12 items-center justify-between border-shell-border border-t border-b bg-shell-header px-3", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "inline-flex size-7 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: _jsx(Sparkles, { className: "size-3.5", "aria-hidden": "true" }) }), _jsx("span", { className: "truncate text-[11px] font-semibold", children: "Medal Social" }), _jsx("div", { className: "ml-2 hidden items-center gap-0.5 rounded-full bg-muted/70 p-0.5 sm:flex", children: PREVIEW_TABS.map((tab) => (_jsx("span", { className: cx('rounded-full px-2 py-1 text-[10px] font-medium', tab === 'Inbox'
|
|
26
|
-
? 'bg-background text-foreground shadow-sm'
|
|
27
|
-
: 'text-muted-foreground'), children: tab }, tab))) })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "hidden rounded-lg bg-primary px-2.5 py-1.5 text-[9px] font-semibold text-primary-foreground sm:inline-flex", children: "+ New" }), _jsx("span", { "data-testid": "shell-auth-preview-panel-toggle", className: "inline-flex size-8 items-center justify-center rounded-lg border border-border bg-card text-muted-foreground shadow-sm", children: _jsx(PanelRightOpen, { className: "size-3.5", "aria-hidden": "true" }) })] })] }), _jsxs("div", { className: "grid min-h-[19.75rem] grid-cols-[44px_minmax(0,1fr)] gap-2 bg-shell-main p-2 sm:grid-cols-[48px_138px_minmax(0,1fr)] lg:grid-cols-[52px_156px_minmax(0,1fr)]", children: [_jsxs("aside", { "data-testid": "shell-auth-preview-icon-rail", className: "flex flex-col items-center gap-1 rounded-2xl bg-shell-rail p-1.5 shadow-sm ring-1 ring-border/70", children: [PREVIEW_RAIL_ITEMS.map((item) => (_jsx(PreviewRailItem, { icon: item.icon, label: item.label, active: item.active, badge: item.badge }, item.label))), _jsx("span", { className: "mt-auto inline-flex size-8 items-center justify-center rounded-lg bg-muted text-[10px] font-semibold text-muted-foreground", children: "AS" })] }), _jsxs("aside", { "data-testid": "shell-auth-preview-context-rail", className: "hidden overflow-hidden rounded-2xl bg-shell-context shadow-sm ring-1 ring-border/70 sm:flex sm:flex-col", children: [_jsxs("div", { className: "border-shell-border border-b px-3 py-3", children: [_jsx("p", { className: "text-[11px] font-semibold text-foreground", children: "Customer OS" }), _jsx("p", { className: "mt-0.5 truncate text-[9px] text-muted-foreground", children: "Auto-CS workspace" })] }), _jsx("nav", { className: "flex flex-col gap-0.5 p-2", children: PREVIEW_CONTEXT_ITEMS.map((item) => (_jsx(PreviewContextItem, { ...item }, item.label))) }), _jsxs("div", { className: "mt-auto border-shell-border border-t p-2", children: [_jsx("p", { className: "text-[9px] font-medium text-muted-foreground", children: "Next sync" }), _jsx("p", { className: "mt-1 truncate text-[10px] font-semibold text-foreground", children: "12 campaigns" })] })] }), _jsxs("main", { className: "min-w-0 overflow-hidden rounded-2xl bg-background/80 p-3 shadow-sm ring-1 ring-border/70", children: [_jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-[9px] font-medium uppercase text-muted-foreground", children: "Inbox command center" }), _jsx("p", { className: "mt-1 truncate text-sm font-semibold", children: "Launch queue and support" })] }), _jsx("span", { className: "rounded-full bg-success/12 px-2 py-1 text-[9px] font-semibold text-success", children: "Live" })] }), _jsxs("div", { className: "mb-3 flex h-9 items-center gap-2 rounded-xl border border-border bg-card px-2.5 text-muted-foreground shadow-sm", children: [_jsx(Search, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), _jsx("span", { className: "truncate text-[10px]", children: "Ask Meda to draft a customer reply..." }), _jsx("kbd", { className: "ml-auto hidden rounded-md bg-muted px-1.5 py-0.5 font-mono text-[8px] text-muted-foreground sm:inline", children: "CMD K" })] }), _jsx("div", { className: "grid gap-2 sm:grid-cols-3", children: PREVIEW_METRICS.map((metric) => (_jsxs("div", { className: "rounded-xl border border-border bg-card p-2.5", children: [_jsx("p", { className: "text-[9px] font-medium text-muted-foreground", children: metric.label }), _jsx("p", { className: "mt-1 text-base font-semibold", children: metric.value })] }, metric.label))) }), _jsxs("div", { className: "mt-3 grid gap-2 lg:grid-cols-[minmax(0,1fr)_112px]", children: [_jsxs("div", { className: "rounded-xl border border-border bg-card p-2.5", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("p", { className: "text-[11px] font-semibold", children: "Priority work" }), _jsx("p", { className: "text-[9px] text-muted-foreground", children: "Updated now" })] }), _jsx("div", { className: "space-y-2", children: PREVIEW_ACTIVITY.map((item) => (_jsx(PreviewActivityRow, { label: item }, item))) })] }), _jsxs("div", { className: "hidden rounded-xl border border-border bg-card p-2.5 lg:block", children: [_jsx("p", { className: "text-[10px] font-semibold", children: "Publish health" }), _jsx("div", { className: "mt-3 h-1.5 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full w-[72%] rounded-full bg-primary" }) }), _jsx("p", { className: "mt-2 text-[9px] leading-4 text-muted-foreground", children: "Response SLA and scheduled posts are on track." })] })] })] })] })] }) }));
|
|
28
|
-
}
|
|
29
|
-
function PreviewRailItem({ icon: Icon, label, active, badge, }) {
|
|
30
|
-
return (_jsxs("span", { className: cx('relative inline-flex size-8 items-center justify-center rounded-lg transition-colors', active ? 'bg-primary/12 text-primary' : 'text-muted-foreground'), title: label, children: [_jsx(Icon, { className: "size-4", "aria-hidden": "true" }), badge && _jsx("span", { className: "absolute top-0.5 right-0.5 size-1.5 rounded-full bg-primary" })] }));
|
|
31
|
-
}
|
|
32
|
-
function PreviewContextItem({ icon: Icon, label, active, shortcut, }) {
|
|
33
|
-
return (_jsxs("span", { className: cx('flex items-center gap-2 rounded-md px-2 py-1.5 text-[10px] transition-colors', active ? 'bg-primary/10 font-semibold text-primary' : 'text-muted-foreground'), children: [_jsx(Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), _jsx("span", { className: "truncate", children: label }), shortcut && (_jsx("kbd", { className: "ml-auto font-mono text-[8px] text-muted-foreground", children: shortcut }))] }));
|
|
34
|
-
}
|
|
35
|
-
function PreviewActivityRow({ label }) {
|
|
36
|
-
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "inline-flex size-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary", children: _jsx(CheckCircle2, { className: "size-3", "aria-hidden": "true" }) }), _jsx("p", { className: "truncate text-[10px] text-muted-foreground", children: label })] }));
|
|
37
|
-
}
|
|
38
|
-
const PREVIEW_TABS = ['Home', 'Inbox', 'Campaigns'];
|
|
39
|
-
const PREVIEW_RAIL_ITEMS = [
|
|
40
|
-
{ icon: BarChart3, label: 'Overview' },
|
|
41
|
-
{ icon: Mail, label: 'Inbox', active: true, badge: '4' },
|
|
42
|
-
{ icon: Send, label: 'Posts' },
|
|
43
|
-
{ icon: UsersRound, label: 'Contacts' },
|
|
44
|
-
];
|
|
45
|
-
const PREVIEW_CONTEXT_ITEMS = [
|
|
46
|
-
{ icon: BarChart3, label: 'Overview', shortcut: '1' },
|
|
47
|
-
{ icon: Mail, label: 'Inbox', active: true, shortcut: '2' },
|
|
48
|
-
{ icon: Send, label: 'Posts', shortcut: '3' },
|
|
49
|
-
{ icon: UsersRound, label: 'Contacts', shortcut: '4' },
|
|
50
|
-
];
|
|
51
|
-
const PREVIEW_METRICS = [
|
|
52
|
-
{ label: 'Queued posts', value: '18' },
|
|
53
|
-
{ label: 'Open tickets', value: '42' },
|
|
54
|
-
{ label: 'Reach', value: '9.8k' },
|
|
55
|
-
];
|
|
56
|
-
const PREVIEW_ACTIVITY = [
|
|
57
|
-
'Campaign approved for publishing',
|
|
58
|
-
'Lead segment synced with CRM',
|
|
59
|
-
'Automated reply drafted for review',
|
|
60
|
-
];
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
export declare function ShellDesktopPanelDock({ defaultView, panelOpen, viewIds, width, onWidthChange, renderPanel, className, }: {
|
|
3
|
-
defaultView: string;
|
|
4
|
-
panelOpen: boolean;
|
|
5
|
-
viewIds?: string[];
|
|
6
|
-
width: number;
|
|
7
|
-
onWidthChange: (width: number) => void;
|
|
8
|
-
renderPanel: (options: {
|
|
9
|
-
defaultView: string;
|
|
10
|
-
viewIds?: string[];
|
|
11
|
-
className?: string;
|
|
12
|
-
}) => React.ReactNode;
|
|
13
|
-
className?: string;
|
|
14
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
function ShellPanelResizeHandle({ width, onWidthChange, }) {
|
|
4
|
-
const startXRef = React.useRef(0);
|
|
5
|
-
const startWidthRef = React.useRef(width);
|
|
6
|
-
const cleanupRef = React.useRef(null);
|
|
7
|
-
React.useEffect(() => {
|
|
8
|
-
return () => {
|
|
9
|
-
cleanupRef.current?.();
|
|
10
|
-
};
|
|
11
|
-
}, []);
|
|
12
|
-
function handlePointerDown(event) {
|
|
13
|
-
if (event.button !== 0)
|
|
14
|
-
return;
|
|
15
|
-
startXRef.current = event.clientX;
|
|
16
|
-
startWidthRef.current = width;
|
|
17
|
-
const handlePointerMove = (moveEvent) => {
|
|
18
|
-
onWidthChange(startWidthRef.current + (startXRef.current - moveEvent.clientX));
|
|
19
|
-
};
|
|
20
|
-
const handlePointerEnd = () => {
|
|
21
|
-
window.removeEventListener('pointermove', handlePointerMove);
|
|
22
|
-
window.removeEventListener('pointerup', handlePointerEnd);
|
|
23
|
-
window.removeEventListener('pointercancel', handlePointerEnd);
|
|
24
|
-
cleanupRef.current = null;
|
|
25
|
-
};
|
|
26
|
-
cleanupRef.current?.();
|
|
27
|
-
window.addEventListener('pointermove', handlePointerMove);
|
|
28
|
-
window.addEventListener('pointerup', handlePointerEnd);
|
|
29
|
-
window.addEventListener('pointercancel', handlePointerEnd);
|
|
30
|
-
cleanupRef.current = handlePointerEnd;
|
|
31
|
-
}
|
|
32
|
-
return (_jsx("button", { type: "button", "aria-label": "Resize panel", onPointerDown: handlePointerDown, className: "absolute inset-y-0 left-0 z-10 flex w-2 shrink-0 cursor-col-resize items-center justify-center bg-transparent transition-colors hover:bg-[var(--primary)]/10", children: _jsx("span", { className: "h-12 w-px rounded-full bg-[var(--border-subtle)]" }) }));
|
|
33
|
-
}
|
|
34
|
-
export function ShellDesktopPanelDock({ defaultView, panelOpen, viewIds, width, onWidthChange, renderPanel, className, }) {
|
|
35
|
-
return (_jsx("div", { "data-testid": "desktop-panel-dock", "aria-hidden": !panelOpen, "data-state": panelOpen ? 'open' : 'closed', className: panelOpen
|
|
36
|
-
? 'pointer-events-auto absolute inset-y-4 right-4 z-20 translate-x-0 opacity-100 transition-[transform,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]'
|
|
37
|
-
: 'pointer-events-none absolute inset-y-4 right-4 z-20 translate-x-[calc(100%+1.25rem)] opacity-0 transition-[transform,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]', style: { width: `${width}px` }, children: _jsxs("div", { className: "relative h-full w-full", children: [_jsx(ShellPanelResizeHandle, { width: width, onWidthChange: onWidthChange }), renderPanel({
|
|
38
|
-
defaultView,
|
|
39
|
-
viewIds,
|
|
40
|
-
className: className ??
|
|
41
|
-
'ml-2 rounded-[var(--meda-shell-panel-dock-radius)] border border-[var(--border-subtle)] bg-[color-mix(in_srgb,var(--side-panel)_90%,transparent)] shadow-[var(--meda-shell-panel-dock-shadow)] backdrop-blur-xl',
|
|
42
|
-
})] }) }));
|
|
43
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
export declare function ShellFrame({ header, navigation, mobileSidebar, mobileHeader, tabBar, content, desktopDock, tabletPanel, mobileBottomNav, commandPalette, }: {
|
|
3
|
-
header: ReactNode;
|
|
4
|
-
navigation: ReactNode;
|
|
5
|
-
mobileSidebar?: ReactNode;
|
|
6
|
-
mobileHeader?: ReactNode;
|
|
7
|
-
tabBar?: ReactNode;
|
|
8
|
-
content: ReactNode;
|
|
9
|
-
desktopDock?: ReactNode;
|
|
10
|
-
tabletPanel?: ReactNode;
|
|
11
|
-
mobileBottomNav?: ReactNode;
|
|
12
|
-
commandPalette?: ReactNode;
|
|
13
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
export function ShellFrame({ header, navigation, mobileSidebar, mobileHeader, tabBar, content, desktopDock, tabletPanel, mobileBottomNav, commandPalette, }) {
|
|
3
|
-
return (_jsxs("div", { "data-testid": "shell-frame", className: "flex h-screen w-full flex-1 flex-col overflow-hidden bg-[var(--background)]", children: [header, _jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [navigation, mobileSidebar, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden", children: [mobileHeader, tabBar, _jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden", children: [content, desktopDock] })] }), tabletPanel, mobileBottomNav] }), commandPalette] }));
|
|
4
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { ShellModuleDefinition, ShellNavItem } from './types.js';
|
|
3
|
-
interface ShellModuleNavRenderArgs {
|
|
4
|
-
children: ReactNode;
|
|
5
|
-
className: string;
|
|
6
|
-
isActive: boolean;
|
|
7
|
-
item: ShellNavItem;
|
|
8
|
-
}
|
|
9
|
-
export interface ShellModuleNavProps {
|
|
10
|
-
module: Pick<ShellModuleDefinition, 'description' | 'items' | 'label'>;
|
|
11
|
-
ariaLabel: string;
|
|
12
|
-
className?: string;
|
|
13
|
-
descriptionClassName?: string;
|
|
14
|
-
headerClassName?: string;
|
|
15
|
-
itemClassName?: string;
|
|
16
|
-
activeItemClassName?: string;
|
|
17
|
-
inactiveItemClassName?: string;
|
|
18
|
-
itemsClassName?: string;
|
|
19
|
-
itemIconClassName?: string;
|
|
20
|
-
itemIconSize?: number;
|
|
21
|
-
itemLabelClassName?: string;
|
|
22
|
-
itemDescriptionClassName?: string;
|
|
23
|
-
itemShortcutClassName?: string;
|
|
24
|
-
titleClassName?: string;
|
|
25
|
-
isItemActive: (item: ShellNavItem) => boolean;
|
|
26
|
-
renderLink: (args: ShellModuleNavRenderArgs) => ReactNode;
|
|
27
|
-
}
|
|
28
|
-
export declare function ShellModuleNav({ module, ariaLabel, className, descriptionClassName, headerClassName, itemClassName, activeItemClassName, inactiveItemClassName, itemsClassName, itemIconClassName, itemIconSize, itemLabelClassName, itemDescriptionClassName, itemShortcutClassName, titleClassName, isItemActive, renderLink, }: ShellModuleNavProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
-
export {};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
function joinClasses(...values) {
|
|
3
|
-
return values.filter(Boolean).join(' ');
|
|
4
|
-
}
|
|
5
|
-
export function ShellModuleNav({ module, ariaLabel, className, descriptionClassName, headerClassName, itemClassName, activeItemClassName, inactiveItemClassName, itemsClassName, itemIconClassName, itemIconSize = 18, itemLabelClassName, itemDescriptionClassName, itemShortcutClassName, titleClassName, isItemActive, renderLink, }) {
|
|
6
|
-
return (_jsxs("div", { className: className, children: [_jsxs("div", { className: headerClassName, children: [_jsx("div", { className: titleClassName, children: module.label }), module.description ? (_jsx("div", { className: descriptionClassName, children: module.description })) : null] }), _jsx("nav", { "aria-label": ariaLabel, className: itemsClassName, children: module.items.map((item) => {
|
|
7
|
-
const isActive = isItemActive(item);
|
|
8
|
-
const Icon = item.icon;
|
|
9
|
-
return renderLink({
|
|
10
|
-
item,
|
|
11
|
-
isActive,
|
|
12
|
-
className: joinClasses(itemClassName, isActive ? activeItemClassName : inactiveItemClassName),
|
|
13
|
-
children: (_jsxs(_Fragment, { children: [_jsx(Icon, { size: itemIconSize, className: itemIconClassName }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: itemLabelClassName, children: item.label }), item.description ? (_jsx("span", { className: itemDescriptionClassName, children: item.description })) : null] }), item.shortcut ? (_jsx("kbd", { className: itemShortcutClassName, children: item.shortcut })) : null] })),
|
|
14
|
-
});
|
|
15
|
-
}) })] }));
|
|
16
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ShellPanelDefinition } from './types.js';
|
|
2
|
-
export interface ShellPanelRailProps {
|
|
3
|
-
moduleViews: ShellPanelDefinition[];
|
|
4
|
-
globalViews: ShellPanelDefinition[];
|
|
5
|
-
activePanelView: string | null;
|
|
6
|
-
className?: string;
|
|
7
|
-
onTogglePanelView: (viewId: string) => void;
|
|
8
|
-
}
|
|
9
|
-
export declare function ShellPanelRail({ moduleViews, globalViews, activePanelView, className, onTogglePanelView, }: ShellPanelRailProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
function joinClasses(...values) {
|
|
3
|
-
return values.filter(Boolean).join(' ');
|
|
4
|
-
}
|
|
5
|
-
function PanelRailButtons({ views, activePanelView, onTogglePanelView, }) {
|
|
6
|
-
return (_jsx("div", { className: "flex w-full flex-col items-center gap-1", children: views.map(({ id, label, icon: Icon }) => {
|
|
7
|
-
const isActive = activePanelView === id;
|
|
8
|
-
return (_jsxs("button", { type: "button", "aria-label": label, "aria-pressed": isActive, onClick: () => onTogglePanelView(id), className: joinClasses('flex w-full flex-col items-center gap-1 rounded-xl px-1 py-2 text-[var(--app-rail-foreground)] transition-colors', isActive
|
|
9
|
-
? 'bg-[var(--app-rail-item-active-bg)] text-[var(--foreground)]'
|
|
10
|
-
: 'hover:bg-[var(--surface-highlight)] hover:text-[var(--foreground)]'), children: [_jsx(Icon, { size: 16 }), _jsx("span", { className: "text-[9px] font-medium leading-none", children: label })] }, id));
|
|
11
|
-
}) }));
|
|
12
|
-
}
|
|
13
|
-
export function ShellPanelRail({ moduleViews, globalViews, activePanelView, className, onTogglePanelView, }) {
|
|
14
|
-
if (moduleViews.length === 0 && globalViews.length === 0)
|
|
15
|
-
return null;
|
|
16
|
-
return (_jsxs("aside", { "aria-label": "Panel rail", className: joinClasses('flex h-full w-[var(--right-rail-width)] shrink-0 flex-col items-center bg-[var(--app-rail)] px-1.5 py-3', className), children: [_jsx(PanelRailButtons, { views: moduleViews, activePanelView: activePanelView, onTogglePanelView: onTogglePanelView }), _jsx("div", { className: "grow" }), _jsx(PanelRailButtons, { views: globalViews, activePanelView: activePanelView, onTogglePanelView: onTogglePanelView })] }));
|
|
17
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { ShellContentLayout } from './types.js';
|
|
3
|
-
export interface ShellScrollableContentProps {
|
|
4
|
-
children: ReactNode;
|
|
5
|
-
desktopDockOffset?: number;
|
|
6
|
-
layout: ShellContentLayout;
|
|
7
|
-
maxWidth?: number;
|
|
8
|
-
}
|
|
9
|
-
export declare function ShellScrollableContent({ children, desktopDockOffset, layout, maxWidth, }: ShellScrollableContentProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
function joinClasses(...values) {
|
|
3
|
-
return values.filter(Boolean).join(' ');
|
|
4
|
-
}
|
|
5
|
-
export function ShellScrollableContent({ children, desktopDockOffset = 0, layout, maxWidth, }) {
|
|
6
|
-
return (_jsx("main", { "data-testid": "shell-workspace-viewport", className: "shell-scrollbar-hidden absolute inset-0 min-w-0 overflow-y-auto overflow-x-hidden pb-[var(--bottom-nav-height)] transition-[padding] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] md:pb-0", style: desktopDockOffset > 0 ? { paddingRight: `${desktopDockOffset}px` } : undefined, children: layout === 'centered' ? (_jsx("div", { "data-testid": "shell-content-centered", className: "flex min-h-full items-center justify-center px-5 py-5 sm:px-6 sm:py-6 md:px-8 md:py-8 lg:px-10", children: _jsx("div", { className: "flex w-full max-w-3xl items-center justify-center", children: children }) })) : layout === 'fullbleed' ? (_jsx("div", { "data-testid": "shell-content-fullbleed", className: "mx-auto w-full px-5 py-5 sm:px-6 sm:py-6 md:px-8 md:py-8 lg:px-10", style: maxWidth ? { maxWidth: `${maxWidth}px` } : undefined, children: children })) : (_jsx("div", { "data-testid": "shell-content-workspace", className: joinClasses('mx-auto w-full px-5 py-5 sm:px-6 sm:py-6 md:px-8 md:py-8 lg:px-10'), style: maxWidth ? { maxWidth: `${maxWidth}px` } : undefined, children: children })) }));
|
|
7
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from 'react';
|
|
2
|
-
export declare const DEFAULT_SIDE_PANEL_WIDTH = 360;
|
|
3
|
-
export declare const MIN_SIDE_PANEL_WIDTH = 280;
|
|
4
|
-
export declare const MAX_SIDE_PANEL_WIDTH = 520;
|
|
5
|
-
export interface ShellSearchParamsAdapter {
|
|
6
|
-
searchParams: URLSearchParams;
|
|
7
|
-
setSearchParams: (updater: (currentSearchParams: URLSearchParams) => URLSearchParams) => void;
|
|
8
|
-
}
|
|
9
|
-
export interface ShellStateContextValue {
|
|
10
|
-
activePanelView: string | null;
|
|
11
|
-
lastPanelView: string | null;
|
|
12
|
-
setActivePanelView: (view: string | null) => void;
|
|
13
|
-
closePanelView: () => void;
|
|
14
|
-
restoreLastPanelView: (fallbackView: string | null) => void;
|
|
15
|
-
togglePanelView: (viewId: string) => void;
|
|
16
|
-
sidePanelWidth: number;
|
|
17
|
-
setSidePanelWidth: (width: number) => void;
|
|
18
|
-
activeRail: string;
|
|
19
|
-
setActiveRail: (rail: string) => void;
|
|
20
|
-
selectedEntityId: string | null;
|
|
21
|
-
selectEntity: (entityId: string, options?: {
|
|
22
|
-
panelView?: string | null;
|
|
23
|
-
}) => void;
|
|
24
|
-
clearEntity: () => void;
|
|
25
|
-
commandPaletteOpen: boolean;
|
|
26
|
-
setCommandPaletteOpen: (open: boolean) => void;
|
|
27
|
-
openCommandPalette: () => void;
|
|
28
|
-
closeCommandPalette: () => void;
|
|
29
|
-
}
|
|
30
|
-
export declare function clampSidePanelWidth(width: number): number;
|
|
31
|
-
export declare function ShellStateProvider({ children, adapter, initialActiveRail, panelQueryParam, selectionQueryParam, sidePanelWidthStorageKey, }: {
|
|
32
|
-
children: ReactNode;
|
|
33
|
-
adapter: ShellSearchParamsAdapter;
|
|
34
|
-
initialActiveRail?: string;
|
|
35
|
-
panelQueryParam?: string;
|
|
36
|
-
selectionQueryParam?: string;
|
|
37
|
-
sidePanelWidthStorageKey?: string;
|
|
38
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
39
|
-
export declare function useShellState(): ShellStateContextValue;
|