npm package: @silly_yi/easytable
Description: Canvas-based virtualized table for huge datasets
Latest version: 0.2.1

# @silly_yi/easytable

一个基于 Canvas 的虚拟化表格，面向大数据量（十万/百万行）场景：滚动、选区、复制、编辑、排序、筛选、固定列、合并单元格、图片单元格等都在 Canvas 上完成渲染。

![NPM Version](https://img.shields.io/npm/v/@silly_yi/easytable)
![License](https://img.shields.io/npm/l/@silly_yi/easytable)

## 特性

- Canvas 渲染，DOM 节点极少，适合大数据量
- 虚拟滚动（按视口渲染行/列），滚动流畅
- 表头排序（支持自定义 sortValue/comparator）
- 表头筛选面板（列表/文本两种模式，支持超大列表虚拟化）
- Excel 风格选区与复制（Ctrl/Cmd+C）
- 单元格编辑（可配合数据源写回）
- 列宽拖拽、固定列（left）、合并单元格、图片单元格

## 安装

```bash
npm i @silly_yi/easytable
```


## 快速开始（数组数据）

```ts
import { EasyTable } from "@silly_yi/easytable";

const container = document.getElementById("table")!;

const rows = Array.from({ length: 10000 }).map((_, i) => ({
  id: i + 1,
  name: `User ${i + 1}`,
  email: `user${i + 1}@example.com`,
}));

const table = new EasyTable(container, {
  data: rows,
  rowHeight: 32,
  headerHeight: 40,
  enableEditing: true,
  enableSelection: true,
  enableClipboard: true,
  enableFiltering: true,
  enableSorting: true,
  enableColumnResize: true,
  columns: [
    { key: "id", title: "ID", width: 80, fixed: "left", sortable: true },
    { key: "name", title: "姓名", width: 160, sortable: true, editable: true },
    { key: "email", title: "邮箱", width: 260, editable: true, filterable: true },
  ],
  onCellChange: (e) => {
    console.log("cell change:", e.row, e.col, e.oldValue, "->", e.newValue);
  },
});
```

## 大数据推荐（getRow 懒数据源）

当数据量很大时，不建议一次性构建巨大数组；推荐使用 `rowCount + getRow` 方式按需生成行数据。

```ts
import { EasyTable } from "@silly_yi/easytable";

const ROWS = 300000;
const COLUMNS = 30;

const overrides = new Map<number, any>();

const data = {
  rowCount: ROWS,
  getRow: (i: number) => {
    const override = overrides.get(i);
    if (override) return override;
    const row: any = { id: i + 1 };
    for (let c = 1; c < COLUMNS; c++) row[`c${c}`] = `R${i + 1}-C${c}`;
    return row;
  },
  setCell: (i: number, key: string, value: unknown) => {
    const base = overrides.get(i) ?? data.getRow(i);
    overrides.set(i, { ...base, [key]: value });
  },
};

new EasyTable("#table", {
  data,
  rowHeight: 32,
  headerHeight: 40,
  enableEditing: true,
  columns: Array.from({ length: COLUMNS }).map((_, i) => ({
    key: i === 0 ? "id" : `c${i}`,
    title: i === 0 ? "ID" : `列${i}`,
    width: i === 0 ? 80 : 120,
    sortable: true,
    editable: true,
    fixed: i < 2 ? "left" : undefined,
  })),
});
```

## 排序说明

- 数组数据（`EasyTableRow[]` 或 `{ rows }`）会在组件内部排序。
- `getRow` 数据源优先走你提供的 `data.sort(state) => number[]`（例如走服务端排序）。
- 如果 `getRow` 未提供 `sort`，组件会在前端以异步分片方式构建排序索引并刷新表格（适合中等规模；超大规模建议使用服务端/预索引）。

## 筛选说明

- 点击表头漏斗图标打开筛选面板。
- 列表模式下，选项采用虚拟列表，只渲染固定数量 DOM。
- 在搜索框输入关键字后，会异步扫描并显示匹配项；同时会把匹配项全部选中、未匹配项取消选中；点击“确定”应用筛选。

## API 概览

### 构造与方法

- `new EasyTable(container, options)`
- `setData(data)`
- `updateOptions(partialOptions)`
- `resize()`
- `destroy()`
- `scrollToRow(rowIndex)`
- `scrollToCell(rowIndex, colIndex)`
- `downloadCSV(filename, { range: "selection" | "all" })`

### Options（常用）

- `data`：支持三种形态：`EasyTableRow[]` / `{ rows }` / `{ rowCount, getRow, setCell?, setRow?, sort? }`
- `columns`：列配置（`key/title/width/fixed/type/sortable/filterable/resizable/editable/...`）
- `rowHeight`、`headerHeight`、`font`、`headerFont`
- `enableSelection`、`enableClipboard`、`enableSorting`、`enableEditing`、`enableColumnResize`、`enableFiltering`
- `theme`、`cells`、`merges`、`cellStyle`
- 回调：`onCellClick/onCellChange/onColumnResize/onFilterChange/...`

更完整的类型定义在 [types.ts](file:///G:/开发项目/公司项目/科丁熊网络/easyTable/src/table/types.ts)。

## 开发与构建

```bash
npm run typecheck
npm run build
```

本仓库内置了一个 `index.html` 演示页，可直接用静态服务器打开：

```bash
python -m http.server 3000
```

然后访问：`http://localhost:3000/index.html`

## License

MIT