Skip to content

Commit

Permalink
Merge pull request #316 from yunfeic/feat/table-tree-select
Browse files Browse the repository at this point in the history
feat(table): 表格支持树形行选中
  • Loading branch information
honkinglin authored Jan 27, 2022
2 parents 42cdd8e + 56f36e1 commit 5ac23b1
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 70 deletions.
2 changes: 1 addition & 1 deletion src/locale/LocalReceiver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { ConfigContext , Locale } from '../config-provider';
import { ConfigContext, Locale } from '../config-provider';

export interface Placement {
[propName: string]: string | number;
Expand Down
144 changes: 144 additions & 0 deletions src/table/_example/tree-select.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useState } from 'react';
import { EnhancedTable, Radio } from 'tdesign-react';

const columns = [
{
colKey: 'row-select',
type: 'multiple',
disabled: ({ row }) => row.instance === 'JQTest2',
width: 50,
},
{ colKey: 'instance', title: '集群名称', width: 150 },
{
colKey: 'status',
title: '状态',
width: 100,
cell({ row }) {
switch (row?.status) {
case 0:
return <p className="status">健康</p>;
case 1:
return <p className="status unhealth">异常</p>;
default:
return null;
}
},
},
{ colKey: 'owner', title: '管理员' },
{ colKey: 'description', title: '描述' },
];
const initData = [
{
id: 1,
instance: 'JQTest1',
status: 0,
owner: 'jenny;peter',
description: 'test',
},
{
id: '2',
instance: 'JQTest2',
status: 1,
owner: 'jenny',
description: 'test',
},
{
id: 3,
instance: 'JQTest3',
status: 0,
owner: 'jenny',
description: 'test',
children: [
{
id: '3-1',
instance: 'JQTest3-1',
status: 1,
owner: 'jenny',
description: 'test',
children: [
{
id: '3-1-1',
instance: 'JQTest3-1-1',
status: 1,
owner: 'jenny',
description: 'test',
},
{
id: '3-1-2',
instance: 'JQTest3-1-2',
status: 1,
owner: 'jenny',
description: 'test',
},
],
},
{
id: '3-2',
instance: 'JQTest3-2',
status: 1,
owner: 'jenny',
description: 'test',
},
],
},
{
id: 4,
instance: 'JQTest4',
status: 1,
owner: 'peter',
description: 'test',
},
{
id: 5,
instance: 'JQTest5',
status: 1,
owner: 'peter',
description: 'test',
children: [
{
id: '5-1',
instance: 'JQTest5-1',
status: 1,
owner: 'jenny',
description: 'test',
},
],
},
{
id: 6,
instance: 'JQTest6',
status: 1,
owner: 'peter',
description: 'test',
},
];

export default function TableSingleSort() {
const [data] = useState([...initData]);
const [selectedRowKeys, setSelectedRowKeys] = useState([1, '2']);
const [checkStrictly, setCheckStrictly] = useState(true);

function onSelectChange(value, selectOptions) {
console.log('onSelectChange', value, selectOptions);
setSelectedRowKeys(value);
}

return (
<div className="demo-table-tree-select">
<Radio.Group value={checkStrictly} onChange={setCheckStrictly}>
<Radio.Button value={true}>父子行选中独立</Radio.Button>
<Radio.Button value={false}>父子行选中关联</Radio.Button>
</Radio.Group>

<EnhancedTable
rowKey="id"
data={data}
columns={columns}
selectedRowKeys={selectedRowKeys}
onSelectChange={onSelectChange}
tree={{ checkStrictly }}
pagination={{ defaultPageSize: 5, defaultCurrent: 1, total: data.length }}
/>
</div>
);
}
12 changes: 10 additions & 2 deletions src/table/base/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { TableContextProvider } from './TableContext';
import { TableColGroup } from './TableColGroup';
import TableFooter from './TableFooter';
import Loading from '../../loading';
import { useEnhancedTableContext } from '../enhanced/TableContext';

export interface ExpandInnerProps {
handleExpandChange?: Function;
Expand Down Expand Up @@ -116,7 +117,6 @@ export default function BaseTable<D extends DataType = DataType>(props: BaseTabl
// ==================== 固定表头、固定列 ====================
const [scrollBarWidth, setScrollBarWidth] = useState(0);
const fixedHeader = height > 0 || maxHeight > 0;
const table = useMemo(() => ({ fixedHeader, flattenColumns }), [fixedHeader, flattenColumns]);
const hasFixedColumns = columns.some(({ fixed }) => ['left', 'right'].includes(fixed));
const scrollHeaderRef = useRef<HTMLDivElement>();
const scrollBodyRef = useRef<HTMLDivElement>();
Expand All @@ -126,6 +126,14 @@ export default function BaseTable<D extends DataType = DataType>(props: BaseTabl
const [scrollableToRight, setScrollableToRight] = useState(false);
const [isHasScrollbar, setIsHasScrollbar] = useState(false);

// ==================== Context ====================
const { useFlattenData } = useEnhancedTableContext();
const flattenData = useFlattenData?.(pageData);
const tableContextValue = useMemo(
() => ({ fixedHeader, flattenColumns, flattenData, pageData }),
[fixedHeader, flattenColumns, flattenData, pageData],
);

useLayoutEffect(() => {
if (fixedHeader) {
setStateScrollBarWidth();
Expand Down Expand Up @@ -314,7 +322,7 @@ export default function BaseTable<D extends DataType = DataType>(props: BaseTabl
)}
style={style}
>
<TableContextProvider value={table}>
<TableContextProvider value={tableContextValue}>
<Loading
loading={!!loading}
showOverlay
Expand Down
10 changes: 5 additions & 5 deletions src/table/base/TableBody.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { forwardRef, useContext } from 'react';
import React, { forwardRef } from 'react';
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
import get from 'lodash/get';
import { TdPrimaryTableProps } from '../type';
import useConfig from '../../_util/useConfig';
import TableRow from './TableRow';
import { ExpandInnerProps, DragSortInnerProps } from './Table';
import { EnhancedTableContext } from '../enhanced/TableContext';
import { useTableContext } from './TableContext';

interface TableBodyProps extends TdPrimaryTableProps, ExpandInnerProps, DragSortInnerProps {}

Expand Down Expand Up @@ -60,14 +60,14 @@ const TableBody = forwardRef((props: TableBodyProps, ref: React.Ref<HTMLTableSec
onDrop,
onDragEnd,
} = props;
const { useTreeData } = useContext(EnhancedTableContext);
const mergedData = useTreeData?.(data) || data;
const { flattenData } = useTableContext();
const flattenDataVisible = flattenData || data;
const rowSkipTdSpanColIndexsMap: RowSkipTdSpanColIndexsMap = {}; // 引用,不可重置。eg: { 0: [1, 3] } 表示第1行,第2、4列两个cell不渲染
const isRowspanAndColspanFn = isFunction(rowspanAndColspan);
const rowEvents = getRowEvents();

// ==================== render ====================
const rows = mergedData.map((row, index) => {
const rows = flattenDataVisible.map((row, index) => {
const rowKeyValue = get(row, rowKey) || index;

return (
Expand Down
12 changes: 10 additions & 2 deletions src/table/base/TableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const TableCell = <D extends DataType>(props: PropsWithChildren<CellProps<D>>) =
} = props;

const { classPrefix } = useConfig();
const { flattenColumns } = useTableContext();
const { flattenColumns, flattenData, pageData } = useTableContext();
const [offset, setOffset] = useState(0);
const [isBoundary, setIsBoundary] = useState(false);

Expand All @@ -65,7 +65,15 @@ const TableCell = <D extends DataType>(props: PropsWithChildren<CellProps<D>>) =
}
}, [ref, flattenColumns, colKey, fixed]);

const cellNode = customRender({ type, row: record, rowIndex, col: columns?.[colIndex], colIndex });
const cellNode = customRender({
type,
row: record,
rowIndex,
col: columns?.[colIndex],
colIndex,
flattenData,
pageData,
});

// ==================== styles ====================
const cellStyle = { ...style };
Expand Down
8 changes: 7 additions & 1 deletion src/table/base/TableContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createContext, useContext } from 'react';
import { BaseTableCol } from '../type';
import { BaseTableCol, TdBaseTableProps } from '../type';

type Data = TdBaseTableProps['data'];

export interface TableContextValue {
/**
Expand All @@ -10,11 +12,15 @@ export interface TableContextValue {
* 扁平后的Columns
*/
flattenColumns: BaseTableCol[];
flattenData: Data;
pageData: Data;
}

export const TableContext = createContext<TableContextValue>({
fixedHeader: false,
flattenColumns: [],
flattenData: [],
pageData: [],
});

export const useTableContext = () => useContext(TableContext);
Expand Down
8 changes: 6 additions & 2 deletions src/table/enhanced/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import { EnhancedTableContextProvider } from './TableContext';
export type EnhancedTableProps = TdEnhancedTableProps & TdPrimaryTableProps;

export default function EnhancedTable(props: EnhancedTableProps) {
const [treeColumns, useTreeData] = useTree(props);
const { treeColumns, useFlattenData, useFlattenRowData, getFlattenPageData } = useTree(props);

const mergeColumns = treeColumns;

return (
<EnhancedTableContextProvider
value={{
useTreeData,
checkStrictly: props?.tree?.checkStrictly,
childrenKey: props?.tree?.childrenKey,
useFlattenData,
useFlattenRowData,
getFlattenPageData,
}}
>
<PrimaryTable {...props} columns={mergeColumns} />
Expand Down
11 changes: 9 additions & 2 deletions src/table/enhanced/TableContext.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { createContext, useContext } from 'react';

export interface EnhancedTableContextValue {
useTreeData?: any;
useFlattenData?: Function;
useFlattenRowData?: Function;
getFlattenPageData?: Function;
checkStrictly?: undefined | boolean;
childrenKey?: string;
[k: string]: any;
}

export const EnhancedTableContext = createContext<EnhancedTableContextValue>({});
export const EnhancedTableContext = createContext<EnhancedTableContextValue>({
checkStrictly: true,
childrenKey: 'children',
});

export const useEnhancedTableContext = () => useContext(EnhancedTableContext);
export const EnhancedTableContextProvider = EnhancedTableContext.Provider;
51 changes: 38 additions & 13 deletions src/table/enhanced/useTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ type Data = TdPrimaryTableProps['data'];
type CellParams = PrimaryTableRenderParams<DataType>;
type Column = PrimaryTableCol<DataType>;

function useTree(props: EnhancedTableProps): [Columns, Function] {
const { columns, tree, rowKey } = props;
interface UseTreeResult {
treeColumns: Columns;
useFlattenData: Function;
useFlattenRowData: Function;
getFlattenPageData: Function;
}

function useTree(props: EnhancedTableProps): UseTreeResult {
const { columns, tree, rowKey, data } = props;
const { classPrefix } = useConfig();
const childrenKey = tree?.childrenKey || 'children';
const indent = tree?.indent || 24;
Expand All @@ -21,25 +28,43 @@ function useTree(props: EnhancedTableProps): [Columns, Function] {
const mergedExpandedKeys = React.useMemo(() => new Set(innerExpandedKeys || []), [innerExpandedKeys]);

const treeNodeColumnIndex = getTreeNodeColumnIndex();
const transformedTreeColumns = getTreeColumns(columns);
const treeColumns = getTreeColumns(columns);

function useFlattenData(pageData) {
const flattenData = useMemo(() => flatVisibleData(pageData), [pageData]);
return flattenData;
}

function useFlattenRowData(needFlat) {
return needFlat ? flatData(data) : data;
}

function getFlattenPageData(pageData) {
return flatData(pageData, 0, true);
}

function useTreeData(pageData) {
const expandedData = useMemo(() => flattenData(pageData), [pageData]);
return expandedData;
function flatData(data: Data, level = 0, needParentRowKey?, parentRowKey?): Data {
const flattenData = [];
data?.forEach((row) => {
flattenData.push({ ...row, level, parentRowKey });
const childrenNodes = get(row, childrenKey);
flattenData.push(...flatData(childrenNodes, level + 1, needParentRowKey, row[rowKey]));
});
return flattenData;
}

function flattenData(data: Data, level = 0): Data {
const flatData = [];
data.forEach((row) => {
flatData.push({ ...row, level });
function flatVisibleData(data: Data, level = 0, parentRowKey?): Data {
const flattenData = [];
data?.forEach((row) => {
flattenData.push({ ...row, level, parentRowKey });
const childrenNodes = get(row, childrenKey);
const rowValue = get(row, rowKey);
const needExpand = Array.isArray(childrenNodes) && childrenNodes.length && mergedExpandedKeys.has(rowValue);
if (needExpand) {
flatData.push(...flattenData(childrenNodes, level + 1));
flattenData.push(...flatVisibleData(childrenNodes, level + 1, rowValue));
}
});
return flatData;
return flattenData;
}

function getTreeNodeColumnIndex() {
Expand Down Expand Up @@ -121,7 +146,7 @@ function useTree(props: EnhancedTableProps): [Columns, Function] {
return get(cellParams.row, colKey);
}

return [transformedTreeColumns, useTreeData];
return { treeColumns, useFlattenData, useFlattenRowData, getFlattenPageData };
}

export default useTree;
Loading

0 comments on commit 5ac23b1

Please sign in to comment.