Get the reusable foundations of custom popovers finished.
This commit is contained in:
parent
4b75526708
commit
e594b6beb0
8 changed files with 219 additions and 63 deletions
134
module/utils/PopoverEventManager.mjs
Normal file
134
module/utils/PopoverEventManager.mjs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import { getTooltipDelay } from "../consts.mjs";
|
||||
import { Logger } from "./Logger.mjs";
|
||||
|
||||
export class PopoverEventManager {
|
||||
#options;
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element The element to attach the listeners to.
|
||||
* @param {GenericPopoverMixin} popoverClass The class reference that represents the popover app
|
||||
*/
|
||||
constructor(element, popoverClass, options = {}) {
|
||||
options.locked ??= false;
|
||||
options.lockable ??= true;
|
||||
|
||||
this.#options = options;
|
||||
this.#element = element;
|
||||
this.#class = popoverClass;
|
||||
|
||||
element.addEventListener(`pointerenter`, this.#pointerEnterHandler.bind(this));
|
||||
element.addEventListener(`pointerout`, this.#pointerOutHandler.bind(this));
|
||||
element.addEventListener(`click`, this.#clickHandler.bind(this));
|
||||
|
||||
if (options.lockable) {
|
||||
element.addEventListener(`pointerup`, this.#pointerUpHandler.bind(this));
|
||||
};
|
||||
};
|
||||
|
||||
destroy() {
|
||||
this.close();
|
||||
this.#element.removeEventListener(`pointerenter`, this.#pointerEnterHandler);
|
||||
this.#element.removeEventListener(`pointerout`, this.#pointerOutHandler);
|
||||
this.#element.removeEventListener(`click`, this.#clickHandler);
|
||||
if (this.#options.lockable) {
|
||||
this.#element.removeEventListener(`pointerup`, this.#pointerUpHandler);
|
||||
};
|
||||
this.stopOpen();
|
||||
this.stopClose();
|
||||
};
|
||||
|
||||
close() {
|
||||
this.#frameless?.close({ force: true });
|
||||
this.#framed?.close({ force: true });
|
||||
};
|
||||
|
||||
stopOpen() {
|
||||
if (this.#openTimeout != null) {
|
||||
clearTimeout(this.#openTimeout);
|
||||
this.#openTimeout = null;
|
||||
};
|
||||
};
|
||||
|
||||
stopClose() {
|
||||
if (this.#closeTimeout != null) {
|
||||
clearTimeout(this.#closeTimeout);
|
||||
this.#closeTimeout = null;
|
||||
}
|
||||
};
|
||||
|
||||
#element;
|
||||
#class;
|
||||
#openTimeout = null;
|
||||
#closeTimeout = null;
|
||||
|
||||
#frameless;
|
||||
#framed;
|
||||
|
||||
#clickHandler() {
|
||||
// Cleanup for the frameless lifecycle
|
||||
this.stopOpen();
|
||||
this.#frameless?.close({ force: true });
|
||||
|
||||
if (!this.#framed) {
|
||||
const app = new this.#class({ popover: { ...this.#options, framed: true } });
|
||||
this.#framed = app;
|
||||
}
|
||||
this.#framed.render({ force: true });
|
||||
};
|
||||
|
||||
#pointerEnterHandler(event) {
|
||||
this.stopClose();
|
||||
|
||||
const pos = event.target.getBoundingClientRect();
|
||||
const x = pos.x + Math.floor(pos.width / 2);
|
||||
const y = pos.y;
|
||||
|
||||
this.#openTimeout = setTimeout(
|
||||
() => {
|
||||
this.#openTimeout = null;
|
||||
|
||||
// When we have the framed version rendered, we might as well just focus
|
||||
// it instead of rendering a new application
|
||||
if (this.#framed?.rendered) {
|
||||
this.#framed.bringToFront();
|
||||
return;
|
||||
};
|
||||
|
||||
if (this.#frameless?.rendered) {
|
||||
const { width, height } = this.#frameless.position;
|
||||
this.#frameless.render({ position: { left: x - Math.floor(width / 2), top: y - height }});
|
||||
return;
|
||||
}
|
||||
|
||||
this.#frameless = new this.#class({
|
||||
popover: {
|
||||
...this.#options,
|
||||
framed: false,
|
||||
x, y,
|
||||
},
|
||||
});
|
||||
this.#frameless.render({ force: true });
|
||||
},
|
||||
getTooltipDelay(),
|
||||
);
|
||||
};
|
||||
|
||||
#pointerOutHandler() {
|
||||
this.stopOpen();
|
||||
|
||||
this.#closeTimeout = setTimeout(
|
||||
() => {
|
||||
this.#closeTimeout = null;
|
||||
this.#frameless?.close();
|
||||
},
|
||||
getTooltipDelay(),
|
||||
);
|
||||
};
|
||||
|
||||
#pointerUpHandler(event) {
|
||||
Logger.debug(event);
|
||||
if (event.button !== 1 || !this.#frameless?.rendered || Tour.tourInProgress) { return };
|
||||
event.preventDefault();
|
||||
this.#frameless.toggleLock();
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue