mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
type(Table): TypeScript improvement (#50351)
* type: Table TypeScript improvement * type: Table TypeScript improvement * type: Table TypeScript improvement * fix: fix demo * fix: fix demo * fix: fix type * fix: fix type * fix: fix type * fix: fix type
This commit is contained in:
parent
f05c3809d5
commit
857ee24e38
@ -1,14 +1,14 @@
|
||||
import type { AnyObject } from '../_util/type';
|
||||
import type { ColumnType } from './interface';
|
||||
|
||||
export interface ColumnProps<RecordType> extends ColumnType<RecordType> {
|
||||
export interface ColumnProps<RecordType extends AnyObject = AnyObject>
|
||||
extends ColumnType<RecordType> {
|
||||
children?: null;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
/** This is a syntactic sugar for `columns` prop. So HOC will not work on this. */
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function Column<RecordType>(_: ColumnProps<RecordType>) {
|
||||
return null;
|
||||
}
|
||||
const Column = <RecordType extends AnyObject>(_: ColumnProps<RecordType>) => null;
|
||||
|
||||
export default Column;
|
||||
|
@ -1,9 +1,11 @@
|
||||
import type * as React from 'react';
|
||||
|
||||
import type { AnyObject } from '../_util/type';
|
||||
import type { ColumnProps } from './Column';
|
||||
import type { ColumnType } from './interface';
|
||||
|
||||
export interface ColumnGroupProps<RecordType> extends Omit<ColumnType<RecordType>, 'children'> {
|
||||
export interface ColumnGroupProps<RecordType extends AnyObject = AnyObject>
|
||||
extends Omit<ColumnType<RecordType>, 'children'> {
|
||||
children:
|
||||
| React.ReactElement<ColumnProps<RecordType>>
|
||||
| React.ReactElement<ColumnProps<RecordType>>[];
|
||||
@ -12,8 +14,6 @@ export interface ColumnGroupProps<RecordType> extends Omit<ColumnType<RecordType
|
||||
/* istanbul ignore next */
|
||||
/** This is a syntactic sugar for `columns` prop. So HOC will not work on this. */
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function ColumnGroup<RecordType>(_: ColumnGroupProps<RecordType>) {
|
||||
return null;
|
||||
}
|
||||
const ColumnGroup = <RecordType extends AnyObject>(_: ColumnGroupProps<RecordType>) => null;
|
||||
|
||||
export default ColumnGroup;
|
||||
|
@ -1,26 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { AnyObject } from '../_util/type';
|
||||
import type { TableLocale } from './interface';
|
||||
|
||||
interface DefaultExpandIconProps<RecordType> {
|
||||
interface DefaultExpandIconProps<RecordType extends AnyObject = AnyObject> {
|
||||
prefixCls: string;
|
||||
onExpand: (record: RecordType, e: React.MouseEvent<HTMLElement>) => void;
|
||||
record: RecordType;
|
||||
expanded: boolean;
|
||||
expandable: boolean;
|
||||
onExpand: (record: RecordType, e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
function renderExpandIcon(locale: TableLocale) {
|
||||
return function expandIcon<RecordType>({
|
||||
prefixCls,
|
||||
onExpand,
|
||||
record,
|
||||
expanded,
|
||||
expandable,
|
||||
}: DefaultExpandIconProps<RecordType>) {
|
||||
return <RecordType extends AnyObject = AnyObject>(props: DefaultExpandIconProps<RecordType>) => {
|
||||
const { prefixCls, onExpand, record, expanded, expandable } = props;
|
||||
const iconPrefix = `${prefixCls}-row-expand-icon`;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
|
@ -57,9 +57,9 @@ import useStyle from './style';
|
||||
|
||||
export type { ColumnsType, TablePaginationConfig };
|
||||
|
||||
const EMPTY_LIST: any[] = [];
|
||||
const EMPTY_LIST: AnyObject[] = [];
|
||||
|
||||
interface ChangeEventInfo<RecordType> {
|
||||
interface ChangeEventInfo<RecordType extends AnyObject = AnyObject> {
|
||||
pagination: {
|
||||
current?: number;
|
||||
pageSize?: number;
|
||||
@ -74,12 +74,7 @@ interface ChangeEventInfo<RecordType> {
|
||||
resetPagination: (current?: number, pageSize?: number) => void;
|
||||
}
|
||||
|
||||
/** Same as `TableProps` but we need record parent render times */
|
||||
export interface InternalTableProps<RecordType> extends TableProps<RecordType> {
|
||||
_renderTimes: number;
|
||||
}
|
||||
|
||||
export interface TableProps<RecordType = any>
|
||||
export interface TableProps<RecordType = AnyObject>
|
||||
extends Omit<
|
||||
RcTableProps<RecordType>,
|
||||
| 'transformColumns'
|
||||
@ -117,6 +112,12 @@ export interface TableProps<RecordType = any>
|
||||
virtual?: boolean;
|
||||
}
|
||||
|
||||
/** Same as `TableProps` but we need record parent render times */
|
||||
export interface InternalTableProps<RecordType extends AnyObject = AnyObject>
|
||||
extends TableProps<RecordType> {
|
||||
_renderTimes: number;
|
||||
}
|
||||
|
||||
const InternalTable = <RecordType extends AnyObject = AnyObject>(
|
||||
props: InternalTableProps<RecordType>,
|
||||
ref: React.MutableRefObject<HTMLDivElement>,
|
||||
@ -176,9 +177,7 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
|
||||
const mergedColumns = React.useMemo(() => {
|
||||
const matched = new Set(Object.keys(screens).filter((m) => screens[m as Breakpoint]));
|
||||
|
||||
return baseColumns.filter(
|
||||
(c) => !c.responsive || c.responsive.some((r: Breakpoint) => matched.has(r)),
|
||||
);
|
||||
return baseColumns.filter((c) => !c.responsive || c.responsive.some((r) => matched.has(r)));
|
||||
}, [baseColumns, screens]);
|
||||
|
||||
const tableProps: TableProps<RecordType> = omit(props, ['className', 'style', 'columns']);
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { genVirtualTable } from 'rc-table';
|
||||
|
||||
import type { AnyObject } from '../../_util/type';
|
||||
import type { InternalTableProps } from '../InternalTable';
|
||||
|
||||
/**
|
||||
* Same as `rc-table` but we modify trigger children update logic instead.
|
||||
*/
|
||||
export default genVirtualTable((prev, next) => {
|
||||
const { _renderTimes: prevRenderTimes } = prev as InternalTableProps<any>;
|
||||
const { _renderTimes: nextRenderTimes } = next as InternalTableProps<any>;
|
||||
|
||||
const RcVirtualTable = genVirtualTable((prev, next) => {
|
||||
const { _renderTimes: prevRenderTimes } = prev as Readonly<InternalTableProps<AnyObject>>;
|
||||
const { _renderTimes: nextRenderTimes } = next as Readonly<InternalTableProps<AnyObject>>;
|
||||
return prevRenderTimes !== nextRenderTimes;
|
||||
});
|
||||
|
||||
export default RcVirtualTable;
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { genTable } from 'rc-table';
|
||||
|
||||
import type { AnyObject } from '../../_util/type';
|
||||
import type { InternalTableProps } from '../InternalTable';
|
||||
|
||||
/**
|
||||
* Same as `rc-table` but we modify trigger children update logic instead.
|
||||
*/
|
||||
export default genTable((prev, next) => {
|
||||
const { _renderTimes: prevRenderTimes } = prev as InternalTableProps<any>;
|
||||
const { _renderTimes: nextRenderTimes } = next as InternalTableProps<any>;
|
||||
|
||||
const RcTable = genTable((prev, next) => {
|
||||
const { _renderTimes: prevRenderTimes } = prev as Readonly<InternalTableProps<AnyObject>>;
|
||||
const { _renderTimes: nextRenderTimes } = next as Readonly<InternalTableProps<AnyObject>>;
|
||||
return prevRenderTimes !== nextRenderTimes;
|
||||
});
|
||||
|
||||
export default RcTable;
|
||||
|
@ -4,7 +4,7 @@ import { Table } from 'antd';
|
||||
import type { SorterResult } from 'antd/es/table/interface';
|
||||
import qs from 'qs';
|
||||
|
||||
type ColumnsType<T> = TableProps<T>['columns'];
|
||||
type ColumnsType<T extends object = object> = TableProps<T>['columns'];
|
||||
type TablePaginationConfig = Exclude<GetProp<TableProps, 'pagination'>, boolean>;
|
||||
|
||||
interface DataType {
|
||||
@ -94,7 +94,7 @@ const App: React.FC = () => {
|
||||
JSON.stringify(tableParams.filters),
|
||||
]);
|
||||
|
||||
const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter) => {
|
||||
const handleTableChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter) => {
|
||||
setTableParams({
|
||||
pagination,
|
||||
filters,
|
||||
|
@ -143,7 +143,7 @@ const App: React.FC = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const mergedColumns: TableProps['columns'] = columns.map((col) => {
|
||||
const mergedColumns: TableProps<Item>['columns'] = columns.map((col) => {
|
||||
if (!col.editable) {
|
||||
return col;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { Button, Flex, Table } from 'antd';
|
||||
import type { TableColumnsType, TableProps } from 'antd';
|
||||
|
||||
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { TableColumnsType, TableProps } from 'antd';
|
||||
|
||||
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { TableColumnsType, TableProps } from 'antd';
|
||||
|
||||
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
|
||||
|
||||
interface DataType {
|
||||
key: React.Key;
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { InputNumber, Table } from 'antd';
|
||||
import type { TableColumnsType, TableProps } from 'antd';
|
||||
|
||||
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
|
||||
|
||||
const RenderTimes = () => {
|
||||
const timesRef = React.useRef(0);
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { Space, Switch, Table } from 'antd';
|
||||
import type { TableColumnsType, TableProps } from 'antd';
|
||||
|
||||
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
|
||||
|
||||
interface DataType {
|
||||
key: React.ReactNode;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import FilterFilled from '@ant-design/icons/FilterFilled';
|
||||
import type { AnyObject } from 'antd/es/_util/type';
|
||||
import classNames from 'classnames';
|
||||
import type { FieldDataNode } from 'rc-tree';
|
||||
import isEqual from 'rc-util/lib/isEqual';
|
||||
@ -119,7 +120,7 @@ function renderFilterItems({
|
||||
|
||||
export type TreeColumnFilterItem = ColumnFilterItem & FilterTreeDataNode;
|
||||
|
||||
export interface FilterDropdownProps<RecordType> {
|
||||
export interface FilterDropdownProps<RecordType extends AnyObject = AnyObject> {
|
||||
tablePrefixCls: string;
|
||||
prefixCls: string;
|
||||
dropdownPrefixCls: string;
|
||||
@ -142,7 +143,9 @@ function wrapStringListType(keys?: FilterKey) {
|
||||
return (keys as string[]) || [];
|
||||
}
|
||||
|
||||
function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
const FilterDropdown = <RecordType extends AnyObject = AnyObject>(
|
||||
props: FilterDropdownProps<RecordType>,
|
||||
) => {
|
||||
const {
|
||||
tablePrefixCls,
|
||||
prefixCls,
|
||||
@ -553,6 +556,6 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default FilterDropdown;
|
||||
|
@ -1,10 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import SearchOutlined from '@ant-design/icons/SearchOutlined';
|
||||
|
||||
import type { AnyObject } from '../../../_util/type';
|
||||
import Input from '../../../input';
|
||||
import type { FilterSearchType, TableLocale } from '../../interface';
|
||||
|
||||
interface FilterSearchProps<RecordType = any> {
|
||||
interface FilterSearchProps<RecordType extends AnyObject = AnyObject> {
|
||||
value: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
filterSearch: FilterSearchType<RecordType>;
|
||||
@ -12,13 +13,10 @@ interface FilterSearchProps<RecordType = any> {
|
||||
locale: TableLocale;
|
||||
}
|
||||
|
||||
function FilterSearch<RecordType>({
|
||||
value,
|
||||
onChange,
|
||||
filterSearch,
|
||||
tablePrefixCls,
|
||||
locale,
|
||||
}: FilterSearchProps<RecordType>) {
|
||||
const FilterSearch = <RecordType extends AnyObject = AnyObject>(
|
||||
props: FilterSearchProps<RecordType>,
|
||||
) => {
|
||||
const { value, filterSearch, tablePrefixCls, locale, onChange } = props;
|
||||
if (!filterSearch) {
|
||||
return null;
|
||||
}
|
||||
@ -35,6 +33,6 @@ function FilterSearch<RecordType>({
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default FilterSearch;
|
||||
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
|
||||
export interface FilterDropdownMenuWrapperProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@ -13,8 +12,10 @@ const onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
|
||||
}
|
||||
};
|
||||
|
||||
const FilterDropdownMenuWrapper = React.forwardRef<HTMLDivElement, FilterDropdownMenuWrapperProps>(
|
||||
(props, ref) => (
|
||||
const FilterDropdownMenuWrapper = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.PropsWithChildren<FilterDropdownMenuWrapperProps>
|
||||
>((props, ref) => (
|
||||
<div
|
||||
className={props.className}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@ -23,8 +24,7 @@ const FilterDropdownMenuWrapper = React.forwardRef<HTMLDivElement, FilterDropdow
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
));
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
FilterDropdownMenuWrapper.displayName = 'FilterDropdownMenuWrapper';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { AnyObject } from '../../../_util/type';
|
||||
import { devUseWarning } from '../../../_util/warning';
|
||||
import type {
|
||||
ColumnsType,
|
||||
@ -16,18 +17,18 @@ import type {
|
||||
import { getColumnKey, getColumnPos, renderColumnTitle } from '../../util';
|
||||
import FilterDropdown, { flattenKeys } from './FilterDropdown';
|
||||
|
||||
export interface FilterState<RecordType> {
|
||||
export interface FilterState<RecordType extends AnyObject = AnyObject> {
|
||||
column: ColumnType<RecordType>;
|
||||
key: Key;
|
||||
filteredKeys?: FilterKey;
|
||||
forceFiltered?: boolean;
|
||||
}
|
||||
|
||||
function collectFilterStates<RecordType>(
|
||||
const collectFilterStates = <RecordType extends AnyObject = AnyObject>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
init: boolean,
|
||||
pos?: string,
|
||||
): FilterState<RecordType>[] {
|
||||
): FilterState<RecordType>[] => {
|
||||
let filterStates: FilterState<RecordType>[] = [];
|
||||
|
||||
(columns || []).forEach((column, index) => {
|
||||
@ -65,9 +66,9 @@ function collectFilterStates<RecordType>(
|
||||
});
|
||||
|
||||
return filterStates;
|
||||
}
|
||||
};
|
||||
|
||||
function injectFilter<RecordType>(
|
||||
function injectFilter<RecordType extends AnyObject = AnyObject>(
|
||||
prefixCls: string,
|
||||
dropdownPrefixCls: string,
|
||||
columns: ColumnsType<RecordType>,
|
||||
@ -112,7 +113,7 @@ function injectFilter<RecordType>(
|
||||
getPopupContainer={getPopupContainer}
|
||||
rootClassName={rootClassName}
|
||||
>
|
||||
{renderColumnTitle(column.title, renderProps)}
|
||||
{renderColumnTitle<RecordType>(column.title, renderProps)}
|
||||
</FilterDropdown>
|
||||
),
|
||||
};
|
||||
@ -139,7 +140,9 @@ function injectFilter<RecordType>(
|
||||
});
|
||||
}
|
||||
|
||||
function generateFilterInfo<RecordType>(filterStates: FilterState<RecordType>[]) {
|
||||
const generateFilterInfo = <RecordType extends AnyObject = AnyObject>(
|
||||
filterStates: FilterState<RecordType>[],
|
||||
) => {
|
||||
const currentFilters: Record<string, FilterValue | null> = {};
|
||||
|
||||
filterStates.forEach(({ key, filteredKeys, column }) => {
|
||||
@ -158,14 +161,14 @@ function generateFilterInfo<RecordType>(filterStates: FilterState<RecordType>[])
|
||||
});
|
||||
|
||||
return currentFilters;
|
||||
}
|
||||
};
|
||||
|
||||
export function getFilterData<RecordType>(
|
||||
export const getFilterData = <RecordType extends AnyObject = AnyObject>(
|
||||
data: RecordType[],
|
||||
filterStates: FilterState<RecordType>[],
|
||||
childrenColumnName: string,
|
||||
) {
|
||||
return filterStates.reduce((currentData, filterState) => {
|
||||
) => {
|
||||
const filterDatas = filterStates.reduce<RecordType[]>((currentData, filterState) => {
|
||||
const {
|
||||
column: { onFilter, filters },
|
||||
filteredKeys,
|
||||
@ -197,9 +200,10 @@ export function getFilterData<RecordType>(
|
||||
}
|
||||
return currentData;
|
||||
}, data);
|
||||
}
|
||||
return filterDatas;
|
||||
};
|
||||
|
||||
export interface FilterConfig<RecordType> {
|
||||
export interface FilterConfig<RecordType extends AnyObject = AnyObject> {
|
||||
prefixCls: string;
|
||||
dropdownPrefixCls: string;
|
||||
mergedColumns: ColumnsType<RecordType>;
|
||||
@ -212,17 +216,24 @@ export interface FilterConfig<RecordType> {
|
||||
rootClassName?: string;
|
||||
}
|
||||
|
||||
const getMergedColumns = <RecordType extends unknown>(
|
||||
const getMergedColumns = <RecordType extends AnyObject = AnyObject>(
|
||||
rawMergedColumns: ColumnsType<RecordType>,
|
||||
): ColumnsType<RecordType> =>
|
||||
rawMergedColumns.flatMap((column) => {
|
||||
if ('children' in column) {
|
||||
return [column, ...getMergedColumns(column.children || [])];
|
||||
return [column, ...getMergedColumns<RecordType>(column.children || [])];
|
||||
}
|
||||
return [column];
|
||||
});
|
||||
|
||||
function useFilter<RecordType>({
|
||||
const useFilter = <RecordType extends AnyObject = AnyObject>(
|
||||
props: FilterConfig<RecordType>,
|
||||
): [
|
||||
TransformColumns<RecordType>,
|
||||
FilterState<RecordType>[],
|
||||
Record<string, FilterValue | null>,
|
||||
] => {
|
||||
const {
|
||||
prefixCls,
|
||||
dropdownPrefixCls,
|
||||
mergedColumns: rawMergedColumns,
|
||||
@ -230,15 +241,11 @@ function useFilter<RecordType>({
|
||||
getPopupContainer,
|
||||
locale: tableLocale,
|
||||
rootClassName,
|
||||
}: FilterConfig<RecordType>): [
|
||||
TransformColumns<RecordType>,
|
||||
FilterState<RecordType>[],
|
||||
Record<string, FilterValue | null>,
|
||||
] {
|
||||
} = props;
|
||||
const warning = devUseWarning('Table');
|
||||
|
||||
const mergedColumns = React.useMemo(
|
||||
() => getMergedColumns(rawMergedColumns || []),
|
||||
() => getMergedColumns<RecordType>(rawMergedColumns || []),
|
||||
[rawMergedColumns],
|
||||
);
|
||||
|
||||
@ -291,13 +298,16 @@ function useFilter<RecordType>({
|
||||
return collectedStates;
|
||||
}, [mergedColumns, filterStates]);
|
||||
|
||||
const filters = React.useMemo(() => generateFilterInfo(mergedFilterStates), [mergedFilterStates]);
|
||||
const filters = React.useMemo(
|
||||
() => generateFilterInfo<RecordType>(mergedFilterStates),
|
||||
[mergedFilterStates],
|
||||
);
|
||||
|
||||
const triggerFilter = (filterState: FilterState<RecordType>) => {
|
||||
const newFilterStates = mergedFilterStates.filter(({ key }) => key !== filterState.key);
|
||||
newFilterStates.push(filterState);
|
||||
setFilterStates(newFilterStates);
|
||||
onFilterChange(generateFilterInfo(newFilterStates), newFilterStates);
|
||||
onFilterChange(generateFilterInfo<RecordType>(newFilterStates), newFilterStates);
|
||||
};
|
||||
|
||||
const transformColumns = (innerColumns: ColumnsType<RecordType>) =>
|
||||
@ -313,8 +323,8 @@ function useFilter<RecordType>({
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
return [transformColumns, mergedFilterStates, filters];
|
||||
}
|
||||
return [transformColumns, mergedFilterStates, filters] as const;
|
||||
};
|
||||
|
||||
export { flattenKeys };
|
||||
|
||||
|
@ -1,19 +1,20 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { AnyObject } from '../../_util/type';
|
||||
import type { GetRowKey, Key } from '../interface';
|
||||
|
||||
interface MapCache<RecordType> {
|
||||
interface MapCache<RecordType extends AnyObject = AnyObject> {
|
||||
data?: readonly RecordType[];
|
||||
childrenColumnName?: string;
|
||||
kvMap?: Map<Key, RecordType>;
|
||||
getRowKey?: (record: RecordType, index: number) => Key;
|
||||
}
|
||||
|
||||
export default function useLazyKVMap<RecordType>(
|
||||
const useLazyKVMap = <RecordType extends AnyObject = AnyObject>(
|
||||
data: readonly RecordType[],
|
||||
childrenColumnName: string,
|
||||
getRowKey: GetRowKey<RecordType>,
|
||||
) {
|
||||
) => {
|
||||
const mapCacheRef = React.useRef<MapCache<RecordType>>({});
|
||||
|
||||
function getRecordByKey(key: Key): RecordType {
|
||||
@ -48,8 +49,10 @@ export default function useLazyKVMap<RecordType>(
|
||||
};
|
||||
}
|
||||
|
||||
return mapCacheRef.current.kvMap!.get(key)!;
|
||||
return mapCacheRef.current.kvMap?.get(key)!;
|
||||
}
|
||||
|
||||
return [getRecordByKey];
|
||||
}
|
||||
return [getRecordByKey] as const;
|
||||
};
|
||||
|
||||
export default useLazyKVMap;
|
||||
|
@ -4,6 +4,7 @@ import CaretUpOutlined from '@ant-design/icons/CaretUpOutlined';
|
||||
import classNames from 'classnames';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
|
||||
import type { AnyObject } from '../../_util/type';
|
||||
import type { TooltipProps } from '../../tooltip';
|
||||
import Tooltip from '../../tooltip';
|
||||
import type {
|
||||
@ -24,16 +25,18 @@ import { getColumnKey, getColumnPos, renderColumnTitle, safeColumnTitle } from '
|
||||
const ASCEND = 'ascend';
|
||||
const DESCEND = 'descend';
|
||||
|
||||
function getMultiplePriority<RecordType>(column: ColumnType<RecordType>): number | false {
|
||||
const getMultiplePriority = <RecordType extends AnyObject = AnyObject>(
|
||||
column: ColumnType<RecordType>,
|
||||
): number | false => {
|
||||
if (typeof column.sorter === 'object' && typeof column.sorter.multiple === 'number') {
|
||||
return column.sorter.multiple;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function getSortFunction<RecordType>(
|
||||
const getSortFunction = <RecordType extends AnyObject = AnyObject>(
|
||||
sorter: ColumnType<RecordType>['sorter'],
|
||||
): CompareFn<RecordType> | false {
|
||||
): CompareFn<RecordType> | false => {
|
||||
if (typeof sorter === 'function') {
|
||||
return sorter;
|
||||
}
|
||||
@ -41,42 +44,40 @@ function getSortFunction<RecordType>(
|
||||
return sorter.compare;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function nextSortDirection(sortDirections: SortOrder[], current: SortOrder | null) {
|
||||
const nextSortDirection = (sortDirections: SortOrder[], current: SortOrder | null) => {
|
||||
if (!current) {
|
||||
return sortDirections[0];
|
||||
}
|
||||
|
||||
return sortDirections[sortDirections.indexOf(current) + 1];
|
||||
}
|
||||
};
|
||||
|
||||
export interface SortState<RecordType> {
|
||||
export interface SortState<RecordType extends AnyObject = AnyObject> {
|
||||
column: ColumnType<RecordType>;
|
||||
key: Key;
|
||||
sortOrder: SortOrder | null;
|
||||
multiplePriority: number | false;
|
||||
}
|
||||
|
||||
function collectSortStates<RecordType>(
|
||||
const collectSortStates = <RecordType extends AnyObject = AnyObject>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
init: boolean,
|
||||
pos?: string,
|
||||
): SortState<RecordType>[] {
|
||||
): SortState<RecordType>[] => {
|
||||
let sortStates: SortState<RecordType>[] = [];
|
||||
|
||||
function pushState(column: ColumnsType<RecordType>[number], columnPos: string) {
|
||||
const pushState = (column: ColumnsType<RecordType>[number], columnPos: string) => {
|
||||
sortStates.push({
|
||||
column,
|
||||
key: getColumnKey(column, columnPos),
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
key: getColumnKey<RecordType>(column, columnPos),
|
||||
multiplePriority: getMultiplePriority<RecordType>(column),
|
||||
sortOrder: column.sortOrder!,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
(columns || []).forEach((column, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
|
||||
if ((column as ColumnGroupType<RecordType>).children) {
|
||||
if ('sortOrder' in column) {
|
||||
// Controlled
|
||||
@ -84,7 +85,11 @@ function collectSortStates<RecordType>(
|
||||
}
|
||||
sortStates = [
|
||||
...sortStates,
|
||||
...collectSortStates((column as ColumnGroupType<RecordType>).children, init, columnPos),
|
||||
...collectSortStates<RecordType>(
|
||||
(column as ColumnGroupType<RecordType>).children,
|
||||
init,
|
||||
columnPos,
|
||||
),
|
||||
];
|
||||
} else if (column.sorter) {
|
||||
if ('sortOrder' in column) {
|
||||
@ -95,7 +100,7 @@ function collectSortStates<RecordType>(
|
||||
sortStates.push({
|
||||
column,
|
||||
key: getColumnKey(column, columnPos),
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
multiplePriority: getMultiplePriority<RecordType>(column),
|
||||
sortOrder: column.defaultSortOrder!,
|
||||
});
|
||||
}
|
||||
@ -103,9 +108,9 @@ function collectSortStates<RecordType>(
|
||||
});
|
||||
|
||||
return sortStates;
|
||||
}
|
||||
};
|
||||
|
||||
function injectSorter<RecordType>(
|
||||
const injectSorter = <RecordType extends AnyObject = AnyObject>(
|
||||
prefixCls: string,
|
||||
columns: ColumnsType<RecordType>,
|
||||
sorterStates: SortState<RecordType>[],
|
||||
@ -114,11 +119,10 @@ function injectSorter<RecordType>(
|
||||
tableLocale?: TableLocale,
|
||||
tableShowSorterTooltip?: boolean | SorterTooltipProps,
|
||||
pos?: string,
|
||||
): ColumnsType<RecordType> {
|
||||
return (columns || []).map((column, index) => {
|
||||
): ColumnsType<RecordType> => {
|
||||
const finalColumns = (columns || []).map((column, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
let newColumn: ColumnsType<RecordType>[number] = column;
|
||||
|
||||
if (newColumn.sorter) {
|
||||
const sortDirections: SortOrder[] = newColumn.sortDirections || defaultSortDirections;
|
||||
const showSorterTooltip =
|
||||
@ -220,7 +224,7 @@ function injectSorter<RecordType>(
|
||||
column,
|
||||
key: columnKey,
|
||||
sortOrder: nextSortOrder,
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
multiplePriority: getMultiplePriority<RecordType>(column),
|
||||
});
|
||||
originOnClick?.(event);
|
||||
};
|
||||
@ -230,7 +234,7 @@ function injectSorter<RecordType>(
|
||||
column,
|
||||
key: columnKey,
|
||||
sortOrder: nextSortOrder,
|
||||
multiplePriority: getMultiplePriority(column),
|
||||
multiplePriority: getMultiplePriority<RecordType>(column),
|
||||
});
|
||||
originOKeyDown?.(event);
|
||||
}
|
||||
@ -273,9 +277,10 @@ function injectSorter<RecordType>(
|
||||
|
||||
return newColumn;
|
||||
});
|
||||
}
|
||||
return finalColumns;
|
||||
};
|
||||
|
||||
const stateToInfo = <RecordType extends any>(
|
||||
const stateToInfo = <RecordType extends AnyObject = AnyObject>(
|
||||
sorterStates: SortState<RecordType>,
|
||||
): SorterResult<RecordType> => {
|
||||
const { column, sortOrder } = sorterStates;
|
||||
@ -287,7 +292,7 @@ const stateToInfo = <RecordType extends any>(
|
||||
};
|
||||
};
|
||||
|
||||
const generateSorterInfo = <RecordType extends any>(
|
||||
const generateSorterInfo = <RecordType extends AnyObject = AnyObject>(
|
||||
sorterStates: SortState<RecordType>[],
|
||||
): SorterResult<RecordType> | SorterResult<RecordType>[] => {
|
||||
const list = sorterStates
|
||||
@ -311,11 +316,11 @@ const generateSorterInfo = <RecordType extends any>(
|
||||
return list;
|
||||
};
|
||||
|
||||
export function getSortData<RecordType>(
|
||||
export const getSortData = <RecordType extends AnyObject = AnyObject>(
|
||||
data: readonly RecordType[],
|
||||
sortStates: SortState<RecordType>[],
|
||||
childrenColumnName: string,
|
||||
): RecordType[] {
|
||||
): RecordType[] => {
|
||||
const innerSorterStates = sortStates
|
||||
.slice()
|
||||
.sort((a, b) => (b.multiplePriority as number) - (a.multiplePriority as number));
|
||||
@ -323,7 +328,7 @@ export function getSortData<RecordType>(
|
||||
const cloneData = data.slice();
|
||||
|
||||
const runningSorters = innerSorterStates.filter(
|
||||
({ column: { sorter }, sortOrder }) => getSortFunction(sorter) && sortOrder,
|
||||
({ column: { sorter }, sortOrder }) => getSortFunction<RecordType>(sorter) && sortOrder,
|
||||
);
|
||||
|
||||
// Skip if no sorter needed
|
||||
@ -340,7 +345,7 @@ export function getSortData<RecordType>(
|
||||
sortOrder,
|
||||
} = sorterState;
|
||||
|
||||
const compareFn = getSortFunction(sorter);
|
||||
const compareFn = getSortFunction<RecordType>(sorter);
|
||||
|
||||
if (compareFn && sortOrder) {
|
||||
const compareResult = compareFn(record1, record2, sortOrder);
|
||||
@ -354,18 +359,18 @@ export function getSortData<RecordType>(
|
||||
return 0;
|
||||
})
|
||||
.map<RecordType>((record) => {
|
||||
const subRecords = (record as any)[childrenColumnName];
|
||||
const subRecords = record[childrenColumnName];
|
||||
if (subRecords) {
|
||||
return {
|
||||
...record,
|
||||
[childrenColumnName]: getSortData(subRecords, sortStates, childrenColumnName),
|
||||
[childrenColumnName]: getSortData<RecordType>(subRecords, sortStates, childrenColumnName),
|
||||
};
|
||||
}
|
||||
return record;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
interface SorterConfig<RecordType> {
|
||||
interface SorterConfig<RecordType extends AnyObject = AnyObject> {
|
||||
prefixCls: string;
|
||||
mergedColumns: ColumnsType<RecordType>;
|
||||
onSorterChange: (
|
||||
@ -377,28 +382,32 @@ interface SorterConfig<RecordType> {
|
||||
showSorterTooltip?: boolean | SorterTooltipProps;
|
||||
}
|
||||
|
||||
export default function useFilterSorter<RecordType>({
|
||||
prefixCls,
|
||||
mergedColumns,
|
||||
onSorterChange,
|
||||
sortDirections,
|
||||
tableLocale,
|
||||
showSorterTooltip,
|
||||
}: SorterConfig<RecordType>): [
|
||||
const useFilterSorter = <RecordType extends AnyObject = AnyObject>(
|
||||
props: SorterConfig<RecordType>,
|
||||
): [
|
||||
TransformColumns<RecordType>,
|
||||
SortState<RecordType>[],
|
||||
ColumnTitleProps<RecordType>,
|
||||
() => SorterResult<RecordType> | SorterResult<RecordType>[],
|
||||
] {
|
||||
] => {
|
||||
const {
|
||||
prefixCls,
|
||||
mergedColumns,
|
||||
sortDirections,
|
||||
tableLocale,
|
||||
showSorterTooltip,
|
||||
onSorterChange,
|
||||
} = props;
|
||||
|
||||
const [sortStates, setSortStates] = React.useState<SortState<RecordType>[]>(
|
||||
collectSortStates(mergedColumns, true),
|
||||
collectSortStates<RecordType>(mergedColumns, true),
|
||||
);
|
||||
|
||||
const getColumnKeys = (columns: ColumnsType<RecordType>, pos?: string): Key[] => {
|
||||
const newKeys: Key[] = [];
|
||||
columns.forEach((item, index) => {
|
||||
const columnPos = getColumnPos(index, pos);
|
||||
newKeys.push(getColumnKey(item, columnPos));
|
||||
newKeys.push(getColumnKey<RecordType>(item, columnPos));
|
||||
if (Array.isArray((item as ColumnGroupType<RecordType>).children)) {
|
||||
const childKeys = getColumnKeys((item as ColumnGroupType<RecordType>).children, columnPos);
|
||||
newKeys.push(...childKeys);
|
||||
@ -408,7 +417,7 @@ export default function useFilterSorter<RecordType>({
|
||||
};
|
||||
const mergedSorterStates = React.useMemo<SortState<RecordType>[]>(() => {
|
||||
let validate = true;
|
||||
const collectedStates = collectSortStates(mergedColumns, false);
|
||||
const collectedStates = collectSortStates<RecordType>(mergedColumns, false);
|
||||
|
||||
// Return if not controlled
|
||||
if (!collectedStates.length) {
|
||||
@ -498,5 +507,7 @@ export default function useFilterSorter<RecordType>({
|
||||
|
||||
const getSorters = () => generateSorterInfo(mergedSorterStates);
|
||||
|
||||
return [transformColumns, mergedSorterStates, columnTitleSorterProps, getSorters];
|
||||
}
|
||||
return [transformColumns, mergedSorterStates, columnTitleSorterProps, getSorters] as const;
|
||||
};
|
||||
|
||||
export default useFilterSorter;
|
||||
|
@ -1,32 +1,38 @@
|
||||
import * as React from 'react';
|
||||
import type { AnyObject } from 'antd/es/_util/type';
|
||||
|
||||
import type { ColumnsType, ColumnTitleProps, TransformColumns } from '../interface';
|
||||
import type {
|
||||
ColumnGroupType,
|
||||
ColumnsType,
|
||||
ColumnTitleProps,
|
||||
ColumnType,
|
||||
TransformColumns,
|
||||
} from '../interface';
|
||||
import { renderColumnTitle } from '../util';
|
||||
|
||||
function fillTitle<RecordType>(
|
||||
const fillTitle = <RecordType extends AnyObject = AnyObject>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
columnTitleProps: ColumnTitleProps<RecordType>,
|
||||
) {
|
||||
return columns.map((column) => {
|
||||
const cloneColumn = { ...column };
|
||||
|
||||
) => {
|
||||
const finalColumns = columns.map((column) => {
|
||||
const cloneColumn: ColumnGroupType<RecordType> | ColumnType<RecordType> = { ...column };
|
||||
cloneColumn.title = renderColumnTitle(column.title, columnTitleProps);
|
||||
|
||||
if ('children' in cloneColumn) {
|
||||
cloneColumn.children = fillTitle(cloneColumn.children, columnTitleProps);
|
||||
cloneColumn.children = fillTitle<RecordType>(cloneColumn.children, columnTitleProps);
|
||||
}
|
||||
|
||||
return cloneColumn;
|
||||
});
|
||||
}
|
||||
return finalColumns;
|
||||
};
|
||||
|
||||
export default function useTitleColumns<RecordType>(
|
||||
const useTitleColumns = <RecordType extends AnyObject = AnyObject>(
|
||||
columnTitleProps: ColumnTitleProps<RecordType>,
|
||||
): [TransformColumns<RecordType>] {
|
||||
const filledColumns = React.useCallback(
|
||||
(columns: ColumnsType<RecordType>) => fillTitle(columns, columnTitleProps),
|
||||
) => {
|
||||
const filledColumns = React.useCallback<TransformColumns<RecordType>>(
|
||||
(columns) => fillTitle<RecordType>(columns, columnTitleProps),
|
||||
[columnTitleProps],
|
||||
);
|
||||
return [filledColumns] as const;
|
||||
};
|
||||
|
||||
return [filledColumns];
|
||||
}
|
||||
export default useTitleColumns;
|
||||
|
@ -66,9 +66,10 @@ export type SorterTooltipProps = TooltipProps & {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const TableActions = ['paginate', 'sort', 'filter'] as const;
|
||||
|
||||
export type TableAction = (typeof TableActions)[number];
|
||||
|
||||
export type CompareFn<T> = (a: T, b: T, sortOrder?: SortOrder) => number;
|
||||
export type CompareFn<T = AnyObject> = (a: T, b: T, sortOrder?: SortOrder) => number;
|
||||
|
||||
export interface ColumnFilterItem {
|
||||
text: React.ReactNode;
|
||||
@ -76,7 +77,7 @@ export interface ColumnFilterItem {
|
||||
children?: ColumnFilterItem[];
|
||||
}
|
||||
|
||||
export interface ColumnTitleProps<RecordType> {
|
||||
export interface ColumnTitleProps<RecordType = AnyObject> {
|
||||
/** @deprecated Please use `sorterColumns` instead. */
|
||||
sortOrder?: SortOrder;
|
||||
/** @deprecated Please use `sorterColumns` instead. */
|
||||
@ -86,7 +87,7 @@ export interface ColumnTitleProps<RecordType> {
|
||||
filters?: Record<string, FilterValue>;
|
||||
}
|
||||
|
||||
export type ColumnTitle<RecordType> =
|
||||
export type ColumnTitle<RecordType = AnyObject> =
|
||||
| React.ReactNode
|
||||
| ((props: ColumnTitleProps<RecordType>) => React.ReactNode);
|
||||
|
||||
@ -115,7 +116,8 @@ export interface FilterDropdownProps {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export interface ColumnType<RecordType> extends Omit<RcColumnType<RecordType>, 'title'> {
|
||||
export interface ColumnType<RecordType = AnyObject>
|
||||
extends Omit<RcColumnType<RecordType>, 'title'> {
|
||||
title?: ColumnTitle<RecordType>;
|
||||
// Sorter
|
||||
sorter?:
|
||||
@ -158,11 +160,12 @@ export interface ColumnType<RecordType> extends Omit<RcColumnType<RecordType>, '
|
||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||
}
|
||||
|
||||
export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>, 'dataIndex'> {
|
||||
export interface ColumnGroupType<RecordType = AnyObject>
|
||||
extends Omit<ColumnType<RecordType>, 'dataIndex'> {
|
||||
children: ColumnsType<RecordType>;
|
||||
}
|
||||
|
||||
export type ColumnsType<RecordType = any> = (
|
||||
export type ColumnsType<RecordType = AnyObject> = (
|
||||
| ColumnGroupType<RecordType>
|
||||
| ColumnType<RecordType>
|
||||
)[];
|
||||
@ -173,7 +176,7 @@ export interface SelectionItem {
|
||||
onSelect?: SelectionItemSelectFn;
|
||||
}
|
||||
|
||||
export type SelectionSelectFn<T> = (
|
||||
export type SelectionSelectFn<T = AnyObject> = (
|
||||
record: T,
|
||||
selected: boolean,
|
||||
selectedRows: T[],
|
||||
@ -182,7 +185,7 @@ export type SelectionSelectFn<T> = (
|
||||
|
||||
export type RowSelectMethod = 'all' | 'none' | 'invert' | 'single' | 'multiple';
|
||||
|
||||
export interface TableRowSelection<T> {
|
||||
export interface TableRowSelection<T = AnyObject> {
|
||||
/** Keep the selection keys in list even the key not exist in `dataSource` anymore */
|
||||
preserveSelectedRowKeys?: boolean;
|
||||
type?: RowSelectionType;
|
||||
@ -214,16 +217,16 @@ export interface TableRowSelection<T> {
|
||||
onCell?: GetComponentProps<T>;
|
||||
}
|
||||
|
||||
export type TransformColumns<RecordType> = (
|
||||
export type TransformColumns<RecordType = AnyObject> = (
|
||||
columns: ColumnsType<RecordType>,
|
||||
) => ColumnsType<RecordType>;
|
||||
|
||||
export interface TableCurrentDataSource<RecordType> {
|
||||
export interface TableCurrentDataSource<RecordType = AnyObject> {
|
||||
currentDataSource: RecordType[];
|
||||
action: TableAction;
|
||||
}
|
||||
|
||||
export interface SorterResult<RecordType> {
|
||||
export interface SorterResult<RecordType = AnyObject> {
|
||||
column?: ColumnType<RecordType>;
|
||||
order?: SortOrder;
|
||||
field?: Key | readonly Key[];
|
||||
|
@ -1,31 +1,33 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import type { AnyObject } from '../_util/type';
|
||||
import type { ColumnTitle, ColumnTitleProps, ColumnType, Key } from './interface';
|
||||
|
||||
export function getColumnKey<RecordType>(column: ColumnType<RecordType>, defaultKey: string): Key {
|
||||
export const getColumnKey = <RecordType extends AnyObject = AnyObject>(
|
||||
column: ColumnType<RecordType>,
|
||||
defaultKey: string,
|
||||
): Key => {
|
||||
if ('key' in column && column.key !== undefined && column.key !== null) {
|
||||
return column.key;
|
||||
}
|
||||
if (column.dataIndex) {
|
||||
return (Array.isArray(column.dataIndex) ? column.dataIndex.join('.') : column.dataIndex) as Key;
|
||||
return Array.isArray(column.dataIndex) ? column.dataIndex.join('.') : (column.dataIndex as Key);
|
||||
}
|
||||
|
||||
return defaultKey;
|
||||
}
|
||||
};
|
||||
|
||||
export function getColumnPos(index: number, pos?: string) {
|
||||
return pos ? `${pos}-${index}` : `${index}`;
|
||||
}
|
||||
|
||||
export function renderColumnTitle<RecordType>(
|
||||
export const renderColumnTitle = <RecordType extends AnyObject = AnyObject>(
|
||||
title: ColumnTitle<RecordType>,
|
||||
props: ColumnTitleProps<RecordType>,
|
||||
) {
|
||||
) => {
|
||||
if (typeof title === 'function') {
|
||||
return title(props);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Safe get column title
|
||||
@ -35,11 +37,13 @@ export function renderColumnTitle<RecordType>(
|
||||
* @param title
|
||||
* @returns
|
||||
*/
|
||||
export function safeColumnTitle<RecordType>(
|
||||
export const safeColumnTitle = <RecordType extends AnyObject = AnyObject>(
|
||||
title: ColumnTitle<RecordType>,
|
||||
props: ColumnTitleProps<RecordType>,
|
||||
) {
|
||||
const res = renderColumnTitle(title, props);
|
||||
if (Object.prototype.toString.call(res) === '[object Object]') return '';
|
||||
) => {
|
||||
const res = renderColumnTitle<RecordType>(title, props);
|
||||
if (Object.prototype.toString.call(res) === '[object Object]') {
|
||||
return '';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import { ConfigProvider, Space, Switch, Table, Tag, Transfer } from 'antd';
|
||||
import type { GetProp, TableColumnsType, TableProps, TransferProps } from 'antd';
|
||||
import difference from 'lodash/difference';
|
||||
|
||||
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||
type TableRowSelection<T extends object = object> = TableProps<T>['rowSelection'];
|
||||
|
||||
type TransferItem = GetProp<TransferProps, 'dataSource'>[number];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user