diff --git a/app/assets/javascripts/discourse/app/components/mount-widget.js b/app/assets/javascripts/discourse/app/components/mount-widget.js index 3701b32d00f..1a91e573afb 100644 --- a/app/assets/javascripts/discourse/app/components/mount-widget.js +++ b/app/assets/javascripts/discourse/app/components/mount-widget.js @@ -2,7 +2,7 @@ import { cancel, scheduleOnce } from "@ember/runloop"; import Component from "@ember/component"; import { diff, patch } from "virtual-dom"; import { WidgetClickHook } from "discourse/widgets/hooks"; -import { queryRegistry } from "discourse/widgets/widget"; +import { queryRegistry, traverseCustomWidgets } from "discourse/widgets/widget"; import { getRegister } from "discourse-common/lib/get-owner"; import DirtyKeys from "discourse/lib/dirty-keys"; import { camelize } from "@ember/string"; @@ -124,12 +124,18 @@ export default Component.extend({ newTree._emberView = this; const patches = diff(this._tree || this._rootNode, newTree); + if (this._tree) { + traverseCustomWidgets(this._tree, w => w.willRerenderWidget()); + } + this.beforePatch(); this._rootNode = patch(this._rootNode, patches); this.afterPatch(); this._tree = newTree; + traverseCustomWidgets(newTree, w => w.didRenderWidget()); + if (this._renderCallback) { this._renderCallback(); this._renderCallback = null; diff --git a/app/assets/javascripts/discourse/app/widgets/glue.js b/app/assets/javascripts/discourse/app/widgets/glue.js index 976aed9a311..408879a6f69 100644 --- a/app/assets/javascripts/discourse/app/widgets/glue.js +++ b/app/assets/javascripts/discourse/app/widgets/glue.js @@ -1,6 +1,6 @@ import { cancel, scheduleOnce } from "@ember/runloop"; import { diff, patch } from "virtual-dom"; -import { queryRegistry } from "discourse/widgets/widget"; +import { queryRegistry, traverseCustomWidgets } from "discourse/widgets/widget"; import DirtyKeys from "discourse/lib/dirty-keys"; import { isTesting } from "discourse-common/config/environment"; @@ -47,23 +47,19 @@ export default class WidgetGlue { }); const patches = diff(this._tree || this._rootNode, newTree); + if (this._tree) { + traverseCustomWidgets(this._tree, w => w.willRerenderWidget()); + } + newTree._rerenderable = this; this._rootNode = patch(this._rootNode, patches); this._tree = newTree; + + traverseCustomWidgets(newTree, w => w.didRenderWidget()); } cleanUp() { - const widgets = []; - const findWidgets = widget => { - widget.vnode.children.forEach(child => { - if (child.constructor.name === "CustomWidget") { - widgets.push(child); - findWidgets(child); - } - }); - }; - findWidgets(this._tree); - widgets.reverse().forEach(widget => widget.destroy()); + traverseCustomWidgets(this._tree, w => w.destroy()); cancel(this._timeout); } diff --git a/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js b/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js index 7992daf1986..3e447362f82 100644 --- a/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js +++ b/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js @@ -1,6 +1,5 @@ import I18n from "I18n"; import { createWidget } from "discourse/widgets/widget"; -import { schedule } from "@ember/runloop"; import hbs from "discourse/widgets/hbs-compiler"; /* @@ -239,40 +238,45 @@ export const WidgetDropdownClass = { } }, - _onTrigger() { - this.state.opened = !this.state.opened; + willRerenderWidget() { + this._popper && this._popper.destroy(); + }, - schedule("afterRender", () => { + didRenderWidget() { + if (this.state.opened) { const dropdownHeader = document.querySelector( `#${this.attrs.id} .widget-dropdown-header` ); + + if (!dropdownHeader) return; + const dropdownBody = document.querySelector( `#${this.attrs.id} .widget-dropdown-body` ); - if (this.state.opened && dropdownHeader && dropdownBody) { - if (this.state.popper) { - this.state.popper.destroy(); - } + if (!dropdownBody) return; - /* global Popper:true */ - this.state.popper = Popper.createPopper(dropdownHeader, dropdownBody, { - strategy: "fixed", - placement: "bottom-start", - modifiers: [ - { - name: "preventOverflow" - }, - { - name: "offset", - options: { - offset: [0, 5] - } + /* global Popper:true */ + this._popper = Popper.createPopper(dropdownHeader, dropdownBody, { + strategy: "fixed", + placement: "bottom-start", + modifiers: [ + { + name: "preventOverflow" + }, + { + name: "offset", + options: { + offset: [0, 5] } - ] - }); - } - }); + } + ] + }); + } + }, + + _onTrigger() { + this.state.opened = !this.state.opened; }, template: hbs` diff --git a/app/assets/javascripts/discourse/app/widgets/widget.js b/app/assets/javascripts/discourse/app/widgets/widget.js index ad54150b33b..4a2f77022fd 100644 --- a/app/assets/javascripts/discourse/app/widgets/widget.js +++ b/app/assets/javascripts/discourse/app/widgets/widget.js @@ -36,6 +36,16 @@ export function decorateWidget(widgetName, cb) { _decorators[widgetName].push(cb); } +export function traverseCustomWidgets(tree, callback) { + if (tree.constructor.name === "CustomWidget") { + callback(tree); + } + + (tree.children || (tree.vnode ? tree.vnode.children : [])).forEach(node => { + traverseCustomWidgets(node, callback); + }); +} + export function applyDecorators(widget, type, attrs, state) { const decorators = _decorators[`${widget.name}:${type}`] || []; @@ -257,6 +267,10 @@ export default class Widget { } } + didRenderWidget() {} + + willRerenderWidget() {} + scheduleRerender() { let widget = this; while (widget) {