From ef26eb61491ec1b4c23965e0088518f4a251bfb6 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Fri, 14 Jun 2024 17:43:17 +0800 Subject: [PATCH 01/37] feat: add getCellAtRelativePosition api --- ...llAtRelativePosition_2024-06-14-09-37.json | 10 + packages/vtable/src/core/BaseTable.ts | 290 +------------ .../src/core/utils/get-cell-position.ts | 386 ++++++++++++++++++ packages/vtable/src/ts-types/base-table.ts | 3 + 4 files changed, 420 insertions(+), 269 deletions(-) create mode 100644 common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json create mode 100644 packages/vtable/src/core/utils/get-cell-position.ts diff --git a/common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json b/common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json new file mode 100644 index 000000000..610dfce7b --- /dev/null +++ b/common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "feat: add getCellAtRelativePosition api", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 723f98989..9b68fba5b 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -95,8 +95,6 @@ import { IconCache } from '../plugins/icons'; import { _applyColWidthLimits, _getScrollableVisibleRect, - _getTargetFrozenColAt, - _getTargetFrozenRowAt, _setDataSource, _setRecords, _toPxWidth, @@ -128,6 +126,16 @@ import { hideCellSelectBorder, restoreCellSelectBorder } from '../scenegraph/sel import type { ITextGraphicAttribute } from '@src/vrender'; import type { ISortedMapItem } from '../data/DataSource'; import { hasAutoImageColumn } from '../layout/layout-helper'; +import { + getCellAt, + getCellAtRelativePosition, + getColAt, + getRowAt, + getTargetColAt, + getTargetColAtConsiderRightFrozen, + getTargetRowAt, + getTargetRowAtConsiderBottomFrozen +} from './utils/get-cell-position'; const { toBoxArray } = utilStyle; const { isTouchEvent } = event; @@ -1734,20 +1742,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ getRowAt(absoluteY: number): { top: number; row: number; bottom: number; height: number } { - const frozen = _getTargetFrozenRowAt(this, absoluteY); - if (frozen) { - return frozen; - } - let row = this.getTargetRowAt(absoluteY); - if (!row) { - row = { - top: -1, - row: -1, - bottom: -1, - height: -1 - }; - } - return row; + return getRowAt(absoluteY, this); } /** * 根据x值计算所在列 @@ -1755,20 +1750,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ getColAt(absoluteX: number): { left: number; col: number; right: number; width: number } { - const frozen = _getTargetFrozenColAt(this, absoluteX); - if (frozen) { - return frozen; - } - let col = this.getTargetColAt(absoluteX); - if (!col) { - col = { - left: -1, - col: -1, - right: -1, - width: 1 - }; - } - return col; + return getColAt(absoluteX, this); } /** * 根据坐标值获取行列位置,index和rect范围 @@ -1777,23 +1759,11 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ getCellAt(absoluteX: number, absoluteY: number): CellAddressWithBound { - const rowInfo = this.getRowAt(absoluteY); - const { row, top, bottom, height } = rowInfo; - const colInfo = this.getColAt(absoluteX); - const { col, left, right, width } = colInfo; - const rect = { - left, - right, - top, - bottom, - width, - height - }; - return { - row, - col, - rect - }; + return getCellAt(absoluteX, absoluteY, this); + } + + getCellAtRelativePosition(relativeX: number, relativeY: number): CellAddressWithBound { + return getCellAtRelativePosition(relativeX, relativeY, this); } /** * 检查行列号是否正确 @@ -2453,67 +2423,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ getTargetColAt(absoluteX: number): { col: number; left: number; right: number; width: number } | null { - if (absoluteX === 0) { - return { left: 0, col: 0, right: 0, width: 0 }; - } - const findBefore = ( - startCol: number, - startRight: number - ): { - left: number; - col: number; - right: number; - width: number; - } | null => { - let right = startRight; - for (let col = startCol; col >= 0; col--) { - const width = this.getColWidth(col); - const left = right - width; - if (Math.round(left) <= Math.round(absoluteX) && Math.round(absoluteX) < Math.round(right)) { - return { - left, - col, - right, - width - }; - } - right = left; - } - return null; - }; - const findAfter = ( - startCol: number, - startRight: number - ): { - left: number; - col: number; - right: number; - width: number; - } | null => { - let left = startRight - this.getColWidth(startCol); - const { colCount } = this.internalProps; - for (let col = startCol; col < colCount; col++) { - const width = this.getColWidth(col); - const right = left + width; - if (Math.round(left) <= Math.round(absoluteX) && Math.round(absoluteX) < Math.round(right)) { - return { - left, - col, - right, - width - }; - } - left = right; - } - return null; - }; - //计算这个位置处是第几行 - const candCol = this.computeTargetColByX(absoluteX); - const right = this.getColsWidth(0, candCol); - if (absoluteX >= right) { - return findAfter(candCol, right); - } - return findBefore(candCol, right); + return getTargetColAt(absoluteX, this); } /** * 根据y获取该位置所处行值 @@ -2522,72 +2432,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ getTargetRowAt(absoluteY: number): { row: number; top: number; bottom: number; height: number } | null { - if (absoluteY === 0) { - return { top: 0, row: 0, bottom: 0, height: 0 }; - } - - const findBefore = ( - startRow: number, - startBottom: number - ): { - top: number; - row: number; - bottom: number; - height: number; - } | null => { - let bottom = startBottom; - for (let row = startRow; row >= 0; row--) { - const height = this.getRowHeight(row); - const top = bottom - height; - if (Math.round(top) <= Math.round(absoluteY) && Math.round(absoluteY) < Math.round(bottom)) { - return { - top, - row, - bottom, - height - }; - } - bottom = top; - } - return null; - }; - const findAfter = ( - startRow: number, - startBottom: number - ): { - top: number; - row: number; - bottom: number; - height: number; - } | null => { - let top = startBottom - this.getRowHeight(startRow); - const { rowCount } = this.internalProps; - for (let row = startRow; row < rowCount; row++) { - const height = this.getRowHeight(row); - const bottom = top + height; - if (Math.round(top) <= Math.round(absoluteY) && Math.round(absoluteY) < Math.round(bottom)) { - return { - top, - row, - bottom, - height - }; - } - top = bottom; - } - return null; - }; - // const candRow = Math.min( - // Math.ceil(absoluteY / this.internalProps.defaultRowHeight), - // this.rowCount - 1 - // ); - //计算这个位置处是第几行 - const candRow = this.computeTargetRowByY(absoluteY); - const bottom = this.getRowsHeight(0, candRow); - if (absoluteY >= bottom) { - return findAfter(candRow, bottom); - } - return findBefore(candRow, bottom); + return getTargetRowAt(absoluteY, this); } /** @@ -2600,26 +2445,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { absoluteX: number, isConsider: boolean ): { col: number; left: number; right: number; width: number } | null { - if (absoluteX === 0) { - return { left: 0, col: 0, right: 0, width: 0 }; - } - if ( - isConsider && - absoluteX > this.tableNoFrameWidth - this.getRightFrozenColsWidth() && - absoluteX < this.tableNoFrameWidth - ) { - for (let i = 0; i < this.rightFrozenColCount; i++) { - if (absoluteX > this.tableNoFrameWidth - this.getColsWidth(this.colCount - i - 1, this.colCount - 1)) { - return { - col: this.colCount - i - 1, - left: undefined, - right: undefined, - width: undefined - }; - } - } - } - return this.getTargetColAt(absoluteX); + return getTargetColAtConsiderRightFrozen(absoluteX, isConsider, this); } /** @@ -2632,83 +2458,9 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { absoluteY: number, isConsider: boolean ): { row: number; top: number; bottom: number; height: number } | null { - if (absoluteY === 0) { - return { top: 0, row: 0, bottom: 0, height: 0 }; - } - if ( - isConsider && - absoluteY > this.tableNoFrameHeight - this.getBottomFrozenRowsHeight() && - absoluteY < this.tableNoFrameHeight - ) { - for (let i = 0; i < this.rightFrozenColCount; i++) { - if (absoluteY > this.tableNoFrameHeight - this.getRowsHeight(this.rowCount - i - 1, this.rowCount - 1)) { - return { - row: this.rowCount - i - 1, - top: undefined, - bottom: undefined, - height: undefined - }; - } - } - } - return this.getTargetRowAt(absoluteY); + return getTargetRowAtConsiderBottomFrozen(absoluteY, isConsider, this); } - /** - * 根据y值(包括了scroll的)计算所在行 - * @param this - * @param absoluteY 左边y值,包含了scroll滚动距离 - * @returns - */ - private computeTargetRowByY(absoluteY: number): number { - let defaultRowHeight = this.internalProps.defaultRowHeight; - - //使用二分法计算出row - if (this._rowRangeHeightsMap.get(`$0$${this.rowCount - 1}`)) { - defaultRowHeight = this._rowRangeHeightsMap.get(`$0$${this.rowCount - 1}`) / this.rowCount; - // let startRow = 0; - // let endRow = this.rowCount - 1; - // while (endRow - startRow > 1) { - // const midRow = Math.floor((startRow + endRow) / 2); - // if (absoluteY < this._rowRangeHeightsMap.get(`$0$${midRow}`)) { - // endRow = midRow; - // } else if (absoluteY > this._rowRangeHeightsMap.get(`$0$${midRow}`)) { - // startRow = midRow; - // } else { - // return midRow; - // } - // } - // return endRow; - } - //否则使用defaultRowHeight大约计算一个row - return Math.min(Math.ceil(absoluteY / defaultRowHeight), this.rowCount - 1); - } - /** - * 根据x值(包括了scroll的)计算所在列 主要借助colRangeWidthsMap缓存来提高计算效率 - * @param this - * @param absoluteX 左边x值,包含了scroll滚动距离 - * @returns - */ - private computeTargetColByX(absoluteX: number): number { - //使用二分法计算出col - if (this._colRangeWidthsMap.get(`$0$${this.colCount - 1}`)) { - let startCol = 0; - let endCol = this.colCount - 1; - while (endCol - startCol > 1) { - const midCol = Math.floor((startCol + endCol) / 2); - if (absoluteX < this._colRangeWidthsMap.get(`$0$${midCol}`)) { - endCol = midCol; - } else if (absoluteX > this._colRangeWidthsMap.get(`$0$${midCol}`)) { - startCol = midCol; - } else { - return midCol; - } - } - return endCol; - } - //否则使用defaultColWidth大约计算一个col - return Math.min(Math.ceil(absoluteX / this.internalProps.defaultColWidth), this.colCount - 1); - } /** * 清除选中单元格 */ diff --git a/packages/vtable/src/core/utils/get-cell-position.ts b/packages/vtable/src/core/utils/get-cell-position.ts new file mode 100644 index 000000000..b7565c47d --- /dev/null +++ b/packages/vtable/src/core/utils/get-cell-position.ts @@ -0,0 +1,386 @@ +import type { CellAddressWithBound } from '../../ts-types'; +import type { BaseTableAPI } from '../../ts-types/base-table'; +import { _getTargetFrozenColAt, _getTargetFrozenRowAt } from '../tableHelper'; + +/** + * 根据y值计算所在行 + * @param absoluteY + * @returns + */ +export function getRowAt( + absoluteY: number, + _this: BaseTableAPI +): { top: number; row: number; bottom: number; height: number } { + const frozen = _getTargetFrozenRowAt(_this, absoluteY); + if (frozen) { + return frozen; + } + let row = getTargetRowAt(absoluteY, _this); + if (!row) { + row = { + top: -1, + row: -1, + bottom: -1, + height: -1 + }; + } + return row; +} +/** + * 根据x值计算所在列 + * @param absoluteX + * @returns + */ +export function getColAt( + absoluteX: number, + _this: BaseTableAPI +): { left: number; col: number; right: number; width: number } { + const frozen = _getTargetFrozenColAt(_this, absoluteX); + if (frozen) { + return frozen; + } + let col = getTargetColAt(absoluteX, _this); + if (!col) { + col = { + left: -1, + col: -1, + right: -1, + width: 1 + }; + } + return col; +} +/** + * 根据坐标值获取行列位置,index和rect范围 + * @param absoluteX + * @param absoluteY + * @returns + */ +export function getCellAt(absoluteX: number, absoluteY: number, _this: BaseTableAPI): CellAddressWithBound { + const rowInfo = getRowAt(absoluteY, _this); + const { row, top, bottom, height } = rowInfo; + const colInfo = getColAt(absoluteX, _this); + const { col, left, right, width } = colInfo; + const rect = { + left, + right, + top, + bottom, + width, + height + }; + return { + row, + col, + rect + }; +} + +/** + * 根据x获取该位置所处列值 + * @param table + * @param absoluteX + * @returns + */ +export function getTargetColAt( + absoluteX: number, + _this: BaseTableAPI +): { col: number; left: number; right: number; width: number } | null { + if (absoluteX === 0) { + return { left: 0, col: 0, right: 0, width: 0 }; + } + const findBefore = ( + startCol: number, + startRight: number + ): { + left: number; + col: number; + right: number; + width: number; + } | null => { + let right = startRight; + for (let col = startCol; col >= 0; col--) { + const width = _this.getColWidth(col); + const left = right - width; + if (Math.round(left) <= Math.round(absoluteX) && Math.round(absoluteX) < Math.round(right)) { + return { + left, + col, + right, + width + }; + } + right = left; + } + return null; + }; + const findAfter = ( + startCol: number, + startRight: number + ): { + left: number; + col: number; + right: number; + width: number; + } | null => { + let left = startRight - _this.getColWidth(startCol); + const { colCount } = _this.internalProps; + for (let col = startCol; col < colCount; col++) { + const width = _this.getColWidth(col); + const right = left + width; + if (Math.round(left) <= Math.round(absoluteX) && Math.round(absoluteX) < Math.round(right)) { + return { + left, + col, + right, + width + }; + } + left = right; + } + return null; + }; + //计算这个位置处是第几行 + const candCol = computeTargetColByX(absoluteX, _this); + const right = _this.getColsWidth(0, candCol); + if (absoluteX >= right) { + return findAfter(candCol, right); + } + return findBefore(candCol, right); +} +/** + * 根据y获取该位置所处行值 + * @param table + * @param absoluteX + * @returns + */ +export function getTargetRowAt( + absoluteY: number, + _this: BaseTableAPI +): { row: number; top: number; bottom: number; height: number } | null { + if (absoluteY === 0) { + return { top: 0, row: 0, bottom: 0, height: 0 }; + } + + const findBefore = ( + startRow: number, + startBottom: number + ): { + top: number; + row: number; + bottom: number; + height: number; + } | null => { + let bottom = startBottom; + for (let row = startRow; row >= 0; row--) { + const height = _this.getRowHeight(row); + const top = bottom - height; + if (Math.round(top) <= Math.round(absoluteY) && Math.round(absoluteY) < Math.round(bottom)) { + return { + top, + row, + bottom, + height + }; + } + bottom = top; + } + return null; + }; + const findAfter = ( + startRow: number, + startBottom: number + ): { + top: number; + row: number; + bottom: number; + height: number; + } | null => { + let top = startBottom - _this.getRowHeight(startRow); + const { rowCount } = _this.internalProps; + for (let row = startRow; row < rowCount; row++) { + const height = _this.getRowHeight(row); + const bottom = top + height; + if (Math.round(top) <= Math.round(absoluteY) && Math.round(absoluteY) < Math.round(bottom)) { + return { + top, + row, + bottom, + height + }; + } + top = bottom; + } + return null; + }; + // const candRow = Math.min( + // Math.ceil(absoluteY / this.internalProps.defaultRowHeight), + // this.rowCount - 1 + // ); + //计算这个位置处是第几行 + const candRow = computeTargetRowByY(absoluteY, _this); + const bottom = _this.getRowsHeight(0, candRow); + if (absoluteY >= bottom) { + return findAfter(candRow, bottom); + } + return findBefore(candRow, bottom); +} + +/** + * 根据x获取该位置所处列值 + * @param table + * @param absoluteX + * @returns + */ +export function getTargetColAtConsiderRightFrozen( + absoluteX: number, + isConsider: boolean, + _this: BaseTableAPI +): { col: number; left: number; right: number; width: number } | null { + if (absoluteX === 0) { + return { left: 0, col: 0, right: 0, width: 0 }; + } + if ( + isConsider && + absoluteX > _this.tableNoFrameWidth - _this.getRightFrozenColsWidth() && + absoluteX < _this.tableNoFrameWidth + ) { + for (let i = 0; i < _this.rightFrozenColCount; i++) { + if (absoluteX > _this.tableNoFrameWidth - _this.getColsWidth(_this.colCount - i - 1, _this.colCount - 1)) { + return { + col: _this.colCount - i - 1, + left: undefined, + right: undefined, + width: undefined + }; + } + } + } + return getTargetColAt(absoluteX, _this); +} + +/** + * 根据y获取该位置所处行值 + * @param table + * @param absoluteX + * @returns + */ +export function getTargetRowAtConsiderBottomFrozen( + absoluteY: number, + isConsider: boolean, + _this: BaseTableAPI +): { row: number; top: number; bottom: number; height: number } | null { + if (absoluteY === 0) { + return { top: 0, row: 0, bottom: 0, height: 0 }; + } + if ( + isConsider && + absoluteY > _this.tableNoFrameHeight - _this.getBottomFrozenRowsHeight() && + absoluteY < _this.tableNoFrameHeight + ) { + for (let i = 0; i < _this.rightFrozenColCount; i++) { + if (absoluteY > _this.tableNoFrameHeight - _this.getRowsHeight(_this.rowCount - i - 1, _this.rowCount - 1)) { + return { + row: _this.rowCount - i - 1, + top: undefined, + bottom: undefined, + height: undefined + }; + } + } + } + return getTargetRowAt(absoluteY, _this); +} + +/** + * 根据y值(包括了scroll的)计算所在行 + * @param this + * @param absoluteY 左边y值,包含了scroll滚动距离 + * @returns + */ +export function computeTargetRowByY(absoluteY: number, _this: BaseTableAPI): number { + let defaultRowHeight = _this.internalProps.defaultRowHeight; + + //使用二分法计算出row + if (_this._rowRangeHeightsMap.get(`$0$${_this.rowCount - 1}`)) { + defaultRowHeight = _this._rowRangeHeightsMap.get(`$0$${_this.rowCount - 1}`) / _this.rowCount; + // let startRow = 0; + // let endRow = this.rowCount - 1; + // while (endRow - startRow > 1) { + // const midRow = Math.floor((startRow + endRow) / 2); + // if (absoluteY < this._rowRangeHeightsMap.get(`$0$${midRow}`)) { + // endRow = midRow; + // } else if (absoluteY > this._rowRangeHeightsMap.get(`$0$${midRow}`)) { + // startRow = midRow; + // } else { + // return midRow; + // } + // } + // return endRow; + } + //否则使用defaultRowHeight大约计算一个row + return Math.min(Math.ceil(absoluteY / defaultRowHeight), _this.rowCount - 1); +} +/** + * 根据x值(包括了scroll的)计算所在列 主要借助colRangeWidthsMap缓存来提高计算效率 + * @param this + * @param absoluteX 左边x值,包含了scroll滚动距离 + * @returns + */ +export function computeTargetColByX(absoluteX: number, _this: BaseTableAPI): number { + //使用二分法计算出col + if (_this._colRangeWidthsMap.get(`$0$${_this.colCount - 1}`)) { + let startCol = 0; + let endCol = _this.colCount - 1; + while (endCol - startCol > 1) { + const midCol = Math.floor((startCol + endCol) / 2); + if (absoluteX < _this._colRangeWidthsMap.get(`$0$${midCol}`)) { + endCol = midCol; + } else if (absoluteX > _this._colRangeWidthsMap.get(`$0$${midCol}`)) { + startCol = midCol; + } else { + return midCol; + } + } + return endCol; + } + //否则使用defaultColWidth大约计算一个col + return Math.min(Math.ceil(absoluteX / _this.internalProps.defaultColWidth), _this.colCount - 1); +} + +// 获取屏幕坐标对应的单元格信息,考虑滚动 +export function getCellAtRelativePosition(x: number, y: number, _this: BaseTableAPI): CellAddressWithBound { + // table border and outer component + x -= _this.tableX; + y -= _this.tableY; + + // bottom frozen + let bottomFrozen = false; + if (y > _this.tableNoFrameHeight - _this.getBottomFrozenRowsHeight() && y < _this.tableNoFrameHeight) { + bottomFrozen = true; + } + // right frozen + let rightFrozen = false; + if (x > _this.tableNoFrameWidth - _this.getRightFrozenColsWidth() && x < _this.tableNoFrameWidth) { + rightFrozen = true; + } + + const colInfo = getTargetColAtConsiderRightFrozen(rightFrozen ? x : x + _this.scrollLeft, rightFrozen, _this); + const rowInfo = getTargetRowAtConsiderBottomFrozen(bottomFrozen ? y : y + _this.scrollTop, bottomFrozen, _this); + + const { row, top, bottom, height } = rowInfo; + const { col, left, right, width } = colInfo; + const rect = { + left, + right, + top, + bottom, + width, + height + }; + return { + row, + col, + rect + }; +} diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index f2a4474e7..3b0ea9b0c 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -565,6 +565,9 @@ export interface BaseTableAPI { columnWidthComputeMode?: 'normal' | 'only-header' | 'only-body'; + _rowRangeHeightsMap: Map; + _colRangeWidthsMap: Map; + /** 获取表格绘制的范围 不包括frame的宽度 */ getDrawRange: () => Rect; /** 将鼠标坐标值 转换成表格坐标系中的坐标位置 */ From 3511d93a216d1849d9efd1fcc75677df270cfdf0 Mon Sep 17 00:00:00 2001 From: YUOrz Date: Fri, 21 Jun 2024 18:16:46 +0800 Subject: [PATCH 02/37] fix: fix keydown enter problem #1965 --- .../src/event/listener/container-dom.ts | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/vtable/src/event/listener/container-dom.ts b/packages/vtable/src/event/listener/container-dom.ts index 7f095cc0c..277f2a7af 100644 --- a/packages/vtable/src/event/listener/container-dom.ts +++ b/packages/vtable/src/event/listener/container-dom.ts @@ -121,22 +121,26 @@ export function bindContainerDomListener(eventManager: EventManager) { } else if (e.key === 'Enter') { // 如果按enter键 可以结束当前的编辑 或开启编辑选中的单元格(仅限单选) if ((table as ListTableAPI).editorManager.editingEditor) { + // 如果是结束当前编辑,且有主动监听keydown事件,则先触发keydown事件,之后再结束编辑 + handleKeydownListener(e); + (table as ListTableAPI).editorManager.completeEdit(); table.getElement().focus(); - } else { - if ( - (table.options.keyboardOptions?.editCellOnEnter ?? true) && - (table.stateManager.select.ranges?.length ?? 0) === 1 - ) { - // 如果开启按enter键进入编辑的配置 且当前有选中的单元格 则进入编辑 - const startCol = table.stateManager.select.ranges[0].start.col; - const startRow = table.stateManager.select.ranges[0].start.row; - const endCol = table.stateManager.select.ranges[0].end.col; - const endRow = table.stateManager.select.ranges[0].end.row; - if (startCol === endCol && startRow === endRow) { - if ((table as ListTableAPI).getEditor(startCol, startRow)) { - (table as ListTableAPI).editorManager.startEditCell(startCol, startRow); - } + // 直接返回,不再触发最后的keydown监听事件相关代码 + return; + } + if ( + (table.options.keyboardOptions?.editCellOnEnter ?? true) && + (table.stateManager.select.ranges?.length ?? 0) === 1 + ) { + // 如果开启按enter键进入编辑的配置 且当前有选中的单元格 则进入编辑 + const startCol = table.stateManager.select.ranges[0].start.col; + const startRow = table.stateManager.select.ranges[0].start.row; + const endCol = table.stateManager.select.ranges[0].end.col; + const endRow = table.stateManager.select.ranges[0].end.row; + if (startCol === endCol && startRow === endRow) { + if ((table as ListTableAPI).getEditor(startCol, startRow)) { + (table as ListTableAPI).editorManager.startEditCell(startCol, startRow); } } } @@ -168,6 +172,13 @@ export function bindContainerDomListener(eventManager: EventManager) { } } + handleKeydownListener(e); + }); + /** + * 处理主动注册的keydown事件 + * @param e + */ + function handleKeydownListener(e: KeyboardEvent) { if ((table as any).hasListeners(TABLE_EVENT_TYPE.KEYDOWN)) { const cellsEvent: KeydownEvent = { keyCode: e.keyCode ?? e.which, @@ -178,7 +189,7 @@ export function bindContainerDomListener(eventManager: EventManager) { }; table.fireListeners(TABLE_EVENT_TYPE.KEYDOWN, cellsEvent); } - }); + } handler.on(table.getElement(), 'copy', (e: KeyboardEvent) => { if (table.keyboardOptions?.copySelected) { From 4825045aee98550484576cb54e18f1d9633fe996 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 25 Jun 2024 12:03:41 +0800 Subject: [PATCH 03/37] fix: fix merge cell update performance problem #1972 --- .../fix-merge-cell-pref_2024-06-25-03-42.json | 10 +++++++++ .../vtable/src/scenegraph/component/custom.ts | 6 +++++- .../scenegraph/group-creater/cell-helper.ts | 21 ++++++++++++++++--- .../group-creater/progress/proxy.ts | 2 ++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json diff --git a/common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json b/common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json new file mode 100644 index 000000000..9074db2c6 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix merge cell update performance problem #1972", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/component/custom.ts b/packages/vtable/src/scenegraph/component/custom.ts index 8548f837f..38b484ea2 100644 --- a/packages/vtable/src/scenegraph/component/custom.ts +++ b/packages/vtable/src/scenegraph/component/custom.ts @@ -519,6 +519,9 @@ function bindAttributeUpdate(group: VGroup, col: number, row: number, index: num function onBeforeAttributeUpdate(val: Record, attribute: any) { // @ts-ignore const graphic = this as any; + if (graphic.skipMergeUpdate) { + return; + } const cellGroup = getTargetCell(graphic) as Group; const table = ((cellGroup as any).stage as any).table as BaseTableAPI; graphic.skipAttributeUpdate = true; @@ -535,7 +538,8 @@ function onBeforeAttributeUpdate(val: Record, attribute: any) { if (col === cellGroup.col && row === cellGroup.row) { continue; } - const cell = table.scenegraph.getCell(col, row); + // const cell = table.scenegraph.getCell(col, row); + const cell = table.scenegraph.highPerformanceGetCell(col, row); if (cell.role === 'cell') { const target = cell.getChildByName(graphic.name, true); if (!target || target.skipAttributeUpdate) { diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 06a30fcdc..895810202 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -746,9 +746,14 @@ export function dealWithMergeCellSize( ) { for (let col = range.start.col; col <= range.end.col; col++) { for (let row = range.start.row; row <= range.end.row; row++) { - const cellGroup = table.scenegraph.getCell(col, row, true); + // const cellGroup = table.scenegraph.getCell(col, row, true); + const cellGroup = table.scenegraph.highPerformanceGetCell(col, row, true); - if (cellGroup.role === 'cell' && range.start.row !== range.end.row && cellGroup.contentHeight !== cellHeight) { + if (cellGroup.role !== 'cell') { + continue; + } + + if (range.start.row !== range.end.row && cellGroup.contentHeight !== cellHeight) { updateCellContentHeight( cellGroup, cellHeight, @@ -761,7 +766,7 @@ export function dealWithMergeCellSize( // 'middle' ); } - if (cellGroup.role === 'cell' && range.start.col !== range.end.col && cellGroup.contentWidth !== cellWidth) { + if (range.start.col !== range.end.col && cellGroup.contentWidth !== cellWidth) { updateCellContentWidth( cellGroup, cellWidth, @@ -801,25 +806,33 @@ export function resizeCellGroup( cellGroup.forEachChildren((child: IGraphic) => { // 利用_dx hack解决掉 合并单元格的范围内的格子依次执行该方法 如果挨个调用updateCell的话 执行多次后dx累计问题 if (typeof child._dx === 'number') { + child.skipMergeUpdate = true; child.setAttributes({ dx: (child._dx ?? 0) + dx }); + child.skipMergeUpdate = false; } else { + child.skipMergeUpdate = true; child._dx = child.attribute.dx ?? 0; child.setAttributes({ dx: (child.attribute.dx ?? 0) + dx }); + child.skipMergeUpdate = false; } if (typeof child._dy === 'number') { + child.skipMergeUpdate = true; child.setAttributes({ dy: (child._dy ?? 0) + dy }); + child.skipMergeUpdate = false; } else { child._dy = child.attribute.dy ?? 0; + child.skipMergeUpdate = true; child.setAttributes({ dy: (child.attribute.dy ?? 0) + dy }); + child.skipMergeUpdate = false; } }); @@ -843,11 +856,13 @@ export function resizeCellGroup( const widthChange = rangeWidth !== cellGroup.attribute.width; const heightChange = rangeHeight !== cellGroup.attribute.height; + (cellGroup as any).skipMergeUpdate = true; cellGroup.setAttributes({ width: rangeWidth, height: rangeHeight, strokeArrayWidth: newLineWidth } as any); + (cellGroup as any).skipMergeUpdate = false; cellGroup.mergeStartCol = range.start.col; cellGroup.mergeStartRow = range.start.row; diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index 801a24b9c..5c72abb70 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -107,6 +107,7 @@ export class SceneProxy { this.totalActualBodyColCount = totalActualBodyColCount; this.totalCol = this.bodyLeftCol + totalActualBodyColCount - 1; // 目标渐进完成的col this.colStart = this.bodyLeftCol; + this.colEnd = this.totalCol; // temp for first screen, will replace in createGroupForFirstScreen() const defaultColWidth = this.table.defaultColWidth; // const defaultColWidth = getDefaultHeight(this.table); this.taskColCount = Math.ceil(this.table.tableNoFrameWidth / defaultColWidth) * 1; @@ -140,6 +141,7 @@ export class SceneProxy { this.totalActualBodyRowCount = totalActualBodyRowCount; this.totalRow = this.bodyTopRow + totalActualBodyRowCount - 1; // 目标渐进完成的row this.rowStart = this.bodyTopRow; + this.rowEnd = this.totalRow; // temp for first screen, will replace in createGroupForFirstScreen() const defaultRowHeight = this.table.defaultRowHeight; // const defaultRowHeight = getDefaultWidth(this.table); this.taskRowCount = Math.ceil(this.table.tableNoFrameHeight / defaultRowHeight) * 1; From b377dd2451c0db5680afdf15f7c6b5e05f775bd0 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 25 Jun 2024 14:46:37 +0800 Subject: [PATCH 04/37] fix: selected_clear event trigger #1981 --- packages/vtable/src/event/listener/table-group.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index b41964d52..15c4420c8 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -347,6 +347,7 @@ export function bindTableGroupListener(eventManager: EventManager) { //点击到表格外部不需要取消选中状态 if (table.options.select?.outsideClickDeselect) { eventManager.dealTableSelect(); + stateManager.endSelectCells(); } }); @@ -738,12 +739,11 @@ export function bindTableGroupListener(eventManager: EventManager) { ) { stateManager.updateInteractionState(InteractionState.default); eventManager.dealTableHover(); - stateManager.endSelectCells(); - // 点击空白区域取消选中 if (table.options.select?.blankAreaClickDeselect ?? true) { eventManager.dealTableSelect(); } + stateManager.endSelectCells(); stateManager.updateCursor(); table.scenegraph.updateChartState(null); From e9296c9cae6d9dc1f2a9480a762231221a4a805f Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 25 Jun 2024 14:47:10 +0800 Subject: [PATCH 05/37] docs: update changlog of rush --- ...981-bug-selected_clear-event_2024-06-25-06-47.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json diff --git a/common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json b/common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json new file mode 100644 index 000000000..f8ea496dc --- /dev/null +++ b/common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: selected_clear event trigger #1981\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From 6fe58aaa9696ab73dd5babf19e576e3c97a3b258 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 25 Jun 2024 15:48:25 +0800 Subject: [PATCH 06/37] fix: fix colEnd & rowEnd update timing --- .../src/scenegraph/group-creater/progress/proxy.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index 5c72abb70..d52993b6a 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -277,6 +277,8 @@ export class SceneProxy { // compute rows height computeRowsHeight(this.table, this.currentRow + 1, endRow, false); + this.rowEnd = endRow; + if (this.table.frozenColCount) { // create row header row cellGroup let maxHeight = 0; @@ -345,9 +347,7 @@ export class SceneProxy { this.table.scenegraph.bodyGroup.setAttribute('height', maxHeight); this.currentRow = endRow; - this.rowEnd = endRow; this.rowUpdatePos = this.rowEnd; - // this.referenceRow = this.rowStart + Math.floor((endRow - this.rowStart) / 2); // update container group size and border this.table.scenegraph.updateContainer(); @@ -359,6 +359,8 @@ export class SceneProxy { const endCol = Math.min(this.totalCol, this.currentCol + onceCount); computeColsWidth(this.table, this.currentCol + 1, endCol); + this.colEnd = endCol; + // update last merge cell size for (let row = 0; row < this.table.rowCount; row++) { const cellGroup = this.highPerformanceGetCell(this.currentCol, row); @@ -441,13 +443,9 @@ export class SceneProxy { ); this.currentCol = endCol; - this.colEnd = endCol; this.colUpdatePos = this.colEnd; - // this.referenceCol = this.colStart + Math.floor((endCol - this.colStart) / 2); - // console.log('async', this.referenceCol, this.colStart, this.colEnd); // update container group size and border - // this.table.scenegraph.updateContainerAttrWidthAndX(); this.table.scenegraph.updateContainer(); this.table.scenegraph.updateBorderSizeAndPosition(); } From 5c9afa9370da0b192c6d774fb1d2790ef3ba401b Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 25 Jun 2024 15:59:32 +0800 Subject: [PATCH 07/37] chore: add docs build script --- common/config/rush/pnpm-lock.yaml | 2 ++ docs/package.json | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 3a292093d..156d9625e 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -22,6 +22,7 @@ importers: '@visactor/vtable-editors': workspace:* '@visactor/vtable-export': workspace:* '@visactor/vtable-search': workspace:* + '@visactor/vutils': ~0.18.9 '@vitejs/plugin-react': 3.1.0 axios: ^1.4.0 buble: ^0.20.0 @@ -50,6 +51,7 @@ importers: '@visactor/vtable-editors': link:../packages/vtable-editors '@visactor/vtable-export': link:../packages/vtable-export '@visactor/vtable-search': link:../packages/vtable-search + '@visactor/vutils': 0.18.9 axios: 1.7.2 buble: 0.20.0 highlight.js: 11.9.0 diff --git a/docs/package.json b/docs/package.json index 3dcb57689..072c7bfcc 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,6 +5,7 @@ "private": true, "scripts": { "serve": "vite --host", + "build": "vite build && cp -r ./assets ./dist", "watch": "node ./libs/template-parse/build.js --env dev --watch", "start": "npx concurrently --kill-others \"npm:watch\" \"npm:serve\"" }, @@ -25,7 +26,8 @@ "react-dom": "^18.0.0", "react-router-dom": "6.9.0", "lodash": "4.17.21", - "openinula": "~0.1.2-SNAPSHOT" + "openinula": "~0.1.2-SNAPSHOT", + "@visactor/vutils": "~0.18.9" }, "devDependencies": { "@internal/eslint-config": "workspace:*", From 509eff4cd32ca99f79d706ec8039759f451a8db9 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 25 Jun 2024 16:48:21 +0800 Subject: [PATCH 08/37] docs: update export demos --- .../demo/en/export/table-export-format.md | 204 ++++-------------- .../en/export/table-export-ignore-icon.md | 200 ++++++++++++++--- .../demo/zh/export/table-export-format.md | 204 ++++-------------- .../zh/export/table-export-ignore-icon.md | 200 ++++++++++++++--- 4 files changed, 412 insertions(+), 396 deletions(-) diff --git a/docs/assets/demo/en/export/table-export-format.md b/docs/assets/demo/en/export/table-export-format.md index ecfd11b36..1b3c9aca9 100644 --- a/docs/assets/demo/en/export/table-export-format.md +++ b/docs/assets/demo/en/export/table-export-format.md @@ -24,180 +24,48 @@ By default, when exporting, the text or image inside the exported cell will be o // } from "@visactor/vtable-export"; // When umd is introduced, the export tool will be mounted to VTable.export -const data = [ +const records = [ + { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, + { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, + { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, + { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, + { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } +]; + +const columns = [ { - 类别: '办公用品', - 销售额: '129.696', - 数量: '2', - 利润: '-60.704', - children: [ - { - 类别: '信封', // 对应原子类别 - 销售额: '125.44', - 数量: '2', - 利润: '42.56', - children: [ - { - 类别: '黄色信封', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '白色信封', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - }, - { - 类别: '器具', // 对应原子类别 - 销售额: '1375.92', - 数量: '3', - 利润: '550.2', - children: [ - { - 类别: '订书机', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '计算器', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - } - ] + field: 'isCheck', + title: '', + width: 60, + headerType: 'checkbox', + cellType: 'checkbox' }, { - 类别: '技术', - 销售额: '229.696', - 数量: '20', - 利润: '90.704', - children: [ - { - 类别: '设备', // 对应原子类别 - 销售额: '225.44', - 数量: '5', - 利润: '462.56' - }, - { - 类别: '配件', // 对应原子类别 - 销售额: '375.92', - 数量: '8', - 利润: '550.2' - }, - { - 类别: '复印机', // 对应原子类别 - 销售额: '425.44', - 数量: '7', - 利润: '342.56' - }, - { - 类别: '电话', // 对应原子类别 - 销售额: '175.92', - 数量: '6', - 利润: '750.2' - } - ] + field: 'productName', + title: 'productName', + width: 120 }, { - 类别: '家具', - 销售额: '129.696', - 数量: '2', - 利润: '-60.704', - children: [ - { - 类别: '桌子', // 对应原子类别 - 销售额: '125.44', - 数量: '2', - 利润: '42.56', - children: [ - { - 类别: '黄色桌子', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '白色桌子', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - }, - { - 类别: '椅子', // 对应原子类别 - 销售额: '1375.92', - 数量: '3', - 利润: '550.2', - children: [ - { - 类别: '老板椅', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '沙发椅', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - } - ] + field: 'price', + title: 'checkbox', + width: 120, + cellType: 'checkbox', + disable: true, + checked: true }, { - 类别: '生活家电(懒加载)', - 销售额: '229.696', - 数量: '20', - 利润: '90.704' + field: 'check', + title: 'checkbox', + width: 120, + cellType: 'checkbox' + // disable: true } ]; const option = { - container: document.getElementById(CONTAINER_ID), - columns: [ - { - field: '类别', - tree: true, - title: '类别', - width: 'auto', - sort: true - }, - { - field: '销售额', - title: '销售额', - width: 'auto', - sort: true - // tree: true, - }, - { - field: '利润', - title: '利润', - width: 'auto', - sort: true - } - ], - showPin: true, //显示VTable内置冻结列图标 - widthMode: 'standard', - allowFrozenColCount: 2, - records: data, - - hierarchyIndent: 20, - hierarchyExpandLevel: 2, - hierarchyTextStartAlignment: true, - sortState: { - field: '销售额', - order: 'asc' - }, - theme: VTable.themes.BRIGHT, - defaultRowHeight: 32 + records, + columns }; const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window.tableInstance = tableInstance; @@ -233,7 +101,13 @@ function bindExport() { exportExcelButton.addEventListener('click', async () => { if (window.tableInstance) { - downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + downloadExcel(await exportVTableToExcel(window.tableInstance, { + formatExportOutput: ({ cellType, cellValue, table, col, row }) => { + if (cellType === 'checkbox') { + return table.getCellCheckboxState(col, row) ? 'true' : 'false'; + } + } + }), 'export'); } }); } diff --git a/docs/assets/demo/en/export/table-export-ignore-icon.md b/docs/assets/demo/en/export/table-export-ignore-icon.md index 3e0c1a4ed..2640825e1 100644 --- a/docs/assets/demo/en/export/table-export-ignore-icon.md +++ b/docs/assets/demo/en/export/table-export-ignore-icon.md @@ -24,48 +24,180 @@ By default, when the cell has an icon, the icon and text will be treated as an i // } from "@visactor/vtable-export"; // When umd is introduced, the export tool will be mounted to VTable.export -const records = [ - { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, - { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, - { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, - { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, - { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, - { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, - { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } -]; - -const columns = [ +const data = [ { - field: 'isCheck', - title: '', - width: 60, - headerType: 'checkbox', - cellType: 'checkbox' + 类别: '办公用品', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '信封', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色信封', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色信封', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '器具', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '订书机', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '计算器', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] }, { - field: 'productName', - title: 'productName', - width: 120 + 类别: '技术', + 销售额: '229.696', + 数量: '20', + 利润: '90.704', + children: [ + { + 类别: '设备', // 对应原子类别 + 销售额: '225.44', + 数量: '5', + 利润: '462.56' + }, + { + 类别: '配件', // 对应原子类别 + 销售额: '375.92', + 数量: '8', + 利润: '550.2' + }, + { + 类别: '复印机', // 对应原子类别 + 销售额: '425.44', + 数量: '7', + 利润: '342.56' + }, + { + 类别: '电话', // 对应原子类别 + 销售额: '175.92', + 数量: '6', + 利润: '750.2' + } + ] }, { - field: 'price', - title: 'checkbox', - width: 120, - cellType: 'checkbox', - disable: true, - checked: true + 类别: '家具', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '桌子', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色桌子', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色桌子', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '椅子', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '老板椅', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '沙发椅', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] }, { - field: 'check', - title: 'checkbox', - width: 120, - cellType: 'checkbox' - // disable: true + 类别: '生活家电(懒加载)', + 销售额: '229.696', + 数量: '20', + 利润: '90.704' } ]; const option = { - records, - columns + container: document.getElementById(CONTAINER_ID), + columns: [ + { + field: '类别', + tree: true, + title: '类别', + width: 'auto', + sort: true + }, + { + field: '销售额', + title: '销售额', + width: 'auto', + sort: true + // tree: true, + }, + { + field: '利润', + title: '利润', + width: 'auto', + sort: true + } + ], + showPin: true, //显示VTable内置冻结列图标 + widthMode: 'standard', + allowFrozenColCount: 2, + records: data, + + hierarchyIndent: 20, + hierarchyExpandLevel: 2, + hierarchyTextStartAlignment: true, + sortState: { + field: '销售额', + order: 'asc' + }, + theme: VTable.themes.BRIGHT, + defaultRowHeight: 32 }; const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window.tableInstance = tableInstance; @@ -101,7 +233,9 @@ function bindExport() { exportExcelButton.addEventListener('click', async () => { if (window.tableInstance) { - downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + downloadExcel(await exportVTableToExcel(window.tableInstance, { + ignoreIcon: true + }), 'export'); } }); } diff --git a/docs/assets/demo/zh/export/table-export-format.md b/docs/assets/demo/zh/export/table-export-format.md index 27806c3a1..c15733142 100644 --- a/docs/assets/demo/zh/export/table-export-format.md +++ b/docs/assets/demo/zh/export/table-export-format.md @@ -24,180 +24,48 @@ link: '../../guide/export/excel' // } from "@visactor/vtable-export"; // umd引入时导出工具会挂载到VTable.export -const data = [ +const records = [ + { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, + { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, + { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, + { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, + { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } +]; + +const columns = [ { - 类别: '办公用品', - 销售额: '129.696', - 数量: '2', - 利润: '-60.704', - children: [ - { - 类别: '信封', // 对应原子类别 - 销售额: '125.44', - 数量: '2', - 利润: '42.56', - children: [ - { - 类别: '黄色信封', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '白色信封', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - }, - { - 类别: '器具', // 对应原子类别 - 销售额: '1375.92', - 数量: '3', - 利润: '550.2', - children: [ - { - 类别: '订书机', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '计算器', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - } - ] + field: 'isCheck', + title: '', + width: 60, + headerType: 'checkbox', + cellType: 'checkbox' }, { - 类别: '技术', - 销售额: '229.696', - 数量: '20', - 利润: '90.704', - children: [ - { - 类别: '设备', // 对应原子类别 - 销售额: '225.44', - 数量: '5', - 利润: '462.56' - }, - { - 类别: '配件', // 对应原子类别 - 销售额: '375.92', - 数量: '8', - 利润: '550.2' - }, - { - 类别: '复印机', // 对应原子类别 - 销售额: '425.44', - 数量: '7', - 利润: '342.56' - }, - { - 类别: '电话', // 对应原子类别 - 销售额: '175.92', - 数量: '6', - 利润: '750.2' - } - ] + field: 'productName', + title: 'productName', + width: 120 }, { - 类别: '家具', - 销售额: '129.696', - 数量: '2', - 利润: '-60.704', - children: [ - { - 类别: '桌子', // 对应原子类别 - 销售额: '125.44', - 数量: '2', - 利润: '42.56', - children: [ - { - 类别: '黄色桌子', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '白色桌子', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - }, - { - 类别: '椅子', // 对应原子类别 - 销售额: '1375.92', - 数量: '3', - 利润: '550.2', - children: [ - { - 类别: '老板椅', - 销售额: '125.44', - 数量: '2', - 利润: '42.56' - }, - { - 类别: '沙发椅', - 销售额: '1375.92', - 数量: '3', - 利润: '550.2' - } - ] - } - ] + field: 'price', + title: 'checkbox', + width: 120, + cellType: 'checkbox', + disable: true, + checked: true }, { - 类别: '生活家电(懒加载)', - 销售额: '229.696', - 数量: '20', - 利润: '90.704' + field: 'check', + title: 'checkbox', + width: 120, + cellType: 'checkbox' + // disable: true } ]; const option = { - container: document.getElementById(CONTAINER_ID), - columns: [ - { - field: '类别', - tree: true, - title: '类别', - width: 'auto', - sort: true - }, - { - field: '销售额', - title: '销售额', - width: 'auto', - sort: true - // tree: true, - }, - { - field: '利润', - title: '利润', - width: 'auto', - sort: true - } - ], - showPin: true, //显示VTable内置冻结列图标 - widthMode: 'standard', - allowFrozenColCount: 2, - records: data, - - hierarchyIndent: 20, - hierarchyExpandLevel: 2, - hierarchyTextStartAlignment: true, - sortState: { - field: '销售额', - order: 'asc' - }, - theme: VTable.themes.BRIGHT, - defaultRowHeight: 32 + records, + columns }; const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window.tableInstance = tableInstance; @@ -233,7 +101,13 @@ function bindExport() { exportExcelButton.addEventListener('click', async () => { if (window.tableInstance) { - downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + downloadExcel(await exportVTableToExcel(window.tableInstance, { + formatExportOutput: ({ cellType, cellValue, table, col, row }) => { + if (cellType === 'checkbox') { + return table.getCellCheckboxState(col, row) ? 'true' : 'false'; + } + } + }), 'export'); } }); } diff --git a/docs/assets/demo/zh/export/table-export-ignore-icon.md b/docs/assets/demo/zh/export/table-export-ignore-icon.md index a435862a4..4f92b790b 100644 --- a/docs/assets/demo/zh/export/table-export-ignore-icon.md +++ b/docs/assets/demo/zh/export/table-export-ignore-icon.md @@ -24,48 +24,180 @@ link: '../../guide/export/excel' // } from "@visactor/vtable-export"; // umd引入时导出工具会挂载到VTable.export -const records = [ - { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, - { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, - { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, - { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, - { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, - { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, - { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } -]; - -const columns = [ +const data = [ { - field: 'isCheck', - title: '', - width: 60, - headerType: 'checkbox', - cellType: 'checkbox' + 类别: '办公用品', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '信封', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色信封', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色信封', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '器具', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '订书机', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '计算器', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] }, { - field: 'productName', - title: 'productName', - width: 120 + 类别: '技术', + 销售额: '229.696', + 数量: '20', + 利润: '90.704', + children: [ + { + 类别: '设备', // 对应原子类别 + 销售额: '225.44', + 数量: '5', + 利润: '462.56' + }, + { + 类别: '配件', // 对应原子类别 + 销售额: '375.92', + 数量: '8', + 利润: '550.2' + }, + { + 类别: '复印机', // 对应原子类别 + 销售额: '425.44', + 数量: '7', + 利润: '342.56' + }, + { + 类别: '电话', // 对应原子类别 + 销售额: '175.92', + 数量: '6', + 利润: '750.2' + } + ] }, { - field: 'price', - title: 'checkbox', - width: 120, - cellType: 'checkbox', - disable: true, - checked: true + 类别: '家具', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '桌子', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色桌子', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色桌子', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '椅子', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '老板椅', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '沙发椅', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] }, { - field: 'check', - title: 'checkbox', - width: 120, - cellType: 'checkbox' - // disable: true + 类别: '生活家电(懒加载)', + 销售额: '229.696', + 数量: '20', + 利润: '90.704' } ]; const option = { - records, - columns + container: document.getElementById(CONTAINER_ID), + columns: [ + { + field: '类别', + tree: true, + title: '类别', + width: 'auto', + sort: true + }, + { + field: '销售额', + title: '销售额', + width: 'auto', + sort: true + // tree: true, + }, + { + field: '利润', + title: '利润', + width: 'auto', + sort: true + } + ], + showPin: true, //显示VTable内置冻结列图标 + widthMode: 'standard', + allowFrozenColCount: 2, + records: data, + + hierarchyIndent: 20, + hierarchyExpandLevel: 2, + hierarchyTextStartAlignment: true, + sortState: { + field: '销售额', + order: 'asc' + }, + theme: VTable.themes.BRIGHT, + defaultRowHeight: 32 }; const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window.tableInstance = tableInstance; @@ -101,7 +233,9 @@ function bindExport() { exportExcelButton.addEventListener('click', async () => { if (window.tableInstance) { - downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + downloadExcel(await exportVTableToExcel(window.tableInstance, { + ignoreIcon: true + }), 'export'); } }); } From c78301cdeb54a0aa38bdd84c6d923bb2da57f221 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 25 Jun 2024 17:10:29 +0800 Subject: [PATCH 09/37] feat: corner title can display row and column diemensionTitle #1926 --- .../pivot-table-corner-title.md | 171 +++++++ .../pivot-analysis-sort-indicator.md | 5 +- docs/assets/demo/menu.json | 7 + .../pivot-table-corner-title.md | 171 +++++++ .../pivot-analysis-sort-indicator.md | 5 +- docs/assets/option/en/common/pivot-corner.md | 4 +- docs/assets/option/zh/common/pivot-corner.md | 11 +- .../vtable/src/layout/pivot-header-layout.ts | 471 ++++++++++++------ packages/vtable/src/ts-types/table-engine.ts | 2 +- 9 files changed, 690 insertions(+), 157 deletions(-) create mode 100644 docs/assets/demo/en/basic-functionality/pivot-table-corner-title.md create mode 100644 docs/assets/demo/zh/basic-functionality/pivot-table-corner-title.md diff --git a/docs/assets/demo/en/basic-functionality/pivot-table-corner-title.md b/docs/assets/demo/en/basic-functionality/pivot-table-corner-title.md new file mode 100644 index 000000000..dc7599f6d --- /dev/null +++ b/docs/assets/demo/en/basic-functionality/pivot-table-corner-title.md @@ -0,0 +1,171 @@ +--- +category: examples +group: Basic Features +title: Display dimension names in pivot table headers +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pivot-table-corner-title.png +link: '../guide/table_type/Pivot_table/pivot_table_useage' +option: PivotTable#corner +--- + +# Display dimension names in pivot table headers + +If you set the header title display content basis to `'all'`, the header cell content will be the concatenation of the row dimension name and the column dimension name. + +titleOnDimension The corner title displays content based on: + +- 'column' column dimension name as header cell content +- 'row' row dimension name as header cell content +- 'none' means the header cell content is empty +- 'all' means the header cell content is the concatenation of the row dimension name and the column dimension name + +## Key Configurations + +- `PivotTable` +- `columns` +- `rows` +- `indicators` +- `corner.titleOnDimension` Corner title display content based on + +## Code Demo + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'Category', + title: 'Category', + headerStyle: { + textStick: true, + bgColor(arg) { + if (arg.dataValue === 'Row Totals') { + return '#ff9900'; + } + return '#ECF1F5'; + } + }, + width: 'auto' + }, + { + dimensionKey: 'Sub-Category', + title: 'Sub-Catogery', + headerStyle: { + textStick: true + }, + width: 'auto' + } + ], + columns: [ + { + dimensionKey: 'Region', + title: 'Region', + headerStyle: { + textStick: true + }, + width: 'auto' + }, + { + dimensionKey: 'Segment', + title: 'Segment', + headerStyle: { + textStick: true + }, + width: 'auto' + } + ], + indicators: [ + { + indicatorKey: 'Quantity', + title: 'Quantity', + width: 'auto', + sort: true, + headerStyle: { + fontWeight: 'normal' + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor(arg) { + const rowHeaderPaths = arg.cellHeaderPaths.rowHeaderPaths; + if (rowHeaderPaths?.[1]?.value === 'Sub Totals') { + return '#ba54ba'; + } else if (rowHeaderPaths?.[0]?.value === 'Row Totals') { + return '#ff9900'; + } + return undefined; + } + } + }, + { + indicatorKey: 'Sales', + title: 'Sales', + width: 'auto', + sort: true, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor(arg) { + const rowHeaderPaths = arg.cellHeaderPaths.rowHeaderPaths; + if (rowHeaderPaths?.[1]?.value === 'Sub Totals') { + return '#ba54ba'; + } else if (rowHeaderPaths?.[0]?.value === 'Row Totals') { + return '#ff9900'; + } + return undefined; + } + } + }, + { + indicatorKey: 'Profit', + title: 'Profit', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor(arg) { + const rowHeaderPaths = arg.cellHeaderPaths.rowHeaderPaths; + if (rowHeaderPaths?.[1]?.value === 'Sub Totals') { + return '#ba54ba'; + } else if (rowHeaderPaths?.[0]?.value === 'Row Totals') { + return '#ff9900'; + } + return undefined; + } + } + } + ], + corner: { + titleOnDimension: 'all' + }, + widthMode: 'standard' + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` diff --git a/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md b/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md index 71c09232e..c372487e6 100644 --- a/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md +++ b/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md @@ -154,10 +154,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American } ], corner: { - titleOnDimension: 'row', - headerStyle: { - textStick: true - } + titleOnDimension: 'row' }, dataConfig: { sortRules: [ diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index 4238264d4..2ab6f8710 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -485,6 +485,13 @@ "zh": "分页(翻页)", "en": "pagination" } + }, + { + "path": "pivot-table-corner-title", + "title": { + "zh": "透视表角头显示维度名", + "en": "Pivot Table corner title" + } } ] }, diff --git a/docs/assets/demo/zh/basic-functionality/pivot-table-corner-title.md b/docs/assets/demo/zh/basic-functionality/pivot-table-corner-title.md new file mode 100644 index 000000000..745b79bec --- /dev/null +++ b/docs/assets/demo/zh/basic-functionality/pivot-table-corner-title.md @@ -0,0 +1,171 @@ +--- +category: examples +group: Basic Features +title: 透视表角头显示维度名称 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pivot-table-corner-title.png +link: '../guide/table_type/Pivot_table/pivot_table_useage' +option: PivotTable#corner +--- + +# 透视表角头显示维度名称 + +将角头标题显示内容依据设置为`'all'`,则角头单元格内容为行维度名称和列维度名称的拼接。 + +titleOnDimension 角头标题显示内容依据: + +- 'column' 列维度名称作为角头单元格内容 +- 'row' 行维度名称作为角头单元格内容 +- 'none' 角头单元格内容为空 +- 'all' 角头单元格内容为行维度名称和列维度名称的拼接 + +## 关键配置 + +- `PivotTable` +- `columns` +- `rows` +- `indicators` +- `corner.titleOnDimension` 角头标题显示内容依据 + +## 代码演示 + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'Category', + title: 'Category', + headerStyle: { + textStick: true, + bgColor(arg) { + if (arg.dataValue === 'Row Totals') { + return '#ff9900'; + } + return '#ECF1F5'; + } + }, + width: 'auto' + }, + { + dimensionKey: 'Sub-Category', + title: 'Sub-Catogery', + headerStyle: { + textStick: true + }, + width: 'auto' + } + ], + columns: [ + { + dimensionKey: 'Region', + title: 'Region', + headerStyle: { + textStick: true + }, + width: 'auto' + }, + { + dimensionKey: 'Segment', + title: 'Segment', + headerStyle: { + textStick: true + }, + width: 'auto' + } + ], + indicators: [ + { + indicatorKey: 'Quantity', + title: 'Quantity', + width: 'auto', + sort: true, + headerStyle: { + fontWeight: 'normal' + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor(arg) { + const rowHeaderPaths = arg.cellHeaderPaths.rowHeaderPaths; + if (rowHeaderPaths?.[1]?.value === 'Sub Totals') { + return '#ba54ba'; + } else if (rowHeaderPaths?.[0]?.value === 'Row Totals') { + return '#ff9900'; + } + return undefined; + } + } + }, + { + indicatorKey: 'Sales', + title: 'Sales', + width: 'auto', + sort: true, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor(arg) { + const rowHeaderPaths = arg.cellHeaderPaths.rowHeaderPaths; + if (rowHeaderPaths?.[1]?.value === 'Sub Totals') { + return '#ba54ba'; + } else if (rowHeaderPaths?.[0]?.value === 'Row Totals') { + return '#ff9900'; + } + return undefined; + } + } + }, + { + indicatorKey: 'Profit', + title: 'Profit', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor(arg) { + const rowHeaderPaths = arg.cellHeaderPaths.rowHeaderPaths; + if (rowHeaderPaths?.[1]?.value === 'Sub Totals') { + return '#ba54ba'; + } else if (rowHeaderPaths?.[0]?.value === 'Row Totals') { + return '#ff9900'; + } + return undefined; + } + } + } + ], + corner: { + titleOnDimension: 'all' + }, + widthMode: 'standard' + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` diff --git a/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md b/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md index a541386a3..cf750fa36 100644 --- a/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md +++ b/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md @@ -154,10 +154,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American } ], corner: { - titleOnDimension: 'row', - headerStyle: { - textStick: true - } + titleOnDimension: 'row' }, dataConfig: { sortRules: [ diff --git a/docs/assets/option/en/common/pivot-corner.md b/docs/assets/option/en/common/pivot-corner.md index 5d4fc075e..f1945b492 100644 --- a/docs/assets/option/en/common/pivot-corner.md +++ b/docs/assets/option/en/common/pivot-corner.md @@ -3,9 +3,11 @@ ${prefix} titleOnDimension(string) ='row' Corner header content display based on: + - 'column' The column dimension name is used as the corner header cell content - 'row' The row dimension name is used as the corner header cell content - 'none' The corner header cell content is empty +- 'all' means the header cell content is the concatenation of the row dimension name and the column dimension name ${prefix} headerType(string) @@ -17,4 +19,4 @@ Header cell style, the configuration items vary slightly depending on the header - For headerType 'text', refer to [headerStyle](../option/PivotTable-columns-text#headerStyle.bgColor) - For headerType 'link', refer to [headerStyle](../option/PivotTable-columns-link#headerStyle.bgColor) -- For headerType 'image', refer to [headerStyle](../option/PivotTable-columns-image#headerStyle.bgColor) \ No newline at end of file +- For headerType 'image', refer to [headerStyle](../option/PivotTable-columns-image#headerStyle.bgColor) diff --git a/docs/assets/option/zh/common/pivot-corner.md b/docs/assets/option/zh/common/pivot-corner.md index 15629c2a1..c93492f5d 100644 --- a/docs/assets/option/zh/common/pivot-corner.md +++ b/docs/assets/option/zh/common/pivot-corner.md @@ -1,12 +1,13 @@ - {{ target: pivot-corner-define }} ${prefix} titleOnDimension(string) ='row' 角头标题显示内容依据: + - 'column' 列维度名称作为角头单元格内容 - 'row' 行维度名称作为角头单元格内容 - 'none' 角头单元格内容为空 +- 'all' 角头单元格内容为行维度名称和列维度名称的拼接 ${prefix} headerType(string) @@ -14,8 +15,8 @@ ${prefix} headerType(string) ${prefix} headerStyle(TODO) -表头单元格样式,配置项根据headerType不同有略微差别。每种headerStyle的配置项可参考: +表头单元格样式,配置项根据 headerType 不同有略微差别。每种 headerStyle 的配置项可参考: -- headerType为'text',对应[headerStyle](../option/PivotTable-columns-text#headerStyle.bgColor) -- headerType为'link',对应[headerStyle](../option/PivotTable-columns-link#headerStyle.bgColor) -- headerType为'image',对应[headerStyle](../option/PivotTable-columns-image#headerStyle.bgColor) \ No newline at end of file +- headerType 为'text',对应[headerStyle](../option/PivotTable-columns-text#headerStyle.bgColor) +- headerType 为'link',对应[headerStyle](../option/PivotTable-columns-link#headerStyle.bgColor) +- headerType 为'image',对应[headerStyle](../option/PivotTable-columns-image#headerStyle.bgColor) diff --git a/packages/vtable/src/layout/pivot-header-layout.ts b/packages/vtable/src/layout/pivot-header-layout.ts index 553fdbb34..c2f5ec77b 100644 --- a/packages/vtable/src/layout/pivot-header-layout.ts +++ b/packages/vtable/src/layout/pivot-header-layout.ts @@ -278,73 +278,71 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.sharedVar.seqId = Math.max(this.sharedVar.seqId, this._headerObjects.length); //生成cornerHeaderObjs及_cornerHeaderCellIds - if (this.cornerSetting.titleOnDimension === 'column') { - let colDimensionKeys = this.columnDimensionTree.dimensionKeysIncludeVirtual.valueArr(); + // if (this.cornerSetting.titleOnDimension === 'all') { + let colDimensionKeys = this.columnDimensionTree.dimensionKeysIncludeVirtual.valueArr(); + //#region 处理需求 当没有数据时仍然显示角头维度名称 + if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree + ) { + colDimensionKeys = this.columnsDefine.map(define => { + if (typeof define === 'string') { + return define; + } + return define.dimensionKey; + }); + if (this.indicatorsAsCol) { + colDimensionKeys.push(this.indicatorDimensionKey); + } + } + //#endregion + + colDimensionKeys = this.columnHeaderTitle ? [''].concat(colDimensionKeys) : colDimensionKeys; + + let rowDimensionKeys: string[]; + let extensionRowDimensions = []; + if (this.rowHierarchyType === 'tree' && this.extensionRows?.length >= 1) { + // 如果是有扩展行维度 + const rowTreeFirstKey = []; + rowTreeFirstKey.push(this.rowDimensionKeys[0]); + this._extensionRowDimensionKeys.forEach(extensionRowKeys => { + rowTreeFirstKey.push(extensionRowKeys[0]); + }); + extensionRowDimensions = this.extensionRows.reduce((dimensions, cur) => { + return dimensions.concat(cur.rows); + }, []); + + rowDimensionKeys = this.rowHeaderTitle ? [''].concat(rowTreeFirstKey as any) : rowTreeFirstKey; + } else { //#region 处理需求 当没有数据时仍然显示角头维度名称 + rowDimensionKeys = this.rowDimensionTree.dimensionKeysIncludeVirtual.valueArr(); if ( this.dataset && (this.dataset.records?.length ?? 0) === 0 && !this.dataset.customColTree && !this.dataset.customRowTree ) { - colDimensionKeys = this.columnsDefine.map(define => { + rowDimensionKeys = this.rowsDefine.map(define => { if (typeof define === 'string') { return define; } return define.dimensionKey; }); - if (this.indicatorsAsCol) { - colDimensionKeys.push(this.indicatorDimensionKey); + if (!this.indicatorsAsCol) { + rowDimensionKeys.push(this.indicatorDimensionKey); } } //#endregion - this.cornerHeaderObjs = this._addCornerHeaders( - this.columnHeaderTitle ? [''].concat(colDimensionKeys) : colDimensionKeys, - this.columnsDefine - ); - } else if (this.cornerSetting.titleOnDimension === 'row') { - if (this.rowHierarchyType === 'tree' && this.extensionRows?.length >= 1) { - // 如果是有扩展行维度 - const rowTreeFirstKey = []; - rowTreeFirstKey.push(this.rowDimensionKeys[0]); - this._extensionRowDimensionKeys.forEach(extensionRowKeys => { - rowTreeFirstKey.push(extensionRowKeys[0]); - }); - const extensionRowDimensions = this.extensionRows.reduce((dimensions, cur) => { - return dimensions.concat(cur.rows); - }, []); - this.cornerHeaderObjs = this._addCornerHeaders( - this.rowHeaderTitle ? [''].concat(rowTreeFirstKey as any) : rowTreeFirstKey, - this.rowsDefine.concat(extensionRowDimensions) - ); - } else { - //#region 处理需求 当没有数据时仍然显示角头维度名称 - let rowDimensionKeys = this.rowDimensionTree.dimensionKeysIncludeVirtual.valueArr(); - if ( - this.dataset && - (this.dataset.records?.length ?? 0) === 0 && - !this.dataset.customColTree && - !this.dataset.customRowTree - ) { - rowDimensionKeys = this.rowsDefine.map(define => { - if (typeof define === 'string') { - return define; - } - return define.dimensionKey; - }); - if (!this.indicatorsAsCol) { - rowDimensionKeys.push(this.indicatorDimensionKey); - } - } - //#endregion - this.cornerHeaderObjs = this._addCornerHeaders( - this.rowHeaderTitle ? [''].concat(rowDimensionKeys) : rowDimensionKeys, - this.rowsDefine - ); - } - } else { - this.cornerHeaderObjs = this._addCornerHeaders(null, undefined); + rowDimensionKeys = this.rowHeaderTitle ? [''].concat(rowDimensionKeys) : rowDimensionKeys; } + + this.cornerHeaderObjs = this._addCornerHeaders( + colDimensionKeys, + rowDimensionKeys, + this.columnsDefine.concat(...this.rowsDefine, ...extensionRowDimensions) + ); this.colIndex = 0; this._headerObjectMap = this._headerObjects.reduce((o, e) => { o[e.id as number] = e; @@ -400,35 +398,6 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { // } return false; }); - - // if (this.indicatorsAsCol) { - // const cell_id = 'rowHeaderEmpty'; - // this._headerObjectMap[cell_id] = { - // id: cell_id, - // title: '', - // field: cell_id, - // headerType: this.cornerSetting.headerType ?? 'text', - // style: this.cornerSetting.headerStyle, - // define: { - // // id: - // } - // }; - // this._headerObjects.push(this._headerObjectMap[cell_id]); - // // this.rowShowAttrs.push(cell_id); - - // // deal with sub indicator axis - - // if (!this.hasTwoIndicatorAxes) { - // // this.colShowAttrs.pop(); - // } - // } else { - // const axisOption = ((this._table as PivotChart).pivotChartAxes as ITableAxisOption[]).find(axisOption => { - // return axisOption.orient === 'left'; - // }); - // if (axisOption?.visible === false) { - // // this.rowShowAttrs.pop(); - // } - // } } this.handleRowSeriesNumber(table.internalProps.rowSeriesNumber); @@ -668,73 +637,290 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { dealHeaderForTreeMode(hd, _headerCellIds, results, roots, row, totalLevel, show, dimensions, this); } } - private _addCornerHeaders(dimensionKeys: string[] | null, dimensions: (string | IDimension)[]) { + private _addCornerHeaders( + colDimensionKeys: string[] | null, + rowDimensionKeys: string[] | null, + dimensions: (string | IDimension)[] + ) { const results: HeaderData[] = []; - if (dimensionKeys) { - dimensionKeys.forEach((dimensionKey: string, key: number) => { - const id = ++this.sharedVar.seqId; - // const dimensionInfo: IDimension = - // (this.rowsDefine?.find(dimension => - // typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey - // ) as IDimension) ?? - // (this.columnsDefine?.find(dimension => - // typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey - // ) as IDimension); - const dimensionInfo: IDimension = dimensions.find(dimension => - typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey - ) as IDimension; - const cell: HeaderData = { - id, - title: - dimensionKey === this.indicatorDimensionKey - ? this.indicatorTitle - : dimensionInfo - ? dimensionInfo.title - : dimensionKey === 'axis' - ? '' - : (dimensionKey as string), - field: dimensionKey, //'维度名称', - style: this.cornerSetting.headerStyle, - headerType: this.cornerSetting.headerType ?? 'text', - showSort: dimensionInfo?.showSortInCorner, - sort: dimensionInfo?.sort, - define: { + if (this.cornerSetting.titleOnDimension === 'all') { + if (this.indicatorsAsCol) { + let indicatorAtIndex = -1; + if (colDimensionKeys) { + colDimensionKeys.forEach((dimensionKey: string, key: number) => { + if (dimensionKey === this.indicatorDimensionKey) { + indicatorAtIndex = key; + } + const id = ++this.sharedVar.seqId; + const dimensionInfo: IDimension = dimensions.find(dimension => + typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + ) as IDimension; + const cell: HeaderData = { + id, + title: + dimensionKey === this.indicatorDimensionKey + ? this.indicatorTitle + : dimensionInfo + ? dimensionInfo.title + : dimensionKey === 'axis' + ? '' + : (dimensionKey as string), + field: dimensionKey, //'维度名称', + style: this.cornerSetting.headerStyle, + headerType: this.cornerSetting.headerType ?? 'text', + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + define: { + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + dimensionKey: dimensionKey, // '维度名称', + id, + value: dimensionKey, + headerEditor: this.cornerSetting.headerEditor, + disableHeaderHover: !!this.cornerSetting.disableHeaderHover, + disableHeaderSelect: !!this.cornerSetting.disableHeaderSelect + }, + dropDownMenu: dimensionInfo?.cornerDropDownMenu, + pivotInfo: { + value: dimensionInfo?.title ?? '', + dimensionKey, + isPivotCorner: true + // customInfo: dimensionInfo?.customInfo + }, + description: dimensionInfo?.cornerDescription + }; + results[id] = cell; + this._headerObjects[id] = cell; + + if (!this._cornerHeaderCellFullPathIds[key]) { + this._cornerHeaderCellFullPathIds[key] = []; + } + for (let r = 0; r < this.rowHeaderLevelCount; r++) { + this._cornerHeaderCellFullPathIds[key][r] = id; + } + }); + } + if (rowDimensionKeys) { + rowDimensionKeys.forEach((dimensionKey: string, key: number) => { + const id = ++this.sharedVar.seqId; + const dimensionInfo: IDimension = dimensions.find(dimension => + typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + ) as IDimension; + const cell: HeaderData = { + id, + title: + dimensionKey === this.indicatorDimensionKey + ? this.indicatorTitle + : dimensionInfo + ? dimensionInfo.title + : dimensionKey === 'axis' + ? '' + : (dimensionKey as string), + field: dimensionKey, //'维度名称', + style: this.cornerSetting.headerStyle, + headerType: this.cornerSetting.headerType ?? 'text', + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + define: { + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + dimensionKey: dimensionKey, // '维度名称', + id, + value: dimensionKey, + headerEditor: this.cornerSetting.headerEditor, + disableHeaderHover: !!this.cornerSetting.disableHeaderHover, + disableHeaderSelect: !!this.cornerSetting.disableHeaderSelect + }, + dropDownMenu: dimensionInfo?.cornerDropDownMenu, + pivotInfo: { + value: dimensionInfo?.title ?? '', + dimensionKey, + isPivotCorner: true + // customInfo: dimensionInfo?.customInfo + }, + description: dimensionInfo?.cornerDescription + }; + results[id] = cell; + this._headerObjects[id] = cell; + if (!this._cornerHeaderCellFullPathIds[indicatorAtIndex]) { + this._cornerHeaderCellFullPathIds[indicatorAtIndex] = []; + } + this._cornerHeaderCellFullPathIds[indicatorAtIndex][key] = id; + }); + } + } else { + let indicatorAtIndex = -1; + if (rowDimensionKeys) { + rowDimensionKeys.forEach((dimensionKey: string, key: number) => { + if (dimensionKey === this.indicatorDimensionKey) { + indicatorAtIndex = key; + } + const id = ++this.sharedVar.seqId; + const dimensionInfo: IDimension = dimensions.find(dimension => + typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + ) as IDimension; + const cell: HeaderData = { + id, + title: + dimensionKey === this.indicatorDimensionKey + ? this.indicatorTitle + : dimensionInfo + ? dimensionInfo.title + : dimensionKey === 'axis' + ? '' + : (dimensionKey as string), + field: dimensionKey, //'维度名称', + style: this.cornerSetting.headerStyle, + headerType: this.cornerSetting.headerType ?? 'text', + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + define: { + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + dimensionKey: dimensionKey, // '维度名称', + id, + value: dimensionKey, + headerEditor: this.cornerSetting.headerEditor, + disableHeaderHover: !!this.cornerSetting.disableHeaderHover, + disableHeaderSelect: !!this.cornerSetting.disableHeaderSelect + }, + dropDownMenu: dimensionInfo?.cornerDropDownMenu, + pivotInfo: { + value: dimensionInfo?.title ?? '', + dimensionKey, + isPivotCorner: true + // customInfo: dimensionInfo?.customInfo + }, + description: dimensionInfo?.cornerDescription + }; + results[id] = cell; + this._headerObjects[id] = cell; + + for (let r = 0; r < this.columnHeaderLevelCount; r++) { + if (!this._cornerHeaderCellFullPathIds[r]) { + this._cornerHeaderCellFullPathIds[r] = []; + } + this._cornerHeaderCellFullPathIds[r][key] = id; + } + }); + } + if (colDimensionKeys) { + colDimensionKeys.forEach((dimensionKey: string, key: number) => { + const id = ++this.sharedVar.seqId; + const dimensionInfo: IDimension = dimensions.find(dimension => + typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + ) as IDimension; + const cell: HeaderData = { + id, + title: + dimensionKey === this.indicatorDimensionKey + ? this.indicatorTitle + : dimensionInfo + ? dimensionInfo.title + : dimensionKey === 'axis' + ? '' + : (dimensionKey as string), + field: dimensionKey, //'维度名称', + style: this.cornerSetting.headerStyle, + headerType: this.cornerSetting.headerType ?? 'text', + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + define: { + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + dimensionKey: dimensionKey, // '维度名称', + id, + value: dimensionKey, + headerEditor: this.cornerSetting.headerEditor, + disableHeaderHover: !!this.cornerSetting.disableHeaderHover, + disableHeaderSelect: !!this.cornerSetting.disableHeaderSelect + }, + dropDownMenu: dimensionInfo?.cornerDropDownMenu, + pivotInfo: { + value: dimensionInfo?.title ?? '', + dimensionKey, + isPivotCorner: true + // customInfo: dimensionInfo?.customInfo + }, + description: dimensionInfo?.cornerDescription + }; + results[id] = cell; + this._headerObjects[id] = cell; + // if (!this._cornerHeaderCellFullPathIds[indicatorAtIndex]) { + // this._cornerHeaderCellFullPathIds[indicatorAtIndex] = []; + // } + this._cornerHeaderCellFullPathIds[key][indicatorAtIndex] = id; + }); + } + } + } else if (this.cornerSetting.titleOnDimension === 'row' || this.cornerSetting.titleOnDimension === 'column') { + const dimensionKeys = this.cornerSetting?.titleOnDimension === 'row' ? rowDimensionKeys : colDimensionKeys; + if (dimensionKeys) { + dimensionKeys.forEach((dimensionKey: string, key: number) => { + const id = ++this.sharedVar.seqId; + // const dimensionInfo: IDimension = + // (this.rowsDefine?.find(dimension => + // typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + // ) as IDimension) ?? + // (this.columnsDefine?.find(dimension => + // typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + // ) as IDimension); + const dimensionInfo: IDimension = dimensions.find(dimension => + typeof dimension === 'string' ? false : dimension.dimensionKey === dimensionKey + ) as IDimension; + const cell: HeaderData = { + id, + title: + dimensionKey === this.indicatorDimensionKey + ? this.indicatorTitle + : dimensionInfo + ? dimensionInfo.title + : dimensionKey === 'axis' + ? '' + : (dimensionKey as string), + field: dimensionKey, //'维度名称', + style: this.cornerSetting.headerStyle, + headerType: this.cornerSetting.headerType ?? 'text', showSort: dimensionInfo?.showSortInCorner, sort: dimensionInfo?.sort, - dimensionKey: dimensionKey, // '维度名称', - id, - value: dimensionKey, - headerEditor: this.cornerSetting.headerEditor, - disableHeaderHover: !!this.cornerSetting.disableHeaderHover, - disableHeaderSelect: !!this.cornerSetting.disableHeaderSelect - }, - dropDownMenu: dimensionInfo?.cornerDropDownMenu, - pivotInfo: { - value: dimensionInfo?.title ?? '', - dimensionKey, - isPivotCorner: true - // customInfo: dimensionInfo?.customInfo - }, - description: dimensionInfo?.cornerDescription - }; - results[id] = cell; - this._headerObjects[id] = cell; - if (this.cornerSetting.titleOnDimension === 'column') { - if (!this._cornerHeaderCellFullPathIds[key]) { - this._cornerHeaderCellFullPathIds[key] = []; - } - for (let r = 0; r < this.rowHeaderLevelCount; r++) { - this._cornerHeaderCellFullPathIds[key][r] = id; - } - } else if (this.cornerSetting.titleOnDimension === 'row') { - for (let r = 0; r < this.columnHeaderLevelCount; r++) { - if (!this._cornerHeaderCellFullPathIds[r]) { - this._cornerHeaderCellFullPathIds[r] = []; + define: { + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + dimensionKey: dimensionKey, // '维度名称', + id, + value: dimensionKey, + headerEditor: this.cornerSetting.headerEditor, + disableHeaderHover: !!this.cornerSetting.disableHeaderHover, + disableHeaderSelect: !!this.cornerSetting.disableHeaderSelect + }, + dropDownMenu: dimensionInfo?.cornerDropDownMenu, + pivotInfo: { + value: dimensionInfo?.title ?? '', + dimensionKey, + isPivotCorner: true + // customInfo: dimensionInfo?.customInfo + }, + description: dimensionInfo?.cornerDescription + }; + results[id] = cell; + this._headerObjects[id] = cell; + if (this.cornerSetting.titleOnDimension === 'column') { + if (!this._cornerHeaderCellFullPathIds[key]) { + this._cornerHeaderCellFullPathIds[key] = []; + } + for (let r = 0; r < this.rowHeaderLevelCount; r++) { + this._cornerHeaderCellFullPathIds[key][r] = id; + } + } else if (this.cornerSetting.titleOnDimension === 'row') { + for (let r = 0; r < this.columnHeaderLevelCount; r++) { + if (!this._cornerHeaderCellFullPathIds[r]) { + this._cornerHeaderCellFullPathIds[r] = []; + } + this._cornerHeaderCellFullPathIds[r][key] = id; } - this._cornerHeaderCellFullPathIds[r][key] = id; } - } - }); + }); + } } else { const id = ++this.sharedVar.seqId; const cell: HeaderData = { @@ -762,6 +948,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { } } } + return results; } private generateExtensionRowTree() { diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index 8cff11ff9..8fadc8409 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -64,7 +64,7 @@ export type WidthModeDef = 'standard' | 'adaptive' | 'autoWidth'; export type HeightModeDef = 'standard' | 'adaptive' | 'autoHeight'; export type WidthAdaptiveModeDef = 'only-body' | 'all'; export type HeightAdaptiveModeDef = 'only-body' | 'all'; -export type ShowColumnRowType = 'column' | 'row' | 'none'; +export type ShowColumnRowType = 'column' | 'row' | 'none' | 'all'; /** 单元格所处表格哪部分 */ export type CellLocation = 'body' | 'rowHeader' | 'columnHeader' | 'cornerHeader'; export type CellSubLocation = From f19aaef7f19f62dc54cdcadfb9a84f07e27eb60a Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 25 Jun 2024 17:12:00 +0800 Subject: [PATCH 10/37] docs: update changlog of rush --- ...-corner-titleondimension-all_2024-06-25-09-12.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json diff --git a/common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json b/common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json new file mode 100644 index 000000000..546a35dc5 --- /dev/null +++ b/common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: corner title can display row and column diemensionTitle #1926\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From 75b9522a1b70452ae266ecca0d1ad6c2a7db5fc5 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 25 Jun 2024 17:23:29 +0800 Subject: [PATCH 11/37] fix: fix vrender export module --- .../vtable/fix-export-name_2024-06-25-09-23.json | 10 ++++++++++ packages/vtable/src/index.ts | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json diff --git a/common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json b/common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json new file mode 100644 index 000000000..0350b9a25 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix vrender export module", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/index.ts b/packages/vtable/src/index.ts index 9841836a2..de9582877 100644 --- a/packages/vtable/src/index.ts +++ b/packages/vtable/src/index.ts @@ -47,7 +47,8 @@ export { getDataCellPath } from './tools/get-data-path'; export * from './render/jsx'; export { getTargetCell } from './event/util'; -export * as VRender from './vrender'; +// export * as VRender from './vrender'; +import * as VRender from './vrender'; export const version = __VERSION__; /** @@ -98,7 +99,8 @@ export { renderChart, graphicUtil, setCustomAlphabetCharSet, - restoreMeasureText + restoreMeasureText, + VRender }; /** @private */ From 7032f8af40a43f81a0d51a25b63657bf3aed3a9f Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 25 Jun 2024 18:08:25 +0800 Subject: [PATCH 12/37] fix: trigger selected_clear event time #1981 --- packages/vtable/src/core/BaseTable.ts | 5 +++-- packages/vtable/src/event/event.ts | 4 +++- packages/vtable/src/event/listener/table-group.ts | 10 +++++++--- packages/vtable/src/state/cell-move/index.ts | 3 ++- packages/vtable/src/state/sort/index.ts | 2 ++ packages/vtable/src/state/state.ts | 10 ++++++---- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 5f8166725..82181f034 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -2753,8 +2753,9 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @param row */ selectCell(col: number, row: number, isShift?: boolean, isCtrl?: boolean) { + const isHasSelected = !!this.stateManager.select.ranges?.length; this.stateManager.updateSelectPos(col, row, isShift, isCtrl); - this.stateManager.endSelectCells(); + this.stateManager.endSelectCells(true, isHasSelected); } /** * 选中单元格区域,可设置多个区域同时选中 @@ -2786,7 +2787,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { this.stateManager.updateInteractionState(InteractionState.grabing); this.stateManager.updateSelectPos(cellRange.end.col, cellRange.end.row, false, index >= 1, false, false, true); } - this.stateManager.endSelectCells(false); + this.stateManager.endSelectCells(false, false); this.stateManager.updateInteractionState(InteractionState.default); }); // 选择后 会自动滚动到所选区域最后一行一列的位置 这里再设置回滚动前位置 diff --git a/packages/vtable/src/event/event.ts b/packages/vtable/src/event/event.ts index 294849caa..1c356f652 100644 --- a/packages/vtable/src/event/event.ts +++ b/packages/vtable/src/event/event.ts @@ -129,7 +129,9 @@ export class EventManager { } else if (funcType === IconFuncTypeEnum.drillDown) { drillClick(this.table); } else if (funcType === IconFuncTypeEnum.collapse || funcType === IconFuncTypeEnum.expand) { - this.table.stateManager.updateSelectPos(-1, -1); + const isHasSelected = !!stateManager.select.ranges?.length; + stateManager.updateSelectPos(-1, -1); + stateManager.endSelectCells(true, isHasSelected); this.table.toggleHierarchyState(col, row); } }); diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index 15c4420c8..459bf4d4d 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -346,8 +346,9 @@ export function bindTableGroupListener(eventManager: EventManager) { eventManager.dealTableHover(); //点击到表格外部不需要取消选中状态 if (table.options.select?.outsideClickDeselect) { + const isHasSelected = !!stateManager.select.ranges?.length; eventManager.dealTableSelect(); - stateManager.endSelectCells(); + stateManager.endSelectCells(true, isHasSelected); } }); @@ -636,8 +637,9 @@ export function bindTableGroupListener(eventManager: EventManager) { const eventArgsSet: SceneEvent = getCellEventArgsSet(e); if (eventManager.touchSetTimeout) { clearTimeout(eventManager.touchSetTimeout); + const isHasSelected = !!stateManager.select.ranges?.length; eventManager.dealTableSelect(eventArgsSet); - stateManager.endSelectCells(); + stateManager.endSelectCells(true, isHasSelected); eventManager.touchSetTimeout = undefined; } } @@ -688,6 +690,7 @@ export function bindTableGroupListener(eventManager: EventManager) { if ( !hitIcon && !eventManager.checkCellFillhandle(eventArgsSet) && + !stateManager.columnResize.resizing && eventManager.checkColumnResize(eventArgsSet, true) ) { // eventManager.startColumnResize(e); @@ -739,11 +742,12 @@ export function bindTableGroupListener(eventManager: EventManager) { ) { stateManager.updateInteractionState(InteractionState.default); eventManager.dealTableHover(); + const isHasSelected = !!stateManager.select.ranges?.length; // 点击空白区域取消选中 if (table.options.select?.blankAreaClickDeselect ?? true) { eventManager.dealTableSelect(); } - stateManager.endSelectCells(); + stateManager.endSelectCells(true, isHasSelected); stateManager.updateCursor(); table.scenegraph.updateChartState(null); diff --git a/packages/vtable/src/state/cell-move/index.ts b/packages/vtable/src/state/cell-move/index.ts index a8dac8bae..8a23d0d28 100644 --- a/packages/vtable/src/state/cell-move/index.ts +++ b/packages/vtable/src/state/cell-move/index.ts @@ -29,8 +29,9 @@ export function startMoveCol(col: number, row: number, x: number, y: number, sta state.table.scenegraph.component.showMoveCol(col, row, delta); // 调整列顺序期间清空选中清空 + const isHasSelected = !!state.select.ranges?.length; state.table.stateManager.updateSelectPos(-1, -1); - + state.table.stateManager.endSelectCells(true, isHasSelected); state.table.scenegraph.updateNextFrame(); } diff --git a/packages/vtable/src/state/sort/index.ts b/packages/vtable/src/state/sort/index.ts index 16d3f6c28..c32383268 100644 --- a/packages/vtable/src/state/sort/index.ts +++ b/packages/vtable/src/state/sort/index.ts @@ -72,7 +72,9 @@ export function dealSort(col: number, row: number, table: ListTableAPI, event: E table.scenegraph.sortCell(); // 排序后,清除选中效果 + const isHasSelected = !!table.stateManager.select.ranges?.length; table.stateManager.updateSelectPos(-1, -1); + table.stateManager.endSelectCells(true, isHasSelected); } function executeSort(newState: SortState, table: BaseTableAPI, headerDefine: HeaderDefine): void { diff --git a/packages/vtable/src/state/state.ts b/packages/vtable/src/state/state.ts index 767261b2d..52fa80bd6 100644 --- a/packages/vtable/src/state/state.ts +++ b/packages/vtable/src/state/state.ts @@ -610,7 +610,7 @@ export class StateManager { isSelecting(): boolean { return this.select.selecting; } - endSelectCells(fireListener: boolean = true) { + endSelectCells(fireListener: boolean = true, fireClear: boolean = true) { if (this.select.selecting) { this.select.selecting = false; if (this.select.ranges.length === 0) { @@ -651,7 +651,7 @@ export class StateManager { col: lastCol, row: lastRow }); - } else if (fireListener) { + } else if (fireClear) { if (this.select.ranges.length === 0) { this.table.fireListeners(TABLE_EVENT_TYPE.SELECTED_CLEAR, {}); } @@ -676,8 +676,9 @@ export class StateManager { this.table.scenegraph.component.showResizeCol(col, y, isRightFrozen); // 调整列宽期间清空选中清空 + const isHasSelected = !!this.select.ranges?.length; this.updateSelectPos(-1, -1); - + this.endSelectCells(true, isHasSelected); this.table.scenegraph.updateNextFrame(); } updateResizeCol(xInTable: number, yInTable: number) { @@ -702,8 +703,9 @@ export class StateManager { this.table.scenegraph.component.showResizeRow(row, x, isBottomFrozen); // 调整列宽期间清空选中清空 + const isHasSelected = !!this.select.ranges?.length; this.updateSelectPos(-1, -1); - + this.endSelectCells(true, isHasSelected); this.table.scenegraph.updateNextFrame(); } updateResizeRow(xInTable: number, yInTable: number) { From 7af992b45ac44d1f2643e8fa9353cc2447632c29 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 26 Jun 2024 11:57:12 +0800 Subject: [PATCH 13/37] docs: add important release log --- docs/assets/changelog/en/important-release.md | 37 ++++++++++++++++++ docs/assets/changelog/menu.json | 7 ++++ docs/assets/changelog/zh/important-release.md | 39 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 docs/assets/changelog/en/important-release.md create mode 100644 docs/assets/changelog/zh/important-release.md diff --git a/docs/assets/changelog/en/important-release.md b/docs/assets/changelog/en/important-release.md new file mode 100644 index 000000000..3c7536db4 --- /dev/null +++ b/docs/assets/changelog/en/important-release.md @@ -0,0 +1,37 @@ +# v1.0.0 + +2024-05-21 + +**💥 Breaking change** + +- **@visactor/vtable**: If the user has previously passed in rowTree and columnTree for the pivot table, under this usage, the result returned by the getCellOriginRecord interface changes from the previous object to an array structure, and if no default aggregation was previously performed, the SUM aggregation rule will be used for data calculation. If you want to cancel the numerical calculation rule, you can specify the aggregation rule as NONE for the indicator. + +Configuration examples, you can also refer to [Tutorial](https://visactor.io/vtable/guide/data_analysis/pivot_table_dataAnalysis): +``` +records:[{ + region: '中南', + province: '广西', + year: '2016', + quarter: '2016-Q1', + sales: 'NULL', + profit: 1546 +}], +dataConfig:{ + aggregationRules: [ + { + indicatorKey: 'sales', + field: 'sales', + aggregationType: VTable.TYPES.AggregationType.NONE, + } + ] +} + +``` +**🆕 New feature** + +- **@visactor/vtable**: rows and tree can combined use [#1644](https://github.com/VisActor/VTable/issues/1644) +- **@visactor/vtable**: add virtual option for rowTree and columnTree [#1644](https://github.com/VisActor/VTable/issues/1644) + + + +[more detail about v1.0.0](https://github.com/VisActor/VTable/releases/tag/v1.0.0) diff --git a/docs/assets/changelog/menu.json b/docs/assets/changelog/menu.json index f62327d9e..09f6defcc 100644 --- a/docs/assets/changelog/menu.json +++ b/docs/assets/changelog/menu.json @@ -1,6 +1,13 @@ { "menu": "changelog", "children": [ + { + "path": "important-release", + "title": { + "zh": "重大版本", + "en": "Important Release" + } + }, { "path": "release", "title": { diff --git a/docs/assets/changelog/zh/important-release.md b/docs/assets/changelog/zh/important-release.md new file mode 100644 index 000000000..7e9654242 --- /dev/null +++ b/docs/assets/changelog/zh/important-release.md @@ -0,0 +1,39 @@ + +# v1.0.0 + +2024-05-21 + +**💥 Breaking change** + +- **@visactor/vtable**: 透视表如果之前用户传入了rowTree和columnTree,在此用法下,getCellOriginRecord接口返回结果由之前对象变为数组结构,并且之前没有做默认聚合目前会使用SUM聚会规则进行数据计算,如果想取消数值计算规则可以为指标指定聚合规则为NONE。 + +配置示例,也可以参考[教程](https://visactor.io/vtable/guide/data_analysis/pivot_table_dataAnalysis): +``` +records:[{ + region: '中南', + province: '广西', + year: '2016', + quarter: '2016-Q1', + sales: 'NULL', + profit: 1546 +}], +dataConfig:{ + aggregationRules: [ + { + indicatorKey: 'sales', //指标名称 + field: 'sales', //指标依据字段 + aggregationType: VTable.TYPES.AggregationType.NONE, //不做聚合 匹配到其中对应数据获取其对应field的值 + } + ] +} + +``` + +**🆕 新增功能** + +- **@visactor/vtable**: 自定义树形表头customTree可以和透视分析能力结合使用 [#1644](https://github.com/VisActor/VTable/issues/1644) +- **@visactor/vtable**: 在 rowTree & columnTree 中加入virtual option [#1644](https://github.com/VisActor/VTable/issues/1644) + + + +[更多详情请查看 v1.0.0](https://github.com/VisActor/VTable/releases/tag/v1.0.0) From 4c04ff48e8cc9d7c30d138994f48ef42145bb3ac Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 26 Jun 2024 14:19:59 +0800 Subject: [PATCH 14/37] fix: corner titleOn all when no records render error --- .../vtable/src/layout/pivot-header-layout.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/vtable/src/layout/pivot-header-layout.ts b/packages/vtable/src/layout/pivot-header-layout.ts index c2f5ec77b..3b3e05641 100644 --- a/packages/vtable/src/layout/pivot-header-layout.ts +++ b/packages/vtable/src/layout/pivot-header-layout.ts @@ -277,7 +277,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { } this.sharedVar.seqId = Math.max(this.sharedVar.seqId, this._headerObjects.length); - //生成cornerHeaderObjs及_cornerHeaderCellIds + //#region 生成cornerHeaderObjs及_cornerHeaderCellIds // if (this.cornerSetting.titleOnDimension === 'all') { let colDimensionKeys = this.columnDimensionTree.dimensionKeysIncludeVirtual.valueArr(); //#region 处理需求 当没有数据时仍然显示角头维度名称 @@ -343,6 +343,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { rowDimensionKeys, this.columnsDefine.concat(...this.rowsDefine, ...extensionRowDimensions) ); + //#endregion this.colIndex = 0; this._headerObjectMap = this._headerObjects.reduce((o, e) => { o[e.id as number] = e; @@ -1403,7 +1404,10 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { if (count === 0 && this.dataset && !this.dataset.customColTree && !this.dataset.customRowTree) { if (this.cornerSetting.titleOnDimension === 'row') { count = 1; - } else if ((this.dataset.records?.length ?? 0) === 0 && this.cornerSetting.titleOnDimension === 'column') { + } else if ( + (this.dataset.records?.length ?? 0) === 0 && + (this.cornerSetting.titleOnDimension === 'column' || this.cornerSetting.titleOnDimension === 'all') + ) { count = this.columnsDefine.length ?? 0; } } else if ( @@ -1412,7 +1416,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { !this.dataset.customColTree && !this.dataset.customRowTree ) { - if (this.cornerSetting.titleOnDimension === 'column') { + if (this.cornerSetting.titleOnDimension === 'column' || this.cornerSetting.titleOnDimension === 'all') { count = this.columnsDefine.length ?? 0; if (!this.hideIndicatorName && this.indicatorsAsCol) { count++; @@ -1467,7 +1471,10 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { if (count === 0 && this.dataset && !this.dataset.customColTree && !this.dataset.customRowTree) { if (this.cornerSetting.titleOnDimension === 'column') { count = 1; - } else if ((this.dataset.records?.length ?? 0) === 0 && this.cornerSetting.titleOnDimension === 'row') { + } else if ( + (this.dataset.records?.length ?? 0) === 0 && + (this.cornerSetting.titleOnDimension === 'row' || this.cornerSetting.titleOnDimension === 'all') + ) { count = this.rowsDefine.length ?? 0; } } else if ( @@ -1476,7 +1483,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { !this.dataset.customColTree && !this.dataset.customRowTree ) { - if (this.cornerSetting.titleOnDimension === 'row') { + if (this.cornerSetting.titleOnDimension === 'row' || this.cornerSetting.titleOnDimension === 'all') { count = this.rowsDefine.length; if (!this.hideIndicatorName && !this.indicatorsAsCol) { count++; From 35721b2b57bf0a06c31f7a296627f1ad35200fcd Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 26 Jun 2024 17:45:18 +0800 Subject: [PATCH 15/37] feat: add column hide config #1991 --- packages/vtable/src/ListTable.ts | 4 +- .../vtable/src/layout/simple-header-layout.ts | 61 ++++++++++++------- .../list-table/define/basic-define.ts | 2 + 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index a069a4fd4..af30270a6 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -1008,7 +1008,9 @@ export class ListTable extends BaseTable implements ListTableAPI { if (order && field && order !== 'normal') { const sortFunc = this._getSortFuncFromHeaderOption(undefined, field); // 如果sort传入的信息不能生成正确的sortFunc,直接更新表格,避免首次加载无法正常显示内容 - const hd = this.internalProps.layoutMap.headerObjects.find((col: any) => col && col.field === field); + const hd = this.internalProps.layoutMap.headerObjectsIncludeHided.find( + (col: any) => col && col.field === field + ); // hd?.define?.sort && //如果这里也判断 那想要利用sortState来排序 但不显示排序图标就实现不了 if (hd.define.sort !== false) { this.dataSource.sort(hd.field, order, sortFunc ?? defaultOrderFn); diff --git a/packages/vtable/src/layout/simple-header-layout.ts b/packages/vtable/src/layout/simple-header-layout.ts index 744a08d66..5df2a9d98 100644 --- a/packages/vtable/src/layout/simple-header-layout.ts +++ b/packages/vtable/src/layout/simple-header-layout.ts @@ -37,9 +37,12 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { private seqId: number = 0; private _headerObjects: HeaderData[]; private _headerObjectMap: { [key in LayoutObjectId]: HeaderData }; + private _headerObjectsIncludeHided: HeaderData[]; + // private _headerObjectMapIncludeHided: { [key in LayoutObjectId]: HeaderData }; // private _headerObjectFieldKey: { [key in string]: HeaderData }; private _headerCellIds: number[][]; private _columns: ColumnData[]; + private _columnsIncludeHided: ColumnData[]; rowSeriesNumberColumn: SeriesNumberColumnData[]; leftRowSeriesNumberColumn: SeriesNumberColumnData[]; rightRowSeriesNumberColumn: SeriesNumberColumnData[]; @@ -69,11 +72,20 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { this._showHeader = showHeader; this._table = table; this._columns = []; + this._columnsIncludeHided = []; this._headerCellIds = []; this.hierarchyIndent = hierarchyIndent ?? 20; this.hierarchyTextStartAlignment = table.options.hierarchyTextStartAlignment; this.columnTree = new DimensionTree(columns as any, { seqId: 0 }); //seqId这里没有利用上 所有顺便传了0 - this._headerObjects = this._addHeaders(0, columns, []); + this._headerObjectsIncludeHided = this._addHeaders(0, columns, []); + // this._headerObjectMapIncludeHided = this._headerObjectsIncludeHided.reduce((o, e) => { + // o[e.id as number] = e; + // return o; + // }, {} as { [key in LayoutObjectId]: HeaderData }); + + this._headerObjects = this._headerObjectsIncludeHided.filter(col => { + return col.define.hide !== true; + }); this._headerObjectMap = this._headerObjects.reduce((o, e) => { o[e.id as number] = e; return o; @@ -729,6 +741,9 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { get columnObjects(): ColumnData[] { return this._columns; } + get headerObjectsIncludeHided(): HeaderData[] { + return this._headerObjectsIncludeHided; + } //对比multi-layout 那个里面有columWidths对象,保持结构一致 get columnWidths(): WidthData[] { if (this.leftRowSeriesNumberColumnCount) { @@ -1114,29 +1129,33 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { c => results.push(c) ); } else { - const colDef = hd; - this._columns.push({ + const colDef = { id: this.seqId++, - field: colDef.field, + field: hd.field, // fieldKey: colDef.fieldKey, - fieldFormat: colDef.fieldFormat, - width: colDef.width, - minWidth: colDef.minWidth, - maxWidth: colDef.maxWidth, - icon: colDef.icon, - cellType: colDef.cellType ?? (colDef as any).columnType ?? 'text', - chartModule: 'chartModule' in colDef ? colDef.chartModule : null, // todo: 放到对应的column对象中 - chartSpec: 'chartSpec' in colDef ? colDef.chartSpec : null, // todo: 放到对应的column对象中 - sparklineSpec: 'sparklineSpec' in colDef ? colDef.sparklineSpec : DefaultSparklineSpec, // todo: 放到对应的column对象中 - style: colDef.style, - define: colDef, - columnWidthComputeMode: colDef.columnWidthComputeMode, - disableColumnResize: colDef?.disableColumnResize, - aggregation: this._getAggregationForColumn(colDef, col), + fieldFormat: hd.fieldFormat, + width: hd.width, + minWidth: hd.minWidth, + maxWidth: hd.maxWidth, + icon: hd.icon, + cellType: hd.cellType ?? (hd as any).columnType ?? 'text', + chartModule: 'chartModule' in hd ? hd.chartModule : null, // todo: 放到对应的column对象中 + chartSpec: 'chartSpec' in hd ? hd.chartSpec : null, // todo: 放到对应的column对象中 + sparklineSpec: 'sparklineSpec' in hd ? hd.sparklineSpec : DefaultSparklineSpec, // todo: 放到对应的column对象中 + style: hd.style, + define: hd, + columnWidthComputeMode: hd.columnWidthComputeMode, + disableColumnResize: hd?.disableColumnResize, + aggregation: this._getAggregationForColumn(hd, col), isChildNode: row >= 1 - }); - for (let r = row + 1; r < this._headerCellIds.length; r++) { - this._headerCellIds[r][col] = id; + }; + this._columnsIncludeHided.push(colDef); + if (hd.hide !== true) { + this._columns.push(colDef); + + for (let r = row + 1; r < this._headerCellIds.length; r++) { + this._headerCellIds[r][col] = id; + } } } }); diff --git a/packages/vtable/src/ts-types/list-table/define/basic-define.ts b/packages/vtable/src/ts-types/list-table/define/basic-define.ts index b74ecf5cb..f2328514f 100644 --- a/packages/vtable/src/ts-types/list-table/define/basic-define.ts +++ b/packages/vtable/src/ts-types/list-table/define/basic-define.ts @@ -80,6 +80,8 @@ export interface IBasicColumnBodyDefine { // style?: ColumnStyleOption | null; /** 是否对相同内容合并单元格 **/ mergeCell?: MergeCellOption; + /** 是否隐藏 */ + hide?: boolean; customRender?: ICustomRender; customLayout?: ICustomLayout; editor?: string | IEditor | ((args: BaseCellInfo & { table: BaseTableAPI }) => string | IEditor); From 0004a2a3dff0b9e55ee5305cc7e90ce464aba13a Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 26 Jun 2024 17:46:25 +0800 Subject: [PATCH 16/37] docs: update changlog of rush --- ...isttable-column-support-hide_2024-06-26-09-46.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json diff --git a/common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json b/common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json new file mode 100644 index 000000000..ca1bba1ab --- /dev/null +++ b/common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: add column hide config #1991\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From c8514aa4499c56f73723ef0c2e2270751bdeb3db Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 26 Jun 2024 20:02:05 +0800 Subject: [PATCH 17/37] docs: supplement hide config --- docs/assets/option/en/column/base-column-type.md | 7 ++++++- docs/assets/option/zh/column/base-column-type.md | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/assets/option/en/column/base-column-type.md b/docs/assets/option/en/column/base-column-type.md index e6ef00ebf..cfb81910b 100644 --- a/docs/assets/option/en/column/base-column-type.md +++ b/docs/assets/option/en/column/base-column-type.md @@ -251,4 +251,9 @@ Data aggregation summary configuration to analyze the column data. Global options can also be configured to configure aggregation rules for each column. -Please refer to the tutorial document +Please refer to [the tutorial document](https://visactor.io/vtable/guide/data_analysis/list_table_dataAnalysis) + +${prefix} hide(boolean) = false +Not required. + +Weather hide column. \ No newline at end of file diff --git a/docs/assets/option/zh/column/base-column-type.md b/docs/assets/option/zh/column/base-column-type.md index c00a89820..b018fbe95 100644 --- a/docs/assets/option/zh/column/base-column-type.md +++ b/docs/assets/option/zh/column/base-column-type.md @@ -253,4 +253,10 @@ ${prefix} aggregation(Aggregation | CustomAggregation | Array) 全局 option 也可以配置,对每一列都配置聚合规则。 -可参考教程文档 +可参考[教程文档](https://visactor.io/vtable/guide/data_analysis/list_table_dataAnalysis) + + +${prefix} hide(boolean) = false +非必填。 + +是否隐藏列 \ No newline at end of file From 8cc5df81f0b6469311bc946ffaa582a8d872fc9f Mon Sep 17 00:00:00 2001 From: xuanhun <717532978@qq.com> Date: Thu, 27 Jun 2024 10:36:21 +0800 Subject: [PATCH 18/37] docs: add promotion task to contributing --- CONTRIBUTING.md | 9 +++++++++ CONTRIBUTING.zh-CN.md | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3514f410a..befd5255b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -204,6 +204,15 @@ $ rush change-all 3. Submit all code and create a Pull Request on Github, inviting others to review it. +### Promotion Task Contribution Guide +A promotion task refers to the action of publicly releasing materials related to VisActor, such as articles, demos, videos, etc., across various media channels. + +You can create a new issue, select the type others and tag it with promotion. Then, post it along with relevant links, screenshots, summaries, etc. + +For example, see https://github.com/VisActor/VChart/issues/2858. + +Every quarter, we will select some promotional works for VisActor and provide the authors with material rewards. + ## Embrace the VisActor Community In addition to contributing code to VisActor, we encourage you to participate in other activities that will make the community more prosperous, such as: diff --git a/CONTRIBUTING.zh-CN.md b/CONTRIBUTING.zh-CN.md index 56d3eda45..6b2fb9591 100644 --- a/CONTRIBUTING.zh-CN.md +++ b/CONTRIBUTING.zh-CN.md @@ -212,7 +212,18 @@ $ rush docs $ rush change-all ``` -3. 提交所有代码,并在 Github 创建 Pull Request,邀请其他人进行 review + +4. 提交所有代码,并在 Github 创建 Pull Request,邀请其他人进行 review + + + +### 推广任务贡献指南 + +推广任务是指你将和VisActor相关的文章、demo、视频 等素材,公开发布到各种媒体渠道的行为。 +你可以新建一个 issue,类型选择 `others` 打上 `promotion` 的标签,然后将相关链接,截图,简介等一起发布即可。 +例如:[https://github.com/VisActor/VChart/issues/2858](https://github.com/VisActor/VChart/issues/2858) + +每个季度我们会评选一些推广VisActor的作品,给予作者一定的物质奖励。 ## 拥抱 VisActor 社区 From b37d70f9a7981ce5124d4e1ff94919feb54db837 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 27 Jun 2024 10:50:58 +0800 Subject: [PATCH 19/37] docs: supplement unit test notes --- docs/assets/guide/en/Contribution_Guide.md | 10 ++++++++++ docs/assets/guide/zh/Contribution_Guide.md | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/docs/assets/guide/en/Contribution_Guide.md b/docs/assets/guide/en/Contribution_Guide.md index 3514f410a..75d475825 100644 --- a/docs/assets/guide/en/Contribution_Guide.md +++ b/docs/assets/guide/en/Contribution_Guide.md @@ -151,6 +151,16 @@ Fill in the changes of this submission according to the template: After filling in the relevant information, click Create pull request to submit. +### Other points to note + +1. Unit Testing: + +If it involves interface modification, please add unit tests in the **tests** directory and run the `rushx test` command for testing. + +When pushing the code, the unit test will be automatically checked before it is pushed. If all pass, the code will be pushed to the remote. If it fails, there should be a problem with the code logic or the unit test data needs to be modified. Please correct it according to the situation. + +If you encounter the error "Cannot find module '@visactor/vtable-editors'" when running unit tests, please execute the `rushx build` command in the vtable-editors project first, and then return to the vtable project to execute the command. + ## Mini Task Development Guide "**good first issue**" is a common label in open source communities, and its purpose is to help new contributors find suitable entry-level issues. diff --git a/docs/assets/guide/zh/Contribution_Guide.md b/docs/assets/guide/zh/Contribution_Guide.md index 56d3eda45..d95155c93 100644 --- a/docs/assets/guide/zh/Contribution_Guide.md +++ b/docs/assets/guide/zh/Contribution_Guide.md @@ -149,6 +149,16 @@ git push origin docs/add-funnel-demo 相关信息填写完成后,点击 Create pull request 提交。 +### 其他注意点 + +1. 单元测试: + +如果是涉及接口修改请在**tests**目录下添加单元测试,并运行`rushx test`命令进行测试。 + +当 push 推送代码前会自动进行单元测试的检查,如果全部通过会推送代码到远程,如果没有通过那么应该是代码逻辑有问题或者需求修改单测数据,请根据情况进行修正。 + +运行单元测试中如果遇到"Cannot find module '@visactor/vtable-editors'" 的错误,请先到 vtable-editors 项目中执行`rushx build`命令,然后再回到 vtable 项目中执行命令。 + ## Mini Task 开发指南 "**good first issue**" 是一个在开源社区常见的标签,这个标签的目的是帮助新贡献者找到适合入门的问题。 From f59ea1a906cfa8c90277f9fe4f29ccca8f4ebfa1 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 27 Jun 2024 16:09:35 +0800 Subject: [PATCH 20/37] fix: when not exit edit state then can not select other cells #1974 --- packages/vtable/src/edit/edit-manager.ts | 12 +++++++----- .../vtable/src/event/listener/table-group.ts | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/vtable/src/edit/edit-manager.ts b/packages/vtable/src/edit/edit-manager.ts index 7b2562095..28574712e 100644 --- a/packages/vtable/src/edit/edit-manager.ts +++ b/packages/vtable/src/edit/edit-manager.ts @@ -108,10 +108,10 @@ export class EditManeger { } } - /** 如果是事件触发调用该接口 请传入原始事件对象 将判断事件对象是否在编辑器本身上面 来处理是否结束编辑 */ - completeEdit(e?: Event) { + /** 如果是事件触发调用该接口 请传入原始事件对象 将判断事件对象是否在编辑器本身上面 来处理是否结束编辑 返回值如果为false说明没有退出编辑状态*/ + completeEdit(e?: Event): boolean { if (!this.editingEditor) { - return; + return true; } const target = e?.target as HTMLElement | undefined; @@ -122,10 +122,10 @@ export class EditManeger { console.warn('VTable Warn: `targetIsOnEditor` is deprecated, please use `isEditorElement` instead.'); if (editor.targetIsOnEditor(target)) { - return; + return false; } } else if (!editor.isEditorElement || editor.isEditorElement(target)) { - return; + return false; } } @@ -148,7 +148,9 @@ export class EditManeger { this.editingEditor.exit?.(); this.editingEditor.onEnd?.(); this.editingEditor = null; + return true; } + return false; } cancelEdit() { diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index b41964d52..b3d3447b9 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -341,7 +341,11 @@ export function bindTableGroupListener(eventManager: EventManager) { } } } - (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); + const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); + if (!isCompleteEdit) { + // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 + return; + } stateManager.updateInteractionState(InteractionState.default); eventManager.dealTableHover(); //点击到表格外部不需要取消选中状态 @@ -386,8 +390,11 @@ export function bindTableGroupListener(eventManager: EventManager) { // 点击在menu外,且不是下拉菜单的icon,移除menu stateManager.hideMenu(); } - (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); - + const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); + if (!isCompleteEdit) { + // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 + return; + } const hitIcon = (eventArgsSet?.eventArgs?.target as any)?.role?.startsWith('icon') ? eventArgsSet.eventArgs.target : (e.target as any).role?.startsWith('icon') @@ -679,7 +686,11 @@ export function bindTableGroupListener(eventManager: EventManager) { if ((eventArgsSet.eventArgs?.target as any) !== stateManager.residentHoverIcon?.icon) { stateManager.hideMenu(); } - (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); + const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); + if (isCompleteEdit) { + // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 + return; + } const hitIcon = (e.target as any).role?.startsWith('icon') ? e.target : undefined; eventManager.downIcon = hitIcon; From 20c25689d7d53d5469cd48a0c72e0e349d854f23 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 27 Jun 2024 16:10:16 +0800 Subject: [PATCH 21/37] docs: update changlog of rush --- ...tevalue-false-notSelectOther_2024-06-27-08-10.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json diff --git a/common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json b/common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json new file mode 100644 index 000000000..0072d1de7 --- /dev/null +++ b/common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: when not exit edit state then can not select other cells #1974\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From 571d84c29927c2e38fc7e5d6779e14ca2e3c0da2 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 27 Jun 2024 17:40:33 +0800 Subject: [PATCH 22/37] refactor: sparkline cellType set aggregationType None automatically #1999 --- packages/vtable/src/layout/layout-helper.ts | 22 +++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/vtable/src/layout/layout-helper.ts b/packages/vtable/src/layout/layout-helper.ts index 8a951c456..4a655d3a8 100644 --- a/packages/vtable/src/layout/layout-helper.ts +++ b/packages/vtable/src/layout/layout-helper.ts @@ -17,6 +17,7 @@ import type { IImageColumnIndicator, IImageHeaderIndicator } from '../ts-types/p import type { IImageColumnBodyDefine, IImageHeaderDefine } from '../ts-types/list-table/define/image-define'; import type { ITreeLayoutHeadNode } from './tree-helper'; import { DimensionTree } from './tree-helper'; +import type { ISparklineColumnIndicator } from '../ts-types/pivot-table/indicator/sparkline-indicator'; export function checkHasAggregation(layoutMap: SimpleHeaderLayoutMap) { const columnObjects = layoutMap.columnObjects; @@ -242,13 +243,22 @@ export function parseColKeyRowKeyForPivotTable(table: PivotTable, options: Pivot keys.push(indicatorObj); } else { keys.push(indicatorObj.indicatorKey); - if ((indicatorObj as IChartColumnIndicator).chartSpec) { + if ( + (indicatorObj as IChartColumnIndicator).chartSpec || + (indicatorObj as ISparklineColumnIndicator).sparklineSpec + ) { if (table.internalProps.dataConfig?.aggregationRules) { - table.internalProps.dataConfig?.aggregationRules.push({ - field: indicatorObj.indicatorKey, - indicatorKey: indicatorObj.indicatorKey, - aggregationType: AggregationType.NONE - }); + if ( + !table.internalProps.dataConfig.aggregationRules.find(aggregation => { + return aggregation.indicatorKey === indicatorObj.indicatorKey; + }) + ) { + table.internalProps.dataConfig.aggregationRules.push({ + field: indicatorObj.indicatorKey, + indicatorKey: indicatorObj.indicatorKey, + aggregationType: AggregationType.NONE + }); + } } else if (table.internalProps.dataConfig) { table.internalProps.dataConfig.aggregationRules = [ { From 3e0e552d966158d3dd69179d6b3c0f976872126b Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 27 Jun 2024 17:41:10 +0800 Subject: [PATCH 23/37] docs: update changlog of rush --- ...elltype-aggregationtype-none_2024-06-27-09-41.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json diff --git a/common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json b/common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json new file mode 100644 index 000000000..d6a4f9f5b --- /dev/null +++ b/common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "refactor: sparkline cellType set aggregationType None automatically #1999\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From 85d22dc0718d6375cef89dc0942dde2261e43c98 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 27 Jun 2024 19:26:57 +0800 Subject: [PATCH 24/37] docs: add pivot table sparkline demo --- .../demo/en/cell-type/pivot-sparkline.md | 133 ++++++++++++++++++ docs/assets/demo/menu.json | 7 + .../demo/zh/cell-type/pivot-sparkline.md | 133 ++++++++++++++++++ .../data_analysis/pivot_table_dataAnalysis.md | 7 + .../data_analysis/pivot_table_dataAnalysis.md | 7 + 5 files changed, 287 insertions(+) create mode 100644 docs/assets/demo/en/cell-type/pivot-sparkline.md create mode 100644 docs/assets/demo/zh/cell-type/pivot-sparkline.md diff --git a/docs/assets/demo/en/cell-type/pivot-sparkline.md b/docs/assets/demo/en/cell-type/pivot-sparkline.md new file mode 100644 index 000000000..c48a4423a --- /dev/null +++ b/docs/assets/demo/en/cell-type/pivot-sparkline.md @@ -0,0 +1,133 @@ +--- +category: examples +group: Cell Type +title: PivotTable display sparkline +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pivot-sparkline.png +link: '../guide/cell_type/chart' +option: PivotTable-indicators-chart#cellType +--- + +# PivotTable display sparkline + +Display the data corresponding to the cell in the form of a mini chart. + +## Key Configurations + +- `cellType: 'sparkline'` specifies the type of chart +- `sparklineSpec: {}` Sparkline spec +- `dataConfig.aggregationRules` configures aggregation rules. The rule used here is of `RECORD` type, which means that the source data record of a cell needs to be collected as the data source of the mini chart + +## Code demo + +```javascript livedemo template=vtable +VTable.register.chartModule('vchart', VChart); +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') + .then(res => res.json()) + .then(data => { + const columns = [ + { + dimensionKey: 'Region', + title: 'Region', + headerStyle: { + textStick: true + } + } + ]; + const rows = ['Category']; + const indicators = [ + { + indicatorKey: 'Sales', + title: 'Sales', + width: 120, + format: rec => { + return '$' + Number(rec).toFixed(2); + } + }, + { + indicatorKey: 'SalesRecords', + title: 'Sales Trend', + cellType: 'sparkline', + width: 500, + sparklineSpec: { + type: 'line', + xField: 'Order Date', + yField: 'Sales', + pointShowRule: 'none', + smooth: true, + line: { + style: { + stroke: '#2E62F1', + strokeWidth: 2 + // interpolate: 'monotone', + } + }, + point: { + hover: { + stroke: 'blue', + strokeWidth: 1, + fill: 'red', + shape: 'circle', + size: 4 + }, + style: { + stroke: 'red', + strokeWidth: 1, + fill: 'yellow', + shape: 'circle', + size: 2 + } + }, + crosshair: { + style: { + stroke: 'gray', + strokeWidth: 1 + } + } + } + } + ]; + const option = { + dataConfig: { + aggregationRules: [ + //做聚合计算的依据,如销售额如果没有配置则默认按聚合sum计算结果显示单元格内容 + { + indicatorKey: 'SalesRecords', //指标名称 + field: 'Sales', //指标依据字段 + aggregationType: VTable.TYPES.AggregationType.RECORD //计算类型 + } + ] + }, + rows, + columns, + indicators, + indicatorsAsCol: true, + records: data, + defaultRowHeight: 80, + defaultHeaderRowHeight: 50, + defaultColWidth: 280, + defaultHeaderColWidth: 130, + indicatorTitle: '指标', + autoWrapText: true, + // widthMode:'adaptive', + // heightMode:'adaptive', + corner: { + titleOnDimension: 'row', + headerStyle: { + autoWrapText: true + } + } + }; + + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + const { LEGEND_ITEM_CLICK } = VTable.ListTable.EVENT_TYPE; + window.tableInstance = tableInstance; + tableInstance.onVChartEvent('click', args => { + console.log('onVChartEvent click', args); + }); + tableInstance.onVChartEvent('mouseover', args => { + console.log('onVChartEvent mouseover', args); + }); + window.tableInstance = tableInstance; + }); +``` diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index 4238264d4..a1424e734 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -325,6 +325,13 @@ "en": "Chart Type In PivotTable" } }, + { + "path": "pivot-sparkline", + "title": { + "zh": "透视表迷你图展示", + "en": "Sparkline In PivotTable" + } + }, { "path": "composite-cellType", "title": { diff --git a/docs/assets/demo/zh/cell-type/pivot-sparkline.md b/docs/assets/demo/zh/cell-type/pivot-sparkline.md new file mode 100644 index 000000000..c3e8d9861 --- /dev/null +++ b/docs/assets/demo/zh/cell-type/pivot-sparkline.md @@ -0,0 +1,133 @@ +--- +category: examples +group: Cell Type +title: 透视表展示迷你图 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pivot-sparkline.png +link: '../guide/cell_type/chart' +option: PivotTable-indicators-chart#cellType +--- + +# 透视表展示迷你图 + +将单元格对应的数据以迷你图表的形式展示。 + +## 关键配置 + +- `cellType: 'sparkline'` 指定类型 chart +- `sparklineSpec: {}` 迷你图 spec +- `dataConfig.aggregationRules` 配置聚合规则 这里用到的规则是`RECORD`类型,表示需要搜集单元格对一个的源数据记录,作为迷你图表的数据源 + +## 代码演示 + +```javascript livedemo template=vtable +VTable.register.chartModule('vchart', VChart); +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') + .then(res => res.json()) + .then(data => { + const columns = [ + { + dimensionKey: 'Region', + title: 'Region', + headerStyle: { + textStick: true + } + } + ]; + const rows = ['Category']; + const indicators = [ + { + indicatorKey: 'Sales', + title: 'Sales', + width: 120, + format: rec => { + return '$' + Number(rec).toFixed(2); + } + }, + { + indicatorKey: 'SalesRecords', + title: 'Sales Trend', + cellType: 'sparkline', + width: 500, + sparklineSpec: { + type: 'line', + xField: 'Order Date', + yField: 'Sales', + pointShowRule: 'none', + smooth: true, + line: { + style: { + stroke: '#2E62F1', + strokeWidth: 2 + // interpolate: 'monotone', + } + }, + point: { + hover: { + stroke: 'blue', + strokeWidth: 1, + fill: 'red', + shape: 'circle', + size: 4 + }, + style: { + stroke: 'red', + strokeWidth: 1, + fill: 'yellow', + shape: 'circle', + size: 2 + } + }, + crosshair: { + style: { + stroke: 'gray', + strokeWidth: 1 + } + } + } + } + ]; + const option = { + dataConfig: { + aggregationRules: [ + //做聚合计算的依据,如销售额如果没有配置则默认按聚合sum计算结果显示单元格内容 + { + indicatorKey: 'SalesRecords', //指标名称 + field: 'Sales', //指标依据字段 + aggregationType: VTable.TYPES.AggregationType.RECORD //计算类型 + } + ] + }, + rows, + columns, + indicators, + indicatorsAsCol: true, + records: data, + defaultRowHeight: 80, + defaultHeaderRowHeight: 50, + defaultColWidth: 280, + defaultHeaderColWidth: 130, + indicatorTitle: '指标', + autoWrapText: true, + // widthMode:'adaptive', + // heightMode:'adaptive', + corner: { + titleOnDimension: 'row', + headerStyle: { + autoWrapText: true + } + } + }; + + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + const { LEGEND_ITEM_CLICK } = VTable.ListTable.EVENT_TYPE; + window.tableInstance = tableInstance; + tableInstance.onVChartEvent('click', args => { + console.log('onVChartEvent click', args); + }); + tableInstance.onVChartEvent('mouseover', args => { + console.log('onVChartEvent mouseover', args); + }); + window.tableInstance = tableInstance; + }); +``` diff --git a/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md b/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md index 2b07f0aef..66b7ee0cb 100644 --- a/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md +++ b/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md @@ -188,6 +188,11 @@ Configuration example: indicatorKey: 'OrderSalesValue', //Indicator name field: 'Sales', //Indicator based on field aggregationType: VTable.TYPES.AggregationType.NONE, //don't aggregate + }, + { + indicatorKey: 'orderRecords', //Indicator name + field: 'Sales', //Indicator based on field + aggregationType: VTable.TYPES.AggregationType.RECORD, //don't aggregate. Match all the corresponding data as the value of the cell } ] ``` @@ -219,6 +224,8 @@ dataConfig:{ The sales indicator in this record is a non-numeric value, and it is required to display `"NULL"` directly in the table cell. In this case, you can set `NONE` to require the internal aggregation logic of VTable to directly obtain the value of the sales field without aggregation. +2. The usage scenario of AggregationType.RECORD indicator without aggregation is mainly used to match all data according to the data record passed by the user, and use it as the display data of the cell. The usage scenario is such as collecting data sets as mini-charts. For specific demo, see: https://visactor.io/vtable/demo/cell-type/pivot-sparkline + ### 5. Derive Field [option description](../../option/PivotTable#dataConfig.derivedFieldRules) diff --git a/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md b/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md index ba23260e6..f7bb70e1b 100644 --- a/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md +++ b/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md @@ -188,6 +188,11 @@ filterRules: [ indicatorKey: 'OrderSalesValue', //指标名称 field: 'Sales', //指标依据字段 aggregationType: VTable.TYPES.AggregationType.NONE, //不做聚合 匹配到其中对应数据获取其对应field的值 + }, + { + indicatorKey: 'orderRecords', //指标名称 + field: 'Sales', //指标依据字段 + aggregationType: VTable.TYPES.AggregationType.RECORD, //不做聚合 匹配到其中对应的全部数据作为单元格的值 } ] ``` @@ -220,6 +225,8 @@ dataConfig:{ 其中该条 record 中 sales 指标是个非数值型的值,而且需求要将`"NULL"`直接显示到表格单元格中,那么可以设置 NONE,要求 VTable 的内部聚合逻辑不聚合直接取`sales`字段值。 +2. AggregationType.RECORD 指标不做聚合的使用场景主要用于根据用户传入数据 record 匹配到所有数据,将其作为单元格的展示数据,用法场景如需要搜集数据集作为迷你图展示,具体 demo 见:https://visactor.io/vtable/demo/cell-type/pivot-sparkline + ### 5. 派生字段 [option 说明](../../option/PivotTable#dataConfig.derivedFieldRules) From 7bd68a8207d7306970949b45916ad8e87f6c07ce Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Thu, 27 Jun 2024 21:50:44 +0800 Subject: [PATCH 25/37] fix: fix split stroke pisition --- .../group-contribution-render.ts | 170 ++++++++++-------- 1 file changed, 95 insertions(+), 75 deletions(-) diff --git a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts index c21c8aca4..e10fe064d 100644 --- a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts +++ b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts @@ -156,21 +156,21 @@ export class SplitGroupAfterRenderContribution implements IGroupRenderContributi return; } const bottomRight = table?.theme.cellBorderClipDirection === 'bottom-right'; - let deltaWidth = 0; - let deltaHeight = 0; + // let deltaWidth = 0; + // let deltaHeight = 0; if (bottomRight) { x = Math.floor(x) - 0.5; y = Math.floor(y) - 0.5; - if (group.role === 'cell') { - const col = (group as any).col as number; - const row = (group as any).row as number; - if (table && col === table.colCount - 1) { - deltaWidth = 1; - } - if (table && row === table.rowCount - 1) { - deltaHeight = 1; - } - } + // if (group.role === 'cell') { + // const col = (group as any).col as number; + // const row = (group as any).row as number; + // if (table && col === table.colCount - 1) { + // deltaWidth = 1; + // } + // if (table && row === table.rowCount - 1) { + // deltaHeight = 1; + // } + // } } else { x = Math.floor(x) + 0.5; y = Math.floor(y) + 0.5; @@ -182,8 +182,9 @@ export class SplitGroupAfterRenderContribution implements IGroupRenderContributi } const { width: widthFroDraw, height: heightFroDraw } = getCellSizeForDraw( group, - Math.ceil(width + deltaWidth), - Math.ceil(height + deltaHeight) + Math.ceil(width), + Math.ceil(height), + bottomRight ); widthForStroke = widthFroDraw; heightForStroke = heightFroDraw; @@ -269,6 +270,7 @@ export function renderStroke( // context.setLineDash(highlightDash); // context.lineCap = 'butt'; // } + // context.lineCap = 'square'; const { lineDash = groupAttribute.lineDash } = group.attribute as any; // const lineDash = context.getLineDash(); @@ -280,16 +282,23 @@ export function renderStroke( context.beginPath(); context.moveTo(x, y); + const strokeTop = (isStrokeTrue || stroke[0]) && (isWidthNumber || strokeArrayWidth[0]); + const strokeRight = (isStrokeTrue || stroke[1]) && (isWidthNumber || strokeArrayWidth[1]); + const strokeBottom = (isStrokeTrue || stroke[2]) && (isWidthNumber || strokeArrayWidth[2]); + const strokeLeft = (isStrokeTrue || stroke[3]) && (isWidthNumber || strokeArrayWidth[3]); + // top - if ((isStrokeTrue || stroke[0]) && (isWidthNumber || strokeArrayWidth[0])) { + if (strokeTop) { // context.lineTo(x + width, y); + const deltaLeft = strokeLeft ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[3]) / 2; + const deltaRight = strokeRight ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[1]) / 2; if (isPart && Array.isArray(part[0])) { - context.moveTo(x + width * part[0][0], y); - context.lineTo(x + width * (part[0][1] - part[0][0]), y); - context.moveTo(x + width, y); + context.moveTo(x - deltaLeft + (width + deltaLeft + deltaRight) * part[0][0], y); + context.lineTo(x - deltaLeft + (width + deltaLeft + deltaRight) * (part[0][1] - part[0][0]), y); + context.moveTo(x + width + deltaRight, y); } else { - context.moveTo(x, y); - context.lineTo(x + width, y); + context.moveTo(x - deltaLeft, y); + context.lineTo(x + width + deltaRight, y); } if (isSplitDraw || isDash) { if (strokeArrayColor && strokeArrayColor[0]) { @@ -312,15 +321,17 @@ export function renderStroke( context.moveTo(x + width, y); } // right - if ((isStrokeTrue || stroke[1]) && (isWidthNumber || strokeArrayWidth[1])) { + if (strokeRight) { // context.lineTo(x + width, y + height); + const deltaTop = strokeTop ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[0]) / 2; + const deltaBottom = strokeBottom ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[2]) / 2; if (isPart && Array.isArray(part[1])) { - context.moveTo(x + width, y + height * part[1][0]); - context.lineTo(x + width, y + height * (part[1][1] - part[1][0])); - context.moveTo(x + width, y + height); + context.moveTo(x + width, y - deltaTop + height * part[1][0]); + context.lineTo(x + width, y - deltaTop + (height + deltaTop + deltaBottom) * (part[1][1] - part[1][0])); + context.moveTo(x + width, y + height + deltaBottom); } else { - context.moveTo(x + width, y); - context.lineTo(x + width, y + height); + context.moveTo(x + width, y - deltaTop); + context.lineTo(x + width, y + height + deltaBottom); } if (isSplitDraw || isDash) { if (strokeArrayColor && strokeArrayColor[1]) { @@ -343,15 +354,17 @@ export function renderStroke( context.moveTo(x + width, y + height); } // bottom - if ((isStrokeTrue || stroke[2]) && (isWidthNumber || strokeArrayWidth[2])) { + if (strokeBottom) { // context.lineTo(x, y + height); + const deltaLeft = strokeLeft ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[3]) / 2; + const deltaRight = strokeRight ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[1]) / 2; if (isPart && Array.isArray(part[2])) { - context.moveTo(x + width * part[2][0], y + height); - context.lineTo(x + width * (part[2][1] - part[2][0]), y + height); - context.moveTo(x, y + height); + context.moveTo(x - deltaLeft + (width + deltaLeft + deltaRight) * part[2][0], y + height); + context.lineTo(x - deltaLeft + (width + deltaLeft + deltaRight) * (part[2][1] - part[2][0]), y + height); + context.moveTo(x - deltaLeft, y + height); } else { - context.moveTo(x, y + height); - context.lineTo(x + width, y + height); + context.moveTo(x - deltaLeft, y + height); + context.lineTo(x + width + deltaRight, y + height); } if (isSplitDraw || isDash) { if (strokeArrayColor && strokeArrayColor[2]) { @@ -374,15 +387,17 @@ export function renderStroke( context.moveTo(x, y + height); } // left - if ((isStrokeTrue || stroke[3]) && (isWidthNumber || strokeArrayWidth[3])) { + if (strokeLeft) { // context.lineTo(x, y); + const deltaTop = strokeTop ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[0]) / 2; + const deltaBottom = strokeBottom ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[2]) / 2; if (isPart && Array.isArray(part[3])) { - context.moveTo(x, y + height * part[3][0]); - context.lineTo(x, y + height * (part[3][1] - part[3][0])); - context.moveTo(x, y); + context.moveTo(x, y - deltaTop + (height + deltaTop + deltaBottom) * part[3][0]); + context.lineTo(x, y - deltaTop + (height + deltaTop + deltaBottom) * (part[3][1] - part[3][0])); + context.moveTo(x, y - deltaTop); } else { - context.moveTo(x, y); - context.lineTo(x, y + height); + context.moveTo(x, y - deltaTop); + context.lineTo(x, y + height + deltaBottom); } if (isSplitDraw || isDash) { if (strokeArrayColor && strokeArrayColor[3]) { @@ -531,21 +546,21 @@ export class DashGroupAfterRenderContribution implements IGroupRenderContributio let heightForStroke; if (lineWidth & 1) { const bottomRight = table.theme.cellBorderClipDirection === 'bottom-right'; - let deltaWidth = 0; - let deltaHeight = 0; + const deltaWidth = 0; + const deltaHeight = 0; if (bottomRight) { x = Math.floor(x) - 0.5; y = Math.floor(y) - 0.5; - if (group.role === 'cell') { - const col = (group as any).col as number; - const row = (group as any).row as number; - if (table && col === table.colCount - 1) { - deltaWidth = 1; - } - if (table && row === table.rowCount - 1) { - deltaHeight = 1; - } - } + // if (group.role === 'cell') { + // const col = (group as any).col as number; + // const row = (group as any).row as number; + // if (table && col === table.colCount - 1) { + // deltaWidth = 1; + // } + // if (table && row === table.rowCount - 1) { + // deltaHeight = 1; + // } + // } } else { x = Math.floor(x) + 0.5; y = Math.floor(y) + 0.5; @@ -554,7 +569,8 @@ export class DashGroupAfterRenderContribution implements IGroupRenderContributio const { width: widthFroDraw, height: heightFroDraw } = getCellSizeForDraw( group, Math.ceil(width + deltaWidth), - Math.ceil(height + deltaHeight) + Math.ceil(height + deltaHeight), + bottomRight ); widthForStroke = widthFroDraw; heightForStroke = heightFroDraw; @@ -736,34 +752,37 @@ export class AdjustPosGroupAfterRenderContribution implements IGroupRenderContri width = Math.round(width); height = Math.round(height); } - const { width: widthFroDraw, height: heightFroDraw } = getCellSizeForDraw( - group, - Math.ceil(width), - Math.ceil(height) - ); + context.beginPath(); const bottomRight = table?.theme.cellBorderClipDirection === 'bottom-right'; - let deltaWidth = 0; - let deltaHeight = 0; + const deltaWidth = 0; + const deltaHeight = 0; if (bottomRight) { x = Math.floor(x) - 0.5; y = Math.floor(y) - 0.5; - if (group.role === 'cell') { - const col = (group as any).col as number; - const row = (group as any).row as number; - if (table && col === table.colCount - 1) { - deltaWidth = 1; - } - if (table && row === table.rowCount - 1) { - deltaHeight = 1; - } - } + // if (group.role === 'cell') { + // const col = (group as any).col as number; + // const row = (group as any).row as number; + // if (table && col === table.colCount - 1) { + // deltaWidth = 1; + // } + // if (table && row === table.rowCount - 1) { + // deltaHeight = 1; + // } + // } } else { x = Math.floor(x) + 0.5; y = Math.floor(y) + 0.5; } + const { width: widthFroDraw, height: heightFroDraw } = getCellSizeForDraw( + group, + Math.ceil(width), + Math.ceil(height), + bottomRight + ); + if (cornerRadius) { // 测试后,cache对于重绘性能提升不大,但是在首屏有一定性能损耗,因此rect不再使用cache createRectPath(context, x, y, widthFroDraw + deltaWidth, heightFroDraw + deltaHeight, cornerRadius); @@ -998,7 +1017,7 @@ export class ClipBodyGroupAfterRenderContribution implements IGroupRenderContrib } } -function getCellSizeForDraw(group: any, width: number, height: number) { +function getCellSizeForDraw(group: any, width: number, height: number, bottomRight: boolean) { const table = group.stage.table as BaseTableAPI; if (group.role === 'cell') { let col = group.col as number; @@ -1009,24 +1028,25 @@ function getCellSizeForDraw(group: any, width: number, height: number) { row = mergeInfo.end.row; } - if (table && col === table.colCount - 1) { + if (table && col === table.colCount - 1 && !bottomRight) { width -= 1; - } else if (table && col === table.frozenColCount - 1 && table.scrollLeft) { + } else if (table && col === table.frozenColCount - 1 && table.scrollLeft && !bottomRight) { width -= 1; } - if (table && row === table.rowCount - 1) { + if (table && row === table.rowCount - 1 && !bottomRight) { height -= 1; - } else if (table && row === table.frozenRowCount - 1 && table.scrollTop) { + } else if (table && row === table.frozenRowCount - 1 && table.scrollTop && !bottomRight) { height -= 1; } } else if (group.role === 'corner-frozen') { - if (table && table.scrollLeft) { + if (table && table.scrollLeft && !bottomRight) { width -= 1; } - if (table && table.scrollTop) { + if (table && table.scrollTop && !bottomRight) { height -= 1; } } + return { width, height }; } From 08e4a5966741e632ed2b9006ad511796dcf062f9 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Fri, 28 Jun 2024 15:22:07 +0800 Subject: [PATCH 26/37] fix: fix lineCap for split stroke --- .../graphic/contributions/group-contribution-render.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts index e10fe064d..946cbe0a5 100644 --- a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts +++ b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts @@ -268,9 +268,9 @@ export function renderStroke( context.setStrokeStyle(group, group.attribute, x, y, groupAttribute); // if (isHighlight) { // context.setLineDash(highlightDash); - // context.lineCap = 'butt'; // } - // context.lineCap = 'square'; + const oldLineCap = context.lineCap; + context.lineCap = 'square'; const { lineDash = groupAttribute.lineDash } = group.attribute as any; // const lineDash = context.getLineDash(); @@ -428,6 +428,7 @@ export function renderStroke( context.stroke(); } context.lineDashOffset = 0; + context.lineCap = oldLineCap; context.setLineDash([]); } From d860a18bd1041e9f63bc7937270b11981f9358b0 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Sat, 29 Jun 2024 21:35:22 +0800 Subject: [PATCH 27/37] fix: pivotTable virtual node edit value not work #2002 --- packages/vtable/src/PivotTable.ts | 240 ++++++++++++++++++------------ 1 file changed, 144 insertions(+), 96 deletions(-) diff --git a/packages/vtable/src/PivotTable.ts b/packages/vtable/src/PivotTable.ts index 2e47ffbcc..8b1345de3 100644 --- a/packages/vtable/src/PivotTable.ts +++ b/packages/vtable/src/PivotTable.ts @@ -572,24 +572,32 @@ export class PivotTable extends BaseTable implements PivotTableAPI { const cellDimensionPath = this.internalProps.layoutMap.getCellHeaderPaths(col, row); if (cellDimensionPath) { let indicatorPosition: { position: 'col' | 'row'; index?: number }; - const colKeys = cellDimensionPath.colHeaderPaths.map((colPath: any, index: number) => { - if (colPath.indicatorKey) { - indicatorPosition = { - position: 'col', - index - }; - } - return colPath.indicatorKey ?? colPath.value; - }); - const rowKeys = cellDimensionPath.rowHeaderPaths.map((rowPath: any, index: number) => { - if (rowPath.indicatorKey) { - indicatorPosition = { - position: 'row', - index - }; - } - return rowPath.indicatorKey ?? rowPath.value; - }); + const colKeys = cellDimensionPath.colHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((colPath: any, index: number) => { + if (colPath.indicatorKey) { + indicatorPosition = { + position: 'col', + index + }; + } + return colPath.indicatorKey ?? colPath.value; + }); + const rowKeys = cellDimensionPath.rowHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((rowPath: any, index: number) => { + if (rowPath.indicatorKey) { + indicatorPosition = { + position: 'row', + index + }; + } + return rowPath.indicatorKey ?? rowPath.value; + }); const aggregator = this.dataset.getAggregator( // !this.internalProps.layoutMap.indicatorsAsCol ? rowKeys.slice(0, -1) : rowKeys, // this.internalProps.layoutMap.indicatorsAsCol ? colKeys.slice(0, -1) : colKeys, @@ -736,24 +744,32 @@ export class PivotTable extends BaseTable implements PivotTableAPI { } else if (this.dataset) { let indicatorPosition: { position: 'col' | 'row'; index?: number }; const cellDimensionPath = this.internalProps.layoutMap.getCellHeaderPaths(col, row); - const colKeys = cellDimensionPath.colHeaderPaths.map((colPath: any, index: number) => { - if (colPath.indicatorKey) { - indicatorPosition = { - position: 'col', - index - }; - } - return colPath.indicatorKey ?? colPath.value; - }); - const rowKeys = cellDimensionPath.rowHeaderPaths.map((rowPath: any, index: number) => { - if (rowPath.indicatorKey) { - indicatorPosition = { - position: 'row', - index - }; - } - return rowPath.indicatorKey ?? rowPath.value; - }); + const colKeys = cellDimensionPath.colHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((colPath: any, index: number) => { + if (colPath.indicatorKey) { + indicatorPosition = { + position: 'col', + index + }; + } + return colPath.indicatorKey ?? colPath.value; + }); + const rowKeys = cellDimensionPath.rowHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((rowPath: any, index: number) => { + if (rowPath.indicatorKey) { + indicatorPosition = { + position: 'row', + index + }; + } + return rowPath.indicatorKey ?? rowPath.value; + }); const aggregator = this.dataset.getAggregator( // !this.internalProps.layoutMap.indicatorsAsCol ? rowKeys.slice(0, -1) : rowKeys, // this.internalProps.layoutMap.indicatorsAsCol ? colKeys.slice(0, -1) : colKeys, @@ -801,24 +817,32 @@ export class PivotTable extends BaseTable implements PivotTableAPI { } else if (this.dataset) { let indicatorPosition: { position: 'col' | 'row'; index?: number }; const cellDimensionPath = this.internalProps.layoutMap.getCellHeaderPaths(col, row); - const colKeys = cellDimensionPath.colHeaderPaths.map((colPath: any, index: number) => { - if (colPath.indicatorKey) { - indicatorPosition = { - position: 'col', - index - }; - } - return colPath.indicatorKey ?? colPath.value; - }); - const rowKeys = cellDimensionPath.rowHeaderPaths.map((rowPath: any, index: number) => { - if (rowPath.indicatorKey) { - indicatorPosition = { - position: 'row', - index - }; - } - return rowPath.indicatorKey ?? rowPath.value; - }); + const colKeys = cellDimensionPath.colHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((colPath: any, index: number) => { + if (colPath.indicatorKey) { + indicatorPosition = { + position: 'col', + index + }; + } + return colPath.indicatorKey ?? colPath.value; + }); + const rowKeys = cellDimensionPath.rowHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((rowPath: any, index: number) => { + if (rowPath.indicatorKey) { + indicatorPosition = { + position: 'row', + index + }; + } + return rowPath.indicatorKey ?? rowPath.value; + }); const aggregator = this.dataset.getAggregator( // !this.internalProps.layoutMap.indicatorsAsCol ? rowKeys.slice(0, -1) : rowKeys, // this.internalProps.layoutMap.indicatorsAsCol ? colKeys.slice(0, -1) : colKeys, @@ -862,24 +886,32 @@ export class PivotTable extends BaseTable implements PivotTableAPI { } else if (this.dataset) { let indicatorPosition: { position: 'col' | 'row'; index?: number }; const cellDimensionPath = this.internalProps.layoutMap.getCellHeaderPaths(col, row); - const colKeys = cellDimensionPath.colHeaderPaths.map((colPath: any, index: number) => { - if (colPath.indicatorKey) { - indicatorPosition = { - position: 'col', - index - }; - } - return colPath.indicatorKey ?? colPath.value; - }); - const rowKeys = cellDimensionPath.rowHeaderPaths.map((rowPath: any, index: number) => { - if (rowPath.indicatorKey) { - indicatorPosition = { - position: 'row', - index - }; - } - return rowPath.indicatorKey ?? rowPath.value; - }); + const colKeys = cellDimensionPath.colHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((colPath: any, index: number) => { + if (colPath.indicatorKey) { + indicatorPosition = { + position: 'col', + index + }; + } + return colPath.indicatorKey ?? colPath.value; + }); + const rowKeys = cellDimensionPath.rowHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((rowPath: any, index: number) => { + if (rowPath.indicatorKey) { + indicatorPosition = { + position: 'row', + index + }; + } + return rowPath.indicatorKey ?? rowPath.value; + }); const aggregator = this.dataset.getAggregator( // !this.internalProps.layoutMap.indicatorsAsCol ? rowKeys.slice(0, -1) : rowKeys, // this.internalProps.layoutMap.indicatorsAsCol ? colKeys.slice(0, -1) : colKeys, @@ -922,24 +954,32 @@ export class PivotTable extends BaseTable implements PivotTableAPI { } else if (this.dataset) { let indicatorPosition: { position: 'col' | 'row'; index?: number }; const cellDimensionPath = this.internalProps.layoutMap.getCellHeaderPaths(col, row); - const colKeys = cellDimensionPath.colHeaderPaths.map((colPath: any, index: number) => { - if (colPath.indicatorKey) { - indicatorPosition = { - position: 'col', - index - }; - } - return colPath.indicatorKey ?? colPath.value; - }); - const rowKeys = cellDimensionPath.rowHeaderPaths.map((rowPath: any, index: number) => { - if (rowPath.indicatorKey) { - indicatorPosition = { - position: 'row', - index - }; - } - return rowPath.indicatorKey ?? rowPath.value; - }); + const colKeys = cellDimensionPath.colHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((colPath: any, index: number) => { + if (colPath.indicatorKey) { + indicatorPosition = { + position: 'col', + index + }; + } + return colPath.indicatorKey ?? colPath.value; + }); + const rowKeys = cellDimensionPath.rowHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((rowPath: any, index: number) => { + if (rowPath.indicatorKey) { + indicatorPosition = { + position: 'row', + index + }; + } + return rowPath.indicatorKey ?? rowPath.value; + }); const aggregator = this.dataset.getAggregator( // !this.internalProps.layoutMap.indicatorsAsCol ? rowKeys.slice(0, -1) : rowKeys, // this.internalProps.layoutMap.indicatorsAsCol ? colKeys.slice(0, -1) : colKeys, @@ -1777,12 +1817,20 @@ export class PivotTable extends BaseTable implements PivotTableAPI { newValue ); } else { - const colKeys = cellDimensionPath.colHeaderPaths.map((colPath: any) => { - return colPath.indicatorKey ?? colPath.value; - }); - const rowKeys = cellDimensionPath.rowHeaderPaths.map((rowPath: any) => { - return rowPath.indicatorKey ?? rowPath.value; - }); + const colKeys = cellDimensionPath.colHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((colPath: any) => { + return colPath.indicatorKey ?? colPath.value; + }); + const rowKeys = cellDimensionPath.rowHeaderPaths + ?.filter((path: any) => { + return !path.virtual; + }) + .map((rowPath: any) => { + return rowPath.indicatorKey ?? rowPath.value; + }); this.dataset.changeTreeNodeValue( !this.internalProps.layoutMap.indicatorsAsCol ? rowKeys.slice(0, -1) : rowKeys, this.internalProps.layoutMap.indicatorsAsCol ? colKeys.slice(0, -1) : colKeys, From abd6d4210f555d7beffd9232e2e234b3c512c007 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Sat, 29 Jun 2024 21:37:53 +0800 Subject: [PATCH 28/37] docs: update changlog of rush --- .../2002-bug-virtual-edit-value_2024-06-29-13-37.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json diff --git a/common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json b/common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json new file mode 100644 index 000000000..61729aa7f --- /dev/null +++ b/common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: pivotTable virtual node edit value not work #2002\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From ba3f6c4358a3157756fadabf24e42a4476dbb48c Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 1 Jul 2024 10:24:47 +0800 Subject: [PATCH 29/37] fix: tooltip content can not be selected #2003 --- .../components/tooltip/logic/BubbleTooltipElement.ts | 8 +++++++- packages/vtable/src/tools/util.ts | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts b/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts index bcaeff0c7..407eb7b1f 100644 --- a/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts +++ b/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts @@ -3,7 +3,7 @@ import type { RectProps } from '../../../ts-types'; import { Placement } from '../../../ts-types'; import { createElement } from '../../../tools/dom'; import { importStyle } from './BubbleTooltipElementStyle'; -import { isMobile } from '../../../tools/util'; +import { isDivSelected, isMobile } from '../../../tools/util'; import type { TooltipOptions } from '../../../ts-types/tooltip'; import type { BaseTableAPI } from '../../../ts-types/base-table'; importStyle(); @@ -41,6 +41,12 @@ export class BubbleTooltipElement { messageElement.addEventListener('wheel', e => { e.stopPropagation(); }); + messageElement.addEventListener('copy', e => { + const isSelected = isDivSelected(messageElement as HTMLDivElement); // 判断tooltip弹框内容是否有选中 + if (isSelected) { + e.stopPropagation(); + } + }); } bindToCell( table: BaseTableAPI, diff --git a/packages/vtable/src/tools/util.ts b/packages/vtable/src/tools/util.ts index 3e639a4d9..6a7f3f46e 100644 --- a/packages/vtable/src/tools/util.ts +++ b/packages/vtable/src/tools/util.ts @@ -422,3 +422,13 @@ export function deduplication(array: number[]) { } return result; } + +/** 判断div中的文本是否有被选中 */ +export function isDivSelected(div: HTMLDivElement) { + const selection = window.getSelection(); + if (selection.rangeCount) { + const range = selection.getRangeAt(0); + return range.endOffset > range.startOffset && div.contains(range.commonAncestorContainer); + } + return false; +} From 73759f587541fe309cc65703469b622b283e4d5e Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 1 Jul 2024 10:25:26 +0800 Subject: [PATCH 30/37] docs: update changlog of rush --- ...copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json diff --git a/common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json b/common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json new file mode 100644 index 000000000..088affc51 --- /dev/null +++ b/common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: tooltip content can not be selected #2003\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file From fc5eccb59eee2ef6dce4ac1514037792de7b7add Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Mon, 1 Jul 2024 11:22:31 +0800 Subject: [PATCH 31/37] fix: fix regexp format for webpack 3 #2005 --- .../@visactor/vtable/fix-reg-s_2024-07-01-03-22.json | 10 ++++++++++ packages/vtable/src/event/listener/container-dom.ts | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json diff --git a/common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json b/common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json new file mode 100644 index 000000000..b8d6fe3fe --- /dev/null +++ b/common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix regexp format for webpack 3 #2005", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/event/listener/container-dom.ts b/packages/vtable/src/event/listener/container-dom.ts index a89cb3623..0d7547f7c 100644 --- a/packages/vtable/src/event/listener/container-dom.ts +++ b/packages/vtable/src/event/listener/container-dom.ts @@ -317,6 +317,12 @@ export function bindContainerDomListener(eventManager: EventManager) { table.resize(); } }); + + // const regex = /]*>(.*?)<\/tr>/gs; // 匹配标签及其内容 + const regex = /]*>([\s\S]*?)<\/tr>/g; // for webpack3 + // const cellRegex = /]*>(.*?)<\/td>/gs; // 匹配标签及其内容 + const cellRegex = /]*>([\s\S]*?)<\/td>/g; // for webpack3 + function pasteHtmlToTable(item: ClipboardItem) { const ranges = table.stateManager.select.ranges; const selectRangeLength = ranges.length; @@ -331,12 +337,10 @@ export function bindContainerDomListener(eventManager: EventManager) { blob.text().then((pastedData: any) => { // 解析html数据 if (pastedData && /(]*>(.*?)<\/tr>/gs; // 匹配标签及其内容 // const matches = pastedData.matchAll(regex); const matches = Array.from(pastedData.matchAll(regex)); for (const match of matches) { const rowContent = match[1]; // 获取标签中的内容 - const cellRegex = /]*>(.*?)<\/td>/gs; // 匹配标签及其内容 const cellMatches: RegExpMatchArray[] = Array.from(rowContent.matchAll(cellRegex)); // 获取标签中的内容 const rowValues = cellMatches.map(cellMatch => { return ( From 539c43d090aee908e650045c7281d3151ecc10ae Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 1 Jul 2024 17:48:56 +0800 Subject: [PATCH 32/37] fix: isCompleteEdit judge --- packages/vtable/src/event/listener/table-group.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index d37c6fe68..d8da91bd2 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -690,7 +690,7 @@ export function bindTableGroupListener(eventManager: EventManager) { stateManager.hideMenu(); } const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); - if (isCompleteEdit) { + if (!isCompleteEdit) { // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 return; } From 7410014c8cda5beac5b8b89b685945b54e15e7be Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 1 Jul 2024 20:28:23 +0800 Subject: [PATCH 33/37] fix: isCompleteEdit judge --- packages/vtable/src/event/listener/table-group.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index d8da91bd2..a84d1c3ef 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -342,7 +342,7 @@ export function bindTableGroupListener(eventManager: EventManager) { } } const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); - if (!isCompleteEdit) { + if (isCompleteEdit === false) { // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 return; } @@ -393,7 +393,7 @@ export function bindTableGroupListener(eventManager: EventManager) { stateManager.hideMenu(); } const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); - if (!isCompleteEdit) { + if (isCompleteEdit === false) { // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 return; } @@ -690,7 +690,7 @@ export function bindTableGroupListener(eventManager: EventManager) { stateManager.hideMenu(); } const isCompleteEdit = (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); - if (!isCompleteEdit) { + if (isCompleteEdit === false) { // 如果没有正常退出编辑状态 则不执行下面的逻辑 如选择其他单元格的逻辑 return; } From 9c11ae70e27e4e994dbb03479566acb8bb69bd1b Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 2 Jul 2024 11:47:04 +0800 Subject: [PATCH 34/37] fix: fix width computation in shrinkSparklineFirst mode --- .../vtable/fix-shrink-sparkline_2024-07-02-03-47.json | 10 ++++++++++ .../vtable/src/scenegraph/layout/compute-col-width.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json diff --git a/common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json b/common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json new file mode 100644 index 000000000..b489d3c08 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix width computation in shrinkSparklineFirst mode", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/packages/vtable/src/scenegraph/layout/compute-col-width.ts b/packages/vtable/src/scenegraph/layout/compute-col-width.ts index a384b98ab..2b828463c 100644 --- a/packages/vtable/src/scenegraph/layout/compute-col-width.ts +++ b/packages/vtable/src/scenegraph/layout/compute-col-width.ts @@ -720,7 +720,7 @@ export function getAdaptiveWidth( if ( table.options.customConfig?.shrinkSparklineFirst && factor < 1 && - totalDrawWidth - actualWidth < totalSparklineAbleWidth + actualWidth - totalDrawWidth < totalSparklineAbleWidth ) { // only shrink sparkline column for (let i = 0; i < sparklineColumns.length; i++) { From 5bc12a657a53224051627f1828bbc3aa31cc26b7 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 2 Jul 2024 14:37:06 +0800 Subject: [PATCH 35/37] feat: add discription of getCellAtRelativePosition --- packages/vtable/src/core/BaseTable.ts | 17 ++++----- .../src/core/utils/get-cell-position.ts | 35 ++++++++++++------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 908ddb71f..08e7ce607 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -1800,6 +1800,13 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { return getCellAt(absoluteX, absoluteY, this); } + /** + * 获取屏幕坐标对应的单元格信息,考虑滚动 + * @param this + * @param relativeX 左边x值,相对于容器左上角,考虑表格滚动 + * @param relativeY 左边y值,相对于容器左上角,考虑表格滚动 + * @returns + */ getCellAtRelativePosition(relativeX: number, relativeY: number): CellAddressWithBound { return getCellAtRelativePosition(relativeX, relativeY, this); } @@ -2482,10 +2489,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ - getTargetColAtConsiderRightFrozen( - absoluteX: number, - isConsider: boolean - ): ColumnInfo | null { + getTargetColAtConsiderRightFrozen(absoluteX: number, isConsider: boolean): ColumnInfo | null { return getTargetColAtConsiderRightFrozen(absoluteX, isConsider, this); } @@ -2496,10 +2500,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @returns */ - getTargetRowAtConsiderBottomFrozen( - absoluteY: number, - isConsider: boolean - ): RowInfo | null { + getTargetRowAtConsiderBottomFrozen(absoluteY: number, isConsider: boolean): RowInfo | null { return getTargetRowAtConsiderBottomFrozen(absoluteY, isConsider, this); } diff --git a/packages/vtable/src/core/utils/get-cell-position.ts b/packages/vtable/src/core/utils/get-cell-position.ts index b7565c47d..c40b720ea 100644 --- a/packages/vtable/src/core/utils/get-cell-position.ts +++ b/packages/vtable/src/core/utils/get-cell-position.ts @@ -4,14 +4,14 @@ import { _getTargetFrozenColAt, _getTargetFrozenRowAt } from '../tableHelper'; /** * 根据y值计算所在行 - * @param absoluteY + * @param absoluteY 相对于表格左上角的y坐标(无滚动) * @returns */ export function getRowAt( absoluteY: number, _this: BaseTableAPI ): { top: number; row: number; bottom: number; height: number } { - const frozen = _getTargetFrozenRowAt(_this, absoluteY); + const frozen = _getTargetFrozenRowAt(_this as any, absoluteY); if (frozen) { return frozen; } @@ -26,16 +26,17 @@ export function getRowAt( } return row; } + /** * 根据x值计算所在列 - * @param absoluteX + * @param absoluteX 相对于表格左上角的x坐标(无滚动) * @returns */ export function getColAt( absoluteX: number, _this: BaseTableAPI ): { left: number; col: number; right: number; width: number } { - const frozen = _getTargetFrozenColAt(_this, absoluteX); + const frozen = _getTargetFrozenColAt(_this as any, absoluteX); if (frozen) { return frozen; } @@ -52,8 +53,8 @@ export function getColAt( } /** * 根据坐标值获取行列位置,index和rect范围 - * @param absoluteX - * @param absoluteY + * @param absoluteX 表格左上角的x坐标(无滚动) + * @param absoluteY 表格左上角的y坐标(无滚动) * @returns */ export function getCellAt(absoluteX: number, absoluteY: number, _this: BaseTableAPI): CellAddressWithBound { @@ -79,7 +80,7 @@ export function getCellAt(absoluteX: number, absoluteY: number, _this: BaseTable /** * 根据x获取该位置所处列值 * @param table - * @param absoluteX + * @param absoluteX 表格左上角的x坐标(无滚动) * @returns */ export function getTargetColAt( @@ -148,10 +149,11 @@ export function getTargetColAt( } return findBefore(candCol, right); } + /** * 根据y获取该位置所处行值 * @param table - * @param absoluteX + * @param absoluteX 表格左上角的y坐标(无滚动) * @returns */ export function getTargetRowAt( @@ -227,9 +229,9 @@ export function getTargetRowAt( } /** - * 根据x获取该位置所处列值 + * 根据x获取右侧冻结中该位置所处列值 * @param table - * @param absoluteX + * @param absoluteX 屏幕坐标x值 * @returns */ export function getTargetColAtConsiderRightFrozen( @@ -260,9 +262,9 @@ export function getTargetColAtConsiderRightFrozen( } /** - * 根据y获取该位置所处行值 + * 根据y获取底部冻结该位置所处行值 * @param table - * @param absoluteX + * @param absoluteX 屏幕坐标y值 * @returns */ export function getTargetRowAtConsiderBottomFrozen( @@ -321,6 +323,7 @@ export function computeTargetRowByY(absoluteY: number, _this: BaseTableAPI): num //否则使用defaultRowHeight大约计算一个row return Math.min(Math.ceil(absoluteY / defaultRowHeight), _this.rowCount - 1); } + /** * 根据x值(包括了scroll的)计算所在列 主要借助colRangeWidthsMap缓存来提高计算效率 * @param this @@ -348,7 +351,13 @@ export function computeTargetColByX(absoluteX: number, _this: BaseTableAPI): num return Math.min(Math.ceil(absoluteX / _this.internalProps.defaultColWidth), _this.colCount - 1); } -// 获取屏幕坐标对应的单元格信息,考虑滚动 +/** + * 获取屏幕坐标对应的单元格信息,考虑滚动 + * @param this + * @param relativeX 左边x值,相对于容器左上角,考虑表格滚动 + * @param relativeY 左边y值,相对于容器左上角,考虑表格滚动 + * @returns + */ export function getCellAtRelativePosition(x: number, y: number, _this: BaseTableAPI): CellAddressWithBound { // table border and outer component x -= _this.tableX; From 2dd940cc7e9425e2a82a92bc2ebdccbe37639a08 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Tue, 2 Jul 2024 19:42:10 +0800 Subject: [PATCH 36/37] fix: fix stroke split render problem --- .../group-contribution-render.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts index 946cbe0a5..33699feb9 100644 --- a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts +++ b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts @@ -269,8 +269,8 @@ export function renderStroke( // if (isHighlight) { // context.setLineDash(highlightDash); // } - const oldLineCap = context.lineCap; - context.lineCap = 'square'; + // const oldLineCap = context.lineCap; + // context.lineCap = 'square'; const { lineDash = groupAttribute.lineDash } = group.attribute as any; // const lineDash = context.getLineDash(); @@ -290,8 +290,8 @@ export function renderStroke( // top if (strokeTop) { // context.lineTo(x + width, y); - const deltaLeft = strokeLeft ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[3]) / 2; - const deltaRight = strokeRight ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[1]) / 2; + const deltaLeft = (isWidthNumber ? widthInfo.width : strokeArrayWidth[0]) / 2; + const deltaRight = (isWidthNumber ? widthInfo.width : strokeArrayWidth[0]) / 2; if (isPart && Array.isArray(part[0])) { context.moveTo(x - deltaLeft + (width + deltaLeft + deltaRight) * part[0][0], y); context.lineTo(x - deltaLeft + (width + deltaLeft + deltaRight) * (part[0][1] - part[0][0]), y); @@ -323,8 +323,8 @@ export function renderStroke( // right if (strokeRight) { // context.lineTo(x + width, y + height); - const deltaTop = strokeTop ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[0]) / 2; - const deltaBottom = strokeBottom ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[2]) / 2; + const deltaTop = (isWidthNumber ? widthInfo.width : strokeArrayWidth[1]) / 2; + const deltaBottom = (isWidthNumber ? widthInfo.width : strokeArrayWidth[1]) / 2; if (isPart && Array.isArray(part[1])) { context.moveTo(x + width, y - deltaTop + height * part[1][0]); context.lineTo(x + width, y - deltaTop + (height + deltaTop + deltaBottom) * (part[1][1] - part[1][0])); @@ -356,8 +356,8 @@ export function renderStroke( // bottom if (strokeBottom) { // context.lineTo(x, y + height); - const deltaLeft = strokeLeft ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[3]) / 2; - const deltaRight = strokeRight ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[1]) / 2; + const deltaLeft = (isWidthNumber ? widthInfo.width : strokeArrayWidth[2]) / 2; + const deltaRight = (isWidthNumber ? widthInfo.width : strokeArrayWidth[2]) / 2; if (isPart && Array.isArray(part[2])) { context.moveTo(x - deltaLeft + (width + deltaLeft + deltaRight) * part[2][0], y + height); context.lineTo(x - deltaLeft + (width + deltaLeft + deltaRight) * (part[2][1] - part[2][0]), y + height); @@ -389,8 +389,8 @@ export function renderStroke( // left if (strokeLeft) { // context.lineTo(x, y); - const deltaTop = strokeTop ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[0]) / 2; - const deltaBottom = strokeBottom ? 0 : (isWidthNumber ? widthInfo.width : strokeArrayWidth[2]) / 2; + const deltaTop = (isWidthNumber ? widthInfo.width : strokeArrayWidth[3]) / 2; + const deltaBottom = (isWidthNumber ? widthInfo.width : strokeArrayWidth[3]) / 2; if (isPart && Array.isArray(part[3])) { context.moveTo(x, y - deltaTop + (height + deltaTop + deltaBottom) * part[3][0]); context.lineTo(x, y - deltaTop + (height + deltaTop + deltaBottom) * (part[3][1] - part[3][0])); @@ -428,7 +428,7 @@ export function renderStroke( context.stroke(); } context.lineDashOffset = 0; - context.lineCap = oldLineCap; + // context.lineCap = oldLineCap; context.setLineDash([]); } From 99e4c8aabcdb729ce87f077cb470f7fe4b289984 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 2 Jul 2024 12:50:45 +0000 Subject: [PATCH 37/37] build: prelease version 1.4.2 --- ...titleondimension-all_2024-06-25-09-12.json | 11 ----- ...false-notSelectOther_2024-06-27-08-10.json | 11 ----- ...selected_clear-event_2024-06-25-06-47.json | 11 ----- ...-column-support-hide_2024-06-26-09-46.json | 11 ----- ...aggregationtype-none_2024-06-27-09-41.json | 11 ----- ...g-virtual-edit-value_2024-06-29-13-37.json | 11 ----- ...-tooltipCannotBeCopy_2024-07-01-02-25.json | 11 ----- ...llAtRelativePosition_2024-06-14-09-37.json | 10 ----- .../fix-export-name_2024-06-25-09-23.json | 10 ----- .../fix-merge-cell-pref_2024-06-25-03-42.json | 10 ----- .../vtable/fix-reg-s_2024-07-01-03-22.json | 10 ----- ...fix-shrink-sparkline_2024-07-02-03-47.json | 10 ----- common/config/rush/version-policies.json | 2 +- packages/openinula-vtable/package.json | 2 +- packages/react-vtable/package.json | 2 +- packages/vtable-editors/package.json | 2 +- packages/vtable-export/package.json | 2 +- packages/vtable-search/package.json | 2 +- packages/vtable/CHANGELOG.json | 45 +++++++++++++++++++ packages/vtable/CHANGELOG.md | 34 +++++++++++++- packages/vtable/package.json | 2 +- 21 files changed, 85 insertions(+), 135 deletions(-) delete mode 100644 common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json delete mode 100644 common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json delete mode 100644 common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json delete mode 100644 common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json delete mode 100644 common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json delete mode 100644 common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json delete mode 100644 common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json delete mode 100644 common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json delete mode 100644 common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json delete mode 100644 common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json delete mode 100644 common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json delete mode 100644 common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json diff --git a/common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json b/common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json deleted file mode 100644 index 546a35dc5..000000000 --- a/common/changes/@visactor/vtable/1926-feature-corner-titleondimension-all_2024-06-25-09-12.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "feat: corner title can display row and column diemensionTitle #1926\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json b/common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json deleted file mode 100644 index 0072d1de7..000000000 --- a/common/changes/@visactor/vtable/1974-bug-validatevalue-false-notSelectOther_2024-06-27-08-10.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "fix: when not exit edit state then can not select other cells #1974\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json b/common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json deleted file mode 100644 index f8ea496dc..000000000 --- a/common/changes/@visactor/vtable/1981-bug-selected_clear-event_2024-06-25-06-47.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "fix: selected_clear event trigger #1981\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json b/common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json deleted file mode 100644 index ca1bba1ab..000000000 --- a/common/changes/@visactor/vtable/1991-feature-listtable-column-support-hide_2024-06-26-09-46.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "feat: add column hide config #1991\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json b/common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json deleted file mode 100644 index d6a4f9f5b..000000000 --- a/common/changes/@visactor/vtable/1999-refactor-sparkline-celltype-aggregationtype-none_2024-06-27-09-41.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "refactor: sparkline cellType set aggregationType None automatically #1999\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json b/common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json deleted file mode 100644 index 61729aa7f..000000000 --- a/common/changes/@visactor/vtable/2002-bug-virtual-edit-value_2024-06-29-13-37.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "fix: pivotTable virtual node edit value not work #2002\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json b/common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json deleted file mode 100644 index 088affc51..000000000 --- a/common/changes/@visactor/vtable/2003-bug-enable-copyCell-tooltipCannotBeCopy_2024-07-01-02-25.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "fix: tooltip content can not be selected #2003\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json b/common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json deleted file mode 100644 index 610dfce7b..000000000 --- a/common/changes/@visactor/vtable/feat-getCellAtRelativePosition_2024-06-14-09-37.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "feat: add getCellAtRelativePosition api", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json b/common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json deleted file mode 100644 index 0350b9a25..000000000 --- a/common/changes/@visactor/vtable/fix-export-name_2024-06-25-09-23.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix vrender export module", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json b/common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json deleted file mode 100644 index 9074db2c6..000000000 --- a/common/changes/@visactor/vtable/fix-merge-cell-pref_2024-06-25-03-42.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix merge cell update performance problem #1972", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json b/common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json deleted file mode 100644 index b8d6fe3fe..000000000 --- a/common/changes/@visactor/vtable/fix-reg-s_2024-07-01-03-22.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix regexp format for webpack 3 #2005", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json b/common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json deleted file mode 100644 index b489d3c08..000000000 --- a/common/changes/@visactor/vtable/fix-shrink-sparkline_2024-07-02-03-47.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@visactor/vtable", - "comment": "fix: fix width computation in shrinkSparklineFirst mode", - "type": "none" - } - ], - "packageName": "@visactor/vtable" -} \ No newline at end of file diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 23b368d9d..d59a4eeae 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.4.1","mainProject":"@visactor/vtable","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.4.2","mainProject":"@visactor/vtable","nextBump":"patch"}] diff --git a/packages/openinula-vtable/package.json b/packages/openinula-vtable/package.json index 476032a2c..f5df8e134 100644 --- a/packages/openinula-vtable/package.json +++ b/packages/openinula-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/openinula-vtable", - "version": "1.4.1", + "version": "1.4.2", "description": "The openinula version of VTable", "keywords": [ "openinula", diff --git a/packages/react-vtable/package.json b/packages/react-vtable/package.json index 693e7f560..bb5fd4634 100644 --- a/packages/react-vtable/package.json +++ b/packages/react-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vtable", - "version": "1.4.1", + "version": "1.4.2", "description": "The react version of VTable", "keywords": [ "react", diff --git a/packages/vtable-editors/package.json b/packages/vtable-editors/package.json index a8b34ba1e..0fbedcc65 100644 --- a/packages/vtable-editors/package.json +++ b/packages/vtable-editors/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-editors", - "version": "1.4.1", + "version": "1.4.2", "description": "", "sideEffects": false, "main": "cjs/index.js", diff --git a/packages/vtable-export/package.json b/packages/vtable-export/package.json index 7b7aab550..6ce572554 100644 --- a/packages/vtable-export/package.json +++ b/packages/vtable-export/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-export", - "version": "1.4.1", + "version": "1.4.2", "description": "The export util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-search/package.json b/packages/vtable-search/package.json index 50b2b6f54..72f4d188b 100644 --- a/packages/vtable-search/package.json +++ b/packages/vtable-search/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-search", - "version": "1.4.1", + "version": "1.4.2", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable/CHANGELOG.json b/packages/vtable/CHANGELOG.json index 3a18da56d..f024bba78 100644 --- a/packages/vtable/CHANGELOG.json +++ b/packages/vtable/CHANGELOG.json @@ -1,6 +1,51 @@ { "name": "@visactor/vtable", "entries": [ + { + "version": "1.4.2", + "tag": "@visactor/vtable_v1.4.2", + "date": "Tue, 02 Jul 2024 12:48:08 GMT", + "comments": { + "none": [ + { + "comment": "feat: corner title can display row and column diemensionTitle #1926\n\n" + }, + { + "comment": "fix: when not exit edit state then can not select other cells #1974\n\n" + }, + { + "comment": "fix: selected_clear event trigger #1981\n\n" + }, + { + "comment": "feat: add column hide config #1991\n\n" + }, + { + "comment": "refactor: sparkline cellType set aggregationType None automatically #1999\n\n" + }, + { + "comment": "fix: pivotTable virtual node edit value not work #2002\n\n" + }, + { + "comment": "fix: tooltip content can not be selected #2003\n\n" + }, + { + "comment": "feat: add getCellAtRelativePosition api" + }, + { + "comment": "fix: fix vrender export module" + }, + { + "comment": "fix: fix merge cell update performance problem #1972" + }, + { + "comment": "fix: fix regexp format for webpack 3 #2005" + }, + { + "comment": "fix: fix width computation in shrinkSparklineFirst mode" + } + ] + } + }, { "version": "1.4.1", "tag": "@visactor/vtable_v1.4.1", diff --git a/packages/vtable/CHANGELOG.md b/packages/vtable/CHANGELOG.md index 99e666d6c..aeab6d496 100644 --- a/packages/vtable/CHANGELOG.md +++ b/packages/vtable/CHANGELOG.md @@ -1,6 +1,38 @@ # Change Log - @visactor/vtable -This log was last generated on Mon, 24 Jun 2024 08:48:40 GMT and should not be manually modified. +This log was last generated on Tue, 02 Jul 2024 12:48:08 GMT and should not be manually modified. + +## 1.4.2 +Tue, 02 Jul 2024 12:48:08 GMT + +### Updates + +- feat: corner title can display row and column diemensionTitle #1926 + + +- fix: when not exit edit state then can not select other cells #1974 + + +- fix: selected_clear event trigger #1981 + + +- feat: add column hide config #1991 + + +- refactor: sparkline cellType set aggregationType None automatically #1999 + + +- fix: pivotTable virtual node edit value not work #2002 + + +- fix: tooltip content can not be selected #2003 + + +- feat: add getCellAtRelativePosition api +- fix: fix vrender export module +- fix: fix merge cell update performance problem #1972 +- fix: fix regexp format for webpack 3 #2005 +- fix: fix width computation in shrinkSparklineFirst mode ## 1.4.1 Mon, 24 Jun 2024 08:48:40 GMT diff --git a/packages/vtable/package.json b/packages/vtable/package.json index 5bd871b5c..0b5984752 100644 --- a/packages/vtable/package.json +++ b/packages/vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable", - "version": "1.4.1", + "version": "1.4.2", "description": "canvas table width high performance", "keywords": [ "grid",