1- import React , { useEffect , useRef } from 'react' ;
1+ import React , { forwardRef , useEffect , useImperativeHandle , useRef } from 'react' ;
22import type { HotTableProps } from '@handsontable/react' ;
33import { HotTable } from '@handsontable/react' ;
44import 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
168167export default SpreadSheet ;
0 commit comments