Lightdash Data App 可调分栏布局实战:使用 shadcn Resizable 构建可拖拽面板
2026/9/17 23:53:59 网站建设 项目流程

Lightdash Data App 可调分栏布局实战:使用 shadcn Resizable 构建可拖拽面板

【免费下载链接】lightdashAgentic BI. Analytics at the speed of code ⚡️项目地址: https://gitcode.com/GitHub_Trending/li/lightdash

本指南以 Lightdash Data App 模板内置的 shadcnResizable组件(底层为react-resizable-panels)为对象,说明如何在数据应用中实现"图表 + 详情/检查器"、"过滤器侧栏 + 主网格"、"表格 + 图表"等可拖拽调整大小的分栏布局。读完本文,你将掌握 Resizable 的三个核心组件(ResizablePanelGroup/ResizablePanel/ResizableHandle)的完整用法、记忆用户偏好的autoSaveId机制、面板最小尺寸保护,以及嵌套分组构建复杂网格布局的正确姿势,并了解模板环境中与之配套的安装、预装依赖与布局约束。

什么时候才应该使用可调面板

Lightdash Data App 模板中的 Resizable 组件不是默认布局方案。它的适用信号非常明确:当布局中存在两个或以上同级区域,且用户确实能从"就地重新分配空间"中获益时,才值得引入。典型的场景包括:

  • 图表 + 详情/检查器面板——"看我刚点击的那根柱子"式的联动明细视图;
  • 仪表盘主区 + 过滤器/侧栏——侧栏与主网格之间的空间再平衡;
  • 同一查询的表格 + 图表并排——表格与可视化各自需要多大空间因人而异;
  • 文档/解释面板 + 实时数据视图——说明文字与数据视图之间需要权衡。

原文档的开篇即强调:不要默认使用它。如果面板的信息比例是固定的(例如 KPI 行位于网格上方、页头位于内容上方),请直接使用 Tailwind 的 flex/grid。可调面板是为"用户对『多看点图表还是多看点明细』有真实偏好"的布局准备的。当用户明确提出"拖拽调整大小""让我自己调整面板尺寸"时,就是触发使用它的时机;否则优先采用固定比例。

模板环境中的 Resizable:从安装到预装

在动手写代码之前,先确认模板环境里 Resizable 是如何落地的:

  • 模板 package.json 在依赖中声明了react-resizable-panels: 4.10.0(shadcn Resizable 的底层实现库),此外还预装了recharts: 3.8.1react: 19.2.5tailwindcss: 3.4.17等配套依赖;
  • bootstrap.sh 通过npx shadcn@2.3.0 add --overwrite --yes ... resizable将 Resizable 组件注册进模板,生成src/components/ui/resizable组件文件;
  • 与 e2b.Dockerfile 中沙箱的构建步骤保持同步,确保生成环境与本地开发环境行为一致;
  • 模板 skill.md 明确列出了预装组件清单,其中Resizable展开为ResizablePanelGroupResizablePanelResizableHandle三个子组件,均可从@/components/ui/<name>导入。

也就是说:在 Lightdash Data App 模板中,你不需要也不能执行npm install/pnpm add(沙箱会失败,且依赖已全部预装),直接导入即可使用。

基础用法:三组件结构与完整示例

shadcnResizable的使用模式固定为三层嵌套:

  • ResizablePanelGroup——容器,通过direction声明主轴方向(horizontalvertical);
  • ResizablePanel——可调整大小的面板,配置defaultSizeminSize等;
  • ResizableHandle——面板之间的拖拽手柄,配合withHandle显示可视化抓手。

以下是一个"横向拆分仪表盘"的完整可运行示例(继承自 原文档):

import { ResizablePanelGroup, ResizablePanel, ResizableHandle, } from '@/components/ui/resizable'; export function SplitDashboard() { return ( <ResizablePanelGroup direction="horizontal" className="h-[calc(100vh-3rem)] rounded-md border" autoSaveId="dashboard-split" // remembers user sizing in localStorage > <ResizablePanel defaultSize={65} minSize={35}> <RevenueByMonth /> </ResizablePanel> <ResizableHandle withHandle /> <ResizablePanel defaultSize={35} minSize={20}> <SegmentBreakdown /> </ResizablePanel> </ResizablePanelGroup> ); }

要点拆解:

  • direction="horizontal"让两个面板左右排列,拖拽手柄位于中间;
  • autoSaveId="dashboard-split"持久化用户拖拽结果的关键(详见下文规则 1);
  • defaultSize为百分比形式的初始宽度(65/35),minSize防止面板被压缩到不可用;
  • className直接叠加 Tailwind 工具类,容器高度用calc(100vh-3rem)留出页头空间。

六条硬性规则:从防坑到最佳实践

原文档给出了六条必须遵守的规则,逐条展开如下。

1. 必须设置autoSaveId:让用户的调整被记住

autoSaveId的值会作为 localStorage 的键使用,用于在刷新/重新进入后恢复用户上次拖拽的面板尺寸。规则要点:

  • 每个布局使用稳定且唯一的 id——一旦上线发布,id 变更会导致用户历史尺寸丢失;
  • 同一页面存在多个可调区域时,id 不能重复;
  • 它的价值在于把"用户有真实偏好"这一前提落到实处:用户调整过一次,之后每次打开都是他熟悉的比例。

2. 每个面板都必须设置minSize

minSize以百分比表示面板的最小尺寸。不设置它的后果很直接:用户可以一路把面板拖到 0,图表内容被完全折叠、再也看不见。建议至少给出 20%~35% 的保底值,具体取决于面板承载内容的密度。

3. 手柄务必加withHandle

ResizableHandle默认是一根 1 像素宽的悬停目标,几乎不可见。加上withHandle后会渲染一个可见的拖拽抓手(带圆角小把手),提供明确的视觉拖拽暗示,避免用户根本不知道这里可以拖动。

4. 网格布局通过嵌套分组实现

两个面板是最简形态,更复杂的布局由嵌套组合而成:

  • "过滤器 | 图表 | 明细"三栏 → 一个direction="horizontal"的分组;
  • "上图下表"(图表在上、表格在下)→direction="vertical"
  • 二者结合:外层横向分三栏,其中一栏内部再嵌一个纵向分组(比如图表 + 明细表纵向堆叠),即可形成完整的可调网格。

5. 不要在Card内部使用

Resizable 需要一个高度确定的父容器(如h-screenh-[600px])。如果把它放进内容撑开高度的Card,面板会在拖拽时塌陷。正确做法是:可调容器自身拥有明确高度,Card作为面板内部的内容承载物出现,而不是反过来。

6. 面板内的图表必须支持响应式重排

硬编码像素宽度的图表在面板尺寸变化时不会自动回流。因此:

  • 使用 Recharts 时,必须用<ResponsiveContainer>包裹图表;
  • 使用其他基于 SVG 的图表时,依赖viewBox进行等比缩放;
  • 这条规则与模板中 Recharts 3 的用法要求一致——图表组件应当以容器为基准自适应,而不是以固定像素为基准。

用嵌套 Resizable 复刻仪表盘网格

当用户的 prompt 附带仪表盘蓝图时,可调面板是重建"过滤器 + 图表区 + 明细"结构的好帮手。仪表盘蓝图文档 描述了 Lightdash 仪表盘的 36 列网格几何(x/w为列单位、y/h约为 55px 的行单位),并强调应按比例翻译到你的布局(例如 12 列 CSS grid 中除以 3),保留相对排布——哪些并排、哪些全宽、自上而下的顺序。这正与 Resizable 的嵌套分组模型吻合:

<ResizablePanelGroup direction="horizontal" autoSaveId="filters-main-detail" className="h-[calc(100vh-3rem)]"> <ResizablePanel defaultSize={20} minSize={15}> {/* 过滤器侧栏 */} </ResizablePanel> <ResizableHandle withHandle /> <ResizablePanel defaultSize={80} minSize={50}> <ResizablePanelGroup direction="vertical" autoSaveId="main-stack" className="h-full"> <ResizablePanel defaultSize={60} minSize={30}> {/* 主图表区 */} </ResizablePanel> <ResizableHandle withHandle /> <ResizablePanel defaultSize={40} minSize={20}> {/* 明细表 */} </ResizablePanel> </ResizablePanelGroup> </ResizablePanel> </ResizablePanelGroup>

注意内层纵向分组需要h-full(或明确高度),因为外层面板已经为其划定了确定的高度边界——这正是规则 5 的延伸:每个 Resizable 层级都要有确定的高度来源

与模板其他约定协同的三个细节

可调面板不是孤立的组件,它在 Lightdash Data App 模板中有几个必须协同的约束:

1. 截图边界(scheduled-delivery capture)。模板要求以data-screenshot-bounds标记内容范围,且内容区域避免 viewport 相对高度。但 模板 skill.md 明确给出了例外:"genuinely needs a fixed height(如可调面板)的嵌套组件仍可使用固定高度"——Resizable 正是这个例外的典型代表。换句话说:整页外壳用min-h-screen,可调面板自身使用明确高度(如h-[600px]h-[calc(100vh-3rem)]),二者互不冲突。

2. 加载与错误状态。模板要求每个使用useLightdash()的组件在loading时显示 spinner、error时显示错误提示(skill.md)。面板内的图表/表格组件同样遵循此规则——spinner 容器高度应与面板内容高度一致,避免数据到达时布局跳动。可调面板只负责空间分配,不负责内容的状态管理。

3. 与全局过滤器联动。当"过滤器侧栏 + 主网格"采用可调布局时,侧栏中的过滤控件仍通过useGlobalFilters()驱动所有查询(filtersFor(EXPLORE)),面板拆分不改变数据流,只改变视觉比例。这保证无论用户如何拖拽面板,图表间的过滤联动始终一致。

快速决策清单

场景方案
用户要求"拖拽调整面板大小"使用ResizablePanelGroup+ResizablePanel+ResizableHandle
图表 + 检查器 / 过滤器 + 网格 / 表格 + 图表可调面板,方向按内容主轴选择
KPI 行 + 网格、页头 + 内容等固定比例普通 Tailwind flex/grid,不用 Resizable
需要多栏 + 上下堆叠嵌套ResizablePanelGroup(横 + 纵)
所有可调布局必须设置autoSaveId、每个面板minSize、手柄withHandle

从模板的 bootstrap.sh 预装 shadcnresizable,到 package.json 声明react-resizable-panels@4.10.0,Lightdash Data App 模板已经为可调分栏布局备齐了全部基础设施。你只需遵守"有明确用户偏好才用、确定高度、设最小尺寸、图表响应式"这几个核心原则,就能为用户交付一个可拖拽、记忆偏好、布局灵活的交互式数据应用。

【免费下载链接】lightdashAgentic BI. Analytics at the speed of code ⚡️项目地址: https://gitcode.com/GitHub_Trending/li/lightdash

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询