Skip to content

Commit ca10cce

Browse files
authored
Merge branch 'master' into fix/issue-56713-stable-header-width
2 parents 573721c + 7977f2b commit ca10cce

11 files changed

Lines changed: 551 additions & 23 deletions

File tree

‎README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,15 @@ Then open `http://localhost:8000`.
8484
| expandable.expandedRowRender | Function(recode, index, indent, expanded):ReactNode | | Content render to expanded row |
8585
| expandable.forceRender | Boolean | false | Force render expanded row content before expansion. In virtual mode, only rows currently mounted by the virtual list are force-rendered; off-screen rows may still be unmounted |
8686
| expandable.expandedRowClassName | `string` \| `(recode, index, indent) => string` | | get expanded row's className |
87+
| expandable.columnTitle | ReactNode \| Function({ expandIcon }) | | Customize expand column title |
8788
| expandable.expandRowByClick | boolean | | Support expand by click row |
8889
| expandable.expandIconColumnIndex | Number | 0 | The index of expandIcon which column will be inserted when expandIconAsCell is false |
89-
| expandable.expandIcon | props => ReactNode | | Customize expand icon |
90+
| expandable.expandIcon | props => ReactNode | | Deprecated. Use `components.ExpandIcon`; retained as a row-only fallback |
9091
| expandable.indentSize | Number | 15 | indentSize for every level of data.i.children, better using with column.width specified |
9192
| expandable.rowExpandable | (record) => boolean | | Config row support expandable |
93+
| expandable.showExpandAll | Boolean | false | Show expand all icon in the expand column header when using `expandedRowRender` |
9294
| expandable.onExpand | Function(expanded, record) | | function to call when click expand icon |
95+
| expandable.onExpandAll | Function(expanded) | | function to call when click expand all icon |
9396
| expandable.onExpandedRowsChange | Function(expandedRows) | | function to call when the expanded rows change |
9497
| expandable.fixed | String \| Boolean | - | this expand icon will be fixed when table scroll horizontally: true or `left` or `right` and `expandIconColumnIndex` need to stay first or last |
9598
| rowKey | string or Function(record, index):string | 'key' | If rowKey is string, `record[rowKey]` will be used as key. If rowKey is function, the return value of `rowKey(record, index)` will be use as key. |
@@ -105,6 +108,7 @@ Then open `http://localhost:8000`.
105108
| emptyText | React.Node or Function | `No Data` | Display text when data is empty |
106109
| columns | Object[] | | The columns config of table, see table below |
107110
| components | Object | | Override table elements, see [#171](https://github.com/react-component/table/pull/171) for more details |
111+
| components.ExpandIcon | `React.ComponentType<ExpandIconProps>` | | Customize both row and expand-all icons. Receives `type` (`row` or `all`), `prefixCls`, `expanded`, `expandable`, `onClick`, and `record` for rows only |
108112
| sticky | boolean \| {offsetHeader?: number, offsetScroll?: number, getContainer?: () => Window \| HTMLElement } | false | stick header and scroll bar |
109113
| summary | (data: readonly RecordType[]) => React.ReactNode | - | `summary` attribute in `table` component is used to define the summary row. |
110114
| rowHoverable | boolean | true | Table hover interaction |

‎README.zh-CN.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,15 @@ npm start
8484
| expandable.expandedRowRender | Function(recode, index, indent, expanded):ReactNode | | 内容渲染到扩展行 |
8585
| expandable.forceRender | Boolean | false | 在展开前强制渲染展开行内容。虚拟模式下,仅强制渲染虚拟列表当前挂载的行;屏幕外的行仍可能被卸载 |
8686
| expandable.expandedRowClassName | `string` \| `(recode, index, indent) => string` | | 获取扩展行的 className |
87+
| expandable.columnTitle | ReactNode \| Function({ expandIcon }) | | 自定义展开列表头 |
8788
| expandable.expandRowByClick | boolean | | 支持点击行展开 |
8889
| expandable.expandIconColumnIndex | Number | 0 | ExpandIconAsCell 为 false 时将插入哪一列的 ExpandIcon 索引 |
89-
| expandable.expandIcon | props => ReactNode | | 自定义展开图标 |
90+
| expandable.expandIcon | props => ReactNode | | 已废弃。请使用 `components.ExpandIcon`;此配置仅作为行展开图标的后备方案保留 |
9091
| expandable.indentSize | Number | 15 | 每一级 `data[i].children` 的缩进尺寸,建议配合指定的 `column.width` 使用 |
9192
| expandable.rowExpandable | (record) => boolean | | 配置行支持可扩展 |
93+
| expandable.showExpandAll | Boolean | false | 使用 `expandedRowRender` 时在展开列的表头中显示全部展开图标 |
9294
| expandable.onExpand | Function(expanded, record) | | 单击展开图标时调用的函数 |
95+
| expandable.onExpandAll | Function(expanded) | | 单击全部展开图标时调用的函数 |
9396
| expandable.onExpandedRowsChange | Function(expandedRows) | | 扩展行更改时调用的函数 |
9497
| expandable.fixed | String \| Boolean | - | 当表格水平滚动时,此展开图标将被修复: true 或 `left` 或 `right` 和 `expandIconColumnIndex` 需要保留在第一个或最后一个 |
9598
| rowKey | string or Function(record, index):string | 'key' | 如果 rowKey 是字符串,则 `record[rowKey]` 将用作键。如果 rowKey 是函数,则 `rowKey(record, index)` 的返回值将用作 key。 |
@@ -105,6 +108,7 @@ npm start
105108
| emptyText | React.Node or Function | `No Data` | 数据为空时显示文本 |
106109
| columns | Object[] | | 表的列配置见下表 |
107110
| components | Object | | 覆盖表元素,请参阅 [#171](https://github.com/react-component/table/pull/171) 了解更多详细信息 |
111+
| components.ExpandIcon | `React.ComponentType<ExpandIconProps>` | | 自定义行展开和全部展开图标。接收 `type`(`row` 或 `all`)、`prefixCls`、`expanded`、`expandable`、`onClick`,仅行图标接收 `record` |
108112
| sticky | boolean \| {offsetHeader?: number, offsetScroll?: number, getContainer?: () => Window \| HTMLElement } | false | 粘贴标题和滚动条 |
109113
| summary | (data: readonly RecordType[]) => React.ReactNode | - | `table` 组件中的 `summary` 属性用于定义汇总行。 |
110114
| rowHoverable | boolean | true | 表格 hover 交互 |

‎assets/index.less‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,18 @@
230230
}
231231

232232
&-row-expand-icon {
233+
box-sizing: border-box;
233234
display: inline-block;
234235
width: 16px;
235236
height: 16px;
237+
padding: 0;
236238
color: #aaa;
239+
font: inherit;
237240
line-height: 16px;
238241
text-align: center;
239242
vertical-align: middle;
243+
appearance: none;
244+
background: transparent;
240245
border: 1px solid currentColor;
241246
cursor: pointer;
242247

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rc-component/table",
3-
"version": "1.11.1",
3+
"version": "1.12.0",
44
"description": "📋 Data table component for React",
55
"engines": {
66
"node": ">=8.x"

‎src/Table.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ const Table = <RecordType extends DefaultRecordType>(
314314
mergedExpandIcon,
315315
mergedChildrenColumnName,
316316
onTriggerExpand,
317+
expandAllInfo,
317318
] = useExpand(props, mergedData, getRowKey);
318319

319320
// ====================== Column ======================
@@ -331,6 +332,8 @@ const Table = <RecordType extends DefaultRecordType>(
331332
// https://github.com/ant-design/ant-design/issues/23894
332333
onTriggerExpand,
333334
expandIcon: mergedExpandIcon,
335+
ExpandIcon: components?.ExpandIcon,
336+
expandAllInfo,
334337
expandIconColumnIndex: expandableConfig.expandIconColumnIndex,
335338
direction,
336339
scrollWidth: useInternalHooks && tailor && typeof scrollX === 'number' ? scrollX : null,

‎src/hooks/useColumns/index.tsx‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import type {
66
ColumnsType,
77
ColumnType,
88
Direction,
9+
ExpandColumnTitle,
10+
ExpandIconComponent,
11+
ExpandIconProps,
912
FixedType,
1013
GetRowKey,
1114
Key,
1215
RenderExpandIcon,
1316
TriggerEventHandler,
1417
} from '../../interface';
18+
import { DefaultExpandIcon } from '../../utils/expandUtil';
1519
import { INTERNAL_COL_DEFINE } from '../../utils/legacyUtil';
1620
import useWidthColumns from './useWidthColumns';
1721

@@ -102,6 +106,8 @@ function useColumns<RecordType>(
102106
getRowKey,
103107
onTriggerExpand,
104108
expandIcon,
109+
ExpandIcon,
110+
expandAllInfo,
105111
rowExpandable,
106112
expandIconColumnIndex,
107113
expandedRowOffset = 0,
@@ -117,10 +123,12 @@ function useColumns<RecordType>(
117123
children?: React.ReactNode;
118124
expandable: boolean;
119125
expandedKeys: Set<Key>;
120-
columnTitle?: React.ReactNode;
126+
columnTitle?: ExpandColumnTitle;
121127
getRowKey: GetRowKey<RecordType>;
122128
onTriggerExpand: TriggerEventHandler<RecordType>;
123-
expandIcon?: RenderExpandIcon<RecordType>;
129+
expandIcon: RenderExpandIcon<RecordType>;
130+
ExpandIcon?: ExpandIconComponent<RecordType>;
131+
expandAllInfo?: Pick<ExpandIconProps<RecordType>, 'expanded' | 'expandable' | 'onClick'>;
124132
rowExpandable?: (record: RecordType) => boolean;
125133
expandIconColumnIndex?: number;
126134
direction?: Direction;
@@ -190,13 +198,22 @@ function useColumns<RecordType>(
190198
fixedColumn = prevColumn ? prevColumn.fixed : null;
191199
}
192200

201+
const MergedExpandIcon = ExpandIcon || DefaultExpandIcon;
202+
const expandAllNode = expandAllInfo ? (
203+
<MergedExpandIcon type="all" prefixCls={prefixCls} {...expandAllInfo} />
204+
) : undefined;
205+
const mergedColumnTitle =
206+
typeof columnTitle === 'function'
207+
? columnTitle({ expandIcon: expandAllNode })
208+
: (columnTitle ?? expandAllNode);
209+
193210
// >>> Create expandable column
194211
const expandColumn = {
195212
[INTERNAL_COL_DEFINE]: {
196213
className: `${prefixCls}-expand-icon-col`,
197214
columnType: 'EXPAND_COLUMN',
198215
},
199-
title: columnTitle,
216+
title: mergedColumnTitle,
200217
fixed: fixedColumn,
201218
className: `${prefixCls}-row-expand-icon-cell`,
202219
width: columnWidth,
@@ -238,7 +255,18 @@ function useColumns<RecordType>(
238255

239256
return baseColumns.filter(col => col !== EXPAND_COLUMN);
240257
// eslint-disable-next-line react-hooks/exhaustive-deps
241-
}, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction, expandedRowOffset]);
258+
}, [
259+
expandable,
260+
baseColumns,
261+
getRowKey,
262+
expandedKeys,
263+
expandIcon,
264+
ExpandIcon,
265+
expandAllInfo,
266+
columnTitle,
267+
direction,
268+
expandedRowOffset,
269+
]);
242270

243271
// ========================= Transform ========================
244272
const mergedColumns = React.useMemo(() => {

‎src/hooks/useExpand.ts‎

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
import { warning } from '@rc-component/util';
1+
import { useEvent, warning } from '@rc-component/util';
22
import * as React from 'react';
33
import { INTERNAL_HOOKS } from '../constant';
44
import type {
55
ExpandableConfig,
66
ExpandableType,
7+
ExpandIconProps,
78
GetRowKey,
89
Key,
910
RenderExpandIcon,
1011
TriggerEventHandler,
1112
} from '../interface';
1213
import type { TableProps } from '../Table';
13-
import { findAllChildrenKeys, renderExpandIcon } from '../utils/expandUtil';
14+
import { findAllChildrenKeys, renderExpandIcon, renderRowExpandIcon } from '../utils/expandUtil';
1415
import { getExpandableProps } from '../utils/legacyUtil';
1516

17+
type ExpandAllInfo<RecordType> = Pick<
18+
ExpandIconProps<RecordType>,
19+
'expanded' | 'expandable' | 'onClick'
20+
>;
21+
1622
export default function useExpand<RecordType>(
1723
props: TableProps<RecordType>,
1824
mergedData: readonly RecordType[],
@@ -24,6 +30,7 @@ export default function useExpand<RecordType>(
2430
expandIcon: RenderExpandIcon<RecordType>,
2531
childrenColumnName: string,
2632
onTriggerExpand: TriggerEventHandler<RecordType>,
33+
expandAllInfo: ExpandAllInfo<RecordType> | undefined,
2734
] {
2835
const expandableConfig = getExpandableProps(props);
2936

@@ -34,11 +41,22 @@ export default function useExpand<RecordType>(
3441
defaultExpandAllRows,
3542
expandedRowRender,
3643
onExpand,
44+
onExpandAll,
3745
onExpandedRowsChange,
3846
childrenColumnName,
47+
rowExpandable,
48+
showExpandAll,
3949
} = expandableConfig;
4050

41-
const mergedExpandIcon = expandIcon || renderExpandIcon;
51+
const customizeExpandIcon = props.components?.ExpandIcon;
52+
const componentExpandIcon = React.useMemo<RenderExpandIcon<RecordType> | undefined>(
53+
() =>
54+
customizeExpandIcon
55+
? iconProps => renderRowExpandIcon(customizeExpandIcon, iconProps)
56+
: undefined,
57+
[customizeExpandIcon],
58+
);
59+
const mergedExpandIcon = componentExpandIcon || expandIcon || renderExpandIcon;
4260
const mergedChildrenColumnName = childrenColumnName || 'children';
4361
const expandableType = React.useMemo<ExpandableType>(() => {
4462
if (expandedRowRender) {
@@ -81,6 +99,30 @@ export default function useExpand<RecordType>(
8199
[expandedRowKeys, innerExpandedKeys],
82100
);
83101

102+
const expandableRows = React.useMemo(() => {
103+
if (!showExpandAll || expandableType !== 'row') {
104+
return [];
105+
}
106+
107+
return mergedData.reduce<{ key: Key; record: RecordType }[]>((rows, record, index) => {
108+
if (!rowExpandable || rowExpandable(record)) {
109+
rows.push({
110+
key: getRowKey(record, index),
111+
record,
112+
});
113+
}
114+
return rows;
115+
}, []);
116+
// `rowExpandable` is intentionally omitted because it is commonly an unstable inline function.
117+
// eslint-disable-next-line react-hooks/exhaustive-deps
118+
}, [expandableType, getRowKey, mergedData, showExpandAll]);
119+
120+
const allExpanded = React.useMemo(
121+
() =>
122+
expandableRows.length > 0 && expandableRows.every(({ key }) => mergedExpandedKeys.has(key)),
123+
[expandableRows, mergedExpandedKeys],
124+
);
125+
84126
const onTriggerExpand: TriggerEventHandler<RecordType> = React.useCallback(
85127
(record: RecordType) => {
86128
const key = getRowKey(record, mergedData.indexOf(record));
@@ -105,6 +147,41 @@ export default function useExpand<RecordType>(
105147
[getRowKey, mergedExpandedKeys, mergedData, onExpand, onExpandedRowsChange],
106148
);
107149

150+
const onTriggerExpandAll: React.MouseEventHandler<HTMLElement> = useEvent(event => {
151+
event.stopPropagation();
152+
if (!expandableRows.length) {
153+
return;
154+
}
155+
156+
const nextExpanded = !allExpanded;
157+
const nextExpandedKeys = new Set(mergedExpandedKeys);
158+
159+
expandableRows.forEach(({ key }) => {
160+
if (nextExpanded) {
161+
nextExpandedKeys.add(key);
162+
} else {
163+
nextExpandedKeys.delete(key);
164+
}
165+
});
166+
167+
const keys = [...nextExpandedKeys];
168+
setInnerExpandedKeys(keys);
169+
onExpandAll?.(nextExpanded);
170+
onExpandedRowsChange?.(keys);
171+
});
172+
173+
const expandAllInfo = React.useMemo<ExpandAllInfo<RecordType> | undefined>(
174+
() =>
175+
showExpandAll && expandableType === 'row'
176+
? {
177+
expanded: allExpanded,
178+
expandable: expandableRows.length > 0,
179+
onClick: onTriggerExpandAll,
180+
}
181+
: undefined,
182+
[allExpanded, expandableRows.length, expandableType, onTriggerExpandAll, showExpandAll],
183+
);
184+
108185
// Warning if use `expandedRowRender` and nest children in the same time
109186
if (
110187
process.env.NODE_ENV !== 'production' &&
@@ -123,5 +200,6 @@ export default function useExpand<RecordType>(
123200
mergedExpandIcon,
124201
mergedChildrenColumnName,
125202
onTriggerExpand,
203+
expandAllInfo,
126204
];
127205
}

‎src/index.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
ColumnType,
55
ColumnsType,
66
DefaultRecordType,
7+
ExpandIconProps,
78
ExpandableConfig,
89
FixedType,
910
GetComponentProps,
@@ -37,6 +38,7 @@ export {
3738
type ColumnType,
3839
type ColumnsType,
3940
type DefaultRecordType,
41+
type ExpandIconProps,
4042
type ExpandableConfig,
4143
type FixedType,
4244
type GetComponentProps,

0 commit comments

Comments
 (0)