feat: table list sorter

This commit is contained in:
jialin
2025-12-21 21:37:05 +08:00
parent 6738dfe0d3
commit 23a761226a
25 changed files with 417 additions and 138 deletions
@@ -1,15 +1,16 @@
import { Col, Row } from 'antd';
import React from 'react';
import { SealColumnProps } from '../types';
import { OnSortFn, SealColumnProps } from '../types';
import TableHeader from './table-header';
interface HeaderProps {
columns: SealColumnProps[];
onSort?: (dataIndex: string, order: any) => void;
sortDirections?: ('ascend' | 'descend' | null)[];
onSort?: OnSortFn;
}
const Header: React.FC<HeaderProps> = (props) => {
const { onSort } = props;
const { onSort, sortDirections } = props;
return (
<Row className="row">
@@ -32,6 +33,7 @@ const Header: React.FC<HeaderProps> = (props) => {
sorter={sorter}
dataIndex={dataIndex}
sortOrder={sortOrder}
sortDirections={sortDirections}
width={width}
defaultSortOrder={defaultSortOrder}
title={title}
@@ -14,19 +14,39 @@ const TableHeader: React.FC<TableHeaderProps> = (props) => {
firstCell,
lastCell,
sortOrder,
sortDirections = ['ascend', 'descend', null],
defaultSortOrder,
onSort,
sorter,
sorter = false,
width,
dataIndex
} = props;
const handleOnSort = () => {
if (sortOrder === 'ascend') {
onSort?.(dataIndex, 'descend');
} else {
onSort?.(dataIndex, 'ascend');
}
const [currentSortOrder, setCurrentSortOrder] = React.useState<
'ascend' | 'descend' | null
>(sortOrder || defaultSortOrder || null);
const getNextSortOrder = (currentOrder: 'ascend' | 'descend' | null) => {
const index = sortDirections.indexOf(currentOrder);
const nextIndex = (index + 1) % sortDirections.length;
return sortDirections[nextIndex];
};
const handleOnSort = () => {
setCurrentSortOrder((prev) => {
const next = getNextSortOrder(prev);
onSort?.(
{
columnKey: dataIndex,
field: dataIndex,
order: next
},
sorter
);
return next;
});
};
return (
<div
style={{ width, ...style }}
@@ -47,12 +67,12 @@ const TableHeader: React.FC<TableHeaderProps> = (props) => {
<span className="sorter">
<CaretUpOutlined
className={classNames('sorter-up', {
'sorter-active': sortOrder === 'ascend'
'sorter-active': currentSortOrder === 'ascend'
})}
></CaretUpOutlined>
<CaretDownOutlined
className={classNames('sorter-down', {
'sorter-active': sortOrder === 'descend'
'sorter-active': currentSortOrder === 'descend'
})}
></CaretDownOutlined>
</span>
+12 -2
View File
@@ -7,6 +7,7 @@ import HeaderPrefix from './components/header-prefix';
import TableBody from './components/table-body';
import './styles/index.less';
import { SealColumnProps, SealTableProps } from './types';
import useSorter from './use-sorter';
const Wrapper = styled.div<{ $token: any }>`
--ant-table-cell-padding-inline: ${(props) =>
@@ -32,7 +33,7 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
childParentKey,
onExpand,
onExpandAll,
onSort,
onTableSort,
onCell,
expandedRowKeys,
loading,
@@ -43,10 +44,15 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
rowSelection,
pagination,
empty,
sortDirections,
renderChildren,
loadChildren,
loadChildrenAPI
} = props;
const { handleOnTableSort } = useSorter({
onTableSort,
columns
});
const { token } = theme.useToken();
const parsedColumns = useMemo(() => {
if (columns) return columns;
@@ -156,7 +162,11 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
disabled={!props.dataSource?.length}
hasColumns={parsedColumns.length > 0}
></HeaderPrefix>
<Header onSort={onSort} columns={parsedColumns}></Header>
<Header
onSort={handleOnTableSort}
columns={parsedColumns}
sortDirections={sortDirections}
></Header>
</div>
<Spin spinning={loading}>
<TableBody
+27 -5
View File
@@ -1,5 +1,14 @@
import React from 'react';
export type OnSortFn = (
order: {
columnKey: string;
field: string;
order: 'ascend' | 'descend' | null;
},
sorter: boolean | { multiple?: number }
) => void;
export interface CellContentProps {
dataIndex: string;
render?: (text: any, record: any) => React.ReactNode;
@@ -20,7 +29,7 @@ export interface SealColumnProps {
span: number;
align?: 'left' | 'center' | 'right';
headerStyle?: React.CSSProperties;
sorter?: boolean;
sorter?: boolean | { multiple?: number };
defaultSortOrder?: 'ascend' | 'descend';
editable?:
| boolean
@@ -34,17 +43,23 @@ export interface SealColumnProps {
}
export interface TableHeaderProps {
sorter?: boolean;
defaultSortOrder?: 'ascend' | 'descend';
sorter?: boolean | { multiple?: number };
sortDirections?: ('ascend' | 'descend' | null)[];
defaultSortOrder?: 'ascend' | 'descend' | null;
sortOrder?: 'ascend' | 'descend' | null;
dataIndex: string;
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void;
onSort?: OnSortFn;
title: React.ReactNode;
style?: React.CSSProperties;
firstCell?: boolean;
lastCell?: boolean;
align?: 'left' | 'center' | 'right';
width?: number | string;
sortedDataIndexList?: Array<{
columnKey: string;
field: string;
order: 'ascend' | 'descend' | null;
}>;
}
export interface RowSelectionProps {
@@ -54,7 +69,14 @@ export interface RowSelectionProps {
removeSelectedKeys: (rowKeys: React.Key[]) => void;
onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => void;
}
export type TableOrder = {
columnKey?: string;
field?: string;
order: 'ascend' | 'descend' | null;
};
export interface SealTableProps {
sortDirections?: ('ascend' | 'descend' | null)[];
columns?: SealColumnProps[];
childParentKey?: string;
expandedRowKeys?: React.Key[];
@@ -68,7 +90,7 @@ export interface SealTableProps {
loading?: boolean;
loadend?: boolean;
onCell?: (record: any, dataIndex: string) => void;
onSort?: (dataIndex: string, order: 'ascend' | 'descend') => void;
onTableSort?: (order: TableOrder | Array<TableOrder>) => void;
onExpand?: (expanded: boolean, record: any, rowKey: any) => void;
onExpandAll?: (expanded: boolean) => void;
renderChildren?: (
+84
View File
@@ -0,0 +1,84 @@
import { isBoolean } from 'lodash';
import { useRef } from 'react';
import { SealColumnProps, TableOrder } from './types';
const initSorterList = (columns: SealColumnProps[]) => {
const list = columns.filter((col) => col.defaultSortOrder);
if (list.length === 0) {
return [];
}
return list.map((col) => ({
columnKey: col.key || col.dataIndex,
field: col.dataIndex,
order: col.defaultSortOrder || null
}));
};
export default function useSorter(options: {
onTableSort?: (TableOrder: TableOrder | Array<TableOrder>) => void;
columns?: SealColumnProps[];
}) {
const { onTableSort, columns } = options;
const sorterListRef = useRef<TableOrder | Array<TableOrder>>(
initSorterList(columns || [])
);
const handleOnTableSort = (
order: TableOrder,
sorter: boolean | { multiple?: number }
) => {
let currentOrder: TableOrder = { ...order };
if (order.order === null) {
currentOrder = {
columnKey: undefined,
field: undefined,
order: null
};
}
// single column sort
if (isBoolean(sorter)) {
sorterListRef.current = currentOrder;
onTableSort?.(sorterListRef.current);
return;
}
// multi column sort
if (sorter && typeof sorter === 'object' && sorter.multiple) {
if (!Array.isArray(sorterListRef.current)) {
if (
sorterListRef.current.columnKey === currentOrder.columnKey ||
sorterListRef.current.field === currentOrder.field
) {
// remove the sorter if order is null
sorterListRef.current = {
...currentOrder
};
} else {
sorterListRef.current = [sorterListRef.current, { ...currentOrder }];
}
} else if (Array.isArray(sorterListRef.current)) {
const existingIndex = sorterListRef.current.findIndex(
(item) =>
item.columnKey === order.columnKey || item.field === order.field
);
if (existingIndex !== -1) {
sorterListRef.current.splice(existingIndex, 1);
sorterListRef.current.push({ ...currentOrder });
} else {
sorterListRef.current.push({ ...currentOrder });
}
}
}
onTableSort?.(sorterListRef.current);
};
return {
sorterListRef,
handleOnTableSort
};
}