Skip to content
Open
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
47 changes: 47 additions & 0 deletions docs/assets/guide/en/plugin/context-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ContextMenu provides rich right-click menu features for VTable tables, supportin
- Support custom menu items and separators
- Support menu item grouping and submenus
- Support custom menu item click callbacks
- Support listening to menu item click events with menu item and cell position details
- Support menu item icons and shortcut hints
- Support numeric input in menu items (e.g., insert multiple rows/columns)

Expand Down Expand Up @@ -132,6 +133,48 @@ const contextMenuPlugin = new ContextMenuPlugin({
});
```

## Listen to Menu Item Click Events

If you need to observe user actions after a right-click menu item is clicked, listen to the `context_menu_click` event fired by `ContextMenuPlugin`. This event is only fired after the right-click menu plugin is installed and enabled. It is fired after the plugin finishes handling the menu item, so built-in operations such as freeze, insert, and delete have already taken effect.

```typescript
import * as VTable from '@visactor/vtable';
import { ContextMenuPlugin } from '@visactor/vtable-plugins';

const contextMenuPlugin = new ContextMenuPlugin();

const tableInstance = new VTable.ListTable({
container: document.getElementById('container'),
columns,
records,
plugins: [contextMenuPlugin]
});

tableInstance.on(VTable.TABLE_EVENT_TYPE.CONTEXT_MENU_CLICK, args => {
const { col, row, contextMenu } = args;

console.log('Context menu click position:', col, row);
console.log('Menu item:', contextMenu.menuKey, contextMenu.menuText);

if (contextMenu.menuKey === 'freeze_to_this_row_and_column') {
console.log('Current frozen counts:', tableInstance.frozenRowCount, tableInstance.frozenColCount);
}
});
```

Event argument description:

| Parameter | Type | Description |
| --- | --- | --- |
| `col` | number | Column index of the menu trigger position; `-1` when there is no corresponding cell |
| `row` | number | Row index of the menu trigger position; `-1` when there is no corresponding cell |
| `contextMenu.menuKey` | string | Unique key of the clicked menu item |
| `contextMenu.menuText` | string | Display text of the clicked menu item |
| `contextMenu.rowIndex` | number | Row index of the menu trigger position |
| `contextMenu.colIndex` | number | Column index of the menu trigger position |
| `contextMenu.cellValue` | any | Cell value at the menu trigger position |
| `contextMenu.inputValue` | number \| string | Input value of an input menu item |

## Complete Example

Here is a complete example showing how to create a table with right-click menu functionality:
Expand Down Expand Up @@ -244,6 +287,10 @@ const generateTestData = (count) => {

// Create table instance
const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option);

tableInstance.on(VTable.TABLE_EVENT_TYPE.CONTEXT_MENU_CLICK, args => {
console.log('Context menu item clicked:', args.contextMenu);
});
```


Expand Down
47 changes: 47 additions & 0 deletions docs/assets/guide/zh/plugin/context-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- 支持自定义菜单项和分隔线
- 支持菜单项分组和子菜单
- 支持自定义菜单项点击回调
- 支持监听菜单项点击事件,获取菜单项和单元格位置信息
- 支持菜单项图标和快捷键提示
- 支持菜单项中的数字输入(如插入多行/列)

Expand Down Expand Up @@ -132,6 +133,48 @@ const contextMenuPlugin = new ContextMenuPlugin({
});
```

## 监听菜单项点击事件

如果需要在右键菜单项点击后统一感知用户操作,可以监听 `ContextMenuPlugin` 触发的 `context_menu_click` 事件。该事件只有安装并启用右键菜单插件后才会触发,并且会在插件完成菜单项处理后触发,因此内置的冻结、插入、删除等菜单操作已经生效。

```typescript
import * as VTable from '@visactor/vtable';
import { ContextMenuPlugin } from '@visactor/vtable-plugins';

const contextMenuPlugin = new ContextMenuPlugin();

const tableInstance = new VTable.ListTable({
container: document.getElementById('container'),
columns,
records,
plugins: [contextMenuPlugin]
});

tableInstance.on(VTable.TABLE_EVENT_TYPE.CONTEXT_MENU_CLICK, args => {
const { col, row, contextMenu } = args;

console.log('右键菜单点击位置:', col, row);
console.log('菜单项信息:', contextMenu.menuKey, contextMenu.menuText);

if (contextMenu.menuKey === 'freeze_to_this_row_and_column') {
console.log('当前冻结行列数:', tableInstance.frozenRowCount, tableInstance.frozenColCount);
}
});
```

事件参数说明:

| 参数 | 类型 | 描述 |
| --- | --- | --- |
| `col` | number | 菜单触发位置对应的列号;没有对应单元格时为 `-1` |
| `row` | number | 菜单触发位置对应的行号;没有对应单元格时为 `-1` |
| `contextMenu.menuKey` | string | 被点击菜单项的唯一标识 |
| `contextMenu.menuText` | string | 被点击菜单项的展示文本 |
| `contextMenu.rowIndex` | number | 菜单触发位置对应的行号 |
| `contextMenu.colIndex` | number | 菜单触发位置对应的列号 |
| `contextMenu.cellValue` | any | 菜单触发位置对应的单元格值 |
| `contextMenu.inputValue` | number \| string | 输入型菜单项中的输入值 |

## 完整示例

以下是一个完整的示例,展示了如何创建具有右键菜单功能的表格:
Expand Down Expand Up @@ -244,6 +287,10 @@ const generateTestData = (count) => {

// 创建表格实例
const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option);

tableInstance.on(VTable.TABLE_EVENT_TYPE.CONTEXT_MENU_CLICK, args => {
console.log('右键菜单项点击:', args.contextMenu);
});
```


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,45 @@ describe('Context menu canvas option', () => {
);
});

test('ContextMenuPlugin emits context_menu_click after menu item click', () => {
const container = createDiv();
const contextMenuPlugin = new ContextMenuPlugin();

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

const fireListeners = jest.spyOn(table, 'fireListeners');

contextMenuPlugin['handleMenuClickCallback'](
{
menuKey: 'freeze_to_this_row_and_column',
menuText: '冻结到本行本列',
rowIndex: 1,
colIndex: 0,
cellValue: 1
},
table
);

expect(table.frozenRowCount).toBe(2);
expect(table.frozenColCount).toBe(1);
expect(fireListeners).toHaveBeenCalledWith(ListTable.EVENT_TYPE.CONTEXT_MENU_CLICK, {
col: 0,
row: 1,
contextMenu: {
menuKey: 'freeze_to_this_row_and_column',
menuText: '冻结到本行本列',
rowIndex: 1,
colIndex: 0,
cellValue: 1
}
});
});

test('ContextMenuPlugin init uses new options when added through updateOption', () => {
const container = createDiv();
const contextMenuPlugin = new ContextMenuPlugin({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as VTable from '@visactor/vtable';
import { ContextMenuPlugin } from '../../src/context-menu';

const CONTAINER_ID = 'vTable';

function createRecords() {
return Array.from({ length: 12 }, (_, index) => ({
id: index + 1,
name: `name-${index + 1}`,
city: ['Beijing', 'Shanghai', 'Shenzhen'][index % 3],
value: Math.round((index + 1) * 12.6)
}));
}

function updateStatus(table: VTable.ListTable, status: HTMLElement, eventArgs?: any) {
const lines = [
`frozenRowCount: ${table.frozenRowCount}`,
`frozenColCount: ${table.frozenColCount}`,
eventArgs
? `last context_menu_click: ${JSON.stringify(eventArgs.contextMenu)}`
: 'last context_menu_click: waiting for menu click'
];
status.textContent = lines.join('\n');
}

export function createTableInstance(status: HTMLElement) {
const plugin = new ContextMenuPlugin();
const option: VTable.ListTableConstructorOptions = {
container: document.getElementById(CONTAINER_ID),
records: createRecords(),
columns: [
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: 'Name', width: 160 },
{ field: 'city', title: 'City', width: 160 },
{ field: 'value', title: 'Value', width: 120 }
],
defaultRowHeight: 40,
defaultHeaderRowHeight: 40,
widthMode: 'standard',
heightMode: 'standard',
plugins: [plugin]
};

const tableInstance = new VTable.ListTable(option);
tableInstance.on(VTable.TABLE_EVENT_TYPE.CONTEXT_MENU_CLICK, args => {
updateStatus(tableInstance, status, args);
// eslint-disable-next-line no-console
console.log('context_menu_click', args);
});

updateStatus(tableInstance, status);
window.tableInstance = tableInstance;
return tableInstance;
}

export function createTable() {
const info = document.createElement('div');
info.style.margin = '10px';
info.style.padding = '10px';
info.style.border = '1px solid #ddd';
info.style.borderRadius = '4px';
info.style.backgroundColor = '#f9f9f9';
info.innerHTML = `
<h3>Issue 4655: context menu click event</h3>
<p>Right-click a body cell, open the freeze submenu, then click freeze or unfreeze.</p>
<p>The table should emit <code>context_menu_click</code> with menu details in <code>contextMenu</code>.</p>
`;
document.body.appendChild(info);

const status = document.createElement('pre');
status.style.margin = '10px';
status.style.padding = '10px';
status.style.border = '1px solid #ddd';
status.style.borderRadius = '4px';
status.style.backgroundColor = '#fff';
document.body.appendChild(status);

const container = document.createElement('div');
container.id = CONTAINER_ID;
container.style.width = '100%';
container.style.height = '460px';
document.body.appendChild(container);

return createTableInstance(status);
}
4 changes: 4 additions & 0 deletions packages/vtable-plugins/demo/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ export const menus = [
path: 'context-menu',
name: 'context-menu'
},
{
path: 'context-menu',
name: 'issue-4655-context-menu-click'
},
{
path: 'context-menu',
name: 'issue-5215-context-menu-canvas'
Expand Down
14 changes: 14 additions & 0 deletions packages/vtable-plugins/src/context-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,22 @@ export class ContextMenuPlugin implements pluginsDefinition.IVTablePlugin {
// 菜单项处理逻辑
this.handleMenuClick(args, table);
}
this.fireContextMenuClick(args, table);
};

private fireContextMenuClick(args: MenuClickEventArgs, table: ListTable): void {
const { colIndex, rowIndex, ...contextMenu } = args;
table.fireListeners(TABLE_EVENT_TYPE.CONTEXT_MENU_CLICK, {
col: colIndex ?? -1,
row: rowIndex ?? -1,
contextMenu: {
...contextMenu,
colIndex,
rowIndex
}
});
}

private showContextMenu(menuItems: MenuItemOrSeparator[], x: number, y: number, col: number, row: number): void {
// 显示菜单
this.menuManager.showMenu(
Expand Down
5 changes: 5 additions & 0 deletions packages/vtable/src/core/TABLE_EVENT_TYPE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export interface TableEvents {
* 画布右键事件
*/
CONTEXTMENU_CANVAS: 'contextmenu_canvas';
/**
* 右键菜单项点击事件
*/
CONTEXT_MENU_CLICK: 'context_menu_click';
/**
* 列宽调整事件
*/
Expand Down Expand Up @@ -287,6 +291,7 @@ export const TABLE_EVENT_TYPE: TableEvents = {
MOUSELEAVE_CELL: 'mouseleave_cell',
CONTEXTMENU_CELL: 'contextmenu_cell',
CONTEXTMENU_CANVAS: 'contextmenu_canvas',
CONTEXT_MENU_CLICK: 'context_menu_click',
RESIZE_COLUMN: 'resize_column',
RESIZE_COLUMN_END: 'resize_column_end',
RESIZE_ROW: 'resize_row',
Expand Down
4 changes: 3 additions & 1 deletion packages/vtable/src/ts-types/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
ListTableConstructorOptions,
PivotTableConstructorOptions
} from './table-engine';
import type { DropDownMenuEventArgs, MenuListItem, PivotInfo } from './menu';
import type { ContextMenuClickEventArgs, DropDownMenuEventArgs, MenuListItem, PivotInfo } from './menu';

import type { IDimensionInfo, MergeCellInfo, RectProps, SortOrder } from './common';
import type { IconFuncTypeEnum, CellInfo, HierarchyState, ColumnsDefine } from '.';
Expand Down Expand Up @@ -84,6 +84,7 @@ export interface TableEventHandlersEventArgumentMap {
mouseup_cell: MousePointerCellEvent;
contextmenu_cell: MousePointerMultiCellEvent;
contextmenu_canvas: MousePointerCellEvent;
context_menu_click: ContextMenuClickEventArgs;
before_keydown: KeydownEvent;
keydown: KeydownEvent;
scroll: {
Expand Down Expand Up @@ -395,6 +396,7 @@ export interface TableEventHandlersReturnMap {
mouseup_cell: void;
contextmenu_cell: void;
contextmenu_canvas: void;
context_menu_click: void;
before_keydown: void;
keydown: void;
scroll: void;
Expand Down
13 changes: 13 additions & 0 deletions packages/vtable/src/ts-types/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ export type DropDownMenuEventArgs = {
highlight: boolean;
} & DropDownMenuEventInfo;

export type ContextMenuClickEventArgs = {
col: number;
row: number;
contextMenu: {
menuKey: string;
menuText: string;
rowIndex?: number;
colIndex?: number;
cellValue?: any;
inputValue?: number | string;
};
};

export type DropDownMenuEventInfo = {
field?: FieldDef;
/**format之后的值 */
Expand Down
Loading