Skip to content

Commit 016aaa2

Browse files
committed
feat(SpreadSheet): add copyTypes and remove showCopyWithHeader
1 parent 6d40b27 commit 016aaa2

File tree

4 files changed

+146
-63
lines changed

4 files changed

+146
-63
lines changed

src/spreadSheet/__tests__/index.test.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,35 @@ describe('test spreadSheet ', () => {
3737
const { getByText, unmount } = render(<SpreadSheet data={data} columns={columns} />);
3838
const cell = getByText('zhangsan');
3939
fireEvent.contextMenu(cell);
40-
const copyBtn = getByText('复制');
40+
const copyBtn = getByText('复制值');
4141
expect(copyBtn).toBeInTheDocument();
4242
fireEvent.click(copyBtn);
4343
unmount();
4444
});
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: 90 additions & 44 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,10 +35,8 @@ 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 || {};
37-
useImperativeHandle(ref, () => ({
38-
tableRef,
39-
}));
38+
const { copyTypes = [], ...restProps } = options || {};
39+
useImperativeHandle(ref, () => tableRef.current);
4040
useEffect(() => {
4141
if (tableRef.current) {
4242
removeRenderClock();
@@ -53,7 +53,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
5353
clearTimeout(_timer.current);
5454
};
5555

56-
const getData = () => {
56+
const getShowData = () => {
5757
let showData = data;
5858
if (!showData?.length) {
5959
const emptyArr = new Array(columns.length).fill('', 0, columns.length);
@@ -75,56 +75,102 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
7575
}
7676
};
7777

78-
const beforeCopy = (arr: any[]) => {
79-
/**
80-
* 去除格式化
81-
*/
78+
/**
79+
* 去除格式化
80+
*/
81+
const beforeCopy = (arr: Array<Array<any>>) => {
8282
const value = arr
8383
.map((row: any[]) => {
8484
return row.join('\t');
8585
})
8686
.join('\n');
87+
8788
copyUtils.copy(value);
8889
return false;
8990
};
9091

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

130176
return (
@@ -143,7 +189,7 @@ const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
143189
: columns?.[index as number];
144190
return `<span title="${title}">${title}</span>`;
145191
}}
146-
data={getData()}
192+
data={getShowData()}
147193
mergeCells={getMergeCells()}
148194
cell={getCell()}
149195
readOnly

0 commit comments

Comments
 (0)