Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,16 @@ Then open `http://localhost:8000`.
| expandable.expandedRowRender | Function(recode, index, indent, expanded):ReactNode | | Content render to expanded row |
| 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 |
| expandable.expandedRowClassName | `string` \| `(recode, index, indent) => string` | | get expanded row's className |
| expandable.columnTitle | ReactNode \| Function(originalNode) | | Customize expand column title |
| expandable.expandRowByClick | boolean | | Support expand by click row |
| expandable.expandIconColumnIndex | Number | 0 | The index of expandIcon which column will be inserted when expandIconAsCell is false |
| expandable.expandIcon | props => ReactNode | | Customize expand icon |
| expandable.expandAllIcon | props => ReactNode | | Customize the icon rendered when `showExpandAll` is enabled |
| expandable.indentSize | Number | 15 | indentSize for every level of data.i.children, better using with column.width specified |
| expandable.rowExpandable | (record) => boolean | | Config row support expandable |
| expandable.showExpandAll | Boolean | false | Show expand all icon in the expand column header when using `expandedRowRender` |
| expandable.onExpand | Function(expanded, record) | | function to call when click expand icon |
| expandable.onExpandAll | Function(expanded, records) | | function to call when click expand all icon |
| expandable.onExpandedRowsChange | Function(expandedRows) | | function to call when the expanded rows change |
| 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 |
| 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. |
Expand Down
4 changes: 4 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,16 @@ npm start
| expandable.expandedRowRender | Function(recode, index, indent, expanded):ReactNode | | 内容渲染到扩展行 |
| expandable.forceRender | Boolean | false | 在展开前强制渲染展开行内容。虚拟模式下,仅强制渲染虚拟列表当前挂载的行;屏幕外的行仍可能被卸载 |
| expandable.expandedRowClassName | `string` \| `(recode, index, indent) => string` | | 获取扩展行的 className |
| expandable.columnTitle | ReactNode \| Function(originalNode) | | 自定义展开列表头 |
| expandable.expandRowByClick | boolean | | 支持点击行展开 |
| expandable.expandIconColumnIndex | Number | 0 | ExpandIconAsCell 为 false 时将插入哪一列的 ExpandIcon 索引 |
| expandable.expandIcon | props => ReactNode | | 自定义展开图标 |
| expandable.expandAllIcon | props => ReactNode | | 自定义启用 `showExpandAll` 时显示的全部展开图标 |
| expandable.indentSize | Number | 15 | 每一级 `data[i].children` 的缩进尺寸,建议配合指定的 `column.width` 使用 |
| expandable.rowExpandable | (record) => boolean | | 配置行支持可扩展 |
| expandable.showExpandAll | Boolean | false | 使用 `expandedRowRender` 时在展开列的表头中显示全部展开图标 |
| expandable.onExpand | Function(expanded, record) | | 单击展开图标时调用的函数 |
| expandable.onExpandAll | Function(expanded, records) | | 单击全部展开图标时调用的函数 |
| expandable.onExpandedRowsChange | Function(expandedRows) | | 扩展行更改时调用的函数 |
| expandable.fixed | String \| Boolean | - | 当表格水平滚动时,此展开图标将被修复: true 或 `left` 或 `right` 和 `expandIconColumnIndex` 需要保留在第一个或最后一个 |
| rowKey | string or Function(record, index):string | 'key' | 如果 rowKey 是字符串,则 `record[rowKey]` 将用作键。如果 rowKey 是函数,则 `rowKey(record, index)` 的返回值将用作 key。 |
Expand Down
5 changes: 5 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,18 @@
}

&-row-expand-icon {
box-sizing: inherit;
display: inline-block;
width: 16px;
height: 16px;
padding: 0;
color: #aaa;
font: inherit;
line-height: 16px;
text-align: center;
vertical-align: middle;
appearance: none;
background: transparent;
border: 1px solid currentColor;
cursor: pointer;

Expand Down
4 changes: 4 additions & 0 deletions src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,10 @@ const Table = <RecordType extends DefaultRecordType>(
expandableType,
mergedExpandedKeys,
mergedExpandIcon,
mergedExpandAllIcon,
mergedChildrenColumnName,
onTriggerExpand,
expandAllInfo,
] = useExpand(props, mergedData, getRowKey);

// ====================== Column ======================
Expand All @@ -331,6 +333,8 @@ const Table = <RecordType extends DefaultRecordType>(
// https://github.com/ant-design/ant-design/issues/23894
onTriggerExpand,
expandIcon: mergedExpandIcon,
expandAllIcon: mergedExpandAllIcon,
expandAllInfo,
expandIconColumnIndex: expandableConfig.expandIconColumnIndex,
direction,
scrollWidth: useInternalHooks && tailor && typeof scrollX === 'number' ? scrollX : null,
Expand Down
32 changes: 29 additions & 3 deletions src/hooks/useColumns/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type {
FixedType,
GetRowKey,
Key,
RenderExpandAllIcon,
RenderExpandAllIconProps,
RenderExpandIcon,
TriggerEventHandler,
} from '../../interface';
Expand Down Expand Up @@ -102,6 +104,8 @@ function useColumns<RecordType>(
getRowKey,
onTriggerExpand,
expandIcon,
expandAllIcon,
expandAllInfo,
rowExpandable,
expandIconColumnIndex,
expandedRowOffset = 0,
Expand All @@ -117,10 +121,12 @@ function useColumns<RecordType>(
children?: React.ReactNode;
expandable: boolean;
expandedKeys: Set<Key>;
columnTitle?: React.ReactNode;
columnTitle?: React.ReactNode | ((originalNode: React.ReactNode) => React.ReactNode);
getRowKey: GetRowKey<RecordType>;
onTriggerExpand: TriggerEventHandler<RecordType>;
expandIcon?: RenderExpandIcon<RecordType>;
expandAllIcon?: RenderExpandAllIcon;
expandAllInfo: Omit<RenderExpandAllIconProps, 'prefixCls'>;
rowExpandable?: (record: RecordType) => boolean;
expandIconColumnIndex?: number;
direction?: Direction;
Expand Down Expand Up @@ -190,13 +196,22 @@ function useColumns<RecordType>(
fixedColumn = prevColumn ? prevColumn.fixed : null;
}

const expandAllNode = expandAllIcon?.({
prefixCls,
...expandAllInfo,
});
const mergedColumnTitle =
typeof columnTitle === 'function'
? columnTitle(expandAllNode)
: (columnTitle ?? expandAllNode);

// >>> Create expandable column
const expandColumn = {
[INTERNAL_COL_DEFINE]: {
className: `${prefixCls}-expand-icon-col`,
columnType: 'EXPAND_COLUMN',
},
title: columnTitle,
title: mergedColumnTitle,
fixed: fixedColumn,
className: `${prefixCls}-row-expand-icon-cell`,
width: columnWidth,
Expand Down Expand Up @@ -238,7 +253,18 @@ function useColumns<RecordType>(

return baseColumns.filter(col => col !== EXPAND_COLUMN);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction, expandedRowOffset]);
}, [
expandable,
baseColumns,
getRowKey,
expandedKeys,
expandIcon,
expandAllIcon,
expandAllInfo,
columnTitle,
direction,
expandedRowOffset,
]);

// ========================= Transform ========================
const mergedColumns = React.useMemo(() => {
Expand Down
67 changes: 66 additions & 1 deletion src/hooks/useExpand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import type {
ExpandableType,
GetRowKey,
Key,
RenderExpandAllIcon,
RenderExpandAllIconProps,
RenderExpandIcon,
TriggerEventHandler,
} from '../interface';
import type { TableProps } from '../Table';
import { findAllChildrenKeys, renderExpandIcon } from '../utils/expandUtil';
import { findAllChildrenKeys, renderExpandAllIcon, renderExpandIcon } from '../utils/expandUtil';
import { getExpandableProps } from '../utils/legacyUtil';

export default function useExpand<RecordType>(
Expand All @@ -22,20 +24,26 @@ export default function useExpand<RecordType>(
expandableType: ExpandableType,
expandedKeys: Set<Key>,
expandIcon: RenderExpandIcon<RecordType>,
expandAllIcon: RenderExpandAllIcon | undefined,
childrenColumnName: string,
onTriggerExpand: TriggerEventHandler<RecordType>,
expandAllInfo: Omit<RenderExpandAllIconProps, 'prefixCls'>,
] {
const expandableConfig = getExpandableProps(props);

const {
expandIcon,
expandAllIcon,
expandedRowKeys,
defaultExpandedRowKeys,
defaultExpandAllRows,
expandedRowRender,
onExpand,
onExpandAll,
onExpandedRowsChange,
childrenColumnName,
rowExpandable,
showExpandAll,
} = expandableConfig;

const mergedExpandIcon = expandIcon || renderExpandIcon;
Expand Down Expand Up @@ -66,6 +74,8 @@ export default function useExpand<RecordType>(
/* eslint-enable */
return false;
}, [!!expandedRowRender, mergedData]);
const mergedExpandAllIcon =
showExpandAll && expandableType === 'row' ? expandAllIcon || renderExpandAllIcon : undefined;

const [innerExpandedKeys, setInnerExpandedKeys] = React.useState(() => {
if (defaultExpandedRowKeys) {
Expand All @@ -81,6 +91,25 @@ export default function useExpand<RecordType>(
[expandedRowKeys, innerExpandedKeys],
);

const expandableRows = React.useMemo(() => {
if (!showExpandAll || expandableType !== 'row') {
return [];
}

return mergedData.reduce<{ key: Key; record: RecordType }[]>((rows, record, index) => {
if (!rowExpandable || rowExpandable(record)) {
rows.push({
key: getRowKey(record, index),
record,
});
}
return rows;
}, []);
}, [expandableType, getRowKey, mergedData, rowExpandable, showExpandAll]);

const allExpanded =
expandableRows.length > 0 && expandableRows.every(({ key }) => mergedExpandedKeys.has(key));

const onTriggerExpand: TriggerEventHandler<RecordType> = React.useCallback(
(record: RecordType) => {
const key = getRowKey(record, mergedData.indexOf(record));
Expand All @@ -105,6 +134,40 @@ export default function useExpand<RecordType>(
[getRowKey, mergedExpandedKeys, mergedData, onExpand, onExpandedRowsChange],
);

const onTriggerExpandAll: React.MouseEventHandler<HTMLElement> = React.useCallback(() => {
if (!expandableRows.length) {
return;
}

const nextExpanded = !allExpanded;
const nextExpandedKeys = new Set(mergedExpandedKeys);

expandableRows.forEach(({ key }) => {
if (nextExpanded) {
nextExpandedKeys.add(key);
} else {
nextExpandedKeys.delete(key);
}
});

const keys = [...nextExpandedKeys];
setInnerExpandedKeys(keys);
onExpandAll?.(
nextExpanded,
expandableRows.map(({ record }) => record),
);
onExpandedRowsChange?.(keys);
}, [allExpanded, expandableRows, mergedExpandedKeys, onExpandAll, onExpandedRowsChange]);

const expandAllInfo = React.useMemo<Omit<RenderExpandAllIconProps, 'prefixCls'>>(
() => ({
expanded: allExpanded,
expandable: expandableRows.length > 0,
onExpand: onTriggerExpandAll,
}),
[allExpanded, expandableRows.length, onTriggerExpandAll],
);

// Warning if use `expandedRowRender` and nest children in the same time
if (
process.env.NODE_ENV !== 'production' &&
Expand All @@ -121,7 +184,9 @@ export default function useExpand<RecordType>(
expandableType,
mergedExpandedKeys,
mergedExpandIcon,
mergedExpandAllIcon,
mergedChildrenColumnName,
onTriggerExpand,
expandAllInfo,
];
}
14 changes: 13 additions & 1 deletion src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,21 +249,33 @@ export type RenderExpandIcon<RecordType> = (
props: RenderExpandIconProps<RecordType>,
) => React.ReactNode;

export interface RenderExpandAllIconProps {
prefixCls: string;
expanded: boolean;
expandable: boolean;
onExpand: React.MouseEventHandler<HTMLElement>;
}

export type RenderExpandAllIcon = (props: RenderExpandAllIconProps) => React.ReactNode;

export interface ExpandableConfig<RecordType> {
expandedRowKeys?: readonly Key[];
defaultExpandedRowKeys?: readonly Key[];
expandedRowRender?: ExpandedRowRender<RecordType>;
forceRender?: boolean;
columnTitle?: React.ReactNode;
columnTitle?: React.ReactNode | ((originalNode: React.ReactNode) => React.ReactNode);
expandRowByClick?: boolean;
expandIcon?: RenderExpandIcon<RecordType>;
expandAllIcon?: RenderExpandAllIcon;
onExpand?: (expanded: boolean, record: RecordType) => void;
onExpandAll?: (expanded: boolean, records: readonly RecordType[]) => void;
onExpandedRowsChange?: (expandedKeys: readonly Key[]) => void;
defaultExpandAllRows?: boolean;
indentSize?: number;
/** @deprecated Please use `EXPAND_COLUMN` in `columns` directly */
expandIconColumnIndex?: number;
showExpandColumn?: boolean;
showExpandAll?: boolean;
expandedRowClassName?: string | RowClassName<RecordType>;
childrenColumnName?: string;
rowExpandable?: (record: RecordType) => boolean;
Expand Down
39 changes: 38 additions & 1 deletion src/utils/expandUtil.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import * as React from 'react';
import { clsx } from 'clsx';
import type { RenderExpandIconProps, Key, GetRowKey, ExpandableConfig } from '../interface';
import type {
RenderExpandAllIconProps,
RenderExpandIconProps,
Key,
GetRowKey,
ExpandableConfig,
} from '../interface';

export function renderExpandIcon<RecordType>({
prefixCls,
Expand Down Expand Up @@ -31,6 +37,37 @@ export function renderExpandIcon<RecordType>({
);
}

export function renderExpandAllIcon({
prefixCls,
onExpand,
expanded,
expandable,
}: RenderExpandAllIconProps) {
const expandClassName = `${prefixCls}-row-expand-icon`;

if (!expandable) {
return <span className={clsx(expandClassName, `${prefixCls}-row-spaced`)} />;
}

const onClick: React.MouseEventHandler<HTMLElement> = event => {
onExpand(event);
event.stopPropagation();
};

return (
<button
type="button"
aria-expanded={expanded}
aria-label={expanded ? 'Collapse all rows' : 'Expand all rows'}
className={clsx(expandClassName, {
[`${prefixCls}-row-expanded`]: expanded,
[`${prefixCls}-row-collapsed`]: !expanded,
})}
onClick={onClick}
/>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
);
}

export function findAllChildrenKeys<RecordType>(
data: readonly RecordType[],
getRowKey: GetRowKey<RecordType>,
Expand Down
Loading
Loading