From b2d6f1fb04d3fa73129669fc7d2dec84943252db Mon Sep 17 00:00:00 2001
From: Jinke Li
Date: Thu, 31 Aug 2023 16:07:57 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20headerActionIcons=20=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E7=BB=86=E7=B2=92=E5=BA=A6=E9=85=8D=E7=BD=AE=20&=20=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E5=BC=82=E6=AD=A5=E6=B8=B2=E6=9F=93=E5=AF=BC=E8=87=B4?=
=?UTF-8?q?=E6=97=A0=E6=B3=95=E8=8E=B7=E5=8F=96=E5=AE=9E=E4=BE=8B=E7=9A=84?=
=?UTF-8?q?=E9=97=AE=E9=A2=98=20(#2301)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: headerActionIcons 支持细粒度配置 & 修复异步渲染导致无法获取实例的问题
* test: fix
---
.../spreadsheet/header-action-icons-spec.ts | 297 ++++++++++++++++++
packages/s2-core/src/cell/base-cell.ts | 4 +-
packages/s2-core/src/cell/data-cell.ts | 4 +-
packages/s2-core/src/cell/header-cell.ts | 83 ++---
.../s2-core/src/common/interface/basic.ts | 108 +++++--
.../s2-core/src/common/interface/theme.ts | 2 +-
.../s2-core/src/utils/cell/header-cell.ts | 41 ++-
.../__snapshots__/index-spec.tsx.snap | 6 +-
.../sheets/mobile-sheet/index-spec.tsx | 6 +-
.../mobile-sheet/mobile-tooltip-spec.tsx | 6 +-
.../unit/components/tooltip/index-spec.tsx | 4 +-
packages/s2-react/playground/config.ts | 2 -
packages/s2-react/playground/index.tsx | 92 +-----
.../src/components/drill-down/index.tsx | 2 +-
.../components/sheets/base-sheet/index.tsx | 71 ++---
.../components/sheets/chart-sheet/index.tsx | 11 +-
.../drag-copy/drag-copy-mask.tsx | 4 +-
.../sheets/editable-sheet/drag-copy/index.tsx | 4 +-
.../sheets/editable-sheet/edit-cell/index.tsx | 24 +-
.../sheets/grid-analysis-sheet/index.tsx | 10 +-
.../s2-react/src/components/sheets/index.tsx | 9 +-
.../components/sheets/mobile-sheet/index.tsx | 20 +-
.../components/sheets/pivot-sheet/index.tsx | 23 +-
.../sheets/strategy-sheet/index.tsx | 2 -
.../components/sheets/table-sheet/index.tsx | 5 +-
.../src/context/SpreadSheetContext.tsx | 6 +-
packages/s2-shared/src/interface.ts | 6 +-
packages/s2-shared/src/styles/drill-down.less | 6 +-
packages/s2-shared/src/utils/drill-down.ts | 40 +--
.../demo/custom-header-action-icon.tsx | 66 ++--
30 files changed, 617 insertions(+), 347 deletions(-)
create mode 100644 packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts
diff --git a/packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts b/packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts
new file mode 100644
index 0000000000..993e89ef0d
--- /dev/null
+++ b/packages/s2-core/__tests__/spreadsheet/header-action-icons-spec.ts
@@ -0,0 +1,297 @@
+import { get, pick } from 'lodash';
+import { type SpreadSheet, type S2Options, OriginEventType } from '../../src';
+import { createFederatedMouseEvent, createPivotSheet } from '../util/helpers';
+
+const s2Options: S2Options = {
+ width: 600,
+ height: 400,
+};
+
+describe('HeaderActionIcons Tests', () => {
+ let s2: SpreadSheet;
+
+ beforeEach(async () => {
+ s2 = createPivotSheet(s2Options);
+ await s2.render();
+ });
+
+ afterEach(() => {
+ s2.destroy();
+ });
+
+ test('should render custom header action icons', async () => {
+ const rowCellDisplayConditionFn = jest.fn();
+ const colCellDisplayConditionFn = jest.fn();
+ const cornerCellDisplayConditionFn = jest.fn();
+
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: ['Plus'],
+ belongsCell: 'rowCell',
+ displayCondition: rowCellDisplayConditionFn,
+ },
+ {
+ icons: ['Trend'],
+ belongsCell: 'colCell',
+ displayCondition: colCellDisplayConditionFn,
+ },
+ {
+ icons: ['Minus'],
+ belongsCell: 'cornerCell',
+ displayCondition: cornerCellDisplayConditionFn,
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ [
+ ...s2.facet.getRowCells(),
+ ...s2.facet.getColCells(),
+ ...s2.facet.getCornerCells(),
+ ].forEach((cell) => {
+ expect(cell.getActionIcons()).toHaveLength(1);
+ });
+
+ expect(rowCellDisplayConditionFn).toHaveBeenCalled();
+ expect(colCellDisplayConditionFn).toHaveBeenCalled();
+ expect(cornerCellDisplayConditionFn).toHaveBeenCalled();
+ });
+
+ test('should custom icon fill color', async () => {
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: [
+ {
+ name: 'Plus',
+ position: 'right',
+ fill: 'red',
+ },
+ ],
+ belongsCell: 'rowCell',
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ s2.facet.getRowCells().forEach((cell) => {
+ expect(get(cell.getActionIcons()[0], 'cfg.fill')).toEqual('red');
+ });
+ });
+
+ test('should default hide icon', async () => {
+ const innerDefaultHide = jest.fn(() => true);
+ const defaultHide = jest.fn(() => false);
+
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: [
+ {
+ name: 'Plus',
+ position: 'right',
+ defaultHide: innerDefaultHide,
+ },
+ ],
+ belongsCell: 'rowCell',
+ defaultHide,
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ s2.facet.getRowCells().forEach((cell) => {
+ expect(cell.getActionIcons()[0].parsedStyle.visibility).toEqual('hidden');
+ });
+
+ expect(innerDefaultHide).toHaveBeenCalled();
+ expect(defaultHide).not.toHaveBeenCalled();
+ });
+
+ test('should not render icons by displayCondition', async () => {
+ const innerDisplayCondition = jest.fn(() => false);
+ const displayCondition = jest.fn(() => true);
+
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: [
+ {
+ name: 'Plus',
+ position: 'right',
+ displayCondition: innerDisplayCondition,
+ },
+ ],
+ belongsCell: 'rowCell',
+ displayCondition,
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ s2.facet.getRowCells().forEach((cell) => {
+ expect(cell.getActionIcons()).toBeEmpty();
+ });
+
+ expect(innerDisplayCondition).toHaveBeenCalled();
+ expect(displayCondition).not.toHaveBeenCalled();
+ });
+
+ test('should render multiple custom position', async () => {
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: [
+ {
+ name: 'Plus',
+ position: 'right',
+ },
+ {
+ name: 'Trend',
+ position: 'left',
+ },
+ ],
+ belongsCell: 'rowCell',
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ const positions = s2.facet.getRowCells().map((cell) => {
+ return cell
+ .getActionIcons()
+ .map((icon) => pick(icon.iconImageShape.attributes, ['x', 'y']));
+ });
+
+ expect(positions).toMatchInlineSnapshot(`
+ Array [
+ Array [
+ Object {
+ "x": 9,
+ "y": 24.5,
+ },
+ Object {
+ "x": 51,
+ "y": 24.5,
+ },
+ ],
+ Array [
+ Object {
+ "x": 158,
+ "y": 9.5,
+ },
+ Object {
+ "x": 200,
+ "y": 9.5,
+ },
+ ],
+ Array [
+ Object {
+ "x": 158,
+ "y": 39.5,
+ },
+ Object {
+ "x": 200,
+ "y": 39.5,
+ },
+ ],
+ ]
+ `);
+ });
+
+ test('should not render icons by displayCondition1', async () => {
+ const onPlusClick = jest.fn();
+ const onPlusHover = jest.fn();
+ const onTrendClick = jest.fn();
+ const onTrendHover = jest.fn();
+
+ const onClick = jest.fn();
+ const onHover = jest.fn();
+
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: [
+ {
+ name: 'Plus',
+ position: 'right',
+ onClick: onPlusClick,
+ onHover: onPlusHover,
+ },
+ {
+ name: 'Trend',
+ position: 'right',
+ onClick: onTrendClick,
+ onHover: onTrendHover,
+ },
+ ],
+ belongsCell: 'rowCell',
+ onClick,
+ onHover,
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ const events = [onTrendClick, onTrendHover, onClick, onHover];
+
+ const plusIcon = s2.facet.getRowCells()[0].getActionIcons()[0];
+
+ plusIcon.dispatchEvent(
+ createFederatedMouseEvent(s2, OriginEventType.CLICK),
+ );
+
+ events.forEach((fn) => {
+ expect(fn).not.toHaveBeenCalled();
+ });
+
+ expect(onPlusClick).toHaveBeenCalledTimes(1);
+ });
+
+ test('should render default right position', async () => {
+ s2.setOptions({
+ headerActionIcons: [
+ {
+ icons: ['Trend'],
+ belongsCell: 'rowCell',
+ },
+ ],
+ });
+
+ await s2.render(false);
+
+ const positions = s2.facet
+ .getRowCells()
+ .map((cell) => pick(cell.getActionIcons()[0], ['cfg.x', 'cfg.y']));
+
+ expect(positions).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "cfg": Object {
+ "x": 37,
+ "y": 24.5,
+ },
+ },
+ Object {
+ "cfg": Object {
+ "x": 186,
+ "y": 9.5,
+ },
+ },
+ Object {
+ "cfg": Object {
+ "x": 186,
+ "y": 39.5,
+ },
+ },
+ ]
+ `);
+ });
+});
diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts
index 6452f1ffea..333bf2940c 100644
--- a/packages/s2-core/src/cell/base-cell.ts
+++ b/packages/s2-core/src/cell/base-cell.ts
@@ -41,7 +41,7 @@ import {
CellClipBox,
CellBorderPosition,
type InteractionStateTheme,
- type FullyIconName,
+ type HeaderActionNameOptions,
type IconPosition,
type InternalFullyTheme,
type InternalFullyCellTheme,
@@ -607,7 +607,7 @@ export abstract class BaseCell extends Group {
};
}
- public getIconConditionResult(): FullyIconName | undefined {
+ public getIconConditionResult(): HeaderActionNameOptions | undefined {
const iconCondition = this.findFieldCondition(
this.conditions?.icon,
) as IconCondition;
diff --git a/packages/s2-core/src/cell/data-cell.ts b/packages/s2-core/src/cell/data-cell.ts
index a07ef22650..eac193a9db 100644
--- a/packages/s2-core/src/cell/data-cell.ts
+++ b/packages/s2-core/src/cell/data-cell.ts
@@ -9,7 +9,7 @@ import {
import {
CellBorderPosition,
CellClipBox,
- type FullyIconName,
+ type HeaderActionNameOptions,
type IconCondition,
type InteractionStateTheme,
} from '../common/interface';
@@ -241,7 +241,7 @@ export class DataCell extends BaseCell {
// 此时 name 是什么值都无所谓,因为后面会根据 mappingResult 来决定
name: '',
position: getIconPosition(iconCondition),
- } as FullyIconName);
+ } as HeaderActionNameOptions);
this.groupedIcons = groupIconsByPosition([], iconCfg);
}
diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts
index 496cabec00..e90fbd2658 100644
--- a/packages/s2-core/src/cell/header-cell.ts
+++ b/packages/s2-core/src/cell/header-cell.ts
@@ -1,6 +1,6 @@
import type {
- FederatedPointerEvent as CanvasEvent,
DisplayObject,
+ FederatedPointerEvent as CanvasEvent,
PointLike,
} from '@antv/g';
import {
@@ -15,7 +15,6 @@ import {
map,
merge,
} from 'lodash';
-import { renderIcon } from '../utils/g-renders';
import { BaseCell } from '../cell/base-cell';
import {
CellType,
@@ -25,14 +24,15 @@ import {
} from '../common/constant';
import { InteractionStateName } from '../common/constant/interaction';
import { GuiIcon } from '../common/icons';
+import type { GuiIconCfg } from '../common/icons/gui-icon';
import type {
CellMeta,
Condition,
+ ConditionMappingResult,
FormatResult,
- FullyIconName,
HeaderActionIconOptions,
+ HeaderActionNameOptions,
InternalFullyHeaderActionIcon,
- ConditionMappingResult,
TextTheme,
} from '../common/interface';
import type { BaseHeaderConfig } from '../facet/header';
@@ -42,6 +42,7 @@ import {
getActionIconConfig,
groupIconsByPosition,
} from '../utils/cell/header-cell';
+import { renderIcon } from '../utils/g-renders';
import { getSortTypeIcon } from '../utils/sort-action';
export abstract class HeaderCell extends BaseCell {
@@ -57,7 +58,7 @@ export abstract class HeaderCell extends BaseCell {
protected hasDefaultHiddenIcon: boolean;
- protected conditionIconMappingResult: FullyIconName | undefined;
+ protected conditionIconMappingResult: HeaderActionNameOptions | undefined;
/** left icon 绘制起始坐标 */
protected leftIconPosition: PointLike;
@@ -157,11 +158,9 @@ export abstract class HeaderCell extends BaseCell {
return false;
}
- protected getActionIconsCount() {
- return this.groupedIcons.left.length + this.groupedIcons.right.length;
- }
-
- protected getActionIconStyle() {
+ protected getActionIconStyle(
+ options: Partial,
+ ): Partial {
const { icon } = this.getStyle()!;
const conditionStyle = this.getTextConditionMappingResult();
const defaultTextFill = conditionStyle?.fill || this.getTextStyle().fill!;
@@ -169,8 +168,9 @@ export abstract class HeaderCell extends BaseCell {
return {
width: icon?.size,
height: icon?.size,
- // 主题 icon 颜色配置优先,若无则默认为文本条件格式颜色优先
- fill: icon?.fill || defaultTextFill,
+ // 优先级: 单个 icon 颜色配置 > 全部 icon 颜色配置 > 主题 icon 颜色配置 > 文本默认颜色
+ fill: options?.fill || icon?.fill || defaultTextFill,
+ cursor: 'pointer',
};
}
@@ -180,14 +180,13 @@ export abstract class HeaderCell extends BaseCell {
}
protected addActionIcon(options: HeaderActionIconOptions) {
- const { x, y, iconName, defaultHide, onClick, onHover, isSortIcon } =
- options;
+ const { x, y, name, defaultHide, onClick, onHover, isSortIcon } = options;
const icon = new GuiIcon({
- name: iconName,
+ ...this.getActionIconStyle(options),
+ name,
x,
y,
- ...this.getActionIconStyle(),
});
// 默认隐藏,hover 可见
@@ -197,7 +196,7 @@ export abstract class HeaderCell extends BaseCell {
this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_HOVER, event);
onHover?.({
hovering: true,
- iconName,
+ name,
meta: this.meta,
event,
});
@@ -207,27 +206,27 @@ export abstract class HeaderCell extends BaseCell {
this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_HOVER_OFF, event);
onHover?.({
hovering: false,
- iconName,
+ name,
meta: this.meta,
event,
});
});
- if (isSortIcon) {
- icon.addEventListener('click', (event: CanvasEvent) => {
- this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_CLICK, event);
+ icon.addEventListener('click', (event: CanvasEvent) => {
+ this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_CLICK, event);
+
+ if (isSortIcon) {
this.spreadsheet.handleGroupSort(event, this.meta);
+
+ return;
+ }
+
+ onClick?.({
+ name,
+ meta: this.meta,
+ event,
});
- } else {
- icon.addEventListener('click', (event: CanvasEvent) => {
- this.spreadsheet.emit(S2Event.GLOBAL_ACTION_ICON_CLICK, event);
- onClick?.({
- iconName,
- meta: this.meta,
- event,
- });
- });
- }
+ });
this.actionIcons.push(icon);
this.appendChild(icon);
@@ -257,10 +256,10 @@ export abstract class HeaderCell extends BaseCell {
this.conditionIconShape = renderIcon(this, {
x,
y,
- name: icon?.name!,
+ name: icon.name,
width: size,
height: size,
- fill: icon?.fill,
+ fill: icon.fill,
});
this.addConditionIconShape(this.conditionIconShape);
@@ -270,22 +269,24 @@ export abstract class HeaderCell extends BaseCell {
const { onClick, onHover, defaultHide, isSortIcon } =
this.actionIconConfig!;
+ const defaultHideHandler = icon.defaultHide ?? defaultHide;
const iconDefaultHide =
- typeof defaultHide === 'function'
- ? defaultHide(this.meta, icon.name)
- : defaultHide;
+ typeof defaultHideHandler === 'function'
+ ? defaultHideHandler(this.meta, icon.name)
+ : defaultHideHandler;
if (iconDefaultHide) {
this.hasDefaultHiddenIcon = true;
}
this.addActionIcon({
- iconName: icon.name,
+ name: icon.name,
+ fill: icon.fill,
x,
y,
defaultHide: iconDefaultHide,
- onClick,
- onHover,
+ onClick: icon.onClick ?? onClick,
+ onHover: icon.onHover ?? onHover,
isSortIcon,
});
});
@@ -451,4 +452,8 @@ export abstract class HeaderCell extends BaseCell {
public getTreeIcon() {
return this.treeIcon;
}
+
+ public getActionIcons() {
+ return this.actionIcons;
+ }
}
diff --git a/packages/s2-core/src/common/interface/basic.ts b/packages/s2-core/src/common/interface/basic.ts
index 4ddf65caec..2618198ea4 100644
--- a/packages/s2-core/src/common/interface/basic.ts
+++ b/packages/s2-core/src/common/interface/basic.ts
@@ -1,5 +1,5 @@
import type { FederatedPointerEvent as Event, Group } from '@antv/g';
-import type { CellType } from '../../common/constant';
+import type { MergedCell } from '../../cell';
import type {
CustomTreeNode,
Data,
@@ -13,13 +13,13 @@ import type { CellData } from '../../data-set/cell-data';
import type { BaseHeaderConfig, Frame } from '../../facet/header';
import type { Node } from '../../facet/layout/node';
import type { SpreadSheet } from '../../sheet-type';
-import type { MergedCell } from '../../cell';
+import type { CellType } from '../constant';
import type { S2CellType } from './interaction';
import type { DataItem } from './s2DataConfig';
export type { GetCellMeta, LayoutResult } from './facet';
-/*
+/**
* 第二个参数在以下情况会传入:
* 1. data cell 格式化
* 2. copy/export
@@ -228,19 +228,29 @@ export interface Pagination {
}
export interface CustomSVGIcon {
- /** icon 名称 */
+ /**
+ * icon 名称
+ */
name: string;
/**
- * 1、base 64
- * 2、svg本地文件(兼容老方式,可以改颜色)
- * 3、线上支持的图片地址
+ * @example 1、base64
+ * @example 2. svg本地文件 (兼容老方式, 可以改颜色)
+
+ import Icon from 'path/to/xxx.svg'
+
+ => { name: 'iconA', svg: Icon }
+ => { name: 'iconB', svg: '' }
+
+ * @example 3. 线上支持的图片地址
+ 带后缀: https://gw.alipayobjects.com/zos/antfincdn/gu1Fsz3fw0/filter%26sort_filter.svg
+ 无后缀: https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*5nsESLuvc_EAAAAAAAAAAAAADmJ7AQ/original
*/
svg: string;
}
export interface HeaderIconClickParams {
- iconName: string;
+ name: string;
meta: Node;
event?: Event;
}
@@ -251,44 +261,86 @@ export interface HeaderIconHoverParams extends HeaderIconClickParams {
hovering: boolean;
}
-export interface HeaderActionIconOptions {
- iconName: string;
+export interface HeaderActionIconOptions extends HeaderActionIconBaseOptions {
+ fill?: string;
+ name: string;
x: number;
y: number;
- onClick?: (headerIconClickParams: HeaderIconClickParams) => void;
- onHover?: (headerIconHoverParams: HeaderIconHoverParams) => void;
isSortIcon?: boolean;
- defaultHide?: boolean;
}
-export type FullyIconName = {
- name: string;
- position: IconPosition;
+export type HeaderActionNameOptions = HeaderActionIconBaseOptions & {
+ /**
+ * icon 颜色配置
+ * @description 优先级: 单个 icon > 主题 icon 配置 > 文本颜色
+ */
fill?: string;
+
+ /**
+ * icon 名称
+ */
+ name: string;
+
+ /**
+ * icon 相对文本的位置
+ * 可选: 'left' | 'right'
+ */
+ position?: IconPosition;
+
+ /**
+ * 是否是条件格式的 icon
+ */
isConditionIcon?: boolean;
};
-export type ActionIconName = string | FullyIconName;
-export interface HeaderActionIcon {
+export type HeaderActionName =
+ | string
+ | Omit;
+
+export interface HeaderActionIconBaseOptions {
/**
- * 已注册的 icon 类型或自定义的 icon 类型名
- * 如果是 string[], 则默认 icon 位置为右侧
+ * 是否默认隐藏, 开启后 hover 后才显示, 关闭后则始终显示, 可根据当前单元格信息动态判断
+ * @example defaultHide: (meta, iconName) => meta.id === 'xxx'
+ * @default false
*/
- icons: ActionIconName[];
- // 所属的 cell 类型
- belongsCell: Omit;
- /** 是否默认隐藏, true 为 hover后显示, false 为一直显示 */
defaultHide?: boolean | ((meta: Node, iconName: string) => boolean);
- /** 是否展示当前 iconNames 配置的 icon */
+
+ /**
+ * 是否展示, 可根据当前单元格信息动态判断
+ * @example displayCondition: (meta, iconName) => !meta.isTotals
+ */
displayCondition?: (mete: Node, iconName: string) => boolean;
- /** 点击回调函数 */
+
+ /**
+ * 点击回调函数
+ */
onClick?: (headerIconClickParams: HeaderIconClickParams) => void;
- /** 悬停回调函数 */
+
+ /**
+ * 悬停回调函数
+ */
onHover?: (headerIconHoverParams: HeaderIconHoverParams) => void;
}
+export interface HeaderActionIcon extends HeaderActionIconBaseOptions {
+ /**
+ * 内置 icon 或通过 @customSVGIcons 自定义的 icon 名称
+ * 如果是 string[], 则默认 icon 位置为右侧
+ * @see https://s2.antv.antgroup.com/manual/advanced/custom/custom-icon
+ * @example icons: ['iconNameA', 'iconNameB']
+ * @example icons: [{ name: 'iconNameA', position: "left", fill: "red" }]
+ */
+ icons: HeaderActionName[];
+
+ /**
+ * 所属的 cell 类型, 即当前 icon 展示在哪种类型单元格中
+ * @example belongsCell: 'rowCell'
+ */
+ belongsCell: Omit;
+}
+
export interface InternalFullyHeaderActionIcon extends HeaderActionIcon {
- icons: FullyIconName[];
+ icons: HeaderActionNameOptions[];
isSortIcon?: boolean;
}
diff --git a/packages/s2-core/src/common/interface/theme.ts b/packages/s2-core/src/common/interface/theme.ts
index bea0b7994f..5a39ef5db0 100644
--- a/packages/s2-core/src/common/interface/theme.ts
+++ b/packages/s2-core/src/common/interface/theme.ts
@@ -1,7 +1,7 @@
import type { LineStyleProps } from '@antv/g';
+import type { CellType } from '../../common/constant/interaction';
import type { InteractionStateName } from '../constant';
import type { PALETTE_MAP } from '../constant/theme';
-import type { CellType } from '../../common/constant/interaction';
import type { DeepRequired } from './util';
// 文本内容的水平对齐方式, 默认 left
diff --git a/packages/s2-core/src/utils/cell/header-cell.ts b/packages/s2-core/src/utils/cell/header-cell.ts
index 9e7758d1c6..f3968efb4b 100644
--- a/packages/s2-core/src/utils/cell/header-cell.ts
+++ b/packages/s2-core/src/utils/cell/header-cell.ts
@@ -1,20 +1,20 @@
import { find, groupBy, isEmpty, isEqual, merge } from 'lodash';
import type {
- FullyIconName,
+ HeaderActionNameOptions,
IconPosition,
IconTheme,
InternalFullyHeaderActionIcon,
} from '../../common/interface';
import { CellType, EXTRA_FIELD } from '../../common/constant';
import type {
- ActionIconName,
+ HeaderActionName,
FormatResult,
HeaderActionIcon,
} from '../../common/interface/basic';
import type { Node } from '../../facet/layout/node';
const normalizeIcons = (
- icons: ActionIconName[],
+ icons: HeaderActionName[],
position: IconPosition = 'right',
) =>
icons.map((icon) => {
@@ -53,13 +53,13 @@ const shouldShowActionIcons = (
return false;
}
- if (!displayCondition) {
- // 没有展示条件参数默认全展示
- return true;
- }
-
// 有任意 iconName 命中展示,则使用当前 headerActionIcon config
- return icons.some((icon) => displayCondition(meta, icon.name));
+ return icons.some(
+ (icon) =>
+ icon.displayCondition?.(meta, icon.name) ??
+ displayCondition?.(meta, icon.name) ??
+ true,
+ );
};
/**
@@ -85,22 +85,21 @@ export const getActionIconConfig = (
}
// 使用配置的 displayCondition 进一步筛选需要展示的 icon
- let nextIcons = iconConfig.icons;
-
- if (iconConfig.displayCondition) {
- nextIcons = nextIcons.filter((iconName) =>
- iconConfig.displayCondition?.(meta, iconName.name),
- );
- }
+ const displayIcons = iconConfig.icons.filter(
+ (icon) =>
+ icon.displayCondition?.(meta, icon.name) ??
+ iconConfig.displayCondition?.(meta, icon.name) ??
+ true,
+ );
return {
...iconConfig,
- icons: nextIcons,
+ icons: displayIcons,
};
};
export const getIconTotalWidth = (
- icons: FullyIconName[] = [],
+ icons: HeaderActionNameOptions[] = [],
iconTheme: IconTheme,
): number => {
if (isEmpty(icons)) {
@@ -117,12 +116,12 @@ export const getIconTotalWidth = (
};
export type GroupedIcons = {
- [key in IconPosition]: FullyIconName[];
+ [key in IconPosition]: HeaderActionNameOptions[];
};
export const groupIconsByPosition = (
- icons: FullyIconName[] = [],
- conditionIcon?: FullyIconName,
+ icons: HeaderActionNameOptions[] = [],
+ conditionIcon?: HeaderActionNameOptions,
) => {
const groupedIcons = merge(
{
diff --git a/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap b/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap
index c92cd07497..cf15fc38d6 100644
--- a/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap
+++ b/packages/s2-react/__tests__/unit/components/drill-down/__snapshots__/index-spec.tsx.snap
@@ -65,6 +65,11 @@ exports[`DrillDown Component Tests should render component 1`] = `
+
@@ -138,7 +143,6 @@ exports[`DrillDown Component Tests should render component 1`] = `
No data
- extra
}>