182 lines
6.1 KiB
JavaScript
182 lines
6.1 KiB
JavaScript
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
|
|
|
export class DhPathBrowserApp extends HandlebarsApplicationMixin(ApplicationV2) {
|
|
constructor(options) {
|
|
super(options);
|
|
this.paths = this.constructor.getChangeChoices();
|
|
}
|
|
|
|
static DEFAULT_OPTIONS = {
|
|
id: "dh-path-browser-app",
|
|
classes: ["daggerheart", "dh-path-browser", "dh-style"],
|
|
tag: "form",
|
|
window: {
|
|
title: "Data Path Browser",
|
|
icon: "fa-solid fa-code",
|
|
resizable: true,
|
|
contentClasses: ["standard-form"]
|
|
},
|
|
position: {
|
|
width: 600,
|
|
height: 700
|
|
},
|
|
actions: {
|
|
copyPath: DhPathBrowserApp.#copyPath
|
|
}
|
|
};
|
|
|
|
static PARTS = {
|
|
header: { template: "modules/dh-path-browser/templates/header.hbs" },
|
|
list: {
|
|
template: "modules/dh-path-browser/templates/list.hbs",
|
|
scrollable: [".paths-list"]
|
|
}
|
|
};
|
|
|
|
async _prepareContext(options) {
|
|
const context = await super._prepareContext(options);
|
|
|
|
const groups = {};
|
|
for (const p of this.paths) {
|
|
if (!groups[p.group]) groups[p.group] = [];
|
|
groups[p.group].push(p);
|
|
}
|
|
|
|
context.groups = Object.entries(groups).map(([group, paths]) => ({
|
|
group,
|
|
paths: paths.sort((a, b) => a.label.localeCompare(b.label))
|
|
})).sort((a, b) => a.group.localeCompare(b.group));
|
|
|
|
return context;
|
|
}
|
|
|
|
_attachPartListeners(partId, htmlElement, options) {
|
|
super._attachPartListeners(partId, htmlElement, options);
|
|
|
|
if (partId === "header") {
|
|
const searchInput = htmlElement.querySelector('input[name="search"]');
|
|
if (searchInput) {
|
|
searchInput.addEventListener("input", (e) => this.#filterPaths(e.target.value));
|
|
}
|
|
}
|
|
}
|
|
|
|
#filterPaths(query) {
|
|
query = query.toLowerCase();
|
|
const listElement = this.element.querySelector(".paths-list");
|
|
if (!listElement) return;
|
|
|
|
const items = listElement.querySelectorAll(".path-item");
|
|
items.forEach(item => {
|
|
const label = item.dataset.label.toLowerCase();
|
|
const value = item.dataset.value.toLowerCase();
|
|
|
|
if (label.includes(query) || value.includes(query)) {
|
|
item.style.display = "";
|
|
} else {
|
|
item.style.display = "none";
|
|
}
|
|
});
|
|
|
|
const groups = listElement.querySelectorAll(".path-group");
|
|
groups.forEach(group => {
|
|
const visibleItems = Array.from(group.querySelectorAll(".path-item")).filter(i => i.style.display !== "none");
|
|
group.style.display = visibleItems.length > 0 ? "" : "none";
|
|
});
|
|
}
|
|
|
|
static async #copyPath(event, target) {
|
|
const path = target.dataset.value;
|
|
if (path) {
|
|
const fullPath = `@system.${path}`;
|
|
await navigator.clipboard.writeText(fullPath);
|
|
ui.notifications.info(`Copied ${fullPath} to clipboard!`);
|
|
}
|
|
}
|
|
|
|
static getChangeChoices() {
|
|
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
|
|
|
|
const getTranslations = (model, path) => {
|
|
if (path === 'resources.hope.max')
|
|
return {
|
|
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxHope.label'),
|
|
hint: ''
|
|
};
|
|
|
|
// Armor overrides
|
|
if (path.endsWith('armor.max')) return { label: game.i18n.localize('DAGGERHEART.ArmorScoreMax'), hint: '' };
|
|
if (path.endsWith('armor.current')) return { label: game.i18n.localize('DAGGERHEART.ArmorScoreCurrent'), hint: '' };
|
|
|
|
const field = model.schema.getField(path);
|
|
return {
|
|
label: field ? game.i18n.localize(field.label) : path,
|
|
hint: field ? game.i18n.localize(field.hint) : ''
|
|
};
|
|
};
|
|
|
|
const getAllLeaves = (model, root, group, parentPath = '') => {
|
|
if (!root) return [];
|
|
const leaves = [];
|
|
const rootKey = `${parentPath ? `${parentPath}.` : ''}${root.name}`;
|
|
|
|
const fields = root.fields || root;
|
|
|
|
for (const [name, field] of Object.entries(fields)) {
|
|
const currentPath = `${rootKey}.${name}`;
|
|
if (field instanceof foundry.data.fields.SchemaField) {
|
|
leaves.push(...getAllLeaves(model, field, group, rootKey));
|
|
} else {
|
|
const trans = getTranslations(model, currentPath);
|
|
leaves.push({
|
|
value: currentPath,
|
|
label: trans.label || name,
|
|
hint: trans.hint || '',
|
|
group
|
|
});
|
|
}
|
|
}
|
|
return leaves;
|
|
};
|
|
|
|
const choices = [];
|
|
|
|
// Process Actors
|
|
for (const [key, model] of Object.entries(game.system.api.models.actors)) {
|
|
if (ignoredActorKeys.includes(key)) continue;
|
|
|
|
const group = game.i18n.localize(model.metadata.label);
|
|
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model.metadata.type);
|
|
|
|
const bars = attributes.bar.flatMap(x => {
|
|
const baseJoined = x.join('.');
|
|
return [
|
|
{ value: `${baseJoined}.max`, ...getTranslations(model, `${baseJoined}.max`), group },
|
|
{ value: `${baseJoined}.value`, ...getTranslations(model, `${baseJoined}.value`), group }
|
|
];
|
|
});
|
|
const values = attributes.value.flatMap(x => {
|
|
const joined = x.join('.');
|
|
return { value: joined, ...getTranslations(model, joined), group };
|
|
});
|
|
|
|
const bonuses = getAllLeaves(model, model.schema.fields.bonuses, group);
|
|
const rules = getAllLeaves(model, model.schema.fields.rules, group);
|
|
const armor = getAllLeaves(model, model.schema.fields.armor, group);
|
|
|
|
choices.push(...bars, ...values, ...rules, ...bonuses, ...armor);
|
|
}
|
|
|
|
// Process Items
|
|
for (const [key, model] of Object.entries(game.system.api.models.items)) {
|
|
const group = `${game.i18n.localize('DOCUMENT.Item')} (${game.i18n.localize(model.metadata.label)})`;
|
|
|
|
const bonuses = getAllLeaves(model, model.schema.fields.bonuses, group);
|
|
const armor = getAllLeaves(model, model.schema.fields.armor, group);
|
|
|
|
choices.push(...bonuses, ...armor);
|
|
}
|
|
|
|
return choices;
|
|
}
|
|
}
|