Skip to content

feat: add copyTypes and remove showCopyWithHeader #545

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

Open
wants to merge 3 commits into
base: 4.x-stable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ All notable changes to this project will be documented in this file. See [standa
### Bug Fixes

- fix immediate option in userList ([#378](https://github.com/DTStack/dt-react-component/issues/378)) ([b8992f3](https://github.com/DTStack/dt-react-component/commit/b8992f38f918dde10cb6349e39aca2c524e70853))
> > > > > > > 272326794683ecd3ed1846ad38fd4258b23dc0f7

## [4.2.0](https://github.com/DTStack/dt-react-component/compare/v4.1.0...v4.2.0) (2023-08-24)

Expand Down
3 changes: 2 additions & 1 deletion docs/guide/migration-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,10 @@ useCookieListener(

- 新增 `observerEle` 属性,支持自定义监听元素。

#### SpreadSheet [#325](https://github.com/DTStack/dt-react-component/pull/325)
#### SpreadSheet [#325](https://github.com/DTStack/dt-react-component/pull/325)、[#545](https://github.com/DTStack/dt-react-component/pull/545)

- 新增 `className` 属性,可自定义外层组件的 class 名。
- 删除 `showCopyWithHeader` 属性,使用 `copyTypes` 属性代替,值为数组,可传入 'copyData'、'copyHeaders'、'copyHeadersAndData',分别代表的功能为:复制值、复制列名、复制列名和值。

#### KeyEventListener [#326](https://github.com/DTStack/dt-react-component/pull/326)

Expand Down
22 changes: 19 additions & 3 deletions src/spreadSheet/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,35 @@ describe('test spreadSheet ', () => {
const { getByText, unmount } = render(<SpreadSheet data={data} columns={columns} />);
const cell = getByText('zhangsan');
fireEvent.contextMenu(cell);
const copyBtn = getByText('复制');
const copyBtn = getByText('复制值');
expect(copyBtn).toBeInTheDocument();
fireEvent.click(copyBtn);
unmount();
});

test('copy value with header', () => {
const { getByText, unmount } = render(
<SpreadSheet data={data} columns={columns} options={{ showCopyWithHeader: true }} />
<SpreadSheet data={data} columns={columns} options={{ copyTypes: ['copyData'] }} />
);
const cell = getByText('zhangsan');
fireEvent.contextMenu(cell);
const copyBtn = getByText('复制值以及列名');
const copyBtn = getByText('复制值');
expect(copyBtn).toBeInTheDocument();
fireEvent.click(copyBtn);
unmount();
});

test('copy value with header', () => {
const { getByText, unmount } = render(
<SpreadSheet
data={data}
columns={columns}
options={{ copyTypes: ['copyData', 'copyHeadersAndData'] }}
/>
);
const cell = getByText('zhangsan');
fireEvent.contextMenu(cell);
const copyBtn = getByText('复制列名和值');
expect(copyBtn).toBeInTheDocument();
fireEvent.click(copyBtn);
unmount();
Expand Down
39 changes: 30 additions & 9 deletions src/spreadSheet/demos/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,35 @@ import { SpreadSheet } from 'dt-react-component';

export default () => {
return (
<SpreadSheet
columns={['name', 'gender', 'age', 'address']}
data={[
['zhangsan', 'male', '20', 'xihu'],
['lisi', 'male', '18', 'yuhang'],
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
]}
options={{ trimWhitespace: false }}
/>
<>
<span>右键菜单:复制值、复制列名</span>
<SpreadSheet
columns={['name', 'gender', 'age', 'address']}
data={[
['zhangsan', 'male', '20', 'xihu'],
['lisi', 'male', '18', 'yuhang'],
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
]}
options={{
trimWhitespace: false,
copyTypes: ['copyData', 'copyHeaders'],
}}
/>

<br />
<span>右键菜单:复制值、复制列名、复制列名和值</span>
<SpreadSheet
columns={['name', 'gender', 'age', 'address']}
data={[
['zhangsan', 'male', '20', 'xihu'],
['lisi', 'male', '18', 'yuhang'],
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
]}
options={{
trimWhitespace: false,
copyTypes: ['copyData', 'copyHeaders', 'copyHeadersAndData'],
}}
/>
</>
);
};
51 changes: 51 additions & 0 deletions src/spreadSheet/demos/changeData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useRef, useState } from 'react';
import { Button } from 'antd';
import { SpreadSheet } from 'dt-react-component';

export default () => {
const _columns = ['name', 'gender', 'age', 'address'];
const _data = [
['zhangsan', 'male', '20', 'xihu'],
['lisi', 'male', '18', 'yuhang'],
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
];
const [columns, setColumns] = useState(_columns);
const [data, setData] = useState(_data);
const hotTableInstanceRef = useRef<any>(null);

const handleData = () => {
setData(data?.length === 2 ? _data : _data.slice(0, 2));
};

const handleColumns = () => {
setColumns(columns?.length === 3 ? _columns : _columns.slice(0, 3));
};

const handleRef = () => {
console.log(hotTableInstanceRef?.current?.hotInstance?.getData());
};

return (
<>
<Button style={{ margin: '0 12px 12px 0' }} type="primary" onClick={handleColumns}>
改变列
</Button>
<Button style={{ margin: '0 12px 12px 0' }} type="primary" onClick={handleData}>
改变数据
</Button>
<Button style={{ margin: '0 12px 12px 0' }} type="primary" onClick={handleRef}>
使用 HotTable 示例(打印数据)
</Button>

<SpreadSheet
ref={hotTableInstanceRef}
columns={columns}
data={data}
options={{
trimWhitespace: false,
copyTypes: ['copyData', 'copyHeaders', 'copyHeadersAndData'],
}}
/>
</>
);
};
19 changes: 10 additions & 9 deletions src/spreadSheet/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: SpreadSheet 多功能表
group: 组件
toc: content
demo:
cols: 2
cols: 1
---

# SpreadSheet 多功能表
Expand All @@ -14,16 +14,17 @@ demo:

## 示例

<code src="./demos/basic.tsx">基础使用</code>
<code src="./demos/basic.tsx" title="基础使用"></code>
<code src="./demos/changeData.tsx" title="改变数据"></code>

## API

### SpreadSheet

| 参数 | 说明 | 类型 | 默认值 |
| -------------------------- | -------------------------------------- | ----------------- | ------ |
| data | 表格数据 | `Array(二维数组)` | - |
| columns | 列名 | `Array` | - |
| className | 外层组件的 class 名 | `string` | - |
| options.showCopyWithHeader | 右键菜单中是否展示“复制值以及列名”按钮 | `boolean` | - |
| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true |
| 参数 | 说明 | 类型 | 默认值 |
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | -------------- |
| data | 表格数据 | `Array(二维数组)` | - |
| columns | 列名 | `Array` | - |
| className | 外层组件的 class 名 | `string` | - |
| options.copyTypes | 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 | `Array<'copyData' \| 'copyHeaders' \| 'copyHeadersAndData'>` | "['copyData']" |
| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true |
133 changes: 89 additions & 44 deletions src/spreadSheet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import CopyUtils from '../utils/copy';
import 'handsontable/dist/handsontable.full.css';
import './style.scss';

type ICopyType = 'copyData' | 'copyHeaders' | 'copyHeadersAndData';

type IOptions = HotTableProps & {
/** 是否展示复制值以及列名 */
showCopyWithHeader?: boolean;
// 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 */
copyTypes?: ICopyType[];
};

export interface ISpreadSheetProps {
Expand All @@ -33,10 +35,8 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
const tableRef = useRef<any>(null);
const copyUtils = new CopyUtils();
const _timer = useRef<NodeJS.Timeout>();
const { showCopyWithHeader, ...restProps } = options || {};
useImperativeHandle(ref, () => ({
tableRef,
}));
const { copyTypes = [], ...restProps } = options || {};
useImperativeHandle(ref, () => tableRef.current);
useEffect(() => {
if (tableRef.current) {
removeRenderClock();
Expand All @@ -53,7 +53,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
clearTimeout(_timer.current);
};

const getData = () => {
const getShowData = () => {
let showData = data;
if (!showData?.length) {
const emptyArr = new Array(columns.length).fill('', 0, columns.length);
Expand All @@ -75,56 +75,101 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
}
};

const beforeCopy = (arr: any[]) => {
/**
* 去除格式化
*/
/**
* 去除格式化
*/
const beforeCopy = (arr: Array<Array<any>>) => {
const value = arr
.map((row: any[]) => {
return row.join('\t');
})
.join('\n');

copyUtils.copy(value);
return false;
};

const getContextMenu = () => {
const items: Record<string, { name: string; callback: Function }> = {
copy: {
name: '复制',
callback: function (this: any, _key: any) {
const indexArr = this.getSelected();
// eslint-disable-next-line prefer-spread
const copyDataArr = this.getData.apply(this, indexArr[0]);
beforeCopy(copyDataArr);
},
},
// 获取值
const getCopyData = () => {
// 调用的是 handsontable 的方法(在 handsontable.d.ts)
const selectedIndexArr = tableRef.current?.hotInstance?.getSelected();
let dataArr: Array<any> = [];

if (Array.isArray(selectedIndexArr)) {
selectedIndexArr.forEach((arr, index) => {
const [r, c, r2, c2] = arr || [];
const colData: [] =
tableRef.current?.hotInstance?.getData(r, c, r2, c2) || [];
if (index === 0) {
dataArr.push(...colData);
} else {
dataArr = dataArr.map((item: any[], index: number) => {
return item.concat(colData[index]);
});
}
});
}
return dataArr;
};
if (showCopyWithHeader) {
const copyWithHeaderItem = {
name: '复制值以及列名',
callback: function (this: any, _key: any, selection: any) {
const indexArr = this.getSelected();
// eslint-disable-next-line prefer-spread
let copyDataArr = this.getData.apply(this, indexArr[0]);
const columnStart = selection?.[0]?.start?.col;
const columnEnd = selection?.[0]?.end?.col;
let columnArr;
// 获取列名
const getCopyHeaders = (selection: Array<any>) => {
let headerArr: Array<any> = [];
if (Array.isArray(selection)) {
selection.forEach((it) => {
const columnStart = it.start?.col;
const columnEnd = it.end?.col;
if (columnStart !== undefined && columnEnd !== undefined) {
columnArr = columns.slice(columnStart, columnEnd + 1);
}
if (columnArr) {
copyDataArr = [columnArr, ...copyDataArr];
headerArr = headerArr.concat(columns.slice(columnStart, columnEnd + 1));
}
beforeCopy(copyDataArr);
},
};
// 目前版本不支持 copy_with_column_headers 暂时用 cut 代替,以达到与copy类似的表现
items['cut'] = copyWithHeaderItem;
});
}
return headerArr;
};

const copyDataItem = {
name: '复制值',
callback: function (_key: string) {
const copyDataArr = getCopyData();
beforeCopy(copyDataArr);
},
};
const copyHeadersItem = {
name: '复制列名',
callback: function (_key: string, selection: Array<any>) {
const copyHeaders = getCopyHeaders(selection);
beforeCopy([copyHeaders]);
},
};
const copyHeadersAndDataItem = {
name: '复制列名和值',
callback: function (_key: string, selection: Array<any>) {
const copyDataArr = getCopyData();
const copyHeaders = getCopyHeaders(selection);
beforeCopy([copyHeaders, ...copyDataArr]);
},
};

// 目前 items 在 https://github.com/handsontable/handsontable/blob/6.2.2/handsontable.d.ts#L779,自定义方法也可以被执行
const items: Partial<Record<ICopyType, any>> = {};
if (Array.isArray(copyTypes) && copyTypes?.length) {
// 复制值
if (copyTypes.includes('copyData')) {
items['copyData'] = copyDataItem;
}
// 复制列名
if (copyTypes.includes('copyHeaders')) {
items['copyHeaders'] = copyHeadersItem;
}
// 复制列名和值
if (copyTypes.includes('copyHeadersAndData')) {
items['copyHeadersAndData'] = copyHeadersAndDataItem;
}
} else {
items['copyData'] = copyDataItem;
}
return {
items,
} as any;

return { items } as any;
};

return (
Expand All @@ -143,7 +188,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
: columns?.[index as number];
return `<span title="${title}">${title}</span>`;
}}
data={getData()}
data={getShowData()}
mergeCells={getMergeCells()}
cell={getCell()}
readOnly
Expand Down
5 changes: 4 additions & 1 deletion src/utils/copy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ export default class CopyUtils {
let succeeded;

try {
succeeded = document.execCommand('copy');
// 浏览器兼容性处理,当前语法已废弃,延迟处理可以保证复制成功
setTimeout(() => {
succeeded = document.execCommand('copy');
});
} catch (err) {
succeeded = false;
}
Expand Down
Loading