refactor: models list table columns from props

This commit is contained in:
jialin
2025-02-19 15:17:17 +08:00
parent a9006f2954
commit 9e0634f449
12 changed files with 372 additions and 345 deletions
+19 -22
View File
@@ -4,16 +4,16 @@ import { SealColumnProps } from '../types';
import TableHeader from './table-header';
interface HeaderProps {
children: React.ReactNode[];
columns: SealColumnProps[];
onSort?: (dataIndex: string, order: any) => void;
}
const Header: React.FC<HeaderProps> = (props) => {
const { onSort } = props;
return (
<Row className="row">
{React.Children.map(props.children, (child, i) => {
const { props: columnProps } = child as any;
{props.columns?.map((columnProps, i) => {
const {
title,
dataIndex,
@@ -24,25 +24,22 @@ const Header: React.FC<HeaderProps> = (props) => {
sorter,
defaultSortOrder
} = columnProps as SealColumnProps;
if (React.isValidElement(child)) {
return (
<Col span={span} key={i}>
<TableHeader
onSort={onSort}
sorter={sorter}
dataIndex={dataIndex}
sortOrder={sortOrder}
defaultSortOrder={defaultSortOrder}
title={title}
style={headerStyle}
firstCell={i === 0}
align={align}
lastCell={i === props.children.length - 1}
></TableHeader>
</Col>
);
}
return null;
return (
<Col span={span} key={dataIndex || i}>
<TableHeader
onSort={onSort}
sorter={sorter}
dataIndex={dataIndex}
sortOrder={sortOrder}
defaultSortOrder={defaultSortOrder}
title={title}
style={headerStyle}
firstCell={i === 0}
align={align}
lastCell={i === props.columns.length - 1}
></TableHeader>
</Col>
);
})}
</Row>
);
@@ -0,0 +1,64 @@
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Button, Checkbox } from 'antd';
import _ from 'lodash';
import React from 'react';
interface RowPrefixProps {
expandable?: boolean | React.ReactNode;
enableSelection?: boolean;
expanded?: boolean;
checked?: boolean;
handleRowExpand?: () => void;
handleSelectChange?: (e: any) => void;
}
const RowPrefix: React.FC<RowPrefixProps> = (props) => {
const {
expandable,
enableSelection,
expanded,
checked,
handleRowExpand,
handleSelectChange
} = props;
if (expandable && enableSelection) {
return (
<div className="row-prefix-wrapper">
<span style={{ marginRight: 5 }}>
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</span>
<Checkbox onChange={handleSelectChange} checked={checked}></Checkbox>
</div>
);
}
if (expandable) {
return (
<div className="row-prefix-wrapper">
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</div>
);
}
if (enableSelection) {
return (
<div className="row-prefix-wrapper">
{<Checkbox onChange={handleSelectChange} checked={checked}></Checkbox>}
</div>
);
}
return null;
};
export default RowPrefix;
@@ -1,13 +0,0 @@
import { Checkbox } from 'antd';
import '../styles/skeleton.less';
const RowSkeleton = () => {
return (
<div className="row-skeleton">
<div className="holder"></div>
<Checkbox></Checkbox>
</div>
);
};
export default RowSkeleton;
@@ -1,10 +1,11 @@
import { Empty } from 'antd';
import React from 'react';
import { SealColumnProps } from '../types';
import TableRow from './table-row';
interface TableBodyProps {
dataSource: any[];
columns: React.ReactNode[];
columns: SealColumnProps[];
rowKey: string;
rowSelection?: any;
expandable?: any;
@@ -2,14 +2,21 @@ import useSetChunkRequest, {
createAxiosToken
} from '@/hooks/use-chunk-request';
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Button, Checkbox, Col, Empty, Row, Spin } from 'antd';
import { Col, Empty, Row, Spin } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import React, { useEffect, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import RowContext from '../row-context';
import TableContext from '../table-context';
import { RowContextProps, SealTableProps } from '../types';
import RowPrefix from './row-prefix';
import TableColumn from './seal-column';
const TableRow: React.FC<
RowContextProps &
@@ -38,7 +45,7 @@ const TableRow: React.FC<
}>(TableContext);
const { setChunkRequest } = useSetChunkRequest();
const [expanded, setExpanded] = useState(false);
const [checked, setChecked] = useState(false);
// const [checked, setChecked] = useState(false);
const [childrenData, setChildrenData] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [firstLoad, setFirstLoad] = useState(true);
@@ -56,17 +63,6 @@ const TableRow: React.FC<
// callback: (list) => renderChildren?.(list)
});
useEffect(() => {
if (rowSelection) {
const { selectedRowKeys } = rowSelection;
if (selectedRowKeys.includes(record[rowKey])) {
setChecked(true);
} else {
setChecked(false);
}
}
}, [rowSelection]);
useEffect(() => {
return () => {
if (pollTimer.current) {
@@ -77,7 +73,23 @@ const TableRow: React.FC<
};
}, []);
const checked = useMemo(() => {
return rowSelection?.selectedRowKeys?.includes(record[rowKey]);
}, [rowSelection?.selectedRowKeys, record, rowKey]);
const renderChildrenData = () => {
if (childrenData.length === 0) {
return (
<Empty
image={loading ? null : Empty.PRESENTED_IMAGE_SIMPLE}
description={loading ? null : undefined}
style={{
marginBlock: 0,
height: 54
}}
></Empty>
);
}
return renderChildren?.(childrenData, record);
};
@@ -92,7 +104,7 @@ const TableRow: React.FC<
}
};
const handleLoadChildren = async () => {
const handleLoadChildren = useCallback(async () => {
try {
axiosToken.current?.cancel?.();
axiosToken.current = createAxiosToken();
@@ -109,7 +121,7 @@ const TableRow: React.FC<
} finally {
setFirstLoad(false);
}
};
}, [record, loadChildren]);
const filterUpdateChildrenHandler = () => {
if (!expandedRowKeys?.includes(record[rowKey])) {
@@ -224,51 +236,6 @@ const TableRow: React.FC<
};
}, [updateChild, tableContext.allChildren]);
const renderRowPrefix = () => {
if (expandable && rowSelection) {
return (
<div className="row-prefix-wrapper">
<span style={{ marginRight: 5 }}>
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</span>
<Checkbox onChange={handleSelectChange} checked={checked}></Checkbox>
</div>
);
}
if (expandable) {
return (
<div className="row-prefix-wrapper">
{_.isBoolean(expandable) ? (
<Button type="text" size="small" onClick={handleRowExpand}>
{expanded ? <DownOutlined /> : <RightOutlined />}
</Button>
) : (
expandable
)}
</div>
);
}
if (rowSelection) {
return (
<div className="row-prefix-wrapper">
{
<Checkbox
onChange={handleSelectChange}
checked={checked}
></Checkbox>
}
</div>
);
}
return null;
};
return (
<RowContext.Provider value={{ row: { ...record, rowIndex }, onCell }}>
<div className="row-box">
@@ -277,40 +244,30 @@ const TableRow: React.FC<
'row-wrapper-selected': checked
})}
>
{renderRowPrefix()}
<RowPrefix
expandable={expandable}
enableSelection={rowSelection?.enableSelection}
expanded={expanded}
checked={checked}
handleRowExpand={handleRowExpand}
handleSelectChange={handleSelectChange}
></RowPrefix>
<Row className="seal-table-row">
{React.Children.map(columns, (child) => {
const { props: columnProps } = child as any;
if (React.isValidElement(child)) {
return (
<Col
key={`${columnProps.dataIndex}-${rowIndex}`}
span={columnProps.span}
>
{child}
</Col>
);
}
return <Col span={columnProps.span}></Col>;
{columns?.map((columnProps) => {
return (
<Col
key={`${columnProps.dataIndex}-${rowIndex}`}
span={columnProps.span}
>
<TableColumn {...columnProps}></TableColumn>
</Col>
);
})}
</Row>
</div>
{expanded && (
<div className="expanded-row">
<Spin spinning={loading}>
{childrenData.length ? (
renderChildrenData()
) : (
<Empty
image={loading ? null : Empty.PRESENTED_IMAGE_SIMPLE}
description={loading ? null : undefined}
style={{
marginBlock: 0,
height: 54
}}
></Empty>
)}
</Spin>
<Spin spinning={loading}>{renderChildrenData()}</Spin>
</div>
)}
</div>
@@ -0,0 +1,32 @@
import { Checkbox } from 'antd';
import React from 'react';
import styled from 'styled-components';
import '../styles/skeleton.less';
const SkeletonItem = () => {
return (
<div className="row-skeleton">
<div className="holder"></div>
<Checkbox></Checkbox>
</div>
);
};
const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
`;
const TableSkeleton = () => {
const dataSource = Array.from({ length: 5 });
return (
<Wrapper>
{dataSource.map((item, index) => (
<SkeletonItem key={index} />
))}
</Wrapper>
);
};
export default React.memo(TableSkeleton);
+43 -29
View File
@@ -1,16 +1,17 @@
import { Pagination, Spin, type PaginationProps } from 'antd';
import _ from 'lodash';
import React, { useEffect, useState } from 'react';
import React, { useMemo } from 'react';
import Header from './components/header';
import HeaderPrefix from './components/header-prefix';
import TableBody from './components/table-body';
import './styles/index.less';
import { SealTableProps } from './types';
import { SealColumnProps, SealTableProps } from './types';
const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
props
) => {
const {
columns,
children,
rowKey,
childParentKey,
@@ -29,33 +30,46 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
loadChildren,
loadChildrenAPI
} = props;
const [selectState, setSelectState] = useState({
selectAll: false,
indeterminate: false
});
useEffect(() => {
if (rowSelection) {
const { selectedRowKeys } = rowSelection;
const selectedKeys = new Set(rowSelection.selectedRowKeys);
const allRowKeys = props.dataSource.map((record) => record[rowKey]);
if (!selectedRowKeys?.length) {
setSelectState({
selectAll: false,
indeterminate: false
});
} else if (allRowKeys.every((key) => selectedKeys.has(key))) {
setSelectState({
selectAll: true,
indeterminate: false
});
} else {
setSelectState({
selectAll: false,
indeterminate: true
});
}
const parsedColumns = useMemo(() => {
if (columns) return columns;
return React.Children.toArray(children)
.filter(React.isValidElement)
.map((child) => {
const column = child as React.ReactElement<SealColumnProps>;
const { title, dataIndex, key, render, ...restProps } = column.props;
return {
title,
dataIndex,
key: key || dataIndex,
render,
...restProps
};
});
}, [columns, children]);
const selectState = useMemo(() => {
const selectedRowKeys = rowSelection?.selectedRowKeys || [];
const selectedKeys = new Set(selectedRowKeys);
const allRowKeys = props.dataSource.map((record) => record[rowKey]);
if (!selectedRowKeys?.length) {
return {
selectAll: false,
indeterminate: false
};
}
if (allRowKeys.every((key) => selectedKeys.has(key))) {
return {
selectAll: true,
indeterminate: false
};
}
return {
selectAll: false,
indeterminate: true
};
}, [rowSelection?.selectedRowKeys, props.dataSource, rowKey]);
const selectAllRows = () => {
@@ -103,12 +117,12 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
expandable={expandable}
enableSelection={rowSelection?.enableSelection}
></HeaderPrefix>
<Header onSort={onSort}>{children}</Header>
<Header onSort={onSort} columns={parsedColumns}></Header>
</div>
<Spin spinning={loading}>
<TableBody
dataSource={props.dataSource}
columns={children}
columns={parsedColumns}
rowSelection={rowSelection}
expandable={expandable}
rowKey={rowKey}
+5 -3
View File
@@ -4,6 +4,7 @@ export interface SealColumnProps {
title: React.ReactNode;
render?: (text: any, record: any) => React.ReactNode;
dataIndex: string;
key?: string;
width?: number;
span: number;
align?: 'left' | 'center' | 'right';
@@ -18,6 +19,7 @@ export interface SealColumnProps {
};
valueType?: 'text' | 'number' | 'date' | 'datetime' | 'time';
sortOrder?: 'ascend' | 'descend' | null;
[key: string]: any;
}
export interface TableHeaderProps {
@@ -26,7 +28,7 @@ export interface TableHeaderProps {
sortOrder?: 'ascend' | 'descend' | null;
dataIndex: string;
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void;
children?: React.ReactNode;
children?: React.ReactElement<SealColumnProps>;
title: React.ReactNode;
style?: React.CSSProperties;
firstCell?: boolean;
@@ -42,10 +44,11 @@ export interface RowSelectionProps {
onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => void;
}
export interface SealTableProps {
columns?: SealColumnProps[];
childParentKey?: string;
expandedRowKeys?: React.Key[];
rowSelection?: RowSelectionProps;
children: React.ReactNode[];
children?: React.ReactElement<SealColumnProps>[];
empty?: React.ReactNode;
expandable?: React.ReactNode;
dataSource: any[];
@@ -65,7 +68,6 @@ export interface SealTableProps {
export interface RowContextProps {
record: Record<string, any>;
columns: React.ReactNode[];
pollingChildren?: boolean;
rowIndex: number;
}