1
- import React , { useEffect , useMemo , useRef , useState } from "react" ;
1
+ import React , {
2
+ useCallback ,
3
+ useEffect ,
4
+ useMemo ,
5
+ useRef ,
6
+ useState ,
7
+ DragEvent ,
8
+ } from "react" ;
2
9
import {
3
10
HTMLTable ,
4
11
EditableText ,
@@ -79,6 +86,11 @@ const getSettings = (blockUid: string) => {
79
86
( c ) => c . text . toLowerCase ( ) === "interactive"
80
87
) ,
81
88
} ;
89
+ const widths = getSubTree ( {
90
+ tree : optionsNode . children ,
91
+ key : "widths" ,
92
+ parentUid : optionsNode . uid ,
93
+ } ) . children . map ( ( c ) => c . text ) ;
82
94
83
95
// fold {{wb-table}} block
84
96
if ( ! isLoaded )
@@ -96,6 +108,7 @@ const getSettings = (blockUid: string) => {
96
108
styles,
97
109
viewNode,
98
110
view,
111
+ widths,
99
112
} ;
100
113
} ;
101
114
const Configuration = ( { blockUid, onSubmit } : ConfigurationProps ) => {
@@ -264,11 +277,37 @@ const Configuration = ({ blockUid, onSubmit }: ConfigurationProps) => {
264
277
</ div >
265
278
) ;
266
279
} ;
280
+
281
+ const dragImage = document . createElement ( "img" ) ;
282
+ dragImage . src =
283
+ "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" ;
284
+
267
285
const DisplayTable = ( { blockUid, setIsEdit } : DisplayTableProps ) => {
268
286
const [ settings , setSettings ] = useState ( ( ) => getSettings ( blockUid ) ) ;
269
287
const [ isMenuOpen , setIsMenuOpen ] = useState ( false ) ;
270
288
const containerRef = useRef < HTMLDivElement > ( null ) ;
271
289
290
+ const columnWidths = useMemo ( ( ) => {
291
+ const widths =
292
+ typeof settings . widths === "string"
293
+ ? [ settings . widths ]
294
+ : settings . widths || [ ] ;
295
+ return Object . fromEntries (
296
+ widths
297
+ . map ( ( w ) => {
298
+ const match = / ^ ( .* ) - ( [ ^ - ] + ) $ / . exec ( w ) ;
299
+ return match ;
300
+ } )
301
+ . filter ( ( m ) : m is RegExpExecArray => ! ! m )
302
+ . map ( ( match ) => {
303
+ return [ match [ 1 ] , match [ 2 ] ] ;
304
+ } )
305
+ ) ;
306
+ } , [ settings ] ) ;
307
+
308
+ const rows = settings . rowsNode . children ;
309
+ const headers = settings . headerNode . children ;
310
+
272
311
const updateText = ( uid : string , value : string ) => {
273
312
updateBlock ( { uid, text : value } ) ;
274
313
} ;
@@ -411,6 +450,61 @@ const DisplayTable = ({ blockUid, setIsEdit }: DisplayTableProps) => {
411
450
/>
412
451
) ;
413
452
} ;
453
+
454
+ const [ thRefs , setThRefs ] = useState <
455
+ React . RefObject < HTMLTableHeaderCellElement > [ ]
456
+ > ( [ ] ) ;
457
+ useEffect ( ( ) => {
458
+ setThRefs (
459
+ headers . map ( ( _ ) => React . createRef < HTMLTableHeaderCellElement > ( ) )
460
+ ) ;
461
+ } , [ headers ] ) ;
462
+ const trRef = useRef < HTMLTableRowElement > ( null ) ;
463
+
464
+ const onDragStart = useCallback (
465
+ ( e ) => {
466
+ e . dataTransfer . setDragImage ( dragImage , 0 , 0 ) ;
467
+ } ,
468
+ [ dragImage ]
469
+ ) ;
470
+
471
+ const dragHandler = useCallback (
472
+ ( e : DragEvent < HTMLDivElement > ) => {
473
+ const delta = e . clientX - e . currentTarget . getBoundingClientRect ( ) . left ;
474
+ const cellWidth =
475
+ e . currentTarget . parentElement ?. getBoundingClientRect ( ) . width ;
476
+ if ( typeof cellWidth === "undefined" ) return ;
477
+ if ( cellWidth + delta <= 0 ) return ;
478
+ const rowWidth =
479
+ e . currentTarget . parentElement ?. parentElement ?. getBoundingClientRect ( )
480
+ . width ;
481
+ if ( typeof rowWidth === "undefined" ) return ;
482
+ if ( cellWidth + delta >= rowWidth ) return ;
483
+ const column = e . currentTarget . getAttribute ( "data-column" ) ;
484
+ const save = e . type === "dragend" ;
485
+ if ( trRef . current ) {
486
+ trRef . current . style . cursor = save ? "" : "ew-resize" ; // Not working.
487
+ }
488
+ const newWidth = `${ ( ( cellWidth + delta ) / rowWidth ) * 100 } %` ;
489
+ if ( ! column ) return ;
490
+ const columnIndex = parseInt ( column . split ( "-" ) [ 1 ] ) - 1 ;
491
+ const th = thRefs [ columnIndex ] ?. current ;
492
+ if ( ! th ) return ;
493
+ th . style . width = newWidth ;
494
+ if ( ! save ) return ;
495
+ th . style . width = newWidth ;
496
+ setInputSettings ( {
497
+ blockUid : settings . optionsNode . uid ,
498
+ key : "widths" ,
499
+ values : thRefs
500
+ . map ( ( ref , index ) => [ index . toString ( ) , ref . current ?. style . width ] )
501
+ . filter ( ( [ index , width ] ) => width )
502
+ . map ( ( [ index , width ] ) => `${ index } - ${ width } ` ) ,
503
+ } ) ;
504
+ } ,
505
+ [ settings . optionsNode . uid , trRef , thRefs ]
506
+ ) ;
507
+
414
508
return (
415
509
< div className = "relative" ref = { containerRef } >
416
510
< span className = "absolute top-1 right-0" >
@@ -427,46 +521,74 @@ const DisplayTable = ({ blockUid, setIsEdit }: DisplayTableProps) => {
427
521
>
428
522
< thead >
429
523
< tr >
430
- { ! ! settings . headerNode . children . length &&
431
- settings . headerNode . children . map ( ( c ) => (
524
+ { ! ! headers &&
525
+ headers . map ( ( header , i ) => (
432
526
< th
433
- key = { c . uid }
434
- className = { `wbt-header-${ sanitizeClassName ( c . text ) } ` }
527
+ ref = { thRefs [ i ] }
528
+ key = { header . uid }
529
+ className = { `wbt-header-${ sanitizeClassName (
530
+ header . text
531
+ ) } overflow-hidden`}
532
+ style = { { width : columnWidths [ i ] } }
435
533
>
436
534
{ settings . view === "embed" ? (
437
- < CellEmbed uid = { c . uid } />
535
+ < CellEmbed uid = { header . uid } />
438
536
) : (
439
537
< EditableText
440
538
placeholder = ""
441
- defaultValue = { c . text }
442
- onConfirm = { ( value ) => updateText ( c . uid , value ) }
539
+ defaultValue = { header . text }
540
+ onConfirm = { ( value ) => updateText ( header . uid , value ) }
443
541
/>
444
542
) }
445
543
</ th >
446
544
) ) }
447
545
</ tr >
448
546
</ thead >
449
547
< tbody >
450
- { ! ! settings . rowsNode . children . length &&
451
- settings . rowsNode . children . map ( ( c ) => (
548
+ { ! ! rows . length &&
549
+ rows . map ( ( row , i ) => (
452
550
< tr
453
- key = { c . uid }
454
- className = { `wbt-row-${ sanitizeClassName ( c . text ) } w-full` }
551
+ ref = { trRef }
552
+ key = { row . uid }
553
+ className = { `wbt-row-${ sanitizeClassName ( row . text ) } w-full` }
554
+ data-column = { `column-${ i + 1 } ` }
455
555
>
456
- { c . children . map ( ( c ) => (
457
- < td key = { c . uid } className = "overflow-hidden" >
556
+ { row . children . map ( ( cell , i ) => (
557
+ < td key = { cell . uid } className = "overflow-hidden relative " >
458
558
{ settings . view === "embed" ? (
459
- < CellEmbed uid = { c . uid } />
559
+ < CellEmbed uid = { cell . uid } />
460
560
) : (
461
561
< EditableText
462
562
placeholder = ""
463
- defaultValue = { c . text }
464
- onConfirm = { ( value ) => updateText ( c . uid , value ) }
563
+ defaultValue = { cell . text }
564
+ onConfirm = { ( value ) => updateText ( cell . uid , value ) }
465
565
className = { `wbt-cell-${ sanitizeClassName (
466
- c . text
566
+ cell . text
467
567
) } w-full`}
468
568
/>
469
569
) }
570
+ < >
571
+ { i < row . children . length - 1 && (
572
+ < div
573
+ style = { {
574
+ width : 11 ,
575
+ cursor : "ew-resize" ,
576
+ position : "absolute" ,
577
+ top : 0 ,
578
+ right : 0 ,
579
+ bottom : 0 ,
580
+ borderRight : `1px solid rgba(16,22,26,0.15)` ,
581
+ paddingLeft : 5 ,
582
+ pointerEvents : "auto" ,
583
+ } }
584
+ data-column = { `column-${ i + 1 } ` }
585
+ draggable
586
+ onDragStart = { onDragStart }
587
+ onDrag = { dragHandler }
588
+ onDragEnd = { dragHandler }
589
+ />
590
+ ) }
591
+ </ >
470
592
</ td >
471
593
) ) }
472
594
</ tr >
0 commit comments