1
This commit is contained in:
+9
@@ -0,0 +1,9 @@
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
# @rc-component/table
|
||||
|
||||
React table component with useful functions.
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] [![bundle size][bundlephobia-image]][bundlephobia-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/@rc-component/table.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/@rc-component/table
|
||||
[github-actions-image]: https://github.com/react-component/table/actions/workflows/main.yml/badge.svg
|
||||
[github-actions-url]: https://github.com/react-component/table/actions/workflows/main.yml
|
||||
[coveralls-image]: https://img.shields.io/coveralls/react-component/table.svg?style=flat-square
|
||||
[coveralls-url]: https://coveralls.io/r/react-component/table?branch=master
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/table/master.svg?style=flat-square
|
||||
[codecov-url]: https://codecov.io/gh/react-component/table/branch/master
|
||||
[david-url]: https://david-dm.org/react-component/table
|
||||
[david-image]: https://david-dm.org/react-component/table/status.svg?style=flat-square
|
||||
[david-dev-url]: https://david-dm.org/react-component/table?type=dev
|
||||
[david-dev-image]: https://david-dm.org/react-component/table/dev-status.svg?style=flat-square
|
||||
[download-image]: https://img.shields.io/npm/dm/@rc-component/table.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/@rc-component/table
|
||||
[bundlephobia-url]: https://bundlephobia.com/result?p=@rc-component/table
|
||||
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/table
|
||||
|
||||
## install
|
||||
|
||||
[](https://npmjs.org/package/@rc-component/table)
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
https://table-react-component.vercel.app/
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import Table from '@rc-component/table';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'Operations',
|
||||
dataIndex: '',
|
||||
key: 'operations',
|
||||
render: () => <a href="#">Delete</a>,
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
{ name: 'Jack', age: 28, address: 'some where', key: '1' },
|
||||
{ name: 'Rose', age: 36, address: 'some where', key: '2' },
|
||||
];
|
||||
|
||||
React.render(<Table columns={columns} data={data} />, mountNode);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| tableLayout | `auto` \| `fixed` | `auto` \| `fixed` for any columns is fixed or ellipsis or header is fixed | https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout |
|
||||
| prefixCls | String | `rc-table` | |
|
||||
| className | String | | additional className |
|
||||
| id | String | | identifier of the container div |
|
||||
| useFixedHeader | Boolean | false | whether use separator table for header. better set width for columns |
|
||||
| scroll | Object | {x: false, y: false} | whether table can be scroll in x/y direction, `x` or `y` can be a number that indicated the width and height of table body |
|
||||
| expandable | Object | | Config expand props |
|
||||
| expandable.defaultExpandAllRows | Boolean | false | Expand All Rows initially |
|
||||
| expandable.defaultExpandedRowKeys | String[] | [] | initial expanded rows keys |
|
||||
| expandable.expandedRowKeys | String[] | | current expanded rows keys |
|
||||
| expandable.expandedRowRender | Function(recode, index, indent, expanded):ReactNode | | Content render to expanded row |
|
||||
| expandable.expandedRowClassName | `string` \| `(recode, index, indent) => string` | | get expanded row's className |
|
||||
| expandable.expandRowByClick | boolean | | Support expand by click row |
|
||||
| expandable.expandIconColumnIndex | Number | 0 | The index of expandIcon which column will be inserted when expandIconAsCell is false |
|
||||
| expandable.expandIcon | props => ReactNode | | Customize expand icon |
|
||||
| expandable.indentSize | Number | 15 | indentSize for every level of data.i.children, better using with column.width specified |
|
||||
| expandable.rowExpandable | (record) => boolean | | Config row support expandable |
|
||||
| expandable.onExpand | Function(expanded, record) | | function to call when click expand icon |
|
||||
| expandable.onExpandedRowsChange | Function(expandedRows) | | function to call when the expanded rows change |
|
||||
| expandable.fixed | String \| Boolean | - | this expand icon will be fixed when table scroll horizontally: true or `left` or `right` and `expandIconColumnIndex` need to stay first or last |
|
||||
| rowKey | string or Function(record, index):string | 'key' | If rowKey is string, `record[rowKey]` will be used as key. If rowKey is function, the return value of `rowKey(record, index)` will be use as key. |
|
||||
| rowClassName | string or Function(record, index, indent):string | | get row's className |
|
||||
| rowRef | Function(record, index, indent):string | | get row's ref key |
|
||||
| data | Object[] | | data record array to be rendered |
|
||||
| onRow | Function(record, index) | | Set custom props per each row. |
|
||||
| onHeaderRow | Function(record, index) | | Set custom props per each header row. |
|
||||
| showHeader | Boolean | true | whether table head is shown |
|
||||
| hidden | Boolean | `false` | Hidden column. |
|
||||
| title | Function(currentData) | | table title render function |
|
||||
| footer | Function(currentData) | | table footer render function |
|
||||
| emptyText | React.Node or Function | `No Data` | Display text when data is empty |
|
||||
| columns | Object[] | | The columns config of table, see table below |
|
||||
| components | Object | | Override table elements, see [#171](https://github.com/react-component/table/pull/171) for more details |
|
||||
| sticky | boolean \| {offsetHeader?: number, offsetScroll?: number, getContainer?: () => Window \| HTMLElement } | false | stick header and scroll bar |
|
||||
| summary | (data: readonly RecordType[]) => React.ReactNode | - | `summary` attribute in `table` component is used to define the summary row. |
|
||||
| rowHoverable | boolean | true | Table hover interaction |
|
||||
|
||||
## Column Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| key | String | | key of this column |
|
||||
| className | String | | className of this column |
|
||||
| colSpan | Number | | thead colSpan of this column |
|
||||
| title | React Node | | title of this column |
|
||||
| dataIndex | String | | display field of the data record |
|
||||
| width | String \| Number | | width of the specific proportion calculation according to the width of the columns |
|
||||
| minWidth | Number | | the minimum width of the column, only worked when tableLayout is auto |
|
||||
| fixed | String \| Boolean | | this column will be fixed when table scroll horizontally: true or 'left' or 'right' |
|
||||
| align | String | | specify how cell content is aligned |
|
||||
| ellipsis | Boolean | | specify whether cell content be ellipsized |
|
||||
| rowScope | 'row' \| 'rowgroup' | | Set scope attribute for all cells in this column |
|
||||
| onCell | Function(record, index) | | Set custom props per each cell. |
|
||||
| onHeaderCell | Function(record) | | Set custom props per each header cell. |
|
||||
| render | Function(value, row, index) | | The render function of cell, has three params: the text of this cell, the record of this row, the index of this row, it's return an object:{ children: value, props: { colSpan: 1, rowSpan:1 } } ==> 'children' is the text of this cell, props is some setting of this cell, eg: 'colspan' set td colspan, 'rowspan' set td rowspan |
|
||||
|
||||
## Summary Props
|
||||
|
||||
### Table.Summary
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| key | String | | key of this summary |
|
||||
| fixed | boolean \| 'top' \| 'bottom' | - | `true` fixes the summary row at the bottom of the table. `top` fixes the summary row at the top of the table, while `bottom` fixes it at the bottom. `undefined` or `false` makes the summary row scrollable along with the table. |
|
||||
|
||||
### Table.Summary.Row
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| key | String | | key of this summary |
|
||||
| className | String | - | className of this summary row |
|
||||
| style | React.CSSProperties | - | style of this summary row |
|
||||
| onClick | (e?: React.MouseEvent\<HTMLElement>) => void | - | The `onClick` attribute in `Table.Summary.Row` component can be used to set a click event handler for the summary row. |
|
||||
|
||||
## License
|
||||
|
||||
@rc-component/table is released under the MIT license.
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
.rc-table-tbody-virtual {
|
||||
border-left: 1px solid red;
|
||||
}
|
||||
.rc-table-tbody-virtual * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.rc-table-tbody-virtual .rc-table-row {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
.rc-table-tbody-virtual .rc-table-row-extra .rc-table-cell {
|
||||
background: rgba(200, 200, 255) !important;
|
||||
}
|
||||
.rc-table-tbody-virtual .rc-table-cell {
|
||||
flex: 0 0 var(--virtual-width);
|
||||
width: var(--virtual-width);
|
||||
padding: 8px 16px;
|
||||
border-right: 1px solid red;
|
||||
border-bottom: 1px solid red;
|
||||
}
|
||||
.rc-table {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.rc-table-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.rc-table table {
|
||||
width: 100%;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
.rc-table th,
|
||||
.rc-table td {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
padding: 16px 8px;
|
||||
white-space: normal;
|
||||
border: 1px solid red;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
transition: box-shadow 0.3s;
|
||||
}
|
||||
.rc-table-rtl.rc-table th,
|
||||
.rc-table-rtl.rc-table td {
|
||||
border-right: 0;
|
||||
border-left: 1px solid red;
|
||||
}
|
||||
.rc-table-cell {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
.rc-table-cell-fix {
|
||||
position: sticky !important;
|
||||
z-index: calc(2 + var(--z-offset));
|
||||
}
|
||||
.rc-table-cell-fix-start-shadow::after,
|
||||
.rc-table-cell-fix-end-shadow::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 5px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
content: '';
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.rc-table-cell-fix-start-shadow-show::after,
|
||||
.rc-table-cell-fix-end-shadow-show::after {
|
||||
opacity: 1;
|
||||
}
|
||||
.rc-table-cell-fix-start-shadow::after {
|
||||
inset-inline-start: 100%;
|
||||
}
|
||||
.rc-table-cell-fix-end-shadow::after {
|
||||
inset-inline-end: 100%;
|
||||
}
|
||||
.rc-table-cell.rc-table-cell-ellipsis {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.rc-table-cell.rc-table-cell-ellipsis.rc-table-cell-fix-start-shadow,
|
||||
.rc-table-cell.rc-table-cell-ellipsis.rc-table-cell-fix-end-shadow {
|
||||
overflow: visible;
|
||||
}
|
||||
.rc-table-cell.rc-table-cell-ellipsis.rc-table-cell-fix-start-shadow .rc-table-cell-content,
|
||||
.rc-table-cell.rc-table-cell-ellipsis.rc-table-cell-fix-end-shadow .rc-table-cell-content {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.rc-table-cell.rc-table-cell-row-hover {
|
||||
background: #fff4f4;
|
||||
}
|
||||
.rc-table-fix-start-shadow::before,
|
||||
.rc-table-fix-end-shadow::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 5px;
|
||||
}
|
||||
.rc-table-fix-start-shadow-show::before {
|
||||
inset-inline-start: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.rc-table-fix-end-shadow-show::after {
|
||||
inset-inline-end: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.rc-table-expand-icon-col {
|
||||
width: 60px;
|
||||
}
|
||||
.rc-table-row-expand-icon-cell {
|
||||
text-align: center;
|
||||
}
|
||||
.rc-table thead th {
|
||||
text-align: center;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.rc-table thead .rc-table-cell-scrollbar::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -1px;
|
||||
width: 1px;
|
||||
background: #f7f7f7;
|
||||
content: '';
|
||||
}
|
||||
.rc-table-rtl.rc-table thead .rc-table-cell-scrollbar::after {
|
||||
right: -1px;
|
||||
left: auto;
|
||||
}
|
||||
.rc-table-header {
|
||||
border: 1px solid red;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.rc-table-placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
.rc-table tbody tr td {
|
||||
background: #fff;
|
||||
}
|
||||
.rc-table tbody tr th {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.rc-table-content {
|
||||
border: 1px solid red;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
border-radius: 5px 0 0 0;
|
||||
}
|
||||
.rc-table-body {
|
||||
border: 1px solid red;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
.rc-table-fixed-column .rc-table-body::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
border-right: 1px solid red;
|
||||
content: '';
|
||||
}
|
||||
.rc-table-expanded-row .rc-table-cell {
|
||||
box-shadow: inset 0 8px 8px -8px green;
|
||||
}
|
||||
.rc-table-expanded-row-fixed {
|
||||
box-sizing: border-box;
|
||||
margin: -16px -8px;
|
||||
margin-right: -10px;
|
||||
padding: 16px 8px;
|
||||
}
|
||||
.rc-table-expanded-row-fixed::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1px;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
border-right: 1px solid red;
|
||||
content: '';
|
||||
}
|
||||
.rc-table-row-expand-icon {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #aaa;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 1px solid currentColor;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rc-table-row-expand-icon.rc-table-row-expanded::after {
|
||||
content: '-';
|
||||
}
|
||||
.rc-table-row-expand-icon.rc-table-row-collapsed::after {
|
||||
content: '+';
|
||||
}
|
||||
.rc-table-row-expand-icon.rc-table-row-spaced {
|
||||
visibility: hidden;
|
||||
}
|
||||
.rc-table-title {
|
||||
padding: 16px 8px;
|
||||
border: 1px solid red;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.rc-table-caption {
|
||||
padding: 16px 8px;
|
||||
border-right: 1px solid red;
|
||||
border-bottom: 1px solid red;
|
||||
}
|
||||
.rc-table-footer {
|
||||
padding: 16px 8px;
|
||||
border: 1px solid red;
|
||||
border-top: 0;
|
||||
}
|
||||
.rc-table tfoot td {
|
||||
background: #fff;
|
||||
}
|
||||
.rc-table-summary {
|
||||
border-top: 1px solid red;
|
||||
border-left: 1px solid red;
|
||||
}
|
||||
.rc-table-sticky-holder {
|
||||
position: sticky;
|
||||
z-index: 2;
|
||||
}
|
||||
.rc-table-sticky-scroll {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top: 1px solid #f3f3f3;
|
||||
opacity: 0.6;
|
||||
transition: transform 0.1s ease-in 0s;
|
||||
}
|
||||
.rc-table-sticky-scroll:hover {
|
||||
transform: scaleY(1.2);
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
.rc-table-sticky-scroll-bar {
|
||||
height: 8px;
|
||||
background-color: #bbb;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.rc-table-sticky-scroll-bar:hover {
|
||||
background-color: #999;
|
||||
}
|
||||
.rc-table-sticky-scroll-bar-active {
|
||||
background-color: #999;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import * as React from 'react';
|
||||
import useRowInfo from '../hooks/useRowInfo';
|
||||
import type { ColumnType, CustomizeComponent } from '../interface';
|
||||
import type { TableProps } from '..';
|
||||
export interface BodyRowProps<RecordType> {
|
||||
record: RecordType;
|
||||
index: number;
|
||||
renderIndex: number;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
classNames: TableProps['classNames']['body'];
|
||||
styles: TableProps['styles']['body'];
|
||||
rowComponent: CustomizeComponent;
|
||||
cellComponent: CustomizeComponent;
|
||||
scopeCellComponent: CustomizeComponent;
|
||||
indent?: number;
|
||||
rowKey: React.Key;
|
||||
rowKeys: React.Key[];
|
||||
expandedRowInfo?: {
|
||||
offset: number;
|
||||
colSpan: number;
|
||||
sticky: number;
|
||||
};
|
||||
}
|
||||
export declare function getCellProps<RecordType>(rowInfo: ReturnType<typeof useRowInfo<RecordType>>, column: ColumnType<RecordType>, colIndex: number, indent: number, index: number, rowKeys?: React.Key[], expandedRowOffset?: number): {
|
||||
key: React.Key;
|
||||
fixedInfo: import("../utils/fixUtil").FixedInfo;
|
||||
appendCellNode: React.ReactNode;
|
||||
additionalCellProps: React.HTMLAttributes<any> & React.TdHTMLAttributes<any>;
|
||||
};
|
||||
declare const _default: {
|
||||
<RecordType extends {
|
||||
children?: readonly RecordType[];
|
||||
}>(props: BodyRowProps<RecordType>): React.JSX.Element;
|
||||
displayName: string;
|
||||
};
|
||||
export default _default;
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import { clsx } from 'clsx';
|
||||
import * as React from 'react';
|
||||
import Cell from "../Cell";
|
||||
import { responseImmutable } from "../context/TableContext";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
import useRowInfo from "../hooks/useRowInfo";
|
||||
import ExpandedRow from "./ExpandedRow";
|
||||
import { computedExpandedClassName } from "../utils/expandUtil";
|
||||
// ==================================================================================
|
||||
// == getCellProps ==
|
||||
// ==================================================================================
|
||||
export function getCellProps(rowInfo, column, colIndex, indent, index, rowKeys = [], expandedRowOffset = 0) {
|
||||
const {
|
||||
record,
|
||||
prefixCls,
|
||||
columnsKey,
|
||||
fixedInfoList,
|
||||
expandIconColumnIndex,
|
||||
nestExpandable,
|
||||
indentSize,
|
||||
expandIcon,
|
||||
expanded,
|
||||
hasNestChildren,
|
||||
onTriggerExpand,
|
||||
expandable,
|
||||
expandedKeys
|
||||
} = rowInfo;
|
||||
const key = columnsKey[colIndex];
|
||||
const fixedInfo = fixedInfoList[colIndex];
|
||||
|
||||
// ============= Used for nest expandable =============
|
||||
let appendCellNode;
|
||||
if (colIndex === (expandIconColumnIndex || 0) && nestExpandable) {
|
||||
appendCellNode = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
||||
style: {
|
||||
paddingLeft: `${indentSize * indent}px`
|
||||
},
|
||||
className: `${prefixCls}-row-indent indent-level-${indent}`
|
||||
}), expandIcon({
|
||||
prefixCls,
|
||||
expanded,
|
||||
expandable: hasNestChildren,
|
||||
record,
|
||||
onExpand: onTriggerExpand
|
||||
}));
|
||||
}
|
||||
const additionalCellProps = column.onCell?.(record, index) || {};
|
||||
|
||||
// Expandable row has offset
|
||||
if (expandedRowOffset) {
|
||||
const {
|
||||
rowSpan = 1
|
||||
} = additionalCellProps;
|
||||
|
||||
// For expandable row with rowSpan,
|
||||
// We should increase the rowSpan if the row is expanded
|
||||
if (expandable && rowSpan && colIndex < expandedRowOffset) {
|
||||
let currentRowSpan = rowSpan;
|
||||
for (let i = index; i < index + rowSpan; i += 1) {
|
||||
const rowKey = rowKeys[i];
|
||||
if (expandedKeys.has(rowKey)) {
|
||||
currentRowSpan += 1;
|
||||
}
|
||||
}
|
||||
additionalCellProps.rowSpan = currentRowSpan;
|
||||
}
|
||||
}
|
||||
return {
|
||||
key,
|
||||
fixedInfo,
|
||||
appendCellNode,
|
||||
additionalCellProps: additionalCellProps
|
||||
};
|
||||
}
|
||||
|
||||
// ==================================================================================
|
||||
// == getCellProps ==
|
||||
// ==================================================================================
|
||||
const BodyRow = props => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
className,
|
||||
style,
|
||||
classNames,
|
||||
styles,
|
||||
record,
|
||||
index,
|
||||
renderIndex,
|
||||
rowKey,
|
||||
rowKeys,
|
||||
indent = 0,
|
||||
rowComponent: RowComponent,
|
||||
cellComponent,
|
||||
scopeCellComponent,
|
||||
expandedRowInfo
|
||||
} = props;
|
||||
const rowInfo = useRowInfo(record, rowKey, index, indent);
|
||||
const {
|
||||
prefixCls,
|
||||
flattenColumns,
|
||||
expandedRowClassName,
|
||||
expandedRowRender,
|
||||
rowProps,
|
||||
// Misc
|
||||
expanded,
|
||||
rowSupportExpand
|
||||
} = rowInfo;
|
||||
|
||||
// Force render expand row if expanded before
|
||||
const expandedRef = React.useRef(false);
|
||||
expandedRef.current ||= expanded;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
|
||||
// 若没有 expandedRowRender 参数, 将使用 baseRowNode 渲染 Children
|
||||
// 此时如果 level > 1 则说明是 expandedRow, 一样需要附加 computedExpandedRowClassName
|
||||
const expandedClsName = computedExpandedClassName(expandedRowClassName, record, index, indent);
|
||||
|
||||
// ======================== Base tr row ========================
|
||||
const baseRowNode = /*#__PURE__*/React.createElement(RowComponent, _extends({}, rowProps, {
|
||||
"data-row-key": rowKey,
|
||||
className: clsx(className, `${prefixCls}-row`, `${prefixCls}-row-level-${indent}`, rowProps?.className, classNames.row, {
|
||||
[expandedClsName]: indent >= 1
|
||||
}),
|
||||
style: {
|
||||
...style,
|
||||
...rowProps?.style,
|
||||
...styles.row
|
||||
}
|
||||
}), flattenColumns.map((column, colIndex) => {
|
||||
const {
|
||||
render,
|
||||
dataIndex,
|
||||
className: columnClassName
|
||||
} = column;
|
||||
const {
|
||||
key,
|
||||
fixedInfo,
|
||||
appendCellNode,
|
||||
additionalCellProps
|
||||
} = getCellProps(rowInfo, column, colIndex, indent, index, rowKeys, expandedRowInfo?.offset);
|
||||
return /*#__PURE__*/React.createElement(Cell, _extends({
|
||||
className: clsx(columnClassName, classNames.cell),
|
||||
style: styles.cell,
|
||||
ellipsis: column.ellipsis,
|
||||
align: column.align,
|
||||
scope: column.rowScope,
|
||||
component: column.rowScope ? scopeCellComponent : cellComponent,
|
||||
prefixCls: prefixCls,
|
||||
key: key,
|
||||
record: record,
|
||||
index: index,
|
||||
renderIndex: renderIndex,
|
||||
dataIndex: dataIndex,
|
||||
render: render,
|
||||
shouldCellUpdate: column.shouldCellUpdate
|
||||
}, fixedInfo, {
|
||||
appendNode: appendCellNode,
|
||||
additionalProps: additionalCellProps
|
||||
}));
|
||||
}));
|
||||
|
||||
// ======================== Expand Row =========================
|
||||
let expandRowNode;
|
||||
if (rowSupportExpand && (expandedRef.current || expanded)) {
|
||||
const expandContent = expandedRowRender(record, index, indent + 1, expanded);
|
||||
expandRowNode = /*#__PURE__*/React.createElement(ExpandedRow, {
|
||||
expanded: expanded,
|
||||
className: clsx(`${prefixCls}-expanded-row`, `${prefixCls}-expanded-row-level-${indent + 1}`, expandedClsName),
|
||||
prefixCls: prefixCls,
|
||||
component: RowComponent,
|
||||
cellComponent: cellComponent,
|
||||
colSpan: expandedRowInfo ? expandedRowInfo.colSpan : flattenColumns.length,
|
||||
isEmpty: false,
|
||||
stickyOffset: expandedRowInfo?.sticky
|
||||
}, expandContent);
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(React.Fragment, null, baseRowNode, expandRowNode);
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
BodyRow.displayName = 'BodyRow';
|
||||
}
|
||||
export default responseImmutable(BodyRow);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import type { CustomizeComponent } from '../interface';
|
||||
export interface ExpandedRowProps {
|
||||
prefixCls: string;
|
||||
component: CustomizeComponent;
|
||||
cellComponent: CustomizeComponent;
|
||||
className: string;
|
||||
expanded: boolean;
|
||||
children: React.ReactNode;
|
||||
colSpan: number;
|
||||
isEmpty: boolean;
|
||||
stickyOffset?: number;
|
||||
}
|
||||
declare const ExpandedRow: React.FC<ExpandedRowProps>;
|
||||
export default ExpandedRow;
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import * as React from 'react';
|
||||
import Cell from "../Cell";
|
||||
import TableContext from "../context/TableContext";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
const ExpandedRow = props => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
prefixCls,
|
||||
children,
|
||||
component: Component,
|
||||
cellComponent,
|
||||
className,
|
||||
expanded,
|
||||
colSpan,
|
||||
isEmpty,
|
||||
stickyOffset = 0
|
||||
} = props;
|
||||
const {
|
||||
scrollbarSize,
|
||||
fixHeader,
|
||||
fixColumn,
|
||||
componentWidth,
|
||||
horizonScroll
|
||||
} = useContext(TableContext, ['scrollbarSize', 'fixHeader', 'fixColumn', 'componentWidth', 'horizonScroll']);
|
||||
|
||||
// Cache render node
|
||||
let contentNode = children;
|
||||
if (isEmpty ? horizonScroll && componentWidth : fixColumn) {
|
||||
contentNode = /*#__PURE__*/React.createElement("div", {
|
||||
style: {
|
||||
width: componentWidth - stickyOffset - (fixHeader && !isEmpty ? scrollbarSize : 0),
|
||||
position: 'sticky',
|
||||
left: stickyOffset,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
className: `${prefixCls}-expanded-row-fixed`
|
||||
}, contentNode);
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(Component, {
|
||||
className: className,
|
||||
style: {
|
||||
display: expanded ? null : 'none'
|
||||
}
|
||||
}, /*#__PURE__*/React.createElement(Cell, {
|
||||
component: cellComponent,
|
||||
prefixCls: prefixCls,
|
||||
colSpan: colSpan
|
||||
}, contentNode));
|
||||
};
|
||||
export default ExpandedRow;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
export interface MeasureCellProps {
|
||||
columnKey: React.Key;
|
||||
onColumnResize: (key: React.Key, width: number) => void;
|
||||
title?: React.ReactNode;
|
||||
}
|
||||
declare const MeasureCell: React.FC<MeasureCellProps>;
|
||||
export default MeasureCell;
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import ResizeObserver from '@rc-component/resize-observer';
|
||||
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
|
||||
const MeasureCell = props => {
|
||||
const {
|
||||
columnKey,
|
||||
onColumnResize,
|
||||
title
|
||||
} = props;
|
||||
const cellRef = React.useRef(null);
|
||||
useLayoutEffect(() => {
|
||||
if (cellRef.current) {
|
||||
onColumnResize(columnKey, cellRef.current.offsetWidth);
|
||||
}
|
||||
}, []);
|
||||
return /*#__PURE__*/React.createElement(ResizeObserver, {
|
||||
data: columnKey
|
||||
}, /*#__PURE__*/React.createElement("td", {
|
||||
ref: cellRef,
|
||||
style: {
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
borderTop: 0,
|
||||
borderBottom: 0,
|
||||
height: 0
|
||||
}
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
style: {
|
||||
height: 0,
|
||||
overflow: 'hidden',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}, title || '\xa0')));
|
||||
};
|
||||
export default MeasureCell;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnType } from '../interface';
|
||||
export interface MeasureRowProps {
|
||||
prefixCls: string;
|
||||
onColumnResize: (key: React.Key, width: number) => void;
|
||||
columnsKey: React.Key[];
|
||||
columns: readonly ColumnType<any>[];
|
||||
}
|
||||
declare const MeasureRow: React.FC<MeasureRowProps>;
|
||||
export default MeasureRow;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import * as React from 'react';
|
||||
import ResizeObserver from '@rc-component/resize-observer';
|
||||
import MeasureCell from "./MeasureCell";
|
||||
import isVisible from "@rc-component/util/es/Dom/isVisible";
|
||||
import { useContext } from '@rc-component/context';
|
||||
import TableContext from "../context/TableContext";
|
||||
const MeasureRow = ({
|
||||
prefixCls,
|
||||
columnsKey,
|
||||
onColumnResize,
|
||||
columns
|
||||
}) => {
|
||||
const ref = React.useRef(null);
|
||||
const {
|
||||
measureRowRender
|
||||
} = useContext(TableContext, ['measureRowRender']);
|
||||
const measureRow = /*#__PURE__*/React.createElement("tr", {
|
||||
"aria-hidden": "true",
|
||||
className: `${prefixCls}-measure-row`,
|
||||
style: {
|
||||
height: 0
|
||||
},
|
||||
ref: ref
|
||||
}, /*#__PURE__*/React.createElement(ResizeObserver.Collection, {
|
||||
onBatchResize: infoList => {
|
||||
if (isVisible(ref.current)) {
|
||||
infoList.forEach(({
|
||||
data: columnKey,
|
||||
size
|
||||
}) => {
|
||||
onColumnResize(columnKey, size.offsetWidth);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, columnsKey.map(columnKey => {
|
||||
const column = columns.find(col => col.key === columnKey);
|
||||
const rawTitle = column?.title;
|
||||
const titleForMeasure = /*#__PURE__*/React.isValidElement(rawTitle) ? /*#__PURE__*/React.cloneElement(rawTitle, {
|
||||
ref: null
|
||||
}) : rawTitle;
|
||||
return /*#__PURE__*/React.createElement(MeasureCell, {
|
||||
key: columnKey,
|
||||
columnKey: columnKey,
|
||||
onColumnResize: onColumnResize,
|
||||
title: titleForMeasure
|
||||
});
|
||||
})));
|
||||
return typeof measureRowRender === 'function' ? measureRowRender(measureRow) : measureRow;
|
||||
};
|
||||
export default MeasureRow;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
export interface BodyProps<RecordType> {
|
||||
data: readonly RecordType[];
|
||||
measureColumnWidth: boolean;
|
||||
}
|
||||
declare const _default: {
|
||||
<RecordType>(props: BodyProps<RecordType>): React.JSX.Element;
|
||||
displayName: string;
|
||||
};
|
||||
export default _default;
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import * as React from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
import PerfContext from "../context/PerfContext";
|
||||
import TableContext, { responseImmutable } from "../context/TableContext";
|
||||
import useFlattenRecords from "../hooks/useFlattenRecords";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
import { getColumnsKey } from "../utils/valueUtil";
|
||||
import BodyRow from "./BodyRow";
|
||||
import ExpandedRow from "./ExpandedRow";
|
||||
import MeasureRow from "./MeasureRow";
|
||||
const Body = props => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
data,
|
||||
measureColumnWidth
|
||||
} = props;
|
||||
const {
|
||||
prefixCls,
|
||||
getComponent,
|
||||
onColumnResize,
|
||||
flattenColumns,
|
||||
getRowKey,
|
||||
expandedKeys,
|
||||
childrenColumnName,
|
||||
emptyNode,
|
||||
classNames,
|
||||
styles,
|
||||
expandedRowOffset = 0,
|
||||
colWidths
|
||||
} = useContext(TableContext, ['prefixCls', 'getComponent', 'onColumnResize', 'flattenColumns', 'getRowKey', 'expandedKeys', 'childrenColumnName', 'emptyNode', 'classNames', 'styles', 'expandedRowOffset', 'fixedInfoList', 'colWidths']);
|
||||
const {
|
||||
body: bodyCls = {}
|
||||
} = classNames || {};
|
||||
const {
|
||||
body: bodyStyles = {}
|
||||
} = styles || {};
|
||||
const flattenData = useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey);
|
||||
const rowKeys = React.useMemo(() => flattenData.map(item => item.rowKey), [flattenData]);
|
||||
|
||||
// =================== Performance ====================
|
||||
const perfRef = React.useRef({
|
||||
renderWithProps: false
|
||||
});
|
||||
|
||||
// ===================== Expanded =====================
|
||||
// `expandedRowOffset` data is same for all the rows.
|
||||
// Let's calc on Body side to save performance.
|
||||
const expandedRowInfo = React.useMemo(() => {
|
||||
const expandedColSpan = flattenColumns.length - expandedRowOffset;
|
||||
let expandedStickyStart = 0;
|
||||
for (let i = 0; i < expandedRowOffset; i += 1) {
|
||||
expandedStickyStart += colWidths[i] || 0;
|
||||
}
|
||||
return {
|
||||
offset: expandedRowOffset,
|
||||
colSpan: expandedColSpan,
|
||||
sticky: expandedStickyStart
|
||||
};
|
||||
}, [flattenColumns.length, expandedRowOffset, colWidths]);
|
||||
|
||||
// ====================== Render ======================
|
||||
const WrapperComponent = getComponent(['body', 'wrapper'], 'tbody');
|
||||
const trComponent = getComponent(['body', 'row'], 'tr');
|
||||
const tdComponent = getComponent(['body', 'cell'], 'td');
|
||||
const thComponent = getComponent(['body', 'cell'], 'th');
|
||||
let rows;
|
||||
if (data.length) {
|
||||
rows = flattenData.map((item, idx) => {
|
||||
const {
|
||||
record,
|
||||
indent,
|
||||
index: renderIndex,
|
||||
rowKey
|
||||
} = item;
|
||||
return /*#__PURE__*/React.createElement(BodyRow, {
|
||||
classNames: bodyCls,
|
||||
styles: bodyStyles,
|
||||
key: rowKey,
|
||||
rowKey: rowKey,
|
||||
rowKeys: rowKeys,
|
||||
record: record,
|
||||
index: idx,
|
||||
renderIndex: renderIndex,
|
||||
rowComponent: trComponent,
|
||||
cellComponent: tdComponent,
|
||||
scopeCellComponent: thComponent,
|
||||
indent: indent
|
||||
// Expanded row info
|
||||
,
|
||||
expandedRowInfo: expandedRowInfo
|
||||
});
|
||||
});
|
||||
} else {
|
||||
rows = /*#__PURE__*/React.createElement(ExpandedRow, {
|
||||
expanded: true,
|
||||
className: `${prefixCls}-placeholder`,
|
||||
prefixCls: prefixCls,
|
||||
component: trComponent,
|
||||
cellComponent: tdComponent,
|
||||
colSpan: flattenColumns.length,
|
||||
isEmpty: true
|
||||
}, emptyNode);
|
||||
}
|
||||
const columnsKey = getColumnsKey(flattenColumns);
|
||||
return /*#__PURE__*/React.createElement(PerfContext.Provider, {
|
||||
value: perfRef.current
|
||||
}, /*#__PURE__*/React.createElement(WrapperComponent, {
|
||||
style: bodyStyles.wrapper,
|
||||
className: clsx(`${prefixCls}-tbody`, bodyCls.wrapper)
|
||||
}, measureColumnWidth && /*#__PURE__*/React.createElement(MeasureRow, {
|
||||
prefixCls: prefixCls,
|
||||
columnsKey: columnsKey,
|
||||
onColumnResize: onColumnResize,
|
||||
columns: flattenColumns
|
||||
}), rows));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Body.displayName = 'Body';
|
||||
}
|
||||
export default responseImmutable(Body);
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import * as React from 'react';
|
||||
import type { AlignType, CellEllipsisType, ColumnType, CustomizeComponent, DataIndex, DefaultRecordType, ScopeType } from '../interface';
|
||||
export interface CellProps<RecordType extends DefaultRecordType> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
record?: RecordType;
|
||||
/** `column` index is the real show rowIndex */
|
||||
index?: number;
|
||||
/** the index of the record. For the render(value, record, renderIndex) */
|
||||
renderIndex?: number;
|
||||
dataIndex?: DataIndex<RecordType>;
|
||||
render?: ColumnType<RecordType>['render'];
|
||||
component?: CustomizeComponent;
|
||||
children?: React.ReactNode;
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
scope?: ScopeType;
|
||||
ellipsis?: CellEllipsisType;
|
||||
align?: AlignType;
|
||||
shouldCellUpdate?: (record: RecordType, prevRecord: RecordType) => boolean;
|
||||
fixStart?: number | false;
|
||||
fixEnd?: number | false;
|
||||
fixedStartShadow?: boolean;
|
||||
fixedEndShadow?: boolean;
|
||||
offsetFixedStartShadow?: number;
|
||||
offsetFixedEndShadow?: number;
|
||||
zIndex?: number;
|
||||
zIndexReverse?: number;
|
||||
allColsFixedLeft?: boolean;
|
||||
/** @private Used for `expandable` with nest tree */
|
||||
appendNode?: React.ReactNode;
|
||||
additionalProps?: React.TdHTMLAttributes<HTMLTableCellElement>;
|
||||
rowType?: 'header' | 'body' | 'footer';
|
||||
isSticky?: boolean;
|
||||
}
|
||||
declare const _default: <RecordType>(props: CellProps<RecordType>) => React.JSX.Element;
|
||||
export default _default;
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { clsx } from 'clsx';
|
||||
import * as React from 'react';
|
||||
import TableContext from "../context/TableContext";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
import useCellRender from "./useCellRender";
|
||||
import useHoverState from "./useHoverState";
|
||||
import { useEvent } from '@rc-component/util';
|
||||
const getTitleFromCellRenderChildren = ({
|
||||
ellipsis,
|
||||
rowType,
|
||||
children
|
||||
}) => {
|
||||
let title;
|
||||
const ellipsisConfig = ellipsis === true ? {
|
||||
showTitle: true
|
||||
} : ellipsis;
|
||||
if (ellipsisConfig && (ellipsisConfig.showTitle || rowType === 'header')) {
|
||||
if (typeof children === 'string' || typeof children === 'number') {
|
||||
title = children.toString();
|
||||
} else if ( /*#__PURE__*/React.isValidElement(children) && typeof children.props?.children === 'string') {
|
||||
title = children.props?.children;
|
||||
}
|
||||
}
|
||||
return title;
|
||||
};
|
||||
const Cell = props => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
component: Component,
|
||||
children,
|
||||
ellipsis,
|
||||
scope,
|
||||
// Style
|
||||
prefixCls,
|
||||
className,
|
||||
style,
|
||||
align,
|
||||
// Value
|
||||
record,
|
||||
render,
|
||||
dataIndex,
|
||||
renderIndex,
|
||||
shouldCellUpdate,
|
||||
// Row
|
||||
index,
|
||||
rowType,
|
||||
// Span
|
||||
colSpan,
|
||||
rowSpan,
|
||||
// Fixed
|
||||
fixStart,
|
||||
fixEnd,
|
||||
fixedStartShadow,
|
||||
fixedEndShadow,
|
||||
offsetFixedStartShadow,
|
||||
offsetFixedEndShadow,
|
||||
zIndex,
|
||||
zIndexReverse,
|
||||
// Private
|
||||
appendNode,
|
||||
additionalProps = {},
|
||||
isSticky
|
||||
} = props;
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
const {
|
||||
allColumnsFixedLeft,
|
||||
rowHoverable
|
||||
} = useContext(TableContext, ['allColumnsFixedLeft', 'rowHoverable']);
|
||||
|
||||
// ====================== Value =======================
|
||||
const [childNode, legacyCellProps] = useCellRender(record, dataIndex, renderIndex, children, render, shouldCellUpdate);
|
||||
|
||||
// ====================== Fixed =======================
|
||||
const fixedStyle = {};
|
||||
const isFixStart = typeof fixStart === 'number' && !allColumnsFixedLeft;
|
||||
const isFixEnd = typeof fixEnd === 'number' && !allColumnsFixedLeft;
|
||||
const [showFixStartShadow, showFixEndShadow] = useContext(TableContext, ({
|
||||
scrollInfo
|
||||
}) => {
|
||||
if (!isFixStart && !isFixEnd) {
|
||||
return [false, false];
|
||||
}
|
||||
const [absScroll, scrollWidth] = scrollInfo;
|
||||
const showStartShadow = (isFixStart && fixedStartShadow && absScroll) -
|
||||
// For precision, we not show shadow by default which has better user experience.
|
||||
offsetFixedStartShadow >= 1;
|
||||
const showEndShadow = (isFixEnd && fixedEndShadow && scrollWidth - absScroll) -
|
||||
// Same as above
|
||||
offsetFixedEndShadow > 1;
|
||||
return [showStartShadow, showEndShadow];
|
||||
});
|
||||
if (isFixStart) {
|
||||
fixedStyle.insetInlineStart = fixStart;
|
||||
fixedStyle['--z-offset'] = zIndex;
|
||||
fixedStyle['--z-offset-reverse'] = zIndexReverse;
|
||||
}
|
||||
if (isFixEnd) {
|
||||
fixedStyle.insetInlineEnd = fixEnd;
|
||||
fixedStyle['--z-offset'] = zIndex;
|
||||
fixedStyle['--z-offset-reverse'] = zIndexReverse;
|
||||
}
|
||||
|
||||
// ================ RowSpan & ColSpan =================
|
||||
const mergedColSpan = legacyCellProps?.colSpan ?? additionalProps.colSpan ?? colSpan ?? 1;
|
||||
const mergedRowSpan = legacyCellProps?.rowSpan ?? additionalProps.rowSpan ?? rowSpan ?? 1;
|
||||
|
||||
// ====================== Hover =======================
|
||||
const [hovering, onHover] = useHoverState(index, mergedRowSpan);
|
||||
const onMouseEnter = useEvent(event => {
|
||||
if (record) {
|
||||
onHover(index, index + mergedRowSpan - 1);
|
||||
}
|
||||
additionalProps?.onMouseEnter?.(event);
|
||||
});
|
||||
const onMouseLeave = useEvent(event => {
|
||||
if (record) {
|
||||
onHover(-1, -1);
|
||||
}
|
||||
additionalProps?.onMouseLeave?.(event);
|
||||
});
|
||||
|
||||
// ====================== Render ======================
|
||||
if (mergedColSpan === 0 || mergedRowSpan === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// >>>>> Title
|
||||
const title = additionalProps.title ?? getTitleFromCellRenderChildren({
|
||||
rowType,
|
||||
ellipsis,
|
||||
children: childNode
|
||||
});
|
||||
|
||||
// >>>>> ClassName
|
||||
const mergedClassName = clsx(cellPrefixCls, className, {
|
||||
// Fixed
|
||||
[`${cellPrefixCls}-fix`]: isFixStart || isFixEnd,
|
||||
[`${cellPrefixCls}-fix-start`]: isFixStart,
|
||||
[`${cellPrefixCls}-fix-end`]: isFixEnd,
|
||||
// Fixed shadow
|
||||
[`${cellPrefixCls}-fix-start-shadow`]: fixedStartShadow,
|
||||
[`${cellPrefixCls}-fix-start-shadow-show`]: fixedStartShadow && showFixStartShadow,
|
||||
[`${cellPrefixCls}-fix-end-shadow`]: fixedEndShadow,
|
||||
[`${cellPrefixCls}-fix-end-shadow-show`]: fixedEndShadow && showFixEndShadow,
|
||||
[`${cellPrefixCls}-ellipsis`]: ellipsis,
|
||||
[`${cellPrefixCls}-with-append`]: appendNode,
|
||||
[`${cellPrefixCls}-fix-sticky`]: (isFixStart || isFixEnd) && isSticky,
|
||||
[`${cellPrefixCls}-row-hover`]: !legacyCellProps && hovering
|
||||
}, additionalProps.className, legacyCellProps?.className);
|
||||
|
||||
// >>>>> Style
|
||||
const alignStyle = {};
|
||||
if (align) {
|
||||
alignStyle.textAlign = align;
|
||||
}
|
||||
|
||||
// The order is important since user can overwrite style.
|
||||
// For example ant-design/ant-design#51763
|
||||
const mergedStyle = {
|
||||
...legacyCellProps?.style,
|
||||
...fixedStyle,
|
||||
...alignStyle,
|
||||
...additionalProps.style,
|
||||
...style
|
||||
};
|
||||
|
||||
// >>>>> Children Node
|
||||
let mergedChildNode = childNode;
|
||||
|
||||
// Not crash if final `childNode` is not validate ReactNode
|
||||
if (typeof mergedChildNode === 'object' && !Array.isArray(mergedChildNode) && ! /*#__PURE__*/React.isValidElement(mergedChildNode)) {
|
||||
mergedChildNode = null;
|
||||
}
|
||||
if (ellipsis && (fixedStartShadow || fixedEndShadow)) {
|
||||
mergedChildNode = /*#__PURE__*/React.createElement("span", {
|
||||
className: `${cellPrefixCls}-content`
|
||||
}, mergedChildNode);
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(Component, _extends({}, legacyCellProps, additionalProps, {
|
||||
className: mergedClassName,
|
||||
style: mergedStyle
|
||||
// A11y
|
||||
,
|
||||
title: title,
|
||||
scope: scope
|
||||
// Hover
|
||||
,
|
||||
onMouseEnter: rowHoverable ? onMouseEnter : undefined,
|
||||
onMouseLeave: rowHoverable ? onMouseLeave : undefined
|
||||
//Span
|
||||
,
|
||||
colSpan: mergedColSpan !== 1 ? mergedColSpan : null,
|
||||
rowSpan: mergedRowSpan !== 1 ? mergedRowSpan : null
|
||||
}), appendNode, mergedChildNode);
|
||||
};
|
||||
export default /*#__PURE__*/React.memo(Cell);
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import * as React from 'react';
|
||||
import type { CellType, ColumnType, DataIndex } from '../interface';
|
||||
export default function useCellRender<RecordType>(record: RecordType, dataIndex: DataIndex<RecordType>, renderIndex: number, children?: React.ReactNode, render?: ColumnType<RecordType>['render'], shouldCellUpdate?: ColumnType<RecordType>['shouldCellUpdate']): [React.ReactNode, CellType<RecordType>] | [React.ReactNode];
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import useMemo from "@rc-component/util/es/hooks/useMemo";
|
||||
import isEqual from "@rc-component/util/es/isEqual";
|
||||
import getValue from "@rc-component/util/es/utils/get";
|
||||
import warning from "@rc-component/util/es/warning";
|
||||
import * as React from 'react';
|
||||
import PerfContext from "../context/PerfContext";
|
||||
import { validateValue } from "../utils/valueUtil";
|
||||
import { useImmutableMark } from "../context/TableContext";
|
||||
function isRenderCell(data) {
|
||||
return data && typeof data === 'object' && !Array.isArray(data) && ! /*#__PURE__*/React.isValidElement(data);
|
||||
}
|
||||
export default function useCellRender(record, dataIndex, renderIndex, children, render, shouldCellUpdate) {
|
||||
// TODO: Remove this after next major version
|
||||
const perfRecord = React.useContext(PerfContext);
|
||||
const mark = useImmutableMark();
|
||||
|
||||
// ======================== Render ========================
|
||||
const retData = useMemo(() => {
|
||||
if (validateValue(children)) {
|
||||
return [children];
|
||||
}
|
||||
const path = dataIndex === null || dataIndex === undefined || dataIndex === '' ? [] : Array.isArray(dataIndex) ? dataIndex : [dataIndex];
|
||||
const value = getValue(record, path);
|
||||
|
||||
// Customize render node
|
||||
let returnChildNode = value;
|
||||
let returnCellProps = undefined;
|
||||
if (render) {
|
||||
const renderData = render(value, record, renderIndex);
|
||||
if (isRenderCell(renderData)) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(false, '`columns.render` return cell props is deprecated with perf issue, please use `onCell` instead.');
|
||||
}
|
||||
returnChildNode = renderData.children;
|
||||
returnCellProps = renderData.props;
|
||||
perfRecord.renderWithProps = true;
|
||||
} else {
|
||||
returnChildNode = renderData;
|
||||
}
|
||||
}
|
||||
return [returnChildNode, returnCellProps];
|
||||
}, [
|
||||
// Force update deps
|
||||
mark,
|
||||
// Normal deps
|
||||
record, children, dataIndex, render, renderIndex], (prev, next) => {
|
||||
if (shouldCellUpdate) {
|
||||
const [, prevRecord] = prev;
|
||||
const [, nextRecord] = next;
|
||||
return shouldCellUpdate(nextRecord, prevRecord);
|
||||
}
|
||||
|
||||
// Legacy mode should always update
|
||||
if (perfRecord.renderWithProps) {
|
||||
return true;
|
||||
}
|
||||
return !isEqual(prev, next, true);
|
||||
});
|
||||
return retData;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import type { OnHover } from '../hooks/useHover';
|
||||
export default function useHoverState(rowIndex: number, rowSpan: number): [hovering: boolean, onHover: OnHover];
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import TableContext from "../context/TableContext";
|
||||
/** Check if cell is in hover range */
|
||||
function inHoverRange(cellStartRow, cellRowSpan, startRow, endRow) {
|
||||
const cellEndRow = cellStartRow + cellRowSpan - 1;
|
||||
return cellStartRow <= endRow && cellEndRow >= startRow;
|
||||
}
|
||||
export default function useHoverState(rowIndex, rowSpan) {
|
||||
return useContext(TableContext, ctx => {
|
||||
const hovering = inHoverRange(rowIndex, rowSpan || 1, ctx.hoverStartRow, ctx.hoverEndRow);
|
||||
return [hovering, ctx.onHover];
|
||||
});
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnType } from './interface';
|
||||
export interface ColGroupProps<RecordType> {
|
||||
colWidths: readonly (number | string)[];
|
||||
columns?: readonly ColumnType<RecordType>[];
|
||||
columCount?: number;
|
||||
}
|
||||
declare const ColGroup: <RecordType>(props: ColGroupProps<RecordType>) => React.JSX.Element;
|
||||
export default ColGroup;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import * as React from 'react';
|
||||
import { INTERNAL_COL_DEFINE } from "./utils/legacyUtil";
|
||||
import { useContext } from '@rc-component/context';
|
||||
import TableContext from "./context/TableContext";
|
||||
const ColGroup = props => {
|
||||
const {
|
||||
colWidths,
|
||||
columns,
|
||||
columCount
|
||||
} = props;
|
||||
const {
|
||||
tableLayout
|
||||
} = useContext(TableContext, ['tableLayout']);
|
||||
const cols = [];
|
||||
const len = columCount || columns.length;
|
||||
|
||||
// Only insert col with width & additional props
|
||||
// Skip if rest col do not have any useful info
|
||||
let mustInsert = false;
|
||||
for (let i = len - 1; i >= 0; i -= 1) {
|
||||
const width = colWidths[i];
|
||||
const column = columns && columns[i];
|
||||
let additionalProps;
|
||||
let minWidth;
|
||||
if (column) {
|
||||
additionalProps = column[INTERNAL_COL_DEFINE];
|
||||
|
||||
// fixed will cause layout problems
|
||||
if (tableLayout === 'auto') {
|
||||
minWidth = column.minWidth;
|
||||
}
|
||||
}
|
||||
if (width || minWidth || additionalProps || mustInsert) {
|
||||
const {
|
||||
columnType,
|
||||
...restAdditionalProps
|
||||
} = additionalProps || {};
|
||||
cols.unshift( /*#__PURE__*/React.createElement("col", _extends({
|
||||
key: i,
|
||||
style: {
|
||||
width,
|
||||
minWidth
|
||||
}
|
||||
}, restAdditionalProps)));
|
||||
mustInsert = true;
|
||||
}
|
||||
}
|
||||
return cols.length > 0 ? /*#__PURE__*/React.createElement("colgroup", null, cols) : null;
|
||||
};
|
||||
export default ColGroup;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import * as React from 'react';
|
||||
import type { HeaderProps } from '../Header/Header';
|
||||
import type { Direction, TableLayout } from '../interface';
|
||||
export interface FixedHeaderProps<RecordType> extends HeaderProps<RecordType> {
|
||||
className: string;
|
||||
style?: React.CSSProperties;
|
||||
noData: boolean;
|
||||
maxContentScroll: boolean;
|
||||
colWidths: readonly number[];
|
||||
columCount: number;
|
||||
direction: Direction;
|
||||
fixHeader: boolean;
|
||||
stickyTopOffset?: number;
|
||||
stickyBottomOffset?: number;
|
||||
stickyClassName?: string;
|
||||
scrollX?: number | string | true;
|
||||
tableLayout?: TableLayout;
|
||||
onScroll: (info: {
|
||||
currentTarget: HTMLDivElement;
|
||||
scrollLeft?: number;
|
||||
}) => void;
|
||||
children: (info: HeaderProps<RecordType>) => React.ReactNode;
|
||||
colGroup?: React.ReactNode;
|
||||
}
|
||||
/** Return a table in div as fixed element which contains sticky info */
|
||||
declare const _default: React.NamedExoticComponent<FixedHeaderProps<any> & React.RefAttributes<HTMLDivElement>>;
|
||||
export default _default;
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { clsx } from 'clsx';
|
||||
import { fillRef } from "@rc-component/util/es/ref";
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import ColGroup from "../ColGroup";
|
||||
import TableContext from "../context/TableContext";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
function useColumnWidth(colWidths, columCount) {
|
||||
return useMemo(() => {
|
||||
const cloneColumns = [];
|
||||
for (let i = 0; i < columCount; i += 1) {
|
||||
const val = colWidths[i];
|
||||
if (val !== undefined) {
|
||||
cloneColumns[i] = val;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return cloneColumns;
|
||||
}, [colWidths.join('_'), columCount]);
|
||||
}
|
||||
const FixedHolder = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
className,
|
||||
style,
|
||||
noData,
|
||||
columns,
|
||||
flattenColumns,
|
||||
colWidths,
|
||||
colGroup,
|
||||
columCount,
|
||||
stickyOffsets,
|
||||
direction,
|
||||
fixHeader,
|
||||
stickyTopOffset,
|
||||
stickyBottomOffset,
|
||||
stickyClassName,
|
||||
scrollX,
|
||||
tableLayout = 'fixed',
|
||||
onScroll,
|
||||
maxContentScroll,
|
||||
children,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
prefixCls,
|
||||
scrollbarSize,
|
||||
isSticky,
|
||||
getComponent
|
||||
} = useContext(TableContext, ['prefixCls', 'scrollbarSize', 'isSticky', 'getComponent']);
|
||||
const TableComponent = getComponent(['header', 'table'], 'table');
|
||||
const combinationScrollBarSize = isSticky && !fixHeader ? 0 : scrollbarSize;
|
||||
|
||||
// Pass wheel to scroll event
|
||||
const scrollRef = React.useRef(null);
|
||||
const setScrollRef = React.useCallback(element => {
|
||||
fillRef(ref, element);
|
||||
fillRef(scrollRef, element);
|
||||
}, []);
|
||||
React.useEffect(() => {
|
||||
function onWheel(e) {
|
||||
const {
|
||||
currentTarget,
|
||||
deltaX
|
||||
} = e;
|
||||
if (deltaX) {
|
||||
const {
|
||||
scrollLeft,
|
||||
scrollWidth,
|
||||
clientWidth
|
||||
} = currentTarget;
|
||||
const maxScrollWidth = scrollWidth - clientWidth;
|
||||
let nextScroll = scrollLeft + deltaX;
|
||||
if (direction === 'rtl') {
|
||||
nextScroll = Math.max(-maxScrollWidth, nextScroll);
|
||||
nextScroll = Math.min(0, nextScroll);
|
||||
} else {
|
||||
nextScroll = Math.min(maxScrollWidth, nextScroll);
|
||||
nextScroll = Math.max(0, nextScroll);
|
||||
}
|
||||
onScroll({
|
||||
currentTarget,
|
||||
scrollLeft: nextScroll
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
const scrollEle = scrollRef.current;
|
||||
scrollEle?.addEventListener('wheel', onWheel, {
|
||||
passive: false
|
||||
});
|
||||
return () => {
|
||||
scrollEle?.removeEventListener('wheel', onWheel);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Add scrollbar column
|
||||
const lastColumn = flattenColumns[flattenColumns.length - 1];
|
||||
const ScrollBarColumn = {
|
||||
fixed: lastColumn ? lastColumn.fixed : null,
|
||||
scrollbar: true,
|
||||
onHeaderCell: () => ({
|
||||
className: `${prefixCls}-cell-scrollbar`
|
||||
})
|
||||
};
|
||||
const columnsWithScrollbar = useMemo(() => combinationScrollBarSize ? [...columns, ScrollBarColumn] : columns, [combinationScrollBarSize, columns]);
|
||||
const flattenColumnsWithScrollbar = useMemo(() => combinationScrollBarSize ? [...flattenColumns, ScrollBarColumn] : flattenColumns, [combinationScrollBarSize, flattenColumns]);
|
||||
|
||||
// Calculate the sticky offsets
|
||||
const headerStickyOffsets = useMemo(() => {
|
||||
const {
|
||||
start,
|
||||
end
|
||||
} = stickyOffsets;
|
||||
return {
|
||||
...stickyOffsets,
|
||||
// left:
|
||||
// direction === 'rtl' ? [...left.map(width => width + combinationScrollBarSize), 0] : left,
|
||||
// right:
|
||||
// direction === 'rtl' ? right : [...right.map(width => width + combinationScrollBarSize), 0],
|
||||
start: start,
|
||||
end: [...end.map(width => width + combinationScrollBarSize), 0],
|
||||
isSticky
|
||||
};
|
||||
}, [combinationScrollBarSize, stickyOffsets, isSticky]);
|
||||
const mergedColumnWidth = useColumnWidth(colWidths, columCount);
|
||||
const isColGroupEmpty = useMemo(() => {
|
||||
// use original ColGroup if no data or no calculated column width, otherwise use calculated column width
|
||||
// Return original colGroup if no data, or mergedColumnWidth is empty, or all widths are falsy
|
||||
const noWidth = !mergedColumnWidth || !mergedColumnWidth.length || mergedColumnWidth.every(w => !w);
|
||||
return noData || noWidth;
|
||||
}, [noData, mergedColumnWidth]);
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
...(isSticky ? {
|
||||
top: stickyTopOffset,
|
||||
bottom: stickyBottomOffset
|
||||
} : {}),
|
||||
...style
|
||||
},
|
||||
ref: setScrollRef,
|
||||
className: clsx(className, {
|
||||
[stickyClassName]: !!stickyClassName
|
||||
})
|
||||
}, /*#__PURE__*/React.createElement(TableComponent, {
|
||||
style: {
|
||||
tableLayout,
|
||||
minWidth: '100%',
|
||||
// https://github.com/ant-design/ant-design/issues/54894
|
||||
width: scrollX
|
||||
}
|
||||
}, isColGroupEmpty ? colGroup : /*#__PURE__*/React.createElement(ColGroup, {
|
||||
colWidths: [...mergedColumnWidth, combinationScrollBarSize],
|
||||
columCount: columCount + 1,
|
||||
columns: flattenColumnsWithScrollbar
|
||||
}), children({
|
||||
...restProps,
|
||||
stickyOffsets: headerStickyOffsets,
|
||||
columns: columnsWithScrollbar,
|
||||
flattenColumns: flattenColumnsWithScrollbar
|
||||
})));
|
||||
});
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
FixedHolder.displayName = 'FixedHolder';
|
||||
}
|
||||
|
||||
/** Return a table in div as fixed element which contains sticky info */
|
||||
// export default responseImmutable(FixedHolder);
|
||||
export default /*#__PURE__*/React.memo(FixedHolder);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import type { AlignType } from '../interface';
|
||||
export interface SummaryCellProps {
|
||||
className?: string;
|
||||
index: number;
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
align?: AlignType;
|
||||
}
|
||||
declare const SummaryCell: React.FC<React.PropsWithChildren<SummaryCellProps>>;
|
||||
export default SummaryCell;
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import * as React from 'react';
|
||||
import Cell from "../Cell";
|
||||
import TableContext from "../context/TableContext";
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { getCellFixedInfo } from "../utils/fixUtil";
|
||||
import SummaryContext from "./SummaryContext";
|
||||
const SummaryCell = props => {
|
||||
const {
|
||||
className,
|
||||
index,
|
||||
children,
|
||||
colSpan = 1,
|
||||
rowSpan,
|
||||
align
|
||||
} = props;
|
||||
const {
|
||||
prefixCls
|
||||
} = useContext(TableContext, ['prefixCls']);
|
||||
const {
|
||||
scrollColumnIndex,
|
||||
stickyOffsets,
|
||||
flattenColumns
|
||||
} = React.useContext(SummaryContext);
|
||||
const lastIndex = index + colSpan - 1;
|
||||
const mergedColSpan = lastIndex + 1 === scrollColumnIndex ? colSpan + 1 : colSpan;
|
||||
const fixedInfo = React.useMemo(() => getCellFixedInfo(index, index + mergedColSpan - 1, flattenColumns, stickyOffsets), [index, mergedColSpan, flattenColumns, stickyOffsets]);
|
||||
return /*#__PURE__*/React.createElement(Cell, _extends({
|
||||
className: className,
|
||||
index: index,
|
||||
component: "td",
|
||||
prefixCls: prefixCls,
|
||||
record: null,
|
||||
dataIndex: null,
|
||||
align: align,
|
||||
colSpan: mergedColSpan,
|
||||
rowSpan: rowSpan,
|
||||
render: () => children
|
||||
}, fixedInfo));
|
||||
};
|
||||
export default SummaryCell;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
export interface FooterRowProps {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
onClick?: React.MouseEventHandler<HTMLTableRowElement>;
|
||||
}
|
||||
declare const FooterRow: React.FC<React.PropsWithChildren<FooterRowProps>>;
|
||||
export default FooterRow;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import * as React from 'react';
|
||||
const FooterRow = props => {
|
||||
const {
|
||||
children,
|
||||
...restProps
|
||||
} = props;
|
||||
return /*#__PURE__*/React.createElement("tr", restProps, children);
|
||||
};
|
||||
export default FooterRow;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import type * as React from 'react';
|
||||
import Cell from './Cell';
|
||||
import Row from './Row';
|
||||
export interface SummaryProps {
|
||||
fixed?: boolean | 'top' | 'bottom';
|
||||
}
|
||||
/**
|
||||
* Syntactic sugar. Do not support HOC.
|
||||
*/
|
||||
declare const Summary: React.FC<React.PropsWithChildren<SummaryProps>> & {
|
||||
Row: typeof Row;
|
||||
Cell: typeof Cell;
|
||||
};
|
||||
export default Summary;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import Cell from "./Cell";
|
||||
import Row from "./Row";
|
||||
/**
|
||||
* Syntactic sugar. Do not support HOC.
|
||||
*/
|
||||
const Summary = props => {
|
||||
const {
|
||||
children
|
||||
} = props;
|
||||
return children;
|
||||
};
|
||||
Summary.Row = Row;
|
||||
Summary.Cell = Cell;
|
||||
export default Summary;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnType, StickyOffsets } from '../interface';
|
||||
type FlattenColumns<RecordType> = readonly (ColumnType<RecordType> & {
|
||||
scrollbar?: boolean;
|
||||
})[];
|
||||
declare const SummaryContext: React.Context<{
|
||||
stickyOffsets?: StickyOffsets;
|
||||
scrollColumnIndex?: number;
|
||||
flattenColumns?: FlattenColumns<any>;
|
||||
}>;
|
||||
export default SummaryContext;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import * as React from 'react';
|
||||
const SummaryContext = /*#__PURE__*/React.createContext({});
|
||||
export default SummaryContext;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnType, StickyOffsets } from '../interface';
|
||||
type FlattenColumns<RecordType> = readonly (ColumnType<RecordType> & {
|
||||
scrollbar?: boolean;
|
||||
})[];
|
||||
export interface FooterProps<RecordType> {
|
||||
children: React.ReactNode;
|
||||
stickyOffsets: StickyOffsets;
|
||||
flattenColumns: FlattenColumns<RecordType>;
|
||||
}
|
||||
declare const _default: <RecordType>(props: FooterProps<RecordType>) => React.JSX.Element;
|
||||
export default _default;
|
||||
export declare const FooterComponents: React.FC<React.PropsWithChildren<import("./Summary").SummaryProps>> & {
|
||||
Row: React.FC<React.PropsWithChildren<import("./Row").FooterRowProps>>;
|
||||
Cell: React.FC<React.PropsWithChildren<import("./Cell").SummaryCellProps>>;
|
||||
};
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import * as React from 'react';
|
||||
import TableContext, { responseImmutable } from "../context/TableContext";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
import Summary from "./Summary";
|
||||
import SummaryContext from "./SummaryContext";
|
||||
const Footer = props => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
children,
|
||||
stickyOffsets,
|
||||
flattenColumns
|
||||
} = props;
|
||||
const prefixCls = useContext(TableContext, 'prefixCls');
|
||||
const lastColumnIndex = flattenColumns.length - 1;
|
||||
const scrollColumn = flattenColumns[lastColumnIndex];
|
||||
const summaryContext = React.useMemo(() => ({
|
||||
stickyOffsets,
|
||||
flattenColumns,
|
||||
scrollColumnIndex: scrollColumn?.scrollbar ? lastColumnIndex : null
|
||||
}), [scrollColumn, flattenColumns, lastColumnIndex, stickyOffsets]);
|
||||
return /*#__PURE__*/React.createElement(SummaryContext.Provider, {
|
||||
value: summaryContext
|
||||
}, /*#__PURE__*/React.createElement("tfoot", {
|
||||
className: `${prefixCls}-summary`
|
||||
}, children));
|
||||
};
|
||||
export default responseImmutable(Footer);
|
||||
export const FooterComponents = Summary;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnsType, ColumnType, GetComponentProps, StickyOffsets } from '../interface';
|
||||
export interface HeaderProps<RecordType> {
|
||||
columns: ColumnsType<RecordType>;
|
||||
flattenColumns: readonly ColumnType<RecordType>[];
|
||||
stickyOffsets: StickyOffsets;
|
||||
onHeaderRow: GetComponentProps<readonly ColumnType<RecordType>[]>;
|
||||
}
|
||||
declare const _default: <RecordType extends unknown>(props: HeaderProps<RecordType>) => React.JSX.Element;
|
||||
export default _default;
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
import * as React from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
import { useContext } from '@rc-component/context';
|
||||
import TableContext, { responseImmutable } from "../context/TableContext";
|
||||
import devRenderTimes from "../hooks/useRenderTimes";
|
||||
import HeaderRow from "./HeaderRow";
|
||||
function parseHeaderRows(rootColumns, classNames, styles) {
|
||||
const rows = [];
|
||||
function fillRowCells(columns, colIndex, rowIndex = 0) {
|
||||
// Init rows
|
||||
rows[rowIndex] = rows[rowIndex] || [];
|
||||
let currentColIndex = colIndex;
|
||||
const colSpans = columns.filter(Boolean).map(column => {
|
||||
const cell = {
|
||||
key: column.key,
|
||||
className: clsx(column.className, classNames.cell) || '',
|
||||
style: styles.cell,
|
||||
children: column.title,
|
||||
column,
|
||||
colStart: currentColIndex
|
||||
};
|
||||
let colSpan = 1;
|
||||
const subColumns = column.children;
|
||||
if (subColumns && subColumns.length > 0) {
|
||||
colSpan = fillRowCells(subColumns, currentColIndex, rowIndex + 1).reduce((total, count) => total + count, 0);
|
||||
cell.hasSubColumns = true;
|
||||
}
|
||||
if ('colSpan' in column) {
|
||||
({
|
||||
colSpan
|
||||
} = column);
|
||||
}
|
||||
if ('rowSpan' in column) {
|
||||
cell.rowSpan = column.rowSpan;
|
||||
}
|
||||
cell.colSpan = colSpan;
|
||||
cell.colEnd = cell.colStart + colSpan - 1;
|
||||
rows[rowIndex].push(cell);
|
||||
currentColIndex += colSpan;
|
||||
return colSpan;
|
||||
});
|
||||
return colSpans;
|
||||
}
|
||||
|
||||
// Generate `rows` cell data
|
||||
fillRowCells(rootColumns, 0);
|
||||
|
||||
// Handle `rowSpan`
|
||||
const rowCount = rows.length;
|
||||
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
||||
rows[rowIndex].forEach(cell => {
|
||||
if (!('rowSpan' in cell) && !cell.hasSubColumns) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
cell.rowSpan = rowCount - rowIndex;
|
||||
}
|
||||
});
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
const Header = props => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devRenderTimes(props);
|
||||
}
|
||||
const {
|
||||
stickyOffsets,
|
||||
columns,
|
||||
flattenColumns,
|
||||
onHeaderRow
|
||||
} = props;
|
||||
const {
|
||||
prefixCls,
|
||||
getComponent,
|
||||
classNames,
|
||||
styles
|
||||
} = useContext(TableContext, ['prefixCls', 'getComponent', 'classNames', 'styles']);
|
||||
const {
|
||||
header: headerCls = {}
|
||||
} = classNames || {};
|
||||
const {
|
||||
header: headerStyles = {}
|
||||
} = styles || {};
|
||||
const rows = React.useMemo(() => parseHeaderRows(columns, headerCls, headerStyles), [columns, headerCls, headerStyles]);
|
||||
const WrapperComponent = getComponent(['header', 'wrapper'], 'thead');
|
||||
const trComponent = getComponent(['header', 'row'], 'tr');
|
||||
const thComponent = getComponent(['header', 'cell'], 'th');
|
||||
return /*#__PURE__*/React.createElement(WrapperComponent, {
|
||||
className: clsx(`${prefixCls}-thead`, headerCls.wrapper),
|
||||
style: headerStyles.wrapper
|
||||
}, rows.map((row, rowIndex) => {
|
||||
const rowNode = /*#__PURE__*/React.createElement(HeaderRow, {
|
||||
classNames: headerCls,
|
||||
styles: headerStyles,
|
||||
key: rowIndex,
|
||||
flattenColumns: flattenColumns,
|
||||
cells: row,
|
||||
stickyOffsets: stickyOffsets,
|
||||
rowComponent: trComponent,
|
||||
cellComponent: thComponent,
|
||||
onHeaderRow: onHeaderRow,
|
||||
index: rowIndex
|
||||
});
|
||||
return rowNode;
|
||||
}));
|
||||
};
|
||||
export default responseImmutable(Header);
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import type { CellType, ColumnType, CustomizeComponent, GetComponentProps, StickyOffsets } from '../interface';
|
||||
import type { TableProps } from '..';
|
||||
export interface RowProps<RecordType> {
|
||||
cells: readonly CellType<RecordType>[];
|
||||
stickyOffsets: StickyOffsets;
|
||||
flattenColumns: readonly ColumnType<RecordType>[];
|
||||
rowComponent: CustomizeComponent;
|
||||
cellComponent: CustomizeComponent;
|
||||
onHeaderRow: GetComponentProps<readonly ColumnType<RecordType>[]>;
|
||||
index: number;
|
||||
classNames: TableProps['classNames']['header'];
|
||||
styles: TableProps['styles']['header'];
|
||||
}
|
||||
declare const HeaderRow: {
|
||||
<RecordType extends unknown>(props: RowProps<RecordType>): React.JSX.Element;
|
||||
displayName: string;
|
||||
};
|
||||
export default HeaderRow;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import * as React from 'react';
|
||||
import Cell from "../Cell";
|
||||
import TableContext from "../context/TableContext";
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { getCellFixedInfo } from "../utils/fixUtil";
|
||||
import { getColumnsKey } from "../utils/valueUtil";
|
||||
const HeaderRow = props => {
|
||||
const {
|
||||
cells,
|
||||
stickyOffsets,
|
||||
flattenColumns,
|
||||
rowComponent: RowComponent,
|
||||
cellComponent: CellComponent,
|
||||
onHeaderRow,
|
||||
index,
|
||||
classNames,
|
||||
styles
|
||||
} = props;
|
||||
const {
|
||||
prefixCls
|
||||
} = useContext(TableContext, ['prefixCls']);
|
||||
let rowProps;
|
||||
if (onHeaderRow) {
|
||||
rowProps = onHeaderRow(cells.map(cell => cell.column), index);
|
||||
}
|
||||
const columnsKey = getColumnsKey(cells.map(cell => cell.column));
|
||||
return /*#__PURE__*/React.createElement(RowComponent, _extends({}, rowProps, {
|
||||
className: classNames.row,
|
||||
style: styles.row
|
||||
}), cells.map((cell, cellIndex) => {
|
||||
const {
|
||||
column,
|
||||
colStart,
|
||||
colEnd,
|
||||
colSpan
|
||||
} = cell;
|
||||
const fixedInfo = getCellFixedInfo(colStart, colEnd, flattenColumns, stickyOffsets);
|
||||
const additionalProps = column?.onHeaderCell?.(column) || {};
|
||||
return /*#__PURE__*/React.createElement(Cell, _extends({}, cell, {
|
||||
scope: column.title ? colSpan > 1 ? 'colgroup' : 'col' : null,
|
||||
ellipsis: column.ellipsis,
|
||||
align: column.align,
|
||||
component: CellComponent,
|
||||
prefixCls: prefixCls,
|
||||
key: columnsKey[cellIndex]
|
||||
}, fixedInfo, {
|
||||
additionalProps: additionalProps,
|
||||
rowType: "header"
|
||||
}));
|
||||
}));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
HeaderRow.displayName = 'HeaderRow';
|
||||
}
|
||||
export default HeaderRow;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
export interface TitleProps {
|
||||
className: string;
|
||||
style: React.CSSProperties;
|
||||
}
|
||||
declare const Panel: React.FC<React.PropsWithChildren<TitleProps>>;
|
||||
export default Panel;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
const Panel = props => {
|
||||
const {
|
||||
children,
|
||||
className,
|
||||
style
|
||||
} = props;
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: className,
|
||||
style: style
|
||||
}, children);
|
||||
};
|
||||
export default Panel;
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Feature:
|
||||
* - fixed not need to set width
|
||||
* - support `rowExpandable` to config row expand logic
|
||||
* - add `summary` to support `() => ReactNode`
|
||||
*
|
||||
* Update:
|
||||
* - `dataIndex` is `array[]` now
|
||||
* - `expandable` wrap all the expand related props
|
||||
*
|
||||
* Removed:
|
||||
* - expandIconAsCell
|
||||
* - useFixedHeader
|
||||
* - rowRef
|
||||
* - columns[number].onCellClick
|
||||
* - onRowClick
|
||||
* - onRowDoubleClick
|
||||
* - onRowMouseEnter
|
||||
* - onRowMouseLeave
|
||||
* - getBodyWrapper
|
||||
* - bodyStyle
|
||||
*
|
||||
* Deprecated:
|
||||
* - All expanded props, move into expandable
|
||||
*/
|
||||
import type { CompareProps } from '@rc-component/context/lib/Immutable';
|
||||
import * as React from 'react';
|
||||
import { EXPAND_COLUMN, INTERNAL_HOOKS } from './constant';
|
||||
import { FooterComponents } from './Footer';
|
||||
import type { ColumnsType, ColumnType, DefaultRecordType, Direction, ExpandableConfig, GetComponentProps, GetRowKey, LegacyExpandableProps, PanelRender, Reference, RowClassName, TableComponents, TableLayout, TableSticky } from './interface';
|
||||
import Column from './sugar/Column';
|
||||
import ColumnGroup from './sugar/ColumnGroup';
|
||||
export declare const DEFAULT_PREFIX = "rc-table";
|
||||
export type SemanticName = 'section' | 'title' | 'footer' | 'content';
|
||||
export type ComponentsSemantic = 'wrapper' | 'cell' | 'row';
|
||||
export interface TableProps<RecordType = any> extends Omit<LegacyExpandableProps<RecordType>, 'showExpandColumn'> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
classNames?: Partial<Record<SemanticName, string>> & {
|
||||
body?: Partial<Record<ComponentsSemantic, string>>;
|
||||
header?: Partial<Record<ComponentsSemantic, string>>;
|
||||
};
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>> & {
|
||||
body?: Partial<Record<ComponentsSemantic, React.CSSProperties>>;
|
||||
header?: Partial<Record<ComponentsSemantic, React.CSSProperties>>;
|
||||
};
|
||||
children?: React.ReactNode;
|
||||
data?: readonly RecordType[];
|
||||
columns?: ColumnsType<RecordType>;
|
||||
rowKey?: string | keyof RecordType | GetRowKey<RecordType>;
|
||||
tableLayout?: TableLayout;
|
||||
scroll?: {
|
||||
x?: number | true | string;
|
||||
y?: number | string;
|
||||
};
|
||||
/** Config expand rows */
|
||||
expandable?: ExpandableConfig<RecordType>;
|
||||
indentSize?: number;
|
||||
rowClassName?: string | RowClassName<RecordType>;
|
||||
footer?: PanelRender<RecordType>;
|
||||
summary?: (data: readonly RecordType[]) => React.ReactNode;
|
||||
caption?: React.ReactNode;
|
||||
id?: string;
|
||||
showHeader?: boolean;
|
||||
components?: TableComponents<RecordType>;
|
||||
onRow?: GetComponentProps<RecordType>;
|
||||
onHeaderRow?: GetComponentProps<readonly ColumnType<RecordType>[]>;
|
||||
emptyText?: React.ReactNode | (() => React.ReactNode);
|
||||
direction?: Direction;
|
||||
sticky?: boolean | TableSticky;
|
||||
rowHoverable?: boolean;
|
||||
onScroll?: React.UIEventHandler<HTMLDivElement>;
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor. Should always use `columns` instead.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
internalHooks?: string;
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor. Should always use `columns` instead.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
transformColumns?: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>;
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
tailor?: boolean;
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
getContainerWidth?: (ele: HTMLElement, width: number) => number;
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
internalRefs?: {
|
||||
body: React.MutableRefObject<HTMLDivElement>;
|
||||
};
|
||||
/**
|
||||
* @private Internal usage, may remove by refactor.
|
||||
*
|
||||
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
|
||||
*/
|
||||
measureRowRender?: (measureRow: React.ReactNode) => React.ReactNode;
|
||||
}
|
||||
export type ForwardGenericTable = (<RecordType extends DefaultRecordType = any>(props: TableProps<RecordType> & React.RefAttributes<Reference>) => React.ReactElement<any>) & {
|
||||
displayName?: string;
|
||||
};
|
||||
export declare const genTable: (shouldTriggerRender?: CompareProps<ForwardGenericTable>) => ForwardGenericTable;
|
||||
declare const ImmutableTable: ForwardGenericTable;
|
||||
type ImmutableTableType = typeof ImmutableTable & {
|
||||
EXPAND_COLUMN: typeof EXPAND_COLUMN;
|
||||
INTERNAL_HOOKS: typeof INTERNAL_HOOKS;
|
||||
Column: typeof Column;
|
||||
ColumnGroup: typeof ColumnGroup;
|
||||
Summary: typeof FooterComponents;
|
||||
};
|
||||
declare const _default: ImmutableTableType;
|
||||
export default _default;
|
||||
+691
@@ -0,0 +1,691 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
/**
|
||||
* Feature:
|
||||
* - fixed not need to set width
|
||||
* - support `rowExpandable` to config row expand logic
|
||||
* - add `summary` to support `() => ReactNode`
|
||||
*
|
||||
* Update:
|
||||
* - `dataIndex` is `array[]` now
|
||||
* - `expandable` wrap all the expand related props
|
||||
*
|
||||
* Removed:
|
||||
* - expandIconAsCell
|
||||
* - useFixedHeader
|
||||
* - rowRef
|
||||
* - columns[number].onCellClick
|
||||
* - onRowClick
|
||||
* - onRowDoubleClick
|
||||
* - onRowMouseEnter
|
||||
* - onRowMouseLeave
|
||||
* - getBodyWrapper
|
||||
* - bodyStyle
|
||||
*
|
||||
* Deprecated:
|
||||
* - All expanded props, move into expandable
|
||||
*/
|
||||
|
||||
import { clsx } from 'clsx';
|
||||
import ResizeObserver from '@rc-component/resize-observer';
|
||||
import { getTargetScrollBarSize } from "@rc-component/util/es/getScrollBarSize";
|
||||
import useEvent from "@rc-component/util/es/hooks/useEvent";
|
||||
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
||||
import getValue from "@rc-component/util/es/utils/get";
|
||||
import warning from "@rc-component/util/es/warning";
|
||||
import * as React from 'react';
|
||||
import Body from "./Body";
|
||||
import ColGroup from "./ColGroup";
|
||||
import { EXPAND_COLUMN, INTERNAL_HOOKS } from "./constant";
|
||||
import TableContext, { makeImmutable } from "./context/TableContext";
|
||||
import FixedHolder from "./FixedHolder";
|
||||
import Footer, { FooterComponents } from "./Footer";
|
||||
import Summary from "./Footer/Summary";
|
||||
import Header from "./Header/Header";
|
||||
import useColumns from "./hooks/useColumns";
|
||||
import useExpand from "./hooks/useExpand";
|
||||
import useFixedInfo from "./hooks/useFixedInfo";
|
||||
import { useTimeoutLock } from "./hooks/useFrame";
|
||||
import useHover from "./hooks/useHover";
|
||||
import useSticky from "./hooks/useSticky";
|
||||
import useStickyOffsets from "./hooks/useStickyOffsets";
|
||||
import Panel from "./Panel";
|
||||
import StickyScrollBar from "./stickyScrollBar";
|
||||
import Column from "./sugar/Column";
|
||||
import ColumnGroup from "./sugar/ColumnGroup";
|
||||
import { getColumnsKey, validateValue, validNumberValue } from "./utils/valueUtil";
|
||||
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
|
||||
import isEqual from "@rc-component/util/es/isEqual";
|
||||
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
|
||||
export const DEFAULT_PREFIX = 'rc-table';
|
||||
|
||||
// Used for conditions cache
|
||||
const EMPTY_DATA = [];
|
||||
|
||||
// Used for customize scroll
|
||||
const EMPTY_SCROLL_TARGET = {};
|
||||
function defaultEmpty() {
|
||||
return 'No Data';
|
||||
}
|
||||
const Table = (tableProps, ref) => {
|
||||
const props = {
|
||||
rowKey: 'key',
|
||||
prefixCls: DEFAULT_PREFIX,
|
||||
emptyText: defaultEmpty,
|
||||
...tableProps
|
||||
};
|
||||
const {
|
||||
prefixCls,
|
||||
className,
|
||||
rowClassName,
|
||||
style,
|
||||
classNames,
|
||||
styles,
|
||||
data,
|
||||
rowKey,
|
||||
scroll,
|
||||
tableLayout,
|
||||
direction,
|
||||
// Additional Part
|
||||
title,
|
||||
footer,
|
||||
summary,
|
||||
caption,
|
||||
// Customize
|
||||
id,
|
||||
showHeader,
|
||||
components,
|
||||
emptyText,
|
||||
onRow,
|
||||
onHeaderRow,
|
||||
// Measure Row
|
||||
measureRowRender,
|
||||
// Events
|
||||
onScroll,
|
||||
// Internal
|
||||
internalHooks,
|
||||
transformColumns,
|
||||
internalRefs,
|
||||
tailor,
|
||||
getContainerWidth,
|
||||
sticky,
|
||||
rowHoverable = true
|
||||
} = props;
|
||||
const mergedData = data || EMPTY_DATA;
|
||||
const hasData = !!mergedData.length;
|
||||
const useInternalHooks = internalHooks === INTERNAL_HOOKS;
|
||||
|
||||
// ===================== Warning ======================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
['onRowClick', 'onRowDoubleClick', 'onRowContextMenu', 'onRowMouseEnter', 'onRowMouseLeave'].forEach(name => {
|
||||
warning(props[name] === undefined, `\`${name}\` is removed, please use \`onRow\` instead.`);
|
||||
});
|
||||
warning(!('getBodyWrapper' in props), '`getBodyWrapper` is deprecated, please use custom `components` instead.');
|
||||
}
|
||||
|
||||
// ==================== Customize =====================
|
||||
const getComponent = React.useCallback((path, defaultComponent) => getValue(components, path) || defaultComponent, [components]);
|
||||
const getRowKey = React.useMemo(() => {
|
||||
if (typeof rowKey === 'function') {
|
||||
return rowKey;
|
||||
}
|
||||
return record => {
|
||||
const key = record && record[rowKey];
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(key !== undefined, 'Each record in table should have a unique `key` prop, or set `rowKey` to an unique primary key.');
|
||||
}
|
||||
return key;
|
||||
};
|
||||
}, [rowKey]);
|
||||
const customizeScrollBody = getComponent(['body']);
|
||||
|
||||
// ====================== Hover =======================
|
||||
const [startRow, endRow, onHover] = useHover();
|
||||
|
||||
// ====================== Expand ======================
|
||||
const [expandableConfig, expandableType, mergedExpandedKeys, mergedExpandIcon, mergedChildrenColumnName, onTriggerExpand] = useExpand(props, mergedData, getRowKey);
|
||||
|
||||
// ====================== Column ======================
|
||||
const scrollX = scroll?.x;
|
||||
const [componentWidth, setComponentWidth] = React.useState(0);
|
||||
const [columns, flattenColumns, flattenScrollX] = useColumns({
|
||||
...props,
|
||||
...expandableConfig,
|
||||
expandable: !!expandableConfig.expandedRowRender,
|
||||
columnTitle: expandableConfig.columnTitle,
|
||||
expandedKeys: mergedExpandedKeys,
|
||||
getRowKey,
|
||||
// https://github.com/ant-design/ant-design/issues/23894
|
||||
onTriggerExpand,
|
||||
expandIcon: mergedExpandIcon,
|
||||
expandIconColumnIndex: expandableConfig.expandIconColumnIndex,
|
||||
direction,
|
||||
scrollWidth: useInternalHooks && tailor && typeof scrollX === 'number' ? scrollX : null,
|
||||
clientWidth: componentWidth
|
||||
}, useInternalHooks ? transformColumns : null);
|
||||
const mergedScrollX = flattenScrollX ?? scrollX;
|
||||
const columnContext = React.useMemo(() => ({
|
||||
columns,
|
||||
flattenColumns
|
||||
}), [columns, flattenColumns]);
|
||||
|
||||
// ======================= Refs =======================
|
||||
const fullTableRef = React.useRef(null);
|
||||
const scrollHeaderRef = React.useRef(null);
|
||||
const scrollBodyRef = React.useRef(null);
|
||||
const scrollBodyContainerRef = React.useRef(null);
|
||||
React.useImperativeHandle(ref, () => {
|
||||
return {
|
||||
nativeElement: fullTableRef.current,
|
||||
scrollTo: config => {
|
||||
if (scrollBodyRef.current instanceof HTMLElement) {
|
||||
// Native scroll
|
||||
const {
|
||||
index,
|
||||
top,
|
||||
key,
|
||||
offset
|
||||
} = config;
|
||||
if (validNumberValue(top)) {
|
||||
// In top mode, offset is ignored
|
||||
scrollBodyRef.current?.scrollTo({
|
||||
top
|
||||
});
|
||||
} else {
|
||||
const mergedKey = key ?? getRowKey(mergedData[index]);
|
||||
const targetElement = scrollBodyRef.current.querySelector(`[data-row-key="${mergedKey}"]`);
|
||||
if (targetElement) {
|
||||
if (!offset) {
|
||||
// No offset, use scrollIntoView for default behavior
|
||||
targetElement.scrollIntoView();
|
||||
} else {
|
||||
// With offset, use element's offsetTop + offset
|
||||
const elementTop = targetElement.offsetTop;
|
||||
scrollBodyRef.current.scrollTo({
|
||||
top: elementTop + offset
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (scrollBodyRef.current?.scrollTo) {
|
||||
// Pass to proxy
|
||||
scrollBodyRef.current.scrollTo(config);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// ====================== Scroll ======================
|
||||
const scrollSummaryRef = React.useRef(null);
|
||||
const [shadowStart, setShadowStart] = React.useState(false);
|
||||
const [shadowEnd, setShadowEnd] = React.useState(false);
|
||||
const [colsWidths, updateColsWidths] = React.useState(new Map());
|
||||
|
||||
// Convert map to number width
|
||||
const colsKeys = getColumnsKey(flattenColumns);
|
||||
const pureColWidths = colsKeys.map(columnKey => colsWidths.get(columnKey));
|
||||
const colWidths = React.useMemo(() => pureColWidths, [pureColWidths.join('_')]);
|
||||
const stickyOffsets = useStickyOffsets(colWidths, flattenColumns);
|
||||
const fixHeader = scroll && validateValue(scroll.y);
|
||||
const horizonScroll = scroll && validateValue(mergedScrollX) || Boolean(expandableConfig.fixed);
|
||||
const fixColumn = horizonScroll && flattenColumns.some(({
|
||||
fixed
|
||||
}) => fixed);
|
||||
|
||||
// Sticky
|
||||
const stickyRef = React.useRef(null);
|
||||
const {
|
||||
isSticky,
|
||||
offsetHeader,
|
||||
offsetSummary,
|
||||
offsetScroll,
|
||||
stickyClassName,
|
||||
container
|
||||
} = useSticky(sticky, prefixCls);
|
||||
|
||||
// Footer (Fix footer must fixed header)
|
||||
const summaryNode = React.useMemo(() => summary?.(mergedData), [summary, mergedData]);
|
||||
const fixFooter = (fixHeader || isSticky) && /*#__PURE__*/React.isValidElement(summaryNode) && summaryNode.type === Summary && summaryNode.props.fixed;
|
||||
|
||||
// Scroll
|
||||
let scrollXStyle;
|
||||
let scrollYStyle;
|
||||
let scrollTableStyle;
|
||||
if (fixHeader) {
|
||||
scrollYStyle = {
|
||||
overflowY: hasData ? 'scroll' : 'auto',
|
||||
maxHeight: scroll.y
|
||||
};
|
||||
}
|
||||
if (horizonScroll) {
|
||||
scrollXStyle = {
|
||||
overflowX: 'auto'
|
||||
};
|
||||
// When no vertical scrollbar, should hide it
|
||||
// https://github.com/ant-design/ant-design/pull/20705
|
||||
// https://github.com/ant-design/ant-design/issues/21879
|
||||
if (!fixHeader) {
|
||||
scrollYStyle = {
|
||||
overflowY: 'hidden'
|
||||
};
|
||||
}
|
||||
scrollTableStyle = {
|
||||
width: mergedScrollX === true ? 'auto' : mergedScrollX,
|
||||
minWidth: '100%'
|
||||
};
|
||||
}
|
||||
const onColumnResize = React.useCallback((columnKey, width) => {
|
||||
updateColsWidths(widths => {
|
||||
if (widths.get(columnKey) !== width) {
|
||||
const newWidths = new Map(widths);
|
||||
newWidths.set(columnKey, width);
|
||||
return newWidths;
|
||||
}
|
||||
return widths;
|
||||
});
|
||||
}, []);
|
||||
const [setScrollTarget, getScrollTarget] = useTimeoutLock(null);
|
||||
function forceScroll(scrollLeft, target) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
if (typeof target === 'function') {
|
||||
target(scrollLeft);
|
||||
} else if (target.scrollLeft !== scrollLeft) {
|
||||
target.scrollLeft = scrollLeft;
|
||||
|
||||
// Delay to force scroll position if not sync
|
||||
// ref: https://github.com/ant-design/ant-design/issues/37179
|
||||
if (target.scrollLeft !== scrollLeft) {
|
||||
setTimeout(() => {
|
||||
target.scrollLeft = scrollLeft;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
const [scrollInfo, setScrollInfo] = React.useState([0, 0]);
|
||||
const onInternalScroll = useEvent(({
|
||||
currentTarget,
|
||||
scrollLeft
|
||||
}) => {
|
||||
const mergedScrollLeft = typeof scrollLeft === 'number' ? scrollLeft : currentTarget.scrollLeft;
|
||||
const compareTarget = currentTarget || EMPTY_SCROLL_TARGET;
|
||||
if (!getScrollTarget() || getScrollTarget() === compareTarget) {
|
||||
setScrollTarget(compareTarget);
|
||||
forceScroll(mergedScrollLeft, scrollHeaderRef.current);
|
||||
forceScroll(mergedScrollLeft, scrollBodyRef.current);
|
||||
forceScroll(mergedScrollLeft, scrollSummaryRef.current);
|
||||
forceScroll(mergedScrollLeft, stickyRef.current?.setScrollLeft);
|
||||
}
|
||||
const measureTarget = currentTarget || scrollHeaderRef.current;
|
||||
if (measureTarget) {
|
||||
const scrollWidth =
|
||||
// Should use mergedScrollX in virtual table(useInternalHooks && tailor === true)
|
||||
useInternalHooks && tailor && typeof mergedScrollX === 'number' ? mergedScrollX : measureTarget.scrollWidth;
|
||||
const clientWidth = measureTarget.clientWidth;
|
||||
const absScrollStart = Math.abs(mergedScrollLeft);
|
||||
setScrollInfo(ori => {
|
||||
const nextScrollInfo = [absScrollStart, scrollWidth - clientWidth];
|
||||
return isEqual(ori, nextScrollInfo) ? ori : nextScrollInfo;
|
||||
});
|
||||
|
||||
// There is no space to scroll
|
||||
if (scrollWidth === clientWidth) {
|
||||
setShadowStart(false);
|
||||
setShadowEnd(false);
|
||||
return;
|
||||
}
|
||||
setShadowStart(absScrollStart > 0);
|
||||
setShadowEnd(absScrollStart < scrollWidth - clientWidth - 1);
|
||||
}
|
||||
});
|
||||
const onBodyScroll = useEvent(e => {
|
||||
onInternalScroll(e);
|
||||
onScroll?.(e);
|
||||
});
|
||||
const triggerOnScroll = () => {
|
||||
if (horizonScroll && scrollBodyRef.current) {
|
||||
onInternalScroll({
|
||||
currentTarget: getDOM(scrollBodyRef.current),
|
||||
scrollLeft: scrollBodyRef.current?.scrollLeft
|
||||
});
|
||||
} else {
|
||||
setShadowStart(false);
|
||||
setShadowEnd(false);
|
||||
}
|
||||
};
|
||||
const onFullTableResize = offsetWidth => {
|
||||
stickyRef.current?.checkScrollBarVisible();
|
||||
let mergedWidth = offsetWidth ?? fullTableRef.current?.offsetWidth ?? 0;
|
||||
if (useInternalHooks && getContainerWidth && fullTableRef.current) {
|
||||
mergedWidth = getContainerWidth(fullTableRef.current, mergedWidth) || mergedWidth;
|
||||
}
|
||||
if (mergedWidth !== componentWidth) {
|
||||
triggerOnScroll();
|
||||
setComponentWidth(mergedWidth);
|
||||
}
|
||||
};
|
||||
|
||||
// fix https://github.com/ant-design/ant-design/issues/49279
|
||||
useLayoutEffect(() => {
|
||||
if (horizonScroll) {
|
||||
onFullTableResize();
|
||||
}
|
||||
}, [horizonScroll]);
|
||||
|
||||
// Sync scroll bar when init or `horizonScroll`, `data` and `columns.length` changed
|
||||
const mounted = React.useRef(false);
|
||||
React.useEffect(() => {
|
||||
// onFullTableResize will be trigger once when ResizeObserver is mounted
|
||||
// This will reduce one duplicated triggerOnScroll time
|
||||
if (mounted.current) {
|
||||
triggerOnScroll();
|
||||
}
|
||||
}, [horizonScroll, data, columns.length]);
|
||||
React.useEffect(() => {
|
||||
mounted.current = true;
|
||||
}, []);
|
||||
|
||||
// ===================== Effects ======================
|
||||
const [scrollbarSize, setScrollbarSize] = React.useState(0);
|
||||
useLayoutEffect(() => {
|
||||
if (!tailor || !useInternalHooks) {
|
||||
if (scrollBodyRef.current instanceof Element) {
|
||||
setScrollbarSize(getTargetScrollBarSize(scrollBodyRef.current).width);
|
||||
} else {
|
||||
setScrollbarSize(getTargetScrollBarSize(scrollBodyContainerRef.current).width);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// ================== INTERNAL HOOKS ==================
|
||||
React.useEffect(() => {
|
||||
if (useInternalHooks && internalRefs) {
|
||||
internalRefs.body.current = scrollBodyRef.current;
|
||||
}
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
// == Render ==
|
||||
// ========================================================================
|
||||
// =================== Render: Func ===================
|
||||
const renderFixedHeaderTable = React.useCallback(fixedHolderPassProps => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Header, fixedHolderPassProps), fixFooter === 'top' && /*#__PURE__*/React.createElement(Footer, fixedHolderPassProps, summaryNode)), [fixFooter, summaryNode]);
|
||||
const renderFixedFooterTable = React.useCallback(fixedHolderPassProps => /*#__PURE__*/React.createElement(Footer, fixedHolderPassProps, summaryNode), [summaryNode]);
|
||||
|
||||
// =================== Render: Node ===================
|
||||
const TableComponent = getComponent(['table'], 'table');
|
||||
|
||||
// Table layout
|
||||
const mergedTableLayout = React.useMemo(() => {
|
||||
if (tableLayout) {
|
||||
return tableLayout;
|
||||
}
|
||||
// https://github.com/ant-design/ant-design/issues/25227
|
||||
// When scroll.x is max-content, no need to fix table layout
|
||||
// it's width should stretch out to fit content
|
||||
if (fixColumn) {
|
||||
return mergedScrollX === 'max-content' ? 'auto' : 'fixed';
|
||||
}
|
||||
if (fixHeader || isSticky || flattenColumns.some(({
|
||||
ellipsis
|
||||
}) => ellipsis)) {
|
||||
return 'fixed';
|
||||
}
|
||||
return 'auto';
|
||||
}, [fixHeader, fixColumn, flattenColumns, tableLayout, isSticky]);
|
||||
let groupTableNode;
|
||||
|
||||
// Header props
|
||||
const headerProps = {
|
||||
colWidths,
|
||||
columCount: flattenColumns.length,
|
||||
stickyOffsets,
|
||||
onHeaderRow,
|
||||
fixHeader,
|
||||
scroll
|
||||
};
|
||||
|
||||
// Empty
|
||||
const emptyNode = React.useMemo(() => {
|
||||
if (hasData) {
|
||||
return null;
|
||||
}
|
||||
if (typeof emptyText === 'function') {
|
||||
return emptyText();
|
||||
}
|
||||
return emptyText;
|
||||
}, [hasData, emptyText]);
|
||||
|
||||
// Body
|
||||
const bodyTable = /*#__PURE__*/React.createElement(Body, {
|
||||
data: mergedData,
|
||||
measureColumnWidth: fixHeader || horizonScroll || isSticky
|
||||
});
|
||||
const bodyColGroup = /*#__PURE__*/React.createElement(ColGroup, {
|
||||
colWidths: flattenColumns.map(({
|
||||
width
|
||||
}) => width),
|
||||
columns: flattenColumns
|
||||
});
|
||||
const captionElement = caption !== null && caption !== undefined ? /*#__PURE__*/React.createElement("caption", {
|
||||
className: `${prefixCls}-caption`
|
||||
}, caption) : undefined;
|
||||
const dataProps = pickAttrs(props, {
|
||||
data: true
|
||||
});
|
||||
const ariaProps = pickAttrs(props, {
|
||||
aria: true
|
||||
});
|
||||
if (fixHeader || isSticky) {
|
||||
// >>>>>> Fixed Header
|
||||
let bodyContent;
|
||||
if (typeof customizeScrollBody === 'function') {
|
||||
bodyContent = customizeScrollBody(mergedData, {
|
||||
scrollbarSize,
|
||||
ref: scrollBodyRef,
|
||||
onScroll: onInternalScroll
|
||||
});
|
||||
headerProps.colWidths = flattenColumns.map(({
|
||||
width
|
||||
}, index) => {
|
||||
const colWidth = index === flattenColumns.length - 1 ? width - scrollbarSize : width;
|
||||
if (typeof colWidth === 'number' && !Number.isNaN(colWidth)) {
|
||||
return colWidth;
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(props.columns.length === 0, 'When use `components.body` with render props. Each column should have a fixed `width` value.');
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
} else {
|
||||
bodyContent = /*#__PURE__*/React.createElement("div", {
|
||||
style: {
|
||||
...scrollXStyle,
|
||||
...scrollYStyle
|
||||
},
|
||||
onScroll: onBodyScroll,
|
||||
ref: scrollBodyRef,
|
||||
className: `${prefixCls}-body`
|
||||
}, /*#__PURE__*/React.createElement(TableComponent, _extends({
|
||||
style: {
|
||||
...scrollTableStyle,
|
||||
tableLayout: mergedTableLayout
|
||||
}
|
||||
}, ariaProps), captionElement, bodyColGroup, bodyTable, !fixFooter && summaryNode && /*#__PURE__*/React.createElement(Footer, {
|
||||
stickyOffsets: stickyOffsets,
|
||||
flattenColumns: flattenColumns
|
||||
}, summaryNode)));
|
||||
}
|
||||
|
||||
// Fixed holder share the props
|
||||
const fixedHolderProps = {
|
||||
noData: !mergedData.length,
|
||||
maxContentScroll: horizonScroll && mergedScrollX === 'max-content',
|
||||
...headerProps,
|
||||
...columnContext,
|
||||
direction,
|
||||
stickyClassName,
|
||||
scrollX: mergedScrollX,
|
||||
tableLayout: mergedTableLayout,
|
||||
onScroll: onInternalScroll
|
||||
};
|
||||
groupTableNode = /*#__PURE__*/React.createElement(React.Fragment, null, showHeader !== false && /*#__PURE__*/React.createElement(FixedHolder, _extends({}, fixedHolderProps, {
|
||||
stickyTopOffset: offsetHeader,
|
||||
className: `${prefixCls}-header`,
|
||||
ref: scrollHeaderRef,
|
||||
colGroup: bodyColGroup
|
||||
}), renderFixedHeaderTable), bodyContent, fixFooter && fixFooter !== 'top' && /*#__PURE__*/React.createElement(FixedHolder, _extends({}, fixedHolderProps, {
|
||||
stickyBottomOffset: offsetSummary,
|
||||
className: `${prefixCls}-summary`,
|
||||
ref: scrollSummaryRef,
|
||||
colGroup: bodyColGroup
|
||||
}), renderFixedFooterTable), isSticky && scrollBodyRef.current && scrollBodyRef.current instanceof Element && /*#__PURE__*/React.createElement(StickyScrollBar, {
|
||||
ref: stickyRef,
|
||||
offsetScroll: offsetScroll,
|
||||
scrollBodyRef: scrollBodyRef,
|
||||
onScroll: onInternalScroll,
|
||||
container: container,
|
||||
direction: direction
|
||||
}));
|
||||
} else {
|
||||
// >>>>>> Unique table
|
||||
groupTableNode = /*#__PURE__*/React.createElement("div", {
|
||||
style: {
|
||||
...scrollXStyle,
|
||||
...scrollYStyle,
|
||||
...styles?.content
|
||||
},
|
||||
className: clsx(`${prefixCls}-content`, classNames?.content),
|
||||
onScroll: onInternalScroll,
|
||||
ref: scrollBodyRef
|
||||
}, /*#__PURE__*/React.createElement(TableComponent, _extends({
|
||||
style: {
|
||||
...scrollTableStyle,
|
||||
tableLayout: mergedTableLayout
|
||||
}
|
||||
}, ariaProps), captionElement, bodyColGroup, showHeader !== false && /*#__PURE__*/React.createElement(Header, _extends({}, headerProps, columnContext)), bodyTable, summaryNode && /*#__PURE__*/React.createElement(Footer, {
|
||||
stickyOffsets: stickyOffsets,
|
||||
flattenColumns: flattenColumns
|
||||
}, summaryNode)));
|
||||
}
|
||||
const tableStyle = {
|
||||
...style
|
||||
};
|
||||
|
||||
// Add css var for sticky header `zIndex` calc
|
||||
if (isSticky) {
|
||||
tableStyle['--columns-count'] = flattenColumns.length;
|
||||
}
|
||||
let fullTable = /*#__PURE__*/React.createElement("div", _extends({
|
||||
className: clsx(prefixCls, className, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-fix-start-shadow`]: horizonScroll,
|
||||
[`${prefixCls}-fix-end-shadow`]: horizonScroll,
|
||||
[`${prefixCls}-fix-start-shadow-show`]: horizonScroll && shadowStart,
|
||||
[`${prefixCls}-fix-end-shadow-show`]: horizonScroll && shadowEnd,
|
||||
[`${prefixCls}-layout-fixed`]: tableLayout === 'fixed',
|
||||
[`${prefixCls}-fixed-header`]: fixHeader,
|
||||
/** No used but for compatible */
|
||||
[`${prefixCls}-fixed-column`]: fixColumn,
|
||||
[`${prefixCls}-scroll-horizontal`]: horizonScroll,
|
||||
[`${prefixCls}-has-fix-start`]: flattenColumns[0]?.fixed,
|
||||
[`${prefixCls}-has-fix-end`]: flattenColumns[flattenColumns.length - 1]?.fixed === 'end'
|
||||
}),
|
||||
style: tableStyle,
|
||||
id: id,
|
||||
ref: fullTableRef
|
||||
}, dataProps), title && /*#__PURE__*/React.createElement(Panel, {
|
||||
className: clsx(`${prefixCls}-title`, classNames?.title),
|
||||
style: styles?.title
|
||||
}, title(mergedData)), /*#__PURE__*/React.createElement("div", {
|
||||
ref: scrollBodyContainerRef,
|
||||
className: clsx(`${prefixCls}-container`, classNames?.section),
|
||||
style: styles?.section
|
||||
}, groupTableNode), footer && /*#__PURE__*/React.createElement(Panel, {
|
||||
className: clsx(`${prefixCls}-footer`, classNames?.footer),
|
||||
style: styles?.footer
|
||||
}, footer(mergedData)));
|
||||
if (horizonScroll) {
|
||||
fullTable = /*#__PURE__*/React.createElement(ResizeObserver, {
|
||||
onResize: ({
|
||||
offsetWidth
|
||||
}) => onFullTableResize(offsetWidth)
|
||||
}, fullTable);
|
||||
}
|
||||
const fixedInfoList = useFixedInfo(flattenColumns, stickyOffsets);
|
||||
const TableContextValue = React.useMemo(() => ({
|
||||
// Scroll
|
||||
scrollX: mergedScrollX,
|
||||
scrollInfo,
|
||||
classNames,
|
||||
styles,
|
||||
// Table
|
||||
prefixCls,
|
||||
getComponent,
|
||||
scrollbarSize,
|
||||
direction,
|
||||
fixedInfoList,
|
||||
isSticky,
|
||||
componentWidth,
|
||||
fixHeader,
|
||||
fixColumn,
|
||||
horizonScroll,
|
||||
// Body
|
||||
tableLayout: mergedTableLayout,
|
||||
rowClassName,
|
||||
expandedRowClassName: expandableConfig.expandedRowClassName,
|
||||
expandIcon: mergedExpandIcon,
|
||||
expandableType,
|
||||
expandRowByClick: expandableConfig.expandRowByClick,
|
||||
expandedRowRender: expandableConfig.expandedRowRender,
|
||||
expandedRowOffset: expandableConfig.expandedRowOffset,
|
||||
onTriggerExpand,
|
||||
expandIconColumnIndex: expandableConfig.expandIconColumnIndex,
|
||||
indentSize: expandableConfig.indentSize,
|
||||
allColumnsFixedLeft: flattenColumns.every(col => col.fixed === 'start'),
|
||||
emptyNode,
|
||||
// Column
|
||||
columns,
|
||||
flattenColumns,
|
||||
onColumnResize,
|
||||
colWidths,
|
||||
// Row
|
||||
hoverStartRow: startRow,
|
||||
hoverEndRow: endRow,
|
||||
onHover,
|
||||
rowExpandable: expandableConfig.rowExpandable,
|
||||
onRow,
|
||||
getRowKey,
|
||||
expandedKeys: mergedExpandedKeys,
|
||||
childrenColumnName: mergedChildrenColumnName,
|
||||
rowHoverable,
|
||||
// Measure Row
|
||||
measureRowRender
|
||||
}), [
|
||||
// Scroll
|
||||
mergedScrollX, scrollInfo, classNames, styles,
|
||||
// Table
|
||||
prefixCls, getComponent, scrollbarSize, direction, fixedInfoList, isSticky, componentWidth, fixHeader, fixColumn, horizonScroll,
|
||||
// Body
|
||||
mergedTableLayout, rowClassName, expandableConfig.expandedRowClassName, mergedExpandIcon, expandableType, expandableConfig.expandRowByClick, expandableConfig.expandedRowRender, expandableConfig.expandedRowOffset, onTriggerExpand, expandableConfig.expandIconColumnIndex, expandableConfig.indentSize, emptyNode,
|
||||
// Column
|
||||
columns, flattenColumns, onColumnResize, colWidths,
|
||||
// Row
|
||||
startRow, endRow, onHover, expandableConfig.rowExpandable, onRow, getRowKey, mergedExpandedKeys, mergedChildrenColumnName, rowHoverable, measureRowRender]);
|
||||
return /*#__PURE__*/React.createElement(TableContext.Provider, {
|
||||
value: TableContextValue
|
||||
}, fullTable);
|
||||
};
|
||||
const RefTable = /*#__PURE__*/React.forwardRef(Table);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefTable.displayName = 'Table';
|
||||
}
|
||||
export const genTable = shouldTriggerRender => {
|
||||
return makeImmutable(RefTable, shouldTriggerRender);
|
||||
};
|
||||
const ImmutableTable = genTable();
|
||||
ImmutableTable.EXPAND_COLUMN = EXPAND_COLUMN;
|
||||
ImmutableTable.INTERNAL_HOOKS = INTERNAL_HOOKS;
|
||||
ImmutableTable.Column = Column;
|
||||
ImmutableTable.ColumnGroup = ColumnGroup;
|
||||
ImmutableTable.Summary = FooterComponents;
|
||||
export default ImmutableTable;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import type { OnCustomizeScroll, ScrollConfig } from '../interface';
|
||||
export interface GridProps<RecordType = any> {
|
||||
data: RecordType[];
|
||||
onScroll: OnCustomizeScroll;
|
||||
}
|
||||
export interface GridRef {
|
||||
scrollLeft: number;
|
||||
nativeElement: HTMLDivElement;
|
||||
scrollTo: (scrollConfig: ScrollConfig) => void;
|
||||
}
|
||||
declare const ResponseGrid: React.ForwardRefExoticComponent<GridProps<any> & React.RefAttributes<GridRef>>;
|
||||
export default ResponseGrid;
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import VirtualList from '@rc-component/virtual-list';
|
||||
import * as React from 'react';
|
||||
import TableContext, { responseImmutable } from "../context/TableContext";
|
||||
import useFlattenRecords from "../hooks/useFlattenRecords";
|
||||
import BodyLine from "./BodyLine";
|
||||
import { GridContext, StaticContext } from "./context";
|
||||
const Grid = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
const {
|
||||
data,
|
||||
onScroll
|
||||
} = props;
|
||||
const {
|
||||
flattenColumns,
|
||||
onColumnResize,
|
||||
getRowKey,
|
||||
expandedKeys,
|
||||
prefixCls,
|
||||
childrenColumnName,
|
||||
scrollX,
|
||||
direction
|
||||
} = useContext(TableContext, ['flattenColumns', 'onColumnResize', 'getRowKey', 'prefixCls', 'expandedKeys', 'childrenColumnName', 'scrollX', 'direction']);
|
||||
const {
|
||||
sticky,
|
||||
scrollY,
|
||||
listItemHeight,
|
||||
getComponent,
|
||||
onScroll: onTablePropScroll
|
||||
} = useContext(StaticContext);
|
||||
|
||||
// =========================== Ref ============================
|
||||
const listRef = React.useRef(null);
|
||||
|
||||
// =========================== Data ===========================
|
||||
const flattenData = useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey);
|
||||
|
||||
// ========================== Column ==========================
|
||||
const columnsWidth = React.useMemo(() => {
|
||||
let total = 0;
|
||||
return flattenColumns.map(({
|
||||
width,
|
||||
minWidth,
|
||||
key
|
||||
}) => {
|
||||
const finalWidth = Math.max(width || 0, minWidth || 0);
|
||||
total += finalWidth;
|
||||
return [key, finalWidth, total];
|
||||
});
|
||||
}, [flattenColumns]);
|
||||
const columnsOffset = React.useMemo(() => columnsWidth.map(colWidth => colWidth[2]), [columnsWidth]);
|
||||
React.useEffect(() => {
|
||||
columnsWidth.forEach(([key, width]) => {
|
||||
onColumnResize(key, width);
|
||||
});
|
||||
}, [columnsWidth]);
|
||||
|
||||
// =========================== Ref ============================
|
||||
React.useImperativeHandle(ref, () => {
|
||||
const obj = {
|
||||
scrollTo: config => {
|
||||
const {
|
||||
offset,
|
||||
...restConfig
|
||||
} = config;
|
||||
|
||||
// If offset is provided, force align to 'top' for consistent behavior
|
||||
if (offset) {
|
||||
listRef.current?.scrollTo({
|
||||
...restConfig,
|
||||
offset,
|
||||
align: 'top'
|
||||
});
|
||||
} else {
|
||||
listRef.current?.scrollTo(config);
|
||||
}
|
||||
},
|
||||
nativeElement: listRef.current?.nativeElement
|
||||
};
|
||||
Object.defineProperty(obj, 'scrollLeft', {
|
||||
get: () => listRef.current?.getScrollInfo().x || 0,
|
||||
set: value => {
|
||||
listRef.current?.scrollTo({
|
||||
left: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/54734
|
||||
Object.defineProperty(obj, 'scrollTop', {
|
||||
get: () => listRef.current?.getScrollInfo().y || 0,
|
||||
set: value => {
|
||||
listRef.current?.scrollTo({
|
||||
top: value
|
||||
});
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
|
||||
// ======================= Col/Row Span =======================
|
||||
const getRowSpan = (column, index) => {
|
||||
const record = flattenData[index]?.record;
|
||||
const {
|
||||
onCell
|
||||
} = column;
|
||||
if (onCell) {
|
||||
const cellProps = onCell(record, index);
|
||||
return cellProps?.rowSpan ?? 1;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
const extraRender = info => {
|
||||
const {
|
||||
start,
|
||||
end,
|
||||
getSize,
|
||||
offsetY
|
||||
} = info;
|
||||
|
||||
// Do nothing if no data
|
||||
if (end < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find first rowSpan column
|
||||
let firstRowSpanColumns = flattenColumns.filter(
|
||||
// rowSpan is 0
|
||||
column => getRowSpan(column, start) === 0);
|
||||
let startIndex = start;
|
||||
for (let i = start; i >= 0; i -= 1) {
|
||||
firstRowSpanColumns = firstRowSpanColumns.filter(column => getRowSpan(column, i) === 0);
|
||||
if (!firstRowSpanColumns.length) {
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find last rowSpan column
|
||||
let lastRowSpanColumns = flattenColumns.filter(
|
||||
// rowSpan is not 1
|
||||
column => getRowSpan(column, end) !== 1);
|
||||
let endIndex = end;
|
||||
for (let i = end; i < flattenData.length; i += 1) {
|
||||
lastRowSpanColumns = lastRowSpanColumns.filter(column => getRowSpan(column, i) !== 1);
|
||||
if (!lastRowSpanColumns.length) {
|
||||
endIndex = Math.max(i - 1, end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect the line who has rowSpan
|
||||
const spanLines = [];
|
||||
for (let i = startIndex; i <= endIndex; i += 1) {
|
||||
const item = flattenData[i];
|
||||
|
||||
// This code will never reach, just incase
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
if (flattenColumns.some(column => getRowSpan(column, i) > 1)) {
|
||||
spanLines.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Patch extra line on the page
|
||||
const nodes = spanLines.map(index => {
|
||||
const item = flattenData[index];
|
||||
const rowKey = getRowKey(item.record, index);
|
||||
const getHeight = rowSpan => {
|
||||
const endItemIndex = index + rowSpan - 1;
|
||||
const endItem = flattenData[endItemIndex];
|
||||
if (!endItem || !endItem.record) {
|
||||
// clamp 到当前可用的最后一行,或退化为默认高度
|
||||
const safeEndIndex = Math.min(endItemIndex, flattenData.length - 1);
|
||||
const safeEndItem = flattenData[safeEndIndex];
|
||||
const endItemKey = getRowKey(safeEndItem.record, safeEndIndex);
|
||||
const sizeInfo = getSize(rowKey, endItemKey);
|
||||
return sizeInfo.bottom - sizeInfo.top;
|
||||
}
|
||||
const endItemKey = getRowKey(endItem.record, endItemIndex);
|
||||
const sizeInfo = getSize(rowKey, endItemKey);
|
||||
return sizeInfo.bottom - sizeInfo.top;
|
||||
};
|
||||
const sizeInfo = getSize(rowKey);
|
||||
return /*#__PURE__*/React.createElement(BodyLine, {
|
||||
key: index,
|
||||
data: item,
|
||||
rowKey: rowKey,
|
||||
index: index,
|
||||
style: {
|
||||
top: -offsetY + sizeInfo.top
|
||||
},
|
||||
extra: true,
|
||||
getHeight: getHeight
|
||||
});
|
||||
});
|
||||
return nodes;
|
||||
};
|
||||
|
||||
// ========================= Context ==========================
|
||||
const gridContext = React.useMemo(() => ({
|
||||
columnsOffset
|
||||
}), [columnsOffset]);
|
||||
|
||||
// ========================== Render ==========================
|
||||
const tblPrefixCls = `${prefixCls}-tbody`;
|
||||
|
||||
// default 'div' in @rc-component/virtual-list
|
||||
const wrapperComponent = getComponent(['body', 'wrapper']);
|
||||
|
||||
// ========================== Sticky Scroll Bar ==========================
|
||||
const horizontalScrollBarStyle = {};
|
||||
if (sticky) {
|
||||
horizontalScrollBarStyle.position = 'sticky';
|
||||
horizontalScrollBarStyle.bottom = 0;
|
||||
if (typeof sticky === 'object' && sticky.offsetScroll) {
|
||||
horizontalScrollBarStyle.bottom = sticky.offsetScroll;
|
||||
}
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(GridContext.Provider, {
|
||||
value: gridContext
|
||||
}, /*#__PURE__*/React.createElement(VirtualList, {
|
||||
fullHeight: false,
|
||||
ref: listRef,
|
||||
prefixCls: `${tblPrefixCls}-virtual`,
|
||||
styles: {
|
||||
horizontalScrollBar: horizontalScrollBarStyle
|
||||
},
|
||||
className: tblPrefixCls,
|
||||
height: scrollY,
|
||||
itemHeight: listItemHeight || 24,
|
||||
data: flattenData,
|
||||
itemKey: item => getRowKey(item.record),
|
||||
component: wrapperComponent,
|
||||
scrollWidth: scrollX,
|
||||
direction: direction,
|
||||
onVirtualScroll: ({
|
||||
x
|
||||
}) => {
|
||||
onScroll({
|
||||
currentTarget: listRef.current?.nativeElement,
|
||||
scrollLeft: x
|
||||
});
|
||||
},
|
||||
onScroll: onTablePropScroll,
|
||||
extraRender: extraRender
|
||||
}, (item, index, itemProps) => {
|
||||
const rowKey = getRowKey(item.record, index);
|
||||
return /*#__PURE__*/React.createElement(BodyLine, {
|
||||
data: item,
|
||||
rowKey: rowKey,
|
||||
index: index,
|
||||
style: itemProps.style
|
||||
});
|
||||
}));
|
||||
});
|
||||
const ResponseGrid = responseImmutable(Grid);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ResponseGrid.displayName = 'ResponseGrid';
|
||||
}
|
||||
export default ResponseGrid;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import type { FlattenData } from '../hooks/useFlattenRecords';
|
||||
export interface BodyLineProps<RecordType = any> {
|
||||
data: FlattenData<RecordType>;
|
||||
index: number;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
rowKey: React.Key;
|
||||
/** Render cell only when it has `rowSpan > 1` */
|
||||
extra?: boolean;
|
||||
getHeight?: (rowSpan: number) => number;
|
||||
}
|
||||
declare const ResponseBodyLine: React.ForwardRefExoticComponent<BodyLineProps<any> & React.RefAttributes<HTMLDivElement>>;
|
||||
export default ResponseBodyLine;
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { clsx } from 'clsx';
|
||||
import * as React from 'react';
|
||||
import Cell from "../Cell";
|
||||
import TableContext, { responseImmutable } from "../context/TableContext";
|
||||
import useRowInfo from "../hooks/useRowInfo";
|
||||
import VirtualCell from "./VirtualCell";
|
||||
import { StaticContext } from "./context";
|
||||
import { computedExpandedClassName } from "../utils/expandUtil";
|
||||
const BodyLine = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
const {
|
||||
data,
|
||||
index,
|
||||
className,
|
||||
rowKey,
|
||||
style,
|
||||
extra,
|
||||
getHeight,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
record,
|
||||
indent,
|
||||
index: renderIndex
|
||||
} = data;
|
||||
const {
|
||||
scrollX,
|
||||
flattenColumns,
|
||||
prefixCls,
|
||||
fixColumn,
|
||||
componentWidth
|
||||
} = useContext(TableContext, ['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX']);
|
||||
const {
|
||||
getComponent
|
||||
} = useContext(StaticContext, ['getComponent']);
|
||||
const rowInfo = useRowInfo(record, rowKey, index, indent);
|
||||
const RowComponent = getComponent(['body', 'row'], 'div');
|
||||
const cellComponent = getComponent(['body', 'cell'], 'div');
|
||||
|
||||
// ========================== Expand ==========================
|
||||
const {
|
||||
rowSupportExpand,
|
||||
expanded,
|
||||
rowProps,
|
||||
expandedRowRender,
|
||||
expandedRowClassName
|
||||
} = rowInfo;
|
||||
let expandRowNode;
|
||||
if (rowSupportExpand && expanded) {
|
||||
const expandContent = expandedRowRender(record, index, indent + 1, expanded);
|
||||
const expandedClsName = computedExpandedClassName(expandedRowClassName, record, index, indent);
|
||||
let additionalProps = {};
|
||||
if (fixColumn) {
|
||||
additionalProps = {
|
||||
style: {
|
||||
['--virtual-width']: `${componentWidth}px`
|
||||
}
|
||||
};
|
||||
}
|
||||
const rowCellCls = `${prefixCls}-expanded-row-cell`;
|
||||
expandRowNode = /*#__PURE__*/React.createElement(RowComponent, {
|
||||
className: clsx(`${prefixCls}-expanded-row`, `${prefixCls}-expanded-row-level-${indent + 1}`, expandedClsName)
|
||||
}, /*#__PURE__*/React.createElement(Cell, {
|
||||
component: cellComponent,
|
||||
prefixCls: prefixCls,
|
||||
className: clsx(rowCellCls, {
|
||||
[`${rowCellCls}-fixed`]: fixColumn
|
||||
}),
|
||||
additionalProps: additionalProps
|
||||
}, expandContent));
|
||||
}
|
||||
|
||||
// ========================== Render ==========================
|
||||
const rowStyle = {
|
||||
...style,
|
||||
width: scrollX
|
||||
};
|
||||
if (extra) {
|
||||
rowStyle.position = 'absolute';
|
||||
rowStyle.pointerEvents = 'none';
|
||||
}
|
||||
const rowNode = /*#__PURE__*/React.createElement(RowComponent, _extends({}, rowProps, restProps, {
|
||||
"data-row-key": rowKey,
|
||||
ref: rowSupportExpand ? null : ref,
|
||||
className: clsx(className, `${prefixCls}-row`, rowProps?.className, {
|
||||
[`${prefixCls}-row-extra`]: extra
|
||||
}),
|
||||
style: {
|
||||
...rowStyle,
|
||||
...rowProps?.style
|
||||
}
|
||||
}), flattenColumns.map((column, colIndex) => {
|
||||
return /*#__PURE__*/React.createElement(VirtualCell, {
|
||||
key: colIndex,
|
||||
component: cellComponent,
|
||||
rowInfo: rowInfo,
|
||||
column: column,
|
||||
colIndex: colIndex,
|
||||
indent: indent,
|
||||
index: index,
|
||||
renderIndex: renderIndex,
|
||||
record: record,
|
||||
inverse: extra,
|
||||
getHeight: getHeight
|
||||
});
|
||||
}));
|
||||
if (rowSupportExpand) {
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
ref: ref
|
||||
}, rowNode, expandRowNode);
|
||||
}
|
||||
return rowNode;
|
||||
});
|
||||
const ResponseBodyLine = responseImmutable(BodyLine);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ResponseBodyLine.displayName = 'BodyLine';
|
||||
}
|
||||
export default ResponseBodyLine;
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import type useRowInfo from '../hooks/useRowInfo';
|
||||
import type { ColumnType, CustomizeComponent } from '../interface';
|
||||
export interface VirtualCellProps<RecordType> {
|
||||
rowInfo: ReturnType<typeof useRowInfo<RecordType>>;
|
||||
column: ColumnType<RecordType>;
|
||||
colIndex: number;
|
||||
indent: number;
|
||||
index: number;
|
||||
component?: CustomizeComponent;
|
||||
/** Used for `column.render` */
|
||||
renderIndex: number;
|
||||
record: RecordType;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
/** Render cell only when it has `rowSpan > 1` */
|
||||
inverse?: boolean;
|
||||
getHeight?: (rowSpan: number) => number;
|
||||
}
|
||||
/**
|
||||
* Return the width of the column by `colSpan`.
|
||||
* When `colSpan` is `0` will be trade as `1`.
|
||||
*/
|
||||
export declare function getColumnWidth(colIndex: number, colSpan: number, columnsOffset: number[]): number;
|
||||
declare const VirtualCell: <RecordType>(props: VirtualCellProps<RecordType>) => React.JSX.Element;
|
||||
export default VirtualCell;
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { clsx } from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { getCellProps } from "../Body/BodyRow";
|
||||
import Cell from "../Cell";
|
||||
import { GridContext } from "./context";
|
||||
/**
|
||||
* Return the width of the column by `colSpan`.
|
||||
* When `colSpan` is `0` will be trade as `1`.
|
||||
*/
|
||||
export function getColumnWidth(colIndex, colSpan, columnsOffset) {
|
||||
const mergedColSpan = colSpan || 1;
|
||||
return columnsOffset[colIndex + mergedColSpan] - (columnsOffset[colIndex] || 0);
|
||||
}
|
||||
const VirtualCell = props => {
|
||||
const {
|
||||
rowInfo,
|
||||
column,
|
||||
colIndex,
|
||||
indent,
|
||||
index,
|
||||
component,
|
||||
renderIndex,
|
||||
record,
|
||||
style,
|
||||
className,
|
||||
inverse,
|
||||
getHeight
|
||||
} = props;
|
||||
const {
|
||||
render,
|
||||
dataIndex,
|
||||
className: columnClassName,
|
||||
width: colWidth
|
||||
} = column;
|
||||
const {
|
||||
columnsOffset
|
||||
} = useContext(GridContext, ['columnsOffset']);
|
||||
|
||||
// TODO: support `expandableRowOffset`
|
||||
const {
|
||||
key,
|
||||
fixedInfo,
|
||||
appendCellNode,
|
||||
additionalCellProps
|
||||
} = getCellProps(rowInfo, column, colIndex, indent, index);
|
||||
const {
|
||||
style: cellStyle,
|
||||
colSpan = 1,
|
||||
rowSpan = 1
|
||||
} = additionalCellProps;
|
||||
|
||||
// ========================= ColWidth =========================
|
||||
// column width
|
||||
const startColIndex = colIndex - 1;
|
||||
const concatColWidth = getColumnWidth(startColIndex, colSpan, columnsOffset);
|
||||
|
||||
// margin offset
|
||||
const marginOffset = colSpan > 1 ? colWidth - concatColWidth : 0;
|
||||
|
||||
// ========================== Style ===========================
|
||||
const mergedStyle = {
|
||||
...cellStyle,
|
||||
...style,
|
||||
flex: `0 0 ${concatColWidth}px`,
|
||||
width: `${concatColWidth}px`,
|
||||
marginRight: marginOffset,
|
||||
pointerEvents: 'auto'
|
||||
};
|
||||
|
||||
// When `colSpan` or `rowSpan` is `0`, should skip render.
|
||||
const needHide = React.useMemo(() => {
|
||||
if (inverse) {
|
||||
return rowSpan <= 1;
|
||||
} else {
|
||||
return colSpan === 0 || rowSpan === 0 || rowSpan > 1;
|
||||
}
|
||||
}, [rowSpan, colSpan, inverse]);
|
||||
|
||||
// 0 rowSpan or colSpan should not render
|
||||
if (needHide) {
|
||||
mergedStyle.visibility = 'hidden';
|
||||
} else if (inverse) {
|
||||
mergedStyle.height = getHeight?.(rowSpan);
|
||||
}
|
||||
const mergedRender = needHide ? () => null : render;
|
||||
|
||||
// ========================== Render ==========================
|
||||
const cellSpan = {};
|
||||
|
||||
// Virtual should reset `colSpan` & `rowSpan`
|
||||
if (rowSpan === 0 || colSpan === 0) {
|
||||
cellSpan.rowSpan = 1;
|
||||
cellSpan.colSpan = 1;
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(Cell, _extends({
|
||||
className: clsx(columnClassName, className),
|
||||
ellipsis: column.ellipsis,
|
||||
align: column.align,
|
||||
scope: column.rowScope,
|
||||
component: component,
|
||||
prefixCls: rowInfo.prefixCls,
|
||||
key: key,
|
||||
record: record,
|
||||
index: index,
|
||||
renderIndex: renderIndex,
|
||||
dataIndex: dataIndex,
|
||||
render: mergedRender,
|
||||
shouldCellUpdate: column.shouldCellUpdate
|
||||
}, fixedInfo, {
|
||||
appendNode: appendCellNode,
|
||||
additionalProps: {
|
||||
...additionalCellProps,
|
||||
style: mergedStyle,
|
||||
...cellSpan
|
||||
}
|
||||
}));
|
||||
};
|
||||
export default VirtualCell;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/// <reference types="react" />
|
||||
import type { GetComponent, TableSticky } from '../interface';
|
||||
export interface StaticContextProps {
|
||||
scrollY: number;
|
||||
listItemHeight: number;
|
||||
sticky: boolean | TableSticky;
|
||||
getComponent: GetComponent;
|
||||
onScroll?: React.UIEventHandler<HTMLDivElement>;
|
||||
}
|
||||
export declare const StaticContext: import("@rc-component/context").SelectorContext<StaticContextProps>;
|
||||
export interface GridContextProps {
|
||||
columnsOffset: number[];
|
||||
}
|
||||
export declare const GridContext: import("@rc-component/context").SelectorContext<GridContextProps>;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { createContext } from '@rc-component/context';
|
||||
export const StaticContext = createContext(null);
|
||||
export const GridContext = createContext(null);
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import type { CompareProps } from '@rc-component/context/lib/Immutable';
|
||||
import * as React from 'react';
|
||||
import type { Reference } from '../interface';
|
||||
import { type TableProps } from '../Table';
|
||||
export interface VirtualTableProps<RecordType> extends Omit<TableProps<RecordType>, 'scroll'> {
|
||||
listItemHeight?: number;
|
||||
scroll: {
|
||||
x?: number;
|
||||
y?: number;
|
||||
};
|
||||
}
|
||||
export type ForwardGenericVirtualTable = (<RecordType>(props: TableProps<RecordType> & React.RefAttributes<Reference>) => React.ReactElement<any>) & {
|
||||
displayName?: string;
|
||||
};
|
||||
export declare const genVirtualTable: (shouldTriggerRender?: CompareProps<ForwardGenericVirtualTable>) => ForwardGenericVirtualTable;
|
||||
declare const _default: ForwardGenericVirtualTable;
|
||||
export default _default;
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
import { clsx } from 'clsx';
|
||||
import { useEvent, warning } from '@rc-component/util';
|
||||
import * as React from 'react';
|
||||
import { INTERNAL_HOOKS } from "../constant";
|
||||
import { makeImmutable } from "../context/TableContext";
|
||||
import Table, { DEFAULT_PREFIX } from "../Table";
|
||||
import Grid from "./BodyGrid";
|
||||
import { StaticContext } from "./context";
|
||||
import getValue from "@rc-component/util/es/utils/get";
|
||||
const renderBody = (rawData, props) => {
|
||||
const {
|
||||
ref,
|
||||
onScroll
|
||||
} = props;
|
||||
return /*#__PURE__*/React.createElement(Grid, {
|
||||
ref: ref,
|
||||
data: rawData,
|
||||
onScroll: onScroll
|
||||
});
|
||||
};
|
||||
const VirtualTable = (props, ref) => {
|
||||
const {
|
||||
data,
|
||||
columns,
|
||||
scroll,
|
||||
sticky,
|
||||
prefixCls = DEFAULT_PREFIX,
|
||||
className,
|
||||
listItemHeight,
|
||||
components,
|
||||
onScroll
|
||||
} = props;
|
||||
let {
|
||||
x: scrollX,
|
||||
y: scrollY
|
||||
} = scroll || {};
|
||||
|
||||
// Fill scrollX
|
||||
if (typeof scrollX !== 'number') {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(!scrollX, '`scroll.x` in virtual table must be number.');
|
||||
}
|
||||
scrollX = 1;
|
||||
}
|
||||
|
||||
// Fill scrollY
|
||||
if (typeof scrollY !== 'number') {
|
||||
scrollY = 500;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(false, '`scroll.y` in virtual table must be number.');
|
||||
}
|
||||
}
|
||||
const getComponent = useEvent((path, defaultComponent) => getValue(components, path) || defaultComponent);
|
||||
|
||||
// Memo this
|
||||
const onInternalScroll = useEvent(onScroll);
|
||||
|
||||
// ========================= Context ==========================
|
||||
const context = React.useMemo(() => ({
|
||||
sticky,
|
||||
scrollY,
|
||||
listItemHeight,
|
||||
getComponent,
|
||||
onScroll: onInternalScroll
|
||||
}), [sticky, scrollY, listItemHeight, getComponent, onInternalScroll]);
|
||||
|
||||
// ========================== Render ==========================
|
||||
return /*#__PURE__*/React.createElement(StaticContext.Provider, {
|
||||
value: context
|
||||
}, /*#__PURE__*/React.createElement(Table, _extends({}, props, {
|
||||
className: clsx(className, `${prefixCls}-virtual`),
|
||||
scroll: {
|
||||
...scroll,
|
||||
x: scrollX
|
||||
},
|
||||
components: {
|
||||
...components,
|
||||
// fix https://github.com/ant-design/ant-design/issues/48991
|
||||
body: data?.length ? renderBody : undefined
|
||||
},
|
||||
columns: columns,
|
||||
internalHooks: INTERNAL_HOOKS,
|
||||
tailor: true,
|
||||
ref: ref
|
||||
})));
|
||||
};
|
||||
const RefVirtualTable = /*#__PURE__*/React.forwardRef(VirtualTable);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefVirtualTable.displayName = 'VirtualTable';
|
||||
}
|
||||
export const genVirtualTable = shouldTriggerRender => {
|
||||
return makeImmutable(RefVirtualTable, shouldTriggerRender);
|
||||
};
|
||||
export default genVirtualTable();
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export declare const EXPAND_COLUMN: {};
|
||||
export declare const INTERNAL_HOOKS = "rc-table-internal-hook";
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const EXPAND_COLUMN = {};
|
||||
export const INTERNAL_HOOKS = 'rc-table-internal-hook';
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
export interface PerfRecord {
|
||||
renderWithProps: boolean;
|
||||
}
|
||||
declare const PerfContext: React.Context<PerfRecord>;
|
||||
export default PerfContext;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
// TODO: Remove when use `responsiveImmutable`
|
||||
const PerfContext = /*#__PURE__*/React.createContext({
|
||||
renderWithProps: false
|
||||
});
|
||||
export default PerfContext;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/// <reference types="react" />
|
||||
import type { ColumnsType, ColumnType, Direction, ExpandableConfig, ExpandableType, ExpandedRowRender, GetComponent, GetComponentProps, GetRowKey, RenderExpandIcon, RowClassName, TableLayout, TriggerEventHandler } from '../interface';
|
||||
import type { FixedInfo } from '../utils/fixUtil';
|
||||
import type { TableProps } from '../Table';
|
||||
declare const makeImmutable: <T extends import("react").ComponentType<any>>(Component: T, shouldTriggerRender?: import("@rc-component/context/lib/Immutable").CompareProps<T>) => T, responseImmutable: <T_1 extends import("react").ComponentType<any>>(Component: T_1, propsAreEqual?: import("@rc-component/context/lib/Immutable").CompareProps<T_1>) => T_1, useImmutableMark: () => number;
|
||||
export { makeImmutable, responseImmutable, useImmutableMark };
|
||||
export type ScrollInfoType = [scrollLeft: number, scrollRange: number];
|
||||
export interface TableContextProps<RecordType = any> {
|
||||
scrollX: number | string | true;
|
||||
classNames?: TableProps['classNames'];
|
||||
styles?: TableProps['styles'];
|
||||
prefixCls: string;
|
||||
getComponent: GetComponent;
|
||||
scrollbarSize: number;
|
||||
direction: Direction;
|
||||
fixedInfoList: readonly FixedInfo[];
|
||||
isSticky: boolean;
|
||||
componentWidth: number;
|
||||
fixHeader: boolean;
|
||||
fixColumn: boolean;
|
||||
horizonScroll: boolean;
|
||||
scrollInfo: ScrollInfoType;
|
||||
rowClassName: string | RowClassName<RecordType>;
|
||||
expandedRowClassName: string | RowClassName<RecordType>;
|
||||
onRow?: GetComponentProps<RecordType>;
|
||||
emptyNode?: React.ReactNode;
|
||||
tableLayout: TableLayout;
|
||||
indentSize: number;
|
||||
expandableType: ExpandableType;
|
||||
expandRowByClick: boolean;
|
||||
expandedRowRender: ExpandedRowRender<RecordType>;
|
||||
expandIcon: RenderExpandIcon<RecordType>;
|
||||
onTriggerExpand: TriggerEventHandler<RecordType>;
|
||||
expandIconColumnIndex: number;
|
||||
allColumnsFixedLeft: boolean;
|
||||
columns: ColumnsType<RecordType>;
|
||||
flattenColumns: readonly ColumnType<RecordType>[];
|
||||
onColumnResize: (columnKey: React.Key, width: number) => void;
|
||||
colWidths: number[];
|
||||
hoverStartRow: number;
|
||||
hoverEndRow: number;
|
||||
onHover: (start: number, end: number) => void;
|
||||
rowExpandable: (record: RecordType) => boolean;
|
||||
expandedKeys: Set<React.Key>;
|
||||
getRowKey: GetRowKey<RecordType>;
|
||||
childrenColumnName: string;
|
||||
rowHoverable?: boolean;
|
||||
expandedRowOffset: ExpandableConfig<RecordType>['expandedRowOffset'];
|
||||
measureRowRender?: (measureRow: React.ReactNode) => React.ReactNode;
|
||||
}
|
||||
declare const TableContext: import("@rc-component/context").SelectorContext<TableContextProps<any>>;
|
||||
export default TableContext;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { createContext, createImmutable } from '@rc-component/context';
|
||||
const {
|
||||
makeImmutable,
|
||||
responseImmutable,
|
||||
useImmutableMark
|
||||
} = createImmutable();
|
||||
export { makeImmutable, responseImmutable, useImmutableMark };
|
||||
const TableContext = createContext();
|
||||
export default TableContext;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnsType, ColumnType, Direction, FixedType, GetRowKey, Key, RenderExpandIcon, TriggerEventHandler } from '../../interface';
|
||||
export declare function convertChildrenToColumns<RecordType>(children: React.ReactNode): ColumnsType<RecordType>;
|
||||
/**
|
||||
* Parse `columns` & `children` into `columns`.
|
||||
*/
|
||||
declare function useColumns<RecordType>({ prefixCls, columns, children, expandable, expandedKeys, columnTitle, getRowKey, onTriggerExpand, expandIcon, rowExpandable, expandIconColumnIndex, expandedRowOffset, direction, expandRowByClick, columnWidth, fixed, scrollWidth, clientWidth, }: {
|
||||
prefixCls?: string;
|
||||
columns?: ColumnsType<RecordType>;
|
||||
children?: React.ReactNode;
|
||||
expandable: boolean;
|
||||
expandedKeys: Set<Key>;
|
||||
columnTitle?: React.ReactNode;
|
||||
getRowKey: GetRowKey<RecordType>;
|
||||
onTriggerExpand: TriggerEventHandler<RecordType>;
|
||||
expandIcon?: RenderExpandIcon<RecordType>;
|
||||
rowExpandable?: (record: RecordType) => boolean;
|
||||
expandIconColumnIndex?: number;
|
||||
direction?: Direction;
|
||||
expandRowByClick?: boolean;
|
||||
columnWidth?: number | string;
|
||||
clientWidth: number;
|
||||
fixed?: FixedType;
|
||||
scrollWidth?: number;
|
||||
expandedRowOffset?: number;
|
||||
}, transformColumns: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>): [
|
||||
columns: ColumnsType<RecordType>,
|
||||
flattenColumns: readonly ColumnType<RecordType>[],
|
||||
realScrollWidth: undefined | number
|
||||
];
|
||||
export default useColumns;
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
import toArray from "@rc-component/util/es/Children/toArray";
|
||||
import warning from "@rc-component/util/es/warning";
|
||||
import * as React from 'react';
|
||||
import { EXPAND_COLUMN } from "../../constant";
|
||||
import { INTERNAL_COL_DEFINE } from "../../utils/legacyUtil";
|
||||
import useWidthColumns from "./useWidthColumns";
|
||||
export function convertChildrenToColumns(children) {
|
||||
return toArray(children).filter(node => /*#__PURE__*/React.isValidElement(node)).map(node => {
|
||||
const {
|
||||
key,
|
||||
props
|
||||
} = node;
|
||||
const {
|
||||
children: nodeChildren,
|
||||
...restProps
|
||||
} = props;
|
||||
const column = {
|
||||
key,
|
||||
...restProps
|
||||
};
|
||||
if (nodeChildren) {
|
||||
column.children = convertChildrenToColumns(nodeChildren);
|
||||
}
|
||||
return column;
|
||||
});
|
||||
}
|
||||
function filterHiddenColumns(columns) {
|
||||
return columns.filter(column => column && typeof column === 'object' && !column.hidden).map(column => {
|
||||
const subColumns = column.children;
|
||||
if (subColumns && subColumns.length > 0) {
|
||||
return {
|
||||
...column,
|
||||
children: filterHiddenColumns(subColumns)
|
||||
};
|
||||
}
|
||||
return column;
|
||||
});
|
||||
}
|
||||
function flatColumns(columns, parentKey = 'key') {
|
||||
return columns.filter(column => column && typeof column === 'object').reduce((list, column, index) => {
|
||||
const {
|
||||
fixed
|
||||
} = column;
|
||||
const parsedFixed = fixed === true || fixed === 'left' ? 'start' : fixed === 'right' ? 'end' : fixed;
|
||||
const mergedKey = `${parentKey}-${index}`;
|
||||
const subColumns = column.children;
|
||||
if (subColumns && subColumns.length > 0) {
|
||||
return [...list, ...flatColumns(subColumns, mergedKey).map(subColum => ({
|
||||
...subColum,
|
||||
fixed: subColum.fixed ?? parsedFixed
|
||||
}))];
|
||||
}
|
||||
return [...list, {
|
||||
key: mergedKey,
|
||||
...column,
|
||||
fixed: parsedFixed
|
||||
}];
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse `columns` & `children` into `columns`.
|
||||
*/
|
||||
function useColumns({
|
||||
prefixCls,
|
||||
columns,
|
||||
children,
|
||||
expandable,
|
||||
expandedKeys,
|
||||
columnTitle,
|
||||
getRowKey,
|
||||
onTriggerExpand,
|
||||
expandIcon,
|
||||
rowExpandable,
|
||||
expandIconColumnIndex,
|
||||
expandedRowOffset = 0,
|
||||
direction,
|
||||
expandRowByClick,
|
||||
columnWidth,
|
||||
fixed,
|
||||
scrollWidth,
|
||||
clientWidth
|
||||
}, transformColumns) {
|
||||
const baseColumns = React.useMemo(() => {
|
||||
const newColumns = columns || convertChildrenToColumns(children) || [];
|
||||
return filterHiddenColumns(newColumns.slice());
|
||||
}, [columns, children]);
|
||||
|
||||
// ========================== Expand ==========================
|
||||
const withExpandColumns = React.useMemo(() => {
|
||||
if (expandable) {
|
||||
let cloneColumns = baseColumns.slice();
|
||||
|
||||
// >>> Warning if use `expandIconColumnIndex`
|
||||
if (process.env.NODE_ENV !== 'production' && expandIconColumnIndex >= 0) {
|
||||
warning(false, '`expandIconColumnIndex` is deprecated. Please use `Table.EXPAND_COLUMN` in `columns` instead.');
|
||||
}
|
||||
|
||||
// >>> Insert expand column if not exist
|
||||
if (!cloneColumns.includes(EXPAND_COLUMN)) {
|
||||
const expandColIndex = expandIconColumnIndex || 0;
|
||||
const insertIndex = expandColIndex === 0 && (fixed === 'right' || fixed === 'end') ? baseColumns.length : expandColIndex;
|
||||
if (insertIndex >= 0) {
|
||||
cloneColumns.splice(insertIndex, 0, EXPAND_COLUMN);
|
||||
}
|
||||
}
|
||||
|
||||
// >>> Deduplicate additional expand column
|
||||
if (process.env.NODE_ENV !== 'production' && cloneColumns.filter(c => c === EXPAND_COLUMN).length > 1) {
|
||||
warning(false, 'There exist more than one `EXPAND_COLUMN` in `columns`.');
|
||||
}
|
||||
const expandColumnIndex = cloneColumns.indexOf(EXPAND_COLUMN);
|
||||
cloneColumns = cloneColumns.filter((column, index) => column !== EXPAND_COLUMN || index === expandColumnIndex);
|
||||
|
||||
// >>> Check if expand column need to fixed
|
||||
const prevColumn = baseColumns[expandColumnIndex];
|
||||
let fixedColumn;
|
||||
if (fixed) {
|
||||
fixedColumn = fixed;
|
||||
} else {
|
||||
fixedColumn = prevColumn ? prevColumn.fixed : null;
|
||||
}
|
||||
|
||||
// >>> Create expandable column
|
||||
const expandColumn = {
|
||||
[INTERNAL_COL_DEFINE]: {
|
||||
className: `${prefixCls}-expand-icon-col`,
|
||||
columnType: 'EXPAND_COLUMN'
|
||||
},
|
||||
title: columnTitle,
|
||||
fixed: fixedColumn,
|
||||
className: `${prefixCls}-row-expand-icon-cell`,
|
||||
width: columnWidth,
|
||||
render: (_, record, index) => {
|
||||
const rowKey = getRowKey(record, index);
|
||||
const expanded = expandedKeys.has(rowKey);
|
||||
const recordExpandable = rowExpandable ? rowExpandable(record) : true;
|
||||
const icon = expandIcon({
|
||||
prefixCls,
|
||||
expanded,
|
||||
expandable: recordExpandable,
|
||||
record,
|
||||
onExpand: onTriggerExpand
|
||||
});
|
||||
if (expandRowByClick) {
|
||||
return /*#__PURE__*/React.createElement("span", {
|
||||
onClick: e => e.stopPropagation()
|
||||
}, icon);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
};
|
||||
return cloneColumns.map((col, index) => {
|
||||
const column = col === EXPAND_COLUMN ? expandColumn : col;
|
||||
if (index < expandedRowOffset) {
|
||||
return {
|
||||
...column,
|
||||
fixed: column.fixed || 'start'
|
||||
};
|
||||
}
|
||||
return column;
|
||||
});
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production' && baseColumns.includes(EXPAND_COLUMN)) {
|
||||
warning(false, '`expandable` is not config but there exist `EXPAND_COLUMN` in `columns`.');
|
||||
}
|
||||
return baseColumns.filter(col => col !== EXPAND_COLUMN);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [expandable, baseColumns, getRowKey, expandedKeys, expandIcon, direction, expandedRowOffset]);
|
||||
|
||||
// ========================= Transform ========================
|
||||
const mergedColumns = React.useMemo(() => {
|
||||
let finalColumns = withExpandColumns;
|
||||
if (transformColumns) {
|
||||
finalColumns = transformColumns(finalColumns);
|
||||
}
|
||||
|
||||
// Always provides at least one column for table display
|
||||
if (!finalColumns.length) {
|
||||
finalColumns = [{
|
||||
render: () => null
|
||||
}];
|
||||
}
|
||||
return finalColumns;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [transformColumns, withExpandColumns, direction]);
|
||||
|
||||
// ========================== Flatten =========================
|
||||
const flattenColumns = React.useMemo(() => flatColumns(mergedColumns),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[mergedColumns, direction, scrollWidth]);
|
||||
|
||||
// ========================= FillWidth ========================
|
||||
const [filledColumns, realScrollWidth] = useWidthColumns(flattenColumns, scrollWidth, clientWidth);
|
||||
return [mergedColumns, filledColumns, realScrollWidth];
|
||||
}
|
||||
export default useColumns;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import type { ColumnsType } from '../../interface';
|
||||
/**
|
||||
* Fill all column with width
|
||||
*/
|
||||
export default function useWidthColumns(flattenColumns: ColumnsType<any>, scrollWidth: number, clientWidth: number): [columns: ColumnsType<any>, realScrollWidth: number];
|
||||
Generated
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
import * as React from 'react';
|
||||
function parseColWidth(totalWidth, width = '') {
|
||||
if (typeof width === 'number') {
|
||||
return width;
|
||||
}
|
||||
if (width.endsWith('%')) {
|
||||
return totalWidth * parseFloat(width) / 100;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all column with width
|
||||
*/
|
||||
export default function useWidthColumns(flattenColumns, scrollWidth, clientWidth) {
|
||||
return React.useMemo(() => {
|
||||
// Fill width if needed
|
||||
if (scrollWidth && scrollWidth > 0) {
|
||||
let totalWidth = 0;
|
||||
let missWidthCount = 0;
|
||||
|
||||
// collect not given width column
|
||||
flattenColumns.forEach(col => {
|
||||
const colWidth = parseColWidth(scrollWidth, col.width);
|
||||
if (colWidth) {
|
||||
totalWidth += colWidth;
|
||||
} else {
|
||||
missWidthCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Fill width
|
||||
const maxFitWidth = Math.max(scrollWidth, clientWidth);
|
||||
let restWidth = Math.max(maxFitWidth - totalWidth, missWidthCount);
|
||||
let restCount = missWidthCount;
|
||||
const avgWidth = restWidth / missWidthCount;
|
||||
let realTotal = 0;
|
||||
const filledColumns = flattenColumns.map(col => {
|
||||
const clone = {
|
||||
...col
|
||||
};
|
||||
const colWidth = parseColWidth(scrollWidth, clone.width);
|
||||
if (colWidth) {
|
||||
clone.width = colWidth;
|
||||
} else {
|
||||
const colAvgWidth = Math.floor(avgWidth);
|
||||
clone.width = restCount === 1 ? restWidth : colAvgWidth;
|
||||
restWidth -= colAvgWidth;
|
||||
restCount -= 1;
|
||||
}
|
||||
realTotal += clone.width;
|
||||
return clone;
|
||||
});
|
||||
|
||||
// If realTotal is less than clientWidth,
|
||||
// We need extend column width
|
||||
if (realTotal < maxFitWidth) {
|
||||
const scale = maxFitWidth / realTotal;
|
||||
restWidth = maxFitWidth;
|
||||
filledColumns.forEach((col, index) => {
|
||||
const colWidth = Math.floor(col.width * scale);
|
||||
col.width = index === filledColumns.length - 1 ? restWidth : colWidth;
|
||||
restWidth -= colWidth;
|
||||
});
|
||||
}
|
||||
return [filledColumns, Math.max(realTotal, maxFitWidth)];
|
||||
}
|
||||
return [flattenColumns, scrollWidth];
|
||||
}, [flattenColumns, scrollWidth, clientWidth]);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { ExpandableConfig, ExpandableType, GetRowKey, Key, RenderExpandIcon, TriggerEventHandler } from '../interface';
|
||||
import type { TableProps } from '../Table';
|
||||
export default function useExpand<RecordType>(props: TableProps<RecordType>, mergedData: readonly RecordType[], getRowKey: GetRowKey<RecordType>): [
|
||||
expandableConfig: ExpandableConfig<RecordType>,
|
||||
expandableType: ExpandableType,
|
||||
expandedKeys: Set<Key>,
|
||||
expandIcon: RenderExpandIcon<RecordType>,
|
||||
childrenColumnName: string,
|
||||
onTriggerExpand: TriggerEventHandler<RecordType>
|
||||
];
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import warning from "@rc-component/util/es/warning";
|
||||
import * as React from 'react';
|
||||
import { INTERNAL_HOOKS } from "../constant";
|
||||
import { findAllChildrenKeys, renderExpandIcon } from "../utils/expandUtil";
|
||||
import { getExpandableProps } from "../utils/legacyUtil";
|
||||
export default function useExpand(props, mergedData, getRowKey) {
|
||||
const expandableConfig = getExpandableProps(props);
|
||||
const {
|
||||
expandIcon,
|
||||
expandedRowKeys,
|
||||
defaultExpandedRowKeys,
|
||||
defaultExpandAllRows,
|
||||
expandedRowRender,
|
||||
onExpand,
|
||||
onExpandedRowsChange,
|
||||
childrenColumnName
|
||||
} = expandableConfig;
|
||||
const mergedExpandIcon = expandIcon || renderExpandIcon;
|
||||
const mergedChildrenColumnName = childrenColumnName || 'children';
|
||||
const expandableType = React.useMemo(() => {
|
||||
if (expandedRowRender) {
|
||||
return 'row';
|
||||
}
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
/**
|
||||
* Fix https://github.com/ant-design/ant-design/issues/21154
|
||||
* This is a workaround to not to break current behavior.
|
||||
* We can remove follow code after final release.
|
||||
*
|
||||
* To other developer:
|
||||
* Do not use `__PARENT_RENDER_ICON__` in prod since we will remove this when refactor
|
||||
*/
|
||||
if (props.expandable && props.internalHooks === INTERNAL_HOOKS && props.expandable.__PARENT_RENDER_ICON__ || mergedData.some(record => record && typeof record === 'object' && record[mergedChildrenColumnName])) {
|
||||
return 'nest';
|
||||
}
|
||||
/* eslint-enable */
|
||||
return false;
|
||||
}, [!!expandedRowRender, mergedData]);
|
||||
const [innerExpandedKeys, setInnerExpandedKeys] = React.useState(() => {
|
||||
if (defaultExpandedRowKeys) {
|
||||
return defaultExpandedRowKeys;
|
||||
}
|
||||
if (defaultExpandAllRows) {
|
||||
return findAllChildrenKeys(mergedData, getRowKey, mergedChildrenColumnName);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const mergedExpandedKeys = React.useMemo(() => new Set(expandedRowKeys || innerExpandedKeys || []), [expandedRowKeys, innerExpandedKeys]);
|
||||
const onTriggerExpand = React.useCallback(record => {
|
||||
const key = getRowKey(record, mergedData.indexOf(record));
|
||||
let newExpandedKeys;
|
||||
const hasKey = mergedExpandedKeys.has(key);
|
||||
if (hasKey) {
|
||||
mergedExpandedKeys.delete(key);
|
||||
newExpandedKeys = [...mergedExpandedKeys];
|
||||
} else {
|
||||
newExpandedKeys = [...mergedExpandedKeys, key];
|
||||
}
|
||||
setInnerExpandedKeys(newExpandedKeys);
|
||||
if (onExpand) {
|
||||
onExpand(!hasKey, record);
|
||||
}
|
||||
if (onExpandedRowsChange) {
|
||||
onExpandedRowsChange(newExpandedKeys);
|
||||
}
|
||||
}, [getRowKey, mergedExpandedKeys, mergedData, onExpand, onExpandedRowsChange]);
|
||||
|
||||
// Warning if use `expandedRowRender` and nest children in the same time
|
||||
if (process.env.NODE_ENV !== 'production' && expandedRowRender && mergedData.some(record => {
|
||||
return Array.isArray(record?.[mergedChildrenColumnName]);
|
||||
})) {
|
||||
warning(false, '`expandedRowRender` should not use with nested Table');
|
||||
}
|
||||
return [expandableConfig, expandableType, mergedExpandedKeys, mergedExpandIcon, mergedChildrenColumnName, onTriggerExpand];
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import type { ColumnType, StickyOffsets } from '../interface';
|
||||
export default function useFixedInfo<RecordType>(flattenColumns: readonly ColumnType<RecordType>[], stickyOffsets: StickyOffsets): import("../utils/fixUtil").FixedInfo[];
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import useMemo from "@rc-component/util/es/hooks/useMemo";
|
||||
import isEqual from "@rc-component/util/es/isEqual";
|
||||
import { getCellFixedInfo } from "../utils/fixUtil";
|
||||
import * as React from 'react';
|
||||
export default function useFixedInfo(flattenColumns, stickyOffsets) {
|
||||
const fixedInfoList = React.useMemo(() => flattenColumns.map((_, colIndex) => getCellFixedInfo(colIndex, colIndex, flattenColumns, stickyOffsets)), [flattenColumns, stickyOffsets]);
|
||||
return useMemo(() => fixedInfoList, [fixedInfoList], (prev, next) => !isEqual(prev, next));
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import type { GetRowKey, Key } from '../interface';
|
||||
export interface FlattenData<RecordType> {
|
||||
record: RecordType;
|
||||
indent: number;
|
||||
index: number;
|
||||
rowKey: Key;
|
||||
}
|
||||
/**
|
||||
* flat tree data on expanded state
|
||||
*
|
||||
* @export
|
||||
* @template T
|
||||
* @param {*} data : table data
|
||||
* @param {string} childrenColumnName : 指定树形结构的列名
|
||||
* @param {Set<Key>} expandedKeys : 展开的行对应的keys
|
||||
* @param {GetRowKey<T>} getRowKey : 获取当前rowKey的方法
|
||||
* @returns flattened data
|
||||
*/
|
||||
export default function useFlattenRecords<T>(data: T[] | readonly T[], childrenColumnName: string, expandedKeys: Set<Key>, getRowKey: GetRowKey<T>): FlattenData<T>[];
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import * as React from 'react';
|
||||
// recursion (flat tree structure)
|
||||
function fillRecords(list, record, indent, childrenColumnName, expandedKeys, getRowKey, index) {
|
||||
const key = getRowKey(record, index);
|
||||
list.push({
|
||||
record,
|
||||
indent,
|
||||
index,
|
||||
rowKey: key
|
||||
});
|
||||
const expanded = expandedKeys?.has(key);
|
||||
if (record && Array.isArray(record[childrenColumnName]) && expanded) {
|
||||
// expanded state, flat record
|
||||
for (let i = 0; i < record[childrenColumnName].length; i += 1) {
|
||||
fillRecords(list, record[childrenColumnName][i], indent + 1, childrenColumnName, expandedKeys, getRowKey, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* flat tree data on expanded state
|
||||
*
|
||||
* @export
|
||||
* @template T
|
||||
* @param {*} data : table data
|
||||
* @param {string} childrenColumnName : 指定树形结构的列名
|
||||
* @param {Set<Key>} expandedKeys : 展开的行对应的keys
|
||||
* @param {GetRowKey<T>} getRowKey : 获取当前rowKey的方法
|
||||
* @returns flattened data
|
||||
*/
|
||||
export default function useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey) {
|
||||
const arr = React.useMemo(() => {
|
||||
if (expandedKeys?.size) {
|
||||
const list = [];
|
||||
|
||||
// collect flattened record
|
||||
for (let i = 0; i < data?.length; i += 1) {
|
||||
const record = data[i];
|
||||
|
||||
// using array.push or spread operator may cause "Maximum call stack size exceeded" exception if array size is big enough.
|
||||
fillRecords(list, record, 0, childrenColumnName, expandedKeys, getRowKey, i);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return data?.map((item, index) => {
|
||||
return {
|
||||
record: item,
|
||||
indent: 0,
|
||||
index,
|
||||
rowKey: getRowKey(item, index)
|
||||
};
|
||||
});
|
||||
}, [data, childrenColumnName, expandedKeys, getRowKey]);
|
||||
return arr;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export type Updater<State> = (prev: State) => State;
|
||||
/**
|
||||
* Execute code before next frame but async
|
||||
*/
|
||||
export declare function useLayoutState<State>(defaultState: State): [State, (updater: Updater<State>) => void];
|
||||
/** Lock frame, when frame pass reset the lock. */
|
||||
export declare function useTimeoutLock<State>(defaultState?: State): [(state: State) => void, () => State | null];
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
/**
|
||||
* Execute code before next frame but async
|
||||
*/
|
||||
export function useLayoutState(defaultState) {
|
||||
const stateRef = useRef(defaultState);
|
||||
const [, forceUpdate] = useState({});
|
||||
const lastPromiseRef = useRef(null);
|
||||
const updateBatchRef = useRef([]);
|
||||
function setFrameState(updater) {
|
||||
updateBatchRef.current.push(updater);
|
||||
const promise = Promise.resolve();
|
||||
lastPromiseRef.current = promise;
|
||||
promise.then(() => {
|
||||
if (lastPromiseRef.current === promise) {
|
||||
const prevBatch = updateBatchRef.current;
|
||||
const prevState = stateRef.current;
|
||||
updateBatchRef.current = [];
|
||||
prevBatch.forEach(batchUpdater => {
|
||||
stateRef.current = batchUpdater(stateRef.current);
|
||||
});
|
||||
lastPromiseRef.current = null;
|
||||
if (prevState !== stateRef.current) {
|
||||
forceUpdate({});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
useEffect(() => () => {
|
||||
lastPromiseRef.current = null;
|
||||
}, []);
|
||||
return [stateRef.current, setFrameState];
|
||||
}
|
||||
|
||||
/** Lock frame, when frame pass reset the lock. */
|
||||
export function useTimeoutLock(defaultState) {
|
||||
const frameRef = useRef(defaultState || null);
|
||||
const timeoutRef = useRef(null);
|
||||
function cleanUp() {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
function setState(newState) {
|
||||
frameRef.current = newState;
|
||||
cleanUp();
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
frameRef.current = null;
|
||||
timeoutRef.current = undefined;
|
||||
}, 100);
|
||||
}
|
||||
function getState() {
|
||||
return frameRef.current;
|
||||
}
|
||||
useEffect(() => cleanUp, []);
|
||||
return [setState, getState];
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export type OnHover = (start: number, end: number) => void;
|
||||
export default function useHover(): [startRow: number, endRow: number, onHover: OnHover];
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
export default function useHover() {
|
||||
const [startRow, setStartRow] = React.useState(-1);
|
||||
const [endRow, setEndRow] = React.useState(-1);
|
||||
const onHover = React.useCallback((start, end) => {
|
||||
setStartRow(start);
|
||||
setEndRow(end);
|
||||
}, []);
|
||||
return [startRow, endRow, onHover];
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import * as React from 'react';
|
||||
declare function useRenderTimes<T extends object>(props?: T, debug?: string): number;
|
||||
declare const _default: typeof useRenderTimes | (() => void);
|
||||
export default _default;
|
||||
export declare const RenderBlock: React.MemoExoticComponent<() => React.JSX.Element>;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/* istanbul ignore file */
|
||||
import * as React from 'react';
|
||||
function useRenderTimes(props, debug) {
|
||||
// Render times
|
||||
const timesRef = React.useRef(0);
|
||||
timesRef.current += 1;
|
||||
|
||||
// Props changed
|
||||
const propsRef = React.useRef(props);
|
||||
const keys = [];
|
||||
Object.keys(props || {}).map(key => {
|
||||
if (props?.[key] !== propsRef.current?.[key]) {
|
||||
keys.push(key);
|
||||
}
|
||||
});
|
||||
propsRef.current = props;
|
||||
|
||||
// Cache keys since React rerender may cause it lost
|
||||
const keysRef = React.useRef([]);
|
||||
if (keys.length) {
|
||||
keysRef.current = keys;
|
||||
}
|
||||
React.useDebugValue(timesRef.current);
|
||||
React.useDebugValue(keysRef.current.join(', '));
|
||||
if (debug) {
|
||||
console.log(`${debug}:`, timesRef.current, keysRef.current);
|
||||
}
|
||||
return timesRef.current;
|
||||
}
|
||||
export default process.env.NODE_ENV !== 'production' ? useRenderTimes : () => {};
|
||||
export const RenderBlock = /*#__PURE__*/React.memo(() => {
|
||||
const times = useRenderTimes();
|
||||
return /*#__PURE__*/React.createElement("h1", null, "Render Times: ", times);
|
||||
});
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RenderBlock.displayName = 'RenderBlock';
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/// <reference types="react" />
|
||||
import type { TableContextProps } from '../context/TableContext';
|
||||
export default function useRowInfo<RecordType>(record: RecordType, rowKey: React.Key, recordIndex: number, indent: number): Pick<TableContextProps, 'prefixCls' | 'fixedInfoList' | 'flattenColumns' | 'expandableType' | 'expandRowByClick' | 'onTriggerExpand' | 'rowClassName' | 'expandedRowClassName' | 'indentSize' | 'expandIcon' | 'expandedRowRender' | 'expandIconColumnIndex' | 'expandedKeys' | 'childrenColumnName' | 'onRow'> & {
|
||||
columnsKey: React.Key[];
|
||||
nestExpandable: boolean;
|
||||
expanded: boolean;
|
||||
hasNestChildren: boolean;
|
||||
record: RecordType;
|
||||
rowSupportExpand: boolean;
|
||||
expandable: boolean;
|
||||
rowProps: React.HTMLAttributes<any> & React.TdHTMLAttributes<any>;
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import TableContext from "../context/TableContext";
|
||||
import { getColumnsKey } from "../utils/valueUtil";
|
||||
import { useEvent } from '@rc-component/util';
|
||||
import { clsx } from 'clsx';
|
||||
export default function useRowInfo(record, rowKey, recordIndex, indent) {
|
||||
const context = useContext(TableContext, ['prefixCls', 'fixedInfoList', 'flattenColumns', 'expandableType', 'expandRowByClick', 'onTriggerExpand', 'rowClassName', 'expandedRowClassName', 'indentSize', 'expandIcon', 'expandedRowRender', 'expandIconColumnIndex', 'expandedKeys', 'childrenColumnName', 'rowExpandable', 'onRow']);
|
||||
const {
|
||||
flattenColumns,
|
||||
expandableType,
|
||||
expandedKeys,
|
||||
childrenColumnName,
|
||||
onTriggerExpand,
|
||||
rowExpandable,
|
||||
onRow,
|
||||
expandRowByClick,
|
||||
rowClassName
|
||||
} = context;
|
||||
|
||||
// ======================= Expandable =======================
|
||||
// Only when row is not expandable and `children` exist in record
|
||||
const nestExpandable = expandableType === 'nest';
|
||||
const rowSupportExpand = expandableType === 'row' && (!rowExpandable || rowExpandable(record));
|
||||
const mergedExpandable = rowSupportExpand || nestExpandable;
|
||||
const expanded = expandedKeys && expandedKeys.has(rowKey);
|
||||
const hasNestChildren = childrenColumnName && record && record[childrenColumnName];
|
||||
const onInternalTriggerExpand = useEvent(onTriggerExpand);
|
||||
|
||||
// ========================= onRow ==========================
|
||||
const rowProps = onRow?.(record, recordIndex);
|
||||
const onRowClick = rowProps?.onClick;
|
||||
const onClick = (event, ...args) => {
|
||||
if (expandRowByClick && mergedExpandable) {
|
||||
onTriggerExpand(record, event);
|
||||
}
|
||||
onRowClick?.(event, ...args);
|
||||
};
|
||||
|
||||
// ====================== RowClassName ======================
|
||||
let computeRowClassName;
|
||||
if (typeof rowClassName === 'string') {
|
||||
computeRowClassName = rowClassName;
|
||||
} else if (typeof rowClassName === 'function') {
|
||||
computeRowClassName = rowClassName(record, recordIndex, indent);
|
||||
}
|
||||
|
||||
// ========================= Column =========================
|
||||
const columnsKey = getColumnsKey(flattenColumns);
|
||||
return {
|
||||
...context,
|
||||
columnsKey,
|
||||
nestExpandable,
|
||||
expanded,
|
||||
hasNestChildren,
|
||||
record,
|
||||
onTriggerExpand: onInternalTriggerExpand,
|
||||
rowSupportExpand,
|
||||
expandable: mergedExpandable,
|
||||
rowProps: {
|
||||
...rowProps,
|
||||
className: clsx(computeRowClassName, rowProps?.className),
|
||||
onClick
|
||||
}
|
||||
};
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { TableSticky } from '../interface';
|
||||
/** Sticky header hooks */
|
||||
export default function useSticky(sticky: boolean | TableSticky, prefixCls: string): {
|
||||
isSticky: boolean;
|
||||
offsetHeader: number;
|
||||
offsetSummary: number;
|
||||
offsetScroll: number;
|
||||
stickyClassName: string;
|
||||
container: Window | HTMLElement;
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import canUseDom from "@rc-component/util/es/Dom/canUseDom";
|
||||
// fix ssr render
|
||||
const defaultContainer = canUseDom() ? window : null;
|
||||
|
||||
/** Sticky header hooks */
|
||||
export default function useSticky(sticky, prefixCls) {
|
||||
const {
|
||||
offsetHeader = 0,
|
||||
offsetSummary = 0,
|
||||
offsetScroll = 0,
|
||||
getContainer = () => defaultContainer
|
||||
} = typeof sticky === 'object' ? sticky : {};
|
||||
const container = getContainer() || defaultContainer;
|
||||
const isSticky = !!sticky;
|
||||
return React.useMemo(() => {
|
||||
return {
|
||||
isSticky,
|
||||
stickyClassName: isSticky ? `${prefixCls}-sticky-holder` : '',
|
||||
offsetHeader,
|
||||
offsetSummary,
|
||||
offsetScroll,
|
||||
container
|
||||
};
|
||||
}, [isSticky, offsetScroll, offsetHeader, offsetSummary, prefixCls, container]);
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import type { ColumnType, StickyOffsets } from '../interface';
|
||||
/**
|
||||
* Get sticky column offset width
|
||||
*/
|
||||
declare function useStickyOffsets<RecordType>(colWidths: number[], flattenColumns: readonly ColumnType<RecordType>[]): StickyOffsets;
|
||||
export default useStickyOffsets;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { useMemo } from 'react';
|
||||
/**
|
||||
* Get sticky column offset width
|
||||
*/
|
||||
function useStickyOffsets(colWidths, flattenColumns) {
|
||||
const stickyOffsets = useMemo(() => {
|
||||
const columnCount = flattenColumns.length;
|
||||
const getOffsets = (startIndex, endIndex, offset) => {
|
||||
const offsets = [];
|
||||
let total = 0;
|
||||
for (let i = startIndex; i !== endIndex; i += offset) {
|
||||
offsets.push(total);
|
||||
if (flattenColumns[i].fixed) {
|
||||
total += colWidths[i] || 0;
|
||||
}
|
||||
}
|
||||
return offsets;
|
||||
};
|
||||
const startOffsets = getOffsets(0, columnCount, 1);
|
||||
const endOffsets = getOffsets(columnCount - 1, -1, -1).reverse();
|
||||
return {
|
||||
start: startOffsets,
|
||||
end: endOffsets,
|
||||
widths: colWidths
|
||||
};
|
||||
}, [colWidths, flattenColumns]);
|
||||
return stickyOffsets;
|
||||
}
|
||||
export default useStickyOffsets;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { EXPAND_COLUMN, INTERNAL_HOOKS } from './constant';
|
||||
import { FooterComponents as Summary } from './Footer';
|
||||
import type { ColumnType, ColumnsType, Reference } from './interface';
|
||||
import Column from './sugar/Column';
|
||||
import ColumnGroup from './sugar/ColumnGroup';
|
||||
import type { TableProps } from './Table';
|
||||
import Table, { genTable } from './Table';
|
||||
import { INTERNAL_COL_DEFINE } from './utils/legacyUtil';
|
||||
import type { VirtualTableProps } from './VirtualTable';
|
||||
import VirtualTable, { genVirtualTable } from './VirtualTable';
|
||||
export { genTable, Summary, Column, ColumnGroup, type TableProps, INTERNAL_COL_DEFINE, EXPAND_COLUMN, INTERNAL_HOOKS, VirtualTable, genVirtualTable, type VirtualTableProps, type Reference, type ColumnType, type ColumnsType, };
|
||||
export default Table;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { EXPAND_COLUMN, INTERNAL_HOOKS } from "./constant";
|
||||
import { FooterComponents as Summary } from "./Footer";
|
||||
import Column from "./sugar/Column";
|
||||
import ColumnGroup from "./sugar/ColumnGroup";
|
||||
import Table, { genTable } from "./Table";
|
||||
import { INTERNAL_COL_DEFINE } from "./utils/legacyUtil";
|
||||
import VirtualTable, { genVirtualTable } from "./VirtualTable";
|
||||
export { genTable, Summary, Column, ColumnGroup, INTERNAL_COL_DEFINE, EXPAND_COLUMN, INTERNAL_HOOKS, VirtualTable, genVirtualTable };
|
||||
export default Table;
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
import type * as React from 'react';
|
||||
import type { DeepNamePath } from './namePathType';
|
||||
/**
|
||||
* ColumnType which applied in antd: https://ant.design/components/table-cn/#Column
|
||||
* - defaultSortOrder
|
||||
* - filterDropdown
|
||||
* - filterDropdownVisible
|
||||
* - filtered
|
||||
* - filteredValue
|
||||
* - filterIcon
|
||||
* - filterMultiple
|
||||
* - filters
|
||||
* - sorter
|
||||
* - sortOrder
|
||||
* - sortDirections
|
||||
* - onFilter
|
||||
* - onFilterDropdownVisibleChange
|
||||
*/
|
||||
export type Key = React.Key;
|
||||
/**
|
||||
* Use `start` or `end` instead. `left` or `right` is deprecated.
|
||||
*/
|
||||
export type FixedType = 'start' | 'end' | 'left' | 'right' | boolean;
|
||||
export type DefaultRecordType = Record<string, any>;
|
||||
export type TableLayout = 'auto' | 'fixed';
|
||||
export type ScrollConfig = {
|
||||
/** The index of the row to scroll to */
|
||||
index?: number;
|
||||
/** The key of the row to scroll to */
|
||||
key?: Key;
|
||||
/** The absolute scroll position from top */
|
||||
top?: number;
|
||||
/**
|
||||
* Additional offset in pixels to apply to the scroll position.
|
||||
* Only effective when using `key` or `index` mode.
|
||||
* Ignored when using `top` mode.
|
||||
* When offset is set, the target element will always be aligned to the top of the container.
|
||||
*/
|
||||
offset?: number;
|
||||
};
|
||||
export type Reference = {
|
||||
nativeElement: HTMLDivElement;
|
||||
scrollTo: (config: ScrollConfig) => void;
|
||||
};
|
||||
export type RowClassName<RecordType> = (record: RecordType, index: number, indent: number) => string;
|
||||
export interface CellType<RecordType> {
|
||||
key?: Key;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
column?: ColumnsType<RecordType>[number];
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
/** Only used for table header */
|
||||
hasSubColumns?: boolean;
|
||||
colStart?: number;
|
||||
colEnd?: number;
|
||||
}
|
||||
export interface RenderedCell<RecordType> {
|
||||
props?: CellType<RecordType>;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export type Direction = 'ltr' | 'rtl';
|
||||
export type SpecialString<T> = T | (string & NonNullable<unknown>);
|
||||
export type DataIndex<T = any> = DeepNamePath<T> | SpecialString<T> | number | (SpecialString<T> | number)[];
|
||||
export type CellEllipsisType = {
|
||||
showTitle?: boolean;
|
||||
} | boolean;
|
||||
export type ColScopeType = 'col' | 'colgroup';
|
||||
export type RowScopeType = 'row' | 'rowgroup';
|
||||
export type ScopeType = ColScopeType | RowScopeType;
|
||||
interface ColumnSharedType<RecordType> {
|
||||
title?: React.ReactNode;
|
||||
key?: Key;
|
||||
className?: string;
|
||||
hidden?: boolean;
|
||||
fixed?: FixedType;
|
||||
onHeaderCell?: GetComponentProps<ColumnsType<RecordType>[number]>;
|
||||
ellipsis?: CellEllipsisType;
|
||||
align?: AlignType;
|
||||
rowScope?: RowScopeType;
|
||||
}
|
||||
export interface ColumnGroupType<RecordType> extends ColumnSharedType<RecordType> {
|
||||
children: ColumnsType<RecordType>;
|
||||
}
|
||||
export type AlignType = 'start' | 'end' | 'left' | 'right' | 'center' | 'justify' | 'match-parent';
|
||||
export interface ColumnType<RecordType> extends ColumnSharedType<RecordType> {
|
||||
colSpan?: number;
|
||||
dataIndex?: DataIndex<RecordType>;
|
||||
render?: (value: any, record: RecordType, index: number) => React.ReactNode | RenderedCell<RecordType>;
|
||||
shouldCellUpdate?: (record: RecordType, prevRecord: RecordType) => boolean;
|
||||
rowSpan?: number;
|
||||
width?: number | string;
|
||||
minWidth?: number;
|
||||
onCell?: GetComponentProps<RecordType>;
|
||||
/** @deprecated Please use `onCell` instead */
|
||||
onCellClick?: (record: RecordType, e: React.MouseEvent<HTMLElement>) => void;
|
||||
}
|
||||
export type ColumnsType<RecordType = unknown> = readonly (ColumnGroupType<RecordType> | ColumnType<RecordType>)[];
|
||||
export type GetRowKey<RecordType> = (record: RecordType, index?: number) => Key;
|
||||
export interface StickyOffsets {
|
||||
start: readonly number[];
|
||||
end: readonly number[];
|
||||
widths: readonly number[];
|
||||
isSticky?: boolean;
|
||||
}
|
||||
export type GetComponentProps<DataType> = (data: DataType, index?: number) => React.HTMLAttributes<any> & React.TdHTMLAttributes<any>;
|
||||
type Component<P> = React.ComponentType<P> | React.ForwardRefExoticComponent<P> | React.FC<P> | keyof React.JSX.IntrinsicElements;
|
||||
export type CustomizeComponent = Component<any>;
|
||||
export type OnCustomizeScroll = (info: {
|
||||
currentTarget?: HTMLElement;
|
||||
scrollLeft?: number;
|
||||
}) => void;
|
||||
export type CustomizeScrollBody<RecordType> = (data: readonly RecordType[], info: {
|
||||
scrollbarSize: number;
|
||||
ref: React.Ref<{
|
||||
scrollLeft: number;
|
||||
scrollTo?: (scrollConfig: ScrollConfig) => void;
|
||||
}>;
|
||||
onScroll: OnCustomizeScroll;
|
||||
}) => React.ReactNode;
|
||||
export interface TableComponents<RecordType> {
|
||||
table?: CustomizeComponent;
|
||||
header?: {
|
||||
table?: CustomizeComponent;
|
||||
wrapper?: CustomizeComponent;
|
||||
row?: CustomizeComponent;
|
||||
cell?: CustomizeComponent;
|
||||
};
|
||||
body?: CustomizeScrollBody<RecordType> | {
|
||||
wrapper?: CustomizeComponent;
|
||||
row?: CustomizeComponent;
|
||||
cell?: CustomizeComponent;
|
||||
};
|
||||
}
|
||||
export type GetComponent = (path: readonly string[], defaultComponent?: CustomizeComponent) => CustomizeComponent;
|
||||
export type ExpandableType = false | 'row' | 'nest';
|
||||
export interface LegacyExpandableProps<RecordType> {
|
||||
/** @deprecated Use `expandable.expandedRowKeys` instead */
|
||||
expandedRowKeys?: Key[];
|
||||
/** @deprecated Use `expandable.defaultExpandedRowKeys` instead */
|
||||
defaultExpandedRowKeys?: Key[];
|
||||
/** @deprecated Use `expandable.expandedRowRender` instead */
|
||||
expandedRowRender?: ExpandedRowRender<RecordType>;
|
||||
/** @deprecated Use `expandable.expandRowByClick` instead */
|
||||
expandRowByClick?: boolean;
|
||||
/** @deprecated Use `expandable.expandIcon` instead */
|
||||
expandIcon?: RenderExpandIcon<RecordType>;
|
||||
/** @deprecated Use `expandable.onExpand` instead */
|
||||
onExpand?: (expanded: boolean, record: RecordType) => void;
|
||||
/** @deprecated Use `expandable.onExpandedRowsChange` instead */
|
||||
onExpandedRowsChange?: (expandedKeys: Key[]) => void;
|
||||
/** @deprecated Use `expandable.defaultExpandAllRows` instead */
|
||||
defaultExpandAllRows?: boolean;
|
||||
/** @deprecated Use `expandable.indentSize` instead */
|
||||
indentSize?: number;
|
||||
/** @deprecated Use `expandable.expandIconColumnIndex` instead */
|
||||
expandIconColumnIndex?: number;
|
||||
/** @deprecated Use `expandable.expandedRowClassName` instead */
|
||||
expandedRowClassName?: RowClassName<RecordType>;
|
||||
/** @deprecated Use `expandable.childrenColumnName` instead */
|
||||
childrenColumnName?: string;
|
||||
title?: PanelRender<RecordType>;
|
||||
}
|
||||
export type ExpandedRowRender<ValueType> = (record: ValueType, index: number, indent: number, expanded: boolean) => React.ReactNode;
|
||||
export interface RenderExpandIconProps<RecordType> {
|
||||
prefixCls: string;
|
||||
expanded: boolean;
|
||||
record: RecordType;
|
||||
expandable: boolean;
|
||||
onExpand: TriggerEventHandler<RecordType>;
|
||||
}
|
||||
export type RenderExpandIcon<RecordType> = (props: RenderExpandIconProps<RecordType>) => React.ReactNode;
|
||||
export interface ExpandableConfig<RecordType> {
|
||||
expandedRowKeys?: readonly Key[];
|
||||
defaultExpandedRowKeys?: readonly Key[];
|
||||
expandedRowRender?: ExpandedRowRender<RecordType>;
|
||||
columnTitle?: React.ReactNode;
|
||||
expandRowByClick?: boolean;
|
||||
expandIcon?: RenderExpandIcon<RecordType>;
|
||||
onExpand?: (expanded: boolean, record: RecordType) => void;
|
||||
onExpandedRowsChange?: (expandedKeys: readonly Key[]) => void;
|
||||
defaultExpandAllRows?: boolean;
|
||||
indentSize?: number;
|
||||
/** @deprecated Please use `EXPAND_COLUMN` in `columns` directly */
|
||||
expandIconColumnIndex?: number;
|
||||
showExpandColumn?: boolean;
|
||||
expandedRowClassName?: string | RowClassName<RecordType>;
|
||||
childrenColumnName?: string;
|
||||
rowExpandable?: (record: RecordType) => boolean;
|
||||
columnWidth?: number | string;
|
||||
fixed?: FixedType;
|
||||
expandedRowOffset?: number;
|
||||
}
|
||||
export type PanelRender<RecordType> = (data: readonly RecordType[]) => React.ReactNode;
|
||||
export type TriggerEventHandler<RecordType> = (record: RecordType, event: React.MouseEvent<HTMLElement>) => void;
|
||||
export interface TableSticky {
|
||||
offsetHeader?: number;
|
||||
offsetSummary?: number;
|
||||
offsetScroll?: number;
|
||||
getContainer?: () => Window | HTMLElement;
|
||||
}
|
||||
export {};
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
type BaseNamePath = string | number | boolean | (string | number | boolean)[];
|
||||
/**
|
||||
* Store: The store type from `FormInstance<Store>`
|
||||
* ParentNamePath: Auto generate by nest logic. Do not fill manually.
|
||||
*/
|
||||
export type DeepNamePath<Store = any, ParentNamePath extends any[] = []> = ParentNamePath['length'] extends 3 ? never : true extends (Store extends BaseNamePath ? true : false) ? ParentNamePath['length'] extends 0 ? Store | BaseNamePath : Store extends any[] ? [...ParentNamePath, number] : never : Store extends any[] ? // Connect path. e.g. { a: { b: string }[] }
|
||||
[
|
||||
...ParentNamePath,
|
||||
number
|
||||
] | DeepNamePath<Store[number], [...ParentNamePath, number]> : keyof Store extends never ? Store : {
|
||||
[FieldKey in keyof Store]: Store[FieldKey] extends Function ? never : (ParentNamePath['length'] extends 0 ? FieldKey : never) | [...ParentNamePath, FieldKey] | DeepNamePath<Required<Store>[FieldKey], [...ParentNamePath, FieldKey]>;
|
||||
}[keyof Store];
|
||||
export {};
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import * as React from 'react';
|
||||
interface StickyScrollBarProps {
|
||||
scrollBodyRef: React.RefObject<HTMLDivElement>;
|
||||
onScroll: (params: {
|
||||
scrollLeft?: number;
|
||||
}) => void;
|
||||
offsetScroll: number;
|
||||
container: HTMLElement | Window;
|
||||
direction: string;
|
||||
}
|
||||
declare const _default: React.ForwardRefExoticComponent<StickyScrollBarProps & React.RefAttributes<unknown>>;
|
||||
export default _default;
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
import { useContext } from '@rc-component/context';
|
||||
import { clsx } from 'clsx';
|
||||
import getScrollBarSize from "@rc-component/util/es/getScrollBarSize";
|
||||
import * as React from 'react';
|
||||
import TableContext from "./context/TableContext";
|
||||
import { useLayoutState } from "./hooks/useFrame";
|
||||
import raf from "@rc-component/util/es/raf";
|
||||
import { getOffset } from "./utils/offsetUtil";
|
||||
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
|
||||
const MOUSEUP_EVENT = 'mouseup';
|
||||
const MOUSEMOVE_EVENT = 'mousemove';
|
||||
const SCROLL_EVENT = 'scroll';
|
||||
const RESIZE_EVENT = 'resize';
|
||||
const StickyScrollBar = (props, ref) => {
|
||||
const {
|
||||
scrollBodyRef,
|
||||
onScroll,
|
||||
offsetScroll,
|
||||
container,
|
||||
direction
|
||||
} = props;
|
||||
const prefixCls = useContext(TableContext, 'prefixCls');
|
||||
const bodyScrollWidth = scrollBodyRef.current?.scrollWidth || 0;
|
||||
const bodyWidth = scrollBodyRef.current?.clientWidth || 0;
|
||||
const scrollBarWidth = bodyScrollWidth && bodyWidth * (bodyWidth / bodyScrollWidth);
|
||||
const scrollBarRef = React.useRef(null);
|
||||
const [scrollState, setScrollState] = useLayoutState({
|
||||
scrollLeft: 0,
|
||||
isHiddenScrollBar: true
|
||||
});
|
||||
const refState = React.useRef({
|
||||
delta: 0,
|
||||
x: 0
|
||||
});
|
||||
const [isActive, setActive] = React.useState(false);
|
||||
const rafRef = React.useRef(null);
|
||||
React.useEffect(() => () => {
|
||||
raf.cancel(rafRef.current);
|
||||
}, []);
|
||||
const onMouseUp = () => {
|
||||
setActive(false);
|
||||
};
|
||||
const onMouseDown = event => {
|
||||
event.persist();
|
||||
refState.current.delta = event.pageX - scrollState.scrollLeft;
|
||||
refState.current.x = 0;
|
||||
setActive(true);
|
||||
event.preventDefault();
|
||||
};
|
||||
const onMouseMove = event => {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
|
||||
const {
|
||||
buttons
|
||||
} = event || window?.event;
|
||||
if (!isActive || buttons === 0) {
|
||||
// If out body mouse up, we can set isActive false when mouse move
|
||||
if (isActive) {
|
||||
setActive(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let left = refState.current.x + event.pageX - refState.current.x - refState.current.delta;
|
||||
const isRTL = direction === 'rtl';
|
||||
// Limit scroll range
|
||||
left = Math.max(isRTL ? scrollBarWidth - bodyWidth : 0, Math.min(isRTL ? 0 : bodyWidth - scrollBarWidth, left));
|
||||
// Calculate the scroll position and update
|
||||
const shouldScroll = !isRTL || Math.abs(left) + Math.abs(scrollBarWidth) < bodyWidth;
|
||||
if (shouldScroll) {
|
||||
onScroll({
|
||||
scrollLeft: left / bodyWidth * (bodyScrollWidth + 2)
|
||||
});
|
||||
refState.current.x = event.pageX;
|
||||
}
|
||||
};
|
||||
const checkScrollBarVisible = () => {
|
||||
raf.cancel(rafRef.current);
|
||||
rafRef.current = raf(() => {
|
||||
if (!scrollBodyRef.current) {
|
||||
return;
|
||||
}
|
||||
const tableOffsetTop = getOffset(scrollBodyRef.current).top;
|
||||
const tableBottomOffset = tableOffsetTop + scrollBodyRef.current.offsetHeight;
|
||||
const currentClientOffset = container === window ? document.documentElement.scrollTop + window.innerHeight : getOffset(container).top + container.clientHeight;
|
||||
if (tableBottomOffset - getScrollBarSize() <= currentClientOffset || tableOffsetTop >= currentClientOffset - offsetScroll) {
|
||||
setScrollState(state => ({
|
||||
...state,
|
||||
isHiddenScrollBar: true
|
||||
}));
|
||||
} else {
|
||||
setScrollState(state => ({
|
||||
...state,
|
||||
isHiddenScrollBar: false
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
const setScrollLeft = left => {
|
||||
setScrollState(state => {
|
||||
return {
|
||||
...state,
|
||||
scrollLeft: left / bodyScrollWidth * bodyWidth || 0
|
||||
};
|
||||
});
|
||||
};
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
setScrollLeft,
|
||||
checkScrollBarVisible
|
||||
}));
|
||||
React.useEffect(() => {
|
||||
document.body.addEventListener(MOUSEUP_EVENT, onMouseUp, false);
|
||||
document.body.addEventListener(MOUSEMOVE_EVENT, onMouseMove, false);
|
||||
checkScrollBarVisible();
|
||||
return () => {
|
||||
document.body.removeEventListener(MOUSEUP_EVENT, onMouseUp);
|
||||
document.body.removeEventListener(MOUSEMOVE_EVENT, onMouseMove);
|
||||
};
|
||||
}, [scrollBarWidth, isActive]);
|
||||
|
||||
// Loop for scroll event check
|
||||
React.useEffect(() => {
|
||||
if (scrollBodyRef.current) {
|
||||
const scrollParents = [];
|
||||
let parent = getDOM(scrollBodyRef.current);
|
||||
while (parent) {
|
||||
scrollParents.push(parent);
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
scrollParents.forEach(p => {
|
||||
p.addEventListener(SCROLL_EVENT, checkScrollBarVisible, false);
|
||||
});
|
||||
window.addEventListener(RESIZE_EVENT, checkScrollBarVisible, false);
|
||||
window.addEventListener(SCROLL_EVENT, checkScrollBarVisible, false);
|
||||
container.addEventListener(SCROLL_EVENT, checkScrollBarVisible, false);
|
||||
return () => {
|
||||
scrollParents.forEach(p => {
|
||||
p.removeEventListener(SCROLL_EVENT, checkScrollBarVisible);
|
||||
});
|
||||
window.removeEventListener(RESIZE_EVENT, checkScrollBarVisible);
|
||||
window.removeEventListener(SCROLL_EVENT, checkScrollBarVisible);
|
||||
container.removeEventListener(SCROLL_EVENT, checkScrollBarVisible);
|
||||
};
|
||||
}
|
||||
}, [container]);
|
||||
React.useEffect(() => {
|
||||
if (!scrollState.isHiddenScrollBar) {
|
||||
setScrollState(state => {
|
||||
const bodyNode = scrollBodyRef.current;
|
||||
if (!bodyNode) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
scrollLeft: bodyNode.scrollLeft / bodyNode.scrollWidth * bodyNode.clientWidth
|
||||
};
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [scrollState.isHiddenScrollBar]);
|
||||
if (bodyScrollWidth <= bodyWidth || !scrollBarWidth || scrollState.isHiddenScrollBar) {
|
||||
return null;
|
||||
}
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
style: {
|
||||
height: getScrollBarSize(),
|
||||
width: bodyWidth,
|
||||
bottom: offsetScroll
|
||||
},
|
||||
className: `${prefixCls}-sticky-scroll`
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
onMouseDown: onMouseDown,
|
||||
ref: scrollBarRef,
|
||||
className: clsx(`${prefixCls}-sticky-scroll-bar`, {
|
||||
[`${prefixCls}-sticky-scroll-bar-active`]: isActive
|
||||
}),
|
||||
style: {
|
||||
width: `${scrollBarWidth}px`,
|
||||
transform: `translate3d(${scrollState.scrollLeft}px, 0, 0)`
|
||||
}
|
||||
}));
|
||||
};
|
||||
export default /*#__PURE__*/React.forwardRef(StickyScrollBar);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { ColumnType } from '../interface';
|
||||
export interface ColumnProps<RecordType> extends ColumnType<RecordType> {
|
||||
children?: null;
|
||||
}
|
||||
/**
|
||||
* This is a syntactic sugar for `columns` prop.
|
||||
* So HOC will not work on this.
|
||||
*/
|
||||
declare function Column<RecordType>(_: ColumnProps<RecordType>): any;
|
||||
export default Column;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/* istanbul ignore next */
|
||||
/**
|
||||
* This is a syntactic sugar for `columns` prop.
|
||||
* So HOC will not work on this.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function Column(_) {
|
||||
return null;
|
||||
}
|
||||
export default Column;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import type * as React from 'react';
|
||||
import type { ColumnProps } from './Column';
|
||||
import type { ColumnType } from '../interface';
|
||||
export interface ColumnGroupProps<RecordType> extends Omit<ColumnType<RecordType>, 'children'> {
|
||||
children: React.ReactElement<ColumnProps<RecordType>> | readonly React.ReactElement<ColumnProps<RecordType>>[];
|
||||
}
|
||||
/**
|
||||
* This is a syntactic sugar for `columns` prop.
|
||||
* So HOC will not work on this.
|
||||
*/
|
||||
declare function ColumnGroup<RecordType>(_: ColumnGroupProps<RecordType>): any;
|
||||
export default ColumnGroup;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/* istanbul ignore next */
|
||||
/**
|
||||
* This is a syntactic sugar for `columns` prop.
|
||||
* So HOC will not work on this.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function ColumnGroup(_) {
|
||||
return null;
|
||||
}
|
||||
export default ColumnGroup;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import type { RenderExpandIconProps, Key, GetRowKey, ExpandableConfig } from '../interface';
|
||||
export declare function renderExpandIcon<RecordType>({ prefixCls, record, onExpand, expanded, expandable, }: RenderExpandIconProps<RecordType>): React.JSX.Element;
|
||||
export declare function findAllChildrenKeys<RecordType>(data: readonly RecordType[], getRowKey: GetRowKey<RecordType>, childrenColumnName: string): Key[];
|
||||
export declare function computedExpandedClassName<RecordType>(cls: ExpandableConfig<RecordType>['expandedRowClassName'], record: RecordType, index: number, indent: number): string;
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import * as React from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
export function renderExpandIcon({
|
||||
prefixCls,
|
||||
record,
|
||||
onExpand,
|
||||
expanded,
|
||||
expandable
|
||||
}) {
|
||||
const expandClassName = `${prefixCls}-row-expand-icon`;
|
||||
if (!expandable) {
|
||||
return /*#__PURE__*/React.createElement("span", {
|
||||
className: clsx(expandClassName, `${prefixCls}-row-spaced`)
|
||||
});
|
||||
}
|
||||
const onClick = event => {
|
||||
onExpand(record, event);
|
||||
event.stopPropagation();
|
||||
};
|
||||
return /*#__PURE__*/React.createElement("span", {
|
||||
className: clsx(expandClassName, {
|
||||
[`${prefixCls}-row-expanded`]: expanded,
|
||||
[`${prefixCls}-row-collapsed`]: !expanded
|
||||
}),
|
||||
onClick: onClick
|
||||
});
|
||||
}
|
||||
export function findAllChildrenKeys(data, getRowKey, childrenColumnName) {
|
||||
const keys = [];
|
||||
function dig(list) {
|
||||
(list || []).forEach((item, index) => {
|
||||
keys.push(getRowKey(item, index));
|
||||
dig(item[childrenColumnName]);
|
||||
});
|
||||
}
|
||||
dig(data);
|
||||
return keys;
|
||||
}
|
||||
export function computedExpandedClassName(cls, record, index, indent) {
|
||||
if (typeof cls === 'string') {
|
||||
return cls;
|
||||
}
|
||||
if (typeof cls === 'function') {
|
||||
return cls(record, index, indent);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import type { FixedType, StickyOffsets } from '../interface';
|
||||
export interface FixedInfo {
|
||||
fixStart: number | false;
|
||||
fixEnd: number | false;
|
||||
isSticky: boolean;
|
||||
/** `fixed: start` with shadow */
|
||||
fixedStartShadow?: boolean;
|
||||
/** `fixed: end` with shadow */
|
||||
fixedEndShadow?: boolean;
|
||||
/** Show the shadow when `scrollLeft` arrive for `fixed: start` */
|
||||
offsetFixedStartShadow?: number;
|
||||
/** Show the shadow when `scrollLeft` arrive for `fixed: end` */
|
||||
offsetFixedEndShadow?: number;
|
||||
/** First sticky column `zIndex` will be larger than next */
|
||||
zIndex?: number;
|
||||
/** First sticky column `zIndex` will be smaller than next */
|
||||
zIndexReverse?: number;
|
||||
}
|
||||
export declare function getCellFixedInfo(colStart: number, colEnd: number, columns: readonly {
|
||||
fixed?: FixedType;
|
||||
}[], stickyOffsets: StickyOffsets): FixedInfo;
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
function isFixedStart(column) {
|
||||
return column.fixed === 'start';
|
||||
}
|
||||
function isFixedEnd(column) {
|
||||
return column.fixed === 'end';
|
||||
}
|
||||
export function getCellFixedInfo(colStart, colEnd, columns, stickyOffsets) {
|
||||
const startColumn = columns[colStart] || {};
|
||||
const endColumn = columns[colEnd] || {};
|
||||
let fixStart = null;
|
||||
let fixEnd = null;
|
||||
if (isFixedStart(startColumn) && isFixedStart(endColumn)) {
|
||||
fixStart = stickyOffsets.start[colStart];
|
||||
} else if (isFixedEnd(endColumn) && isFixedEnd(startColumn)) {
|
||||
fixEnd = stickyOffsets.end[colEnd];
|
||||
}
|
||||
|
||||
// check if need to add shadow
|
||||
let fixedStartShadow = false;
|
||||
let fixedEndShadow = false;
|
||||
|
||||
// Calc `zIndex`.
|
||||
// first fixed start (start -> end) column `zIndex` should be greater than next column.
|
||||
// first fixed end (end -> start) column `zIndex` should be greater than next column.
|
||||
let zIndex = 0;
|
||||
let zIndexReverse = 0;
|
||||
if (fixStart !== null) {
|
||||
fixedStartShadow = !columns[colEnd + 1] || !isFixedStart(columns[colEnd + 1]);
|
||||
zIndex = columns.length * 2 - colStart; // Fix start always overlay fix end
|
||||
zIndexReverse = columns.length + colStart;
|
||||
}
|
||||
if (fixEnd !== null) {
|
||||
fixedEndShadow = !columns[colStart - 1] || !isFixedEnd(columns[colStart - 1]);
|
||||
zIndex = colEnd;
|
||||
zIndexReverse = columns.length - colEnd; // Fix end always overlay fix start
|
||||
}
|
||||
|
||||
// Check if scrollLeft will show the shadow
|
||||
let offsetFixedStartShadow = 0;
|
||||
let offsetFixedEndShadow = 0;
|
||||
if (fixedStartShadow) {
|
||||
for (let i = 0; i < colStart; i += 1) {
|
||||
if (!isFixedStart(columns[i])) {
|
||||
offsetFixedStartShadow += stickyOffsets.widths[i] || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fixedEndShadow) {
|
||||
for (let i = columns.length - 1; i > colEnd; i -= 1) {
|
||||
if (!isFixedEnd(columns[i])) {
|
||||
offsetFixedEndShadow += stickyOffsets.widths[i] || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
fixStart,
|
||||
fixEnd,
|
||||
fixedStartShadow,
|
||||
fixedEndShadow,
|
||||
offsetFixedStartShadow,
|
||||
offsetFixedEndShadow,
|
||||
isSticky: stickyOffsets.isSticky,
|
||||
zIndex,
|
||||
zIndexReverse
|
||||
};
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import type { ExpandableConfig, LegacyExpandableProps } from '../interface';
|
||||
export declare const INTERNAL_COL_DEFINE = "RC_TABLE_INTERNAL_COL_DEFINE";
|
||||
export declare function getExpandableProps<RecordType>(props: LegacyExpandableProps<RecordType> & {
|
||||
expandable?: ExpandableConfig<RecordType>;
|
||||
}): ExpandableConfig<RecordType>;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import warning from "@rc-component/util/es/warning";
|
||||
export const INTERNAL_COL_DEFINE = 'RC_TABLE_INTERNAL_COL_DEFINE';
|
||||
export function getExpandableProps(props) {
|
||||
const {
|
||||
expandable,
|
||||
...legacyExpandableConfig
|
||||
} = props;
|
||||
let config;
|
||||
if ('expandable' in props) {
|
||||
config = {
|
||||
...legacyExpandableConfig,
|
||||
...expandable
|
||||
};
|
||||
} else {
|
||||
if (process.env.NODE_ENV !== 'production' && ['indentSize', 'expandedRowKeys', 'defaultExpandedRowKeys', 'defaultExpandAllRows', 'expandedRowRender', 'expandRowByClick', 'expandIcon', 'onExpand', 'onExpandedRowsChange', 'expandedRowClassName', 'expandIconColumnIndex', 'showExpandColumn', 'title'].some(prop => prop in props)) {
|
||||
warning(false, 'expanded related props have been moved into `expandable`.');
|
||||
}
|
||||
config = legacyExpandableConfig;
|
||||
}
|
||||
if (config.showExpandColumn === false) {
|
||||
config.expandIconColumnIndex = -1;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export declare function getOffset(node: HTMLElement | Window): {
|
||||
left: number;
|
||||
top: number;
|
||||
};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
|
||||
|
||||
// Copy from `rc-component/util/Dom/css.js`
|
||||
export function getOffset(node) {
|
||||
const element = getDOM(node);
|
||||
const box = element.getBoundingClientRect();
|
||||
const docElem = document.documentElement;
|
||||
|
||||
// < ie8 not support win.pageXOffset, use docElem.scrollLeft instead
|
||||
return {
|
||||
left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || document.body.clientLeft || 0),
|
||||
top: box.top + (window.pageYOffset || docElem.scrollTop) - (docElem.clientTop || document.body.clientTop || 0)
|
||||
};
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/// <reference types="react" />
|
||||
import type { DataIndex, Key } from '../interface';
|
||||
export interface GetColumnKeyColumn<T = any> {
|
||||
key?: Key;
|
||||
dataIndex?: DataIndex<T>;
|
||||
}
|
||||
export declare function getColumnsKey<T = any>(columns: readonly GetColumnKeyColumn<T>[]): import("react").Key[];
|
||||
export declare function validateValue<T>(val: T): boolean;
|
||||
export declare function validNumberValue(value: any): boolean;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user