Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Table] 一些特性和缺陷(每周定时处理) #1028

Merged
merged 10 commits into from
Jul 3, 2022
28 changes: 19 additions & 9 deletions src/table/EditableCell.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useMemo, useRef, useState, MouseEvent } from 'react';
import get from 'lodash/get';
import set from 'lodash/set';
import isFunction from 'lodash/isFunction';
import { Edit1Icon } from 'tdesign-icons-react';
import classNames from 'classnames';
import { TableRowData, PrimaryTableCol } from './type';
Expand All @@ -18,18 +19,21 @@ export interface EditableCellProps {
}

const EditableCell = (props: EditableCellProps) => {
const { row, col } = props;
const { row, col, rowIndex, colIndex } = props;
const { tableBaseClass } = useClassName();
const tableEditableCellRef = useRef(null);
const [isEdit, setIsEdit] = useState(false);
const [editValue, setEditValue] = useState();
const [errorList, setErrorList] = useState([]);

const currentRow = useMemo(() => {
const getCurrentRow = (row: TableRowData, colKey: string, value: any) => {
const newRow = { ...row };
set(newRow, col.colKey, editValue);
set(newRow, colKey, value);
return newRow;
}, [col.colKey, editValue, row]);
};

// eslint-disable-next-line react-hooks/exhaustive-deps
const currentRow = useMemo(() => getCurrentRow(row, col.colKey, editValue), [col.colKey, editValue, row]);

const cellNode = useMemo(() => {
const node = renderCell({
Expand All @@ -44,15 +48,17 @@ const EditableCell = (props: EditableCellProps) => {
const componentProps = useMemo(() => {
const { edit } = col;
if (!edit) return {};
const editProps = { ...edit.props };
const editProps = isFunction(edit.props)
? edit.props({ col, row, rowIndex, colIndex, editedRow: currentRow })
: { ...edit.props };
// to remove warn: runtime-core.esm-bundler.js:38 [Vue warn]: Invalid prop: type check failed for prop "onChange". Expected Function, got Array
delete editProps.onChange;
delete editProps.value;
edit.abortEditOnEvent?.forEach((item) => {
delete editProps[item];
});
return editProps;
}, [col]);
}, [col, colIndex, currentRow, row, rowIndex]);

const isAbortEditOnChange = useMemo(() => {
const { edit } = col;
Expand Down Expand Up @@ -88,7 +94,7 @@ const EditableCell = (props: EditableCellProps) => {
validateEdit().then((result) => {
if (result !== true) return;
// 相同的值无需触发变化
if (!isSame(editValue, get(row, col.colKey))) {
if (!isSame(args[0].value, get(row, col.colKey))) {
outsideAbortEvent?.(...args);
}
// 此处必须在事件执行完成后异步销毁编辑组件,否则会导致事件清楚不及时引起的其他问题
Expand All @@ -112,6 +118,7 @@ const EditableCell = (props: EditableCellProps) => {
updateAndSaveAbort(
outsideAbortEvent,
{
value: editValue,
trigger: itemEvent,
newRowData: currentRow,
rowIndex: props.rowIndex,
Expand All @@ -128,11 +135,13 @@ const EditableCell = (props: EditableCellProps) => {
setEditValue(val);
if (isAbortEditOnChange) {
const outsideAbortEvent = col.edit?.onEdited;
// editValue 和 currentRow 更新完成后再执行这个函数
updateAndSaveAbort(
outsideAbortEvent,
{
value: val,
trigger: 'onChange',
newRowData: currentRow,
newRowData: getCurrentRow(currentRow, col.colKey, val),
rowIndex: props.rowIndex,
},
...args,
Expand All @@ -144,9 +153,10 @@ const EditableCell = (props: EditableCellProps) => {
if (!col.edit || !col.edit.component) return;
if (!isEdit || !tableEditableCellRef?.current) return;
// @ts-ignore
if (e.path?.includes(tableEditableCellRef?.current)) return;
if (e.path?.includes(tableEditableCellRef?.current?.currentElement)) return;
const outsideAbortEvent = col.edit.onEdited;
updateAndSaveAbort(outsideAbortEvent, {
value: editValue,
trigger: 'document',
newRowData: currentRow,
rowIndex: props.rowIndex,
Expand Down
6 changes: 4 additions & 2 deletions src/table/EnhancedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const EnhancedTable = forwardRef((props: TEnhancedTableProps, ref) => {

const treeDataMap = store?.treeDataMap;

const { onInnerSelectChange } = useTreeSelect(props, treeDataMap);
const { tIndeterminateSelectedRowKeys, onInnerSelectChange } = useTreeSelect(props, treeDataMap);

// 影响列和单元格内容的因素有:树形节点需要添加操作符 [+] [-]
const getColumns = (columns: PrimaryTableCol<TableRowData>[]) => {
Expand All @@ -42,7 +42,7 @@ const EnhancedTable = forwardRef((props: TEnhancedTableProps, ref) => {
return isTreeData ? columns : getColumns(columns);
})();

useImperativeHandle(ref, () => ({ ...treeInstanceFunctions }));
useImperativeHandle(ref, () => ({ treeDataMap, ...treeInstanceFunctions }));

const onDragSortChange = (params: DragSortContext<TableRowData>) => {
if (props.beforeDragSort && !props.beforeDragSort(params)) return;
Expand All @@ -60,6 +60,8 @@ const EnhancedTable = forwardRef((props: TEnhancedTableProps, ref) => {
...props,
data: dataSource,
columns: tColumns,
// 半选状态节点
indeterminateSelectedRowKeys: tIndeterminateSelectedRowKeys,
// 树形结构不允许本地数据分页
disableDataPage: Boolean(tree && Object.keys(tree).length),
onSelectChange: onInnerSelectChange,
Expand Down
6 changes: 2 additions & 4 deletions src/table/__tests__/__snapshots__/table.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -22869,11 +22869,10 @@ exports[`tree-select.jsx 1`] = `
class=""
>
<label
class="t-checkbox t-is-disabled"
class="t-checkbox"
>
<input
class="t-checkbox__former"
disabled=""
type="checkbox"
value=""
/>
Expand Down Expand Up @@ -23070,11 +23069,10 @@ exports[`tree-select.jsx 1`] = `
class=""
>
<label
class="t-checkbox t-is-disabled"
class="t-checkbox"
>
<input
class="t-checkbox__former"
disabled=""
type="checkbox"
value=""
/>
Expand Down
21 changes: 15 additions & 6 deletions src/table/_example/editable-cell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default function EditableCellTable() {
}));

const [data, setData] = useState([...initData]);
const [relationSelect, setRelationSelect] = useState({});

const columns = useMemo(
() => [
Expand Down Expand Up @@ -74,6 +75,12 @@ export default function EditableCellTable() {
setData([...data]);
console.log('Edit Framework:', context);
MessagePlugin.success('Success');
// 记录编辑结果
const { newRowData } = context;
setRelationSelect({
...relationSelect,
[newRowData.key]: newRowData.framework,
});
},
},
},
Expand All @@ -84,7 +91,8 @@ export default function EditableCellTable() {
edit: {
component: Select,
// props, 透传全部属性到 Select 组件
props: {
// props 为函数时,参数有:col, row, rowIndex, colIndex, editedRow。一般用于实现编辑组件之间的联动
props: ({ editedRow }) => ({
multiple: true,
minCollapsedNum: 1,
options: [
Expand All @@ -93,10 +101,11 @@ export default function EditableCellTable() {
{ label: 'C', value: 'C' },
{ label: 'D', value: 'D' },
{ label: 'E', value: 'E' },
{ label: 'G', value: 'G' },
{ label: 'H', value: 'H' },
],
},
// 如果框架选择了 React,则 Letters 隐藏 G 和 H
{ label: 'G', value: 'G', show: () => editedRow.framework !== 'React' },
{ label: 'H', value: 'H', show: () => editedRow.framework !== 'React' },
].filter(t => (t.show === undefined ? true : t.show())),
}),
// abortEditOnEvent: ['onChange'],
onEdited: (context) => {
data.splice(context.rowIndex, 1, context.newRowData);
Expand Down Expand Up @@ -126,7 +135,7 @@ export default function EditableCellTable() {
},
},
],
[data],
[data, relationSelect],
);

// 当前示例包含:输入框、单选、多选、日期 等场景
Expand Down
10 changes: 7 additions & 3 deletions src/table/_example/filter-controlled.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Table, Button, DatePicker, Space } from 'tdesign-react';
import { Table, Button, DateRangePickerPanel, Space } from 'tdesign-react';

const columns = [
{
Expand Down Expand Up @@ -61,10 +61,14 @@ const columns = [
// 自定义过滤组件
filter: {
// 直接传入组件,请确保自定义过滤组件包含 value 和 onChange 等两个参数,组件内部会自动处理
component: DatePicker,
component: DateRangePickerPanel,
props: {
clearable: true,
firstDayOfWeek: 7,
},
// 是否显示重置取消按钮,一般情况不需要显示
showConfirmAndReset: true,
// 日期范围是一个组件,重置时需赋值为 []
resetValue: [],
},
},
];
Expand Down
20 changes: 16 additions & 4 deletions src/table/_example/tree-select.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { EnhancedTable, Radio, Space } from 'tdesign-react';
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';

const CHILDREN_KEY = 'childrenList';

const initData = [];
for (let i = 0; i < 5; i++) {
Expand All @@ -19,7 +22,7 @@ for (let i = 0; i < 5; i++) {
key: secondIndex,
instance: `JQTest${secondIndex}`,
};
secondObj.childrenList = new Array(5).fill(null).map((m, n) => {
secondObj.childrenList = new Array(3).fill(null).map((m, n) => {
const thirdIndex = secondIndex * 1000 + 100 * m + (n + 1) * 10;
return {
...obj,
Expand All @@ -42,7 +45,7 @@ const columns = [

// 禁用行选中方式二:使用 checkProps 禁用行(示例代码有效,勿删)
// 这种方式禁用行选中,行文本不会变灰
checkProps: ({ row }) => ({ disabled: row.status !== 0 }),
checkProps: ({ row }) => ({ disabled: !get(row, CHILDREN_KEY) && row.status === 0 }),
// 自由调整宽度,如果发现元素看不见,请加大宽度
width: 20,
},
Expand All @@ -66,6 +69,12 @@ export default function TableSingleSort() {
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [checkStrictly, setCheckStrictly] = useState(true);
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
const treeTableRef = useRef();

useEffect(() => {
// 包含 treeDataMap 及各类树形操作方法
console.log(treeTableRef.current);
}, []);

useEffect(
() => {
Expand All @@ -76,6 +85,7 @@ export default function TableSingleSort() {
[checkStrictly.value],
);

// 可使用 treeTableRef.current.treeDataMap 判断是否为叶子结点,或任意结点的层级
function onSelectChange(value, selectOptions) {
console.log('onSelectChange', value, selectOptions);
setSelectedRowKeys(value);
Expand All @@ -93,12 +103,14 @@ export default function TableSingleSort() {
</Radio.Group>

<EnhancedTable
ref={treeTableRef}
rowKey="key"
data={data}
columns={columns}
// indeterminateSelectedRowKeys={[1]}
selectedRowKeys={selectedRowKeys}
onSelectChange={onSelectChange}
tree={{ checkStrictly, childrenKey: 'childrenList' }}
tree={{ checkStrictly, childrenKey: CHILDREN_KEY }}
expandedRow={({ row }) => <div>这是展开项数据,我是 {row.key} 号</div>}
expandedRowKeys={expandedRowKeys}
onExpandChange={onExpandChange}
Expand Down
8 changes: 1 addition & 7 deletions src/table/defaultProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,10 @@ export const baseTableDefaultProps: TdBaseTableProps = {

export const primaryTableDefaultProps: Pick<
TdPrimaryTableProps,
| 'columnControllerVisible'
| 'columns'
| 'defaultExpandedRowKeys'
| 'expandIcon'
| 'multipleSort'
| 'sortOnRowDraggable'
'columnControllerVisible' | 'columns' | 'expandIcon' | 'multipleSort' | 'sortOnRowDraggable'
> = {
columnControllerVisible: undefined,
columns: [],
defaultExpandedRowKeys: [],
expandIcon: true,
multipleSort: false,
sortOnRowDraggable: false,
Expand Down
15 changes: 15 additions & 0 deletions src/table/hooks/tree-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ class TableTreeStore<T extends TableRowData = TableRowData> {
this.initialTreeDataMap(this.treeDataMap, dataSource, columns[0], keys);
}

/**
* 获取所有节点的唯一标识
*/
getAllUniqueKeys(data: T[], keys: KeysType, arr: T[] = []) {
for (let i = 0, len = data.length; i < len; i++) {
const item = data[i];
arr.push(get(item, keys.rowKey));
const children = get(item, keys.childrenKey);
if (children?.length) {
this.getAllUniqueKeys(children, keys, arr);
}
}
return arr;
}

toggleExpandData(p: { rowIndex: number; row: T }, dataSource: T[], keys: KeysType) {
if (!p) {
log.error('EnhancedTable', 'the node you want to toggleExpand doest not exist in `data`');
Expand Down
4 changes: 3 additions & 1 deletion src/table/hooks/useRowExpand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export default function useRowExpand(props: TdPrimaryTableProps) {
const [locale] = useLocaleReceiver('table');
const { tableExpandClasses, positiveRotate90, tableFullRowClasses } = useClassName();
// controlled and uncontrolled
const [tExpandedRowKeys, setTExpandedRowKeys] = useControlled(props, 'expandedRowKeys', props.onExpandChange);
const [tExpandedRowKeys, setTExpandedRowKeys] = useControlled(props, 'expandedRowKeys', props.onExpandChange, {
defaultExpandedRowKeys: props.defaultExpandedRowKeys || [],
});

const showExpandedRow = Boolean(props.expandedRow);

Expand Down
15 changes: 11 additions & 4 deletions src/table/hooks/useRowSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import { ClassName } from '../../common';
import log from '../../_common/js/log';

export default function useRowSelect(props: TdPrimaryTableProps) {
const { selectedRowKeys, columns, data, rowKey } = props;
const { selectedRowKeys, columns, data, rowKey, indeterminateSelectedRowKeys } = props;
const { tableSelectedClasses } = useClassName();
const [selectedRowClassNames, setSelectedRowClassNames] = useState<TdBaseTableProps['rowClassName']>();
const [tSelectedRowKeys, setTSelectedRowKeys] = useControlled(props, 'selectedRowKeys', props.onSelectChange);
const [tSelectedRowKeys, setTSelectedRowKeys] = useControlled(props, 'selectedRowKeys', props.onSelectChange, {
defaultSelectedRowKeys: props.defaultSelectedRowKeys || [],
});
const selectColumn = props.columns.find(({ type }) => ['multiple', 'single'].includes(type));
const canSelectedRows = props.data.filter((row, rowIndex): boolean => !isDisabled(row, rowIndex));
// 选中的行,和所有可以选择的行,交集,用于计算 isSelectedAll 和 isIndeterminate
Expand Down Expand Up @@ -86,7 +88,12 @@ export default function useRowSelect(props: TdPrimaryTableProps) {
e?.stopPropagation();
};
if (column.type === 'single') return <Radio {...selectBoxProps} onClick={onCheckClick} />;
if (column.type === 'multiple') return <Checkbox {...selectBoxProps} onClick={onCheckClick} />;
if (column.type === 'multiple') {
const isIndeterminate = indeterminateSelectedRowKeys?.length
? indeterminateSelectedRowKeys.includes(get(row, rowKey))
: false;
return <Checkbox indeterminate={isIndeterminate} {...selectBoxProps} onClick={onCheckClick} />;
}
return null;
}

Expand Down Expand Up @@ -118,7 +125,7 @@ export default function useRowSelect(props: TdPrimaryTableProps) {
const disabledSelectedRowKeys = selectedRowKeys?.filter((id) => !canSelectedRowKeys.includes(id)) || [];
const allIds = checked ? [...disabledSelectedRowKeys, ...canSelectedRowKeys] : [...disabledSelectedRowKeys];
setTSelectedRowKeys(allIds, {
selectedRowData: filterDataByIds(props.data, allIds, reRowKey),
selectedRowData: checked ? filterDataByIds(props.data, allIds, reRowKey) : [],
type: checked ? 'check' : 'uncheck',
currentRowKey: 'CHECK_ALL_BOX',
});
Expand Down
Loading