Skip to content

Commit 0f505de

Browse files
committed
feat: add copyTypes and remove showCopyWithHeader
1 parent 6d40b27 commit 0f505de

File tree

4 files changed

+144
-59
lines changed

4 files changed

+144
-59
lines changed

src/spreadSheet/__tests__/index.test.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,27 @@ describe('test spreadSheet ', () => {
4545

4646
test('copy value with header', () => {
4747
const { getByText, unmount } = render(
48-
<SpreadSheet data={data} columns={columns} options={{ showCopyWithHeader: true }} />
48+
<SpreadSheet data={data} columns={columns} options={{ copyTypes: ['copyData'] }} />
4949
);
5050
const cell = getByText('zhangsan');
5151
fireEvent.contextMenu(cell);
52-
const copyBtn = getByText('复制值以及列名');
52+
const copyBtn = getByText('复制值');
53+
expect(copyBtn).toBeInTheDocument();
54+
fireEvent.click(copyBtn);
55+
unmount();
56+
});
57+
58+
test('copy value with header', () => {
59+
const { getByText, unmount } = render(
60+
<SpreadSheet
61+
data={data}
62+
columns={columns}
63+
options={{ copyTypes: ['copyData', 'copyHeadersAndData'] }}
64+
/>
65+
);
66+
const cell = getByText('zhangsan');
67+
fireEvent.contextMenu(cell);
68+
const copyBtn = getByText('复制列名和值');
5369
expect(copyBtn).toBeInTheDocument();
5470
fireEvent.click(copyBtn);
5571
unmount();

src/spreadSheet/demos/basic.tsx

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,35 @@ import { SpreadSheet } from 'dt-react-component';
33

44
export default () => {
55
return (
6-
<SpreadSheet
7-
columns={['name', 'gender', 'age', 'address']}
8-
data={[
9-
['zhangsan', 'male', '20', 'xihu'],
10-
['lisi', 'male', '18', 'yuhang'],
11-
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
12-
]}
13-
options={{ trimWhitespace: false }}
14-
/>
6+
<>
7+
<span>右键菜单:复制值、复制列名</span>
8+
<SpreadSheet
9+
columns={['name', 'gender', 'age', 'address']}
10+
data={[
11+
['zhangsan', 'male', '20', 'xihu'],
12+
['lisi', 'male', '18', 'yuhang'],
13+
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
14+
]}
15+
options={{
16+
trimWhitespace: false,
17+
copyTypes: ['copyData', 'copyHeaders'],
18+
}}
19+
/>
20+
21+
<br />
22+
<span>右键菜单:复制值、复制列名、复制列名和值</span>
23+
<SpreadSheet
24+
columns={['name', 'gender', 'age', 'address']}
25+
data={[
26+
['zhangsan', 'male', '20', 'xihu'],
27+
['lisi', 'male', '18', 'yuhang'],
28+
[' 前面有空格', '后面有空格 ', '中间有 空 格', 'yuhang'],
29+
]}
30+
options={{
31+
trimWhitespace: false,
32+
copyTypes: ['copyData', 'copyHeaders', 'copyHeadersAndData'],
33+
}}
34+
/>
35+
</>
1536
);
1637
};

src/spreadSheet/index.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ demo:
2020

2121
### SpreadSheet
2222

23-
| 参数 | 说明 | 类型 | 默认值 |
24-
| -------------------------- | -------------------------------------- | ----------------- | ------ |
25-
| data | 表格数据 | `Array(二维数组)` | - |
26-
| columns | 列名 | `Array` | - |
27-
| className | 外层组件的 class 名 | `string` | - |
28-
| options.showCopyWithHeader | 右键菜单中是否展示“复制值以及列名”按钮 | `boolean` | - |
29-
| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true |
23+
| 参数 | 说明 | 类型 | 默认值 |
24+
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | -------------- |
25+
| data | 表格数据 | `Array(二维数组)` | - |
26+
| columns | 列名 | `Array` | - |
27+
| className | 外层组件的 class 名 | `string` | - |
28+
| options.copyTypes | 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 | `Array<'copyData' \| 'copyHeaders' \| 'copyHeadersAndData'>` | "['copyData']" |
29+
| options.trimWhitespace | 是否去除内容里的空格 | `boolean` | true |

src/spreadSheet/index.tsx

Lines changed: 89 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import CopyUtils from '../utils/copy';
88
import 'handsontable/dist/handsontable.full.css';
99
import './style.scss';
1010

11+
type ICopyType = 'copyData' | 'copyHeaders' | 'copyHeadersAndData';
12+
1113
type IOptions = HotTableProps & {
12-
/** 是否展示复制值以及列名 */
13-
showCopyWithHeader?: boolean;
14+
// 右键菜单中展示的选项 复制值/复制列名/复制列名和值 按钮 */
15+
copyTypes?: ICopyType[];
1416
};
1517

1618
export interface ISpreadSheetProps {
@@ -33,7 +35,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
3335
const tableRef = useRef<any>(null);
3436
const copyUtils = new CopyUtils();
3537
const _timer = useRef<NodeJS.Timeout>();
36-
const { showCopyWithHeader, ...restProps } = options || {};
38+
const { copyTypes = [], ...restProps } = options || {};
3739
useImperativeHandle(ref, () => ({
3840
tableRef,
3941
}));
@@ -53,7 +55,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
5355
clearTimeout(_timer.current);
5456
};
5557

56-
const getData = () => {
58+
const getShowData = () => {
5759
let showData = data;
5860
if (!showData?.length) {
5961
const emptyArr = new Array(columns.length).fill('', 0, columns.length);
@@ -75,56 +77,102 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
7577
}
7678
};
7779

78-
const beforeCopy = (arr: any[]) => {
79-
/**
80-
* 去除格式化
81-
*/
80+
/**
81+
* 去除格式化
82+
*/
83+
const beforeCopy = (arr: Array<Array<any>>) => {
8284
const value = arr
8385
.map((row: any[]) => {
8486
return row.join('\t');
8587
})
8688
.join('\n');
89+
8790
copyUtils.copy(value);
8891
return false;
8992
};
9093

9194
const getContextMenu = () => {
92-
const items: Record<string, { name: string; callback: Function }> = {
93-
copy: {
94-
name: '复制',
95-
callback: function (this: any, _key: any) {
96-
const indexArr = this.getSelected();
97-
// eslint-disable-next-line prefer-spread
98-
const copyDataArr = this.getData.apply(this, indexArr[0]);
99-
beforeCopy(copyDataArr);
100-
},
101-
},
95+
// 获取值
96+
const getCopyData = () => {
97+
// 调用的是 handsontable 的方法(在 handsontable.d.ts), this/that 调用的是当前文件的方法
98+
const selectedIndexArr = tableRef.current?.hotInstance?.getSelected();
99+
let dataArr: Array<any> = [];
100+
101+
if (Array.isArray(selectedIndexArr)) {
102+
selectedIndexArr.forEach((arr, index) => {
103+
const [r, c, r2, c2] = arr || [];
104+
const colData: [] =
105+
tableRef.current?.hotInstance?.getData(r, c, r2, c2) || [];
106+
if (index === 0) {
107+
dataArr.push(...colData);
108+
} else {
109+
dataArr = dataArr.map((item: any[], index: number) => {
110+
return item.concat(colData[index]);
111+
});
112+
}
113+
});
114+
}
115+
return dataArr;
102116
};
103-
if (showCopyWithHeader) {
104-
const copyWithHeaderItem = {
105-
name: '复制值以及列名',
106-
callback: function (this: any, _key: any, selection: any) {
107-
const indexArr = this.getSelected();
108-
// eslint-disable-next-line prefer-spread
109-
let copyDataArr = this.getData.apply(this, indexArr[0]);
110-
const columnStart = selection?.[0]?.start?.col;
111-
const columnEnd = selection?.[0]?.end?.col;
112-
let columnArr;
117+
// 获取列名
118+
const getCopyHeaders = (selection: Array<any>) => {
119+
// _that 调用的是 handsontable 的方法(在 handsontable.d.ts), this/that 调用的是当前文件的方法
120+
let headerArr: Array<any> = [];
121+
if (Array.isArray(selection)) {
122+
selection.forEach((it) => {
123+
const columnStart = it.start?.col;
124+
const columnEnd = it.end?.col;
113125
if (columnStart !== undefined && columnEnd !== undefined) {
114-
columnArr = columns.slice(columnStart, columnEnd + 1);
115-
}
116-
if (columnArr) {
117-
copyDataArr = [columnArr, ...copyDataArr];
126+
headerArr = headerArr.concat(columns.slice(columnStart, columnEnd + 1));
118127
}
119-
beforeCopy(copyDataArr);
120-
},
121-
};
122-
// 目前版本不支持 copy_with_column_headers 暂时用 cut 代替,以达到与copy类似的表现
123-
items['cut'] = copyWithHeaderItem;
128+
});
129+
}
130+
return headerArr;
131+
};
132+
133+
const copyDataItem = {
134+
name: '复制值',
135+
callback: function (_key: string) {
136+
const copyDataArr = getCopyData();
137+
beforeCopy(copyDataArr);
138+
},
139+
};
140+
const copyHeadersItem = {
141+
name: '复制列名',
142+
callback: function (_key: string, selection: Array<any>) {
143+
const copyHeaders = getCopyHeaders(selection);
144+
beforeCopy([copyHeaders]);
145+
},
146+
};
147+
const copyHeadersAndDataItem = {
148+
name: '复制列名和值',
149+
callback: function (_key: string, selection: Array<any>) {
150+
const copyDataArr = getCopyData();
151+
const copyHeaders = getCopyHeaders(selection);
152+
beforeCopy([copyHeaders, ...copyDataArr]);
153+
},
154+
};
155+
156+
// 目前 items 在 https://github.com/handsontable/handsontable/blob/6.2.2/handsontable.d.ts#L779,自定义方法也可以被执行
157+
const items: Partial<Record<ICopyType, any>> = {};
158+
if (Array.isArray(copyTypes) && copyTypes?.length) {
159+
// 复制值
160+
if (copyTypes.includes('copyData')) {
161+
items['copyData'] = copyDataItem;
162+
}
163+
// 复制列名
164+
if (copyTypes.includes('copyHeaders')) {
165+
items['copyHeaders'] = copyHeadersItem;
166+
}
167+
// 复制列名和值
168+
if (copyTypes.includes('copyHeadersAndData')) {
169+
items['copyHeadersAndData'] = copyHeadersAndDataItem;
170+
}
171+
} else {
172+
items['copyData'] = copyDataItem;
124173
}
125-
return {
126-
items,
127-
} as any;
174+
175+
return { items } as any;
128176
};
129177

130178
return (
@@ -143,7 +191,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
143191
: columns?.[index as number];
144192
return `<span title="${title}">${title}</span>`;
145193
}}
146-
data={getData()}
194+
data={getShowData()}
147195
mergeCells={getMergeCells()}
148196
cell={getCell()}
149197
readOnly

0 commit comments

Comments
 (0)