@taocompany/magic-grid 0.4.8 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +372 -78
  2. package/dist/components/MagicGrid/MagicGrid.vue.d.ts +9 -2
  3. package/dist/components/MagicGrid/MagicGrid.vue.d.ts.map +1 -1
  4. package/dist/components/MagicGrid/useGridApi.d.ts +6 -0
  5. package/dist/components/MagicGrid/useGridApi.d.ts.map +1 -0
  6. package/dist/components/MagicGrid/useGridLifecycle.d.ts +11 -0
  7. package/dist/components/MagicGrid/useGridLifecycle.d.ts.map +1 -0
  8. package/dist/core/api/gridApi.d.ts.map +1 -1
  9. package/dist/core/cell/cellCtrl.d.ts.map +1 -1
  10. package/dist/core/column/columnModel.d.ts.map +1 -1
  11. package/dist/core/editing/builtInEditors.d.ts +15 -7
  12. package/dist/core/editing/builtInEditors.d.ts.map +1 -1
  13. package/dist/core/editing/cellEditPipeline.d.ts +1 -2
  14. package/dist/core/editing/cellEditPipeline.d.ts.map +1 -1
  15. package/dist/core/editing/cellEditorHost.d.ts.map +1 -1
  16. package/dist/core/editing/checkboxEditorValue.d.ts +14 -0
  17. package/dist/core/editing/checkboxEditorValue.d.ts.map +1 -0
  18. package/dist/core/editing/index.d.ts +4 -2
  19. package/dist/core/editing/index.d.ts.map +1 -1
  20. package/dist/core/editing/inlineCellEditorHost.d.ts +1 -1
  21. package/dist/core/editing/inlineCellEditorHost.d.ts.map +1 -1
  22. package/dist/core/editing/inlineCellEditorService.d.ts.map +1 -1
  23. package/dist/core/editing/readCellEditorValue.d.ts +3 -1
  24. package/dist/core/editing/readCellEditorValue.d.ts.map +1 -1
  25. package/dist/core/editing/switchEditorValue.d.ts +6 -0
  26. package/dist/core/editing/switchEditorValue.d.ts.map +1 -0
  27. package/dist/core/editing/toggleEditorValue.d.ts +23 -0
  28. package/dist/core/editing/toggleEditorValue.d.ts.map +1 -0
  29. package/dist/core/grid/cellEditing/editorParams.d.ts.map +1 -1
  30. package/dist/core/grid/grid.d.ts +3 -1
  31. package/dist/core/grid/grid.d.ts.map +1 -1
  32. package/dist/core/grid/gridInteraction/cellRangeDragSession.d.ts +1 -1
  33. package/dist/core/grid/gridInteraction/cellRangeDragSession.d.ts.map +1 -1
  34. package/dist/core/grid/gridInteraction/focusController.d.ts +2 -2
  35. package/dist/core/grid/gridInteraction/focusController.d.ts.map +1 -1
  36. package/dist/core/types/cellEditor.d.ts +8 -2
  37. package/dist/core/types/cellEditor.d.ts.map +1 -1
  38. package/dist/core/types/checkboxEditor.d.ts +8 -0
  39. package/dist/core/types/checkboxEditor.d.ts.map +1 -0
  40. package/dist/core/types/column.d.ts +6 -0
  41. package/dist/core/types/column.d.ts.map +1 -1
  42. package/dist/core/types/events.d.ts +6 -1
  43. package/dist/core/types/events.d.ts.map +1 -1
  44. package/dist/core/types/gridLifecycle.d.ts +24 -0
  45. package/dist/core/types/gridLifecycle.d.ts.map +1 -0
  46. package/dist/core/types/index.d.ts +1 -1
  47. package/dist/core/types/index.d.ts.map +1 -1
  48. package/dist/core/types/switchEditor.d.ts +8 -0
  49. package/dist/core/types/switchEditor.d.ts.map +1 -0
  50. package/dist/index.cjs.js +5 -5
  51. package/dist/index.cjs.js.map +1 -1
  52. package/dist/index.d.ts +6 -1
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.es.js +3931 -3780
  55. package/dist/index.es.js.map +1 -1
  56. package/dist/style.css +1 -1
  57. package/dist/types/core.d.ts +60 -6
  58. package/dist/types/core.d.ts.map +1 -1
  59. package/dist/types/magic-grid-expose.d.ts +2 -3
  60. package/dist/types/magic-grid-expose.d.ts.map +1 -1
  61. package/dist/types/magic-grid.d.ts +9 -1
  62. package/dist/types/magic-grid.d.ts.map +1 -1
  63. package/package.json +1 -1
package/README.md CHANGED
@@ -21,6 +21,8 @@
21
21
  - [Expose](#expose)
22
22
  - [Slots](#slots)
23
23
  - [单元格编辑](#单元格编辑)
24
+ - [呈现模式:overlay 与 inline](#呈现模式overlay-与-inline)
25
+ - [内置编辑器](#内置编辑器)
24
26
  - [数据校验](#数据校验)
25
27
  - [键盘导航与快捷键](#键盘导航与快捷键)
26
28
  - [辅助组件](#辅助组件)
@@ -31,7 +33,6 @@
31
33
  - [包入口与导出](#包入口与导出)
32
34
  - [TypeScript 集成](#typescript-集成)
33
35
  - [类型与校验子路径](#类型与校验子路径)
34
- - [本地开发与 Playground](#本地开发与-playground)
35
36
 
36
37
  ---
37
38
 
@@ -43,11 +44,11 @@
43
44
  |------|------|
44
45
  | **性能** | 行/列双轴虚拟化、DOM 池复用、RAF 帧预算分片、Cell Renderer 滚动调度(idle flush · 按行合并 · placeholder-then-vue)、增量数据管线 |
45
46
  | **数据** | 排序、列筛选、Quick Filter、事务增量更新(`applyTransaction`)、行 CRUD |
46
- | **交互** | 单元格/行编辑、校验、行选择、索引列定位、框选与剪贴板 |
47
+ | **交互** | 单元格/行编辑(内置 text/number/checkbox/switch · overlay/inline)、校验、行选择、索引列定位、框选与剪贴板 |
47
48
  | **布局** | 固定列、列拖拽/调整宽度、列有效性与显隐(`available` / `hidden`)、合并单元格、动态行高、主从展开行 |
48
49
  | **结构** | 行分组、树形表格(含懒加载)、表尾汇总 |
49
50
  | **体验** | 溢出 Tooltip、单元格批注、空态 Overlay、暗色主题、底部状态栏 |
50
- | **扩展** | 列设置面板、Vue 第三方编辑器 Composable、inline 常驻编辑器、async-validator 校验 |
51
+ | **扩展** | 列设置面板、内置编辑器(text/number/checkbox/switch)、Vue 第三方编辑器 Composable、inline 常驻编辑器、async-validator 校验 |
51
52
 
52
53
  当前版本:**0.3.0**(`@taocompany/magic-grid`)。
53
54
 
@@ -192,7 +193,7 @@ const columns: ColumnDef[] = [
192
193
  - 避免在 `cellRenderer` / `formatter` / `valueGetter` 中创建重量级对象或触发外部副作用;这些函数在滚动时会高频调用。
193
194
  - 多列使用 `#cell-{colId}` Vue 插槽时,滚停后可能出现 renderer 列空白拖尾;见下方 [Cell Renderer 滚动渲染](#cell-renderer-滚动渲染) 与 [docs/41-cell-renderer-scroll-performance.md](docs/41-cell-renderer-scroll-performance.md)。
194
195
  - 合并单元格(`enableCellSpan`)会启用 `style.top` 行定位并增加 span cache 开销,仅在确有需求时开启。
195
- - Playground 内置 `getMetrics()` 可观察 `activeDomRowCount`、`lastSetDataMs` 等指标;`/phase41` 另提供 `getPendingFrameTaskCount()` / `getPendingRendererTaskCount()`;生产环境可通过 `scroll` / `data-rendered` 事件订阅。
196
+ - 生产环境可通过 `scroll` / `data-rendered` 事件订阅渲染完成时机;开发调试时可关注 DOM 行池规模与 `setData` 耗时。
196
197
 
197
198
  ### Cell Renderer 滚动渲染
198
199
 
@@ -225,7 +226,7 @@ const columns: ColumnDef[] = [
225
226
  />
226
227
  ```
227
228
 
228
- `placeholder-then-vue` 模式下,slot 列需配置可读 `prop` 或 `formatter`,滚动态才有文字兜底。Playground 验收:**`/phase41`**。完整方案见 [docs/41-cell-renderer-scroll-performance.md](docs/41-cell-renderer-scroll-performance.md)。
229
+ `placeholder-then-vue` 模式下,slot 列需配置可读 `prop` 或 `formatter`,滚动态才有文字兜底。完整方案见 [docs/41-cell-renderer-scroll-performance.md](docs/41-cell-renderer-scroll-performance.md)。
229
230
 
230
231
  ---
231
232
 
@@ -837,8 +838,6 @@ api.getColumns().some((c) => c.colId === 'internalCode') // false
837
838
 
838
839
  修改 `columns` 中某列的 `available` 或 `hidden` 后,Grid 会走 `setColumns` 重解析。`hidden` 在 `columnDefs` 未改 `hidden` 声明时,会保留运行时 API/菜单设置的值(与 Phase 22 列显隐语义一致)。
839
840
 
840
- Playground 验收:开发环境 `/phase22` · **T14–T15**(`legacyToken` 列可切换 `available`)。
841
-
842
841
  #### 尺寸
843
842
 
844
843
  | 字段 | 类型 | 说明 |
@@ -895,7 +894,9 @@ Playground 验收:开发环境 `/phase22` · **T14–T15**(`legacyToken` 列
895
894
  | `formatter` | `(row, value) => string` | 展示格式化 |
896
895
  | `cellRenderer` | `CellRendererFn` | 自定义单元格渲染器(函数;Vue 组件通过 `#cell-{colId}` 插槽) |
897
896
  | `cellRendererMount` | `'vue' \| 'sync'` | 列级 renderer 挂载类型;配合 `cellRendererDefer: 'sync-only'` 使用 |
898
- | `cellEditor` | `CellEditorDef` | 自定义单元格编辑器(函数、内置 `text`/`number`/`checkbox`、或命名引用) |
897
+ | `cellEditor` | `CellEditorDef` | 自定义单元格编辑器(函数、内置 `text`/`number`/`checkbox`/`switch`、或命名引用) |
898
+ | `checkboxEditorParams` | `CheckboxEditorParams` | 内置 checkbox:自定义选中/未选中写回值(默认 `true`/`false`) |
899
+ | `switchEditorParams` | `SwitchEditorParams` | 内置 switch:自定义开启/未开启写回值(默认 `true`/`false`) |
899
900
  | `cellEditorMode` | `'overlay' \| 'inline'` | 编辑器呈现方式;默认 `overlay` |
900
901
  | `valueParser` | `(value, params) => unknown` | 提交前解析编辑值 |
901
902
  | `valueSetter` | `(params) => boolean \| Promise<boolean>` | 自定义写回逻辑;返回 `false` 拒绝提交 |
@@ -1342,7 +1343,52 @@ api.clearAllFilters()
1342
1343
 
1343
1344
  ## Events
1344
1345
 
1345
- MagicGrid 通过 Vue 事件向外暴露 Grid 内核事件。事件名采用 **kebab-case**。除 Vue 事件外,也可通过 `gridRef.api.on(...)` 订阅 camelCase 内核事件(见 [Expose](#expose))。
1346
+ MagicGrid 通过 Vue 事件向外暴露 Grid 内核事件。事件名采用 **kebab-case**。除 Vue 事件外,也可通过 `gridRef.value?.api.on(...)` 订阅 camelCase 内核事件(见 [Expose](#expose))。
1347
+
1348
+ ### 生命周期
1349
+
1350
+ | Vue 事件 | 内核事件 | 频率 | 说明 |
1351
+ |----------|----------|------|------|
1352
+ | `@grid-ready` | `gridReady` | 每次 Grid 实例创建 | `initGrid` 完成、`setData` 已调用;`event.api` 可立即使用 |
1353
+ | `@grid-destroyed` | `gridDestroyed` | 每次 Grid 实例销毁 | reinit 或组件卸载前;`event.reason` 为 `'reinit'` \| `'unmount'` |
1354
+ | `@first-rendered` | `firstRendered` | 每个实例一次 | 该实例首次 RAF 渲染完成;payload 含 `api` 与 `metrics` |
1355
+
1356
+ `@data-rendered` 仍表示**每一轮**数据渲染完成(见下节),与 `@first-rendered` 语义分离。
1357
+
1358
+ ```vue
1359
+ <script setup lang="ts">
1360
+ import { useGridLifecycle } from '@taocompany/magic-grid'
1361
+
1362
+ const lifecycle = useGridLifecycle({
1363
+ onReady: ({ api }) => {
1364
+ api.setSortModel([{ colId: 'name', sort: 'asc' }])
1365
+ },
1366
+ onDestroyed: ({ reason }) => {
1367
+ if (reason === 'reinit') {
1368
+ // 清理旧 api 订阅
1369
+ }
1370
+ },
1371
+ onFirstRendered: ({ api }) => {
1372
+ api.ensureIndexVisible(0, 'top')
1373
+ },
1374
+ })
1375
+ </script>
1376
+
1377
+ <template>
1378
+ <MagicGrid :columns="columns" :data="rows" v-on="lifecycle" />
1379
+ </template>
1380
+ ```
1381
+
1382
+ 也可直接在模板绑定 `@grid-ready` / `@grid-destroyed` / `@first-rendered`。
1383
+
1384
+ 内核订阅(payload 与 Vue 事件相同):
1385
+
1386
+ ```ts
1387
+ function onGridReady({ api }: GridReadyEvent) {
1388
+ const off = api.on('selectionChanged', handler)
1389
+ // 在 @grid-destroyed 或 api.on('gridDestroyed') 里 off()
1390
+ }
1391
+ ```
1346
1392
 
1347
1393
  ### 渲染与滚动
1348
1394
 
@@ -1735,7 +1781,7 @@ payload 为 `RowExpansionChangedEvent`:
1735
1781
 
1736
1782
  | 成员 | 类型 | 说明 |
1737
1783
  |------|------|------|
1738
- | `api` | `Ref<GridApi \| undefined>` | GridApi 实例;挂载完成后可用 |
1784
+ | `api` | `GridApi \| undefined` | GridApi 实例;挂载完成后可用 |
1739
1785
  | `getMetrics()` | `() => GridMetrics \| undefined` | 读取渲染与滚动指标(同 `scroll` / `data-rendered` 事件 payload) |
1740
1786
  | `scrollTo(scrollTop, scrollLeft?)` | `(number, number?) => void` | 编程式滚动;scrollLeft 省略时保持当前值 |
1741
1787
  | `setSortModel(model)` | `(SortModelItem[]) => void` | 设置排序模型并重算 display 行 |
@@ -1754,7 +1800,7 @@ payload 为 `RowExpansionChangedEvent`:
1754
1800
 
1755
1801
  ```ts
1756
1802
  interface MagicGridExpose {
1757
- api: Ref<GridApi | undefined>
1803
+ api: GridApi | undefined
1758
1804
  getMetrics: () => GridMetrics | undefined
1759
1805
  scrollTo: (scrollTop: number, scrollLeft?: number) => void
1760
1806
  setSortModel: (model: SortModelItem[]) => void
@@ -1776,31 +1822,31 @@ interface MagicGridExpose {
1776
1822
  ```vue
1777
1823
  <script setup lang="ts">
1778
1824
  import { ref } from 'vue'
1779
- import type { MagicGridExpose } from '@taocompany/magic-grid/types/core'
1825
+ import type { GridReadyEvent, MagicGridExpose } from '@taocompany/magic-grid/types/core'
1780
1826
 
1781
1827
  const gridRef = ref<MagicGridExpose>()
1782
1828
 
1829
+ function onGridReady({ api }: GridReadyEvent) {
1830
+ api.setSortModel([{ colId: 'name', sort: 'asc' }])
1831
+ }
1832
+
1783
1833
  async function addRow() {
1784
1834
  await gridRef.value?.applyTransaction({
1785
1835
  add: [{ id: Date.now(), name: '新行' }],
1786
1836
  })
1787
1837
  }
1788
-
1789
- function exportData() {
1790
- return gridRef.value?.getData({ sourceOrder: true })
1791
- }
1792
-
1793
- function scrollToRow(index: number) {
1794
- gridRef.value?.ensureIndexVisible(index, 'middle')
1795
- }
1796
1838
  </script>
1797
1839
 
1798
1840
  <template>
1799
- <MagicGrid ref="gridRef" ... />
1841
+ <MagicGrid ref="gridRef" @grid-ready="onGridReady" ... />
1800
1842
  </template>
1801
1843
  ```
1802
1844
 
1803
- ### GridApi(`gridRef.api`)
1845
+ 获取 api 的推荐方式见 [Events · 生命周期](#生命周期)。ref 亦可直接调用:`gridRef.value?.api?.getData()`。
1846
+
1847
+ 在 `computed` / 模板中追踪 api 时,可用 `useGridApi(gridRef)`(等价于 `computed(() => gridRef.value?.api)`)。
1848
+
1849
+ ### GridApi(`gridRef.value?.api`)
1804
1850
 
1805
1851
  `api` 是完整的命令式 API 面。以下按职责分组列出全部公开方法;入参 rowId 均接受 `string | number`,出参 rowId 恒为 `string`。
1806
1852
 
@@ -1945,6 +1991,9 @@ GridApi 支持 `on(eventType, handler)` 订阅内核事件(camelCase),返
1945
1991
 
1946
1992
  | 内核事件名 | 对应 Vue 事件 |
1947
1993
  |------------|---------------|
1994
+ | `gridReady` | `grid-ready` |
1995
+ | `gridDestroyed` | `grid-destroyed` |
1996
+ | `firstRendered` | `first-rendered` |
1948
1997
  | `selectionChanged` | `selection-changed` |
1949
1998
  | `cellValueChanged` | `cell-value-changed` |
1950
1999
  | `sortChanged` | `sort-changed` |
@@ -2167,17 +2216,18 @@ Magic Grid 支持 **overlay**(点击/Enter 进入编辑浮层)与 **inline**
2167
2216
  | 模式 | 配置 | 行为 |
2168
2217
  |------|------|------|
2169
2218
  | **overlay**(默认) | `cellEditorMode: 'overlay'` 或省略 | 非编辑态展示 formatter / cellRenderer;进入编辑态后在单元格上方挂载编辑器 |
2170
- | **inline** | `cellEditorMode: 'inline'` + 必须配置 `cellEditor` | 编辑器始终渲染在格内;空格切换 checkbox;适合布尔列、简单输入 |
2219
+ | **inline** | `cellEditorMode: 'inline'` + 必须配置 `cellEditor` | 编辑器始终渲染在格内;单击单元格或 Space 切换 checkbox/switch;适合布尔列、简单输入 |
2171
2220
 
2172
2221
  **inline 限制**(开发模式会 console.warn):
2173
2222
 
2174
2223
  - 系统列(索引 / 行选择)不支持 inline。
2175
- - 推荐搭配内置 `'text'` / `'number'` / `'checkbox'`;依赖 teleport/下拉的第三方组件(如 Select)应使用 overlay + `useCellEditor({ strategy: 'overlay' })`。
2176
- - inline 列通过 `params.commit()` / 空格(checkbox)提交;不走 overlay 的 Enter 捕获逻辑。
2224
+ - 推荐搭配内置 `'text'` / `'number'` / `'checkbox'` / `'switch'`;依赖 teleport/下拉的第三方组件(如 Select)应使用 overlay + `useCellEditor({ strategy: 'overlay' })`。
2225
+ - inline 列通过 `params.commit()` / 单击单元格 / 空格(checkbox/switch)提交;不走 overlay 的 Enter 捕获逻辑。
2177
2226
 
2178
2227
  ```ts
2179
2228
  const columns: ColumnDef[] = [
2180
2229
  { prop: 'done', label: '完成', width: 80, cellEditorMode: 'inline', cellEditor: 'checkbox' },
2230
+ { prop: 'enabled', label: '启用', width: 88, cellEditorMode: 'inline', cellEditor: 'switch' },
2181
2231
  { prop: 'qty', label: '数量', cellEditorMode: 'inline', cellEditor: 'number', editable: true },
2182
2232
  ]
2183
2233
  ```
@@ -2186,13 +2236,298 @@ Grid 级 `editBehavior: 'row'` 时整行同时进入编辑;Tab 在同行可编
2186
2236
 
2187
2237
  ### 内置编辑器
2188
2238
 
2189
- | 名称 | 说明 |
2190
- |------|------|
2191
- | `'text'` | 单行文本 input |
2192
- | `'number'` | `type="number"` input |
2193
- | `'checkbox'` | 布尔 checkbox;inline 模式下空格切换 |
2239
+ Magic Grid 提供四种 **零注册** 内置编辑器,列定义中直接引用字符串即可:`cellEditor: 'text' | 'number' | 'checkbox' | 'switch'`。未配置 `cellEditor` 时,overlay 编辑 **默认回退为 `'text'`**。
2240
+
2241
+ 内置名 **不可** 用于 `registerCellEditor` 覆盖;命名注册表仅用于自定义编辑器。
2242
+
2243
+ #### 总览
2244
+
2245
+ | 名称 | 控件 | 典型场景 | 推荐 `cellEditorMode` | 默认写回类型 |
2246
+ |------|------|----------|----------------------|--------------|
2247
+ | `'text'` | `<input type="text">` | 单行文本 | `overlay` | `string` |
2248
+ | `'number'` | `<input type="number">` | 数值 | `overlay` 或 `inline` | `number`(空串拒绝提交) |
2249
+ | `'checkbox'` | `<input type="checkbox">` | 布尔 / 二元状态 | **`inline`**(也可 `overlay`) | `boolean`(可自定义) |
2250
+ | `'switch'` | `<input type="checkbox" role="switch">` | 开关态 | **`inline`**(也可 `overlay`) | `boolean`(可自定义) |
2251
+
2252
+ #### 解析优先级
2253
+
2254
+ 列进入编辑(overlay)或 inline 常驻挂载时,`cellEditor: 'someName'` 按以下顺序解析:
2255
+
2256
+ 1. **内置名** `'text'` | `'number'` | `'checkbox'` | `'switch'`
2257
+ 2. **列级函数** `(params, ctx) => HTMLElement`
2258
+ 3. **Grid 实例级** `cellEditorRegistry` / `cellEditors.byName`
2259
+ 4. **全局** `registerCellEditor(name)`
2260
+ 5. 未找到 → `console.warn` + 回退 **`text`** 编辑器
2261
+
2262
+ #### `'text'` — 单行文本
2263
+
2264
+ | 项 | 说明 |
2265
+ |----|------|
2266
+ | DOM | `<input type="text" class="mg-cell-editor">` |
2267
+ | 初始值 | `params.formattedValue`(经 formatter 格式化后的字符串) |
2268
+ | 默认 `valueParser` | 原样返回 raw 字符串 |
2269
+ | overlay 提交 | Enter / blur / Tab(Tab 可链式下一 editable 格) |
2270
+ | inline | 支持;`change` 或 `params.commit()` 提交;不走 Enter 捕获 |
2271
+
2272
+ ```ts
2273
+ { prop: 'name', label: '姓名', editable: true, cellEditor: 'text' }
2274
+ // 省略 cellEditor 时 overlay 编辑同样使用 text 编辑器
2275
+ ```
2276
+
2277
+ #### `'number'` — 数字输入
2278
+
2279
+ | 项 | 说明 |
2280
+ |----|------|
2281
+ | DOM | `<input type="number" class="mg-cell-editor">` |
2282
+ | 初始值 | 单元格值为 `null` / `''` 时显示空串,否则 `String(value)` |
2283
+ | 默认 `valueParser` | 去空白后 `Number()`;空串或非有限数 → **拒绝提交** |
2284
+ | 自定义解析 | 推荐显式配置 `valueParser`,例如 `(v) => (v === '' ? null : Number(v))` |
2285
+
2286
+ ```ts
2287
+ {
2288
+ prop: 'qty',
2289
+ label: '数量',
2290
+ editable: true,
2291
+ cellEditor: 'number',
2292
+ valueParser: (raw) => {
2293
+ const n = Number(raw)
2294
+ if (!Number.isFinite(n)) throw new Error('请输入有效数字')
2295
+ return n
2296
+ },
2297
+ }
2298
+ ```
2299
+
2300
+ #### `'checkbox'` — 复选框
2301
+
2302
+ checkbox 适用于二元字段。支持 **overlay**(进入编辑态后显示控件)与 **inline**(控件常驻格内,改值即提交)两种呈现;布尔列 **强烈推荐 inline**。
2303
+
2304
+ | 项 | 说明 |
2305
+ |----|------|
2306
+ | DOM | `<input type="checkbox" class="mg-cell-editor [mg-inline-checkbox]">` |
2307
+ | 初始 checked | `coerceCheckboxEditorValue(value, checkboxEditorParams)` |
2308
+ | 读值 | `readCheckboxControlValue` → JSON 序列化 raw(默认 `'true'` / `'false'`) |
2309
+ | 默认 `valueParser` | `parseCheckboxEditorRawValue` |
2310
+ | inline 提交 | 控件 `@change` 即 commit;**单击单元格空白** 同样 toggle + commit |
2311
+ | overlay 提交 | 进入编辑态后 change / Enter / blur 走常规 overlay 管线 |
2312
+ | 键盘 | 焦点在 inline checkbox 格时 **Space** toggle + commit(不触发行选择) |
2313
+ | disabled | `editable: false` 或 `editable(row) => false` → `.mg-cell--inline-disabled` |
2314
+
2315
+ **自定义写回值**(`checkboxEditorParams`):
2316
+
2317
+ ```ts
2318
+ interface CheckboxEditorParams {
2319
+ /** 选中态写回值;默认 `true` */
2320
+ checkedValue?: unknown
2321
+ /** 未选中态写回值;默认 `false` */
2322
+ uncheckedValue?: unknown
2323
+ }
2324
+ ```
2325
+
2326
+ | 示例配置 | 写回值 |
2327
+ |----------|--------|
2328
+ | 默认(省略 params) | `true` / `false` |
2329
+ | `{ checkedValue: 1, uncheckedValue: 0 }` | `1` / `0` |
2330
+ | `{ checkedValue: 'Y', uncheckedValue: 'N' }` | `'Y'` / `'N'` |
2331
+
2332
+ **值 → checked 态映射规则**:
2333
+
2334
+ - 单元格值 **严格等于** `checkedValue` → checked
2335
+ - 单元格值 **严格等于** `uncheckedValue` → unchecked
2336
+ - **未配置自定义值** 时,额外兼容常见 falsy/truthy:`null` / `''` / `false` / `'false'` / `0` / `'0'` → unchecked;`true` / `'true'` / `1` / `'1'` → checked;其余走 `Boolean(value)`
2337
+ - **已配置自定义值** 时,仅精确匹配 on/off 值;不匹配则默认为 unchecked
2338
+
2339
+ ```ts
2340
+ const columns: ColumnDef[] = [
2341
+ // inline · 默认 boolean
2342
+ { prop: 'active', label: '启用', width: 72, cellEditorMode: 'inline', cellEditor: 'checkbox' },
2343
+ // inline · 写回 1/0
2344
+ {
2345
+ prop: 'activeFlag',
2346
+ label: '激活',
2347
+ width: 72,
2348
+ cellEditorMode: 'inline',
2349
+ cellEditor: 'checkbox',
2350
+ checkboxEditorParams: { checkedValue: 1, uncheckedValue: 0 },
2351
+ },
2352
+ // overlay · 写回 Y/N
2353
+ {
2354
+ prop: 'confirmed',
2355
+ label: '已确认',
2356
+ width: 88,
2357
+ editable: true,
2358
+ cellEditor: 'checkbox',
2359
+ checkboxEditorParams: { checkedValue: 'Y', uncheckedValue: 'N' },
2360
+ },
2361
+ ]
2362
+ ```
2363
+
2364
+ #### `'switch'` — 开关
2365
+
2366
+ switch 在语义与管线上与 checkbox **共用 toggle 值引擎**,UI 为 pill 形开关(`role="switch"`)。交互、inline/overlay 行为、键盘与 disabled 规则与 checkbox 一致。
2367
+
2368
+ | 项 | 说明 |
2369
+ |----|------|
2370
+ | DOM | `<input type="checkbox" role="switch" class="mg-cell-editor mg-switch-editor [mg-inline-switch]">` |
2371
+ | 初始 checked | `coerceSwitchEditorValue(value, switchEditorParams)` |
2372
+ | 读值 | `readSwitchControlValue` |
2373
+ | 默认 `valueParser` | `parseSwitchEditorRawValue` |
2374
+ | 样式类 | `.mg-switch-editor` · `.mg-inline-switch`;尺寸随 `--mg-checkbox-size` token |
2375
+
2376
+ **自定义写回值**(`switchEditorParams`):
2377
+
2378
+ ```ts
2379
+ interface SwitchEditorParams {
2380
+ /** 开启态写回值;默认 `true` */
2381
+ onValue?: unknown
2382
+ /** 未开启态写回值;默认 `false` */
2383
+ offValue?: unknown
2384
+ }
2385
+ ```
2386
+
2387
+ ```ts
2388
+ const columns: ColumnDef[] = [
2389
+ // inline · 默认 boolean
2390
+ { prop: 'enabled', label: '启用', width: 88, cellEditorMode: 'inline', cellEditor: 'switch' },
2391
+ // overlay · 写回 ON/OFF 字符串
2392
+ {
2393
+ prop: 'mode',
2394
+ label: '模式',
2395
+ width: 100,
2396
+ editable: true,
2397
+ cellEditor: 'switch',
2398
+ switchEditorParams: { onValue: 'ON', offValue: 'OFF' },
2399
+ },
2400
+ ]
2401
+ ```
2402
+
2403
+ #### overlay 与 inline 行为对照
2404
+
2405
+ | 维度 | overlay | inline |
2406
+ |------|---------|--------|
2407
+ | 挂载时机 | 进入编辑会话(单击/双击/Enter/API) | `CellCtrl.refreshCell` 常驻 |
2408
+ | DOM 容器 | `.mg-cell-editor-host` | `.mg-cell__inline-editor` |
2409
+ | 非编辑态展示 | formatter / cellRenderer | **忽略** cellRenderer,始终显示编辑器 |
2410
+ | 编辑会话 | 有(pin 行 · `cellEditingStarted/Stopped`) | **无** |
2411
+ | 提交触发 | Enter / blur / Tab / 控件 change | `@change` / 单击单元格 / Space(checkbox/switch) |
2412
+ | Tab | 提交并链式下一 editable 格 | 仅移动焦点 |
2413
+ | Esc | 取消 draft | 无 draft;committing 期间 Esc 可 abort |
2414
+ | 行编辑 `editBehavior: 'row'` | 参与整行编辑 | **排除**(不参与整行编辑器挂载) |
2415
+ | 适用编辑器 | 全部内置 + Vue 第三方 | 推荐 `text` / `number` / `checkbox` / `switch` |
2416
+
2417
+ > **注意**:inline 模式搭配依赖 teleport/下拉的第三方组件(如 Select、DatePicker)时,开发环境会 `console.warn`;此类列应使用 overlay + `useCellEditor({ strategy: 'overlay' })`。
2418
+
2419
+ #### 与行选择列的区别
2420
+
2421
+ 行选择列(`rowSelection`)与 inline checkbox **UI 相似但职责完全不同**:
2422
+
2423
+ | 维度 | 行选择列 `__select__` | inline checkbox / switch 列 |
2424
+ |------|----------------------|----------------------------|
2425
+ | 状态源 | `SelectionService` | `row.data[prop]` |
2426
+ | 事件 | `selectionChanged` | `cellValueChanged` |
2427
+ | 管线 | 无 valueParser / valueSetter | 完整编辑管线(校验 · async 写回) |
2428
+ | DOM 类名 | `mg-selection-checkbox` | `mg-inline-checkbox` / `mg-inline-switch` |
2429
+ | 表头 | 多选时全选 checkbox | 用户自定义 label |
2430
+
2431
+ #### 样式与 CSS 变量
2432
+
2433
+ 内置 checkbox / switch 尺寸与主题色可通过 CSS 变量覆盖(需已引入 `style.css`):
2434
+
2435
+ | Token | 默认值 | 作用 |
2436
+ |-------|--------|------|
2437
+ | `--mg-checkbox-size` | `14px` | checkbox 边长;switch 宽高按比例推导 |
2438
+ | `--mg-color-primary` | 主题主色 | checkbox `accent-color` · switch 开启态背景 |
2439
+ | `--mg-color-border` | 边框色 | switch 关闭态背景 |
2440
+
2441
+ 相关类名:`.mg-cell--inline-editor` · `.mg-cell--inline-disabled` · `.mg-cell--committing`(async 提交中)。
2194
2442
 
2195
- 列定义引用:`cellEditor: 'text'`。内置名不可用于 `registerCellEditor` 覆盖。
2443
+ #### 完整示例
2444
+
2445
+ ```vue
2446
+ <script setup lang="ts">
2447
+ import { ref } from 'vue'
2448
+ import { MagicGrid } from '@taocompany/magic-grid'
2449
+ import type { ColumnDef, RowData } from '@taocompany/magic-grid/types/core'
2450
+ import '@taocompany/magic-grid/style.css'
2451
+
2452
+ interface ProductRow extends RowData {
2453
+ name: string
2454
+ qty: number | null
2455
+ active: boolean
2456
+ activeFlag: number
2457
+ confirmed: string
2458
+ enabled: boolean
2459
+ mode: string
2460
+ }
2461
+
2462
+ const data = ref<ProductRow[]>([
2463
+ { id: 1, name: '商品 A', qty: 10, active: true, activeFlag: 1, confirmed: 'Y', enabled: true, mode: 'ON' },
2464
+ { id: 2, name: '商品 B', qty: null, active: false, activeFlag: 0, confirmed: 'N', enabled: false, mode: 'OFF' },
2465
+ ])
2466
+
2467
+ const columns: ColumnDef[] = [
2468
+ { prop: 'name', label: '名称', width: 140, editable: true, cellEditor: 'text' },
2469
+ { prop: 'qty', label: '库存', width: 88, cellEditorMode: 'inline', cellEditor: 'number', editable: true },
2470
+ { prop: 'active', label: '上架', width: 72, cellEditorMode: 'inline', cellEditor: 'checkbox' },
2471
+ {
2472
+ prop: 'activeFlag',
2473
+ label: '标记',
2474
+ width: 72,
2475
+ cellEditorMode: 'inline',
2476
+ cellEditor: 'checkbox',
2477
+ checkboxEditorParams: { checkedValue: 1, uncheckedValue: 0 },
2478
+ },
2479
+ {
2480
+ prop: 'confirmed',
2481
+ label: '确认',
2482
+ width: 80,
2483
+ editable: true,
2484
+ cellEditor: 'checkbox',
2485
+ checkboxEditorParams: { checkedValue: 'Y', uncheckedValue: 'N' },
2486
+ },
2487
+ { prop: 'enabled', label: '启用', width: 88, cellEditorMode: 'inline', cellEditor: 'switch' },
2488
+ {
2489
+ prop: 'mode',
2490
+ label: '模式',
2491
+ width: 100,
2492
+ editable: true,
2493
+ cellEditor: 'switch',
2494
+ switchEditorParams: { onValue: 'ON', offValue: 'OFF' },
2495
+ },
2496
+ ]
2497
+
2498
+ function onCellValueChanged(event: { colId: string; rowId: string | number; oldValue: unknown; newValue: unknown }) {
2499
+ console.log(`${event.colId}: ${String(event.oldValue)} → ${String(event.newValue)}`)
2500
+ }
2501
+ </script>
2502
+
2503
+ <template>
2504
+ <MagicGrid
2505
+ :columns="columns"
2506
+ :data="data"
2507
+ row-key="id"
2508
+ height="360"
2509
+ @cell-value-changed="onCellValueChanged"
2510
+ />
2511
+ </template>
2512
+ ```
2513
+
2514
+ #### 提交管线(内置编辑器共用)
2515
+
2516
+ 无论 overlay 还是 inline,提交均走统一管线:
2517
+
2518
+ ```
2519
+ 控件读值 (getValue / readCheckboxControlValue / readSwitchControlValue)
2520
+ → valueParser(列级优先;内置编辑器有默认 parser)
2521
+ → cellValidator / validationRules
2522
+ → valueSetter(支持 async · signal · cancelCommit)
2523
+ → applyTransaction 写回 row.data
2524
+ → cellValueChanged
2525
+ ```
2526
+
2527
+ - **校验失败** 或 **valueSetter 返回 false**:按 `invalidEditValueMode`(`keep` / `revert` / `block`)处理;inline checkbox/switch 会 **revert 控件 checked 态**。
2528
+ - **async valueSetter**:提交期间单元格加 `.mg-cell--committing`,控件 disabled;Esc 可 `cancelCommit()` abort。
2529
+
2530
+ 更深入的 inline 架构说明见 [docs/24-phase13-inline-cell-editor.md](docs/24-phase13-inline-cell-editor.md)。
2196
2531
 
2197
2532
  ### Vue 自定义编辑器
2198
2533
 
@@ -2347,7 +2682,7 @@ api.clearValidation()
2347
2682
  | `Ctrl/Cmd` + 方向键 | 跳转到首/末行或导航序首/末列 |
2348
2683
  | `Tab` / `Shift+Tab` | 下一 / 上一可聚焦格(表头 ↔ 表体可衔接,`navigateHeader` / `navigateFooter` 控制) |
2349
2684
  | `Enter` | 表头:排序 / 全选;索引列:定位;树/分组:展开折叠;数据格:进入编辑 |
2350
- | `Space` | 行选择 checkbox;inline checkbox 切换;表头/索引/树/分组同 Enter 的切换逻辑 |
2685
+ | `Space` | 行选择 checkbox;inline checkbox/switch 切换;表头/索引/树/分组同 Enter 的切换逻辑 |
2351
2686
  | `F2` | 进入编辑(若列可编辑) |
2352
2687
  | `Shift+F2` | 新建/打开单元格批注(批注功能启用时) |
2353
2688
 
@@ -2715,7 +3050,7 @@ import type { ColumnSettingsMenuIconName } from '@taocompany/magic-grid/types/co
2715
3050
  **单元格编辑器**
2716
3051
 
2717
3052
  - `useCellEditor`、`VueCellEditorHost`、`createVueCellEditor`、`createSlotCellEditor`
2718
- - `textCellEditor`、`numberCellEditor`、`checkboxCellEditor`(内置实现,高级场景)
3053
+ - `textCellEditor`、`numberCellEditor`、`checkboxCellEditor`、`switchCellEditor`(内置实现,高级场景)
2719
3054
  - `registerCellEditor` / `unregisterCellEditor` / `getCellEditor` / `hasCellEditor` / `getRegisteredCellEditorNames`
2720
3055
  - `BUILTIN_CELL_EDITOR_NAMES`、`isBuiltInCellEditor`
2721
3056
  - `DEFAULT_CELL_EDITOR_MODE`、`resolveCellEditorMode`、`isInlineEditorColumn`
@@ -2761,15 +3096,16 @@ import type { MagicGridProps } from '@taocompany/magic-grid/types/core'
2761
3096
  ### 组件 ref 类型
2762
3097
 
2763
3098
  ```ts
2764
- import type { MagicGridExpose, GridApi } from '@taocompany/magic-grid/types/core'
3099
+ import type { MagicGridExpose } from '@taocompany/magic-grid/types/core'
2765
3100
 
2766
3101
  const gridRef = ref<MagicGridExpose>()
2767
- const api = computed(() => gridRef.value?.api) // Ref<GridApi | undefined>
3102
+ // 命令式:gridRef.value?.api?.getData()
3103
+ // 初始化:@grid-ready="({ api }) => ..."
2768
3104
  ```
2769
3105
 
2770
3106
  ### 事件 payload
2771
3107
 
2772
- 事件回调参数类型均从 `@taocompany/magic-grid/types/core` 导出,例如 `CellValueChangedEvent`、`SelectionChangedEvent`、`FilterModel`、`SortModelItem`。
3108
+ 事件回调参数类型均从 `@taocompany/magic-grid/types/core` 导出,例如 `GridReadyEvent`、`CellValueChangedEvent`、`FilterModel`。
2773
3109
 
2774
3110
  ### 严格模式建议
2775
3111
 
@@ -2861,48 +3197,6 @@ import {
2861
3197
 
2862
3198
  ---
2863
3199
 
2864
-
2865
- ## 本地开发与 Playground
2866
-
2867
- ### 环境
2868
-
2869
- ```bash
2870
- pnpm install
2871
- pnpm dev # 启动 Playground(默认 Vite dev server)
2872
- pnpm build # 构建 npm 包至 dist/
2873
- pnpm test:run # 单元测试
2874
- pnpm typecheck # Vue + TS 类型检查
2875
- ```
2876
-
2877
- 要求 Node.js >= 20.19,包管理器推荐 pnpm 11.x。
2878
-
2879
- ### Playground 路由
2880
-
2881
- 开发服务器按 Phase 组织演示页,便于逐项验收能力:
2882
-
2883
- | 路由 | 主题 |
2884
- |------|------|
2885
- | `/phase2` – `/phase27` | 各 Phase 功能演示与验收用例 |
2886
- | `/phase41` | Cell Renderer 滚动性能(5000 行 × 多 `#cell-xxx` · pending 指标 · 调参) |
2887
- | `/glossary` | 术语表 |
2888
-
2889
- 典型入口:`pnpm dev` 后访问控制台输出的本地 URL,从侧边栏切换 Phase。例如列显隐验收 **`/phase22`**(T14–T15 · `available` 列切换);多 Vue slot 列滚停拖尾验收 **`/phase41`**。
2890
-
2891
- Playground 源码位于仓库 `playground/` 目录;自定义 Vue 编辑器示例见 `playground/components/editors/`(Element Plus 集成参考)。
2892
-
2893
- ### 构建产物
2894
-
2895
- `pnpm build` 输出:
2896
-
2897
- - `dist/index.es.js` / `dist/index.cjs.js` — 主包
2898
- - `dist/types/*.js` — 类型子路径运行时垫片
2899
- - `dist/style.css` — 样式
2900
- - `dist/*.d.ts` — 类型声明(api-extractor rollup)
2901
-
2902
- 发布前会自动执行 `prepublishOnly` → `pnpm run build`。
2903
-
2904
- ---
2905
-
2906
3200
  ## License
2907
3201
 
2908
3202
  MIT © [songjiuzhang](mailto:jiuzhang.song@taoandcompany02.com)
@@ -1,10 +1,11 @@
1
- import { PropType, DefineComponent, ExtractPropTypes, ShallowRef, ComponentOptionsMixin, PublicProps, ComponentProvideOptions } from 'vue';
1
+ import { PropType, DefineComponent, ExtractPropTypes, ComponentOptionsMixin, PublicProps, ComponentProvideOptions } from 'vue';
2
2
  import { GridApi } from '../../core/api/gridApi';
3
3
  import { RowSelectionInput } from '../../core/types/rowSelectionOptions';
4
4
  import { CellEditorFn, CellEditType, CellEditBehavior } from '../../core/types/cellEditor';
5
5
  import { RowValidator, InvalidEditValueMode } from '../../core/types/validation';
6
6
  import { AsyncRowMutationHandlers } from '../../core/types/rowMutationAsync';
7
7
  import { GridMetrics } from '../../core/grid/gridMetrics';
8
+ import { FirstRenderedEvent, GridDestroyedEvent, GridReadyEvent } from '../../core/types/gridLifecycle';
8
9
  import { CellClickedEvent, RowClickedEvent, RowDoubleClickedEvent, CellValueChangedEvent, CellEditingStartedEvent, CellEditingStoppedEvent, RowEditingStartedEvent, RowEditingStoppedEvent, RowValueChangedEvent, CellCommitStartedEvent, CellCommitFinishedEvent, EditCommitAbortedEvent, RowCommitStartedEvent, RowCommitFinishedEvent, CellValidationFailedEvent, RowValidationFailedEvent, RowsAddedEvent, RowsRemovedEvent, RowIdPromotedEvent, FilterModel, RowNodeTransaction, RowTransaction, SelectionChangedEvent, IndexFocusChangedEvent, SortModelItem, ColumnMovedEvent, ColumnResizedEvent, ColumnPinnedEvent, ColumnHiddenChangedEvent, RowMovedEvent, RowDragEndEvent, RowDragCommitStartedEvent, RowDragCommitFinishedEvent, RowHeightChangedEvent, RowExpansionChangedEvent, RowGroupChangedEvent, RowGroupOpenedEvent } from '../../core/types';
9
10
  import { TreeChildrenLoadedEvent, TreeChildrenLoadFailedEvent, TreeChildrenLoadingEvent, TreeNodeOpenedEvent, GetDataPathFn, TreeValueGetterFn, TreeDisplayType, TreeDragScope, SetDataPathFn, HasTreeChildrenFn, LoadTreeChildrenFn } from '../../core/types/treeTable';
10
11
  import { CellCommentChangedEvent, CellCommentsDataSource, CellCommentTrigger } from '../../core/types/cellComment';
@@ -662,7 +663,7 @@ declare const __VLS_base: DefineComponent<ExtractPropTypes<{
662
663
  default: undefined;
663
664
  };
664
665
  }>, {
665
- api: ShallowRef<GridApi | undefined, GridApi | undefined>;
666
+ readonly api: GridApi | undefined;
666
667
  getMetrics: () => GridMetrics | undefined;
667
668
  scrollTo: (scrollTop: number, scrollLeft?: number) => void;
668
669
  setSortModel: (model: SortModelItem[]) => void;
@@ -679,6 +680,9 @@ declare const __VLS_base: DefineComponent<ExtractPropTypes<{
679
680
  }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
680
681
  scroll: (metrics: GridMetrics) => any;
681
682
  "data-rendered": (metrics: GridMetrics) => any;
683
+ "grid-ready": (event: GridReadyEvent) => any;
684
+ "grid-destroyed": (event: GridDestroyedEvent) => any;
685
+ "first-rendered": (event: FirstRenderedEvent) => any;
682
686
  "cell-clicked": (event: CellClickedEvent) => any;
683
687
  "row-click": (event: RowClickedEvent) => any;
684
688
  "row-dblclick": (event: RowDoubleClickedEvent) => any;
@@ -1321,6 +1325,9 @@ declare const __VLS_base: DefineComponent<ExtractPropTypes<{
1321
1325
  }>> & Readonly<{
1322
1326
  onScroll?: ((metrics: GridMetrics) => any) | undefined;
1323
1327
  "onData-rendered"?: ((metrics: GridMetrics) => any) | undefined;
1328
+ "onGrid-ready"?: ((event: GridReadyEvent) => any) | undefined;
1329
+ "onGrid-destroyed"?: ((event: GridDestroyedEvent) => any) | undefined;
1330
+ "onFirst-rendered"?: ((event: FirstRenderedEvent) => any) | undefined;
1324
1331
  "onCell-clicked"?: ((event: CellClickedEvent) => any) | undefined;
1325
1332
  "onRow-click"?: ((event: RowClickedEvent) => any) | undefined;
1326
1333
  "onRow-dblclick"?: ((event: RowDoubleClickedEvent) => any) | undefined;