Initial
This commit is contained in:
171
resources/app/client/apps/sidebar/package-configuration.js
Normal file
171
resources/app/client/apps/sidebar/package-configuration.js
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* An application for configuring data across all installed and active packages.
|
||||
*/
|
||||
class PackageConfiguration extends FormApplication {
|
||||
|
||||
static get categoryOrder() {
|
||||
return ["all", "core", "system", "module", "unmapped"];
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the currently active tab.
|
||||
* @type {string}
|
||||
*/
|
||||
get activeCategory() {
|
||||
return this._tabs[0].active;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ["package-configuration"],
|
||||
template: "templates/sidebar/apps/package-configuration.html",
|
||||
categoryTemplate: undefined,
|
||||
width: 780,
|
||||
height: 680,
|
||||
resizable: true,
|
||||
scrollY: [".filters", ".categories"],
|
||||
tabs: [{navSelector: ".tabs", contentSelector: "form .scrollable", initial: "all"}],
|
||||
filters: [{inputSelector: 'input[name="filter"]', contentSelector: ".categories"}],
|
||||
submitButton: false
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData(options={}) {
|
||||
const data = this._prepareCategoryData();
|
||||
data.categoryTemplate = this.options.categoryTemplate;
|
||||
data.submitButton = this.options.submitButton;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Prepare the structure of category data which is rendered in this configuration form.
|
||||
* @abstract
|
||||
* @protected
|
||||
*/
|
||||
_prepareCategoryData() {
|
||||
return {categories: [], total: 0};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Classify what Category an Action belongs to
|
||||
* @param {string} namespace The entry to classify
|
||||
* @returns {{id: string, title: string}} The category the entry belongs to
|
||||
* @protected
|
||||
*/
|
||||
_categorizeEntry(namespace) {
|
||||
if ( namespace === "core" ) return {
|
||||
id: "core",
|
||||
title: game.i18n.localize("PACKAGECONFIG.Core")
|
||||
};
|
||||
else if ( namespace === game.system.id ) return {
|
||||
id: "system",
|
||||
title: game.system.title
|
||||
};
|
||||
else {
|
||||
const module = game.modules.get(namespace);
|
||||
if ( module ) return {
|
||||
id: module.id,
|
||||
title: module.title
|
||||
};
|
||||
return {
|
||||
id: "unmapped",
|
||||
title: game.i18n.localize("PACKAGECONFIG.Unmapped")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Reusable logic for how categories are sorted in relation to each other.
|
||||
* @param {object} a
|
||||
* @param {object} b
|
||||
* @protected
|
||||
*/
|
||||
_sortCategories(a, b) {
|
||||
const categories = this.constructor.categoryOrder;
|
||||
let ia = categories.indexOf(a.id);
|
||||
if ( ia === -1 ) ia = categories.length - 2; // Modules second from last
|
||||
let ib = this.constructor.categoryOrder.indexOf(b.id);
|
||||
if ( ib === -1 ) ib = categories.length - 2; // Modules second from last
|
||||
return (ia - ib) || a.title.localeCompare(b.title, game.i18n.lang);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
async _render(force, {activeCategory, ...options}={}) {
|
||||
await loadTemplates([this.options.categoryTemplate]);
|
||||
await super._render(force, options);
|
||||
if ( activeCategory ) this._tabs[0].activate(activeCategory);
|
||||
const activeTab = this._tabs[0]?.active;
|
||||
if ( activeTab ) this.element[0].querySelector(`.tabs [data-tab="${activeTab}"]`)?.scrollIntoView();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @inheritDoc */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
if ( this.activeCategory === "all" ) {
|
||||
this._tabs[0]._content.querySelectorAll(".tab").forEach(tab => tab.classList.add("active"));
|
||||
}
|
||||
html.find("button.reset-all").click(this._onResetDefaults.bind(this));
|
||||
html.find("input[name=filter]").focus();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_onChangeTab(event, tabs, active) {
|
||||
if ( active === "all" ) {
|
||||
tabs._content.querySelectorAll(".tab").forEach(tab => tab.classList.add("active"));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_onSearchFilter(event, query, rgx, html) {
|
||||
const visibleCategories = new Set();
|
||||
|
||||
// Hide entries
|
||||
for ( const entry of html.querySelectorAll(".form-group") ) {
|
||||
if ( !query ) {
|
||||
entry.classList.remove("hidden");
|
||||
continue;
|
||||
}
|
||||
const label = entry.querySelector("label")?.textContent;
|
||||
const notes = entry.querySelector(".notes")?.textContent;
|
||||
const match = (label && rgx.test(SearchFilter.cleanQuery(label)))
|
||||
|| (notes && rgx.test(SearchFilter.cleanQuery(notes)));
|
||||
entry.classList.toggle("hidden", !match);
|
||||
if ( match ) visibleCategories.add(entry.parentElement.dataset.category);
|
||||
}
|
||||
|
||||
// Hide categories which have no visible children
|
||||
for ( const category of html.querySelectorAll(".category") ) {
|
||||
category.classList.toggle("hidden", query && !visibleCategories.has(category.dataset.category));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle button click to reset default settings
|
||||
* @param {Event} event The initial button click event
|
||||
* @abstract
|
||||
* @protected
|
||||
*/
|
||||
_onResetDefaults(event) {}
|
||||
}
|
||||
Reference in New Issue
Block a user