initial commit
This commit is contained in:
commit
af8b02071e
6 changed files with 715 additions and 0 deletions
233
scripts/dh-attribution-sources.mjs
Normal file
233
scripts/dh-attribution-sources.mjs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
let registeredKeys = new Set();
|
||||
|
||||
/**
|
||||
* Apply the custom attribution sources to the system CONFIG object.
|
||||
*/
|
||||
export function applyAttributionSources() {
|
||||
if (!CONFIG.DH?.GENERAL?.attributionSources) return;
|
||||
|
||||
// Remove previously registered custom sources
|
||||
for (const key of registeredKeys) {
|
||||
delete CONFIG.DH.GENERAL.attributionSources[key];
|
||||
}
|
||||
registeredKeys.clear();
|
||||
|
||||
// Get current custom sources from setting
|
||||
const customSources = game.settings.get("dh-attribution-sources", "customSources") || [];
|
||||
|
||||
// Inject custom sources
|
||||
for (const source of customSources) {
|
||||
if (!source.id) continue;
|
||||
const key = `custom_${source.id}`;
|
||||
|
||||
CONFIG.DH.GENERAL.attributionSources[key] = {
|
||||
label: source.label || source.id,
|
||||
values: (source.values || []).map(val => ({
|
||||
label: val.label || "",
|
||||
hint: val.hint || ""
|
||||
}))
|
||||
};
|
||||
registeredKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom application sheet to add, update, and remove custom attribution sources.
|
||||
*/
|
||||
export class CustomAttributionSourcesForm extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
const saved = game.settings.get("dh-attribution-sources", "customSources") || [];
|
||||
this.sources = foundry.utils.deepClone(saved);
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'dh-attribution-sources-settings',
|
||||
classes: ['dh-attribution-sources', 'dialog', 'settings-app'],
|
||||
position: { width: 620, height: 650 },
|
||||
window: {
|
||||
title: "Custom Attribution Sources",
|
||||
icon: 'fa-solid fa-signature',
|
||||
resizable: true
|
||||
},
|
||||
actions: {
|
||||
addSource: CustomAttributionSourcesForm.#onAddSource,
|
||||
removeSource: CustomAttributionSourcesForm.#onRemoveSource,
|
||||
addValue: CustomAttributionSourcesForm.#onAddValue,
|
||||
removeValue: CustomAttributionSourcesForm.#onRemoveValue,
|
||||
reset: CustomAttributionSourcesForm.#onReset,
|
||||
save: CustomAttributionSourcesForm.#onSave
|
||||
},
|
||||
form: {
|
||||
handler: CustomAttributionSourcesForm.#onSubmit,
|
||||
submitOnChange: false,
|
||||
closeOnSubmit: false
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: 'modules/dh-attribution-sources/templates/settings.hbs',
|
||||
scrollable: ['.sources-list']
|
||||
},
|
||||
footer: {
|
||||
template: 'modules/dh-attribution-sources/templates/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
context.sources = this.sources;
|
||||
return context;
|
||||
}
|
||||
|
||||
static #serializeForm(target) {
|
||||
const form = target.closest('form');
|
||||
const formData = new foundry.applications.ux.FormDataExtended(form);
|
||||
const data = foundry.utils.expandObject(formData.object);
|
||||
|
||||
// Convert dot-notation structures back to nested arrays
|
||||
const sources = Object.values(data.sources || {}).map(src => {
|
||||
const values = Object.values(src.values || {}).map(val => ({
|
||||
label: val.label || "",
|
||||
hint: val.hint || ""
|
||||
}));
|
||||
return {
|
||||
id: src.id || "",
|
||||
label: src.label || "",
|
||||
values: values
|
||||
};
|
||||
});
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
static async #onAddSource(event, target) {
|
||||
const sources = CustomAttributionSourcesForm.#serializeForm(target);
|
||||
sources.push({
|
||||
id: `group-${foundry.utils.randomID(4)}`,
|
||||
label: "New Source Group",
|
||||
values: [{ label: "", hint: "" }]
|
||||
});
|
||||
this.sources = sources;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #onRemoveSource(event, target) {
|
||||
const sources = CustomAttributionSourcesForm.#serializeForm(target);
|
||||
const index = parseInt(target.dataset.sourceIndex);
|
||||
if (!isNaN(index)) {
|
||||
sources.splice(index, 1);
|
||||
}
|
||||
this.sources = sources;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #onAddValue(event, target) {
|
||||
const sources = CustomAttributionSourcesForm.#serializeForm(target);
|
||||
const index = parseInt(target.dataset.sourceIndex);
|
||||
if (!isNaN(index)) {
|
||||
sources[index].values.push({ label: "", hint: "" });
|
||||
}
|
||||
this.sources = sources;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #onRemoveValue(event, target) {
|
||||
const sources = CustomAttributionSourcesForm.#serializeForm(target);
|
||||
const sourceIndex = parseInt(target.dataset.sourceIndex);
|
||||
const valueIndex = parseInt(target.dataset.valueIndex);
|
||||
if (!isNaN(sourceIndex) && !isNaN(valueIndex)) {
|
||||
sources[sourceIndex].values.splice(valueIndex, 1);
|
||||
}
|
||||
this.sources = sources;
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #onReset(event, target) {
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: {
|
||||
title: "Reset Custom Attribution Sources"
|
||||
},
|
||||
content: "Are you sure you want to discard your unsaved changes and reload settings?"
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
const saved = game.settings.get("dh-attribution-sources", "customSources") || [];
|
||||
this.sources = foundry.utils.deepClone(saved);
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #onSave(event, target) {
|
||||
const sources = CustomAttributionSourcesForm.#serializeForm(target);
|
||||
|
||||
// Validation
|
||||
const ids = new Set();
|
||||
for (const src of sources) {
|
||||
const cleanId = src.id.trim();
|
||||
if (!cleanId) {
|
||||
ui.notifications.error("All attribution source groups must have a non-empty ID.");
|
||||
return;
|
||||
}
|
||||
if (ids.has(cleanId)) {
|
||||
ui.notifications.error(`Duplicate ID found: "${cleanId}". Each attribution source group must have a unique ID.`);
|
||||
return;
|
||||
}
|
||||
ids.add(cleanId);
|
||||
|
||||
src.id = cleanId;
|
||||
src.label = src.label.trim() || cleanId;
|
||||
|
||||
if (src.values.length === 0) {
|
||||
ui.notifications.error(`Group "${src.label}" must contain at least one attribution value.`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const val of src.values) {
|
||||
val.label = val.label.trim();
|
||||
val.hint = val.hint.trim();
|
||||
if (!val.label) {
|
||||
ui.notifications.error(`Group "${src.label}" has an empty attribution label.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await game.settings.set("dh-attribution-sources", "customSources", sources);
|
||||
applyAttributionSources();
|
||||
ui.notifications.info("Custom attribution sources saved successfully.");
|
||||
this.close();
|
||||
}
|
||||
|
||||
static async #onSubmit(event, form, formData) {
|
||||
// Form submit falls through; handled by onSave button click
|
||||
}
|
||||
}
|
||||
|
||||
// Hook into initialization to register the settings and configuration panel
|
||||
Hooks.once('init', () => {
|
||||
game.settings.register("dh-attribution-sources", "customSources", {
|
||||
name: "Custom Attribution Sources List",
|
||||
scope: "world",
|
||||
config: false,
|
||||
type: Array,
|
||||
default: []
|
||||
});
|
||||
|
||||
game.settings.registerMenu("dh-attribution-sources", "manageSources", {
|
||||
name: "Custom Attribution Sources",
|
||||
label: "Manage Custom Sources",
|
||||
hint: "Configure your custom item attribution sources.",
|
||||
icon: "fas fa-signature",
|
||||
type: CustomAttributionSourcesForm,
|
||||
restricted: true
|
||||
});
|
||||
});
|
||||
|
||||
// Setup hook to run once the system has registered its configuration
|
||||
Hooks.on('setup', () => {
|
||||
applyAttributionSources();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue