@@ -4,6 +4,7 @@ import {Container} from "@bokehjs/core/layout/grid"
44import type * as p from "@bokehjs/core/properties"
55import { GridAlignmentLayout } from "@bokehjs/models/layouts/alignments"
66import { LayoutDOMView } from "@bokehjs/models/layouts/layout_dom"
7+ import type { UIElementView } from "@bokehjs/models/ui/ui_element"
78
89import { Column , ColumnView } from "./column"
910
@@ -22,6 +23,9 @@ export class CardView extends ColumnView {
2223
2324 button_el : HTMLButtonElement
2425 header_el : HTMLElement
26+ visible_child_views : Map < UIElementView , boolean > = new Map ( )
27+ protected _updating_child_visibility : Set < UIElementView > = new Set ( )
28+ protected _child_visible_callbacks : Map < UIElementView , ( ) => void > = new Map ( )
2529
2630 readonly collapsed_style = new DOM . InlineStyleSheet ( )
2731
@@ -40,6 +44,10 @@ export class CardView extends ColumnView {
4044 this . child_views [ 0 ] . el . style . backgroundColor = header_background
4145 this . header_el . style . backgroundColor = header_background
4246 } )
47+
48+ for ( const child_view of this . child_views . slice ( 1 ) ) {
49+ this . _register_child_view ( child_view )
50+ }
4351 }
4452
4553 override stylesheets ( ) : StyleSheetLike [ ] {
@@ -106,11 +114,9 @@ export class CardView extends ColumnView {
106114 header . r_after_render ( )
107115 }
108116
109- if ( this . model . collapsed ) {
110- return
111- }
112-
113117 for ( const child_view of this . child_views . slice ( 1 ) ) {
118+ this . _register_child_view ( child_view )
119+ this . _apply_child_visible ( child_view )
114120 this . shadow_el . appendChild ( child_view . el )
115121 child_view . render ( )
116122 child_view . r_after_render ( )
@@ -119,6 +125,15 @@ export class CardView extends ColumnView {
119125
120126 override async update_children ( ) : Promise < void > {
121127 await this . build_child_views ( )
128+ const child_views = new Set ( this . child_views . slice ( 1 ) )
129+ for ( const child_view of this . visible_child_views . keys ( ) ) {
130+ if ( ! child_views . has ( child_view ) ) {
131+ this . _unregister_child_view ( child_view )
132+ }
133+ }
134+ for ( const child_view of child_views ) {
135+ this . _register_child_view ( child_view )
136+ }
122137 this . render ( )
123138 this . invalidate_layout ( )
124139 }
@@ -210,14 +225,15 @@ export class CardView extends ColumnView {
210225
211226 _collapse ( ) : void {
212227 for ( const child_view of this . child_views . slice ( 1 ) ) {
228+ this . _register_child_view ( child_view )
213229 if ( this . model . collapsed ) {
214230 this . shadow_el . removeChild ( child_view . el )
215- child_view . model . visible = false
231+ this . _set_child_visible ( child_view , false )
216232 } else {
217233 child_view . render ( )
218234 child_view . after_render ( )
219235 this . shadow_el . appendChild ( child_view . el )
220- child_view . model . visible = true
236+ this . _apply_child_visible ( child_view )
221237 }
222238 }
223239 if ( this . model . collapsed ) {
@@ -232,6 +248,54 @@ export class CardView extends ColumnView {
232248 this . invalidate_layout ( )
233249 }
234250
251+ protected _set_child_visible ( child_view : UIElementView , visible : boolean ) : void {
252+ if ( child_view . model . visible == visible ) {
253+ return
254+ }
255+ this . _updating_child_visibility . add ( child_view )
256+ try {
257+ child_view . model . visible = visible
258+ } finally {
259+ this . _updating_child_visibility . delete ( child_view )
260+ }
261+ }
262+
263+ protected _apply_child_visible ( child_view : UIElementView ) : void {
264+ const desired_visible = this . visible_child_views . get ( child_view ) ?? child_view . model . visible
265+ this . visible_child_views . set ( child_view , desired_visible )
266+ this . _set_child_visible ( child_view , desired_visible && ! this . model . collapsed )
267+ }
268+
269+ protected _register_child_view ( child_view : UIElementView ) : void {
270+ if ( this . visible_child_views . has ( child_view ) ) {
271+ return
272+ }
273+ this . visible_child_views . set ( child_view , child_view . model . visible )
274+ const { visible} = child_view . model . properties
275+ const callback = ( ) => {
276+ if ( this . _updating_child_visibility . has ( child_view ) ) {
277+ return
278+ }
279+ const desired_visible = child_view . model . visible
280+ this . visible_child_views . set ( child_view , desired_visible )
281+ if ( this . model . collapsed && desired_visible ) {
282+ this . _set_child_visible ( child_view , false )
283+ }
284+ }
285+ this . _child_visible_callbacks . set ( child_view , callback )
286+ this . on_change ( visible , callback )
287+ }
288+
289+ protected _unregister_child_view ( child_view : UIElementView ) : void {
290+ const callback = this . _child_visible_callbacks . get ( child_view )
291+ if ( callback != null ) {
292+ child_view . model . properties . visible . change . disconnect ( callback )
293+ this . _child_visible_callbacks . delete ( child_view )
294+ }
295+ this . visible_child_views . delete ( child_view )
296+ this . _updating_child_visibility . delete ( child_view )
297+ }
298+
235299 protected override _create_element ( ) : HTMLElement {
236300 return DOM . create_element ( ( this . model . tag as any ) , { class : this . css_classes ( ) } )
237301 }
0 commit comments