@@ -15,6 +15,7 @@ import {
1515 Tooltip ,
1616 ButtonGroup ,
1717 useTheme ,
18+ SxProps
1819} from '@mui/material' ;
1920
2021import { VegaLite } from 'react-vega'
@@ -67,13 +68,15 @@ let SingleThreadView: FC<{
6768 threadIdx : number ,
6869 leafTable : DictTable ;
6970 chartElements : { tableId : string , chartId : string , element : any } [ ] ;
70- usedTableIds : string [ ]
71+ usedTableIds : string [ ] ,
72+ sx ?: SxProps
7173} > = function ( {
7274 scrollRef,
7375 threadIdx,
7476 leafTable,
7577 chartElements,
7678 usedTableIds, // tables that have been used
79+ sx
7780} ) {
7881 let theme = useTheme ( ) ;
7982
@@ -84,7 +87,6 @@ let SingleThreadView: FC<{
8487
8588 let focusedChart = charts . find ( c => c . id == focusedChartId ) ;
8689
87-
8890 const dispatch = useDispatch ( ) ;
8991
9092 let [ collapsed , setCollapsed ] = useState < boolean > ( false ) ;
@@ -118,7 +120,8 @@ let SingleThreadView: FC<{
118120 let fieldsIdentical = _ . isEqual ( previousActiveFields , currentActiveFields )
119121
120122 let triggerCard = < div key = { 'thread-card-trigger-box' } >
121- < Box sx = { { flex : 1 } } /*sx={{ width: 'calc(100% - 8px)', marginLeft: 1, borderLeft: '1px dashed darkgray' }}*/ >
123+ < Box sx = { { flex : 1 } }
124+ /*sx={{ width: 'calc(100% - 8px)', marginLeft: 1, borderLeft: '1px dashed darkgray' }}*/ >
122125 < TriggerCard className = { selectedClassName } trigger = { trigger } hideFields = { fieldsIdentical } />
123126 </ Box >
124127 </ div > ;
@@ -228,11 +231,12 @@ let SingleThreadView: FC<{
228231 key = { `table-${ tableId } ` }
229232 sx = { { display : 'flex' , flexDirection : 'row' } } >
230233 < div style = { {
231- minWidth : '1px' , padding : '0px' , width : '17px' , flex : 'none' , display : 'flex'
232- //borderLeft: '1px dashed darkgray',
234+ minWidth : '1px' , padding : '0px' , width : '8px' , flex : 'none' , display : 'flex' ,
235+ marginLeft : '8px' ,
236+ borderLeft : '1px dashed darkgray' ,
233237 } } >
234238 < Box sx = { {
235- padding : 0 , width : '1px' , margin : 'auto' , height : '100%' ,
239+ padding : 0 , width : '1px' , margin : 'auto' ,
236240 //borderLeft: 'thin solid lightgray',
237241 // the following for
238242 backgroundImage : 'linear-gradient(180deg, darkgray, darkgray 75%, transparent 75%, transparent 100%)' ,
@@ -252,31 +256,9 @@ let SingleThreadView: FC<{
252256 content = w ( tableList , triggerCards , "" )
253257
254258 return < Box sx = { {
255- backgroundColor : ( threadIdx % 2 == 1 ? "rgba(0, 0, 0, 0.02)" : 'white' ) , //threadIsFocused ? alpha(theme.palette.primary.main, 0.05) :
256- padding : '8px 8px'
257- } } >
258- { /* <Tooltip title={collapsed ? 'expand' : 'collapse'}>
259- <Button fullWidth sx={{display: 'flex', direction: 'ltr'}} color="primary" onClick={() => setCollapsed(!collapsed)}>
260- <Divider flexItem sx={{
261- "& .MuiDivider-wrapper": {
262- display: 'flex', flexDirection: 'row',
263- },
264- "&::before, &::after": {
265- borderColor: theme.palette.primary.light,
266- opacity: 0.5,
267- borderWidth: '4px',
268- width: 50,
269-
270- },
271- }}
272- >
273- <Typography sx={{fontSize: "10px", fontWeight: 'bold', textTransform: 'none'}}>
274- {`thread - ${threadIdx + 1}`}
275- </Typography>
276- {!collapsed ? <ExpandLess sx={{fontSize: 14}}/> : <ExpandMore sx={{fontSize: 14}}/> }
277- </Divider>
278- </Button>
279- </Tooltip>*/ }
259+
260+ ...sx
261+ } } data-thread-index = { threadIdx } >
280262 < Box sx = { { display : 'flex' , direction : 'ltr' , margin : 1 } } >
281263 < Divider flexItem sx = { {
282264 margin : 'auto' ,
@@ -462,41 +444,129 @@ export const DataThread: FC<{}> = function ({ }) {
462444 let refTables = tables ;
463445 let leafTables = refTables . filter ( t => ! refTables . some ( t2 => t2 . derive ?. trigger . tableId == t . id ) ) ;
464446
465-
466447 let drawerOpen = leafTables . length > 1 && threadDrawerOpen ;
467-
468- let view = < Box sx = { { width : '100%' , margin : "0px 0px 8px 0px" , display : 'flex' , flexDirection : drawerOpen ? 'row-reverse' : 'column' , paddingBottom : 2 } } >
448+ let threadDrawerWidth = Math . max ( Math . min ( 600 , leafTables . length * 200 ) , 212 )
449+
450+ let view = < Box width = { drawerOpen ? threadDrawerWidth + 12 : 224 } sx = { {
451+ overflowY : 'auto' ,
452+ position : 'relative' ,
453+ display : 'flex' ,
454+ flexDirection : drawerOpen ? 'row-reverse' : 'column' ,
455+ } } >
469456 { leafTables . map ( ( lt , i ) => {
470457 let usedTableIds = leafTables . slice ( 0 , i )
471458 . map ( x => [ x . id , ...getTriggers ( x , tables ) . map ( y => y . tableId ) || [ ] ] ) . flat ( ) ;
472459 return < SingleThreadView
473460 key = { `thread-${ lt . id } ` }
474- scrollRef = { scrollRef } threadIdx = { i } leafTable = { lt } chartElements = { chartElements } usedTableIds = { usedTableIds } />
461+ scrollRef = { scrollRef }
462+ threadIdx = { i }
463+ leafTable = { lt }
464+ chartElements = { chartElements }
465+ usedTableIds = { usedTableIds }
466+ sx = { {
467+ backgroundColor : ( i % 2 == 1 ? "rgba(0, 0, 0, 0.02)" : 'white' ) ,
468+ padding : '8px 8px' ,
469+ flex : 1 ,
470+ display : 'flex' ,
471+ flexDirection : 'column' ,
472+ height : '100%'
473+ } } />
475474 } ) }
476475 </ Box >
477476
478- let threadDrawerWidth = Math . max ( Math . min ( Math . max ( 600 , window . innerWidth * 0.8 ) , leafTables . length * 200 ) , 212 )
477+
478+ let jumpButtonsDrawerOpen = < ButtonGroup size = "small" color = "primary" >
479+ { _ . chunk ( Array . from ( { length : leafTables . length } , ( _ , i ) => i ) , 3 ) . map ( ( group , groupIdx ) => {
480+ const startNum = group [ 0 ] + 1 ;
481+ const endNum = group [ group . length - 1 ] + 1 ;
482+ const label = startNum === endNum ? `${ startNum } ` : `${ startNum } -${ endNum } ` ;
483+
484+ return (
485+ < Tooltip key = { `thread-nav-group-${ groupIdx } ` } title = { `Jump to thread${ startNum === endNum ? '' : 's' } ${ label } ` } >
486+ < IconButton
487+ size = "small"
488+ color = "primary"
489+ sx = { { fontSize : '12px' } }
490+ onClick = { ( ) => {
491+ setTimeout ( ( ) => {
492+ // Get currently most visible thread index
493+ const viewportCenter = window . innerWidth / 2 ;
494+ const currentIndex = Array . from ( document . querySelectorAll ( '[data-thread-index]' ) ) . reduce ( ( closest , element ) => {
495+ const rect = element . getBoundingClientRect ( ) ;
496+ const distance = Math . abs ( rect . left + rect . width / 2 - viewportCenter ) ;
497+ if ( ! closest || distance < closest . distance ) {
498+ return { index : parseInt ( element . getAttribute ( 'data-thread-index' ) || '0' ) , distance } ;
499+ }
500+ return closest ;
501+ } , null as { index : number , distance : number } | null ) ?. index || 0 ;
502+
503+ // If moving from larger to smaller numbers (scrolling left), target first element
504+ // If moving from smaller to larger numbers (scrolling right), target last element
505+ const targetIndex = currentIndex > group [ 0 ] ? group [ 0 ] : group [ group . length - 1 ] ;
506+
507+ const targetElement = document . querySelector ( `[data-thread-index="${ targetIndex } "]` ) ;
508+ if ( targetElement ) {
509+ targetElement . scrollIntoView ( {
510+ behavior : 'smooth' ,
511+ block : 'nearest' , // Don't change vertical scroll
512+ inline : currentIndex > group [ group . length - 1 ] ? 'start' : 'end'
513+ } ) ;
514+ }
515+ } , 100 ) ;
516+ } }
517+ >
518+ { label }
519+ </ IconButton >
520+ </ Tooltip >
521+ ) ;
522+ } ) }
523+ </ ButtonGroup >
524+
525+ let jumpButtonDrawerClosed = < ButtonGroup size = "small" color = "primary" sx = { { gap : 0 } } >
526+ { leafTables . map ( ( _ , idx ) => (
527+ < Tooltip key = { `thread-nav-${ idx } ` } title = { `Jump to thread ${ idx + 1 } ` } >
528+ < IconButton
529+ size = "small"
530+ color = "primary"
531+ sx = { { fontSize : '12px' , padding : '4px' } }
532+ onClick = { ( ) => {
533+ const threadElement = document . querySelector ( `[data-thread-index="${ idx } "]` ) ;
534+ threadElement ?. scrollIntoView ( { behavior : 'smooth' } ) ;
535+ } }
536+ >
537+ { idx + 1 }
538+ </ IconButton >
539+ </ Tooltip >
540+ ) ) }
541+ </ ButtonGroup >
542+
543+ let jumpButtons = drawerOpen ? jumpButtonsDrawerOpen : jumpButtonDrawerClosed ;
544+
479545
480546 let carousel = (
481547 < Box className = "data-thread" sx = { { overflow : 'hidden' , } } >
482548 < Box sx = { {
483549 direction : 'ltr' , display : 'flex' ,
484550 paddingTop : "10px" , paddingLeft : '12px' , alignItems : 'center' , justifyContent : 'space-between'
485551 } } >
486- < Typography className = "view-title" component = "h2" sx = { { marginTop : "6px" } } >
487- Data Threads
488- </ Typography >
552+ < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 1 } } >
553+ < Typography className = "view-title" component = "h2" sx = { { marginTop : "6px" } } >
554+ Data Threads
555+ </ Typography >
556+ { jumpButtons }
557+ </ Box >
558+
489559 < Tooltip title = { drawerOpen ? "collapse" : "expand" } >
490560 < IconButton size = { 'small' } color = "primary" disabled = { leafTables . length <= 1 } onClick = { ( ) => { setThreadDrawerOpen ( ! threadDrawerOpen ) ; } } >
491561 { drawerOpen ? < ChevronLeftIcon /> : < ChevronRightIcon /> }
492562 </ IconButton >
493563 </ Tooltip >
494564 </ Box >
495565 < Box sx = { {
496- transition : 'width 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms' , overflow : 'auto' ,
497- direction : 'rtl' , display : 'flex ' , flex : 1
566+ transition : 'width 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms' , overflowY : 'auto' ,
567+ direction : 'rtl' , display : 'block ' , flex : 1
498568 } }
499- width = { drawerOpen ? threadDrawerWidth + 12 : 224 } className = "thread-view-mode" >
569+ className = "thread-view-mode" >
500570 { view }
501571 </ Box >
502572 </ Box >
0 commit comments