Files

821 lines
32 KiB
TypeScript

import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
useDefaultLayout,
} from '@signozhq/ui';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { BarChart3, Code, Database, FileText, Settings, Terminal } from 'lucide-react';
const meta: Meta<typeof ResizablePanelGroup> = {
title: 'Components/Resizable',
component: ResizablePanelGroup,
parameters: {
layout: 'fullscreen',
},
argTypes: {
orientation: {
control: 'select',
options: ['horizontal', 'vertical'],
description:
'Specifies the resizable orientation ("horizontal" or "vertical"); defaults to "horizontal"',
table: {
category: 'Layout',
type: { summary: "'horizontal' | 'vertical'" },
defaultValue: { summary: 'horizontal' },
},
},
defaultLayout: {
control: false,
description:
'Default layout for the Group. This value allows layouts to be remembered between page reloads.',
table: { category: 'Layout', type: { summary: 'Layout' } },
},
onLayoutChange: {
control: false,
description:
"Called when the Group's layout is changing. ⚠️ For layout changes caused by pointer events, this method is called each time the pointer is moved. For most cases, it is recommended to use the `onLayoutChanged` callback instead.",
table: { category: 'Events', type: { summary: '(layout: Layout) => void' } },
},
onLayoutChanged: {
control: false,
description:
"Called after the Group's layout has been changed. For layout changes caused by pointer events, this method is not called until the pointer has been released. This method is recommended when saving layouts to some storage api.",
table: { category: 'Events', type: { summary: '(layout: Layout) => void' } },
},
disabled: {
control: 'boolean',
description: 'Disable resize functionality.',
table: {
category: 'Behavior',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
disableCursor: {
control: 'boolean',
description:
'This library sets custom mouse cursor styles to indicate drag state. Use this prop to disable that behavior for Panels and Separators in this group.',
table: {
category: 'Behavior',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
resizeTargetMinimumSize: {
control: false,
description:
'Minimum size of the resizable hit target area (either Separator or Panel edge). This threshold ensures are large enough to avoid mis-clicks.',
table: { category: 'Behavior', type: { summary: '{ coarse: number; fine: number }' } },
},
groupRef: {
control: false,
description:
'Exposes the following imperative API: getLayout(): Layout and setLayout(layout: Layout): void. The useGroupRef and useGroupCallbackRef hooks are exported for convenience use in TypeScript projects.',
table: { category: 'Advanced', type: { summary: 'Ref<GroupImperativeHandle | null>' } },
},
style: {
control: false,
description:
'CSS properties. ⚠️ The following styles cannot be overridden: display, flex-direction, flex-wrap, and overflow.',
table: { category: 'Styling', type: { summary: 'CSSProperties' } },
},
id: {
control: 'text',
description:
'Uniquely identifies this group within an application. Falls back to useId when not provided. This value will also be assigned to the data-group attribute.',
table: { category: 'Accessibility', type: { summary: 'string' } },
},
className: {
control: 'text',
description: 'Additional CSS classes to apply to the panel group',
table: { category: 'Styling', type: { summary: 'string' } },
},
children: {
control: false,
description: 'Panel and Separator components that comprise this group.',
table: { category: 'Content', type: { summary: 'ReactNode' } },
},
testId: {
control: 'text',
description: 'The testId associated with the panel group for testing purposes.',
table: { category: 'Testing', type: { summary: 'string' } },
},
},
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof ResizablePanelGroup>;
export const Default: Story = {
render: () => (
<div className="space-y-8 p-6 bg-background">
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Horizontal Layout</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="25%" minSize="20%">
<div className="flex h-full items-center justify-center bg-muted">
<div className="text-center">
<FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">File Explorer</span>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="50%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<Code className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Code Editor</span>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="25%" minSize="20%">
<div className="flex h-full items-center justify-center bg-muted">
<div className="text-center">
<Settings className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Properties</span>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Vertical Layout</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="70%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<BarChart3 className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Main Dashboard</span>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="30%" minSize="25%">
<div className="flex h-full items-center justify-center bg-muted">
<div className="text-center">
<Terminal className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Console Output</span>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
</div>
),
};
export const HorizontalLayout: Story = {
render: () => (
<div className="space-y-6 p-6 bg-background">
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Two Panel Layout</h2>
<div className="h-[300px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="30%" minSize="20%" maxSize="50%">
<div className="flex h-full flex-col p-4 bg-muted">
<h3 className="font-medium mb-2">Sidebar</h3>
<p className="text-sm text-muted-foreground">Navigation and tools</p>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="70%">
<div className="flex h-full flex-col p-4">
<h3 className="font-medium mb-2">Main Content</h3>
<p className="text-sm text-muted-foreground">Primary workspace area</p>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Three Panel Layout</h2>
<div className="h-[300px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="25%" minSize="15%" maxSize="40%">
<div className="flex h-full flex-col p-4 bg-muted">
<FileText className="h-5 w-5 mb-2 text-muted-foreground" />
<h3 className="font-medium mb-2">Explorer</h3>
<div className="text-xs text-muted-foreground space-y-1">
<div>📁 src/</div>
<div className="ml-3">📄 index.ts</div>
<div className="ml-3">📄 app.tsx</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="50%">
<div className="flex h-full flex-col p-4">
<Code className="h-5 w-5 mb-2 text-muted-foreground" />
<h3 className="font-medium mb-2">Editor</h3>
<div className="flex-1 bg-slate-950 rounded text-green-400 p-3 font-mono text-xs">
<div>function App() {'{'}</div>
<div className="ml-2">return &lt;h1&gt;Hello World&lt;/h1&gt;;</div>
<div>{'}'}</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="25%" minSize="20%" maxSize="40%">
<div className="flex h-full flex-col p-4 bg-muted">
<Settings className="h-5 w-5 mb-2 text-muted-foreground" />
<h3 className="font-medium mb-2">Properties</h3>
<div className="text-xs text-muted-foreground space-y-2">
<div>Type: Component</div>
<div>Props: 3</div>
<div>State: Active</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Dashboard Layout</h2>
<div className="h-[300px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="20%" minSize="15%" maxSize="30%">
<div className="flex h-full flex-col p-4 bg-muted">
<BarChart3 className="h-5 w-5 mb-2 text-muted-foreground" />
<h3 className="font-medium mb-2">Metrics</h3>
<div className="text-xs text-muted-foreground space-y-1">
<div>CPU: 45%</div>
<div>Memory: 2.1GB</div>
<div>Disk: 67%</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize="60%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<div className="h-32 w-32 mx-auto mb-4 bg-gradient-to-br from-blue-400 to-purple-500 rounded-lg flex items-center justify-center">
<span className="text-white font-bold">CHART</span>
</div>
<span className="text-sm font-medium">Performance Graph</span>
</div>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize="20%" minSize="15%" maxSize="30%">
<div className="flex h-full flex-col p-4 bg-muted">
<Database className="h-5 w-5 mb-2 text-muted-foreground" />
<h3 className="font-medium mb-2">Status</h3>
<div className="text-xs text-muted-foreground space-y-1">
<div>🟢 API Online</div>
<div>🟢 DB Connected</div>
<div>🟡 Cache Warming</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
</div>
),
};
export const VerticalLayout: Story = {
render: () => (
<div className="space-y-6 p-6 bg-background">
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Application Layout</h2>
<div className="h-[500px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="15%" minSize="10%" maxSize="25%">
<div className="flex h-full items-center justify-between px-6 py-3 bg-muted border-b">
<h3 className="font-medium">Navigation Bar</h3>
<div className="flex gap-2">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<div className="w-2 h-2 bg-yellow-500 rounded-full"></div>
<div className="w-2 h-2 bg-red-500 rounded-full"></div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="65%">
<div className="flex h-full flex-col p-6">
<h3 className="font-medium mb-4">Main Content Area</h3>
<div className="flex-1 bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 rounded-lg flex items-center justify-center">
<span className="text-lg text-muted-foreground">Primary workspace content</span>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="20%" minSize="15%" maxSize="30%">
<div className="flex h-full flex-col p-4 bg-muted border-t">
<Terminal className="h-5 w-5 mb-2 text-muted-foreground" />
<h3 className="font-medium mb-3">Footer / Status Bar</h3>
<div className="text-xs text-muted-foreground space-y-1">
<div>Ready • Line 42, Col 12</div>
<div>UTF-8 • TypeScript • Git:main</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Chat Interface</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="75%">
<div className="flex h-full flex-col p-4">
<h3 className="font-medium mb-3">Messages</h3>
<div className="flex-1 space-y-3">
<div className="flex justify-start">
<div className="bg-muted px-3 py-2 rounded-lg max-w-xs">
<p className="text-sm">Hello! How can I help you today?</p>
</div>
</div>
<div className="flex justify-end">
<div className="bg-primary text-primary-foreground px-3 py-2 rounded-lg max-w-xs">
<p className="text-sm">I need help with the resizable panels.</p>
</div>
</div>
<div className="flex justify-start">
<div className="bg-muted px-3 py-2 rounded-lg max-w-xs">
<p className="text-sm">Sure! You can drag the handles to resize panels.</p>
</div>
</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="25%" minSize="20%" maxSize="40%">
<div className="flex h-full flex-col p-4 bg-muted">
<h3 className="font-medium mb-3">Input Area</h3>
<div className="flex-1 flex flex-col">
<div className="flex-1 bg-background rounded border p-2 text-sm text-muted-foreground">
Type your message...
</div>
<div className="flex justify-end mt-2">
<button
type="button"
className="bg-primary text-primary-foreground px-3 py-1 rounded text-sm"
>
Send
</button>
</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Development Environment</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="60%">
<div className="flex h-full flex-col p-4">
<div className="flex items-center gap-2 mb-3">
<Code className="h-4 w-4 text-muted-foreground" />
<h3 className="font-medium">Code Editor</h3>
<span className="text-xs bg-muted px-2 py-1 rounded">main.tsx</span>
</div>
<div className="flex-1 bg-slate-950 rounded text-green-400 p-4 font-mono text-sm overflow-auto">
<div className="text-gray-500">1</div>
<div className="text-gray-500">2</div>
<div className="text-gray-500">3</div>
<div className="text-gray-500">4</div>
<div className="text-gray-500">5</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="25%" minSize="20%">
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-3">
<Terminal className="h-4 w-4 text-muted-foreground" />
<h3 className="font-medium">Terminal</h3>
</div>
<div className="flex-1 bg-slate-950 rounded text-green-400 p-3 font-mono text-xs">
<div>$ npm run dev</div>
<div className="text-blue-400">Server running on http://localhost:3000</div>
<div className="animate-pulse">█</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="15%" minSize="10%" maxSize="25%">
<div className="flex h-full items-center justify-between px-4 py-2 bg-slate-100 dark:bg-slate-800 border-t">
<div className="text-xs text-muted-foreground">
Problems: 0 • Warnings: 2 • Info: 5
</div>
<div className="text-xs text-muted-foreground">Ln 42, Col 12</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
</div>
),
};
export const CollapsiblePanels: Story = {
render: () => (
<div className="space-y-6 p-6 bg-background">
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Collapsible Sidebar</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="25%" minSize="15%" maxSize="40%" collapsible={true}>
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-4">
<FileText className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">File Explorer</h3>
</div>
<div className="text-xs text-muted-foreground space-y-2">
<div className="flex items-center gap-1">
<span>📁</span> src/
</div>
<div className="flex items-center gap-1 ml-4">
<span>📄</span> App.tsx
</div>
<div className="flex items-center gap-1 ml-4">
<span>📄</span> index.ts
</div>
<div className="flex items-center gap-1">
<span>📁</span> components/
</div>
<div className="flex items-center gap-1 ml-4">
<span>📄</span> Button.tsx
</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="75%">
<div className="flex h-full flex-col p-6">
<h3 className="font-medium mb-4">Code Editor</h3>
<div className="flex-1 bg-slate-950 rounded text-green-400 p-4 font-mono text-sm">
<div>import React from &apos;react&apos;;</div>
<div></div>
<div>function App() {'{'}</div>
<div className="ml-4">return &lt;div&gt;Hello World&lt;/div&gt;</div>
<div>{'}'}</div>
<div></div>
<div>export default App;</div>
</div>
<p className="text-sm text-muted-foreground mt-2">
Try dragging the left panel all the way to collapse it!
</p>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Collapsible Bottom Panel</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="70%">
<div className="flex h-full flex-col p-6">
<h3 className="font-medium mb-4">Main Workspace</h3>
<div className="flex-1 bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-blue-900/20 dark:to-indigo-900/20 rounded-lg flex items-center justify-center">
<div className="text-center">
<BarChart3 className="mx-auto mb-2 h-12 w-12 text-blue-500" />
<span className="text-lg font-medium">Dashboard Content</span>
</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="30%" minSize="20%" maxSize="50%" collapsible={true}>
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-3">
<Terminal className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">Console</h3>
</div>
<div className="flex-1 bg-slate-950 rounded text-green-400 p-3 font-mono text-xs">
<div>$ npm run dev</div>
<div className="text-blue-400">✓ Local server running</div>
<div className="text-yellow-400">⚠ 2 warnings found</div>
<div className="text-gray-500">Watching for changes...</div>
<div className="animate-pulse">█</div>
</div>
<p className="text-xs text-muted-foreground mt-2">
Drag this panel down to collapse it
</p>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
<div>
<h2 className="text-lg font-semibold mb-4 text-foreground">Multiple Collapsible Panels</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="20%" minSize="15%" maxSize="35%" collapsible={true}>
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-3">
<Settings className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">Tools</h3>
</div>
<div className="text-sm text-muted-foreground space-y-2">
<div>🔧 Settings</div>
<div>📊 Analytics</div>
<div>🎨 Themes</div>
<div>🔌 Plugins</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="60%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<Code className="mx-auto mb-2 h-12 w-12 text-muted-foreground" />
<span className="text-lg font-medium">Main Editor</span>
<p className="text-sm text-muted-foreground mt-2">
Both side panels can be collapsed
</p>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="20%" minSize="15%" maxSize="35%" collapsible={true}>
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-3">
<Database className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">Inspector</h3>
</div>
<div className="text-sm text-muted-foreground space-y-2">
<div>🏷️ Properties</div>
<div>🔍 Details</div>
<div>📝 Metadata</div>
<div>🔗 Relations</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
</div>
</div>
),
};
export const PanelGroupPlayground: Story = {
parameters: {
controls: { disable: false },
},
args: {
orientation: 'horizontal',
},
argTypes: {
orientation: {
control: 'select',
options: ['horizontal', 'vertical'],
description: 'Layout orientation of the panel group',
},
},
render: (args) => (
<div className="p-6 bg-background">
<h2 className="text-lg font-semibold mb-4 text-foreground">Interactive Panel Group</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup {...args}>
<ResizablePanel defaultSize="25%" minSize="20%">
<div className="flex h-full items-center justify-center bg-muted">
<div className="text-center">
<FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Panel 1</span>
<p className="text-xs text-muted-foreground mt-1">25% default size</p>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="50%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<Code className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Panel 2</span>
<p className="text-xs text-muted-foreground mt-1">50% default size</p>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="25%">
<div className="flex h-full items-center justify-center bg-muted">
<div className="text-center">
<Settings className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Panel 3</span>
<p className="text-xs text-muted-foreground mt-1">25% default size</p>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
<div className="mt-4 p-4 bg-muted rounded-lg">
<h3 className="font-medium mb-2">Instructions:</h3>
<ul className="text-sm text-muted-foreground space-y-1">
<li>• Change the orientation to see horizontal vs vertical layouts</li>
<li>
• Use useDefaultLayout with groupId for persistent layouts (see Persistent Layout story)
</li>
<li>• Drag the resize handles to adjust panel sizes</li>
</ul>
</div>
</div>
),
};
export const PanelPlayground: Story = {
parameters: {
controls: { disable: false },
},
args: {},
argTypes: {},
render: () => (
<div className="p-6 bg-background">
<h2 className="text-lg font-semibold mb-4 text-foreground">Interactive Panel Properties</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="30%" minSize="20%" maxSize="60%" collapsible={false}>
<div className="flex h-full items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-blue-900/20 dark:to-indigo-900/20">
<div className="text-center">
<BarChart3 className="mx-auto mb-2 h-8 w-8 text-blue-600" />
<span className="text-sm font-medium">Configurable Panel</span>
<div className="text-xs text-muted-foreground mt-2 space-y-1">
<div>Default: 30%</div>
<div>Min: 20%</div>
<div>Max: 60%</div>
<div>Collapsible: No</div>
</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="70%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<Code className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Fixed Panel</span>
<p className="text-xs text-muted-foreground mt-1">Responds to left panel changes</p>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
<div className="mt-4 p-4 bg-muted rounded-lg">
<h3 className="font-medium mb-2">Try these interactions:</h3>
<ul className="text-sm text-muted-foreground space-y-1">
<li>• Adjust the sliders to see how constraints affect resizing</li>
<li>• Enable collapsible and try dragging the panel to minimum size</li>
<li>• Notice how minSize and maxSize limit the resize range</li>
</ul>
</div>
</div>
),
};
export const ResizeHandlePlayground: Story = {
parameters: {
controls: { disable: false },
},
args: {},
argTypes: {},
render: () => (
<div className="p-6 bg-background">
<h2 className="text-lg font-semibold mb-4 text-foreground">Interactive Resize Handle</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel defaultSize="40%">
<div className="flex h-full items-center justify-center bg-muted">
<div className="text-center">
<FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Left Panel</span>
<p className="text-xs text-muted-foreground mt-1">Drag the handle to resize</p>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle={true} disabled={false} />
<ResizablePanel defaultSize="60%">
<div className="flex h-full items-center justify-center">
<div className="text-center">
<Settings className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
<span className="text-sm font-medium">Right Panel</span>
<div className="text-xs text-muted-foreground mt-2 space-y-1">
<div>Handle visible: Yes</div>
<div>Disabled: No</div>
</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
<div className="mt-4 p-4 bg-muted rounded-lg">
<h3 className="font-medium mb-2">Handle Options:</h3>
<ul className="text-sm text-muted-foreground space-y-1">
<li>
• <strong>withHandle:</strong> Shows/hides the visual drag indicator
</li>
<li>
• <strong>disabled:</strong> Prevents resizing when enabled
</li>
<li>• Handle is still functional even when visual indicator is hidden</li>
</ul>
</div>
</div>
),
};
function PersistentLayoutContent({ groupId }: { groupId: string }) {
const { defaultLayout, onLayoutChange } = useDefaultLayout({
groupId: groupId || 'demo-layout',
storage: typeof localStorage !== 'undefined' ? localStorage : undefined,
});
return (
<div className="p-6 bg-background">
<h2 className="text-lg font-semibold mb-4 text-foreground">Persistent Layout Demo</h2>
<div className="h-[400px] border rounded-lg overflow-hidden">
<ResizablePanelGroup
orientation="horizontal"
defaultLayout={defaultLayout}
onLayoutChange={onLayoutChange}
>
<ResizablePanel defaultSize="25%" collapsible>
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-3">
<Database className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">Persistent Sidebar</h3>
</div>
<div className="text-xs text-muted-foreground space-y-2">
<div>This layout persists!</div>
<div>Resize panels and refresh the page</div>
<div>Your layout will be restored</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="50%">
<div className="flex h-full flex-col p-4">
<h3 className="font-medium mb-3">Main Content</h3>
<div className="flex-1 bg-gradient-to-br from-green-50 to-emerald-100 dark:from-green-900/20 dark:to-emerald-900/20 rounded-lg flex items-center justify-center">
<div className="text-center">
<Code className="mx-auto mb-2 h-8 w-8 text-green-600" />
<span className="font-medium">Layout Memory</span>
<p className="text-sm text-muted-foreground mt-2">
groupId: &quot;{groupId || 'demo-layout'}&quot;
</p>
</div>
</div>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize="25%" collapsible>
<div className="flex h-full flex-col p-4 bg-muted">
<div className="flex items-center gap-2 mb-3">
<Settings className="h-5 w-5 text-muted-foreground" />
<h3 className="font-medium">Properties Panel</h3>
</div>
<div className="text-xs text-muted-foreground space-y-2">
<div>Change the groupId to create different saved layouts</div>
<div>Each ID maintains its own layout state</div>
</div>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</div>
<div className="mt-4 p-4 bg-muted rounded-lg">
<h3 className="font-medium mb-2">Persistence Features:</h3>
<ul className="text-sm text-muted-foreground space-y-1">
<li>• Layout automatically saved to localStorage (useDefaultLayout)</li>
<li>• Restore layout on page refresh or revisit</li>
<li>• Different groupId values create separate saved layouts</li>
<li>• Try resizing panels, then refresh the page to see persistence in action</li>
</ul>
</div>
</div>
);
}
export const PersistentLayout: StoryObj<typeof PersistentLayoutContent> = {
parameters: {
controls: { disable: false },
},
args: {
groupId: 'demo-layout',
},
argTypes: {
groupId: {
control: 'text',
description: 'Unique ID for saving layout to localStorage (useDefaultLayout)',
},
},
render: (args) => <PersistentLayoutContent groupId={args.groupId} />,
};