Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix: support context menu plugin on canvas blank area",
"type": "patch",
"packageName": "@visactor/vtable-plugins"
}
],
"packageName": "@visactor/vtable-plugins",
"email": "github@visactor.io"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix: allow plugins to initialize before first render",
"type": "patch",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "github@visactor.io"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix: support updating checkbox state by record index",
"type": "patch",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "github@visactor.io"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix: refresh vue custom layout dom content after sort",
"type": "patch",
"packageName": "@visactor/vue-vtable"
}
],
"packageName": "@visactor/vue-vtable",
"email": "github@visactor.io"
}
22 changes: 22 additions & 0 deletions docs/assets/api/en/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2143,6 +2143,28 @@ setCellCheckboxState(col: number, row: number, checked: boolean) => void
- row: Row number
- checked: Whether selected

## setCellCheckboxStateByRecordIndex(Function)

Set the checkbox state by source records index and field. For tree tables, pass a children path such as `[0, 1]` for the second child of the first root record. The state is updated even when the target node is collapsed and not currently visible.

```
setCellCheckboxStateByRecordIndex(recordIndex: number | number[], field: string | number, checked: boolean | 'indeterminate') => void
```

- recordIndex: Source data index; number for normal tables, number[] for tree tables
- field: Field of the checkbox column
- checked: Checkbox state, including `'indeterminate'`

## clearCheckboxState(Function)

Clear all checkbox checked states under the specified field. `clearAllCheckboxState(field)` is an alias of this method.

```
clearCheckboxState(field: string | number) => void
```

- field: Field of the checkbox column

## setCellRadioState(Function)

Set the radio state of the cell to selected state
Expand Down
22 changes: 22 additions & 0 deletions docs/assets/api/zh/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2142,6 +2142,28 @@ setCellCheckboxState(col: number, row: number, checked: boolean) => void
- row: 行号
- checked: 是否选中

## setCellCheckboxStateByRecordIndex(Function)

根据源数据 records 的 index 和 field 设置 checkbox 状态。树形表格可传入 children 路径,例如 `[0, 1]` 表示第 1 条根节点下第 2 条子节点;即使该节点当前处于折叠不可见状态,也会更新其 checkbox 状态。

```
setCellCheckboxStateByRecordIndex(recordIndex: number | number[], field: string | number, checked: boolean | 'indeterminate') => void
```

- recordIndex: 源数据索引;普通表格为 number,树形表格为 number[]
- field: checkbox 所在字段
- checked: 是否选中,支持半选状态 `'indeterminate'`

## clearCheckboxState(Function)

清除指定 field 下所有 checkbox 的选中状态。`clearAllCheckboxState(field)` 是该方法的别名。

```
clearCheckboxState(field: string | number) => void
```

- field: checkbox 所在字段

## setCellRadioState(Function)

将单元格的 radio 状态设置为选中状态
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-nocheck
import { ListTable } from '@visactor/vtable';
import { createDiv } from '../../../vtable/__tests__/dom';
import { ContextMenuPlugin } from '../../src/context-menu';
import { MenuHandler } from '../../src/contextmenu/handle-menu-helper';
import { TableSeriesNumber } from '../../src/table-series-number';

Expand Down Expand Up @@ -63,3 +64,29 @@ describe('Context menu row deletion', () => {
expect(table.records.map(record => record.id)).toEqual([0, 4]);
});
});

describe('Context menu canvas option', () => {
let table: ListTable;

afterEach(() => {
table?.release();
document.body.innerHTML = '';
});

test('ContextMenuPlugin enables canvas context menu through contextMenuWorkOnlyCell', () => {
const container = createDiv();
const contextMenuPlugin = new ContextMenuPlugin({
contextMenuWorkOnlyCell: false
});

table = new ListTable({
container,
columns: [{ field: 'id', title: 'ID' }],
records: [{ id: 1 }],
plugins: [contextMenuPlugin]
});

expect(table.options.menu.contextMenuWorkOnlyCell).toBe(false);
expect(contextMenuPlugin.runTime).toContain(ListTable.EVENT_TYPE.CONTEXTMENU_CANVAS);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as VTable from '@visactor/vtable';
import { ContextMenuPlugin } from '../../src/context-menu';

const CONTAINER_ID = 'vTable';

export function createTableInstance() {
const records = [
{ id: 1, name: 'alpha', value: 12 },
{ id: 2, name: 'beta', value: 34 }
];

const plugin = new ContextMenuPlugin({
contextMenuWorkOnlyCell: false,
bodyCellMenuItems: [
{ text: 'Copy from plugin', menuKey: 'copy' },
{ text: 'Canvas menu item', menuKey: 'canvas_menu_item' }
],
beforeShowAdjustMenuItems: (menuItems, _table, col, row) => {
if (col === -1 && row === -1) {
return [{ text: 'Canvas blank area', menuKey: 'canvas_blank_area' }, ...menuItems];
}
return menuItems;
}
});

const option: VTable.ListTableConstructorOptions = {
container: document.getElementById(CONTAINER_ID),
records,
columns: [
{ field: 'id', title: 'ID', width: 100 },
{ field: 'name', title: 'Name', width: 160 },
{ field: 'value', title: 'Value', width: 120 }
],
defaultRowHeight: 40,
defaultHeaderRowHeight: 40,
widthMode: 'standard',
heightMode: 'standard',
plugins: [plugin]
};

const tableInstance = new VTable.ListTable(option);
window.tableInstance = tableInstance;
return tableInstance;
}
4 changes: 4 additions & 0 deletions packages/vtable-plugins/demo/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ export const menus = [
path: 'context-menu',
name: 'context-menu'
},
{
path: 'context-menu',
name: 'issue-5215-context-menu-canvas'
},
{
path: 'context-menu',
name: 'issue-5214-reverse-selected-row-delete'
Expand Down
31 changes: 30 additions & 1 deletion packages/vtable-plugins/src/context-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface ContextMenuOptions {
headerCellMenuItems?: MenuItemOrSeparator[];
/** 表体菜单项 */
bodyCellMenuItems?: MenuItemOrSeparator[];
/** 右键菜单是否只工作在单元格上。默认 true;配置 false 时空白画布区域也弹出菜单。 */
contextMenuWorkOnlyCell?: boolean;
/** 自定义菜单样式 */
customMenuAttributions?: MenuAttributions;
/** 菜单点击回调。如果设置是函数,则忽略内部默认的菜单项处理逻辑。如果这里配置的是个对象(对象的key为menuKey),则有匹配的menuKey时忽略内部默认的菜单项处理逻辑,
Expand Down Expand Up @@ -56,7 +58,7 @@ export type MenuClickCallback = (args: MenuClickEventArgs, table: ListTable) =>
export class ContextMenuPlugin implements pluginsDefinition.IVTablePlugin {
id = `context-menu`;
name = 'Context Menu';
runTime = [TABLE_EVENT_TYPE.CONTEXTMENU_CELL, TABLE_EVENT_TYPE.PLUGIN_EVENT];
runTime = [TABLE_EVENT_TYPE.CONTEXTMENU_CELL, TABLE_EVENT_TYPE.CONTEXTMENU_CANVAS, TABLE_EVENT_TYPE.PLUGIN_EVENT];
pluginOptions: ContextMenuOptions;
table: ListTable;
/** 菜单管理器 */
Expand Down Expand Up @@ -156,6 +158,22 @@ export class ContextMenuPlugin implements pluginsDefinition.IVTablePlugin {
}
};

/**
* 处理空白画布右键菜单事件
*/
private handleContextMenuCanvas = (eventArgs: any, table: BaseTableAPI): void => {
let menuItems = this.pluginOptions.bodyCellMenuItems || [];
const { col = -1, row = -1 } = eventArgs;

if (this.pluginOptions.beforeShowAdjustMenuItems) {
menuItems = this.pluginOptions.beforeShowAdjustMenuItems(menuItems, table as ListTable, col, row);
}

if (menuItems.length > 0) {
this.showContextMenu(menuItems, eventArgs.event.clientX, eventArgs.event.clientY, col, row);
}
};

/**
* 处理插件事件
*/
Expand Down Expand Up @@ -191,6 +209,15 @@ export class ContextMenuPlugin implements pluginsDefinition.IVTablePlugin {
/**
* 运行插件
*/
init(_table: BaseTableAPI, options: BaseTableAPI['options']) {
if (this.pluginOptions.contextMenuWorkOnlyCell === false) {
options.menu = {
...options.menu,
contextMenuWorkOnlyCell: false
};
}
}

run(...args: any[]) {
const eventArgs = args[0];
const runTime = args[1];
Expand All @@ -203,6 +230,8 @@ export class ContextMenuPlugin implements pluginsDefinition.IVTablePlugin {
// 根据事件类型处理不同的右键菜单
if (runTime === TABLE_EVENT_TYPE.CONTEXTMENU_CELL) {
this.handleContextMenuCell(eventArgs, table);
} else if (runTime === TABLE_EVENT_TYPE.CONTEXTMENU_CANVAS) {
this.handleContextMenuCanvas(eventArgs, table);
} else if (runTime === TABLE_EVENT_TYPE.PLUGIN_EVENT) {
this.handlePluginEvent(eventArgs, table);
}
Expand Down
72 changes: 72 additions & 0 deletions packages/vtable/__tests__/listTable-checkbox-record-index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// @ts-nocheck
import { ListTable, TYPES } from '../src';
import { createDiv } from './dom';

global.__VERSION__ = 'none';

describe('ListTable checkbox record index api', () => {
let table: ListTable;

afterEach(() => {
table?.release();
document.body.innerHTML = '';
});

test('sets collapsed tree child checkbox state by record index path', () => {
table = new ListTable({
container: createDiv(),
columns: [
{
field: 'task',
title: 'Task',
tree: true,
cellType: 'checkbox',
headerType: 'checkbox'
}
],
records: [
{
task: { text: 'parent', checked: true },
hierarchyState: TYPES.HierarchyState.collapse,
children: [{ task: { text: 'child', checked: true } }]
}
],
enableCheckboxCascade: false
});

table.setCellCheckboxStateByRecordIndex([0, 0], 'task', false);

expect(table.stateManager.checkedState.get('0,0').task).toBe(false);
});

test('clears all checkbox states for a field', () => {
table = new ListTable({
container: createDiv(),
columns: [
{
field: 'task',
title: 'Task',
tree: true,
cellType: 'checkbox',
headerType: 'checkbox'
}
],
records: [
{
task: { text: 'parent', checked: true },
hierarchyState: TYPES.HierarchyState.collapse,
children: [{ task: { text: 'child', checked: true } }]
},
{ task: { text: 'sibling', checked: true } }
],
enableCheckboxCascade: false
});

table.clearAllCheckboxState('task');

expect(table.stateManager.checkedState.get('0').task).toBe(false);
expect(table.stateManager.checkedState.get('0,0').task).toBe(false);
expect(table.stateManager.checkedState.get('1').task).toBe(false);
expect(table.stateManager.headerCheckedState.task).toBe(false);
});
});
Loading
Loading