From 9e411b555ef320b856f67a0fcf0da8971de1c529 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Mon, 6 Dec 2021 13:40:34 +0800 Subject: [PATCH] feat(tooltip): enhance tooltip (#862) * feat(tooltip): enhance tooltip * feat(tooltip): update docs * feat(tooltip): fix test * feat(tooltip): fix test * feat(tooltip): fix test * feat(tooltip): update docs * feat(tooltip): update demo * feat(tooltip): update docs * feat(tooltip): support config corner and rename cell => data * feat(tooltip): update docs --- CONTRIBUTING.md | 10 +- README.en-US.md | 8 +- README.md | 18 ++- packages/s2-core/README.md | 15 +- .../click/data-cell-click-spec.ts | 43 +++++- .../unit/interaction/brush-selection-spec.ts | 39 +++++ .../unit/sheet-type/pivot-sheet-spec.ts | 112 +++++++++----- .../__tests__/unit/ui/tooltip/index-spec.ts | 15 +- .../__tests__/unit/utils/tooltip-spec.ts | 58 +++++++- .../s2-core/src/common/interface/events.ts | 7 +- .../src/common/interface/interaction.ts | 2 +- .../s2-core/src/common/interface/tooltip.ts | 11 +- packages/s2-core/src/facet/header/corner.ts | 4 +- .../base-interaction/click/data-cell-click.ts | 1 + .../src/interaction/brush-selection.ts | 16 +- .../s2-core/src/sheet-type/spread-sheet.ts | 20 ++- packages/s2-core/src/ui/tooltip/index.ts | 2 +- packages/s2-core/src/utils/tooltip.ts | 12 +- .../__tests__/spreadsheet/tooltip-spec.tsx | 67 ++++++++- packages/s2-react/playground/config.ts | 7 +- packages/s2-react/playground/index.tsx | 36 +++-- .../components/sheets/base-sheet/index.tsx | 108 +++++++++----- .../sheets/grid-analysis-sheet/index.tsx | 55 ++----- .../src/components/sheets/interface.ts | 8 +- .../components/sheets/table-sheet/index.tsx | 62 +++----- .../tooltip/components/operator/index.tsx | 9 +- .../src/components/tooltip/custom-tooltip.tsx | 22 +-- .../s2-react/src/components/tooltip/index.tsx | 4 +- .../src/components/tooltip/interface.ts | 8 +- s2-site/docs/api/basic-class/base-cell.en.md | 6 + s2-site/docs/api/basic-class/base-cell.zh.md | 96 ++++++++++++ .../docs/api/basic-class/interaction.en.md | 1 + s2-site/docs/api/basic-class/node.zh.md | 37 +++++ .../docs/api/basic-class/spreadsheet.en.md | 1 + .../docs/api/components/sheet-component.zh.md | 34 +++-- s2-site/docs/api/general/S2DataConfig.zh.md | 4 +- s2-site/docs/api/general/S2Options.zh.md | 2 +- s2-site/docs/common/custom-tooltip.zh.md | 28 ++-- .../docs/common/custom/customTreeItem.zh.md | 4 +- s2-site/docs/common/tooltip.zh.md | 10 +- .../advanced/custom/category-tree.zh.md | 30 ++-- s2-site/docs/manual/basic/tooltip.zh.md | 139 ++++++++++++++---- s2-site/docs/manual/contribution.zh.md | 10 +- s2-site/docs/manual/introduction.zh.md | 42 +++++- .../interaction/custom/demo/meta.json | 8 + .../custom/demo/row-col-hover-tooltip.ts | 56 +++++++ ...ltip.tsx => custom-click-show-tooltip.tsx} | 1 + .../demo/custom-hover-show-tooltip.tsx | 65 ++++++++ .../react-component/tooltip/demo/meta.json | 16 +- 49 files changed, 1040 insertions(+), 329 deletions(-) create mode 100644 s2-site/docs/api/basic-class/base-cell.en.md create mode 100644 s2-site/docs/api/basic-class/base-cell.zh.md create mode 100644 s2-site/docs/api/basic-class/node.zh.md create mode 100644 s2-site/examples/interaction/custom/demo/row-col-hover-tooltip.ts rename s2-site/examples/react-component/tooltip/demo/{custom-interaction-tooltip.tsx => custom-click-show-tooltip.tsx} (99%) create mode 100644 s2-site/examples/react-component/tooltip/demo/custom-hover-show-tooltip.tsx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a17763af12..5b95c1189f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ 2. 安装依赖:`yarn bootstrap` 或者 `yarn` 3. 提交你的改动,commit 请遵守 [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.uyo6cb12dt6w) 4. 如果你的改动是修复 bug, 还可以在提交信息后面加上 `close #issue 号`, 这样可以在 pr 合并后,可以自动关闭对应的 issue, 比如 `fix: render bug close #123` -5. 确保加上了对应的单元测试 +5. 确保加上了对应的单元测试和文档 (如有必要) 6. 所有 Lint 和 Test 检查通过后,并且 review 通过,我们会合并你的 pr. ![preview](https://gw.alipayobjects.com/zos/antfincdn/ssOxFrycD/86339514-5f9a-4101-8690-e47c97cd8af5.png) @@ -54,6 +54,8 @@ npm i -g yarn 1. `yarn bootstrap` 安装依赖 2. `yarn site:bootstrap` 安装网站相关依赖 3. `yarn site:start` 启动本地的 `S2` 网站 -4. `yarn core:start` 可视化的方式调试测试 -5. `yarn build` 构建 `S2`, 输出 `umd`, `esm` 和 `lib` 目录 -6. `yarn test` 运行单元格测试 +4. `yarn core:start` 可视化的方式调试核心层测试 (基于 jest-electron) +5. `yarn react:start` 可视化的方式调试组件层测试 (基于 jest-electron) +6. `yarn react:playground` 启动本地的组件层demo (基于 vite) +7. `yarn build` 构建 `@antv/s2` 和 `@antv/s2-react` 两个包, 分别输出 `umd`, `esm` 和 `lib` 目录 +8. `yarn test` 运行单元格测试 diff --git a/README.en-US.md b/README.en-US.md index 1a8cb77b56..864a82a1bb 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -197,14 +197,14 @@ yarn site:bootstrap yarn site:start ``` -## 👬 Contributors - -![https://github.com/antvis/s2/graphs/contributors](https://contrib.rocks/image?repo=antvis/s2) - ## 📧 Contact Us S2 +## 👬 Contributors + +![https://github.com/antvis/s2/graphs/contributors](https://contrib.rocks/image?repo=antvis/s2) + ## 📄 License MIT@[AntV](https://github.com/antvis). diff --git a/README.md b/README.md index e58181ef7a..4a9724a8b9 100644 --- a/README.md +++ b/README.md @@ -179,14 +179,26 @@ git clone git@github.com:antvis/S2.git cd S2 -yarn +# 安装依赖 +yarn # 或者 yarn bootstrap +# 调试 s2-core yarn core:start -# 本地启动官网 +# 调试 s2-react +yarn react:playground -yarn site:bootstrap +# 单元测试 +yarn test + +# 打包 +yarn build +# 代码风格和类型检测 +yarn lint + +# 本地启动官网 +yarn site:bootstrap yarn site:start ``` diff --git a/packages/s2-core/README.md b/packages/s2-core/README.md index fe78edc66a..ea0ceb972c 100644 --- a/packages/s2-core/README.md +++ b/packages/s2-core/README.md @@ -174,14 +174,23 @@ git clone git@github.com:antvis/S2.git cd S2 -yarn +# 安装依赖 +yarn # 或者 yarn bootstrap +# 调试 yarn core:start -# 本地启动官网 +# 单元测试 +yarn test -yarn site:bootstrap +# 打包 +yarn build + +# 代码风格和类型检测 +yarn lint +# 本地启动官网 +yarn site:bootstrap yarn site:start ``` diff --git a/packages/s2-core/__tests__/unit/interaction/base-interaction/click/data-cell-click-spec.ts b/packages/s2-core/__tests__/unit/interaction/base-interaction/click/data-cell-click-spec.ts index 2ba1e380f8..a26176a845 100644 --- a/packages/s2-core/__tests__/unit/interaction/base-interaction/click/data-cell-click-spec.ts +++ b/packages/s2-core/__tests__/unit/interaction/base-interaction/click/data-cell-click-spec.ts @@ -1,9 +1,17 @@ -import { createFakeSpreadSheet, createMockCellInfo } from 'tests/util/helpers'; +import { + createFakeSpreadSheet, + createMockCellInfo, + sleep, +} from 'tests/util/helpers'; import { Event as GEvent } from '@antv/g-canvas'; import { DataCellClick } from '@/interaction/base-interaction/click'; import { S2Options } from '@/common/interface'; import { SpreadSheet } from '@/sheet-type'; -import { InteractionStateName, S2Event } from '@/common/constant'; +import { + HOVER_FOCUS_TIME, + InteractionStateName, + S2Event, +} from '@/common/constant'; jest.mock('@/interaction/event-controller'); @@ -22,6 +30,9 @@ describe('Interaction Data Cell Click Tests', () => { trend: false, }, }, + interaction: { + hoverHighlight: true, + }, } as S2Options; s2.isTableMode = jest.fn(() => true); }); @@ -76,4 +87,32 @@ describe('Interaction Data Cell Click Tests', () => { record: mockCellData.data, }); }); + + test('should clear hover timer when data cell toggle click', async () => { + const showTooltipWithInfoSpy = jest + .spyOn(s2, 'showTooltipWithInfo') + .mockImplementation(() => {}); + + const event = { + stopPropagation() {}, + } as unknown as GEvent; + + // trigger hover + s2.emit(S2Event.DATA_CELL_HOVER, event); + + // select + s2.emit(S2Event.DATA_CELL_CLICK, event); + // unselect + s2.emit(S2Event.DATA_CELL_CLICK, event); + + // wait hover focus time trigger + await sleep(HOVER_FOCUS_TIME + 500); + + expect(s2.interaction.getCurrentStateName()).not.toEqual( + InteractionStateName.HOVER_FOCUS, + ); + + // only call show tooltip once for cell clicked + expect(showTooltipWithInfoSpy).toHaveReturnedTimes(1); + }); }); diff --git a/packages/s2-core/__tests__/unit/interaction/brush-selection-spec.ts b/packages/s2-core/__tests__/unit/interaction/brush-selection-spec.ts index d0e72ec976..6ab99fc043 100644 --- a/packages/s2-core/__tests__/unit/interaction/brush-selection-spec.ts +++ b/packages/s2-core/__tests__/unit/interaction/brush-selection-spec.ts @@ -160,6 +160,45 @@ describe('Interaction Brush Selection Tests', () => { ).toHaveBeenCalled(); }); + // https://github.com/antvis/S2/issues/852 + test('should clear brush selection state when mouse down and context menu clicked', () => { + const globalMouseUp = jest.fn(); + mockSpreadSheetInstance.on(S2Event.GLOBAL_MOUSE_UP, globalMouseUp); + + emitEvent(S2Event.DATA_CELL_MOUSE_DOWN, { + layerX: 10, + layerY: 20, + }); + emitEvent(S2Event.DATA_CELL_MOUSE_MOVE, { + layerX: 12, + layerY: 22, + }); + + expect(brushSelectionInstance.brushSelectionStage).toEqual( + InteractionBrushSelectionStage.DRAGGED, + ); + + emitEvent(S2Event.GLOBAL_CONTEXT_MENU, {}); + + expect(globalMouseUp).not.toHaveBeenCalled(); + expect(brushSelectionInstance.brushSelectionStage).toEqual( + InteractionBrushSelectionStage.UN_DRAGGED, + ); + expect( + brushSelectionInstance.spreadsheet.interaction.hasIntercepts([ + InterceptType.HOVER, + ]), + ).toBeFalsy(); + expect( + brushSelectionInstance.spreadsheet.interaction.hasIntercepts([ + InterceptType.BRUSH_SELECTION, + ]), + ).toBeFalsy(); + expect( + brushSelectionInstance.hidePrepareSelectMaskShape, + ).toHaveReturnedTimes(1); + }); + test('should skip brush selection if mouse move less than valid distance', () => { emitEvent(S2Event.DATA_CELL_MOUSE_MOVE, {}); diff --git a/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts b/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts index 5049933e11..e705ac405d 100644 --- a/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts +++ b/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts @@ -62,10 +62,36 @@ describe('PivotSheet Tests', () => { return { [CellTypes.ROW_CELL]: 'row', [CellTypes.COL_CELL]: 'col', - [CellTypes.DATA_CELL]: 'cell', + [CellTypes.DATA_CELL]: 'data', + [CellTypes.CORNER_CELL]: 'corner', }[cellType]; }; + test('should support callback tooltip content for string', () => { + s2.showTooltip({ + position: { + x: 10, + y: 10, + }, + content: () => 'custom callback content', + }); + + expect(s2.tooltip.container.innerHTML).toEqual('custom callback content'); + }); + + test('should support callback tooltip content for element', () => { + const content = document.createElement('div'); + s2.showTooltip({ + position: { + x: 10, + y: 10, + }, + content: () => content, + }); + + expect(s2.tooltip.container.contains(content)).toBeTruthy(); + }); + test('should init tooltip', () => { s2.showTooltip({ position: { x: 0, y: 0 } }); @@ -157,7 +183,12 @@ describe('PivotSheet Tests', () => { expect(sheet.tooltip.container.innerHTML).toEqual(tooltipContent); }); - test.each([CellTypes.ROW_CELL, CellTypes.COL_CELL, CellTypes.DATA_CELL])( + test.each([ + CellTypes.ROW_CELL, + CellTypes.COL_CELL, + CellTypes.DATA_CELL, + CellTypes.CORNER_CELL, + ])( 'should use %o tooltip content from tooltip config first for string content', (cellType) => { const tooltipContent = `${cellType} tooltip content`; @@ -417,43 +448,6 @@ describe('PivotSheet Tests', () => { expect(afterRender).toHaveBeenCalledTimes(1); }); - test('should destroy sheet', () => { - const facetDestroySpy = jest - .spyOn(s2.facet, 'destroy') - .mockImplementation(() => {}); - - const hdAdapterDestroySpy = jest - .spyOn(s2.hdAdapter, 'destroy') - .mockImplementation(() => {}); - - s2.render(false); - - s2.store.set('test', 111); - s2.tooltip.container.classList.add('destroy-test'); - s2.interaction.addIntercepts([InterceptType.HOVER]); - s2.interaction.interactions.set('test-interaction', null); - s2.destroy(); - - // clear store - expect(s2.store.size()).toEqual(0); - // clear interaction - expect(s2.interaction.getState()).toEqual({ - cells: [], - force: false, - }); - expect(s2.interaction.getHoverTimer()).toBeNull(); - expect(s2.interaction.interactions.size).toEqual(0); - expect(s2.interaction.intercepts.size).toEqual(0); - expect(s2.interaction.eventController.canvasEventHandlers).toHaveLength(0); - expect(s2.interaction.eventController.domEventListeners).toHaveLength(0); - // destroy tooltip - expect(s2.tooltip.container.children).toHaveLength(0); - // destroy facet - expect(facetDestroySpy).toHaveBeenCalledTimes(1); - // destroy hdAdapter - expect(hdAdapterDestroySpy).toHaveBeenCalledTimes(1); - }); - test('should updatePagination', () => { s2.updatePagination({ current: 2, @@ -665,6 +659,46 @@ describe('PivotSheet Tests', () => { expect(renderSpy).toHaveBeenCalledTimes(2); }); + test('should destroy sheet', () => { + const facetDestroySpy = jest + .spyOn(s2.facet, 'destroy') + .mockImplementation(() => {}); + + const hdAdapterDestroySpy = jest + .spyOn(s2.hdAdapter, 'destroy') + .mockImplementation(() => {}); + + s2.render(false); + + s2.store.set('test', 111); + s2.tooltip.container.classList.add('destroy-test'); + s2.interaction.addIntercepts([InterceptType.HOVER]); + s2.interaction.interactions.set('test-interaction', null); + s2.container.on('test-event', () => {}); + s2.destroy(); + + // clear store + expect(s2.store.size()).toEqual(0); + // clear interaction + expect(s2.interaction.getState()).toEqual({ + cells: [], + force: false, + }); + expect(s2.interaction.getHoverTimer()).toBeNull(); + expect(s2.interaction.interactions.size).toEqual(0); + expect(s2.interaction.intercepts.size).toEqual(0); + expect(s2.interaction.eventController.canvasEventHandlers).toHaveLength(0); + expect(s2.interaction.eventController.domEventListeners).toHaveLength(0); + // destroy tooltip + expect(s2.tooltip.container.children).toHaveLength(0); + // destroy facet + expect(facetDestroySpy).toHaveBeenCalledTimes(1); + // destroy hdAdapter + expect(hdAdapterDestroySpy).toHaveBeenCalledTimes(1); + // clear all canvas events + expect(s2.getEvents()).toEqual({}); + }); + describe('Test Layout by dataCfg fields', () => { beforeEach(() => { s2.destroy(); diff --git a/packages/s2-core/__tests__/unit/ui/tooltip/index-spec.ts b/packages/s2-core/__tests__/unit/ui/tooltip/index-spec.ts index d99f239d9d..51c70cca24 100644 --- a/packages/s2-core/__tests__/unit/ui/tooltip/index-spec.ts +++ b/packages/s2-core/__tests__/unit/ui/tooltip/index-spec.ts @@ -1,4 +1,4 @@ -import { createFakeSpreadSheet } from 'tests/util/helpers'; +import { createFakeSpreadSheet, sleep } from 'tests/util/helpers'; import type { SpreadSheet } from '@/sheet-type/spread-sheet'; import { BaseTooltip } from '@/ui/tooltip'; import { TOOLTIP_CONTAINER_CLS, TOOLTIP_POSITION_OFFSET } from '@/common'; @@ -127,30 +127,30 @@ describe('Tooltip Tests', () => { expect(tooltip.container.innerHTML).toEqual('text'); }); - test('should display custom dom element', () => { + test('should display custom dom element', async () => { const element1 = document.createElement('span'); const element2 = document.createElement('span'); + const position = { x: 10, y: 10, }; - element1.className = 'text1'; - element2.className = 'text2'; - tooltip.show({ position, content: element1, }); - expect(tooltip.container.querySelector('.text1')).toBeTruthy(); + expect(tooltip.container.contains(element1)).toBeTruthy(); tooltip.show({ position, content: element2, }); - expect(tooltip.container.querySelector('.text2')).toBeTruthy(); + await sleep(500); + + expect(tooltip.container.contains(element2)).toBeTruthy(); expect(tooltip.container.children).toHaveLength(1); }); @@ -193,6 +193,7 @@ describe('Tooltip Tests', () => { }); expect(tooltip.container.querySelector('.text')).toBeTruthy(); + expect(tooltip.container.contains(element)).toBeTruthy(); }); test('should replace tooltip content by call method', () => { diff --git a/packages/s2-core/__tests__/unit/utils/tooltip-spec.ts b/packages/s2-core/__tests__/unit/utils/tooltip-spec.ts index eb8d94aec3..7535610338 100644 --- a/packages/s2-core/__tests__/unit/utils/tooltip-spec.ts +++ b/packages/s2-core/__tests__/unit/utils/tooltip-spec.ts @@ -1,7 +1,16 @@ import { createFakeSpreadSheet } from 'tests/util/helpers'; import { BBox } from '@antv/g-canvas'; -import { getAutoAdjustPosition, setContainerStyle } from '@/utils/tooltip'; -import { SpreadSheet, TOOLTIP_POSITION_OFFSET } from '@/index'; +import { + getAutoAdjustPosition, + setContainerStyle, + getTooltipOptions, +} from '@/utils/tooltip'; +import { + CellTypes, + SpreadSheet, + Tooltip, + TOOLTIP_POSITION_OFFSET, +} from '@/index'; import { BaseFacet } from '@/facet/base-facet'; describe('Tooltip Utils Tests', () => { @@ -186,6 +195,51 @@ describe('Tooltip Utils Tests', () => { }); }); + describe('Get Tooltip Options Tests', () => { + const getCellNameByType = (cellType: CellTypes) => { + return { + [CellTypes.ROW_CELL]: 'row', + [CellTypes.COL_CELL]: 'col', + [CellTypes.DATA_CELL]: 'data', + [CellTypes.CORNER_CELL]: 'corner', + }[cellType]; + }; + test.each([ + CellTypes.ROW_CELL, + CellTypes.COL_CELL, + CellTypes.DATA_CELL, + CellTypes.CORNER_CELL, + ])( + 'should use %o tooltip content from tooltip config first for string content', + (cellType) => { + const tooltipContent = `${cellType} tooltip content`; + const defaultTooltipContent = 'default tooltip content'; + const type = getCellNameByType(cellType); + + const tooltip: Tooltip = { + content: defaultTooltipContent, + [type]: { + content: tooltipContent, + }, + }; + + const spreadsheet = { + getCellType: () => cellType, + options: { + tooltip, + }, + } as unknown as SpreadSheet; + + expect(getTooltipOptions(spreadsheet, {} as Event)).toEqual({ + content: tooltipContent, + [type]: { + content: tooltipContent, + }, + }); + }, + ); + }); + test('should set container style', () => { const container = document.createElement('div'); diff --git a/packages/s2-core/src/common/interface/events.ts b/packages/s2-core/src/common/interface/events.ts index f966fdafc7..da651abb5c 100644 --- a/packages/s2-core/src/common/interface/events.ts +++ b/packages/s2-core/src/common/interface/events.ts @@ -1,6 +1,7 @@ // 这里存放 emit 事件 透出的信息 -import { Event } from '@antv/g-canvas'; +import { Event as GEvent } from '@antv/g-canvas'; +import { S2CellType } from './interaction'; import { Node } from '@/facet/layout/node'; export interface CellScrollPosition { @@ -21,7 +22,7 @@ export type LayoutRow = [number, string, string]; export type LayoutCol = [number, string, string]; export interface TargetCellInfo { - target: any; - event: Event; + target: S2CellType; + event: GEvent; viewMeta: Node; } diff --git a/packages/s2-core/src/common/interface/interaction.ts b/packages/s2-core/src/common/interface/interaction.ts index 150b835d94..1542d087a6 100644 --- a/packages/s2-core/src/common/interface/interaction.ts +++ b/packages/s2-core/src/common/interface/interaction.ts @@ -1,4 +1,4 @@ -import { SimpleBBox } from '@antv/g-canvas'; +import type { SimpleBBox } from '@antv/g-canvas'; import { InteractionStateName, CellTypes, InterceptType } from '../constant'; import { ViewMeta } from './basic'; import { diff --git a/packages/s2-core/src/common/interface/tooltip.ts b/packages/s2-core/src/common/interface/tooltip.ts index 5cdffd13f7..cf687d9c61 100644 --- a/packages/s2-core/src/common/interface/tooltip.ts +++ b/packages/s2-core/src/common/interface/tooltip.ts @@ -1,5 +1,6 @@ +import type { Event as CanvasEvent } from '@antv/g-canvas'; import type { SpreadSheet } from '@/sheet-type'; -import type { SortParam } from '@/common/interface'; +import type { S2CellType, SortParam } from '@/common/interface'; import type { BaseTooltip } from '@/ui/tooltip'; export type TooltipDataItem = Record; @@ -80,7 +81,8 @@ export type TooltipShowOptions = { data?: TooltipData; cellInfos?: TooltipDataItem[]; options?: TooltipOptions; - content?: T; + content?: ((cell: S2CellType) => T) | T; + event?: CanvasEvent | MouseEvent; }; export type TooltipData = { @@ -140,7 +142,7 @@ export type TooltipContentType = Element | string; export interface BaseTooltipConfig { readonly showTooltip?: boolean; // Custom content - readonly content?: T; + readonly content?: TooltipShowOptions['content']; // Tooltip operation readonly operation?: TooltipOperation; readonly autoAdjustBoundary?: TooltipAutoAdjustBoundary; @@ -150,7 +152,8 @@ export interface BaseTooltipConfig { export interface Tooltip extends BaseTooltipConfig { readonly row?: BaseTooltipConfig; readonly col?: BaseTooltipConfig; - readonly cell?: BaseTooltipConfig; + readonly corner?: BaseTooltipConfig; + readonly data?: BaseTooltipConfig; } export interface TooltipOperation { diff --git a/packages/s2-core/src/facet/header/corner.ts b/packages/s2-core/src/facet/header/corner.ts index 4810a839c9..6b3198a8a6 100644 --- a/packages/s2-core/src/facet/header/corner.ts +++ b/packages/s2-core/src/facet/header/corner.ts @@ -1,8 +1,8 @@ import { Group, Point } from '@antv/g-canvas'; import { get, includes, isEmpty } from 'lodash'; -import { CornerBBox } from '../bbox/cornerBBox'; -import { PanelBBox } from '../bbox/panelBBox'; import { BaseHeader, BaseHeaderConfig } from './base'; +import { PanelBBox } from '@/facet/bbox/panelBBox'; +import { CornerBBox } from '@/facet/bbox/cornerBBox'; import { translateGroupX } from '@/facet/utils'; import { CornerCell } from '@/cell/corner-cell'; import { KEY_SERIES_NUMBER_NODE } from '@/common/constant'; diff --git a/packages/s2-core/src/interaction/base-interaction/click/data-cell-click.ts b/packages/s2-core/src/interaction/base-interaction/click/data-cell-click.ts index 758ced7cf1..0e76bb64c9 100644 --- a/packages/s2-core/src/interaction/base-interaction/click/data-cell-click.ts +++ b/packages/s2-core/src/interaction/base-interaction/click/data-cell-click.ts @@ -29,6 +29,7 @@ export class DataCellClick extends BaseEvent implements BaseEventImplement { return; } + interaction.clearHoverTimer(); this.emitLinkFieldClickEvent(event); const cell: DataCell = this.spreadsheet.getCell(event.target); diff --git a/packages/s2-core/src/interaction/brush-selection.ts b/packages/s2-core/src/interaction/brush-selection.ts index 5426884628..718cd8f4cd 100644 --- a/packages/s2-core/src/interaction/brush-selection.ts +++ b/packages/s2-core/src/interaction/brush-selection.ts @@ -105,7 +105,7 @@ export class BrushSelection extends BaseEvent implements BaseEventImplement { } private bindMouseUp() { - // The constant 'GLOBAL_MOUSE_UP' is used to monitor the event of the mouse moving off the table. + // 使用全局的 mouseup, 而不是 canvas 的 mouse up 防止刷选过程中移出表格区域时无法响应事件 this.spreadsheet.on(S2Event.GLOBAL_MOUSE_UP, (event) => { event.preventDefault(); @@ -119,9 +119,19 @@ export class BrushSelection extends BaseEvent implements BaseEventImplement { getActiveCellsTooltipData(this.spreadsheet), ); } - this.hidePrepareSelectMaskShape(); - this.setBrushSelectionStage(InteractionBrushSelectionStage.UN_DRAGGED); + this.resetDrag(); }); + + // 刷选过程中右键弹出系统菜单时, 应该重置刷选, 防止系统菜单关闭后 mouse up 未相应依然是刷选状态 + this.spreadsheet.on(S2Event.GLOBAL_CONTEXT_MENU, () => { + this.spreadsheet.interaction.removeIntercepts([InterceptType.HOVER]); + this.resetDrag(); + }); + } + + private resetDrag() { + this.hidePrepareSelectMaskShape(); + this.setBrushSelectionStage(InteractionBrushSelectionStage.UN_DRAGGED); } private isValidBrushSelection() { diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index cd2dda5c11..b250325e43 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -3,9 +3,11 @@ import { Canvas, Event as CanvasEvent, IGroup } from '@antv/g-canvas'; import { clone, forEach, + forIn, get, includes, isEmpty, + isFunction, isString, merge, once, @@ -253,7 +255,14 @@ export abstract class SpreadSheet extends EE { public showTooltip( showOptions: TooltipShowOptions, ) { - this.tooltip.show?.(showOptions); + const { content, event } = showOptions; + const cell = this.getCell(event?.target); + const displayContent = isFunction(content) ? content(cell) : content; + + this.tooltip.show?.({ + ...showOptions, + content: displayContent, + }); } public showTooltipWithInfo( @@ -282,6 +291,7 @@ export abstract class SpreadSheet extends EE { enterable: true, ...options, }, + event, content, }); } @@ -345,6 +355,7 @@ export abstract class SpreadSheet extends EE { this.interaction.destroy(); this.store.clear(); this.destroyTooltip(); + this.clearCanvasEvent(); } /** @@ -565,4 +576,11 @@ export abstract class SpreadSheet extends EE { } hideColumnsByThunkGroup(this, hiddenColumnFields, true); }); + + private clearCanvasEvent() { + const canvasEvents = this.getEvents(); + forIn(canvasEvents, (_, event: keyof EmitterType) => { + this.off(event); + }); + } } diff --git a/packages/s2-core/src/ui/tooltip/index.ts b/packages/s2-core/src/ui/tooltip/index.ts index d8667817cb..307e26ed36 100644 --- a/packages/s2-core/src/ui/tooltip/index.ts +++ b/packages/s2-core/src/ui/tooltip/index.ts @@ -48,7 +48,7 @@ export class BaseTooltip { this.options = showOptions as unknown as TooltipShowOptions; - this.renderContent(content); + this.renderContent(content as T); const { x, y } = getAutoAdjustPosition({ spreadsheet: this.spreadsheet, diff --git a/packages/s2-core/src/utils/tooltip.ts b/packages/s2-core/src/utils/tooltip.ts index 032bf8d9ff..8051d8249c 100644 --- a/packages/s2-core/src/utils/tooltip.ts +++ b/packages/s2-core/src/utils/tooltip.ts @@ -536,12 +536,13 @@ export const getActiveCellsTooltipData = ( export const getTooltipOptionsByCellType = ( cellTooltipConfig: Tooltip = {}, cellType: CellTypes, -) => { +): Tooltip => { const getOptionsByCell = (cellConfig: BaseTooltipConfig) => { return { ...cellTooltipConfig, ...cellConfig }; }; - const { col, row, cell } = cellTooltipConfig; + const { col, row, data, corner } = cellTooltipConfig; + if (cellType === CellTypes.COL_CELL) { return getOptionsByCell(col); } @@ -549,7 +550,10 @@ export const getTooltipOptionsByCellType = ( return getOptionsByCell(row); } if (cellType === CellTypes.DATA_CELL) { - return getOptionsByCell(cell); + return getOptionsByCell(data); + } + if (cellType === CellTypes.CORNER_CELL) { + return getOptionsByCell(corner); } return { ...cellTooltipConfig }; @@ -558,7 +562,7 @@ export const getTooltipOptionsByCellType = ( export const getTooltipOptions = ( spreadsheet: SpreadSheet, event: CanvasEvent | MouseEvent | Event, -) => { +): Tooltip => { const cellType = spreadsheet.getCellType?.(event.target); return getTooltipOptionsByCellType(spreadsheet.options.tooltip, cellType); }; diff --git a/packages/s2-react/__tests__/spreadsheet/tooltip-spec.tsx b/packages/s2-react/__tests__/spreadsheet/tooltip-spec.tsx index c5d1b2a012..10ba78ebb8 100644 --- a/packages/s2-react/__tests__/spreadsheet/tooltip-spec.tsx +++ b/packages/s2-react/__tests__/spreadsheet/tooltip-spec.tsx @@ -50,7 +50,7 @@ describe('SheetComponent Tooltip Tests', () => { await sleep(1000); const showTooltip = () => { - s2.tooltip.show({ position: { x: 0, y: 0 }, content: '111' }); + s2.showTooltip({ position: { x: 0, y: 0 }, content: '111' }); }; Array.from({ length: 10 }).forEach(() => { @@ -86,4 +86,69 @@ describe('SheetComponent Tooltip Tests', () => { expect(s2.tooltip.container.querySelector('#custom-content')).toBeTruthy(); }); + + test('should support callback tooltip content for string', async () => { + await sleep(1000); + + s2.showTooltip({ + position: { + x: 10, + y: 10, + }, + content: () => 'custom callback content', + }); + + expect(s2.tooltip.container.innerHTML).toEqual('custom callback content'); + }); + + test('should support callback tooltip content for element', async () => { + await sleep(1000); + + const content = ( +
custom callback content
+ ); + + s2.showTooltip({ + position: { + x: 10, + y: 10, + }, + content: () => content, + }); + + expect( + s2.tooltip.container.querySelector('#custom-callback-content'), + ).toBeTruthy(); + }); + + // https://github.com/antvis/S2/issues/852 + test('should not show unique key prop warning', async () => { + await sleep(1000); + + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + const content = ( +
+
content1
+
content2
+
+ ); + + Array.from({ length: 3 }).forEach(() => { + s2.showTooltip({ + position: { + x: 10, + y: 10, + }, + content, + }); + }); + + expect(errorSpy).not.toThrowError(); + expect(warnSpy).not.toThrowError(); + + errorSpy.mockRestore(); + warnSpy.mockRestore(); + }); }); diff --git a/packages/s2-react/playground/config.ts b/packages/s2-react/playground/config.ts index bb278b672c..8e8da152c8 100644 --- a/packages/s2-react/playground/config.ts +++ b/packages/s2-react/playground/config.ts @@ -1,5 +1,6 @@ -import { S2DataConfig, S2Options } from '@antv/s2'; -import { SliderSingleProps } from 'antd'; +import type { S2DataConfig, S2Options } from '@antv/s2'; +import type { SliderSingleProps } from 'antd'; +import React from 'react'; import { data, totalData, meta } from '../__tests__/data/mock-dataset.json'; export const tableSheetDataCfg: Partial = { @@ -24,7 +25,7 @@ export const pivotSheetDataCfg: S2DataConfig = { }, }; -export const s2Options: S2Options = { +export const s2Options: S2Options = { debug: true, width: 600, height: 600, diff --git a/packages/s2-react/playground/index.tsx b/packages/s2-react/playground/index.tsx index 00bb33d092..ab5b282c79 100644 --- a/packages/s2-react/playground/index.tsx +++ b/packages/s2-react/playground/index.tsx @@ -38,9 +38,14 @@ import './index.less'; import 'antd/dist/antd.min.css'; import '@antv/s2/esm/style.css'; -const CustomTooltip = () =>
自定义 Tooltip
; +const CustomTooltip = () => ( +
+ 自定义 Tooltip
1
+
2
+
+); -const ColTooltip =
custom colTooltip
; +const CustomColTooltip = () =>
custom colTooltip
; const ActionIconTooltip = ({ name }) =>
{name} Tooltip
; @@ -65,7 +70,7 @@ function MainLayout() { const [showPagination, setShowPagination] = React.useState(false); const [showTotals, setShowTotals] = React.useState(false); const [themeName, setThemeName] = React.useState('default'); - const [showCustomTooltip, setShowCustomTooltip] = React.useState(false); + const [showCustomTooltip, setShowCustomTooltip] = React.useState(true); const [adaptive, setAdaptive] = React.useState(false); const [showResizeArea, setShowResizeArea] = React.useState(false); const [options, setOptions] = @@ -142,12 +147,20 @@ function MainLayout() { } }; - const onColCellClick = ({ event }: TargetCellInfo) => { - console.log('onColCellClick: ', event); + const logHandler = (name: string) => (cellInfo: TargetCellInfo) => { + if (options.debug) { + console.debug(name, cellInfo); + } + }; + + const onColCellClick = (cellInfo: TargetCellInfo) => { + console.log('cellInfo: ', cellInfo); + logHandler('onColCellClick')(cellInfo); if (!options.showDefaultHeaderActionIcon) { + const { event } = cellInfo; s2Ref.current.showTooltip({ position: { x: event.clientX, y: event.clientY }, - content: ColTooltip, + content: , }); } }; @@ -266,12 +279,6 @@ function MainLayout() { updateOptions({ debug: checked }); }} /> - {JSON.stringify(options, undefined, 2)}} - > - -
@@ -522,9 +529,12 @@ function MainLayout() { header={{ title: 'Title', description: 'description', - extra: [], + extra: , }} onColCellClick={onColCellClick} + onRowCellClick={logHandler('onRowCellClick')} + onCornerCellClick={logHandler('onCornerCellClick')} + onDataCellClick={logHandler('onDataCellClick')} /> )}
diff --git a/packages/s2-react/src/components/sheets/base-sheet/index.tsx b/packages/s2-react/src/components/sheets/base-sheet/index.tsx index 5c89fad683..7058adf21a 100644 --- a/packages/s2-react/src/components/sheets/base-sheet/index.tsx +++ b/packages/s2-react/src/components/sheets/base-sheet/index.tsx @@ -1,6 +1,6 @@ import { Event as GEvent } from '@antv/g-canvas'; import { Spin } from 'antd'; -import { forIn, isEmpty, isFunction, merge } from 'lodash'; +import { forIn, isEmpty, isFunction } from 'lodash'; import React, { memo, StrictMode, useEffect, useRef, useState } from 'react'; import { S2Event, @@ -19,6 +19,7 @@ import { getBaseCellData, getTooltipOptions, getSafetyDataConfig, + customMerge, } from '@antv/s2'; import { DrillDown } from '@/components/drill-down'; import { Header } from '@/components/header'; @@ -49,12 +50,27 @@ export const BaseSheet: React.FC = memo((props) => { onRowCellScroll, onColCellScroll, onCellScroll, + onRowCellClick, - onColCellClick, - onMergedCellsClick, + onRowCellHover, onRowCellDoubleClick, + + onColCellClick, + onColCellHover, onColCellDoubleClick, + + onDataCellClick, + onDataCellHover, + onDataCellDoubleClick, + + onMergedCellClick, + onMergedCellHover, onMergedCellsDoubleClick, + + onCornerCellDoubleClick, + onCornerCellHover, + onCornerCellClick, + onDataCellMouseUp, getSpreadSheet, partDrillDown, @@ -113,27 +129,60 @@ export const BaseSheet: React.FC = memo((props) => { string, (...args: unknown[]) => unknown > = { - [S2Event.DATA_CELL_MOUSE_UP]: (ev: GEvent) => { - onDataCellMouseUp?.(getBaseCellData(ev)); + [S2Event.DATA_CELL_MOUSE_UP]: (event: GEvent) => { + onDataCellMouseUp?.(getBaseCellData(event)); + }, + + [S2Event.MERGED_CELLS_CLICK]: (event: GEvent) => { + onMergedCellClick?.(getBaseCellData(event)); }, - [S2Event.MERGED_CELLS_CLICK]: (ev: GEvent) => { - onMergedCellsClick?.(getBaseCellData(ev)); + [S2Event.MERGED_CELLS_HOVER]: (event: GEvent) => { + onMergedCellHover?.(getBaseCellData(event)); }, - [S2Event.ROW_CELL_CLICK]: (ev: GEvent) => { - onRowCellClick?.(getBaseCellData(ev)); + [S2Event.MERGED_CELLS_DOUBLE_CLICK]: (event: GEvent) => { + onMergedCellsDoubleClick?.(getBaseCellData(event)); }, - [S2Event.COL_CELL_CLICK]: (ev: GEvent) => { - onColCellClick?.(getBaseCellData(ev)); + + [S2Event.ROW_CELL_CLICK]: (event: GEvent) => { + onRowCellClick?.(getBaseCellData(event)); }, - [S2Event.MERGED_CELLS_DOUBLE_CLICK]: (ev: GEvent) => { - onMergedCellsDoubleClick?.(getBaseCellData(ev)); + [S2Event.ROW_CELL_HOVER]: (event: GEvent) => { + onRowCellHover?.(getBaseCellData(event)); + }, + [S2Event.ROW_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onRowCellDoubleClick?.(getBaseCellData(event)); + }, + + [S2Event.COL_CELL_CLICK]: (event: GEvent) => { + onColCellClick?.(getBaseCellData(event)); }, - [S2Event.ROW_CELL_DOUBLE_CLICK]: (ev: GEvent) => { - onRowCellDoubleClick?.(getBaseCellData(ev)); + [S2Event.COL_CELL_HOVER]: (event: GEvent) => { + onColCellHover?.(getBaseCellData(event)); }, - [S2Event.COL_CELL_DOUBLE_CLICK]: (ev: GEvent) => { - onColCellDoubleClick?.(getBaseCellData(ev)); + [S2Event.COL_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onColCellDoubleClick?.(getBaseCellData(event)); }, + + [S2Event.DATA_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onDataCellDoubleClick?.(getBaseCellData(event)); + }, + [S2Event.DATA_CELL_CLICK]: (event: GEvent) => { + onDataCellClick?.(getBaseCellData(event)); + }, + [S2Event.DATA_CELL_HOVER]: (event: GEvent) => { + onDataCellHover?.(getBaseCellData(event)); + }, + + [S2Event.CORNER_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onCornerCellDoubleClick?.(getBaseCellData(event)); + }, + [S2Event.CORNER_CELL_HOVER]: (event: GEvent) => { + onCornerCellHover?.(getBaseCellData(event)); + }, + [S2Event.CORNER_CELL_CLICK]: (event: GEvent) => { + onCornerCellClick?.(getBaseCellData(event)); + }, + [S2Event.LAYOUT_ROW_NODE_BORDER_REACHED]: ( targetRow: TargetLayoutNode, ) => { @@ -157,23 +206,7 @@ export const BaseSheet: React.FC = memo((props) => { }); }; - const unBindEvent = () => { - [ - S2Event.LAYOUT_AFTER_HEADER_LAYOUT, - S2Event.LAYOUT_ROW_NODE_BORDER_REACHED, - S2Event.LAYOUT_COL_NODE_BORDER_REACHED, - S2Event.LAYOUT_CELL_SCROLL, - S2Event.RANGE_SORT, - S2Event.MERGED_CELLS_CLICK, - S2Event.ROW_CELL_CLICK, - S2Event.COL_CELL_CLICK, - S2Event.DATA_CELL_MOUSE_UP, - ].forEach((eventName) => { - baseSpreadsheet.current.off(eventName); - }); - }; - - const iconClickCallback = ( + const onIconClick = ( sheetInstance: SpreadSheet, cacheDrillFields?: string[], disabledFields?: string[], @@ -213,7 +246,7 @@ export const BaseSheet: React.FC = memo((props) => { // 处理下钻参数 if (partDrillDown) { - curOptions = handleDrillDownIcon(curProps, curSheet, iconClickCallback); + curOptions = handleDrillDownIcon(curProps, curSheet, onIconClick); } curSheet.setOptions(curOptions); @@ -261,7 +294,6 @@ export const BaseSheet: React.FC = memo((props) => { useEffect(() => { buildSpreadSheet(); return () => { - unBindEvent(); baseSpreadsheet.current.destroy(); }; }, []); @@ -328,13 +360,13 @@ export const BaseSheet: React.FC = memo((props) => { useEffect(() => { if (!ownSpreadsheet || isEmpty(options.pagination)) return; - const newOptions = merge({}, options, { + const newOptions = customMerge(options, { pagination: { current, pageSize, }, }); - const newProps = merge({}, props, { + const newProps = customMerge(props, { options: newOptions, }); setOptions(ownSpreadsheet, newProps); diff --git a/packages/s2-react/src/components/sheets/grid-analysis-sheet/index.tsx b/packages/s2-react/src/components/sheets/grid-analysis-sheet/index.tsx index b4666b4163..9026a3e980 100644 --- a/packages/s2-react/src/components/sheets/grid-analysis-sheet/index.tsx +++ b/packages/s2-react/src/components/sheets/grid-analysis-sheet/index.tsx @@ -1,6 +1,5 @@ // TODO 抽取不同sheet组件的公共方法 import React, { useEffect, useState } from 'react'; -import { isFunction } from 'lodash'; import { Spin } from 'antd'; import { Event } from '@antv/g-canvas'; import { @@ -33,7 +32,7 @@ export const GridAnalysisSheet: React.FC = (props) => { isLoading, onRowCellClick, onColCellClick, - onMergedCellsClick, + onMergedCellClick, onRowCellDoubleClick, onColCellDoubleClick, onMergedCellsDoubleClick, @@ -84,52 +83,29 @@ export const GridAnalysisSheet: React.FC = (props) => { }; const bindEvent = () => { - baseSpreadsheet.on(S2Event.DATA_CELL_MOUSE_UP, (ev: Event) => { - if (isFunction(onDataCellMouseUp)) { - onDataCellMouseUp(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.DATA_CELL_MOUSE_UP, (event: Event) => { + onDataCellMouseUp?.(getBaseCellData(event)); }); - baseSpreadsheet.on(S2Event.ROW_CELL_CLICK, (ev: Event) => { - if (isFunction(onRowCellClick)) { - onRowCellClick(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.ROW_CELL_CLICK, (event: Event) => { + onRowCellClick?.(getBaseCellData(event)); }); - baseSpreadsheet.on(S2Event.COL_CELL_CLICK, (ev: Event) => { - if (isFunction(onColCellClick)) { - onColCellClick(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.COL_CELL_CLICK, (event: Event) => { + onColCellClick?.(getBaseCellData(event)); }); - - baseSpreadsheet.on(S2Event.MERGED_CELLS_CLICK, (ev: Event) => { - if (isFunction(onMergedCellsClick)) { - onMergedCellsClick(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.MERGED_CELLS_CLICK, (event: Event) => { + onMergedCellClick?.(getBaseCellData(event)); }); - baseSpreadsheet.on(S2Event.ROW_CELL_DOUBLE_CLICK, (ev: Event) => { - if (isFunction(onRowCellClick)) { - onRowCellDoubleClick(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.ROW_CELL_DOUBLE_CLICK, (event: Event) => { + onRowCellDoubleClick?.(getBaseCellData(event)); }); - baseSpreadsheet.on(S2Event.COL_CELL_DOUBLE_CLICK, (ev: Event) => { - if (isFunction(onColCellClick)) { - onColCellDoubleClick(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.COL_CELL_DOUBLE_CLICK, (event: Event) => { + onColCellDoubleClick?.(getBaseCellData(event)); }); - - baseSpreadsheet.on(S2Event.MERGED_CELLS_DOUBLE_CLICK, (ev: Event) => { - if (isFunction(onMergedCellsClick)) { - onMergedCellsDoubleClick(getBaseCellData(ev)); - } + baseSpreadsheet.on(S2Event.MERGED_CELLS_DOUBLE_CLICK, (event: Event) => { + onMergedCellsDoubleClick?.(getBaseCellData(event)); }); }; - const unBindEvent = () => { - baseSpreadsheet.off(S2Event.MERGED_CELLS_CLICK); - baseSpreadsheet.off(S2Event.ROW_CELL_CLICK); - baseSpreadsheet.off(S2Event.COL_CELL_CLICK); - baseSpreadsheet.off(S2Event.DATA_CELL_MOUSE_UP); - }; - const buildSpreadSheet = () => { if (!baseSpreadsheet) { baseSpreadsheet = initSpreadSheet(); @@ -147,7 +123,6 @@ export const GridAnalysisSheet: React.FC = (props) => { useEffect(() => { buildSpreadSheet(); return () => { - unBindEvent(); baseSpreadsheet.destroy(); }; }, []); diff --git a/packages/s2-react/src/components/sheets/interface.ts b/packages/s2-react/src/components/sheets/interface.ts index 5bb254835b..260d01ca8f 100644 --- a/packages/s2-react/src/components/sheets/interface.ts +++ b/packages/s2-react/src/components/sheets/interface.ts @@ -86,8 +86,14 @@ export interface BaseSheetProps { onDataCellClick?: (data: TargetCellInfo) => void; onDataCellDoubleClick?: (data: TargetCellInfo) => void; onDataCellMouseUp?: (data: TargetCellInfo) => void; - onMergedCellsClick?: (data: TargetCellInfo) => void; + onMergedCellClick?: (data: TargetCellInfo) => void; onMergedCellsDoubleClick?: (data: TargetCellInfo) => void; onContextMenu?: (data: TargetCellInfo) => void; + onRowCellHover?: (data: TargetCellInfo) => void; + onColCellHover?: (data: TargetCellInfo) => void; + onDataCellHover?: (data: TargetCellInfo) => void; + onMergedCellHover?: (data: TargetCellInfo) => void; + onCornerCellDoubleClick?: (data: TargetCellInfo) => void; + onCornerCellHover?: (data: TargetCellInfo) => void; getSpreadSheet?: (spreadsheet: SpreadSheet) => void; } diff --git a/packages/s2-react/src/components/sheets/table-sheet/index.tsx b/packages/s2-react/src/components/sheets/table-sheet/index.tsx index aa2c369d02..380ba4ff99 100644 --- a/packages/s2-react/src/components/sheets/table-sheet/index.tsx +++ b/packages/s2-react/src/components/sheets/table-sheet/index.tsx @@ -40,7 +40,7 @@ export const TableSheet: React.FC = memo((props) => { onDataCellDoubleClick, onRowCellClick, onColCellClick, - onMergedCellsClick, + onMergedCellClick, onRowCellDoubleClick, onColCellDoubleClick, onMergedCellsDoubleClick, @@ -80,35 +80,38 @@ export const TableSheet: React.FC = memo((props) => { string, (...args: unknown[]) => unknown > = { - [S2Event.DATA_CELL_MOUSE_UP]: (ev: GEvent) => { - onDataCellMouseUp?.(getBaseCellData(ev)); + [S2Event.DATA_CELL_MOUSE_UP]: (event: GEvent) => { + onDataCellMouseUp?.(getBaseCellData(event)); }, - [S2Event.MERGED_CELLS_CLICK]: (ev: GEvent) => { - onMergedCellsClick?.(getBaseCellData(ev)); + [S2Event.MERGED_CELLS_CLICK]: (event: GEvent) => { + onMergedCellClick?.(getBaseCellData(event)); }, - [S2Event.ROW_CELL_CLICK]: (ev: GEvent) => { - onRowCellClick?.(getBaseCellData(ev)); + [S2Event.ROW_CELL_CLICK]: (event: GEvent) => { + onRowCellClick?.(getBaseCellData(event)); }, - [S2Event.COL_CELL_CLICK]: (ev: GEvent) => { - onColCellClick?.(getBaseCellData(ev)); + [S2Event.COL_CELL_CLICK]: (event: GEvent) => { + onColCellClick?.(getBaseCellData(event)); }, - [S2Event.DATA_CELL_CLICK]: (ev: GEvent) => { - onDataCellClick?.(getBaseCellData(ev)); + [S2Event.DATA_CELL_CLICK]: (event: GEvent) => { + onDataCellClick?.(getBaseCellData(event)); }, - [S2Event.MERGED_CELLS_DOUBLE_CLICK]: (ev: GEvent) => { - onMergedCellsDoubleClick?.(getBaseCellData(ev)); + [S2Event.DATA_CELL_HOVER]: (event: GEvent) => { + onDataCellClick?.(getBaseCellData(event)); }, - [S2Event.ROW_CELL_DOUBLE_CLICK]: (ev: GEvent) => { - onRowCellDoubleClick?.(getBaseCellData(ev)); + [S2Event.MERGED_CELLS_DOUBLE_CLICK]: (event: GEvent) => { + onMergedCellsDoubleClick?.(getBaseCellData(event)); }, - [S2Event.COL_CELL_DOUBLE_CLICK]: (ev: GEvent) => { - onColCellDoubleClick?.(getBaseCellData(ev)); + [S2Event.ROW_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onRowCellDoubleClick?.(getBaseCellData(event)); }, - [S2Event.DATA_CELL_DOUBLE_CLICK]: (ev: GEvent) => { - onDataCellDoubleClick?.(getBaseCellData(ev)); + [S2Event.COL_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onColCellDoubleClick?.(getBaseCellData(event)); }, - [S2Event.GLOBAL_CONTEXT_MENU]: (ev: GEvent) => { - onContextMenu?.(getBaseCellData(ev)); + [S2Event.DATA_CELL_DOUBLE_CLICK]: (event: GEvent) => { + onDataCellDoubleClick?.(getBaseCellData(event)); + }, + [S2Event.GLOBAL_CONTEXT_MENU]: (event: GEvent) => { + onContextMenu?.(getBaseCellData(event)); }, [S2Event.LAYOUT_ROW_NODE_BORDER_REACHED]: ( targetRow: TargetLayoutNode, @@ -133,22 +136,6 @@ export const TableSheet: React.FC = memo((props) => { }); }; - const unBindEvent = () => { - [ - S2Event.LAYOUT_AFTER_HEADER_LAYOUT, - S2Event.LAYOUT_ROW_NODE_BORDER_REACHED, - S2Event.LAYOUT_COL_NODE_BORDER_REACHED, - S2Event.LAYOUT_CELL_SCROLL, - S2Event.RANGE_SORT, - S2Event.MERGED_CELLS_CLICK, - S2Event.ROW_CELL_CLICK, - S2Event.COL_CELL_CLICK, - S2Event.DATA_CELL_MOUSE_UP, - ].forEach((eventName) => { - baseSpreadsheet.current.off(eventName); - }); - }; - const setOptions = (newOptions?: S2Options) => { const curOptions = newOptions || options; ownSpreadsheet.setOptions(curOptions); @@ -186,7 +173,6 @@ export const TableSheet: React.FC = memo((props) => { useEffect(() => { buildSpreadSheet(); return () => { - unBindEvent(); baseSpreadsheet.current.destroy(); }; }, []); diff --git a/packages/s2-react/src/components/tooltip/components/operator/index.tsx b/packages/s2-react/src/components/tooltip/components/operator/index.tsx index e30ead2a34..e8340b2c72 100644 --- a/packages/s2-react/src/components/tooltip/components/operator/index.tsx +++ b/packages/s2-react/src/components/tooltip/components/operator/index.tsx @@ -35,7 +35,7 @@ export const TooltipOperator = (props: TooltipOperatorOptions) => { return ( {map(children, (subMenu: TooltipOperatorMenu) => renderMenu(subMenu))} @@ -61,18 +61,17 @@ export const TooltipOperator = (props: TooltipOperatorOptions) => { ); } + return map(menus, (menu: TooltipOperatorMenu) => { const { id, icon, text, children } = menu; - const menuRender = size(children) ? ( + const menuRender = !isEmpty(children) && ( {map(children, (subMenu: TooltipOperatorMenu) => renderMenu(subMenu))} - ) : ( - <> ); return ( diff --git a/packages/s2-react/src/components/tooltip/custom-tooltip.tsx b/packages/s2-react/src/components/tooltip/custom-tooltip.tsx index c6d3ec3147..eb830f1e88 100644 --- a/packages/s2-react/src/components/tooltip/custom-tooltip.tsx +++ b/packages/s2-react/src/components/tooltip/custom-tooltip.tsx @@ -1,7 +1,6 @@ import { BaseTooltip, SpreadSheet } from '@antv/s2'; import React from 'react'; import ReactDOM from 'react-dom'; -import { merge } from 'lodash'; import { TooltipComponent } from '@/components/tooltip'; import { TooltipRenderProps } from '@/components/tooltip/interface'; @@ -15,19 +14,22 @@ export class CustomTooltip extends BaseTooltip { const { content: contentFromOptions } = this.spreadsheet.options.tooltip; // 方法级 s2.showTooltip({ content: '' }) const showOptions = this.options; - // 优先级: 方法级 > 配置级 - const tooltipProps: TooltipRenderProps = merge( - {}, - { - content: contentFromOptions, - }, - showOptions, + const tooltipProps: TooltipRenderProps = { + ...showOptions, + }; + // 优先级: 方法级 > 配置级, 兼容 content 为空字符串的场景 + const content = showOptions.content ?? contentFromOptions; + + ReactDOM.render( + , + this.container, ); - ReactDOM.render(, this.container); } destroy() { super.destroy(); - ReactDOM.unmountComponentAtNode(this.container); + if (this.container) { + ReactDOM.unmountComponentAtNode(this.container); + } } } diff --git a/packages/s2-react/src/components/tooltip/index.tsx b/packages/s2-react/src/components/tooltip/index.tsx index 408bfd758d..d1acf85cdd 100644 --- a/packages/s2-react/src/components/tooltip/index.tsx +++ b/packages/s2-react/src/components/tooltip/index.tsx @@ -23,7 +23,7 @@ import { TooltipRenderProps } from './interface'; import './index.less'; -export const TooltipComponent = (props: TooltipRenderProps) => { +export const TooltipComponent: React.FC = (props) => { const renderDivider = () => { return ; }; @@ -105,7 +105,7 @@ export const TooltipComponent = (props: TooltipRenderProps) => { const { data, options, content } = props; if (!isNil(content)) { - return <>{content}; + return content as React.ReactElement; } return <>{renderContent(data, options)}; diff --git a/packages/s2-react/src/components/tooltip/interface.ts b/packages/s2-react/src/components/tooltip/interface.ts index a07e121d6a..9ebf87d872 100644 --- a/packages/s2-react/src/components/tooltip/interface.ts +++ b/packages/s2-react/src/components/tooltip/interface.ts @@ -1,8 +1,10 @@ import { TooltipShowOptions } from '@antv/s2'; -export interface TooltipRenderProps - extends TooltipShowOptions { - readonly content?: React.ReactNode; + +export interface TooltipRenderProps + extends TooltipShowOptions { + readonly content?: T; } + export type TooltipInfosProps = { infos: string; }; diff --git a/s2-site/docs/api/basic-class/base-cell.en.md b/s2-site/docs/api/basic-class/base-cell.en.md new file mode 100644 index 0000000000..1932511764 --- /dev/null +++ b/s2-site/docs/api/basic-class/base-cell.en.md @@ -0,0 +1,6 @@ +--- +title: BaseCell +order: 4 +--- + +`markdown:docs/api/basic-class/base-cell.zh.md` diff --git a/s2-site/docs/api/basic-class/base-cell.zh.md b/s2-site/docs/api/basic-class/base-cell.zh.md new file mode 100644 index 0000000000..c2b1013a97 --- /dev/null +++ b/s2-site/docs/api/basic-class/base-cell.zh.md @@ -0,0 +1,96 @@ +--- +title: BaseCell +order: 4 +--- + +功能描述:单元格基类。[详情](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/base-cell.ts) + +| 参数 | 说明 | 类型 | +| --- | --- | --- | +| getMeta | 获取单元格元数据 | () => [ViewMeta](#viewmeta) | +| setMeta | 设置单元格元数据 | (vieMeta: [ViewMeta](#viewmeta)) => void | +| getIconStyle | 获取单元格图标样式 | () => [IconTheme](/zh/docs/api/general/S2Theme) | +| getStyle | 获取单元格样式 | () => [IconTheme](/zh/docs/api/general/S2Theme) | +| getTextAndIconPosition | 获取单元格文本和图标的位置 | (iconCount: `number`) => [TextAndIconPosition](#textandiconposition) | +| getActualText | 获取绘制的文本 | `() => string` | +| cellType | 单元格类型 | [CellTypes](#celltypes) | +| initCell | 初始化单元格 | `() => void` | +| update | 更新单元格 | `() => void` | +| getTextStyle | 获取文本样式 | `() => void` | +| getFormattedFieldValue | 获取格式化后的字段值 | `() => { formattedValue: string, value: string }` | +| getMaxTextWidth | 获取文本最大宽度 | `() => number` | +| getTextPosition | 获取文本最大宽度 | [Point](#point) | +| getContentArea | 获取内容区域 | `() => { x: number, y: number, width: number, height: number }` | +| updateByState | 根据状态更新单元格样式 | `(stateName: InteractionStateName, cell: S2CellType) => void` | +| hideInteractionShape | 隐藏单元格的交互图层 | `() => void` | +| clearUnselectedState | 清空未选中状态 | `() => void` | + +### ViewMeta + +```ts +interface ViewMeta { + spreadsheet: SpreadSheet; + id: string; + x: number; + y: number; + width: number; + height: number; + data: Record; + rowIndex: number; + colIndex: number; + valueField: string; + fieldValue: DataItem; + isTotals?: boolean; + rowQuery?: Record; + colQuery?: Record; + rowId?: string; + colId?: string; + [key: string]: any; +} +``` + +### Point + +```ts +interface Point { + x: number, + y: number +} +``` + +### TextAndIconPosition + +```ts +interface TextAndIconPosition { + text: Point + icon: Point +} +``` + +### CellTypes + +```ts +export enum CellTypes { + DATA_CELL = 'dataCell', + HEADER_CELL = 'headerCell', + ROW_CELL = 'rowCell', + COL_CELL = 'colCell', + CORNER_CELL = 'cornerCell', + MERGED_CELL = 'mergedCell', +} +``` + +### S2CellType + +```ts +import type { SimpleBBox } from '@antv/g-canvas'; + +export type S2CellType = + | DataCell + | HeaderCell + | ColCell + | CornerCell + | RowCell + | MergedCell + | BaseCell; +``` diff --git a/s2-site/docs/api/basic-class/interaction.en.md b/s2-site/docs/api/basic-class/interaction.en.md index 3553c049d2..7863e95b98 100644 --- a/s2-site/docs/api/basic-class/interaction.en.md +++ b/s2-site/docs/api/basic-class/interaction.en.md @@ -2,4 +2,5 @@ title: Interaction order: 2 --- + `markdown:docs/api/basic-class/interaction.zh.md` diff --git a/s2-site/docs/api/basic-class/node.zh.md b/s2-site/docs/api/basic-class/node.zh.md new file mode 100644 index 0000000000..fda6f8647e --- /dev/null +++ b/s2-site/docs/api/basic-class/node.zh.md @@ -0,0 +1,37 @@ +--- +title: Node +order: 5 +--- + +功能描述:布局节点。[详情](https://github.com/antvis/S2/blob/master/packages/s2-core/src/facet/layout/node.ts) + +| 参数 | 说明 | 类型 | +| --- | --- | --- | +| id | 节点id | `string` | +| key | 节点key | `string` | +| value | 节点值 | `string` | +| label | 节点标题 | `string` | +| level | 节点等级 | `number` | +| rowIndex | 行头索引 | `number` | +| colIndex | 列头索引 | `number` | +| parent | 父节点 | [Node](/zh/docs/api/basic-class/node) | +| isTotals | 是否是汇总 | `boolean` | +| isSubTotals | 是否是小计 | `boolean` | +| isGrandTotals | 是否是总计 | `boolean` | +| isCollapsed | 是否展开 | `boolean` | +| hierarchy | 层级结构 | [Hierarchy](#) | +| isPivotMode | 是否是透视表 | `boolean` | +| seriesNumberWidth | 序号宽度 | `number` | +| field | dataCfg 对应的 field | `string` | +| spreadsheet | 表格实例 | [SpreadSheet](/zh/docs/api/basic-class/spreadsheet) | +| query | 当前节点对应的数据 | `Record` | +| belongsCell | 对应的单元格 | [S2CellType](/zh/docs/api/basic-class/base-cell) | +| isTotalMeasure | 是否是数值小计 | `boolean` | +| inCollapseNode | 是否展开的节点 | `boolean` | +| isLeaf | 是否是叶子节点 | `boolean` | +| x | x轴坐标 | `number` | +| y | y轴坐标 | `number` | +| width | 宽度 | `number` | +| height | 高度 | `number` | +| padding | 间距 | `number` | +| children | 子节点 | [Node[]](/zh/docs/api/basic-class/node) | diff --git a/s2-site/docs/api/basic-class/spreadsheet.en.md b/s2-site/docs/api/basic-class/spreadsheet.en.md index 7780741a17..ad06c90298 100644 --- a/s2-site/docs/api/basic-class/spreadsheet.en.md +++ b/s2-site/docs/api/basic-class/spreadsheet.en.md @@ -2,4 +2,5 @@ title: SpreadSheet order: 1 --- + `markdown:docs/api/basic-class/spreadsheet.zh.md` diff --git a/s2-site/docs/api/components/sheet-component.zh.md b/s2-site/docs/api/components/sheet-component.zh.md index 3ad09caad5..55b7e02395 100644 --- a/s2-site/docs/api/components/sheet-component.zh.md +++ b/s2-site/docs/api/components/sheet-component.zh.md @@ -5,7 +5,7 @@ order: 0 ## SpreadsheetProps -功能描述: 基于 core 层封装的 react 版开箱即用组件。 +功能描述: 基于 `core` 层封装的 `react` 版开箱即用的组件。 | 参数 | 说明 | 类型 | 默认值 | 必选 | | :--- | :--- | :--- | :--- | :---: | @@ -21,16 +21,22 @@ order: 0 | header | 表头配置项 | [HeaderCfgProps](/zh/docs/api/components/header) | | | | getSpreadSheet | 获取表实例 | (spreadsheet: [SpreadSheet](/zh/docs/api/basic-class/spreadsheet)) => void; | | | | onListSort | 排序回调,用于做自定义排序 | (params: [ListSortParams](#listsortparams) ) => void; | | | -| onRowCellClick| 行头单击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onRowCellDoubleClick| 行头双击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onColCellClick| 列头单击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onColCellDoubleClick| 列头双击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onCornerCellClick| 角头单击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onDataCellClick| | (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onDataCellDoubleClick| 交叉单元格双击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onMergedCellClick| | (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onMergedCellDoubleClick| 合并单元格双击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | -| onContextMenu| 右键单元格单击回调事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onRowCellClick | 行头鼠标单击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onRowCellHover | 行头鼠标悬停事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onRowCellDoubleClick | 行头鼠标双击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onColCellClick | 列头鼠标单击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onColCellHover | 列头鼠标悬停事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onColCellDoubleClick | 列头鼠标双击事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onCornerCellClick | 角头鼠标单击事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onCornerCellHover | 角头鼠标悬停事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onCornerCellDoubleClick | 角头鼠标双击事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onDataCellClick | 数值单元格鼠标点击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onDataCellHover | 数值单元格鼠标悬停事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onDataCellDoubleClick | 交叉单元格双击事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onMergedCellHover | 合并单元格鼠标悬停事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onMergedCellClick | 合并单元格鼠标点击事件 | (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onMergedCellDoubleClick | 合并单元格鼠标双击事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | +| onContextMenu | 右键单元格单击事件| (data: [TargetCellInfo](#targetcellinfo)) => void | | | ### ListSortParams @@ -51,6 +57,6 @@ order: 0 | 参数 | 说明 | 类型 | 默认值 | 必选 | | :--- | :--- | :--- | :--- | :---: | -| target | 交互作用对象 | `any` | | ✓ | -| event | 事件 | `Event` | | ✓ | -| viewMeta | 当前交互作用的结点信息 | `Node` | | ✓ | +| target | 交互作用对象 | [S2CellType](/zh/docs/api/basic-class/base-cell) | | | +| event | 事件 | [Event](#) | | | +| viewMeta | 当前节点信息 | [Node](/zh/docs/api/basic-class/node) | | | diff --git a/s2-site/docs/api/general/S2DataConfig.zh.md b/s2-site/docs/api/general/S2DataConfig.zh.md index 78f2128d4c..5d782cfb20 100644 --- a/s2-site/docs/api/general/S2DataConfig.zh.md +++ b/s2-site/docs/api/general/S2DataConfig.zh.md @@ -13,8 +13,8 @@ redirect_from: | data | 原始数据 | [Data[]](#data) | | ✓ | | fields | 维度指标配置项 | [Fields](#fields) | | ✓ | | totalData | 总计数据 | [Data[]](#data) | | | -| meta | 全局化配置表数数据元信息,以度量为单位进行配置。在 `meta` 上的配置将同时影响所有组件的文本信息。 | `Meta[]` | | | -| sortParams | 全局化配置表数数据元信息,以度量为单位进行配置。在 `meta` 上的配置将同时影响所有组件的文本信息。 | `SortParams` | | | +| meta | 全局化配置表数数据元信息,以度量为单位进行配置。在 `meta` 上的配置将同时影响所有组件的文本信息。 | [Meta[]](#meta) | | | +| sortParams | 全局化配置表数数据元信息,以度量为单位进行配置。在 `meta` 上的配置将同时影响所有组件的文本信息。 | [SortParams](#sortparams) | | | ### Data diff --git a/s2-site/docs/api/general/S2Options.zh.md b/s2-site/docs/api/general/S2Options.zh.md index b56f4db4ea..8098663d1a 100644 --- a/s2-site/docs/api/general/S2Options.zh.md +++ b/s2-site/docs/api/general/S2Options.zh.md @@ -20,7 +20,7 @@ order: 1 | showDefaultHeaderActionIcon |`boolean` | | `true` | 是否展示默认行列头操作图标 | | headerActionIcons | [HeaderActionIcon[]](#headeractionicon) | | `false` | 自定义行列头操作图标(需要将 `showDefaultHeaderActionIcon` 置为 `false`) | | customSVGIcons | [CustomSVGIcon[]](#customsvgicon) | | `false` | 自定义 svg 图标 | -| style | [Style](#style) | | | 附加样式 | +| style | [Style](#style) | | | 单元格样式设置, 比如布局类型, 宽高, 边距, 是否隐藏数值列头等 | | frozenRowCount | `number` | | | 冻结行的数量,从顶部开始计数 (明细表有效) | | frozenColCount | `number` | | | 冻结列的数量,从左侧开始计数 (明细表有效) | | frozenTrailingRowCount | `number` | | | 冻结行数量,从底部开始计数 (明细表有效) | diff --git a/s2-site/docs/common/custom-tooltip.zh.md b/s2-site/docs/common/custom-tooltip.zh.md index eae5d8af18..7789a9167e 100644 --- a/s2-site/docs/common/custom-tooltip.zh.md +++ b/s2-site/docs/common/custom-tooltip.zh.md @@ -10,11 +10,11 @@ object **必选**,_default:null_ 功能描述: tooltip 显示配置 | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | --------- | ----------------------------------- | :---: | ------ | ----------------------- | -| position | [TooltipPosition](#TooltipPosition) | ✓ | | tooltip 显示位置 | -| data | [TooltipData](#TooltipData) | | | tooltip 展示层数据 | -| cellInfos | `Record` | | | 当前事件单元格信息 | -| options | [TooltipOptions](#TooltipOptions) | | | tooltip 部分配置 | -| element | `React.ReactElement` | | | 自定义 tooltip 弹框组件 | +| position | [TooltipPosition](#tooltipposition) | ✓ | | tooltip 显示位置 | +| data | [TooltipData](#tooltipdata) | | | tooltip 数据 | +| cellInfos | `Record` | | | 单元格信息 | +| options | [TooltipOptions](#tooltipoptions) | | | tooltip 部分配置 | +| content | `React.ReactNode` | | | 自定义 tooltip 内容 | ### TooltipPosition @@ -27,13 +27,13 @@ object **必选**,_default:null_ 功能描述: tooltip 显示位置 ### TooltipData -object **可选**,_default:null_ 功能描述: tooltip 展示层数据 +object **可选**,_default:null_ 功能描述: tooltip 数据 | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | --------- | ----------------------------------------------- | :---: | ------ | ------------------------------------ | -| summaries | [TooltipSummaryOptions](#TooltipSummaryOptions) | | | 所选项统计(按度量值区分)列表 | -| details | [ListItem](#ListItem) | | | 数据点明细信息 | -| headInfo | [TooltipHeadInfo](#TooltipHeadInfo) | | | 轴(行/列头)列表 | +| summaries | [TooltipSummaryOptions](#tooltipsummaryoptions) | | | 所选项统计(按度量值区分)列表 | +| details | [ListItem](#listitem) | | | 数据点明细信息 | +| headInfo | [TooltipHeadInfo](#tooltipheadinfo) | | | 轴(行/列头)列表 | | name | `string` | | | 当前单元格名称 | | tips | `string` | | | 提示/说明信息 | | infos | `string` | | | 底部提示信息(可用于快捷键操作提示) | @@ -54,8 +54,8 @@ object **可选**,_default:null_ 功能描述: tooltip 轴(行/列头) | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | ---- | --------------------- | :---: | ------ | -------- | -| rows | [ListItem](#ListItem) | ✓ | | 行头列表 | -| cols | [ListItem](#ListItem) | ✓ | | 列头列表 | +| rows | [ListItem](#listitem) | ✓ | | 行头列表 | +| cols | [ListItem](#listitem) | ✓ | | 列头列表 | #### ListItem @@ -74,7 +74,7 @@ object **必选**,_default:null_ 功能描述: tooltip 部分配置 | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | -------------- | ------------------------------------------------- | :---: | ------ | ---------------------------- | | hideSummary | `boolean` | | | 是否隐藏所选项统计信息 | -| operator | [TooltipOperatorOptions](#TooltipOperatorOptions) | | | 操作栏配置 | +| operator | [TooltipOperatorOptions](#tooltipoperatoroptions) | | | 操作栏配置 | | onlyMenu | `boolean` | | | tooltip 是否只展示操作菜单项 | | enterable | `boolean` | | | 是否可进入 tooltip 组件 | | isTotals | `boolean` | | | 是否是 总计/小计 单元格 | @@ -86,7 +86,7 @@ object **可选**,_default:null_ 功能描述: tooltip 操作栏配置 | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | ------------- | ------------------------------------------- | :---: | ------ | ---------- | -| menus | [TooltipOperatorMenu](#TooltipOperatorMenu) | ✓ | | 操作项列表 | +| menus | [TooltipOperatorMenu](#tooltipoperatormenu) | ✓ | | 操作项列表 | | onClick | `() => void` | ✓ | | 点击事件 | | [key: string] | `boolean` | | | 其他 | @@ -99,4 +99,4 @@ object **必选**,_default:null_ 功能描述: tooltip 操作项列表 | id | `string` | ✓ | | 值 | | text | `boolean` | | | 名称 | | icon | `React.ReactNode` | | | 自定义图标组件 | -| children | [TooltipOperatorMenu](#TooltipOperatorMenu) | | | 子菜单列表 | +| children | [TooltipOperatorMenu](#tooltipoperatormenu) | | | 子菜单列表 | diff --git a/s2-site/docs/common/custom/customTreeItem.zh.md b/s2-site/docs/common/custom/customTreeItem.zh.md index 477ac39fc6..4a0202fdb5 100644 --- a/s2-site/docs/common/custom/customTreeItem.zh.md +++ b/s2-site/docs/common/custom/customTreeItem.zh.md @@ -5,7 +5,9 @@ order: 8 ## CustomTreeItem -功能描述:自定义树状结构的配置,基本兼容 [AntD Tree](https://ant.design/components/tree-cn/) 数据配置项; +[可参考自定义目录树](/zh/docs/manual/advanced/custom/category-tree) + +功能描述:自定义树状结构的配置,兼容 [antd Tree](https://ant.design/components/tree-cn/) 数据配置项; | 参数 | 说明 | 类型 | 默认值 | 必选 | | --- | --- | --- | --- | :-: | diff --git a/s2-site/docs/common/tooltip.zh.md b/s2-site/docs/common/tooltip.zh.md index 41f775b0c3..889bf06d3f 100644 --- a/s2-site/docs/common/tooltip.zh.md +++ b/s2-site/docs/common/tooltip.zh.md @@ -13,9 +13,10 @@ object **必选**,_default:null_ 功能描述: tooltip 配置 | operation | tooltip 操作配置项 | [TooltipOperation](#tooltipoperation) | - | | | row | 行头配置 | [BaseTooltipConfig](#basetooltipconfig) | - | | | col | 列头配置 | [BaseTooltipConfig](#basetooltipconfig) | - | | -| cell | 单元格配置 | [BaseTooltipConfig](#basetooltipconfig) | - | | +| data | 数值配置 | [BaseTooltipConfig](#basetooltipconfig) | - | | +| corner | 角头配置 | [BaseTooltipConfig](#basetooltipconfig) | - | | | renderTooltip | 自定义整个 tooltip, 可以继承 BaseTooltip 自己重写一些方法 | [RenderTooltip](#rendertooltip) | - | | -| content | 自定义 tooltip 弹框组件 | `Element | string` | - | | +| content | 自定义 tooltip 内容 | `React.ReactNode | Element | string` | - | | | autoAdjustBoundary | 当 tooltip 超过边界时自动调整显示位置, container: 图表区域, body: 整个浏览器窗口, 设置为 `null` 可关闭此功能 | `container` \| `body` | `body` | | ### BaseTooltipConfig @@ -24,7 +25,7 @@ object **必选**,_default:null_ 功能描述: tooltip 配置 | ---------------- | ----------------------- | ------------------------------------- | ------ | :--: | | showTooltip | 是否展示 tooltip | `boolean` | `false` | | | operation | tooltip 操作配置项 | [TooltipOperation](#tooltipoperation) | - | | -| content | 自定义 tooltip 弹框组件 | `JSX.Element` | - | | +| content | 自定义 tooltip 内容 | `React.ReactNode | Element | string` | - | | ### TooltipOperation @@ -32,6 +33,7 @@ object **必选**,_default:null_ 功能描述: tooltip 操作配置项 | 参数 | 说明 | 类型 | 默认值 | 必选 | | ------------- | ----------------------------- | --------- | ------- | :--: | -| hiddenColumns | 是否开启隐藏列 (明细表有效) | `boolean` | `false` | | +| hiddenColumns | 是否开启隐藏列 (明细表有效) | `boolean` | `false` | | | trend | 是否显示趋势图 icon | `boolean` | `false` | | | sort | 是否开启组内排序 | `boolean` | `false` | | +| tableSort | 是否开启明细表列头排序 | `boolean` | `false` | | diff --git a/s2-site/docs/manual/advanced/custom/category-tree.zh.md b/s2-site/docs/manual/advanced/custom/category-tree.zh.md index 15e0065437..e358006c1d 100644 --- a/s2-site/docs/manual/advanced/custom/category-tree.zh.md +++ b/s2-site/docs/manual/advanced/custom/category-tree.zh.md @@ -20,29 +20,29 @@ order: 2 ```ts const s2options = { - width: 660, - height: 600, - hierarchyType: 'customTree', // 目录树 + width: 660, + height: 600, + hierarchyType: 'customTree', // 目录树 }; const s2DataConfig = { - fields: { - rows: [], // 空 - columns: ['type', 'sub_type'], - values: [ - 'measure-a', - ], - customTreeItems, // 行自定义树结构 - valueInCols: false, - }, - data, + fields: { + rows: [], // 空 + columns: ['type', 'sub_type'], + values: [ + 'measure-a', + ], + customTreeItems, // 行自定义树结构 + valueInCols: false, + }, + data, }; ``` ## 结构描述 -`dataCfg.fields.customTreeItems` 结构完全兼容 [AntD Tree](https://ant.design/components/tree-cn/) 树形的配置方式,并且在其基础上增加额外的功能元信息。 +`dataCfg.fields.customTreeItems` 结构完全兼容 [antd Tree](https://ant.design/components/tree-cn/) 树形的配置方式,并且在其基础上增加额外的功能元信息。 ```ts export interface CustomTreeItem { @@ -59,4 +59,4 @@ export interface CustomTreeItem { } ``` -此结构也可以通过内部的一个方法 `transformCustomTreeItems` 进行转换,参考[例子](/zh/examples/custom/custom-tree#custom-tree) +此结构也可以通过内部的一个方法 `transformCustomTreeItems` 进行转换,参考 [例子](/zh/examples/custom/custom-tree#custom-tree) diff --git a/s2-site/docs/manual/basic/tooltip.zh.md b/s2-site/docs/manual/basic/tooltip.zh.md index 6a108abdb6..3b5e1c7cba 100644 --- a/s2-site/docs/manual/basic/tooltip.zh.md +++ b/s2-site/docs/manual/basic/tooltip.zh.md @@ -11,12 +11,28 @@ order: 7 ## 使用 -在 `s2options` 中配置 [tooltip](/zh/docs/api/general/S2Options#tooltip) 字段,还可通过 `row`、`col`、`cell` 分别配置行头、列头、数据单元格 +在 `s2options` 中配置 [tooltip](/zh/docs/api/general/S2Options#tooltip) 字段,默认作用于**所有**单元格 + +```ts +const s2options = { + tooltip: {} +}; +``` + +还可以对不同类型的单元格单独配置: + +- `corner`: 角头 +- `row`: 行头 +- `col`: 列头 +- `data`: 数值 ```ts const s2options = { tooltip: { - ... + corner: {}, + row: {}, + col: {}, + data: {}, } }; ``` @@ -28,11 +44,11 @@ const s2options = { ```ts const s2options = { tooltip: { - showTooltip: true, - row: { - // 单独设置行头不显示 - showTooltip: false, - } + showTooltip: true, + row: { + // 单独设置行头不显示 + showTooltip: false, + } } }; ``` @@ -61,8 +77,8 @@ const s2options = { 通过配置 `autoAdjustBoundary` 字段开启: -- `container` : tooltip 超出表格容器范围时,自动调整位置,始终在表格内显示 -- `body` : tooltip 超出浏览器窗口可视范围时,自动调整位置,始终在可视范围内显示 +- `container` : tooltip 超出**表格容器**范围时,自动调整位置,始终在表格内显示 +- `body` : tooltip 超出**浏览器窗口**可视范围时,自动调整位置,始终在可视范围内显示 - `null` : 关闭自动调整 ```ts @@ -78,7 +94,7 @@ const s2options = { #### 自定义 Tooltip 内容 -对于 `@antv/s2` 类的使用方式: tooltip 内容 可以是任意 `dom` 节点或者 `字符串` +对于 `@antv/s2` 类的使用方式:tooltip 内容 可以是任意 `dom` 节点或者 `字符串` ```ts const content = document.createElement('div') @@ -91,7 +107,7 @@ const s2options = { }; ``` -对于 `@antv/s2-react` 组件的使用方式: tooltip 内容 可以是任意的 `jsx` 元素 +对于 `@antv/s2-react` 组件的使用方式:tooltip 内容 可以是任意的 `jsx` 元素 ```ts const content = ( @@ -107,11 +123,25 @@ const s2options = { }; ``` -##### 1. 配置级 +同时, `content` 还支持回调的方式, 可以根据[当前单元格信息](/zh/docs/api/basic-class/interaction)然后动态的更改内容 + +```ts +const TooltipContent = (props) =>
...
+ +const s2options = { + tooltip: { + content: (cell) => { + const meta = cell.getMeta() + console.log('当前单元格信息:', meta) + return + }, + }, +}; +``` -配置 `tooltip.content` 自定义 `Tooltip` 内容,还可以給 `行 (row)`、`列 (col)`、`数据 (cell)` 三种类型的单元格分别配置自定义内容 +##### 1. 配置级 -`tooltip.content` 优先级 小于 `row.content`, `col.content`, `cell.content` +对不同的单元格进行配置时, `tooltip.content` 的优先级 小于 `row.content`, `col.content`, `data.content`, `corner.content` ```tsx const TooltipContent = ( @@ -126,21 +156,21 @@ const ColTooltipContent = (
colTooltip
); -const CellTooltipContent = ( -
cellTooltip
+const DataTooltipContent = ( +
dataTooltip
); const s2options = { tooltip: { - content: , + content: TooltipContent, row: { - content: , + content: RowTooltipContent, }, col: { - content: + content: ColTooltipContent } - cell: { - content: + data: { + content: DataTooltipContent } }, }; @@ -156,7 +186,7 @@ const TooltipContent = ( ); s2.showTooltip({ - content: + content: TooltipContent }) // 或者 s2.tooltip.show({ content: TooltipContent }) @@ -172,7 +202,7 @@ s2.showTooltip({ #### 自定义 Tooltip 类 -继承 `BaseTooltip` 基类,自定义 `显示 (show)`, `隐藏 (hide)`, `销毁 (destroy)` 等方法 +继承 `BaseTooltip` 基类,可重写 `显示 (show)`, `隐藏 (hide)`, `销毁 (destroy)` 等方法,结合 `this.spreadsheet` 实例,来实现满足你业务的 `tooltip` ```ts import { BaseTooltip, SpreadSheet } from '@antv/s2'; @@ -186,7 +216,9 @@ export class CustomTooltip extends BaseTooltip { clearContent() {} - show(showOptions) {} + show(showOptions) { + console.log(this.spreadsheet) + } hide() {} @@ -194,7 +226,7 @@ export class CustomTooltip extends BaseTooltip { } ``` -让表格渲染时,使用你自定义的 `Tooltip` +覆盖默认,使用你自定义的 `Tooltip` ```ts const s2Options = { @@ -207,6 +239,63 @@ const s2Options = { +#### 自定义 Tooltip 显示时机 + +在 `tooltip` 开启前提下的默认情况: + +- 行列头**点击**时显示 `tooltip`, 单元格文字**被省略**时悬停显示 `tooltip` +- 数值单元格悬停超过 **800ms** 显示 `tooltip` + +比如想自定义成鼠标悬停行头时显示 `tooltip`, 可通过自定义交互 [详情](/zh/docs/manual/advanced/interaction/custom), 监听行头单元格的 [交互事件](/zh/docs/manual/advanced/interaction/basic#%E4%BA%A4%E4%BA%92%E4%BA%8B%E4%BB%B6) `S2Event.ROW_CELL_HOVER`. [例子](/zh/examples/interaction/custom#row-col-hover-tooltip) + +```ts +import { PivotSheet, BaseEvent, S2Event } from '@antv/s2'; + +class RowHoverInteraction extends BaseEvent { + bindEvents() { + this.spreadsheet.on(S2Event.ROW_CELL_HOVER, (event) => { + this.spreadsheet.tooltip.show({ + position: { x:0, y:0 }, + content: "..." + }) + }) + } +} + +const s2options = { + tooltip: { + showTooltip: true, + } + interaction: { + customInteractions: [ + { + key: 'RowHoverInteraction', + interaction: RowHoverInteraction, + }, + ], + } +}; + +``` + +如果使用的是 `React` 组件, 也可以使用 [单元格回调函数](zh/docs/api/components/sheet-component) 来进行自定义. [例子](/zh/examples/react-component/tooltip#custom-hover-show-tooltip) + +```tsx +const CustomColCellTooltip = () =>
col cell tooltip
; + +const onRowCellHover = ({ event, viewMeta }) => { + viewMeta.spreadsheet.tooltip.show({ + position: { + x: event.clientX, + y: event.clientY, + }, + content: , + }); +}; + + +``` + #### 重写展示方法 除了上面说到的 `自定义 Tooltip 类` 自定义展示方法外,也可以修改 [表格实例]([`spreadsheet`](/zh/docs/api/basic-class/spreadsheet)) 上 `Tooltip` 的方法 `spreadsheet.showTooltip()` ([如何获取表格实例](zh/docs/manual/advanced/get-instance)) diff --git a/s2-site/docs/manual/contribution.zh.md b/s2-site/docs/manual/contribution.zh.md index d3f0e0ad00..6eeb5b3b43 100644 --- a/s2-site/docs/manual/contribution.zh.md +++ b/s2-site/docs/manual/contribution.zh.md @@ -36,7 +36,7 @@ order: 7 2. 安装依赖:`yarn bootstrap` 或者 `yarn` 3. 提交你的改动,commit 请遵守 [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.uyo6cb12dt6w) 4. 如果你的改动是修复 bug, 还可以在提交信息后面加上 `close #issue 号`, 这样可以在 pr 合并后,可以自动关闭对应的 issue, 比如 `fix: render bug close #123` -5. 确保加上了对应的单元测试 +5. 确保加上了对应的单元测试和文档 (如有必要) 6. 所有 Lint 和 Test 检查通过后,并且 review 通过,我们会合并你的 pr. ![preview](https://gw.alipayobjects.com/zos/antfincdn/ssOxFrycD/86339514-5f9a-4101-8690-e47c97cd8af5.png) @@ -52,6 +52,8 @@ npm i -g yarn 1. `yarn bootstrap` 安装依赖 2. `yarn site:bootstrap` 安装网站相关依赖 3. `yarn site:start` 启动本地的 `S2` 网站 -4. `yarn core:start` 可视化的方式调试测试 -5. `yarn build` 构建 `S2`, 输出 `umd`, `esm` 和 `lib` 目录 -6. `yarn test` 运行单元格测试 +4. `yarn core:start` 可视化的方式调试核心层测试 (基于 jest-electron) +5. `yarn react:start` 可视化的方式调试组件层测试 (基于 jest-electron) +6. `yarn react:playground` 启动本地的组件层demo (基于 vite) +7. `yarn build` 构建 `@antv/s2` 和 `@antv/s2-react` 两个包, 分别输出 `umd`, `esm` 和 `lib` 目录 +8. `yarn test` 运行单元格测试 diff --git a/s2-site/docs/manual/introduction.zh.md b/s2-site/docs/manual/introduction.zh.md index 6b6e55f9a8..eb39fb55fa 100644 --- a/s2-site/docs/manual/introduction.zh.md +++ b/s2-site/docs/manual/introduction.zh.md @@ -5,8 +5,6 @@ redirect_from: - /zh/docs/manual --- - - ![introduction](https://gw.alipayobjects.com/mdn/rms_56cbb2/afts/img/A*X_KJQZAxjKUAAAAAAAAAAAAAARQnAQ)
@@ -182,7 +180,6 @@ const s2options = { ```ts import { PivotSheet } from '@antv/s2'; -import '@antv/s2/dist/s2.min.css' const container = document.getElementById('container'); @@ -195,26 +192,55 @@ s2.render() preview +### 📦 Packages + +| Package | Latest | Beta | Size | Download | +|---|---|---|---| --- | +| [@antv/s2](https://github.com/antvis/S2/tree/master/packages/s2-core) | ![latest](https://img.shields.io/npm/v/@antv/s2/latest.svg) | ![beta](https://img.shields.io/npm/v/@antv/s2/beta.svg) | ![size](https://img.badgesize.io/https:/unpkg.com/@antv/s2@latest/dist/index.min.js?label=gzip%20size&compression=gzip) | ![download](https://img.shields.io/npm/dm/@antv/s2.svg) | +| [@antv/s2-react](https://github.com/antvis/S2/tree/master/packages/s2-react) | ![latest](https://img.shields.io/npm/v/@antv/s2-react/latest.svg) | ![beta](https://img.shields.io/npm/v/@antv/s2-react/beta.svg) | ![size](https://img.badgesize.io/https:/unpkg.com/@antv/s2-react@latest/dist/index.min.js?label=gzip%20size&compression=gzip) | ![download](https://img.shields.io/npm/dm/@antv/s2-react.svg) | + ## 👤 Author [**@AntV**](https://github.com/orgs/antvis/people) ## 🤝 参与贡献 -初次使用 S2,建议从[快速上手](zh/docs/manual/getting-started)教程开始了解,如果有遇到问题或不满足的需求,可以移步至[issue](https://github.com/antvis/s2/issues) 区给我们留下建议。 - -提交代码前请参考我们的[贡献指南](zh/docs/manual/contribution) - ```bash git clone git@github.com:antvis/S2.git cd S2 -yarn +# 安装依赖 +yarn # 或者 yarn bootstrap +# 调试 s2-core yarn core:start + +# 调试 s2-react +yarn react:playground + +# 单元测试 +yarn test + +# 打包 +yarn build + +# 代码风格和类型检测 +yarn lint + +# 本地启动官网 +yarn site:bootstrap +yarn site:start ``` +## 📧 联系我们 + +S2 + +## 👬 Contributors + +![https://github.com/antvis/s2/graphs/contributors](https://contrib.rocks/image?repo=antvis/s2) + ## 📄 License MIT@[AntV](https://github.com/antvis). diff --git a/s2-site/examples/interaction/custom/demo/meta.json b/s2-site/examples/interaction/custom/demo/meta.json index e5aca52304..8222568fad 100644 --- a/s2-site/examples/interaction/custom/demo/meta.json +++ b/s2-site/examples/interaction/custom/demo/meta.json @@ -11,6 +11,14 @@ "en": "Double click hide columns" }, "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/ukpGbSUqFz/double-click.gif" + }, + { + "filename": "row-col-hover-tooltip.ts", + "title": { + "zh": "行列头悬停显示 tooltip", + "en": "Show tooltip when hover row or column" + }, + "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/c%24ICO1qvb/Kapture%2525202021-12-03%252520at%25252016.30.11.gif" } ] } diff --git a/s2-site/examples/interaction/custom/demo/row-col-hover-tooltip.ts b/s2-site/examples/interaction/custom/demo/row-col-hover-tooltip.ts new file mode 100644 index 0000000000..d38f9b5849 --- /dev/null +++ b/s2-site/examples/interaction/custom/demo/row-col-hover-tooltip.ts @@ -0,0 +1,56 @@ +import { PivotSheet, BaseEvent, S2Event } from '@antv/s2'; + +class RowColumnHoverTooltipInteraction extends BaseEvent { + bindEvents() { + // 行头hover + this.spreadsheet.on(S2Event.ROW_CELL_HOVER, (event) => { + this.showTooltip(event); + }); + // 列头hover + this.spreadsheet.on(S2Event.COL_CELL_HOVER, (event) => { + this.showTooltip(event); + }); + } + + showTooltip(event) { + const cell = this.spreadsheet.getCell(event.target); + const meta = cell.getMeta(); + const content = meta.value; + + this.spreadsheet.tooltip.show({ + position: { + x: event.clientX, + y: event.clientY, + }, + content, + }); + } +} + +fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/2a5dbbc8-d0a7-4d02-b7c9-34f6ca63cff6.json', +) + .then((res) => res.json()) + .then((dataCfg) => { + const container = document.getElementById('container'); + + const s2Options = { + width: 600, + height: 480, + tooltip: { + showTooltip: true, + }, + interaction: { + customInteractions: [ + { + key: 'RowColumnHoverTooltipInteraction', + interaction: RowColumnHoverTooltipInteraction, + }, + ], + }, + }; + + const s2 = new PivotSheet(container, dataCfg, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/react-component/tooltip/demo/custom-interaction-tooltip.tsx b/s2-site/examples/react-component/tooltip/demo/custom-click-show-tooltip.tsx similarity index 99% rename from s2-site/examples/react-component/tooltip/demo/custom-interaction-tooltip.tsx rename to s2-site/examples/react-component/tooltip/demo/custom-click-show-tooltip.tsx index 1c371ab942..550dfe3ca5 100644 --- a/s2-site/examples/react-component/tooltip/demo/custom-interaction-tooltip.tsx +++ b/s2-site/examples/react-component/tooltip/demo/custom-click-show-tooltip.tsx @@ -39,6 +39,7 @@ fetch( spreadsheet.hideTooltip(); } }; + ReactDOM.render( res.json()) + .then((dataCfg) => { + const s2Options = { + width: 600, + height: 480, + tooltip: { + showTooltip: true, + }, + }; + + const CustomColCellTooltip = () =>
col cell tooltip
; + const CustomRowCellTooltip = () =>
row cell tooltip
; + const CustomDataCellTooltip = () =>
data cell tooltip
; + + const onColCellHover = ({ event, viewMeta }) => { + viewMeta.spreadsheet.tooltip.show({ + position: { + x: event.clientX, + y: event.clientY, + }, + content: , + }); + }; + + const onRowCellHover = ({ event, viewMeta }) => { + viewMeta.spreadsheet.tooltip.show({ + position: { + x: event.clientX, + y: event.clientY, + }, + content: , + }); + }; + + const onDataCellHover = ({ event, viewMeta }) => { + viewMeta.spreadsheet.tooltip.show({ + position: { + x: event.clientX, + y: event.clientY, + }, + content: , + }); + }; + + ReactDOM.render( + , + document.getElementById('container'), + ); + }); diff --git a/s2-site/examples/react-component/tooltip/demo/meta.json b/s2-site/examples/react-component/tooltip/demo/meta.json index fd7e96e3e8..a6fea22c45 100644 --- a/s2-site/examples/react-component/tooltip/demo/meta.json +++ b/s2-site/examples/react-component/tooltip/demo/meta.json @@ -7,7 +7,7 @@ { "filename": "basic.tsx", "title": { - "zh": "hover 显示", + "zh": "基本展示", "en": "Basic Show" }, "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/UuEx0wndW7/efa70cc6-5106-4d81-9957-25c99b4fad32.png" @@ -45,12 +45,20 @@ "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/sSx1H%24pJOs/6305fe29-5640-4d4d-b1ef-577cc6a32eb3.png" }, { - "filename": "custom-interaction-tooltip.tsx", + "filename": "custom-click-show-tooltip.tsx", "title": { - "zh": "自定义点击交互 Tooltip", - "en": "Custom Click Tooltip" + "zh": "自定义点击显示 Tooltip", + "en": "Custom Click Show Tooltip" }, "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/kpvnavJgo0/tooltip.gif" + }, + { + "filename": "custom-hover-show-tooltip.tsx", + "title": { + "zh": "自定义悬停显示 Tooltip", + "en": "Custom Hover Show Tooltip" + }, + "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/c%24ICO1qvb/Kapture%2525202021-12-03%252520at%25252016.30.11.gif" } ] }