@@ -236,8 +236,14 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
236236 this . canvas = new TimelineCanvas ( this ) ;
237237 this . canvas . appendTo ( this . $centerContainer ) ;
238238 this . timeline . on ( "changed" , ( ) => {
239- this . draw_canvas ( ) ;
240239 this . load_initial_data ( ) ;
240+ // Defer drawing until after DOM settles (e.g., after group collapse/expand)
241+ const draw = ( ) => this . draw_canvas ( ) ;
242+ if ( typeof window !== "undefined" && window . requestAnimationFrame ) {
243+ window . requestAnimationFrame ( draw ) ;
244+ } else {
245+ setTimeout ( draw , 0 ) ;
246+ }
241247 } ) ;
242248 } ,
243249
@@ -269,12 +275,17 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
269275 const item = items [ key ] ;
270276 const data = datas . get ( key ) ;
271277 if ( ! data || ! data . evt ) {
272- return ;
278+ continue ; // Skip items without data or event payload
279+ }
280+ const deps = data . evt [ this . dependency_arrow ] ;
281+ if ( ! Array . isArray ( deps ) || deps . length === 0 ) {
282+ continue ;
273283 }
274- for ( const id of data . evt [ this . dependency_arrow ] ) {
284+ for ( const id of deps ) {
275285 for ( const k of keys ) {
276286 if ( k . split ( "_" ) [ 0 ] . toString ( ) === id . toString ( ) ) {
277- this . draw_dependency ( item , items [ k ] ) ;
287+ const toItem = items [ k ] ;
288+ this . draw_dependency ( item , toItem ) ;
278289 }
279290 }
280291 }
@@ -292,9 +303,25 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
292303 * @private
293304 */
294305 draw_dependency : function ( from , to , options ) {
306+ // Skip if any item is missing, hidden, or its DOM node is not available/attached
307+ if ( ! from || ! to ) {
308+ return ;
309+ }
295310 if ( ! from . displayed || ! to . displayed ) {
296311 return ;
297312 }
313+ if ( ! from . dom || ! to . dom || ! from . dom . box || ! to . dom . box ) {
314+ return ;
315+ }
316+ // In some cases after collapsing a group, items can be logically displayed
317+ // but their DOM nodes are detached. Guard against that situation.
318+ if (
319+ typeof document !== "undefined" &&
320+ ( ! document . body . contains ( from . dom . box ) ||
321+ ! document . body . contains ( to . dom . box ) )
322+ ) {
323+ return ;
324+ }
298325 const defaults = _ . defaults ( { } , options , {
299326 line_color : "black" ,
300327 line_width : 1 ,
0 commit comments