Files
Foundry-VTT-Docker/resources/app/client/apps/sidebar/sidebar-tab.js

179 lines
4.6 KiB
JavaScript
Raw Normal View History

2025-01-04 00:34:03 +01:00
/**
* An abstract pattern followed by the different tabs of the sidebar
* @abstract
* @interface
*/
class SidebarTab extends Application {
constructor(...args) {
super(...args);
/**
* A reference to the pop-out variant of this SidebarTab, if one exists
* @type {SidebarTab}
* @protected
*/
this._popout = null;
/**
* Denote whether this is the original version of the sidebar tab, or a pop-out variant
* @type {SidebarTab}
*/
this._original = null;
// Adjust options
if ( this.options.popOut ) this.options.classes.push("sidebar-popout");
this.options.classes.push(`${this.tabName}-sidebar`);
// Register the tab as the sidebar singleton
if ( !this.popOut && ui.sidebar ) ui.sidebar.tabs[this.tabName] = this;
}
/* -------------------------------------------- */
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: null,
popOut: false,
width: 300,
height: "auto",
classes: ["tab", "sidebar-tab"],
baseApplication: "SidebarTab"
});
}
/* -------------------------------------------- */
/** @override */
get id() {
return `${this.options.id}${this._original ? "-popout" : ""}`;
}
/* -------------------------------------------- */
/**
* The base name of this sidebar tab
* @type {string}
*/
get tabName() {
return this.constructor.defaultOptions.id ?? this.id;
}
/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */
/** @override */
async getData(options={}) {
return {
cssId: this.id,
cssClass: this.options.classes.join(" "),
tabName: this.tabName,
user: game.user
};
}
/* -------------------------------------------- */
/** @override */
async _render(force=false, options={}) {
await super._render(force, options);
if ( this._popout ) await this._popout._render(force, options);
}
/* -------------------------------------------- */
/** @override */
async _renderInner(data) {
let html = await super._renderInner(data);
if ( ui.sidebar?.activeTab === this.id ) html.addClass("active");
if ( this.popOut ) html.removeClass("tab");
return html;
}
/* -------------------------------------------- */
/* Methods */
/* -------------------------------------------- */
/**
* Activate this SidebarTab, switching focus to it
*/
activate() {
ui.sidebar.activateTab(this.tabName);
}
/* -------------------------------------------- */
/** @override */
async close(options) {
if ( this.popOut ) {
const base = this._original;
if ( base ) base._popout = null;
return super.close(options);
}
}
/* -------------------------------------------- */
/**
* Create a second instance of this SidebarTab class which represents a singleton popped-out container
* @returns {SidebarTab} The popped out sidebar tab instance
*/
createPopout() {
if ( this._popout ) return this._popout;
// Retain options from the main tab
const options = {...this.options, popOut: true};
delete options.id;
delete options.classes;
// Create a popout application
const pop = new this.constructor(options);
this._popout = pop;
pop._original = this;
return pop;
}
/* -------------------------------------------- */
/**
* Render the SidebarTab as a pop-out container
*/
renderPopout() {
const pop = this.createPopout();
pop.render(true);
}
/* -------------------------------------------- */
/* Event Handlers */
/* -------------------------------------------- */
/**
* Handle lazy loading for sidebar images to only load them once they become observed
* @param {HTMLElement[]} entries The entries which are now observed
* @param {IntersectionObserver} observer The intersection observer instance
*/
_onLazyLoadImage(entries, observer) {
for ( let e of entries ) {
if ( !e.isIntersecting ) continue;
const li = e.target;
// Background Image
if ( li.dataset.backgroundImage ) {
li.style["background-image"] = `url("${li.dataset.backgroundImage}")`;
delete li.dataset.backgroundImage;
}
// Avatar image
const img = li.querySelector("img");
if ( img && img.dataset.src ) {
img.src = img.dataset.src;
delete img.dataset.src;
}
// No longer observe the target
observer.unobserve(e.target);
}
}
}