@@ -12,6 +12,8 @@ import { eachDayOfInterval, isMonday, differenceInDays } from 'date-fns';
1212import { getMonday } from '../../../utils/datetime.utils' ;
1313import { toDateString } from 'shared' ;
1414import { GANTT_CHART_CELL_SIZE , GANTT_CHART_GAP_SIZE } from '../../../utils/gantt.utils' ;
15+ import { useRef , useCallback , useEffect } from 'react' ;
16+
1517export interface GanttEditability < E , T > {
1618 highlightTaskComparator : HighlightTaskComparator < T > ;
1719 highlightSubtaskComparator : HighlightTaskComparator < T > ;
@@ -38,38 +40,103 @@ const GanttChart = <E, T>({ startDate, endDate, collections, editability }: Gant
3840
3941 const today = new Date ( new Date ( ) . setHours ( 0 , 0 , 0 , 0 ) ) ;
4042 const currentWeekCol = days . findIndex ( ( day ) => toDateString ( day ) === toDateString ( getMonday ( today ) ) ) + 1 ;
41-
4243 const daysIntoWeek = differenceInDays ( today , getMonday ( today ) ) ;
4344 const dailyOffset = daysIntoWeek * ( parseFloat ( GANTT_CHART_CELL_SIZE ) / 7 ) ;
4445
46+ const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
47+
48+ const validCollections = collections . filter ( ( c ) => c . tasks ) ;
49+
50+ const sectionDataRef = useRef <
51+ { sectionEl : HTMLDivElement | null ; placeholderEl : HTMLDivElement | null ; height : number } [ ]
52+ > ( [ ] ) ;
53+
54+ if ( sectionDataRef . current . length !== validCollections . length ) {
55+ sectionDataRef . current = validCollections . map ( ( _ , i ) => ( {
56+ sectionEl : sectionDataRef . current [ i ] ?. sectionEl ?? null ,
57+ placeholderEl : sectionDataRef . current [ i ] ?. placeholderEl ?? null ,
58+ height : sectionDataRef . current [ i ] ?. height ?? 0
59+ } ) ) ;
60+ }
61+
62+ const updateVisibility = useCallback ( ( ) => {
63+ const container = scrollContainerRef . current ;
64+ if ( ! container ) return ;
65+
66+ const containerRect = container . getBoundingClientRect ( ) ;
67+ const viewportBottom = containerRect . bottom ;
68+
69+ for ( const data of sectionDataRef . current ) {
70+ const { sectionEl, placeholderEl } = data ;
71+ if ( ! sectionEl || ! placeholderEl ) continue ;
72+
73+ const el = sectionEl . style . display === 'none' ? placeholderEl : sectionEl ;
74+ const elTop = el . getBoundingClientRect ( ) . top ;
75+ const measuredHeight = sectionEl . offsetHeight || placeholderEl . offsetHeight ;
76+
77+ const isVisible = elTop <= viewportBottom ;
78+
79+ sectionEl . style . display = isVisible ? '' : 'none' ;
80+ placeholderEl . style . display = isVisible ? 'none' : '' ;
81+ placeholderEl . style . height = `${ measuredHeight } px` ;
82+ }
83+ } , [ ] ) ;
84+
85+ useEffect ( ( ) => {
86+ const container = scrollContainerRef . current ;
87+ if ( ! container ) return ;
88+
89+ container . addEventListener ( 'scroll' , updateVisibility , { passive : true } ) ;
90+ // Also re-check on container resize
91+ const ro = new ResizeObserver ( updateVisibility ) ;
92+ ro . observe ( container ) ;
93+ updateVisibility ( ) ; // initial pass
94+
95+ return ( ) => {
96+ container . removeEventListener ( 'scroll' , updateVisibility ) ;
97+ ro . disconnect ( ) ;
98+ } ;
99+ } , [ updateVisibility ] ) ;
100+
45101 return (
46102 < Box
103+ ref = { scrollContainerRef }
47104 sx = { {
48105 width : '100%' ,
49- height : { xs : 'calc(100vh - 9.5rem )' , md : 'calc(100vh - 6.25rem)' } ,
106+ height : { xs : 'calc(100vh - 9.5rem)' , md : 'calc(100vh - 6.25rem)' } ,
50107 overflow : 'scroll' ,
51108 position : 'relative' ,
52- '&::-webkit-scrollbar' : {
53- display : 'none'
54- } ,
55- scrollbarWidth : 'none' , // Firefox
56- msOverflowStyle : 'none' // IE and Edge
109+ '&::-webkit-scrollbar' : { display : 'none' } ,
110+ scrollbarWidth : 'none' ,
111+ msOverflowStyle : 'none'
57112 } }
58113 >
59114 < GanttChartTimeline start = { startDate } end = { endDate } />
60115 < Box sx = { { position : 'relative' } } >
61- { collections . map ( ( collection ) => {
62- return collection . tasks ? (
63- < GanttChartCollectionSection
64- startDate = { startDate }
65- endDate = { endDate }
66- collection = { collection }
67- editability = { editability }
116+ { validCollections . map ( ( collection , idx ) => (
117+ < Box key = { idx } >
118+ { /* Real */ }
119+ < Box
120+ ref = { ( el : HTMLDivElement | null ) => {
121+ if ( sectionDataRef . current [ idx ] ) sectionDataRef . current [ idx ] . sectionEl = el ;
122+ } }
123+ >
124+ < GanttChartCollectionSection
125+ startDate = { startDate }
126+ endDate = { endDate }
127+ collection = { collection }
128+ editability = { editability }
129+ />
130+ </ Box >
131+ { /* Placeholder */ }
132+ < Box
133+ ref = { ( el : HTMLDivElement | null ) => {
134+ if ( sectionDataRef . current [ idx ] ) sectionDataRef . current [ idx ] . placeholderEl = el ;
135+ } }
136+ style = { { display : 'none' , height : 0 } }
68137 />
69- ) : (
70- < > </ >
71- ) ;
72- } ) }
138+ </ Box >
139+ ) ) }
73140
74141 { currentWeekCol > 0 && (
75142 < Box
0 commit comments