npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@scvzerng/ag-grid-core

v0.1.1

Published

Core business logic for ag-grid based table components (model / state / data source). Framework-agnostic via composition API subset.

Readme

@scvzerng/ag-grid-core

ag-grid 业务封装核心包 — 合并单元格 + 树展开 + 编辑流 + 选中同步

monorepo 根: element-plus-ag-grid/ 公共 API 100% 兼容,跨 vue 2.7+ / vue 3 通用

简介

@scvzerng/ag-grid-core 封装 ag-grid 社区版的"合并单元格"业务场景:

  • SpanMatrix: 纯数据模型, 不知道 ag-grid / vue 存在
    • data + columns → span tree, 支持任意嵌套层级
    • 增量更新 API (setRow / setArrayLength)
    • O(1) span 查询 (getCellSpan)
  • 行模型: ActualRow / EditableRow / CellEditableRow / EditSession
    • 透出 ag-grid IRowNode 兼容的 GridRow interface
    • 编辑流 (row mode / cell mode)
  • Vue hook: useGrid (vue 2.7+ / vue 3 通用)
    • 列状态持久化 (useGridState)
    • 事件代理 (buildProxyOptions)
  • 类型守卫: isGridRow / isActualRow

业务方能独立用 core: 只想算 span / 数据查询, 不需要 Grid 组件, 也能直接用 SpanMatrix / RowDataModel.

安装

# monorepo 内
pnpm add @scvzerng/ag-grid-core --filter <your-package>

# 外部使用
pnpm add @scvzerng/ag-grid-core ag-grid-community@^34

公共 API

1. 纯数据模型 (无 ag-grid / vue 依赖)

| 导出 | 用途 | |---|---| | SpanMatrix | 树展开 + span 计算 (核心) | | SpanNode (type) | span 树节点 | | SpanColumn (type) | 列定义 ({ colId, cellDataType? }) | | SetRowContext (type) | setRow 上下文 ({ columns }) | | CellValue | 单 cell 值 (含 span 信息) | | RowSpans | CellValue span cache 代理 | | RowDataModel | 单行数据查询 (Phase 2 抽离) | | ProjectionRow | 投影行 (合并行分裂的子行) | | GridRow (type) | 行模型 interface | | isGridRow / isActualRow | 类型守卫 |

2. 行模型 (ag-grid runtime 抽象)

| 导出 | 用途 | |---|---| | ActualRow | 真实行 (thin facade, 数据查询委派 RowDataModel) | | EditableRow | 整行编辑 (row mode) | | CellEditableRow | 单 cell 编辑 (cell mode) | | EditSession (abstract) | Phase 5 抽离的公共基类 (业务方一般不直接用) | | GridDataCache | 行容器 (支持 replace / insertRows / deleteRows) |

3. Vue hook (vue 2.7+ / vue 3 通用)

| 导出 | 用途 | |---|---| | useGrid | 业务主 hook, 返回 gridOptions / dataCache / gridApi / gridElRef / reload / startEditing / stopEditing / insertRows / existRow / getRows / refresh | | useGridState | 列状态持久化 (列宽 / 排序 / 列顺序), 持久化通过回调注入 | | useGridScroll | 滚动到底加载更多 | | useFooter | 表格底部 footer | | ServerGetRowsParamsRequest | 分页请求参数构造 | | RequestErrorCallback (type) | 错误回调类型 | | UseGridProps (type) | useGrid props 类型 | | UseFooterProps (type) | useFooter props 类型 |

4. 事件代理 (UI 库回调注入)

| 导出 | 用途 | |---|---| | buildProxyOptions | ag-grid 事件代理 (hover 同步 / cell 点击切编辑 / row 选中同步 / 合并行 row class 注入) | | ProxyCtx (type) | buildProxyOptions 参数 | | InstanceTraversalCtx (type) | 实例遍历上下文 | | RefLike (type) | vue Ref 类型兼容抽象 ({ value: T }) |

5. 列 class rules 辅助

| 导出 | 用途 | |---|---| | selectionColumnCellClassRules | 选中列的 cell class rules | | defaultColDefCellClassRules | 默认列的 cell class rules | | mergeRowClass | 合并多 row class 规则 |

6. 业务模块 (可选)

| 导出 | 用途 | |---|---| | RowSelectionModule / SharedRowSelectionModule | ag-grid 行选中模块 | | ClientSideRowModelModule / ClientSideRowModelApiModule | ag-grid 客户端行模型模块 |

7. 数据源 / 工具

| 导出 | 用途 | |---|---| | GridDataSource | 数据源 (支持静态 / 分页异步) | | DataGetter (type) | 数据获取函数 | | PageFetchParams / FetchParams (type) | 分页参数 | | SortModel (type) | 排序模型 | | GridUtils | 工具函数集合 | | ID_SPLITTER / ID_FIRST_DEEP (常量) | ID 切分常量 | | _getSortedColumns 等 | 列排序工具 |

用法示例

1. 独立使用 SpanMatrix (不依赖 vue / ag-grid)

典型场景: 后端 / 业务侧只想算 span 树, 不需要渲染。

import { SpanMatrix } from '@scvzerng/ag-grid-core'

// 1. 准备数据 (任意嵌套层级)
const data = {
  id: '0',
  name: '高 0 班',
  teachers: [
    { name: '张老师', students: [{ name: '学生甲' }, { name: '学生乙' }] },
    { name: '李老师', students: [{ name: '学生丙' }] },
  ],
}

// 2. 定义列 (colId 用 . 分隔 path, array path 自动展开)
const columns = [
  { colId: 'name' },                    // 班级名
  { colId: 'teachers.name' },           // 任课老师名 (展开)
  { colId: 'teachers.students.name' },  // 任课老师的学生 (嵌套展开)
]

// 3. 创建 SpanMatrix
const matrix = new SpanMatrix({ 0: data }, columns)

// 4. 查询每个 cell 的 span
const rootLeaves = matrix.getLeaves('0')
// returns SpanNode[] - 树状结构, 每节点含 id / value / spanRow / path / nestingIndexes

// 5. 查某 cell 的 span (rowSpan 值)
const span = matrix.getCellSpan('0', 0, 'teachers.name')  // 0: 第一行, col: teachers.name
// returns 1 (1 行) 或 2 (合并 2 行)

// 6. 增量更新: 数组长度变化
matrix.setArrayLength('0', 'teachers', 5)  // teachers 数组变成 5 个
// 内部只重算 parent 子树, 不重建整行

2. 独立使用 RowDataModel (查询 + 增量更新)

典型场景: 想从行 + 列拿到 cell value, 不想直接碰 ag-grid。

import { RowDataModel } from '@scvzerng/ag-grid-core'

// RowDataModel 是行级别的纯数据模型
// 业务方一般通过 ActualRow 间接用, 但也能直接 new
const data = {
  id: '0',
  name: '高 0 班',
  teachers: [{ name: '张老师' }],
}
const columns = [
  { colId: 'name' },
  { colId: 'teachers.name' },
]

const model = new RowDataModel('0', data, columns)

// 查 cell value (走 SpanMatrix.getCell)
const cell = model.getCell({ getColId: () => 'name' } as any, [])
// cell.value === '高 0 班'

// 增量更新
model.setArrayLength('teachers', 3)  // 增 2 个 teacher

// 整行重建 (当 data 整体改时)
model.rebuild({ ...data, name: '改名' })

3. 类型守卫 (替代 instanceof)

import { isGridRow, isActualRow } from '@scvzerng/ag-grid-core'

function handleRow(row: any) {
  if (isGridRow(row)) {
    // row 满足 GridRow interface
    row.getId()  // OK
    row.getData()  // OK
  }

  if (isActualRow(row)) {
    // row 进一步缩窄到 ActualRow
    row.startEditing()  // OK
    row.delete()  // OK
  }
}

4. 完整 Grid 集成 (走 useGrid + vue adapter)

<script setup lang="ts">
import { useGrid } from '@scvzerng/ag-grid-core'
import { ElMessage } from 'element-plus'  // 或 element-ui

interface Class {
  id: string
  name: string
  teachers: { name: string }[]
}

const props = defineProps<{
  data: Class[]
}>()

const {
  gridOptions,    // 给 ag-grid 用
  dataCache,      // 行容器
  gridApi,        // ag-grid api
  gridElRef,      // 容器 ref
  reload,         // 重新加载
  startEditing,   // 进入行编辑
  stopEditing,    // 退出行编辑
  insertRows,     // 插入新行
  getRows,        // 拿所有行
} = useGrid<Class>({
  data: props.data,
  rowIdMapper: (row) => row.id,  // 必填, 唯一 id
  onError: (msg) => ElMessage.error(msg),  // 错误消息回调
  onMessageError: (msg) => ElMessage.error(msg),  // use-proxy 内部错误
})
</script>

<template>
  <ag-grid-vue
    :gridOptions="gridOptions"
    :modules="modules"
    style="width: 100%; height: 600px"
  />
</template>

5. 事件代理 (buildProxyOptions)

import { buildProxyOptions } from '@scvzerng/ag-grid-core'

const proxyOptions = buildProxyOptions({
  options: userGridOptions,
  elRef: gridElRef,
  editable: 'cell',  // 'cell' / 'row' / undefined
  onRowChange: async (row) => {
    // cell 模式下保存草稿
    await saveToServer(row)
  },
  onMessageError: (msg) => ElMessage.error(msg),
})

buildProxyOptions 做的事:

  • onCellMouseOver / onCellMouseOut: 合并行同步 hover (cell-level + event.relatedTarget 判断同 rootId 兄弟)
  • onCellClicked: cell 模式 → 切 cell 编辑
  • onRowSelected: 选中一行同步勾选它的所有合并行 (用 getRootId === rootId 严格匹配)
  • getRowClass: 注入 row_${rootId} (合并行查询需要)
  • onRowDoubleClicked: row 模式 → 进整行编辑

跟 vue2 / vue3 adapter 的关系

element-plus-ag-grid       (vue 3 + element-plus)
  └─ Grid.vue (vue3 wrapper)
      └─ useGrid() (core)         ← 你在这里
      └─ buildProxyOptions()      ← 你在这里
      └─ ElMessage.error          ← 注入 UI 回调

element-ui-ag-grid-vue2     (vue 2.7+ + element-ui)
  └─ Grid.vue (vue2 wrapper)
      └─ useGrid() (core)         ← 同一个 useGrid
      └─ buildProxyOptions()      ← 同一个 buildProxyOptions
      └─ Message.error (element-ui) ← 注入 UI 回调

core 不依赖 UI 库: 错误消息 / 弹框 / 通知都通过回调 (onError / onMessageError / InstanceTraversalCtx) 注入, vue2 / vue3 adapter 各自接 element-ui / element-plus.

高级用法

SpanMatrix 增量更新

setArrayLength 是 O(被影响叶子数), 跟 setRow 全量重建对比:

// 假设一行有 1000 个嵌套元素
// 改一个 array 长度 (1 -> 5), 受影响叶子数 = 4 (新增 4 个)

// 老实现: 全量 rebuild, O(1000) cell span 重算
// 新实现: setArrayLength 增量, O(4) parent 子树重算
// 性能: 250x 改进
matrix.setArrayLength('0', 'students', 5)

详见 docs/SPAN_MATRIX_MIGRATION.md §7 性能对比。

自定义 useGridState 持久化

import { useGridState } from '@scvzerng/ag-grid-core'

const { columnState, onColumnStateChanged } = useGridState({
  // 业务方注入存储后端
  save: async (state) => {
    await localStorage.setItem('grid-state', JSON.stringify(state))
  },
  load: async () => {
    const json = await localStorage.getItem('grid-state')
    return json ? JSON.parse(json) : null
  },
})

类型定义扩展 (CellDataType)

import type { SpanColumn } from '@scvzerng/ag-grid-core'

const columns: SpanColumn[] = [
  { colId: 'name' },                          // 字符串列 (按 data 形状自动推断)
  { colId: 'amount', cellDataType: 'number' },
  { colId: 'createdAt', cellDataType: 'date' },
  { colId: 'meta', cellDataType: 'native' },  // native: 跨整组 (不展开)
]

文档

  • docs/REDESIGN.md (24KB) - 6-phase 蓝图, 整体设计理念
  • docs/SPAN_MATRIX_MIGRATION.md (26KB) - Phase 1 详细迁移日志
  • docs/PHASE2_ROW_DATA_MODEL.md (8KB) - Phase 2 RowDataModel 抽离
  • docs/PHASE3_HOVER_SELECT.md (7KB) - Phase 3 hover/select 同步
  • docs/PHASE5_EDIT_SESSION.md (12KB) - Phase 5 EditSession 抽离
  • docs/EXAMPLES.md (Phase 6) - 常见合并场景示例
  • docs/PERFORMANCE.md (Phase 6) - 性能对比报告

公共 API 兼容性

@scvzerng/ag-grid-core 公共 API 100% 兼容 (类名 + 方法签名):

  • SpanMatrix.setRow / setArrayLength / getCellSpan / getCellPath / getMaxArrayLength / getLeaves / getDirtyRoots / clearDirty
  • ActualRow.startEditing / startEditingCell / redraw / redrawCell / delete / getId / getData / isEditing / isCellEditing / flat
  • EditableRow.persistent / isTransient
  • GridDataCache.getRows / setRows / appendRows / insertRows / remove / deleteRows / createRow / replace
  • useGrid / useGridState / useGridScroll / useFooter
  • buildProxyOptions

每次重构保持公共方法签名不变, 改的是内部实现。

测试

175 tests 全过 (从 monorepo 根跑 pnpm -r test):

56 SpanMatrix
16 RowDataModel
25 render-integration (Phase 1 7 个集成 bug 回归)
22 use-proxy (9 核心 + 13 边界)
56 EditSession (29 核心 + 27 边界)

License

MIT