179 lines
4.6 KiB
JavaScript
179 lines
4.6 KiB
JavaScript
|
|
/**
|
||
|
|
* 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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|