mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 17:44:35 +08:00
feat: Responsive table columns (#23298)
This commit is contained in:
parent
7935651899
commit
f09686d3df
@ -3,14 +3,17 @@ import classNames from 'classnames';
|
|||||||
import omit from 'omit.js';
|
import omit from 'omit.js';
|
||||||
import RcTable from 'rc-table';
|
import RcTable from 'rc-table';
|
||||||
import { TableProps as RcTableProps, INTERNAL_HOOKS } from 'rc-table/lib/Table';
|
import { TableProps as RcTableProps, INTERNAL_HOOKS } from 'rc-table/lib/Table';
|
||||||
|
import { convertChildrenToColumns } from 'rc-table/lib/hooks/useColumns';
|
||||||
import Spin, { SpinProps } from '../spin';
|
import Spin, { SpinProps } from '../spin';
|
||||||
import Pagination, { PaginationConfig } from '../pagination';
|
import Pagination, { PaginationConfig } from '../pagination';
|
||||||
import { ConfigContext } from '../config-provider/context';
|
import { ConfigContext } from '../config-provider/context';
|
||||||
import usePagination, { DEFAULT_PAGE_SIZE, getPaginationParam } from './hooks/usePagination';
|
import usePagination, { DEFAULT_PAGE_SIZE, getPaginationParam } from './hooks/usePagination';
|
||||||
import useLazyKVMap from './hooks/useLazyKVMap';
|
import useLazyKVMap from './hooks/useLazyKVMap';
|
||||||
|
import { Breakpoint } from '../_util/responsiveObserve';
|
||||||
import {
|
import {
|
||||||
TableRowSelection,
|
TableRowSelection,
|
||||||
GetRowKey,
|
GetRowKey,
|
||||||
|
ColumnType,
|
||||||
ColumnsType,
|
ColumnsType,
|
||||||
TableCurrentDataSource,
|
TableCurrentDataSource,
|
||||||
SorterResult,
|
SorterResult,
|
||||||
@ -33,6 +36,7 @@ import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
|||||||
import Column from './Column';
|
import Column from './Column';
|
||||||
import ColumnGroup from './ColumnGroup';
|
import ColumnGroup from './ColumnGroup';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
|
import useBreakpoint from '../grid/hooks/useBreakpoint';
|
||||||
|
|
||||||
export { ColumnsType, TablePaginationConfig };
|
export { ColumnsType, TablePaginationConfig };
|
||||||
|
|
||||||
@ -113,7 +117,17 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
showSorterTooltip = true,
|
showSorterTooltip = true,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const tableProps = omit(props, ['className', 'style']) as TableProps<RecordType>;
|
const screens = useBreakpoint();
|
||||||
|
const mergedColumns = React.useMemo(() => {
|
||||||
|
const matched = new Set(Object.keys(screens).filter((m: Breakpoint) => screens[m]));
|
||||||
|
|
||||||
|
return (columns || convertChildrenToColumns(children)).filter(
|
||||||
|
(c: ColumnType<RecordType>) =>
|
||||||
|
!c.responsive || c.responsive.some((r: Breakpoint) => matched.has(r)),
|
||||||
|
);
|
||||||
|
}, [children, columns, screens]);
|
||||||
|
|
||||||
|
const tableProps = omit(props, ['className', 'style', 'columns']) as TableProps<RecordType>;
|
||||||
|
|
||||||
const size = React.useContext(SizeContext);
|
const size = React.useContext(SizeContext);
|
||||||
const { locale: contextLocale = defaultLocale, renderEmpty, direction } = React.useContext(
|
const { locale: contextLocale = defaultLocale, renderEmpty, direction } = React.useContext(
|
||||||
@ -222,8 +236,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
};
|
};
|
||||||
const [transformSorterColumns, sortStates, sorterTitleProps, getSorters] = useSorter<RecordType>({
|
const [transformSorterColumns, sortStates, sorterTitleProps, getSorters] = useSorter<RecordType>({
|
||||||
prefixCls,
|
prefixCls,
|
||||||
columns,
|
mergedColumns,
|
||||||
children,
|
|
||||||
onSorterChange,
|
onSorterChange,
|
||||||
sortDirections: sortDirections || ['ascend', 'descend'],
|
sortDirections: sortDirections || ['ascend', 'descend'],
|
||||||
tableLocale,
|
tableLocale,
|
||||||
@ -255,8 +268,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
prefixCls,
|
prefixCls,
|
||||||
locale: tableLocale,
|
locale: tableLocale,
|
||||||
dropdownPrefixCls,
|
dropdownPrefixCls,
|
||||||
columns,
|
mergedColumns,
|
||||||
children,
|
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
getPopupContainer,
|
getPopupContainer,
|
||||||
});
|
});
|
||||||
@ -313,8 +325,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
return mergedData;
|
return mergedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentPageData = mergedData.slice((current - 1) * pageSize, current * pageSize);
|
return mergedData.slice((current - 1) * pageSize, current * pageSize);
|
||||||
return currentPageData;
|
|
||||||
}, [
|
}, [
|
||||||
!!pagination,
|
!!pagination,
|
||||||
mergedData,
|
mergedData,
|
||||||
@ -439,6 +450,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
|
|||||||
{topPaginationNode}
|
{topPaginationNode}
|
||||||
<RcTable<RecordType>
|
<RcTable<RecordType>
|
||||||
{...tableProps}
|
{...tableProps}
|
||||||
|
columns={mergedColumns}
|
||||||
direction={direction}
|
direction={direction}
|
||||||
expandable={mergedExpandable}
|
expandable={mergedExpandable}
|
||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
|
@ -11541,6 +11541,141 @@ exports[`renders ./components/table/demo/resizable-column.md correctly 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/table/demo/responsive.md correctly 1`] = `
|
||||||
|
<div
|
||||||
|
class="ant-table-wrapper"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-spin-nested-loading"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-spin-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-table"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-table-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-table-content"
|
||||||
|
>
|
||||||
|
<table
|
||||||
|
style="table-layout:auto"
|
||||||
|
>
|
||||||
|
<colgroup />
|
||||||
|
<thead
|
||||||
|
class="ant-table-thead"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
class="ant-table-cell"
|
||||||
|
>
|
||||||
|
Name (all screens)
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody
|
||||||
|
class="ant-table-tbody"
|
||||||
|
>
|
||||||
|
<tr
|
||||||
|
class="ant-table-row ant-table-row-level-0"
|
||||||
|
data-row-key="1"
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
class="ant-table-cell"
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
John Brown
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
class="ant-pagination ant-table-pagination ant-table-pagination-right"
|
||||||
|
unselectable="unselectable"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
aria-disabled="true"
|
||||||
|
class="ant-pagination-prev ant-pagination-disabled"
|
||||||
|
title="Previous Page"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="ant-pagination-item-link"
|
||||||
|
disabled=""
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-label="left"
|
||||||
|
class="anticon anticon-left"
|
||||||
|
role="img"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="left"
|
||||||
|
fill="currentColor"
|
||||||
|
focusable="false"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||||
|
tabindex="0"
|
||||||
|
title="1"
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
1
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
aria-disabled="true"
|
||||||
|
class="ant-pagination-next ant-pagination-disabled"
|
||||||
|
title="Next Page"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="ant-pagination-item-link"
|
||||||
|
disabled=""
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-label="right"
|
||||||
|
class="anticon anticon-right"
|
||||||
|
role="img"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="right"
|
||||||
|
fill="currentColor"
|
||||||
|
focusable="false"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/table/demo/row-selection.md correctly 1`] = `
|
exports[`renders ./components/table/demo/row-selection.md correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
|
50
components/table/demo/responsive.md
Normal file
50
components/table/demo/responsive.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
order: 31
|
||||||
|
title:
|
||||||
|
zh-CN: 响应式
|
||||||
|
en-US: Responsive
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
响应式配置列的展示。
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
Responsive columns.
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { Table } from 'antd';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'Name (all screens)',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
render: text => <a>{text}</a>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Age (medium screen or bigger)',
|
||||||
|
dataIndex: 'age',
|
||||||
|
key: 'age',
|
||||||
|
responsive: ['md'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Address (large screen or bigger)',
|
||||||
|
dataIndex: 'address',
|
||||||
|
key: 'address',
|
||||||
|
responsive: ['lg'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
name: 'John Brown',
|
||||||
|
age: 32,
|
||||||
|
address: 'New York No. 1 Lake Park',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);
|
||||||
|
```
|
@ -1,5 +1,4 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { convertChildrenToColumns } from 'rc-table/lib/hooks/useColumns';
|
|
||||||
import {
|
import {
|
||||||
TransformColumns,
|
TransformColumns,
|
||||||
ColumnsType,
|
ColumnsType,
|
||||||
@ -162,8 +161,7 @@ export function getFilterData<RecordType>(
|
|||||||
interface FilterConfig<RecordType> {
|
interface FilterConfig<RecordType> {
|
||||||
prefixCls: string;
|
prefixCls: string;
|
||||||
dropdownPrefixCls: string;
|
dropdownPrefixCls: string;
|
||||||
columns?: ColumnsType<RecordType>;
|
mergedColumns: ColumnsType<RecordType>;
|
||||||
children?: React.ReactNode;
|
|
||||||
locale: TableLocale;
|
locale: TableLocale;
|
||||||
onFilterChange: (
|
onFilterChange: (
|
||||||
filters: Record<string, Key[] | null>,
|
filters: Record<string, Key[] | null>,
|
||||||
@ -175,8 +173,7 @@ interface FilterConfig<RecordType> {
|
|||||||
function useFilter<RecordType>({
|
function useFilter<RecordType>({
|
||||||
prefixCls,
|
prefixCls,
|
||||||
dropdownPrefixCls,
|
dropdownPrefixCls,
|
||||||
columns,
|
mergedColumns,
|
||||||
children,
|
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
getPopupContainer,
|
getPopupContainer,
|
||||||
locale: tableLocale,
|
locale: tableLocale,
|
||||||
@ -185,10 +182,6 @@ function useFilter<RecordType>({
|
|||||||
FilterState<RecordType>[],
|
FilterState<RecordType>[],
|
||||||
() => Record<string, Key[] | null>,
|
() => Record<string, Key[] | null>,
|
||||||
] {
|
] {
|
||||||
const mergedColumns = React.useMemo(() => {
|
|
||||||
return columns || convertChildrenToColumns(children);
|
|
||||||
}, [children, columns]);
|
|
||||||
|
|
||||||
const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>(
|
const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>(
|
||||||
collectFilterStates(mergedColumns, true),
|
collectFilterStates(mergedColumns, true),
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { convertChildrenToColumns } from 'rc-table/lib/hooks/useColumns';
|
|
||||||
import CaretDownOutlined from '@ant-design/icons/CaretDownOutlined';
|
import CaretDownOutlined from '@ant-design/icons/CaretDownOutlined';
|
||||||
import CaretUpOutlined from '@ant-design/icons/CaretUpOutlined';
|
import CaretUpOutlined from '@ant-design/icons/CaretUpOutlined';
|
||||||
import {
|
import {
|
||||||
@ -300,8 +299,7 @@ export function getSortData<RecordType>(
|
|||||||
|
|
||||||
interface SorterConfig<RecordType> {
|
interface SorterConfig<RecordType> {
|
||||||
prefixCls: string;
|
prefixCls: string;
|
||||||
columns?: ColumnsType<RecordType>;
|
mergedColumns: ColumnsType<RecordType>;
|
||||||
children?: React.ReactNode;
|
|
||||||
onSorterChange: (
|
onSorterChange: (
|
||||||
sorterResult: SorterResult<RecordType> | SorterResult<RecordType>[],
|
sorterResult: SorterResult<RecordType> | SorterResult<RecordType>[],
|
||||||
sortStates: SortState<RecordType>[],
|
sortStates: SortState<RecordType>[],
|
||||||
@ -313,8 +311,7 @@ interface SorterConfig<RecordType> {
|
|||||||
|
|
||||||
export default function useFilterSorter<RecordType>({
|
export default function useFilterSorter<RecordType>({
|
||||||
prefixCls,
|
prefixCls,
|
||||||
columns,
|
mergedColumns,
|
||||||
children,
|
|
||||||
onSorterChange,
|
onSorterChange,
|
||||||
sortDirections,
|
sortDirections,
|
||||||
tableLocale,
|
tableLocale,
|
||||||
@ -325,10 +322,6 @@ export default function useFilterSorter<RecordType>({
|
|||||||
ColumnTitleProps<RecordType>,
|
ColumnTitleProps<RecordType>,
|
||||||
() => SorterResult<RecordType> | SorterResult<RecordType>[],
|
() => SorterResult<RecordType> | SorterResult<RecordType>[],
|
||||||
] {
|
] {
|
||||||
const mergedColumns = React.useMemo(() => {
|
|
||||||
return columns || convertChildrenToColumns(children);
|
|
||||||
}, [children, columns]);
|
|
||||||
|
|
||||||
const [sortStates, setSortStates] = React.useState<SortState<RecordType>[]>(
|
const [sortStates, setSortStates] = React.useState<SortState<RecordType>[]>(
|
||||||
collectSortStates(mergedColumns, true),
|
collectSortStates(mergedColumns, true),
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
} from 'rc-table/lib/interface';
|
} from 'rc-table/lib/interface';
|
||||||
import { CheckboxProps } from '../checkbox';
|
import { CheckboxProps } from '../checkbox';
|
||||||
import { PaginationConfig } from '../pagination';
|
import { PaginationConfig } from '../pagination';
|
||||||
|
import { Breakpoint } from '../_util/responsiveObserve';
|
||||||
|
|
||||||
export { GetRowKey, ExpandableConfig };
|
export { GetRowKey, ExpandableConfig };
|
||||||
|
|
||||||
@ -95,6 +96,9 @@ export interface ColumnType<RecordType> extends RcColumnType<RecordType> {
|
|||||||
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
||||||
filterDropdownVisible?: boolean;
|
filterDropdownVisible?: boolean;
|
||||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||||
|
|
||||||
|
// Responsive
|
||||||
|
responsive?: Breakpoint[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>, 'dataIndex'> {
|
export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>, 'dataIndex'> {
|
||||||
|
@ -3,6 +3,7 @@ import './index.less';
|
|||||||
|
|
||||||
// style dependencies
|
// style dependencies
|
||||||
// deps-lint-skip: menu
|
// deps-lint-skip: menu
|
||||||
|
// deps-lint-skip: grid
|
||||||
import '../../button/style';
|
import '../../button/style';
|
||||||
import '../../empty/style';
|
import '../../empty/style';
|
||||||
import '../../radio/style';
|
import '../../radio/style';
|
||||||
|
Loading…
Reference in New Issue
Block a user