feat: table list sorter
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?: (
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user