diff --git a/js/ui/BUILD.bazel b/js/ui/BUILD.bazel index 5e2858d..789245d 100644 --- a/js/ui/BUILD.bazel +++ b/js/ui/BUILD.bazel @@ -88,6 +88,7 @@ closure_js_library( "@stackb_rules_closure//closure/goog/dom:dataset", "@stackb_rules_closure//closure/goog/dom:tagname", "@stackb_rules_closure//closure/goog/events:event", + "@stackb_rules_closure//closure/goog/ui:component", ], ) diff --git a/js/ui/route.js b/js/ui/route.js index 6c66732..ba5c47f 100644 --- a/js/ui/route.js +++ b/js/ui/route.js @@ -140,13 +140,6 @@ class Route extends EventTarget { return this.path_.slice(0, this.index_); } - // /** Peek at the next path segment. - // * @return {string} - // */ - // next() { - // return this.path_[this.index_ + 1]; - // } - /** Get the current path segment. * @return {string} */ diff --git a/js/ui/select.js b/js/ui/select.js index 7e61321..2217158 100644 --- a/js/ui/select.js +++ b/js/ui/select.js @@ -3,6 +3,7 @@ */ goog.module('stack.ui.Select'); +const ComponentEventType = goog.require('goog.ui.Component.EventType'); const TabEvent = goog.require('stack.ui.TabEvent'); const Template = goog.require('stack.ui.Template'); const asserts = goog.require('goog.asserts'); @@ -97,7 +98,12 @@ class Select extends Component { showTab(name) { var tab = this.getTab(name); if (tab) { - this.hideCurrent(); + // Don't tear down the current tab when re-selecting it (e.g. + // navigating from /modules/foo/0.5.4 to /modules/foo/0.5.5 — at the + // outer BodySelect level the tab name is still "modules"). + if (this.current_ !== name) { + this.hideCurrent(); + } this.current_ = name; //var path = this.getPath(); //path.push(name); @@ -227,19 +233,34 @@ class Select extends Component { } /** - * Hide the current tab and make it the previous. + * Hide the current tab and make it the previous. The outgoing tab is + * removed from this Select's children, detached from the DOM, and + * disposed — so its component lifecycle reaches `exitDocument` and + * `disposeInternal`, releasing event handlers and any custom state. + * Subscribers that key off the tab name (e.g. SelectNav highlights) get + * a final HIDE event before the element goes away. + * + * Tabs are recreated on demand by `selectFail` if the user navigates + * back to them. + * * @return {?Component} */ hideCurrent() { - var prev = null; - if (this.current_) { - this.prev_ = this.current_; - prev = this.getTab(this.prev_); - if (prev) { - prev.hide(); - } + if (!this.current_) { + return null; } + const name = this.current_; + const prev = this.getTab(name); + this.prev_ = name; this.current_ = null; + if (prev) { + // Notify listeners (e.g. SelectNav) before the element is gone. + prev.dispatchEvent(ComponentEventType.HIDE); + // removeChild(c, true) detaches the element and fires exitDocument. + this.removeChild(prev, true); + delete this.name2id_[name]; + prev.dispose(); + } return prev; }