Update the popover management to work with origin rerenders, and rerendering the popovers directly.
This commit is contained in:
parent
69bf712eca
commit
96e4d09e7b
4 changed files with 93 additions and 29 deletions
|
|
@ -82,22 +82,16 @@ export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {Map<string, PopoverEventManager>} */
|
|
||||||
#popoverManagers = new Map();
|
|
||||||
/** @type {Map<number, string>} */
|
|
||||||
#hookIDs = new Map();
|
|
||||||
/** @this {HeroSkillsCardV1} */
|
/** @this {HeroSkillsCardV1} */
|
||||||
static async _createPopoverListeners() {
|
static async _createPopoverListeners() {
|
||||||
const ammoInfoIcon = this.element.querySelector(`.ammo-info-icon`);
|
const ammoInfoIcon = this.element.querySelector(`.ammo-info-icon`);
|
||||||
|
const idPrefix = this.actor.uuid;
|
||||||
|
|
||||||
this.#popoverManagers.set(
|
const manager = new PopoverEventManager(`${idPrefix}.ammo-info-icon`, ammoInfoIcon, AmmoTracker);
|
||||||
`.ammo-info-icon`,
|
this._popoverManagers.set(`.ammo-info-icon`, manager);
|
||||||
new PopoverEventManager(ammoInfoIcon, AmmoTracker),
|
this._hookIDs.set(Hooks.on(`prepare${manager.id}Context`, (ctx) => {
|
||||||
);
|
ctx.ammos = this.actor.itemTypes.ammo;
|
||||||
|
}), `prepare${manager.id}Context`);
|
||||||
this.#hookIDs.set(Hooks.on(`get${AmmoTracker.name}Options`, (opts) => {
|
|
||||||
opts.ammo = this.actor.itemTypes.ammo;
|
|
||||||
}), `get${AmmoTracker.name}Options`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async _preparePartContext(partId, ctx, opts) {
|
async _preparePartContext(partId, ctx, opts) {
|
||||||
|
|
@ -194,17 +188,6 @@ export class HeroSkillsCardV1 extends GenericAppMixin(HandlebarsApplicationMixin
|
||||||
}
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
_tearDown(options) {
|
|
||||||
for (const manager of this.#popoverManagers.values()) {
|
|
||||||
manager.destroy();
|
|
||||||
};
|
|
||||||
this.#popoverManagers.clear();
|
|
||||||
for (const [id, hook] of this.#hookIDs.entries()) {
|
|
||||||
Hooks.off(hook, id);
|
|
||||||
}
|
|
||||||
super._tearDown(options);
|
|
||||||
};
|
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Actions
|
// #region Actions
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,13 @@ export function GenericAppMixin(HandlebarsApp) {
|
||||||
};
|
};
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
|
// #region Instance Data
|
||||||
|
/** @type {Map<string, PopoverEventManager>} */
|
||||||
|
_popoverManagers = new Map();
|
||||||
|
/** @type {Map<number, string>} */
|
||||||
|
_hookIDs = new Map();
|
||||||
|
// #endregion
|
||||||
|
|
||||||
// #region Lifecycle
|
// #region Lifecycle
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
|
@ -45,6 +52,13 @@ export function GenericAppMixin(HandlebarsApp) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async _onRender() {
|
||||||
|
await super._onRender();
|
||||||
|
for (const manager of this._popoverManagers.values()) {
|
||||||
|
manager.render();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
async _preparePartContext(partId, ctx, opts) {
|
async _preparePartContext(partId, ctx, opts) {
|
||||||
ctx = await super._preparePartContext(partId, ctx, opts);
|
ctx = await super._preparePartContext(partId, ctx, opts);
|
||||||
delete ctx.document;
|
delete ctx.document;
|
||||||
|
|
@ -60,6 +74,22 @@ export function GenericAppMixin(HandlebarsApp) {
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_tearDown(options) {
|
||||||
|
// Clear all popovers associated with the app
|
||||||
|
for (const manager of this._popoverManagers.values()) {
|
||||||
|
manager.destroy();
|
||||||
|
};
|
||||||
|
this._popoverManagers.clear();
|
||||||
|
|
||||||
|
// Remove any hooks added for this app
|
||||||
|
for (const [id, hook] of this._hookIDs.entries()) {
|
||||||
|
Hooks.off(hook, id);
|
||||||
|
};
|
||||||
|
this._hookIDs.clear();
|
||||||
|
|
||||||
|
super._tearDown(options);
|
||||||
|
};
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Actions
|
// #region Actions
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ const { ApplicationV2 } = foundry.applications.api;
|
||||||
/**
|
/**
|
||||||
* This mixin provides the ability to designate an Application as a "popover",
|
* This mixin provides the ability to designate an Application as a "popover",
|
||||||
* which means that it will spawn near the x/y coordinates provided it won't
|
* which means that it will spawn near the x/y coordinates provided it won't
|
||||||
* overflow the bounds of the screen.
|
* overflow the bounds of the screen. This also implements a _preparePartContext
|
||||||
|
* in order to allow the parent application passing new data into the popover
|
||||||
|
* whenever it rerenders; how the popover handles this data is up to the
|
||||||
|
* specific implementation.
|
||||||
*/
|
*/
|
||||||
export function GenericPopoverMixin(HandlebarsApp) {
|
export function GenericPopoverMixin(HandlebarsApp) {
|
||||||
class GenericRipCryptPopover extends HandlebarsApp {
|
class GenericRipCryptPopover extends HandlebarsApp {
|
||||||
|
|
@ -29,7 +32,6 @@ export function GenericPopoverMixin(HandlebarsApp) {
|
||||||
popover.framed ??= true;
|
popover.framed ??= true;
|
||||||
popover.locked ??= false;
|
popover.locked ??= false;
|
||||||
|
|
||||||
|
|
||||||
if (popover.framed) {
|
if (popover.framed) {
|
||||||
options.window ??= {};
|
options.window ??= {};
|
||||||
options.window.frame = true;
|
options.window.frame = true;
|
||||||
|
|
@ -151,6 +153,17 @@ export function GenericPopoverMixin(HandlebarsApp) {
|
||||||
scale,
|
scale,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is here in order allow things that are not this Application
|
||||||
|
* to provide / augment the context data for the lifecycle of the app.
|
||||||
|
*/
|
||||||
|
async _prepareContext(_partId, _context, options) {
|
||||||
|
const context = {};
|
||||||
|
Hooks.callAll(`prepare${this.constructor.name}Context`, context, options);
|
||||||
|
Hooks.callAll(`prepare${this.popover.managerId}Context`, context, options);
|
||||||
|
return context;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
return GenericRipCryptPopover;
|
return GenericRipCryptPopover;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,28 @@
|
||||||
import { getTooltipDelay } from "../consts.mjs";
|
import { getTooltipDelay } from "../consts.mjs";
|
||||||
|
import { Logger } from "./Logger.mjs";
|
||||||
|
|
||||||
export class PopoverEventManager {
|
export class PopoverEventManager {
|
||||||
#options;
|
#options;
|
||||||
|
id;
|
||||||
|
|
||||||
|
/** @type {Map<string, PopoverEventManager>} */
|
||||||
|
static #existing = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {HTMLElement} element The element to attach the listeners to.
|
* @param {HTMLElement} element The element to attach the listeners to.
|
||||||
* @param {GenericPopoverMixin} popoverClass The class reference that represents the popover app
|
* @param {GenericPopoverMixin} popoverClass The class reference that represents the popover app
|
||||||
*/
|
*/
|
||||||
constructor(element, popoverClass, options = {}) {
|
constructor(id, element, popoverClass, options = {}) {
|
||||||
|
id = `${id}-${popoverClass.name}`;
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
|
if (PopoverEventManager.#existing.has(id)) {
|
||||||
|
const manager = PopoverEventManager.#existing.get(id);
|
||||||
|
manager.#addListeners(element);
|
||||||
|
return manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
options.managerId = id;
|
||||||
options.locked ??= false;
|
options.locked ??= false;
|
||||||
options.lockable ??= true;
|
options.lockable ??= true;
|
||||||
|
|
||||||
|
|
@ -15,11 +30,19 @@ export class PopoverEventManager {
|
||||||
this.#element = element;
|
this.#element = element;
|
||||||
this.#class = popoverClass;
|
this.#class = popoverClass;
|
||||||
|
|
||||||
|
this.#addListeners(element);
|
||||||
|
PopoverEventManager.#existing.set(id, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} element
|
||||||
|
*/
|
||||||
|
#addListeners(element) {
|
||||||
element.addEventListener(`pointerenter`, this.#pointerEnterHandler.bind(this));
|
element.addEventListener(`pointerenter`, this.#pointerEnterHandler.bind(this));
|
||||||
element.addEventListener(`pointerout`, this.#pointerOutHandler.bind(this));
|
element.addEventListener(`pointerout`, this.#pointerOutHandler.bind(this));
|
||||||
element.addEventListener(`click`, this.#clickHandler.bind(this));
|
element.addEventListener(`click`, this.#clickHandler.bind(this));
|
||||||
|
|
||||||
if (options.lockable) {
|
if (this.#options.lockable) {
|
||||||
element.addEventListener(`pointerup`, this.#pointerUpHandler.bind(this));
|
element.addEventListener(`pointerup`, this.#pointerUpHandler.bind(this));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -55,6 +78,19 @@ export class PopoverEventManager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
get rendered() {
|
||||||
|
return Boolean(this.#frameless?.rendered || this.#framed?.rendered);
|
||||||
|
};
|
||||||
|
|
||||||
|
render(options) {
|
||||||
|
if (this.#framed?.rendered) {
|
||||||
|
this.#framed.render(options);
|
||||||
|
};
|
||||||
|
if (this.#frameless?.rendered) {
|
||||||
|
this.#frameless.render(options);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
#element;
|
#element;
|
||||||
#class;
|
#class;
|
||||||
#openTimeout = null;
|
#openTimeout = null;
|
||||||
|
|
@ -64,12 +100,14 @@ export class PopoverEventManager {
|
||||||
#framed;
|
#framed;
|
||||||
|
|
||||||
#construct(options) {
|
#construct(options) {
|
||||||
const valid = Hooks.call(`get${this.#class.name}Options`, options);
|
options.popover ??= {};
|
||||||
if (!valid) { return };
|
options.popover.managerId = this.id;
|
||||||
|
|
||||||
return new this.#class(options);
|
return new this.#class(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
#clickHandler() {
|
#clickHandler() {
|
||||||
|
Logger.debug(`click event handler`);
|
||||||
// Cleanup for the frameless lifecycle
|
// Cleanup for the frameless lifecycle
|
||||||
this.stopOpen();
|
this.stopOpen();
|
||||||
this.stopClose();
|
this.stopClose();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue