Skip to content

Commit 361486c

Browse files
authored
fix: upgrade SpreadSheet component for can be binded ref (#404)
Co-authored-by: zhaoge <>
1 parent 1faa376 commit 361486c

File tree

1 file changed

+127
-128
lines changed

1 file changed

+127
-128
lines changed

src/spreadSheet/index.tsx

Lines changed: 127 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useRef } from 'react';
1+
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
22
import type { HotTableProps } from '@handsontable/react';
33
import { HotTable } from '@handsontable/react';
44
import classNames from 'classnames';
@@ -25,144 +25,143 @@ export interface ISpreadSheetProps {
2525
/** 字段类型 */
2626
type: string;
2727
}>;
28+
ref?: any;
2829
}
2930

30-
const SpreadSheet: React.FC<ISpreadSheetProps> = ({
31-
data,
32-
columns = [],
33-
className,
34-
options,
35-
columnTypes = [],
36-
}) => {
37-
const tableRef = useRef<any>(null);
38-
const copyUtils = new CopyUtils();
39-
const _timer = useRef<NodeJS.Timeout>();
40-
const { showCopyWithHeader, ...restProps } = options || {};
31+
const SpreadSheet: React.FC<ISpreadSheetProps> = forwardRef(
32+
({ data, columns = [], className, options, columnTypes = [] }, ref) => {
33+
const tableRef = useRef<any>(null);
34+
const copyUtils = new CopyUtils();
35+
const _timer = useRef<NodeJS.Timeout>();
36+
const { showCopyWithHeader, ...restProps } = options || {};
37+
useImperativeHandle(ref, () => ({
38+
tableRef,
39+
}));
40+
useEffect(() => {
41+
if (tableRef.current) {
42+
removeRenderClock();
43+
_timer.current = setTimeout(() => {
44+
tableRef.current.hotInstance.render();
45+
}, 100);
46+
}
47+
return () => {
48+
removeRenderClock();
49+
};
50+
}, [data, columns]);
4151

42-
useEffect(() => {
43-
if (tableRef.current) {
44-
removeRenderClock();
45-
_timer.current = setTimeout(() => {
46-
tableRef.current.hotInstance.render();
47-
}, 100);
48-
}
49-
return () => {
50-
removeRenderClock();
52+
const removeRenderClock = () => {
53+
clearTimeout(_timer.current);
5154
};
52-
}, [data, columns]);
53-
54-
const removeRenderClock = () => {
55-
clearTimeout(_timer.current);
56-
};
57-
58-
const getData = () => {
59-
let showData = data;
60-
if (!showData?.length) {
61-
const emptyArr = new Array(columns.length).fill('', 0, columns.length);
62-
emptyArr[0] = '暂无数据';
63-
showData = [emptyArr];
64-
}
65-
return showData;
66-
};
6755

68-
const getMergeCells = () => {
69-
if (!data?.length) {
70-
return [{ row: 0, col: 0, rowspan: 1, colspan: columns.length }];
71-
}
72-
};
56+
const getData = () => {
57+
let showData = data;
58+
if (!showData?.length) {
59+
const emptyArr = new Array(columns.length).fill('', 0, columns.length);
60+
emptyArr[0] = '暂无数据';
61+
showData = [emptyArr];
62+
}
63+
return showData;
64+
};
7365

74-
const getCell = () => {
75-
if (!data || !data.length) {
76-
return [{ row: 0, col: 0, className: 'htCenter htMiddle' }];
77-
}
78-
};
66+
const getMergeCells = () => {
67+
if (!data?.length) {
68+
return [{ row: 0, col: 0, rowspan: 1, colspan: columns.length }];
69+
}
70+
};
7971

80-
const beforeCopy = (arr: any[]) => {
81-
/**
82-
* 去除格式化
83-
*/
84-
const value = arr
85-
.map((row: any[]) => {
86-
return row.join('\t');
87-
})
88-
.join('\n');
89-
copyUtils.copy(value);
90-
return false;
91-
};
72+
const getCell = () => {
73+
if (!data || !data.length) {
74+
return [{ row: 0, col: 0, className: 'htCenter htMiddle' }];
75+
}
76+
};
9277

93-
const getContextMenu = () => {
94-
const items: Record<string, { name: string; callback: Function }> = {
95-
copy: {
96-
name: '复制',
97-
callback: function (this: any, _key: any) {
98-
const indexArr = this.getSelected();
99-
// eslint-disable-next-line prefer-spread
100-
const copyDataArr = this.getData.apply(this, indexArr[0]);
101-
beforeCopy(copyDataArr);
102-
},
103-
},
78+
const beforeCopy = (arr: any[]) => {
79+
/**
80+
* 去除格式化
81+
*/
82+
const value = arr
83+
.map((row: any[]) => {
84+
return row.join('\t');
85+
})
86+
.join('\n');
87+
copyUtils.copy(value);
88+
return false;
10489
};
105-
if (showCopyWithHeader) {
106-
const copyWithHeaderItem = {
107-
name: '复制值以及列名',
108-
callback: function (this: any, _key: any, selection: any) {
109-
const indexArr = this.getSelected();
110-
// eslint-disable-next-line prefer-spread
111-
let copyDataArr = this.getData.apply(this, indexArr[0]);
112-
const columnStart = selection?.[0]?.start?.col;
113-
const columnEnd = selection?.[0]?.end?.col;
114-
let columnArr;
115-
if (columnStart !== undefined && columnEnd !== undefined) {
116-
columnArr = columns.slice(columnStart, columnEnd + 1);
117-
}
118-
if (columnArr) {
119-
copyDataArr = [columnArr, ...copyDataArr];
120-
}
121-
beforeCopy(copyDataArr);
90+
91+
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+
},
122101
},
123102
};
124-
// 目前版本不支持 copy_with_column_headers 暂时用 cut 代替,以达到与copy类似的表现
125-
items['cut'] = copyWithHeaderItem;
126-
}
127-
return {
128-
items,
129-
} as any;
130-
};
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;
113+
if (columnStart !== undefined && columnEnd !== undefined) {
114+
columnArr = columns.slice(columnStart, columnEnd + 1);
115+
}
116+
if (columnArr) {
117+
copyDataArr = [columnArr, ...copyDataArr];
118+
}
119+
beforeCopy(copyDataArr);
120+
},
121+
};
122+
// 目前版本不支持 copy_with_column_headers 暂时用 cut 代替,以达到与copy类似的表现
123+
items['cut'] = copyWithHeaderItem;
124+
}
125+
return {
126+
items,
127+
} as any;
128+
};
131129

132-
return (
133-
<HotTable
134-
ref={tableRef}
135-
className={classNames('dtc-handsontable-no-border', className)}
136-
language="zh-CN"
137-
// 空数组情况,不显示colHeaders,否则colHeaders默认会按照 A、B...显示
138-
// 具体可见 https://handsontable.com/docs/7.1.1/Options.html#colHeaders
139-
colHeaders={(index) => {
140-
if (!columns?.length) return false;
141-
// handsontable 不支持 renderCustomHeader,所以只能用 html string 实现 tooltip
142-
const fieldTypeStr = columnTypes?.[index as number]?.type;
143-
const title = fieldTypeStr
144-
? `${columns?.[index as number]}: ${fieldTypeStr}`
145-
: columns?.[index as number];
146-
return `<span title="${title}">${title}</span>`;
147-
}}
148-
data={getData()}
149-
mergeCells={getMergeCells()}
150-
cell={getCell()}
151-
readOnly
152-
rowHeaders // 数字行号
153-
fillHandle={false} // 拖动复制单元格
154-
manualRowResize // 拉伸功能
155-
manualColumnResize // 拉伸功能
156-
autoColumnSize
157-
colWidths={200}
158-
beforeCopy={beforeCopy}
159-
beforeCut={() => false}
160-
columnHeaderHeight={25}
161-
contextMenu={getContextMenu()}
162-
stretchH="all" // 填充空白区域
163-
{...restProps}
164-
/>
165-
);
166-
};
130+
return (
131+
<HotTable
132+
ref={tableRef}
133+
className={classNames('dtc-handsontable-no-border', className)}
134+
language="zh-CN"
135+
// 空数组情况,不显示colHeaders,否则colHeaders默认会按照 A、B...显示
136+
// 具体可见 https://handsontable.com/docs/7.1.1/Options.html#colHeaders
137+
colHeaders={(index) => {
138+
if (!columns?.length) return false;
139+
// handsontable 不支持 renderCustomHeader,所以只能用 html string 实现 tooltip
140+
const fieldTypeStr = columnTypes?.[index as number]?.type;
141+
const title = fieldTypeStr
142+
? `${columns?.[index as number]}: ${fieldTypeStr}`
143+
: columns?.[index as number];
144+
return `<span title="${title}">${title}</span>`;
145+
}}
146+
data={getData()}
147+
mergeCells={getMergeCells()}
148+
cell={getCell()}
149+
readOnly
150+
rowHeaders // 数字行号
151+
fillHandle={false} // 拖动复制单元格
152+
manualRowResize // 拉伸功能
153+
manualColumnResize // 拉伸功能
154+
autoColumnSize
155+
colWidths={200}
156+
beforeCopy={beforeCopy}
157+
beforeCut={() => false}
158+
columnHeaderHeight={25}
159+
contextMenu={getContextMenu()}
160+
stretchH="all" // 填充空白区域
161+
{...restProps}
162+
/>
163+
);
164+
}
165+
);
167166

168167
export default SpreadSheet;

0 commit comments

Comments
 (0)