refactor: table

pull/4639/head
tangjinzhou 2021-09-04 17:11:18 +08:00
parent 60ea53ce91
commit a948e663a9
13 changed files with 199 additions and 128 deletions

View File

@ -142,26 +142,6 @@ export default defineComponent<BodyRowProps<unknown>>({
const key = columnsKey[colIndex]; const key = columnsKey[colIndex];
const fixedInfo = fixedInfoList[colIndex]; const fixedInfo = fixedInfoList[colIndex];
// ============= Used for nest expandable =============
let appendCellNode;
if (colIndex === (expandIconColumnIndex || 0) && nestExpandable.value) {
appendCellNode = (
<>
<span
style={{ paddingLeft: `${indentSize * indent}px` }}
class={`${prefixCls}-row-indent indent-level-${indent}`}
/>
{expandIcon({
prefixCls,
expanded: expanded.value,
expandable: hasNestChildren.value,
record,
onExpand: onInternalTriggerExpand,
})}
</>
);
}
let additionalCellProps; let additionalCellProps;
if (column.customCell) { if (column.customCell) {
additionalCellProps = column.customCell(record, index); additionalCellProps = column.customCell(record, index);
@ -180,8 +160,30 @@ export default defineComponent<BodyRowProps<unknown>>({
dataIndex={dataIndex} dataIndex={dataIndex}
customRender={customRender} customRender={customRender}
{...fixedInfo} {...fixedInfo}
appendNode={appendCellNode}
additionalProps={additionalCellProps} additionalProps={additionalCellProps}
v-slots={{
// ============= Used for nest expandable =============
appendNode: () => {
if (colIndex === (expandIconColumnIndex || 0) && nestExpandable.value) {
return (
<>
<span
style={{ paddingLeft: `${indentSize * indent}px` }}
class={`${prefixCls}-row-indent indent-level-${indent}`}
/>
{expandIcon({
prefixCls,
expanded: expanded.value,
expandable: hasNestChildren.value,
record,
onExpand: onInternalTriggerExpand,
})}
</>
);
}
return null;
},
}}
/> />
); );
})} })}

View File

@ -43,6 +43,19 @@ export default defineComponent<ExpandedRowProps>({
colSpan, colSpan,
} = props; } = props;
return (
<Component
class={attrs.class}
style={{
display: expanded ? null : 'none',
}}
>
<Cell
component={cellComponent}
prefixCls={prefixCls}
colSpan={colSpan}
v-slots={{
default: () => {
let contentNode: any = slots.default?.(); let contentNode: any = slots.default?.();
if (fixColumn) { if (fixColumn) {
@ -60,17 +73,10 @@ export default defineComponent<ExpandedRowProps>({
</div> </div>
); );
} }
return contentNode;
return ( },
<Component
class={attrs.class}
style={{
display: expanded ? null : 'none',
}} }}
> ></Cell>
<Cell component={cellComponent} prefixCls={prefixCls} colSpan={colSpan}>
{contentNode}
</Cell>
</Component> </Component>
); );
}; };

View File

@ -16,7 +16,6 @@ export interface BodyProps<RecordType> {
expandedKeys: Set<Key>; expandedKeys: Set<Key>;
customRow: GetComponentProps<RecordType>; customRow: GetComponentProps<RecordType>;
rowExpandable: (record: RecordType) => boolean; rowExpandable: (record: RecordType) => boolean;
// emptyNode: React.ReactNode;
childrenColumnName: string; childrenColumnName: string;
} }

View File

@ -22,14 +22,12 @@ function isRenderCell<RecordType = DefaultRecordType>(
export interface CellProps<RecordType = DefaultRecordType> { export interface CellProps<RecordType = DefaultRecordType> {
prefixCls?: string; prefixCls?: string;
className?: string;
record?: RecordType; record?: RecordType;
/** `record` index. Not `column` index. */ /** `record` index. Not `column` index. */
index?: number; index?: number;
dataIndex?: DataIndex; dataIndex?: DataIndex;
customRender?: ColumnType<RecordType>['customRender']; customRender?: ColumnType<RecordType>['customRender'];
component?: CustomizeComponent; component?: CustomizeComponent;
children?: any;
colSpan?: number; colSpan?: number;
rowSpan?: number; rowSpan?: number;
ellipsis?: CellEllipsisType; ellipsis?: CellEllipsisType;
@ -46,6 +44,7 @@ export interface CellProps<RecordType = DefaultRecordType> {
// Additional // Additional
/** @private Used for `expandable` with nest tree */ /** @private Used for `expandable` with nest tree */
appendNode?: any; appendNode?: any;
additionalProps?: HTMLAttributes; additionalProps?: HTMLAttributes;
rowType?: 'header' | 'body' | 'footer'; rowType?: 'header' | 'body' | 'footer';
@ -56,18 +55,39 @@ export interface CellProps<RecordType = DefaultRecordType> {
} }
export default defineComponent<CellProps>({ export default defineComponent<CellProps>({
name: 'Cell', name: 'Cell',
props: [] as any, props: [
'prefixCls',
'record',
'index',
'dataIndex',
'customRender',
'children',
'component',
'colSpan',
'rowSpan',
'fixLeft',
'fixRight',
'firstFixLeft',
'lastFixLeft',
'firstFixRight',
'lastFixRight',
'appendNode',
'additionalProps',
'ellipsis',
'align',
'rowType',
'isSticky',
'column',
] as any,
slots: ['appendNode'], slots: ['appendNode'],
setup(props) { setup(props, { slots }) {
return () => { return () => {
const { const {
prefixCls, prefixCls,
className,
record, record,
index, index,
dataIndex, dataIndex,
customRender, customRender,
children,
component: Component = 'td', component: Component = 'td',
colSpan, colSpan,
rowSpan, rowSpan,
@ -77,7 +97,7 @@ export default defineComponent<CellProps>({
lastFixLeft, lastFixLeft,
firstFixRight, firstFixRight,
lastFixRight, lastFixRight,
appendNode, appendNode = slots.appendNode?.(),
additionalProps = {}, additionalProps = {},
ellipsis, ellipsis,
align, align,
@ -90,7 +110,7 @@ export default defineComponent<CellProps>({
// ==================== Child Node ==================== // ==================== Child Node ====================
let cellProps: CellType; let cellProps: CellType;
let childNode; let childNode;
const children = slots.default?.();
if (validateValue(children)) { if (validateValue(children)) {
childNode = children; childNode = children;
} else { } else {
@ -127,8 +147,7 @@ export default defineComponent<CellProps>({
colSpan: cellColSpan, colSpan: cellColSpan,
rowSpan: cellRowSpan, rowSpan: cellRowSpan,
style: cellStyle, style: cellStyle,
className: cellClassName, class: cellClassName,
class: cellClass,
...restCellProps ...restCellProps
} = cellProps || {}; } = cellProps || {};
const mergedColSpan = cellColSpan !== undefined ? cellColSpan : colSpan; const mergedColSpan = cellColSpan !== undefined ? cellColSpan : colSpan;
@ -179,7 +198,6 @@ export default defineComponent<CellProps>({
rowSpan: mergedRowSpan && mergedRowSpan !== 1 ? mergedRowSpan : null, rowSpan: mergedRowSpan && mergedRowSpan !== 1 ? mergedRowSpan : null,
class: classNames( class: classNames(
cellPrefixCls, cellPrefixCls,
className,
{ {
[`${cellPrefixCls}-fix-left`]: isFixLeft, [`${cellPrefixCls}-fix-left`]: isFixLeft,
[`${cellPrefixCls}-fix-left-first`]: firstFixLeft, [`${cellPrefixCls}-fix-left-first`]: firstFixLeft,
@ -193,7 +211,6 @@ export default defineComponent<CellProps>({
}, },
additionalProps.class, additionalProps.class,
cellClassName, cellClassName,
cellClass,
), ),
style: { style: {
...parseStyleText(additionalProps.style as any), ...parseStyleText(additionalProps.style as any),

View File

@ -6,8 +6,6 @@ import type { AlignType } from '../interface';
import { getCellFixedInfo } from '../utils/fixUtil'; import { getCellFixedInfo } from '../utils/fixUtil';
export interface SummaryCellProps { export interface SummaryCellProps {
className?: string;
children?: any;
index: number; index: number;
colSpan?: number; colSpan?: number;
rowSpan?: number; rowSpan?: number;
@ -38,7 +36,7 @@ export default defineComponent<SummaryCellProps>({
return ( return (
<Cell <Cell
className={attrs.class as string} class={attrs.class as string}
index={index} index={index}
component="td" component="td"
prefixCls={prefixCls} prefixCls={prefixCls}

View File

@ -1,3 +1,8 @@
export default function FooterRow(props, { slots }) { import { defineComponent } from 'vue';
return <tr {...props}>{slots.default?.()}</tr>;
} export default defineComponent({
name: 'FooterRow',
setup(_props, { slots }) {
return () => <tr>{slots.default?.()}</tr>;
},
});

View File

@ -1,4 +1,5 @@
import { FunctionalComponent } from 'vue'; import { computed, defineComponent, FunctionalComponent, onBeforeUnmount, watchEffect } from 'vue';
import { useInjectTable } from '../context/TableContext';
import Cell from './Cell'; import Cell from './Cell';
import Row from './Row'; import Row from './Row';
@ -11,16 +12,22 @@ export interface SummaryFC extends FunctionalComponent<SummaryProps> {
Cell: typeof Cell; Cell: typeof Cell;
} }
/** let indexGuid = 0;
* Syntactic sugar. Do not support HOC. const Summary = defineComponent<SummaryProps>({
*/ props: ['fixed'] as any,
const Summary: SummaryFC = (_props, { slots }) => { name: 'Summary',
return slots.default?.(); setup(props, { slots }) {
}; const tableContext = useInjectTable();
const uniKey = `table-summary-uni-key-${++indexGuid}`;
Summary.Row = Row; const fixed = computed(() => (props.fixed as string) === '' || props.fixed);
Summary.Cell = Cell; watchEffect(() => {
tableContext.summaryCollect(uniKey, fixed.value);
Summary.displayName = 'Summary'; });
onBeforeUnmount(() => {
tableContext.summaryCollect(uniKey, false);
});
return () => slots.default?.();
},
});
export default Summary; export default Summary;

View File

@ -29,8 +29,8 @@ function parseHeaderRows<RecordType>(
const colSpans: number[] = columns.filter(Boolean).map(column => { const colSpans: number[] = columns.filter(Boolean).map(column => {
const cell: CellType<RecordType> = { const cell: CellType<RecordType> = {
key: column.key, key: column.key,
className: classNames(column.className, column.class), class: classNames(column.className, column.class),
children: column.title, // children: column.title,
column, column,
colStart: currentColIndex, colStart: currentColIndex,
}; };

View File

@ -86,6 +86,7 @@ export default defineComponent<RowProps>({
additionalProps={additionalProps} additionalProps={additionalProps}
rowType="header" rowType="header"
column={column} column={column}
v-slots={{ default: () => column.title }}
/> />
); );
})} })}

View File

@ -1,26 +1,20 @@
import ColumnGroup from './sugar/ColumnGroup';
import Column from './sugar/Column';
import Header from './Header/Header'; import Header from './Header/Header';
import type { import type {
GetRowKey, GetRowKey,
ColumnsType, ColumnsType,
TableComponents, TableComponents,
Key, Key,
DefaultRecordType,
TriggerEventHandler, TriggerEventHandler,
GetComponentProps, GetComponentProps,
ExpandableConfig, ExpandableConfig,
LegacyExpandableProps, LegacyExpandableProps,
GetComponent,
PanelRender, PanelRender,
TableLayout, TableLayout,
ExpandableType,
RowClassName, RowClassName,
CustomizeComponent, CustomizeComponent,
ColumnType, ColumnType,
CustomizeScrollBody, CustomizeScrollBody,
TableSticky, TableSticky,
FixedType,
} from './interface'; } from './interface';
import Body from './Body'; import Body from './Body';
import useColumns from './hooks/useColumns'; import useColumns from './hooks/useColumns';
@ -28,16 +22,13 @@ import { useLayoutState, useTimeoutLock } from './hooks/useFrame';
import { getPathValue, mergeObject, validateValue, getColumnsKey } from './utils/valueUtil'; import { getPathValue, mergeObject, validateValue, getColumnsKey } from './utils/valueUtil';
import useStickyOffsets from './hooks/useStickyOffsets'; import useStickyOffsets from './hooks/useStickyOffsets';
import ColGroup from './ColGroup'; import ColGroup from './ColGroup';
import { getExpandableProps, getDataAndAriaProps } from './utils/legacyUtil';
import Panel from './Panel'; import Panel from './Panel';
import Footer, { FooterComponents } from './Footer'; import Footer from './Footer';
import { findAllChildrenKeys, renderExpandIcon } from './utils/expandUtil'; import { findAllChildrenKeys, renderExpandIcon } from './utils/expandUtil';
import { getCellFixedInfo } from './utils/fixUtil'; import { getCellFixedInfo } from './utils/fixUtil';
import StickyScrollBar from './stickyScrollBar'; import StickyScrollBar from './stickyScrollBar';
import useSticky from './hooks/useSticky'; import useSticky from './hooks/useSticky';
import FixedHolder from './FixedHolder'; import FixedHolder from './FixedHolder';
import type { SummaryProps } from './Footer/Summary';
import Summary from './Footer/Summary';
import { import {
computed, computed,
CSSProperties, CSSProperties,
@ -59,6 +50,10 @@ import isVisible from '../vc-util/Dom/isVisible';
import { getTargetScrollBarSize } from '../_util/getScrollBarSize'; import { getTargetScrollBarSize } from '../_util/getScrollBarSize';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { EventHandler } from '../_util/EventInterface'; import { EventHandler } from '../_util/EventInterface';
import VCResizeObserver from '../vc-resize-observer';
import { useProvideTable } from './context/TableContext';
import { useProvideBody } from './context/BodyContext';
import { useProvideResize } from './context/ResizeContext';
// Used for conditions cache // Used for conditions cache
const EMPTY_DATA = []; const EMPTY_DATA = [];
@ -85,8 +80,8 @@ export interface TableProps<RecordType = unknown> extends LegacyExpandableProps<
rowClassName?: string | RowClassName<RecordType>; rowClassName?: string | RowClassName<RecordType>;
// Additional Part // Additional Part
// title?: PanelRender<RecordType>; title?: PanelRender<RecordType>;
// footer?: PanelRender<RecordType>; footer?: PanelRender<RecordType>;
// summary?: (data: readonly RecordType[]) => any; // summary?: (data: readonly RecordType[]) => any;
// Customize // Customize
@ -103,40 +98,14 @@ export interface TableProps<RecordType = unknown> extends LegacyExpandableProps<
expandColumnWidth?: number; expandColumnWidth?: number;
expandIconColumnIndex?: number; expandIconColumnIndex?: number;
// // =================================== Internal ===================================
// /**
// * @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 !!!
// */
// // Used for antd table transform column with additional column
// transformColumns?: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>;
// /**
// * @private Internal usage, may remove by refactor.
// *
// * !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
// */
// internalRefs?: {
// body: React.MutableRefObject<HTMLDivElement>;
// };
sticky?: boolean | TableSticky; sticky?: boolean | TableSticky;
} }
export default defineComponent<TableProps>({ export default defineComponent<TableProps>({
name: 'Table', name: 'Table',
slots: ['title', 'footer', 'summary', 'emptyText'], slots: ['title', 'footer', 'summary', 'emptyText'],
inheritAttrs: false,
emits: ['expand', 'expandedRowsChange'], emits: ['expand', 'expandedRowsChange'],
setup(props, { slots, attrs, emit }) { setup(props, { slots, emit }) {
const mergedData = computed(() => props.data || EMPTY_DATA); const mergedData = computed(() => props.data || EMPTY_DATA);
const hasData = computed(() => !!mergedData.value.length); const hasData = computed(() => !!mergedData.value.length);
@ -432,13 +401,58 @@ export default defineComponent<TableProps>({
const emptyNode = () => { const emptyNode = () => {
return hasData.value ? null : slots.emptyText?.() || 'No Data'; return hasData.value ? null : slots.emptyText?.() || 'No Data';
}; };
useProvideTable(
reactive({
...reactivePick(props, 'prefixCls', 'direction'),
getComponent,
scrollbarSize,
fixedInfoList: computed(() =>
flattenColumns.value.map((_, colIndex) =>
getCellFixedInfo(
colIndex,
colIndex,
flattenColumns.value,
stickyOffsets.value,
props.direction,
),
),
),
isSticky: computed(() => stickyState.value.isSticky),
summaryCollect,
}),
);
useProvideBody(
reactive({
...reactivePick(
props,
'rowClassName',
'expandedRowClassName',
'expandRowByClick',
'expandedRowRender',
'expandIconColumnIndex',
'indentSize',
),
columns,
flattenColumns,
tableLayout: mergedTableLayout,
componentWidth,
fixHeader,
fixColumn,
horizonScroll,
expandIcon: mergedExpandIcon,
expandableType,
onTriggerExpand,
}),
);
useProvideResize({
onColumnResize,
});
return () => { return () => {
const { const {
prefixCls, prefixCls,
rowClassName,
data,
rowKey,
scroll, scroll,
tableLayout, tableLayout,
direction, direction,
@ -450,10 +464,8 @@ export default defineComponent<TableProps>({
// Customize // Customize
id, id,
showHeader, showHeader,
components,
customHeaderRow, customHeaderRow,
rowExpandable, rowExpandable,
sticky,
customRow, customRow,
} = props; } = props;
@ -648,7 +660,36 @@ export default defineComponent<TableProps>({
</div> </div>
); );
} }
return null;
let fullTable = (
<div
class={classNames(prefixCls, {
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-ping-left`]: pingedLeft.value,
[`${prefixCls}-ping-right`]: pingedRight.value,
[`${prefixCls}-layout-fixed`]: tableLayout === 'fixed',
[`${prefixCls}-fixed-header`]: fixHeader.value,
/** No used but for compatible */
[`${prefixCls}-fixed-column`]: fixColumn.value,
[`${prefixCls}-scroll-horizontal`]: horizonScroll.value,
[`${prefixCls}-has-fix-left`]: flattenColumns.value[0] && flattenColumns.value[0].fixed,
[`${prefixCls}-has-fix-right`]:
flattenColumns.value[columnCount.value - 1] &&
flattenColumns.value[columnCount.value - 1].fixed === 'right',
})}
id={id}
ref={fullTableRef}
>
{title && <Panel class={`${prefixCls}-title`}>{title(mergedData.value)}</Panel>}
<div class={`${prefixCls}-container`}>{groupTableNode}</div>
{footer && <Panel class={`${prefixCls}-footer`}>{footer(mergedData.value)}</Panel>}
</div>
);
if (horizonScroll.value) {
fullTable = <VCResizeObserver onResize={onFullTableResize}>{fullTable}</VCResizeObserver>;
}
return fullTable;
}; };
}, },
}); });

View File

@ -15,6 +15,8 @@ export interface TableContextProps {
fixedInfoList: readonly FixedInfo[]; fixedInfoList: readonly FixedInfo[];
isSticky: boolean; isSticky: boolean;
summaryCollect: (uniKey: string, fixed: boolean | string) => void;
} }
export const BodyContextKey: InjectionKey<TableContextProps> = Symbol('TableContextProps'); export const BodyContextKey: InjectionKey<TableContextProps> = Symbol('TableContextProps');

View File

@ -120,7 +120,6 @@ function useColumns<RecordType>({
}: { }: {
prefixCls?: Ref<string>; prefixCls?: Ref<string>;
columns?: Ref<ColumnsType<RecordType>>; columns?: Ref<ColumnsType<RecordType>>;
// children?: React.ReactNode;
expandable: Ref<boolean>; expandable: Ref<boolean>;
expandedKeys: Ref<Set<Key>>; expandedKeys: Ref<Set<Key>>;
getRowKey: Ref<GetRowKey<RecordType>>; getRowKey: Ref<GetRowKey<RecordType>>;
@ -134,11 +133,6 @@ function useColumns<RecordType>({
expandFixed?: Ref<FixedType>; expandFixed?: Ref<FixedType>;
}): // transformColumns: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>, }): // transformColumns: (columns: ColumnsType<RecordType>) => ColumnsType<RecordType>,
[ComputedRef<ColumnsType<RecordType>>, ComputedRef<readonly ColumnType<RecordType>[]>] { [ComputedRef<ColumnsType<RecordType>>, ComputedRef<readonly ColumnType<RecordType>[]>] {
// const baseColumns = React.useMemo<ColumnsType<RecordType>>(
// () => columns || convertChildrenToColumns(children),
// [columns, children],
// );
// Add expand column // Add expand column
const withExpandColumns = computed<ColumnsType<RecordType>>(() => { const withExpandColumns = computed<ColumnsType<RecordType>>(() => {
if (expandable.value) { if (expandable.value) {

View File

@ -36,9 +36,8 @@ export type RowClassName<RecordType> = (
export interface CellType<RecordType = DefaultRecordType> { export interface CellType<RecordType = DefaultRecordType> {
key?: Key; key?: Key;
class?: string; class?: string;
className?: string;
style?: CSSProperties; style?: CSSProperties;
children?: any; // children?: any;
column?: ColumnsType<RecordType>[number]; column?: ColumnsType<RecordType>[number];
colSpan?: number; colSpan?: number;
rowSpan?: number; rowSpan?: number;