From c197a408fa27eaf6f1ff36d11022e38ea1ce8a23 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Mon, 3 Jun 2024 19:44:35 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(components):=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=B1=82=E6=9B=B4=E6=96=B0=E6=97=B6=E5=A2=9E=E5=8A=A0=20loadin?= =?UTF-8?q?g=20=E6=95=88=E6=9E=9C=20close=20#1790?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-core/src/common/interface/s2Options.ts | 2 +- packages/s2-core/src/facet/header/corner.ts | 2 +- packages/s2-core/src/facet/table-facet.ts | 5 +- .../click/row-column-click.ts | 4 +- .../src/interaction/row-column-resize.ts | 8 +- .../s2-core/src/sheet-type/pivot-sheet.ts | 18 ++-- .../s2-core/src/sheet-type/spread-sheet.ts | 21 +++-- .../s2-core/src/sheet-type/table-sheet.ts | 7 +- packages/s2-core/src/ui/hd-adapter/index.ts | 18 ++-- .../custom-cell/edit-cell/index.tsx | 4 +- .../drag-copy/drag-copy-mask.tsx | 41 ++++----- .../src/components/switcher/header.tsx | 4 +- packages/s2-react/src/hooks/usePagination.ts | 20 +++-- .../s2-react/src/hooks/usePivotSheetUpdate.ts | 2 +- packages/s2-react/src/hooks/useSpreadSheet.ts | 86 +++++++++++-------- packages/s2-shared/src/utils/drill-down.ts | 4 +- packages/s2-shared/src/utils/resize.ts | 8 +- packages/s2-vue/src/hooks/usePagination.ts | 4 +- packages/s2-vue/src/hooks/useSheetUpdate.ts | 11 ++- packages/s2-vue/src/hooks/useSpreadSheet.ts | 15 +++- .../docs/api/basic-class/spreadsheet.zh.md | 4 +- s2-site/docs/manual/migration-v2.zh.md | 18 +++- s2-site/examples/case/art/demo/lost-text.tsx | 18 ++-- 23 files changed, 190 insertions(+), 134 deletions(-) diff --git a/packages/s2-core/src/common/interface/s2Options.ts b/packages/s2-core/src/common/interface/s2Options.ts index 32d1ac6dc7..ee880b7881 100644 --- a/packages/s2-core/src/common/interface/s2Options.ts +++ b/packages/s2-core/src/common/interface/s2Options.ts @@ -374,7 +374,7 @@ export interface S2RenderOptions { /** * 是否重新生成数据集 */ - reBuildDataSet?: boolean; + rebuildDataSet?: boolean; /** * 是否重新生成列头隐藏信息 diff --git a/packages/s2-core/src/facet/header/corner.ts b/packages/s2-core/src/facet/header/corner.ts index 21b02d6862..386e0e6baf 100644 --- a/packages/s2-core/src/facet/header/corner.ts +++ b/packages/s2-core/src/facet/header/corner.ts @@ -231,7 +231,7 @@ export class CornerHeader extends BaseHeader { } /** - * Make cornerHeader scroll with hScrollBar + * Make cornerHeader scroll with hScrollBar * @param scrollX */ public onCorScroll(scrollX: number, type: string): void { diff --git a/packages/s2-core/src/facet/table-facet.ts b/packages/s2-core/src/facet/table-facet.ts index f286c2a0b4..ef37dbe1ac 100644 --- a/packages/s2-core/src/facet/table-facet.ts +++ b/packages/s2-core/src/facet/table-facet.ts @@ -235,7 +235,7 @@ export class TableFacet extends FrozenFacet { } } - private onSortHandler = (sortParams: SortParams) => { + private onSortHandler = async (sortParams: SortParams) => { const s2 = this.spreadsheet; let params = sortParams; @@ -274,7 +274,8 @@ export class TableFacet extends FrozenFacet { set(s2.dataCfg, 'sortParams', [...oldConfigs, ...params]); s2.setDataCfg(s2.dataCfg); - s2.render(true); + await s2.render(true); + s2.emit( S2Event.RANGE_SORTED, (s2.dataSet as TableDataSet).getDisplayDataSet(), diff --git a/packages/s2-core/src/interaction/base-interaction/click/row-column-click.ts b/packages/s2-core/src/interaction/base-interaction/click/row-column-click.ts index 2b183a81bc..99d004e5b8 100644 --- a/packages/s2-core/src/interaction/base-interaction/click/row-column-click.ts +++ b/packages/s2-core/src/interaction/base-interaction/click/row-column-click.ts @@ -214,7 +214,7 @@ export class RowColumnClick extends BaseEvent implements BaseEventImplement { await hideColumnsByThunkGroup(this.spreadsheet, selectedColumnFields, true); } - private handleExpandIconClick(node: Node) { + private async handleExpandIconClick(node: Node) { const lastHiddenColumnsDetail = this.spreadsheet.store.get( 'hiddenColumnsDetail', [], @@ -249,6 +249,6 @@ export class RowColumnClick extends BaseEvent implements BaseEventImplement { }); this.spreadsheet.store.set('hiddenColumnsDetail', hiddenColumnsDetail); this.spreadsheet.interaction.reset(); - this.spreadsheet.render(false); + await this.spreadsheet.render(false); } } diff --git a/packages/s2-core/src/interaction/row-column-resize.ts b/packages/s2-core/src/interaction/row-column-resize.ts index a9f2507699..880ec4efb9 100644 --- a/packages/s2-core/src/interaction/row-column-resize.ts +++ b/packages/s2-core/src/interaction/row-column-resize.ts @@ -578,7 +578,7 @@ export class RowColumnResize extends BaseEvent implements BaseEventImplement { }); } - private renderResizedResult() { + private async renderResizedResult() { const resizeInfo = this.getResizeInfo(); const { style, @@ -607,7 +607,7 @@ export class RowColumnResize extends BaseEvent implements BaseEventImplement { } this.spreadsheet.store.set('resized', true); - this.render(); + await this.render(); } private getResizeInfo(): ResizeInfo { @@ -638,10 +638,10 @@ export class RowColumnResize extends BaseEvent implements BaseEventImplement { }; } - private render() { + private async render() { this.resizeStartPosition = {}; this.resizeTarget = null; this.resizeReferenceGroup = null; - this.spreadsheet.render(false); + await this.spreadsheet.render(false); } } diff --git a/packages/s2-core/src/sheet-type/pivot-sheet.ts b/packages/s2-core/src/sheet-type/pivot-sheet.ts index 024766bf80..a9ffcd66ef 100644 --- a/packages/s2-core/src/sheet-type/pivot-sheet.ts +++ b/packages/s2-core/src/sheet-type/pivot-sheet.ts @@ -66,14 +66,14 @@ export class PivotSheet extends SpreadSheet { return this.dataSet?.fields?.valueInCols!; } - public clearDrillDownData(rowNodeId?: string, preventRender?: boolean) { + public async clearDrillDownData(rowNodeId?: string, preventRender?: boolean) { if (this.dataSet instanceof PivotDataSet) { const cleaned = this.dataSet.clearDrillDownData(rowNodeId); if (cleaned && !preventRender) { // 重置当前交互 this.interaction.reset(); - this.render(false); + await this.render(false); } } } @@ -97,7 +97,7 @@ export class PivotSheet extends SpreadSheet { ); } - protected handleRowCellCollapsed(data: RowCellCollapsedParams) { + protected async handleRowCellCollapsed(data: RowCellCollapsedParams) { const { isCollapsed, node } = data; const { collapseFields: defaultCollapsedFields } = this.options.style?.rowCell!; @@ -114,7 +114,8 @@ export class PivotSheet extends SpreadSheet { }, }, }); - this.render(false); + + await this.render(false); this.emit(S2Event.ROW_CELL_COLLAPSED, { isCollapsed, collapseFields, @@ -122,7 +123,7 @@ export class PivotSheet extends SpreadSheet { }); } - protected handleRowCellToggleCollapseAll(isCollapsed: boolean) { + protected async handleRowCellToggleCollapseAll(isCollapsed: boolean) { const collapseAll = !isCollapsed; this.setOptions({ @@ -134,11 +135,12 @@ export class PivotSheet extends SpreadSheet { }, }, }); - this.render(false); + + await this.render(false); this.emit(S2Event.ROW_CELL_ALL_COLLAPSED, collapseAll); } - public groupSortByMethod(sortMethod: SortMethod, meta: Node) { + public async groupSortByMethod(sortMethod: SortMethod, meta: Node) { const { rows, columns } = this.dataCfg.fields; const { hideValue } = this.options.style!.colCell!; const sortField = this.isValueInCols() ? last(rows) : last(columns); @@ -173,6 +175,6 @@ export class PivotSheet extends SpreadSheet { ...this.dataCfg, sortParams, }); - this.render(); + await this.render(); } } diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index 8e0ec242db..022bcd09ab 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -121,9 +121,12 @@ export abstract class SpreadSheet extends EE { public abstract clearDrillDownData( rowNodeId?: string, preventRender?: boolean, - ): void; + ): Promise; - public abstract groupSortByMethod(sortMethod: SortMethod, meta: Node): void; + public abstract groupSortByMethod( + sortMethod: SortMethod, + meta: Node, + ): Promise | void; public constructor( dom: S2MountContainer, @@ -240,9 +243,9 @@ export abstract class SpreadSheet extends EE { return this.options.tooltip?.render?.(this) || new BaseTooltip(this); } - private getTargetCell(target: CellEventTarget) { + private getTargetCell(target: CellEventTarget): S2CellType { // 刷选等场景, 以最后一个发生交互的单元格为准 - return this.getCell(target) || last(this.interaction.getInteractedCells()); + return this.getCell(target) || last(this.interaction.getInteractedCells())!; } /** @@ -419,13 +422,13 @@ export abstract class SpreadSheet extends EE { const { reloadData = true, - reBuildDataSet = false, + rebuildDataSet = false, reBuildHiddenColumnsDetail = true, } = options || {}; this.emit(S2Event.LAYOUT_BEFORE_RENDER); - if (reBuildDataSet) { + if (rebuildDataSet) { this.dataSet = this.getDataSet(); } @@ -452,7 +455,7 @@ export abstract class SpreadSheet extends EE { s2.render(false) s2.render({ reloadData: true; - reBuildDataSet: true; + rebuildDataSet: true; reBuildHiddenColumnsDetail: true; }) */ @@ -902,8 +905,8 @@ export abstract class SpreadSheet extends EE { menu: { selectedKeys, items: menuItems, - onClick: ({ key: sortMethod }) => { - this.groupSortByMethod(sortMethod as SortMethod, meta); + onClick: async ({ key: sortMethod }) => { + await this.groupSortByMethod(sortMethod as SortMethod, meta); this.emit(S2Event.RANGE_SORTED, event); }, }, diff --git a/packages/s2-core/src/sheet-type/table-sheet.ts b/packages/s2-core/src/sheet-type/table-sheet.ts index 2bd1d9cca2..3ed7e92f05 100644 --- a/packages/s2-core/src/sheet-type/table-sheet.ts +++ b/packages/s2-core/src/sheet-type/table-sheet.ts @@ -49,7 +49,8 @@ export class TableSheet extends SpreadSheet { return false; } - public clearDrillDownData(): void {} + // eslint-disable-next-line no-empty-function + public async clearDrillDownData() {} /** * Check if the value is in the columns @@ -81,7 +82,7 @@ export class TableSheet extends SpreadSheet { this.off(S2Event.RANGE_FILTER); } - public groupSortByMethod = (sortMethod: SortMethod, meta: Node) => { + public groupSortByMethod(sortMethod: SortMethod, meta: Node) { const { field } = meta; const sortParam: SortParam = { @@ -91,5 +92,5 @@ export class TableSheet extends SpreadSheet { this.updateSortMethodMap(meta.id, sortMethod); this.emit(S2Event.RANGE_SORT, [sortParam]); - }; + } } diff --git a/packages/s2-core/src/ui/hd-adapter/index.ts b/packages/s2-core/src/ui/hd-adapter/index.ts index 882ff05709..e2bef607cd 100644 --- a/packages/s2-core/src/ui/hd-adapter/index.ts +++ b/packages/s2-core/src/ui/hd-adapter/index.ts @@ -86,17 +86,19 @@ export class HdAdapter { * DPR 改变也会触发 visualViewport 的 resize 事件, 预期是只监听双指缩放, 所以这里规避掉 * @see https://github.com/antvis/S2/issues/2072 */ - private renderByZoomScaleWithoutResizeEffect = (event: Event) => { + private renderByZoomScaleWithoutResizeEffect = async (event: Event) => { this.isDevicePixelRatioChange = false; - this.renderByZoomScale(event); + await this.renderByZoomScale(event); }; - private renderByDevicePixelRatioChanged = () => { + private renderByDevicePixelRatioChanged = async () => { this.isDevicePixelRatioChange = true; - this.renderByDevicePixelRatio(); + await this.renderByDevicePixelRatio(); }; - private renderByDevicePixelRatio = (ratio = window.devicePixelRatio) => { + private renderByDevicePixelRatio = async ( + ratio = window.devicePixelRatio, + ) => { const { container, options: { width, height }, @@ -112,14 +114,14 @@ export class HdAdapter { container.getConfig().devicePixelRatio = ratio; container.resize(width!, height!); - this.spreadsheet.render(false); + await this.spreadsheet.render(false); }; - private renderByZoomScale = debounce((event: Event) => { + private renderByZoomScale = debounce(async (event: Event) => { const ratio = Math.ceil((event.target as VisualViewport)?.scale); if (ratio >= 1 && !this.isDevicePixelRatioChange) { - this.renderByDevicePixelRatio(ratio); + await this.renderByDevicePixelRatio(ratio); } }, 350); } diff --git a/packages/s2-react/src/components/sheets/editable-sheet/custom-cell/edit-cell/index.tsx b/packages/s2-react/src/components/sheets/editable-sheet/custom-cell/edit-cell/index.tsx index 62e14210b2..56e86c872c 100644 --- a/packages/s2-react/src/components/sheets/editable-sheet/custom-cell/edit-cell/index.tsx +++ b/packages/s2-react/src/components/sheets/editable-sheet/custom-cell/edit-cell/index.tsx @@ -87,14 +87,14 @@ function EditCellComponent( const inputRef = React.useRef(null); const containerRef = React.useRef(null); - const onSave = () => { + const onSave = async () => { const { rowIndex, valueField, id } = cell!.getMeta(); const displayData = s2.dataSet.getDisplayDataSet(); displayData[rowIndex][valueField] = inputVal; // 编辑后的值作为格式化后的结果, formatter 不再触发, 避免二次格式化 s2.dataSet.displayFormattedValueMap?.set(id, inputVal); - s2.render(); + await s2.render(); const editedMeta = customMerge(cell!.getMeta(), { fieldValue: inputVal, diff --git a/packages/s2-react/src/components/sheets/editable-sheet/drag-copy/drag-copy-mask.tsx b/packages/s2-react/src/components/sheets/editable-sheet/drag-copy/drag-copy-mask.tsx index eec58fa23a..1022028ab3 100644 --- a/packages/s2-react/src/components/sheets/editable-sheet/drag-copy/drag-copy-mask.tsx +++ b/packages/s2-react/src/components/sheets/editable-sheet/drag-copy/drag-copy-mask.tsx @@ -15,7 +15,7 @@ type DragCopyProps = { }; export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { - const spreadsheet = useSpreadSheetInstance(); + const s2 = useSpreadSheetInstance(); const [startCell, setStartCell] = useState(); const [maskPosition, setMaskPosition] = useState({ right: 0, bottom: 0 }); @@ -29,8 +29,8 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { 'height', 'fieldValue', ]); - const scrollOffset = spreadsheet.facet.getScrollOffset(); - const sampleColNode = spreadsheet.facet.getColNodes()[0]; + const scrollOffset = s2.facet.getScrollOffset(); + const sampleColNode = s2.facet.getColNodes()[0]; const sampleColNodeHeight = sampleColNode?.height || 0; // 点位偏移 const pointX = point.x; @@ -51,8 +51,8 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { /** 判断当前位置是否在表格可视区域内 */ const judgePointInView = (point: Point) => { - const rect = spreadsheet.getCanvasElement().getBoundingClientRect(); - const { frozenRow } = (spreadsheet.facet as any).frozenGroupInfo; + const rect = s2.getCanvasElement().getBoundingClientRect(); + const { frozenRow } = (s2.facet as any).frozenGroupInfo; const viewMinX = rect.x; const viewMaxX = rect.x + rect.width; // 减去列头高度 @@ -68,8 +68,8 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { }; const getCurrentHoverCell = (event: MouseEvent) => { - const rect = spreadsheet.getCanvasElement().getBoundingClientRect(); - const allCells = spreadsheet.facet.getDataCells(); + const rect = s2.getCanvasElement().getBoundingClientRect(); + const allCells = s2.facet.getDataCells(); return allCells.find((dataCell) => isInCell({ y: event.y - rect.y, x: event.x - rect.x }, dataCell), @@ -83,7 +83,7 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { const maxX = Math.max(startCellMeta.colIndex, endCellMeta.colIndex); const maxY = Math.max(startCellMeta.rowIndex, endCellMeta.rowIndex); const minY = Math.min(startCellMeta.rowIndex, endCellMeta.rowIndex); - const allCells = spreadsheet.facet.getDataCells(); + const allCells = s2.facet.getDataCells(); return allCells.filter((item) => { const itemMeta = item.getMeta(); @@ -123,14 +123,14 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { const selectedRange = getSelectedCellRange(startCell, targetCell!); // 更改选中状态 - spreadsheet.interaction.changeState({ + s2.interaction.changeState({ cells: selectedRange.map((v) => v.getMeta() as any), stateName: InteractionStateName.PREPARE_SELECT, force: true, }); }, 10); - const dragMouseUp = (event: MouseEvent) => { + const dragMouseUp = async (event: MouseEvent) => { if (!startCell) { return; } @@ -139,7 +139,7 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { getCurrentHoverCell(event) || getCurrentHoverCell(lastHoverPoint as MouseEvent); - const displayData = spreadsheet.dataSet.getDisplayDataSet(); + const displayData = s2.dataSet.getDisplayDataSet(); const selectedRange = getSelectedCellRange(startCell, targetCell!); const { fieldValue } = startCell.getMeta(); @@ -157,23 +157,24 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { }); // 更改选中状态 - spreadsheet.interaction.changeState({ + s2.interaction.changeState({ cells: changedCells.map((v) => v.getMeta() as any), stateName: InteractionStateName.PREPARE_SELECT, force: true, }); - spreadsheet.render(true); + await s2.render(true); + setMaskPosition({ right: 0, bottom: 0 }); - spreadsheet.off(S2Event.GLOBAL_MOUSE_MOVE, dragMove); - spreadsheet.off(S2Event.GLOBAL_MOUSE_UP, dragMouseUp); + s2.off(S2Event.GLOBAL_MOUSE_MOVE, dragMove); + s2.off(S2Event.GLOBAL_MOUSE_UP, dragMouseUp); setStartCell(undefined); onCopyFinished?.(); }; useEffect(() => { if (startCell) { - spreadsheet.on(S2Event.GLOBAL_MOUSE_MOVE, dragMove); - spreadsheet.on(S2Event.GLOBAL_MOUSE_UP, dragMouseUp); + s2.on(S2Event.GLOBAL_MOUSE_MOVE, dragMove); + s2.on(S2Event.GLOBAL_MOUSE_UP, dragMouseUp); } }, [startCell]); @@ -187,7 +188,7 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { top: string; left: string; }; - const allCells = spreadsheet.facet.getDataCells(); + const allCells = s2.facet.getDataCells(); const targetCell = allCells.find((v) => isInCell({ y: parseFloat(top), x: parseFloat(left) }, v), ); @@ -199,14 +200,14 @@ export const DragCopyMask = memo(({ onCopyFinished }: DragCopyProps) => { useEffect(() => { const pointElement = document.getElementById('spreadsheet-drag-copy-point'); - if (pointElement && spreadsheet) { + if (pointElement && s2) { pointElement.addEventListener('mousedown', dragMouseDown); } return () => { pointElement?.removeEventListener('mousedown', dragMouseDown); }; - }, [spreadsheet]); + }, [s2]); return (
= ({ ); }, [sheet, dataCfg, options?.interaction?.hiddenColumnFields]); - const onSubmit = (result: SwitcherResult) => { + const onSubmit = async (result: SwitcherResult) => { const { fields: currentFields, hiddenColumnFields } = generateSheetConfig( sheet, result, @@ -75,7 +75,7 @@ export const SwitcherHeader: React.FC = ({ sheet.setOptions({ interaction: { hiddenColumnFields } }); } - sheet.render(); + await sheet.render(); setFields( generateSwitcherFieldsCfgFromResult( diff --git a/packages/s2-react/src/hooks/usePagination.ts b/packages/s2-react/src/hooks/usePagination.ts index b6b4aebd99..4b9cdf1eac 100644 --- a/packages/s2-react/src/hooks/usePagination.ts +++ b/packages/s2-react/src/hooks/usePagination.ts @@ -46,15 +46,19 @@ export const usePagination = ( // sync state.pagination -> s2.pagination useUpdateEffect(() => { - if (!s2 || isEmpty(paginationCfg)) { - return; - } + const render = async () => { + if (!s2 || isEmpty(paginationCfg)) { + return; + } + + s2.updatePagination({ + current: pagination.current, + pageSize: pagination.pageSize, + }); + await s2.render(false); + }; - s2.updatePagination({ - current: pagination.current, - pageSize: pagination.pageSize, - }); - s2.render(false); + render(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [pagination.current, pagination.pageSize, s2]); diff --git a/packages/s2-react/src/hooks/usePivotSheetUpdate.ts b/packages/s2-react/src/hooks/usePivotSheetUpdate.ts index 1f84328052..39321e5cbd 100644 --- a/packages/s2-react/src/hooks/usePivotSheetUpdate.ts +++ b/packages/s2-react/src/hooks/usePivotSheetUpdate.ts @@ -39,7 +39,7 @@ export const usePivotSheetUpdate = (partDrillDown: PartDrillDown) => { return { reloadData: shouldReloadData || renderOptions.reloadData, - reBuildDataSet: renderOptions.reBuildDataSet, + rebuildDataSet: renderOptions.rebuildDataSet, }; // eslint-disable-next-line react-hooks/exhaustive-deps }, callbackDeps); diff --git a/packages/s2-react/src/hooks/useSpreadSheet.ts b/packages/s2-react/src/hooks/useSpreadSheet.ts index db957ad689..2ac37dd67f 100644 --- a/packages/s2-react/src/hooks/useSpreadSheet.ts +++ b/packages/s2-react/src/hooks/useSpreadSheet.ts @@ -90,61 +90,71 @@ export function useSpreadSheet(props: SheetComponentProps) { shouldInit.current = false; return () => { + setLoading(false); s2Ref.current?.destroy?.(); }; }, []); // 重渲 effect:dataCfg, options or theme changed useUpdateEffect(() => { - const [prevDataCfg, prevOptions, prevThemeCfg] = updatePrevDepsRef.current; + const render = async () => { + setLoading(true); - updatePrevDepsRef.current = [dataCfg, options!, themeCfg!]; + const [prevDataCfg, prevOptions, prevThemeCfg] = + updatePrevDepsRef.current; - let reloadData = false; - let reBuildDataSet = false; + updatePrevDepsRef.current = [dataCfg, options!, themeCfg!]; - if (!Object.is(prevDataCfg, dataCfg)) { - // 列头变化需要重新计算初始叶子节点 - if ( - prevDataCfg?.fields?.columns?.length !== - dataCfg?.fields?.columns?.length - ) { - s2Ref.current?.facet.clearInitColLeafNodes(); - } + let reloadData = false; + let rebuildDataSet = false; - reloadData = true; - s2Ref.current?.setDataCfg(dataCfg); - } + if (!Object.is(prevDataCfg, dataCfg)) { + // 列头变化需要重新计算初始叶子节点 + if ( + prevDataCfg?.fields?.columns?.length !== + dataCfg?.fields?.columns?.length + ) { + s2Ref.current?.facet.clearInitColLeafNodes(); + } - if (!Object.is(prevOptions, options)) { - if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) { - // 自定义树目录需要重新构建 CustomTreePivotDataSet - reBuildDataSet = true; reloadData = true; s2Ref.current?.setDataCfg(dataCfg); } - s2Ref.current?.setOptions(options as S2Options); - s2Ref.current?.changeSheetSize(options!.width, options!.height); - } + if (!Object.is(prevOptions, options)) { + if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) { + // 自定义树目录需要重新构建 CustomTreePivotDataSet + rebuildDataSet = true; + reloadData = true; + s2Ref.current?.setDataCfg(dataCfg); + } - if (!Object.is(prevThemeCfg, themeCfg)) { - s2Ref.current?.setThemeCfg(themeCfg); - } + s2Ref.current?.setOptions(options as S2Options); + s2Ref.current?.changeSheetSize(options!.width, options!.height); + } - /** - * onSheetUpdate 交出控制权 - * 由传入方决定最终的 render 模式 - */ - const renderOptions = onSheetUpdate({ - reloadData, - reBuildDataSet, - }); - - s2Ref.current?.render({ - reloadData: renderOptions!.reloadData, - reBuildDataSet: renderOptions!.reBuildDataSet, - }); + if (!Object.is(prevThemeCfg, themeCfg)) { + s2Ref.current?.setThemeCfg(themeCfg); + } + + /** + * onSheetUpdate 交出控制权 + * 由传入方决定最终的 render 模式 + */ + const renderOptions = onSheetUpdate({ + reloadData, + rebuildDataSet, + }); + + await s2Ref.current?.render({ + reloadData: renderOptions!.reloadData, + rebuildDataSet: renderOptions!.rebuildDataSet, + }); + + setLoading(false); + }; + + render(); }, [dataCfg, options, themeCfg, onSheetUpdate]); useResize({ diff --git a/packages/s2-shared/src/utils/drill-down.ts b/packages/s2-shared/src/utils/drill-down.ts index 6eba2fe8c0..f030bfc7b6 100644 --- a/packages/s2-shared/src/utils/drill-down.ts +++ b/packages/s2-shared/src/utils/drill-down.ts @@ -192,7 +192,7 @@ export const handleDrillDown = (params: DrillDownParams) => { return; } - fetchData(meta, drillFields).then((info) => { + fetchData(meta, drillFields).then(async (info) => { const { drillData, drillField } = info; (spreadsheet.dataSet as PivotDataSet).transformDrillDownData( @@ -217,6 +217,6 @@ export const handleDrillDown = (params: DrillDownParams) => { // 重置当前交互 spreadsheet.interaction.reset(); - spreadsheet.render(false); + await spreadsheet.render(false); }); }; diff --git a/packages/s2-shared/src/utils/resize.ts b/packages/s2-shared/src/utils/resize.ts index 65e6ddc693..5e0c1fd46a 100644 --- a/packages/s2-shared/src/utils/resize.ts +++ b/packages/s2-shared/src/utils/resize.ts @@ -35,14 +35,14 @@ export const createResizeObserver = (params: ResizeEffectParams) => { return; } - const render = (width?: number, height?: number) => { + const render = async (width?: number, height?: number) => { s2?.changeSheetSize(width, height); - s2?.render(false); + await s2?.render(false); }; const debounceRender = debounce(render, RESIZE_RENDER_DELAY); - const onResize = () => { + const onResize = async () => { // 解决父容器有缩放, 获取宽高不对的问题: https://github.com/antvis/S2/pull/1425 const { clientWidth: containerWidth, clientHeight: containerHeight } = container; @@ -60,7 +60,7 @@ export const createResizeObserver = (params: ResizeEffectParams) => { } if (isFirstRender) { - render(width, height); + await render(width, height); isFirstRender = false; return; diff --git a/packages/s2-vue/src/hooks/usePagination.ts b/packages/s2-vue/src/hooks/usePagination.ts index b9d4dd9419..1c45663aff 100644 --- a/packages/s2-vue/src/hooks/usePagination.ts +++ b/packages/s2-vue/src/hooks/usePagination.ts @@ -36,7 +36,7 @@ export const usePagination = ( }); // sync state.pagination -> s2.pagination - watch([current, pageSize], () => { + watch([current, pageSize], async () => { if (!s2Ref.value) { return; } @@ -49,7 +49,7 @@ export const usePagination = ( }; s2Ref.value.updatePagination(nextPagination); - s2Ref.value.render(false); + await s2Ref.value.render(false); }); // sync props.pagination -> state.pagination diff --git a/packages/s2-vue/src/hooks/useSheetUpdate.ts b/packages/s2-vue/src/hooks/useSheetUpdate.ts index 298c362ae6..398c4f0171 100644 --- a/packages/s2-vue/src/hooks/useSheetUpdate.ts +++ b/packages/s2-vue/src/hooks/useSheetUpdate.ts @@ -11,6 +11,7 @@ import type { BaseSheetProps } from '../utils/initPropAndEmits'; export const useSheetUpdate = ( s2Ref: ShallowRef, props: BaseSheetProps, + hooks?: { before?: () => void; after: () => void }, ) => { const updateFlag = reactive({ rerender: false, @@ -63,15 +64,19 @@ export const useSheetUpdate = ( }, ); - watch(updateFlag, (flag) => { + watch(updateFlag, async (flag) => { if (!flag.rerender) { return; } - s2Ref.value?.render({ + hooks?.before?.(); + + await s2Ref.value?.render({ reloadData: flag.reloadData, - reBuildDataSet: flag.rebuildDataset, + rebuildDataSet: flag.rebuildDataset, }); + + hooks?.after?.(); flag.rerender = false; flag.reloadData = false; flag.rebuildDataset = false; diff --git a/packages/s2-vue/src/hooks/useSpreadSheet.ts b/packages/s2-vue/src/hooks/useSpreadSheet.ts index 083e791dc6..17452f97b9 100644 --- a/packages/s2-vue/src/hooks/useSpreadSheet.ts +++ b/packages/s2-vue/src/hooks/useSpreadSheet.ts @@ -52,21 +52,30 @@ export function useSpreadSheet( return new PivotSheet(container, rawDataCfg, s2Options); }; - const buildSpreadSheet = () => { + const buildSpreadSheet = async () => { setLoading(true); s2Ref.value = renderSpreadSheet(containerRef.value!); s2Ref.value.setThemeCfg(toRaw(themeCfg)); - s2Ref.value.render(); + await s2Ref.value.render(); + setLoading(false); onS2Mounted?.(s2Ref.value); }; onMounted(buildSpreadSheet); useEvents(s2Ref, emit); - useSheetUpdate(s2Ref, props); + useSheetUpdate(s2Ref, props, { + before() { + setLoading(true); + }, + after() { + setLoading(false); + }, + }); useResize(s2Ref, props, { wrapperRef, containerRef }); onBeforeUnmount(() => { + setLoading(false); s2Ref.value?.destroy(); }); diff --git a/s2-site/docs/api/basic-class/spreadsheet.zh.md b/s2-site/docs/api/basic-class/spreadsheet.zh.md index fc43019118..99975c9eed 100644 --- a/s2-site/docs/api/basic-class/spreadsheet.zh.md +++ b/s2-site/docs/api/basic-class/spreadsheet.zh.md @@ -33,7 +33,7 @@ s2.isPivotMode() | isFrozenRowHeader | 是否是冻结行头状态 | `() => boolean` | | | isTableMode | 是否是明细表 | `() => boolean` | | | isValueInCols | 是否是数值置于行头 | `() => boolean` | | -| clearDrillDownData | 清除下钻数据 | `(rowNodeId?: string) => void` | | +| clearDrillDownData | 清除下钻数据 | (rowNodeId?: `string`) => `Promise` | | | showTooltip | 显示 tooltip (别名 `tooltip.show`) | (showOptions: [TooltipShowOptions](/docs/api/common/custom-tooltip)) => void | | | showTooltipWithInfo | 显示 tooltip, 并且展示一些默认信息 | (event: `CanvasEvent \| MouseEvent`, data: [TooltipData[]](/docs/api/common/custom-tooltip), options?: [TooltipOptions](/docs/api/common/custom-tooltip)) => void | | hideTooltip | 隐藏 tooltip (别名:`tooltip.hide`) | `() => void` | | @@ -65,7 +65,7 @@ s2.isPivotMode() | measureText | 获取文本在画布中的测量信息 | (text: `string`, font: [TextTheme](/docs/api/general/S2Theme#texttheme)) => [TextMetrics](https://developer.mozilla.org/zh-CN/docs/Web/API/TextMetrics) \| `null` | | | measureTextWidth | 获取文本在画布中的测量宽度 | (text: `string`, font: [TextTheme](/docs/api/general/S2Theme#texttheme)) => `number` \| `null` | | | measureTextHeight | 获取文本在画布中的测量高度 | (text:`string`, font: [TextTheme](/docs/api/general/S2Theme#texttheme)) => `number` \| `null` | | -| groupSortByMethod | 组内排序(透视表有效) | (sortMethod: `'asc' \| 'desc'`, meta: [Node](/docs/api/basic-class/node)) => void | | +| groupSortByMethod | 组内排序(透视表有效) | (sortMethod: `'asc' \| 'desc'`, meta: [Node](/docs/api/basic-class/node)) => `Promise \| void` | | | getSeriesNumberText | 获取序号文本(根据 `s2Options.series.text` 配置,默认 "序号") | () => string | | ### S2MountContainer diff --git a/s2-site/docs/manual/migration-v2.zh.md b/s2-site/docs/manual/migration-v2.zh.md index 40bbb98a69..13cb173811 100644 --- a/s2-site/docs/manual/migration-v2.zh.md +++ b/s2-site/docs/manual/migration-v2.zh.md @@ -403,7 +403,7 @@ const s2DataConfig = { 透视表和明细表的行列冻结配置统一收拢到 `frozen`。 -在透视表中,`frozenFirstRow` 使用 `rowCount: 1` 替代。在明细表多级列头冻结时,在 `1.x` 中是以最最顶层节点为准,而在 `2.x` 是以叶子节点的数量为准。 +在透视表中,`frozenFirstRow` 使用 `rowCount: 1` 替代。在明细表多级列头冻结时,在 `1.x` 中是以最最顶层节点为准,而在 `2.x` 是以叶子节点的数量为准。 ```diff const s2Options = { @@ -535,6 +535,15 @@ render 函数的参数从 `boolean` 扩展为 `boolean | object`, 当为 `boolea + }); ``` +`reBuildDataSet` 重命名为 `rebuildDataSet`: + +```diff ++ s2.render({ +- reBuildDataSet: false, ++ rebuildDataSet: false, ++ }); +``` + #### 小计总计配置参数变更 总计配置统一增加 `grandTotals` 和 `subTotals` 前缀,避免歧义。 @@ -868,6 +877,13 @@ s2.showTooltip({ ``` +#### SheetComponentsProps 类型调整 + +```diff +- interface SheetComponentsProps {} ++ interface SheetComponentProps {} +``` + ## ✍️ API 调整 具体请查看标记为 NewUpdated 的 [`API 文档`](/api) diff --git a/s2-site/examples/case/art/demo/lost-text.tsx b/s2-site/examples/case/art/demo/lost-text.tsx index 0141edbd89..8a8d5537f8 100644 --- a/s2-site/examples/case/art/demo/lost-text.tsx +++ b/s2-site/examples/case/art/demo/lost-text.tsx @@ -1,3 +1,4 @@ +// organize-imports-ignore /* eslint-disable max-classes-per-file */ import { BaseEvent, @@ -47,7 +48,7 @@ class CustomInteraction extends BaseEvent { count = 0; - changeCell(cellType: CellType) { + async changeCell(cellType: CellType) { this.count++; const defaultTheme = getTheme(null)?.[cellType]; @@ -55,7 +56,8 @@ class CustomInteraction extends BaseEvent { this.spreadsheet.setTheme({ [cellType]: defaultTheme, }); - this.spreadsheet.render(false); + + await this.spreadsheet.render(false); if (this.count >= 3) { clearInterval(this.timer); @@ -63,10 +65,10 @@ class CustomInteraction extends BaseEvent { } } - resetCell() { + async resetCell() { this.count = 0; this.spreadsheet.setTheme(Theme); - this.spreadsheet.render(false); + await this.spreadsheet.render(false); } showSuccessTips() { @@ -118,20 +120,20 @@ class CustomInteraction extends BaseEvent { 2: CellType.DATA_CELL, }; - this.spreadsheet.on(S2Event.CORNER_CELL_MOUSE_DOWN, () => { + this.spreadsheet.on(S2Event.CORNER_CELL_MOUSE_DOWN, async () => { clearInterval(this.timer); - this.resetCell(); + await this.resetCell(); this.timer = setInterval(() => { this.changeCell(countMap[this.count]); }, 1000); }); - this.spreadsheet.on(S2Event.CORNER_CELL_MOUSE_UP, () => { + this.spreadsheet.on(S2Event.CORNER_CELL_MOUSE_UP, async () => { clearInterval(this.timer); if (this.count < 3) { - this.resetCell(); + await this.resetCell(); } }); } From b17ea1ad5952171fab31c90f45e0737cbd0bfe9e Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 5 Jun 2024 11:39:58 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20onSheetUpdate=20=E9=87=8D=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E4=B8=BA=20onUpdate,=20=E6=96=B0=E5=A2=9E=20onUpdateA?= =?UTF-8?q?fterRender?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierignore | 1 + .../s2-core/src/common/interface/s2Options.ts | 3 + packages/s2-core/src/utils/g-mini-charts.ts | 2 +- .../unit/hooks/useSpreadSheet-spec.ts | 113 +++++++++++++++--- .../components/sheets/pivot-sheet/index.tsx | 6 +- .../s2-react/src/hooks/usePivotSheetUpdate.ts | 4 +- packages/s2-react/src/hooks/useSpreadSheet.ts | 15 ++- packages/s2-shared/src/interface.ts | 5 +- packages/s2-vue/src/hooks/useSheetUpdate.ts | 6 +- packages/s2-vue/src/utils/initPropAndEmits.ts | 4 + .../docs/api/components/sheet-component.zh.md | 16 ++- s2-site/docs/manual/migration-v2.zh.md | 10 ++ .../react-component/sheet/demo/pivot.tsx | 26 +++- .../react-component/sheet/demo/table.tsx | 22 +++- 14 files changed, 192 insertions(+), 41 deletions(-) diff --git a/.prettierignore b/.prettierignore index 1fe6728b63..c66cc5d075 100644 --- a/.prettierignore +++ b/.prettierignore @@ -21,6 +21,7 @@ CNAME LICENSE s2-site/.cache s2-site/public +s2-site/*.tsx .history esm dist diff --git a/packages/s2-core/src/common/interface/s2Options.ts b/packages/s2-core/src/common/interface/s2Options.ts index ee880b7881..7b3d296644 100644 --- a/packages/s2-core/src/common/interface/s2Options.ts +++ b/packages/s2-core/src/common/interface/s2Options.ts @@ -365,6 +365,9 @@ export interface S2Options< frozen?: S2PivotSheetFrozenOptions & S2BaseFrozenOptions; } +/** + * 自定义渲染模式 + */ export interface S2RenderOptions { /** * 是否重新加载数据 diff --git a/packages/s2-core/src/utils/g-mini-charts.ts b/packages/s2-core/src/utils/g-mini-charts.ts index 5d714642ec..5eaed0490b 100644 --- a/packages/s2-core/src/utils/g-mini-charts.ts +++ b/packages/s2-core/src/utils/g-mini-charts.ts @@ -67,7 +67,7 @@ export const scale = (chartData: BaseChartData, cell: S2CellType) => { ? (xEnd - xStart - (measures.length - 1) * intervalPadding) / measures.length + intervalPadding - : (xEnd - xStart) / (measures.length - 1) ?? 0; + : (xEnd - xStart) / (measures.length - 1) || 0; const box: number[][] = []; const points = map( diff --git a/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts b/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts index b8111a6849..f4ff94af13 100644 --- a/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts +++ b/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts @@ -9,7 +9,6 @@ import { } from '@antv/s2'; import { waitFor } from '@testing-library/react'; import { act, renderHook } from '@testing-library/react-hooks'; -import { cloneDeep } from 'lodash'; import * as mockDataConfig from 'tests/data/simple-data.json'; import { getContainer } from 'tests/util/helpers'; @@ -85,11 +84,15 @@ describe('useSpreadSheet tests', () => { valueInCols: true, }; - const props = cloneDeep({ + const props = { ...getConfig(defaultFields), sheetType: 'strategy' as const, - }); - const { result } = renderHook(() => useSpreadSheet(props)); + }; + + const { result, rerender } = renderHook( + (innerProps) => useSpreadSheet(innerProps), + { initialProps: props }, + ); await waitFor(() => { const s2 = result.current.s2Ref.current; @@ -98,23 +101,23 @@ describe('useSpreadSheet tests', () => { expect(s2!.facet.getInitColLeafNodes()).toHaveLength(2); }); - /* - * 很奇怪, rerender 之后始终拿到的两次 dataCfg 是一样的, 暂时先注释了 - * act(() => { - * const fields: S2DataConfig['fields'] = { - * rows: ['province', 'city'], - * columns: ['type'], - * values: ['price'], - * valueInCols: false, - * }; - */ + act(() => { + const fields: S2DataConfig['fields'] = { + ...defaultFields, + columns: ['type'], + }; + + rerender({ + ...props, + ...getConfig(fields), + }); + }); - /* - * rerender(getConfig(fields)); - * }); - */ + await waitFor(() => { + const s2 = result.current.s2Ref.current; - // expect(s2.store.get('initColLeafNodes')).toEqual([]); + expect(s2!.facet.getInitColLeafNodes()).toHaveLength(0); + }); }); test('should destroy sheet after unmount component', async () => { @@ -169,4 +172,76 @@ describe('useSpreadSheet tests', () => { expect(onMounted).toHaveBeenCalledWith(s2); }); }); + + test('should call onUpdate and onUpdateAfterRender when sheet updated', async () => { + const onUpdate = jest.fn(); + const onUpdateAfterRender = jest.fn(); + + const props = { + ...getConfig(), + sheetType: 'pivot' as const, + onUpdate, + onUpdateAfterRender, + }; + const { rerender } = renderHook( + (innerProps) => useSpreadSheet(innerProps), + { + initialProps: props, + }, + ); + + await waitFor(() => { + expect(onUpdate).toHaveBeenCalledTimes(0); + expect(onUpdateAfterRender).toHaveBeenCalledTimes(0); + }); + + act(() => { + rerender({ ...props, options: { width: 200 } }); + }); + + await waitFor(() => { + expect(onUpdate).toHaveBeenCalledWith({ + rebuildDataSet: false, + reloadData: false, + }); + expect(onUpdateAfterRender).toHaveBeenCalledWith({ + rebuildDataSet: false, + reloadData: false, + }); + }); + }); + + test('should use custom render mode by onUpdate', async () => { + const onUpdate = jest.fn((options) => ({ ...options, reloadData: true })); + const onUpdateAfterRender = jest.fn(); + + const props = { + ...getConfig(), + sheetType: 'pivot' as const, + onUpdate, + onUpdateAfterRender, + }; + const { rerender } = renderHook( + (innerProps) => useSpreadSheet(innerProps), + { + initialProps: props, + }, + ); + + act(() => { + rerender({ ...props, options: { width: 200 } }); + }); + + await waitFor(() => { + expect(onUpdate).toHaveBeenCalledWith({ + rebuildDataSet: false, + reloadData: false, + }); + expect(onUpdateAfterRender).toHaveBeenCalledWith({ + rebuildDataSet: false, + // 由于使用了自定义的 onUpdate,所以这里应该为 true + reloadData: true, + }); + }); + }); }); diff --git a/packages/s2-react/src/components/sheets/pivot-sheet/index.tsx b/packages/s2-react/src/components/sheets/pivot-sheet/index.tsx index 587b9a845c..4d715c3565 100644 --- a/packages/s2-react/src/components/sheets/pivot-sheet/index.tsx +++ b/packages/s2-react/src/components/sheets/pivot-sheet/index.tsx @@ -107,11 +107,9 @@ export const PivotSheet: React.FC = React.memo((props) => { /** * 控制交叉表 render */ - const onSheetUpdate = usePivotSheetUpdate(partDrillDown!); + const onUpdate = usePivotSheetUpdate(partDrillDown!); - return ( - - ); + return ; }); PivotSheet.displayName = 'PivotSheet'; diff --git a/packages/s2-react/src/hooks/usePivotSheetUpdate.ts b/packages/s2-react/src/hooks/usePivotSheetUpdate.ts index 39321e5cbd..4b35700907 100644 --- a/packages/s2-react/src/hooks/usePivotSheetUpdate.ts +++ b/packages/s2-react/src/hooks/usePivotSheetUpdate.ts @@ -17,7 +17,9 @@ const DRILL_DOWN_ATTR_TO_DIFF = [ * @param partDrillDown 下钻参数 * @returns update callback */ -export const usePivotSheetUpdate = (partDrillDown: PartDrillDown) => { +export const usePivotSheetUpdate = ( + partDrillDown: PartDrillDown, +): SheetUpdateCallback => { const prePartDrillDownRef = React.useRef(partDrillDown); /** 属性值发生变化时,才更新 callback,触发底表 render */ diff --git a/packages/s2-react/src/hooks/useSpreadSheet.ts b/packages/s2-react/src/hooks/useSpreadSheet.ts index 2ac37dd67f..86c8a16d1e 100644 --- a/packages/s2-react/src/hooks/useSpreadSheet.ts +++ b/packages/s2-react/src/hooks/useSpreadSheet.ts @@ -31,7 +31,8 @@ export function useSpreadSheet(props: SheetComponentProps) { options, themeCfg, sheetType, - onSheetUpdate = identity, + onUpdate = identity, + onUpdateAfterRender, } = props; /** 保存重渲 effect 的 deps */ @@ -138,24 +139,22 @@ export function useSpreadSheet(props: SheetComponentProps) { } /** - * onSheetUpdate 交出控制权 + * onUpdate 交出控制权 * 由传入方决定最终的 render 模式 */ - const renderOptions = onSheetUpdate({ + const renderOptions = onUpdate?.({ reloadData, rebuildDataSet, }); - await s2Ref.current?.render({ - reloadData: renderOptions!.reloadData, - rebuildDataSet: renderOptions!.rebuildDataSet, - }); + await s2Ref.current?.render(renderOptions!); setLoading(false); + onUpdateAfterRender?.(renderOptions!); }; render(); - }, [dataCfg, options, themeCfg, onSheetUpdate]); + }, [dataCfg, options, themeCfg, onUpdate]); useResize({ s2: s2Ref.current!, diff --git a/packages/s2-shared/src/interface.ts b/packages/s2-shared/src/interface.ts index 33257692b5..6e7d5c4fd2 100644 --- a/packages/s2-shared/src/interface.ts +++ b/packages/s2-shared/src/interface.ts @@ -95,8 +95,6 @@ export interface BaseSheetComponentProps< showPagination?: ShowPagination; themeCfg?: ThemeCfg; header?: Header; - /** 底表 render callback */ - onSheetUpdate?: SheetUpdateCallback; // ============== Row Cell ==================== onRowCellHover?: (data: TargetCellInfo) => void; @@ -124,7 +122,6 @@ export interface BaseSheetComponentProps< currentHiddenColumnsInfo: HiddenColumnsInfo; hiddenColumnsDetail: HiddenColumnsInfo[]; }) => void; - onColCellRender?: (cell: ColCell) => void; // ============== Data Cell ==================== @@ -182,6 +179,8 @@ export interface BaseSheetComponentProps< onBeforeRender?: () => void; onAfterRender?: () => void; onMounted?: (spreadsheet: SpreadSheet) => void; + onUpdate?: (renderOptions: S2RenderOptions) => S2RenderOptions; + onUpdateAfterRender?: (renderOptions: S2RenderOptions) => void; onDestroy?: () => void; // ============== Resize ==================== diff --git a/packages/s2-vue/src/hooks/useSheetUpdate.ts b/packages/s2-vue/src/hooks/useSheetUpdate.ts index 398c4f0171..4eca76ad10 100644 --- a/packages/s2-vue/src/hooks/useSheetUpdate.ts +++ b/packages/s2-vue/src/hooks/useSheetUpdate.ts @@ -71,11 +71,15 @@ export const useSheetUpdate = ( hooks?.before?.(); - await s2Ref.value?.render({ + const renderOptions = props?.onUpdate?.({ reloadData: flag.reloadData, rebuildDataSet: flag.rebuildDataset, }); + await s2Ref.value?.render(renderOptions); + + props?.onUpdateAfterRender?.(renderOptions); + hooks?.after?.(); flag.rerender = false; flag.reloadData = false; diff --git a/packages/s2-vue/src/utils/initPropAndEmits.ts b/packages/s2-vue/src/utils/initPropAndEmits.ts index 5571ec1380..a8dd08df1d 100644 --- a/packages/s2-vue/src/utils/initPropAndEmits.ts +++ b/packages/s2-vue/src/utils/initPropAndEmits.ts @@ -34,6 +34,10 @@ export const initBaseSheetProps = () => { }, spreadsheet: Function as PropType, onMounted: Function as PropType, + onUpdate: Function as PropType, + onUpdateAfterRender: Function as PropType< + SheetComponentProps['onUpdateAfterRender'] + >, }; }; diff --git a/s2-site/docs/api/components/sheet-component.zh.md b/s2-site/docs/api/components/sheet-component.zh.md index 0847ad5a31..63924d285f 100644 --- a/s2-site/docs/api/components/sheet-component.zh.md +++ b/s2-site/docs/api/components/sheet-component.zh.md @@ -75,7 +75,9 @@ tag: Updated | onLayoutAfterCollapseRows | 树状模式下收起行头后的事件回调 | ({collapseFields: `Record`, meta: [Node](/docs/api/basic-class/node) ) => void; | | | | onBeforeRender | 开始 render 前的事件 | () => void; | | | | onAfterRender | render 完成的事件 | () => void; | | | -| onMounted | 表格加载完成事件,可拿到表实例 [详情](/docs/manual/advanced/get-instance) | (spreadsheet: [SpreadSheet](/docs/api/basic-class/spreadsheet)) => void; | | | +| onMounted | 组件层表格挂载完成事件,可拿到表实例 [详情](/docs/manual/advanced/get-instance) | (spreadsheet: [SpreadSheet](/docs/api/basic-class/spreadsheet)) => void; | | | +| onUpdate | 组件层表格更新事件,当 `数据 (S2DataConfig)` 或 `配置 (S2Options)` 更新时触发,可手动控制更新时的 [渲染模式](#s2renderoptions) | (renderOptions: [S2RenderOptions](#s2renderoptions)) => [S2RenderOptions](#s2renderoptions) | | | +| onUpdateAfterRender | 组件层表格更新事件,当 `数据 (S2DataConfig)` 或 `配置 (S2Options)` 更新时,并且在重渲染 `s2.render()` 完成后触发 | (renderOptions: [S2RenderOptions](#s2renderoptions)) => void | | | | onDestroy | 表格销毁事件 | () => void; | | | | onLayoutResize | 表格整体 changeSize 事件 | (params: [ResizeParams](#resizeparams)) => void; | | | | onLayoutResizeSeriesWidth | 表格序号行宽事件 | (params: [ResizeParams](#resizeparams)) => void; | | | @@ -200,6 +202,8 @@ type SheetComponentOptions = S2Options< | beforeRender | 开始 render 前的事件 | () => void; | | | | afterRender | render 完成的事件 | () => void; | | | | mounted | 表格加载完成事件,可拿到表实例 [详情](/docs/manual/advanced/get-instance) | (spreadsheet: [SpreadSheet](/docs/api/basic-class/spreadsheet)) => void; | | | +| update | 组件层表格更新事件,当 `数据 (S2DataConfig)` 或 `配置 (S2Options)` 更新时触发,可手动控制更新时的 [渲染模式](#s2renderoptions) | (renderOptions: [S2RenderOptions](#s2renderoptions)) => [S2RenderOptions](#s2renderoptions) | | | +| updateAfterRender | 组件层表格更新事件,当 `数据 (S2DataConfig)` 或 `配置 (S2Options)` 更新时,并且在重渲染 `s2.render()` 完成后触发 | (renderOptions: [S2RenderOptions](#s2renderoptions)) => void | | | | destroy | 表格销毁事件 | () => void; | | | | layoutResize | 表格整体 changeSize 事件 | (params: [ResizeParams](#resizeparams)) => void; | | | | layoutResizeSeriesWidth | 表格序号行宽事件 | (params: [ResizeParams](#resizeparams)) => void; | | | @@ -307,4 +311,14 @@ type SheetComponentOptions = S2Options< | resizedWidth | 拖拽后的宽度 | `number` | | | | resizedHeight | 拖拽后的高度 | `number` | | | +### S2RenderOptions + +功能描述:自定义渲染模式 + +| 参数 | 说明 | 类型 | 默认值 | 必选 | +| -- | -- | -- | -- | --- | +| reloadData | 是否重新加载数据 | `boolean` | | | +| rebuildDataSet | 是否重新生成数据集 | `boolean` | | | +| reBuildHiddenColumnsDetail | 是否重新生成列头隐藏信息 | `boolean` | | | + diff --git a/s2-site/docs/manual/migration-v2.zh.md b/s2-site/docs/manual/migration-v2.zh.md index 13cb173811..6edf19957f 100644 --- a/s2-site/docs/manual/migration-v2.zh.md +++ b/s2-site/docs/manual/migration-v2.zh.md @@ -877,6 +877,16 @@ s2.showTooltip({ ``` +#### `onSheetUpdate` 更名为 `onUpdate`, 并新增 `onUpdateAfterRender` + +- `onUpdate`: 组件层表格更新事件,当 `数据 (S2DataConfig)` 或 `配置 (S2Options)` 更新时触发。 +- `onUpdateAfterRender`: 组件层表格更新事件,当 `数据 (S2DataConfig)` 或 `配置 (S2Options)` 更新时,并且在重渲染 `s2.render()` 完成后触发。 + +```diff +- ++ +``` + #### SheetComponentsProps 类型调整 ```diff diff --git a/s2-site/examples/react-component/sheet/demo/pivot.tsx b/s2-site/examples/react-component/sheet/demo/pivot.tsx index a295bb7751..f2b724d6ae 100644 --- a/s2-site/examples/react-component/sheet/demo/pivot.tsx +++ b/s2-site/examples/react-component/sheet/demo/pivot.tsx @@ -1,4 +1,6 @@ +// organize-imports-ignore import React from 'react'; +import type { S2RenderOptions, SpreadSheet } from '@antv/s2'; import { SheetComponent, SheetComponentOptions } from '@antv/s2-react'; import '@antv/s2-react/dist/style.min.css'; @@ -12,7 +14,29 @@ fetch( height: 480, }; + const onMounted = (spreadsheet: SpreadSheet) => { + console.log('onMounted:', spreadsheet); + }; + + const onUpdate = (renderOptions: S2RenderOptions) => { + console.log('onUpdate:', renderOptions); + + return renderOptions; + }; + + const onUpdateAfterRender = (renderOptions: S2RenderOptions) => { + console.log('onUpdateAfterRender:', renderOptions); + }; + reactDOMClient .createRoot(document.getElementById('container')) - .render(); + .render( + , + ); }); diff --git a/s2-site/examples/react-component/sheet/demo/table.tsx b/s2-site/examples/react-component/sheet/demo/table.tsx index c84ba0d313..ee4b9b5719 100644 --- a/s2-site/examples/react-component/sheet/demo/table.tsx +++ b/s2-site/examples/react-component/sheet/demo/table.tsx @@ -1,7 +1,8 @@ -import { S2DataConfig } from '@antv/s2'; +// organize-imports-ignore +import React from 'react'; +import { S2DataConfig, type S2RenderOptions, type SpreadSheet } from '@antv/s2'; import { SheetComponent, SheetComponentOptions } from '@antv/s2-react'; import '@antv/s2-react/dist/style.min.css'; -import React from 'react'; fetch('https://assets.antv.antgroup.com/s2/basic-table-mode.json') .then((res) => res.json()) @@ -40,6 +41,20 @@ fetch('https://assets.antv.antgroup.com/s2/basic-table-mode.json') data: res, }; + const onMounted = (spreadsheet: SpreadSheet) => { + console.log('onMounted:', spreadsheet); + }; + + const onUpdate = (renderOptions: S2RenderOptions) => { + console.log('onUpdate:', renderOptions); + + return renderOptions; + }; + + const onUpdateAfterRender = (renderOptions: S2RenderOptions) => { + console.log('onUpdateAfterRender:', renderOptions); + }; + reactDOMClient .createRoot(document.getElementById('container')) .render( @@ -47,6 +62,9 @@ fetch('https://assets.antv.antgroup.com/s2/basic-table-mode.json') dataCfg={s2DataConfig} options={s2Options} sheetType="table" + onMounted={onMounted} + onUpdate={onUpdate} + onUpdateAfterRender={onUpdateAfterRender} />, ); }); From f22908b33c946f83b01a993cc8b0034a4ecee284 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 5 Jun 2024 15:09:28 +0800 Subject: [PATCH 3/5] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spreadsheet/spread-sheet-collapse-spec.ts | 8 +++++--- .../unit/sheet-type/pivot-sheet-spec.ts | 16 +++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/spread-sheet-collapse-spec.ts b/packages/s2-core/__tests__/spreadsheet/spread-sheet-collapse-spec.ts index 1e31d020dd..1159c57726 100644 --- a/packages/s2-core/__tests__/spreadsheet/spread-sheet-collapse-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/spread-sheet-collapse-spec.ts @@ -280,7 +280,7 @@ describe('SpreadSheet Collapse/Expand Tests', () => { expectCornerIconName(s2, 'Plus'); }); - test('should emit collapse event', () => { + test('should emit collapse event', async () => { const onCollapsed = jest.fn(); s2.on(S2Event.ROW_CELL_COLLAPSED, onCollapsed); @@ -301,19 +301,21 @@ describe('SpreadSheet Collapse/Expand Tests', () => { s2.emit(S2Event.ROW_CELL_COLLAPSED__PRIVATE, treeRowType); + await sleep(500); expect(onCollapsed).toHaveBeenCalledWith(params); expectCornerIconName(s2, 'Minus'); }); - test('should emit collapse all event', () => { + test('should emit collapse all event', async () => { const onCollapsed = jest.fn(); s2.on(S2Event.ROW_CELL_ALL_COLLAPSED, onCollapsed); s2.emit(S2Event.ROW_CELL_ALL_COLLAPSED__PRIVATE, false); + await sleep(500); expect(onCollapsed).toHaveBeenCalledWith(true); - expectCornerIconName(s2, 'Minus'); + expectCornerIconName(s2, 'Plus'); }); // https://github.com/antvis/S2/issues/2607 diff --git a/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts b/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts index fa0cf8ade5..40543a1ea8 100644 --- a/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts +++ b/packages/s2-core/__tests__/unit/sheet-type/pivot-sheet-spec.ts @@ -25,9 +25,10 @@ import { cloneDeep, last } from 'lodash'; import dataCfg from 'tests/data/simple-data.json'; import { waitForRender } from 'tests/util'; import { createPivotSheet, getContainer, sleep } from 'tests/util/helpers'; -import type { +import { BaseEvent, BaseTooltipOperatorMenuOptions, + CornerCell, HeaderCell, TooltipOptions, } from '../../../src'; @@ -843,7 +844,7 @@ describe('PivotSheet Tests', () => { ); await pivotSheet.render(); - const extraField = last(pivotSheet.facet.getCornerCells()); + const extraField = last(pivotSheet.facet.getCornerCells()); expect(extraField?.getActualText()).toEqual('数值'); }); @@ -867,16 +868,19 @@ describe('PivotSheet Tests', () => { await pivotSheet.render(); - const extraField = last(pivotSheet.facet.getCornerCells()); + const extraField = last(pivotSheet.facet.getCornerCells()); expect(extraField?.getActualText()).toEqual(cornerExtraFieldText); }); describe('Tree Collapse Tests', () => { - test('should collapse rows with tree mode', () => { + test('should collapse rows with tree mode', async () => { s2.setOptions({ hierarchyType: 'tree', }); + + await s2.render(false); + const renderSpy = jest .spyOn(s2, 'render') .mockImplementation(async () => {}); @@ -901,6 +905,8 @@ describe('PivotSheet Tests', () => { s2.emit(S2Event.ROW_CELL_COLLAPSED__PRIVATE, treeRowType); + await sleep(500); + expect(collapseRows).toHaveBeenCalledWith(collapsedRowsType); expect(s2.options.style?.rowCell?.collapseFields).toEqual( collapsedRowsType.collapseFields, @@ -1187,7 +1193,7 @@ describe('PivotSheet Tests', () => { // g5.0 destroy expect(destroyFn).toHaveBeenCalled(); - expect(document.body.contains(s2.getCanvasElement())).toBeFalse(); + expect(document.body.contains(s2.getCanvasElement())).toBeFalsy(); }); describe('Test Layout by dataCfg fields', () => { From 3b34829f0228ea1d19acf20f20f38393e05b5578 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 5 Jun 2024 15:23:55 +0800 Subject: [PATCH 4/5] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts b/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts index f4ff94af13..ba5d76e0ec 100644 --- a/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts +++ b/packages/s2-react/__tests__/unit/hooks/useSpreadSheet-spec.ts @@ -204,10 +204,7 @@ describe('useSpreadSheet tests', () => { rebuildDataSet: false, reloadData: false, }); - expect(onUpdateAfterRender).toHaveBeenCalledWith({ - rebuildDataSet: false, - reloadData: false, - }); + expect(onUpdateAfterRender).toHaveBeenCalledTimes(1); }); }); From d5a18c31a3ef87403d8d47ca39ec1d20c4d6ba11 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Tue, 11 Jun 2024 16:16:25 +0800 Subject: [PATCH 5/5] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/unit/hooks/usePivotSheetUpdate-spec.ts | 8 ++++---- s2-site/docs/api/basic-class/spreadsheet.en.md | 2 +- s2-site/docs/api/basic-class/spreadsheet.zh.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/s2-react/__tests__/unit/hooks/usePivotSheetUpdate-spec.ts b/packages/s2-react/__tests__/unit/hooks/usePivotSheetUpdate-spec.ts index 0cba612d5c..4cc8cf73a1 100644 --- a/packages/s2-react/__tests__/unit/hooks/usePivotSheetUpdate-spec.ts +++ b/packages/s2-react/__tests__/unit/hooks/usePivotSheetUpdate-spec.ts @@ -55,19 +55,19 @@ describe('usePivotSheetUpdate tests', () => { // 执行第一次,reload 为 true let callbackResult = result.current({ reloadData: false, - reBuildDataSet: false, + rebuildDataSet: false, }); - expect(callbackResult).toEqual({ reloadData: true, reBuildDataSet: false }); + expect(callbackResult).toEqual({ reloadData: true, rebuildDataSet: false }); // 执行第二次,reload 为 false callbackResult = result.current({ reloadData: false, - reBuildDataSet: false, + rebuildDataSet: false, }); expect(callbackResult).toEqual({ reloadData: false, - reBuildDataSet: false, + rebuildDataSet: false, }); }); }); diff --git a/s2-site/docs/api/basic-class/spreadsheet.en.md b/s2-site/docs/api/basic-class/spreadsheet.en.md index 66ed61acdc..7ea09c2b0d 100644 --- a/s2-site/docs/api/basic-class/spreadsheet.en.md +++ b/s2-site/docs/api/basic-class/spreadsheet.en.md @@ -40,7 +40,7 @@ s2.isPivotMode() | registerIcons | Register custom svg icons (according to `options.customSVGIcons` ) | `() => void` | | | setDataCfg | Update data configuration | (dataCfg: [S2DataConfig](/docs/api/general/S2DataConfig) , reset?: boolean ) => void | The `reset` parameter needs to be used in `@antv/s2^1.34.0` version | | setOptions | Update table configuration | (options: [S2Options](/docs/api/general/S2Options) , reset?: boolean) => void | The `reset` parameter needs to be used in `@antv/s2^1.34.0` version | -| render | Re-render the table, if `reloadData` = true, the data will be recalculated, `reBuildDataSet` = true, rebuild the data set, `reBuildHiddenColumnsDetail` = true rebuild hidden column information | `(reloadData?: boolean, { reBuildDataSet?: boolean; reBuildHiddenColumnsDetail?: boolean }) => Promise` | | +| render | Re-render the table, if `reloadData` = true, the data will be recalculated, `rebuildDataSet` = true, rebuild the data set, `reBuildHiddenColumnsDetail` = true rebuild hidden column information | `(reloadData?: boolean, { rebuildDataSet?: boolean; reBuildHiddenColumnsDetail?: boolean }) => Promise` | | | destroy | destroy form | `() => void` | | | setThemeCfg | Update theme configuration (including theme schema, color palette, theme name) | (themeCfg: [ThemeCfg](/docs/api/general/S2Theme/#themecfg) ) => void | | | setTheme | Update theme (only contains theme scheme) | (theme: [S2Theme](/docs/api/general/S2Theme/#s2theme) ) => void | | diff --git a/s2-site/docs/api/basic-class/spreadsheet.zh.md b/s2-site/docs/api/basic-class/spreadsheet.zh.md index 99975c9eed..a19dc4b525 100644 --- a/s2-site/docs/api/basic-class/spreadsheet.zh.md +++ b/s2-site/docs/api/basic-class/spreadsheet.zh.md @@ -43,7 +43,7 @@ s2.isPivotMode() | setOptions | 更新表格配置 | (options: [S2Options](/docs/api/general/S2Options), reset?: boolean) => void | `reset` 参数需在 `@antv/s2^1.34.0`版本使用 | | resetDataCfg | 重置表格数据 | () => void | | | resetOptions | 重置表格配置 | () => void | | -| render | 重新渲染表格,如果 `reloadData` = true, 则会重新计算数据,`reBuildDataSet` = true, 重新构建数据集,`reBuildHiddenColumnsDetail` = true 重新构建隐藏列信息 | `(reloadData?: boolean \| { reloadData?: boolean, reBuildDataSet?: boolean; reBuildHiddenColumnsDetail?: boolean }) => Promise` | | +| render | 重新渲染表格,如果 `reloadData` = true, 则会重新计算数据,`rebuildDataSet` = true, 重新构建数据集,`reBuildHiddenColumnsDetail` = true 重新构建隐藏列信息 | `(reloadData?: boolean \| { reloadData?: boolean, rebuildDataSet?: boolean; reBuildHiddenColumnsDetail?: boolean }) => Promise` | | | destroy | 销毁表格 | `() => void` | | | setThemeCfg | 更新主题配置 (含主题 schema, 色板,主题名) | (themeCfg: [ThemeCfg](/docs/api/general/S2Theme/#themecfg)) => void | | | setTheme | 更新主题 (只包含主题 scheme) | (theme: [S2Theme](/docs/api/general/S2Theme/#s2theme)) => void | |