Skip to content

Commit

Permalink
feat(layout): 支持自定义行/列单元格宽度 close #1585 (#1591)
Browse files Browse the repository at this point in the history
* feat(layout): 支持自定义行/列单元格宽度 close #1585

* fix: 适配明细表

* fix: 改名 & 修复测试

* fix: 还原 demo
  • Loading branch information
lijinke666 authored Jul 21, 2022
1 parent 74210b0 commit d4f7dda
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 96 deletions.
28 changes: 26 additions & 2 deletions packages/s2-core/__tests__/spreadsheet/scroll-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable jest/no-conditional-expect */
import * as mockDataConfig from 'tests/data/simple-data.json';
import { createMockCellInfo, getContainer, sleep } from 'tests/util/helpers';
import { ScrollType } from '../../src/ui/scrollbar';
import type { CellScrollPosition } from './../../src/common/interface/scroll';
import { PivotSheet, SpreadSheet } from '@/sheet-type';
import type {
Expand Down Expand Up @@ -72,7 +73,7 @@ describe('Scroll Tests', () => {
test('should hide tooltip when start scroll', () => {
const hideTooltipSpy = jest
.spyOn(s2, 'hideTooltip')
.mockImplementation(() => {});
.mockImplementationOnce(() => {});

canvas.dispatchEvent(new WheelEvent('wheel', { deltaX: 20, deltaY: 0 }));
expect(hideTooltipSpy).toHaveBeenCalledTimes(1);
Expand All @@ -81,12 +82,35 @@ describe('Scroll Tests', () => {
test('should clear hover timer when start scroll', () => {
const clearHoverTimerSpy = jest
.spyOn(s2.interaction, 'clearHoverTimer')
.mockImplementation(() => {});
.mockImplementationOnce(() => {});

canvas.dispatchEvent(new WheelEvent('wheel', { deltaX: 20, deltaY: 0 }));
expect(clearHoverTimerSpy).toHaveBeenCalledTimes(1);
});

test('should clear hover timer and hide tooltip when drag scroll bar', () => {
const hideTooltipSpy = jest
.spyOn(s2, 'hideTooltip')
.mockImplementationOnce(() => {});

const clearHoverTimerSpy = jest
.spyOn(s2.interaction, 'clearHoverTimer')
.mockImplementationOnce(() => {});

s2.facet.hScrollBar.emit(ScrollType.ScrollChange, {
offset: 10,
updateThumbOffset: 10,
});

s2.facet.vScrollBar.emit(ScrollType.ScrollChange, {
offset: 10,
updateThumbOffset: 10,
});

expect(clearHoverTimerSpy).toHaveBeenCalledTimes(2);
expect(hideTooltipSpy).toHaveBeenCalledTimes(2);
});

test('should not trigger scroll if not scroll over the viewport', () => {
const expectScroll = getScrollExpect();

Expand Down
38 changes: 36 additions & 2 deletions packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ jest.mock('@/sheet-type', () => {
getFreezeCornerDiffWidth: jest.fn(),
},
getCanvasElement: () => container.get('el'),
hideTooltip: jest.fn(),
interaction: {
clearHoverTimer: jest.fn(),
},
};
}),
};
Expand Down Expand Up @@ -184,10 +188,10 @@ describe('Pivot Mode Facet Test', () => {

describe('should get correct result when tree mode', () => {
s2.isHierarchyTreeType = jest.fn().mockReturnValue(true);
const ds = new MockPivotDataSet(s2);
const mockDataSet = new MockPivotDataSet(s2);
const treeFacet = new PivotFacet({
spreadsheet: s2,
dataSet: ds,
dataSet: mockDataSet,
...assembleDataCfg().fields,
...assembleOptions(),
...DEFAULT_STYLE,
Expand Down Expand Up @@ -280,4 +284,34 @@ describe('Pivot Mode Facet Test', () => {
});
},
);

// https://github.com/antvis/S2/pull/1591
test.each([
{ width: 200, useFunc: false },
{ width: 300, useFunc: true },
])(
'should render custom column leaf node width by %o',
({ width, useFunc }) => {
const mockDataSet = new MockPivotDataSet(s2);
const widthFn = jest.fn(() => width);
const customWidthFacet = new PivotFacet({
spreadsheet: s2,
dataSet: mockDataSet,
...assembleDataCfg().fields,
...assembleOptions(),
colCfg: {
width: useFunc ? widthFn : width,
},
});

customWidthFacet.layoutResult.colLeafNodes.forEach((node) => {
expect(node.width).toStrictEqual(width);
});

if (useFunc) {
// eslint-disable-next-line jest/no-conditional-expect
expect(widthFn).toHaveReturnedTimes(4);
}
},
);
});
48 changes: 45 additions & 3 deletions packages/s2-core/__tests__/unit/facet/table-facet-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ jest.mock('@/sheet-type', () => {
getColumnLeafNodes: jest.fn().mockReturnValue([]),
isHierarchyTreeType: jest.fn(),
getCanvasElement: () => container.get('el'),
hideTooltip: jest.fn(),
interaction: {
clearHoverTimer: jest.fn(),
},
};
}),
};
Expand Down Expand Up @@ -457,10 +461,10 @@ describe('Table Mode Facet Test With Zero Height', () => {
});

describe('Table Mode Facet With Frozen layoutCoordinate Test', () => {
const ss: SpreadSheet = new MockSpreadSheet();
const dataSet: TableDataSet = new MockTableDataSet(ss);
const s2: SpreadSheet = new MockSpreadSheet();
const dataSet: TableDataSet = new MockTableDataSet(s2);
const facet: TableFacet = new TableFacet({
spreadsheet: ss,
spreadsheet: s2,
dataSet,
...assembleDataCfg().fields,
...assembleOptions({
Expand All @@ -482,3 +486,41 @@ describe('Table Mode Facet With Frozen layoutCoordinate Test', () => {
});
});
});

describe('Custom Column Width Tests', () => {
// https://github.com/antvis/S2/pull/1591
test.each([
{ width: 200, useFunc: false },
{ width: 300, useFunc: true },
])(
'should render custom column leaf node width by %o',
({ width, useFunc }) => {
const s2: SpreadSheet = new MockSpreadSheet();
const dataSet: TableDataSet = new MockTableDataSet(s2);
const widthFn = jest.fn(() => width);
const customWidthFacet = new TableFacet({
spreadsheet: s2,
dataSet,
...assembleDataCfg().fields,
...assembleOptions(),
...DEFAULT_STYLE,
colCfg: {
width: useFunc ? widthFn : width,
},
});

customWidthFacet.layoutResult.colNodes.forEach((node) => {
expect(node.width).toStrictEqual(width);
});

customWidthFacet.layoutResult.colLeafNodes.forEach((node) => {
expect(node.width).toStrictEqual(width);
});

if (useFunc) {
// eslint-disable-next-line jest/no-conditional-expect
expect(widthFn).toHaveReturnedTimes(2);
}
},
);
});
6 changes: 5 additions & 1 deletion packages/s2-core/src/common/interface/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ export type HierarchyCallback = (
node: Node,
) => HierarchyResult;

export type CellCustomWidth = number | ((node: Node) => number);

export interface CellCfg {
width?: number;
height?: number;
Expand All @@ -297,7 +299,7 @@ export interface CellCfg {

export interface RowCfg {
// row's cell width
width?: number;
width?: CellCustomWidth;
// specific some row field's width
widthByField?: Record<string, number>;
heightByField?: Record<string, number>;
Expand All @@ -306,6 +308,8 @@ export interface RowCfg {
}

export interface ColCfg {
// custom column width
width?: CellCustomWidth;
// columns height(for normal state)
height?: number;
// specific some col field's width
Expand Down
25 changes: 24 additions & 1 deletion packages/s2-core/src/facet/base-facet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ import { Group } from '@antv/g-canvas';
import { type GestureEvent, Wheel } from '@antv/g-gesture';
import { interpolateArray } from 'd3-interpolate';
import { timer, type Timer } from 'd3-timer';
import { clamp, debounce, each, find, isUndefined, last, reduce } from 'lodash';
import {
clamp,
debounce,
each,
find,
get,
isFunction,
isUndefined,
last,
reduce,
} from 'lodash';
import { DataCell } from '../cell';
import {
InterceptType,
Expand All @@ -22,6 +32,7 @@ import {
DEBUG_VIEW_RENDER,
} from '../common/debug';
import type {
CellCustomWidth,
FrameConfig,
GridInfo,
LayoutResult,
Expand Down Expand Up @@ -134,6 +145,15 @@ export abstract class BaseFacet {
this.init();
}

protected getCellCustomWidth(node: Node, width: CellCustomWidth) {
return isFunction(width) ? width?.(node) : width;
}

protected getCellDraggedWidth(node: Node): number {
const { colCfg } = this.cfg;
return get(colCfg?.widthByFieldValue, `${node.value}`, node.width);
}

hideScrollBar = () => {
this.hRowScrollBar?.hide();
this.hScrollBar?.hide();
Expand Down Expand Up @@ -1218,6 +1238,9 @@ export abstract class BaseFacet {
this.panelBBox.viewportHeight,
);

this.spreadsheet.hideTooltip();
this.spreadsheet.interaction.clearHoverTimer();

this.realCellRender(scrollX, scrollY);
this.updatePanelScrollGroup();
this.translateRelatedGroups(scrollX, scrollY, hRowScrollX);
Expand Down
63 changes: 44 additions & 19 deletions packages/s2-core/src/facet/pivot-facet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@ import {
get,
isArray,
isEmpty,
isFunction,
isNil,
keys,
last,
maxBy,
merge,
reduce,
size,
} from 'lodash';
import { LAYOUT_SAMPLE_COUNT, type IconTheme, type MultiData } from '../common';
import {
LAYOUT_SAMPLE_COUNT,
type CellCustomWidth,
type ColCfg,
type IconTheme,
type MultiData,
type RowCfg,
} from '../common';
import { EXTRA_FIELD, LayoutWidthTypes, VALUE_FIELD } from '../common/constant';
import { CellTypes } from '../common/constant/interaction';
import { DebuggerUtil } from '../common/debug';
Expand Down Expand Up @@ -245,16 +254,21 @@ export class PivotFacet extends BaseFacet {
rowHeaderWidth: number,
): number {
const { colCfg, dataSet, filterDisplayDataItem } = this.cfg;
// current.width = get(colCfg, `widthByFieldValue.${current.value}`, current.width);
const userDragWidth = get(
get(colCfg, 'widthByFieldValue'),
`${col.value}`,
col.width,
);
if (userDragWidth) {
return userDragWidth;

const cellDraggedWidth = this.getCellDraggedWidth(col);

// 1. 拖拽后的宽度优先级最高
if (cellDraggedWidth) {
return cellDraggedWidth;
}

// 2. 其次是自定义, 返回 null 则使用默认宽度
const cellCustomWidth = this.getCellCustomWidth(col, colCfg?.width);
if (!isNil(cellCustomWidth)) {
return cellCustomWidth;
}

// 3. 紧凑布局
if (this.spreadsheet.getLayoutWidthType() === LayoutWidthTypes.Compact) {
const {
bolderText: colCellTextStyle,
Expand Down Expand Up @@ -326,10 +340,13 @@ export class PivotFacet extends BaseFacet {
appendedWidth
);
}
// adaptive

// 4. 自适应 adaptive
// 4.1 树状自定义
if (this.spreadsheet.isHierarchyTreeType()) {
return this.getAdaptTreeColWidth(col, colLeafNodes, rowLeafNodes);
}
// 4.2 网格自定义
return this.getAdaptGridColWidth(colLeafNodes, rowHeaderWidth);
}

Expand Down Expand Up @@ -598,14 +615,15 @@ export class PivotFacet extends BaseFacet {
private calculateGridRowNodesWidth(node: Node, colLeafNodes: Node[]): number {
const { rowCfg, spreadsheet } = this.cfg;

const userDragWidth = get(rowCfg, `widthByField.${node.key}`);
const userCustomWidth = get(rowCfg, 'width');
if (userDragWidth) {
return userDragWidth;
const cellDraggedWidth = get(rowCfg, `widthByField.${node.key}`);

if (cellDraggedWidth) {
return cellDraggedWidth;
}

if (userCustomWidth) {
return userCustomWidth;
const cellCustomWidth = this.getCellCustomWidth(node, rowCfg?.width);
if (!isNil(cellCustomWidth)) {
return cellCustomWidth;
}

if (spreadsheet.getLayoutWidthType() !== LayoutWidthTypes.Adaptive) {
Expand Down Expand Up @@ -709,12 +727,19 @@ export class PivotFacet extends BaseFacet {
*/
private getTreeRowHeaderWidth(): number {
const { rows, dataSet, rowCfg, treeRowsWidth } = this.cfg;
// user drag happened

// 1. 用户拖拽或手动指定的行头宽度优先级最高
if (rowCfg?.treeRowsWidth) {
return rowCfg?.treeRowsWidth;
}

// + province/city/level
// 2. 其次是自定义
const customRowWidth = this.getCellCustomWidth(null, rowCfg?.width);
if (customRowWidth) {
return customRowWidth;
}

// 3. 然后是计算 (+ icon province/city/level)
const treeHeaderLabel = rows
.map((key: string): string => dataSet.getFieldName(key))
.join('/');
Expand Down Expand Up @@ -758,7 +783,7 @@ export class PivotFacet extends BaseFacet {
} = spreadsheet.theme.cornerCell;
const { field, isLeaf } = node;

// calc rowNodeWitdh
// calc rowNode width
const rowIconWidth = this.getExpectedCellIconWidth(
CellTypes.ROW_CELL,
!spreadsheet.isValueInCols() &&
Expand Down
Loading

0 comments on commit d4f7dda

Please sign in to comment.