@jeyabbalas /数据表
一个客户端TypeScript库,用于交互式、可探索的数据表。 建立在 DuckDB WASM ——所有 分析完全在浏览器中运行,所以没有数据离开用户 机器。
尝试现场演示:打开CSV文件, JSON或Parquet文件,或Excel工作簿,演示程序将其转换,以及 探索它。参见 例子 和 键盘快捷键.
- 逐列可视化(直方图、值计数、日期/时间直方图) 带笔刷/点击交叉滤镜;嵌套列获得非空/空摘要 Bar,它不过滤
- 手动过滤UI每列+ raw-SQL
WHERE过滤器 - 固定/隐藏/重新排序/调整列的大小;虚拟滚动
- 派生列(SQL表达式或js提供的值向量) 同名依赖关系的替换
- 稳定的合成
__rowid__+精确只读值读取 (actions.getColumnValues,actions.getCellValue)为应用程序端行 对齐和图表馈送 - 嵌套列(列表、数组、结构体、映射、联合、变体):DuckDB的
单元格中的文本,限制为32项;按值排序;CSV格式的标准JSON
在剪贴板上。对于它们和JSON列:精确过滤
比较文本(过滤器面板的,或)
valueType: 'text'在代码中),a 将整个值显示为键盘树的值检查器(F2或者 双击)并将其复制为JSON,以及一个提取按钮,该按钮可以将任何 字段放入自己的列中(actions.addNestedFieldColumn在代码) - 可编程的行/列/单元格注释-严重性层,弹出窗口,
JSON往返,会话持久化,一百万次测试;
F8从1开始 到下一个,过滤器只显示带注释的行 - 编程的列头工具提示- xss安全的结构化弹出窗口 (标题/描述/项目+枚举芯片)
- 自定义列统计面板-替换内置的两行统计信息
在列标题中显示您自己的DOM和DuckDB查询
BaseStatsPanel+每个StatsPanelRegistry - 公共CodeMirror SQL编辑器原语-嵌入模式-和
DuckDB-aware SQL编辑器在您自己的UI shell中的任何地方
createSqlExtensions/buildCompletionContext(filter-preset 编辑器、派生列向导、查询模板编辑器);生活或 文字模式,可选的库主题 - 过滤器预设(导入/导出JSON)
- 撤销/重做和IndexedDB会话持久性
- CSV / JSON /拼花导出
- 通过CSS自定义属性自动亮/暗模式
安装
npm install @jeyabbalas/data-table \
@duckdb/duckdb-wasm \
@codemirror/autocomplete @codemirror/commands @codemirror/lang-sql \
@codemirror/language @codemirror/state @codemirror/view @lezer/highlight
@duckdb/duckdb-wasm 是必需的对等依赖项。这个 @codemirror/*
和 @lezer/* 包是可选的对等体——只有在使用时才安装它们
默认的SQL表达式编辑器(用于派生列和原始SQL过滤器)。
如果你提供你自己的 editorFactory 或者禁用这些功能,你可以
忽略它们。
为了严格的内容安全策略,自己提供DuckDB-WASM的文件
或者离线部署,安装 @duckdb/duckdb-wasm 在完全
1.33.1-dev57.0 (npm install --save-exact):看
CSP和离线部署.
快速启动
给容器一个有界的高度——表根据它进行虚拟化,并且 如果没有一个,虚拟化就会被悄无声息地打败。看到 调整容器大小.
<div id="my-table" style="height: 600px"></div>
import { createDataTable } from '@jeyabbalas/data-table';
import '@jeyabbalas/data-table/styles';
const table = await createDataTable({
container: document.getElementById('my-table')!,
source: myCsvFileOrUrl, // File | string URL | ArrayBuffer | Blob
});
table.on('filterChange', ({ filters, filteredRowCount }) => {
console.log(`${filters.length} filters, ${filteredRowCount} rows match`);
});
// When unmounting (e.g., route change in an SPA):
await table.destroy();
对于文件选择器、URL输入或“交换数据集”流,只需挂载一次即可加载
关于用户操作——没有 destroy()/重现需要的舞蹈:
const table = await createDataTable({
container: document.getElementById('my-table')!,
});
document.getElementById('file-picker')!.addEventListener('change', async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) await table.loadData(file);
});
loadData(source) 接受一个 File,绝对URL (https://…,
http://…, file:, data:, blob:),与协议相关的URL
(//host/…),根相对路径(/data.csv),一个点前缀的亲戚
路径(./data.csv, ../data.csv),一个 ArrayBuffer,一个 Blob,或内联
CSV/JSON内容(多行文本,或以 [ / {)。
相对url解析 window.location.href,所以。 <base href> 在
该页不适用于它们。二义字符串(单行) sample.csv 没有
例如,前导斜杠)抛出 LoadError 使用代码 SOURCE_AMBIGUOUS
而不是将文本解析为CSV内容。
在安装前探测所需的浏览器api:
import { checkBrowserSupport } from '@jeyabbalas/data-table';
const support = checkBrowserSupport();
if (!support.supported) {
renderUnsupportedScreen(support.missing); // ['Worker', 'WebAssembly', …]
return;
}
// safe to call createDataTable(...) here
图书馆是 ESM-only 因为v0.4.0。使用 import 语法(每个现代的)
bundle - Vite, webpack 5, Rollup, esbuild, Bun -解析ESM构建
默认情况下)。它仅限浏览器,在SSR期间评估是不安全的
框架的集成 下面是客户端
安装模式。
调整容器大小
挂载容器必须具有限定高度。这是一个要求,不是 风格偏好-这是什么使表快。
表是虚拟化的:它只呈现适合视图的行,大约
⌈containerHeight / rowHeight⌉ + 10 它们。它知道是多少
测量容器。因此,集装箱的高度就是旋钮
限制每个滚动,过滤和排序需要做多少工作- 600像素
无论数据集有1000行还是10行,容器都会呈现~29行
百万。
用以下两种方式给它一个高度:
<!-- 1. An explicit height — simplest, and always correct -->
<div id="my-table" style="height: 600px"></div>
/* 2. A flex or grid child that fills the space left over */
html,
body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
}
#my-table {
flex: 1;
min-height: 0; /* required — see below */
}
min-height: 0 在伸缩情况下不是可选的。Flex项默认为
min-height: auto,它拒绝缩小到内容的内在
高度——这个表的内容是数据集中的每一行。省略它
在下面的容器中,您将看到无限制生长失败
尺寸是正确的。这同样适用于网格子节点。
height: 100% 在容器上单独做什么,除非 每一个 祖先
视口也有一个已解决的高度。这是通常的原因
“有高度”的容器仍然表现得好像没有高度一样。
没有它会出什么问题
没有什么错误。表呈现,在一个小数据集上它看起来很好 这就是为什么这很容易发布,但之后会很痛苦。
如果高度没有限定,则库的根元素(height: 100%)解决
设置为内容大小。然后是滚动区域
生长到其顶高- min(rowCount × rowHeight, 15,000,000 px)
所以滚轮测量的“可见区域”是这样的 整个范围.
虚拟化被悄无声息地打败了:表为表中的每一行构建DOM
range和问题块获取来填充所有这些。
在默认的32像素行高,帽对应于~468,750行,所以 这就是伤害饱和的地方,而不是随着数据集的变化而变化 安慰:成千上万的DOM行和大量的块读取 填满。标签页停滞不前,内存上升,滚动无法使用。与一个 有界容器相同的数据集呈现~29行并保持交互。
简并的情况正好相反:容器是 零高在 Mount根本不呈现任何行,并且库记录一个控制台警告 所以。容器,仅仅是 无限 没有警告,因为它有 完美的非零高度——只是错了。
安装后调整大小
在安装之前给集装箱一个高度,最好是一个高度
浏览器通过CSS (flex: 1, 100%, vh)除以你指定的1
在事实发生之后。坐垫:坐垫后变化的高度:
每当表的主体调整大小时,表就会计算出哪些行在视图中
窗口大小调整,面板在其上方折叠,或表安装在隐藏的
选项卡和后面显示的填充行没有滚动。
每一个 可运行的例子 使用flex模式,和
docs/integrations/cdn.md 有一个完整的
可复制粘贴的HTML启动器。
文档
完整文件位于 docs/。快速索引:
从这里开始
参考
指南
- 加载数据 · 过滤器 · 派生列 · 事件
- 注释 · 列标题工具提示 · 可视化 · 属性面板 · SQL编辑器原语
- 会话持久性 · 主题 · i18n · 可访问性 · 多表指示板
- CSP和离线 · 过滤器预设
概念
集成
来源
- 一级出口存在
src/index.ts;二级的src/advanced.ts.
框架的集成
图书馆是 浏览器只 -它使用Web Workers, window, document,
和IndexedDB,在SSR期间评估不安全。挂载
表在框架的客户端生命周期钩子中。
反应
import { useEffect, useRef } from 'react';
import { createDataTable, type DataTable } from '@jeyabbalas/data-table';
import '@jeyabbalas/data-table/styles';
export function Table({ source }: { source: File | string }) {
const hostRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!hostRef.current) return;
let cancelled = false;
let instance: DataTable | undefined;
void createDataTable({ container: hostRef.current, source }).then((t) => {
if (cancelled) {
void t.destroy();
return;
}
instance = t;
});
return () => {
cancelled = true;
if (instance && !instance.isDestroyed()) void instance.destroy();
};
}, [source]);
return <div ref={hostRef} style={{ height: 600 }} />;
}
这个 cancelled Flag处理效果之前重新运行的情况
createDataTable 解决了。 isDestroyed() 防止双重破坏
当React的严格模式在dev中双重调用效果时。
Vue 3
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { createDataTable, type DataTable } from '@jeyabbalas/data-table';
import '@jeyabbalas/data-table/styles';
const host = ref<HTMLElement | null>(null);
let table: DataTable | undefined;
onMounted(async () => {
if (host.value) table = await createDataTable({ container: host.value, source: props.source });
});
onBeforeUnmount(async () => {
if (table && !table.isDestroyed()) await table.destroy();
});
</script>
<template>
<div ref="host" style="height: 600px" />
</template>
后 destroy(),公共方法(loadData, on, off,
openExportDialog, clearSession, setColorScheme)把 DestroyedError.
检查 table.isDestroyed() 在调用长寿命闭包之前。
功能切换
所有功能都是默认开启的;通过 false 或者一个要自定义的配置对象:
| 选项 | 默认的 | 笔记 |
|---|---|---|
persistence | true | 自动保存过滤器/排序/列到IndexedDB |
presets | true | 显示“预设”按钮,用于保存过滤器设置 |
undoRedo | true | Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z和Ctrl+Y键盘快捷键 |
expressionFilter | true | 显示“表达式”(原始SQL)筛选按钮 |
derivedColumns | true | 显示“+”, f(x) 和嵌套的/JSON提取按钮,以及值检查器的“添加为列” |
visualizations | true | 自动附加列标题图(直方图,值计数,…) |
exportDialog | true | table.openExportDialog() 打开CSV/JSON/Parquet模式 |
annotationNavigation | true | F8 / Shift+F8 和 Alt+↓ / Alt+↑ 转到下一个/上一个注释 |
有关完整的选项界面(安装、worker、UI、自定义),请参见 文档/ api-reference.md # createdatatableoptions.
跳过CodeMirror
当两个 expressionFilter: false 和 derivedColumns: false 是固定的,
库在运行时不再达到codemmirror绑定的模态。的消费者
此配置可以省略 @codemirror/* 和 @lezer/highlight 同行
依赖项(已标记) optional 在 peerDependenciesMeta) -现代
打包器将模态块拆分为单独的运行时模块
无法到达的代码路径永远不会加载。编程API
(actions.addFilter({ type: 'raw-sql' }), actions.addDerivedColumn,
FilterPresetManager)一直在这种模式下工作。当旗子升起的时候
第一次点击SQL或派生列按钮现在获取一个小模态块
按需加载,而不是急切地将其加载到桌面安装上。
事件
订阅与 table.on(event, handler) -返回退订函数。
事件总线包括 ready, loadStart / loadProgress / loadComplete /
loadError, filterChange, sortChange, selectionChange, columnChange,
derivedChange ( kind: 'added' | 'removed' | 'replaced' | 'updated'
鉴别者和受影响者 columnName), undoChange, destroy,再加上
error 和 warning 对于可恢复的故障模式。注释突变
流经一个独立的 table.annotations.on('change', …) 频道-见
这个 注释指南。有效载荷类型
看到 文档/ api-reference.md #事件目录.
主题
所有颜色、间距、排版和z索引都由CSS自定义驱动
属性(74 --dt-* 令牌-覆盖整个调色板,大小比例,
z索引堆叠阶梯、注释严重性色调和列标头
工具提示分层)。覆盖你关心的那些 :root,在…上
每个实例元素,或在运行时。
:root {
--dt-primary: #10b981;
--dt-radius: 4px;
--dt-z-modal: 1500; /* raise above your app's modal layer */
--dt-panel-width: 420px; /* widen filter / preset / derived-edit panels */
}
接着是明暗模式 prefers-color-scheme 默认情况下。通过
colorScheme: 'light' | 'dark' | 'auto' 强加一个主题或号召
table.setColorScheme(...) 在运行时;身体传送的情态保持同步。
看到 文档/指导/ theming.md 对于完整的 变量参考与亮/暗默认值并排,暗模式 作用域模型、堆栈阶梯深度挖掘和每个实例覆盖模式。
CSS隔离
所有选择器都带有 dt- 前缀。列拖动光标,CodeMirror
自动完成和模态堆叠都是有作用域的,所以它们不会碰撞
主持人的风格。为实现更严格的隔离(一页上有两个库副本),
严品牌墙),过关 classPrefix: 'myapp-dt' 每个选择器,
模式,并且工具提示以该前缀重新呈现。
阴影DOM故意不使用-模态传送到光DOM,所以他们
可以继承 --dt-* 变量 :root。将图书馆包裹在阴影中
如果你需要的话,把你自己根起来,然后转发主题变量+
portalTarget 相应的行动。
错误处理
每个错误都会扩展 DataTableError (延伸 Error). 订阅
这个 error 事件到由子系统路由,或到 warning 对非致命
退化等 STYLESHEET_MISSING / PERSISTENCE_UNAVAILABLE:
table.on('error', ({ error, source }) => {
if (error.code === 'PARSE_FAILED') toast('Could not read that file.');
else if (source === 'persistence') console.warn(error);
else reportToSentry(error);
});
有关带有触发器和修复程序的错误代码的完整列表,请参见 文档/ troubleshooting.md.
多表、CSP和脱机
- 多个表: 共享一个
WorkerBridge(可选)SessionStore/FilterPresetManager)以避免旋转两个DuckDB 实例。给每个表一个不同的名称tableName会话快照 不要互相攻击。 - CSP /气隙式: 从您的原始文件中复制DuckDB-WASM文件
从
@duckdb/duckdb-wasm精确地固定在一起1.33.1-dev57.0,并通过 他们是bridgeOptions.duckdbBundles(相对url解析 页面)。镜子DuckDB的icu,parquet和json扩展和集合custom_extension_repository,并发送一个策略,与'wasm-unsafe-eval'和worker-src 'self' blob:,每一个回应。
看到 代理。md§3 (h) 用于共享桥接 片段, CSP和离线部署 用于 自托管配方,和 文档/故障排除。md§4 当DuckDB未启动时。
可访问性
网格目标为WCAG 2.1 AA: role="grid" 与 aria-rowcount /
aria-colcount,通过发布的单个游标 aria-activedescendant 和
由方向键(+)驱动 F2 要访问列的控件,
Shift+F2 调整大小和重新排序,和 F8 去下一个
注释),一个不随
列数,礼貌 aria-live 关于过滤/排序/行计数的通知
更改,焦点陷阱+ escape-to-close的每个模式,和 axe-core 作为CI
门。已知范围外:单元内编辑,移动触摸手势,RTL。
自定义可视化
子类 BaseVisualization (从 /advanced),并在网上登记
每个 VisualizationRegistry:
import { createDataTable, VisualizationRegistry } from '@jeyabbalas/data-table';
import { BaseVisualization } from '@jeyabbalas/data-table/advanced';
class MyViz extends BaseVisualization {
/* a constructor that starts the first fetch, fetchData(), render() and six input handlers */
}
const registry = new VisualizationRegistry();
registry.register({
name: 'my-viz',
isApplicable: (t) => t === 'float',
constructor: MyViz,
priority: 10,
});
const table = await createDataTable({ container, source, visualizationRegistry: registry });
可运行版本 例子/ 08-custom-visualization.
国际化
每个面向用户的字符串都来自类型 Strings 对象。通过一项
DeepPartial<Strings> 通过 messages;丢失的钥匙回到英语
默认值。消息在构造时一次解析—重新创建表
切换语言环境。
await createDataTable({
container,
source,
messages: {
common: { close: 'Fermer', apply: 'Appliquer' },
filters: { panelTitle: 'Filtres' },
},
});
可运行版本 例子/ 07-i18n-french.
浏览器支持
| API | 用于 |
|---|---|
Worker | DuckDB运行在一个专用的worker中 |
WebAssembly | DuckDB被编译成Wasm |
IndexedDB | 会话持久性(跳过) persistence: false) |
ResizeObserver | 列大小调整,响应式可视化 |
BigInt | DuckDB整型列作为BigInt跨越worker边界 |
structuredClone | 桥接快照结果集 |
大概是Chrome/Edge 98+, Firefox 94+, Safari 15.4+。在运行时探测
checkBrowserSupport() 或者选择通过自动快速初始化
strictBrowserCheck: true. 看到
文档/ api-reference.md # browser-support-probe.
高级:模块化API
根条目(@jeyabbalas/data-table)暴露了立面加上一个小的
一套稳定的挂钩。想要直接编排堆栈的高级用户
自定义可视化生命周期,无头使用,驱动桥
自己——可以从 /advanced:
import {
TableContainer,
FilterBar,
ExportDialog,
BaseVisualization /* … */,
} from '@jeyabbalas/data-table/advanced';
这个 /advanced 表面以稳定性换取灵活性:的确如此 不
由与根条目相同的semver保证覆盖。看到
文档/ api-reference.md # tier-2-exports
查看完整的符号列表。
开发
看到 DEVELOPMENT.md 对于本地设置、测试、构建管道和发布工作流。看到 CONTRIBUTING.md 了解如何报告错误和提交更改。
许可证
麻省理工学院