Files
tubestation/browser/components/tabpreview/panel-wrapped-element.mjs

106 lines
3.9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
/**
* MozPanelWrappedElement bridges XULPanel's imperative API with a more
* Lit-friendly declarative option. Lit components whose display and positioning
* should be handled by a XUL panel can extend this class to control this behavior
* declaratively.
*
* The panel can be shown or hidden by changing this.panelState, and repositioned
* by changing this.panelAnchor.
*
* NOTE: This likely won't work correctly unless the class that inherits
* MozPanelWrappedElement is at the top of the Lit component stack, hence the need
* to extend this class instead of just wrapping the component tree with something.
*
* @property {string} panelID ID attribute to use for the panel element
* @property {"open"|"closed"} panelState controls whether panel is showing
* @property {HTMLElement} panelAnchor element that the panel should anchor to
* @property {boolean} noautofocus sets the panel's noautofocus attribute
* @property {boolean} norolluponanchor sets the panel's norolluponanchor attribute
* @property {boolean} rolluponmousewheel sets the panel's rolluponmousewheel attribute
* @property {boolean} consumeoutsideclicks sets the panel's consumeoutsideclicks attribute
* @property {boolean} level sets the panel's level attribute
*/
export class MozPanelWrappedElement extends MozLitElement {
static properties = {
panelID: { type: String },
panelState: { type: String },
panelAnchor: { type: HTMLElement },
noautofocus: { type: Boolean },
norolluponanchor: { type: Boolean },
rolluponmousewheel: { type: Boolean },
consumeoutsideclicks: { type: String },
level: { type: String },
};
/**
* Override this in a child class to do something when the popup is shown
*/
on_popupshown() {}
/**
* Override this in a child class to do something when the popup is hidden
*/
on_popuphidden() {}
createRenderRoot() {
if (!document.createXULElement) {
console.error(
"Unable to create panel: document.createXULElement is not available"
);
return super.createRenderRoot();
}
this.attachShadow({ mode: "open" });
this._panel = document.createXULElement("panel");
this._panel.setAttribute("id", "tabpreviewpanel");
this.syncPanelAttributes();
this._panel.addEventListener("popupshown", this.on_popupshown.bind(this));
this._panel.addEventListener("popuphidden", this.on_popuphidden.bind(this));
document.body.appendChild(this._panel);
this.shadowRoot.append(this._panel);
return this._panel;
}
syncPanelAttributes() {
this._panel.setAttribute("noautofocus", this.noautofocus);
this._panel.setAttribute("norolluponanchor", this.norolluponanchor);
this._panel.setAttribute(
"consumeoutsideclicks",
this.consumeoutsideclicks.toString()
);
this._panel.setAttribute("rolluponmousewheel", this.rolluponmousewheel);
this._panel.setAttribute("level", this.level);
}
updated(changedProperties) {
this.syncPanelAttributes();
if (changedProperties.has("panelState")) {
if (
this.panelState === "open" &&
!["open", "showing"].includes(this._panel.state)
) {
this._panel.openPopup(this.panelAnchor, {
position: "bottomleft topleft",
y: -2,
isContextMenu: false,
});
} else if (
this.panelState === "closed" &&
!["closed", "hiding"].includes(this._panel.state)
) {
this._panel.hidePopup();
}
} else if (
this.panelState === "open" &&
changedProperties.has("panelAnchor")
) {
this._panel.moveToAnchor(this.panelAnchor, "bottomleft topleft", 0, -2);
}
}
}