Skip to content

Commit b77bf31

Browse files
authored
Fix/damage customizer theming (#955)
* Fix damage customizer theming * Fix up the damage customizer! * Add an array sorting handlebars helper * Update the charname container * Fix item quality cost template * Update icon insert window * Use scrolling for the character list * Add zoom/panning to the bonds graph * Fix line scaling on tab switching * Get the import journal entry working! * Fix double open for the inline codex handler * Set max height in dialogs
1 parent 94fd959 commit b77bf31

39 files changed

+605
-202
lines changed

lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,6 +1935,7 @@
19351935
"Codex": "Codex",
19361936
"Tags": "Tags",
19371937
"Hidden": "Hidden",
1938+
"Entry": "Entry",
19381939
"BrowseImage": "Browse Image",
19391940
"UploadClipboardImage": "Upload Clipboard Image",
19401941
"CodexUploadDirectory": "Codex Upload Directory",

module/enrichers/inline-common.mjs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,16 @@ const inlineCodexEnricher = {
135135
onRender: async (element) => {
136136
const renderContext = await InlineHelper.getRenderContext(element);
137137
const entry = renderContext.dataset.entry;
138-
element.addEventListener('click', async function (event) {
139-
return FUPartySheet.viewCodexEntry(entry);
140-
});
138+
139+
element._codexAbort?.abort();
140+
element._codexAbort = new AbortController();
141+
element.addEventListener(
142+
'click',
143+
async function (event) {
144+
return FUPartySheet.viewCodexEntry(entry);
145+
},
146+
{ signal: element._codexAbort.signal },
147+
);
141148
},
142149
};
143150

module/helpers/config.mjs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,16 @@ FU.damageTypes = {
9494
* @type {Object<DamageType, string>}
9595
*/
9696
FU.affIcon = {
97-
physical: 'fua fu-physical',
98-
air: 'fua fu-air',
99-
bolt: 'fua fu-bolt',
100-
dark: 'fua fu-dark',
101-
earth: 'fua fu-earth',
102-
fire: 'fua fu-fire',
103-
ice: 'fua fu-ice',
104-
light: 'fua fu-light',
105-
poison: 'fua fu-poison',
106-
untyped: 'fua fu-untyped',
97+
physical: 'fu-physical',
98+
air: 'fu-air',
99+
bolt: 'fu-bolt',
100+
dark: 'fu-dark',
101+
earth: 'fu-earth',
102+
fire: 'fu-fire',
103+
ice: 'fu-ice',
104+
light: 'fu-light',
105+
poison: 'fu-poison',
106+
untyped: 'fu-untyped',
107107
};
108108

109109
/**
@@ -133,6 +133,7 @@ FU.checkIcons = {
133133
ritual: 'fu-check-ritual',
134134
difficulty: 'fu-roll-difficulty',
135135
result: 'fu-roll-result',
136+
base: 'fu-roll-base',
136137
mod: 'fu-roll-modifier',
137138
hr: 'fu-roll-high',
138139
target: 'fu-roll-target',

module/helpers/foundry-utils.mjs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { systemTemplatePath } from './system-utils.mjs';
33
import { TextEditor } from './text-editor.mjs';
44
import { CompendiumIndex } from '../ui/compendium/compendium-index.mjs';
55
import { ObjectUtils } from './object-utils.mjs';
6+
import { ItemSelectionDialog } from '../ui/features/item-selection-dialog.mjs';
67

78
const { api, fields, handlebars } = foundry.applications;
89

@@ -451,7 +452,6 @@ export default class FoundryUtils {
451452
}
452453

453454
/**
454-
*
455455
* @param {String} title
456456
* @param {String} message
457457
* @returns {Promise<Boolean>}
@@ -475,6 +475,28 @@ export default class FoundryUtils {
475475
});
476476
}
477477

478+
/**
479+
* @param {String} title
480+
* @param {String} content
481+
* @returns {Promise<Boolean>}
482+
*/
483+
static async confirm(title, content) {
484+
return foundry.applications.api.DialogV2.confirm({
485+
window: {
486+
title: title,
487+
},
488+
classes: ['projectfu', 'sheet', 'backgroundstyle', 'fu-dialog'],
489+
content: content,
490+
rejectClose: false,
491+
yes: {
492+
label: 'FU.Confirm',
493+
},
494+
no: {
495+
label: 'FU.Cancel',
496+
},
497+
});
498+
}
499+
478500
/**
479501
* @param {Record<String, String>} record
480502
* @param {((key: string, value: string) => string)} labelSelector
@@ -649,6 +671,56 @@ export default class FoundryUtils {
649671
});
650672
}
651673

674+
/**
675+
* @returns {Promise<JournalEntryData[]>}
676+
*/
677+
static async selectJournalEntries() {
678+
const journals = game.journal.contents;
679+
if (!journals.length) {
680+
ui.notifications.warn('No journal entries found in this world.');
681+
return;
682+
}
683+
684+
const title = `${StringUtils.localize('CONTROLS.CommonSelect')} ${StringUtils.localize('DOCUMENT.JournalEntry')}`;
685+
686+
const data = {
687+
title: title,
688+
style: 'list',
689+
items: journals,
690+
getDescription: async (item) => {
691+
const text = item.name ?? '';
692+
return text;
693+
},
694+
};
695+
const dialog = new ItemSelectionDialog(data);
696+
return await dialog.open();
697+
}
698+
699+
/**
700+
* @returns {Promise<FUActor[]>}
701+
*/
702+
static async selectActors() {
703+
const actors = game.actors.contents;
704+
if (!actors.length) {
705+
ui.notifications.warn('No actors found in this world.');
706+
return;
707+
}
708+
const title = `${StringUtils.localize('CONTROLS.CommonSelect')} ${StringUtils.localize('DOCUMENT.Actor')}`;
709+
710+
const data = {
711+
title: title,
712+
style: 'list',
713+
items: actors,
714+
getDescription: async (item) => {
715+
const text = item.name ?? '';
716+
return text;
717+
},
718+
};
719+
const dialog = new ItemSelectionDialog(data);
720+
const result = await dialog.open();
721+
return result;
722+
}
723+
652724
/**
653725
* @desc Migrates the data of an item onto another.
654726
* @param {FUItem} sourceItem

module/helpers/handlebars.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ export const FUHandlebars = Object.freeze({
192192
return new Handlebars.SafeString(`<span>${resourceName}</span><span class="digit-row">${digitBoxes}</span>`);
193193
});
194194

195+
Handlebars.registerHelper('pfuSort', (array, key) => {
196+
return [...array].sort((a, b) => (a[key] > b[key] ? 1 : -1));
197+
});
198+
195199
Handlebars.registerHelper('pfuMath', function (left, operator, right) {
196200
left = parseFloat(left);
197201
right = parseFloat(right);

module/helpers/object-utils.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,21 @@ function deepFreeze(obj) {
7676
return Object.freeze(obj);
7777
}
7878

79+
/**
80+
* @param {Object[]} array
81+
* @param {String} property
82+
* @returns {*[]}
83+
*/
84+
function sortArray(array, property) {
85+
return [...array].sort((a, b) => (a[property] > b[property] ? 1 : -1));
86+
}
87+
7988
export const ObjectUtils = Object.freeze({
8089
mergeRecursive,
8190
getProperty,
8291
setProperty,
8392
cleanObject,
8493
pick,
8594
deepFreeze,
95+
sortArray,
8696
});

module/helpers/text-editor-command-dropdown.mjs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FU } from './config.mjs';
22
import { InlineEffects } from '../enrichers/inline-effects.mjs';
3+
import FoundryUtils from './foundry-utils.mjs';
34

45
async function promptDamageDialog(state, dispatch, view) {
56
const result = await foundry.applications.api.DialogV2.prompt({
@@ -86,11 +87,15 @@ async function promptResourceLossDialog(state, dispatch, view) {
8687
}
8788

8889
async function promptIconSelectionDialog(state, dispatch, view) {
89-
const result = await foundry.applications.api.DialogV2.prompt({
90+
const content = await FoundryUtils.renderTemplate('dialog/dialog-command-icon', { allIcon: FU.allIcon });
91+
const result = await FoundryUtils.prompt({
9092
window: { title: game.i18n.localize('FU.TextEditorDialogSelectIconTitle') },
93+
position: {
94+
width: 600,
95+
height: 480,
96+
},
97+
content,
9198
label: game.i18n.localize('FU.TextEditorDialogButtonInsert'),
92-
content: await foundry.applications.handlebars.renderTemplate('systems/projectfu/templates/dialog/dialog-command-icon.hbs', { allIcon: FU.allIcon }),
93-
options: { classes: ['projectfu', 'unique-dialog', 'backgroundstyle'] },
9499
ok: {
95100
callback: (event, button, dialog) => {
96101
const icon = dialog.element.querySelector('.icon-radio:checked').value;

module/helpers/typedefs.mjs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,50 @@ export {};
3030
* @extends {DataModel<DocumentData, DocumentContext>}
3131
*/
3232

33+
/**
34+
* @typedef JournalEntryData
35+
* @property {string|null} _id The _id which uniquely identifies this JournalEntry document
36+
* @property {string} name The name of this JournalEntry
37+
* @property {JournalEntryPageData[]} pages The pages contained within this JournalEntry document
38+
* @property {string|null} folder The _id of a Folder which contains this JournalEntry
39+
* @property {JournalEntryCategoryData[]} categories The categories contained within this JournalEntry.
40+
* @property {number} [sort] The numeric sort value which orders this JournalEntry
41+
* relative to its siblings
42+
* @property {object} [ownership] An object which configures ownership of this JournalEntry
43+
* @property {DocumentFlags} flags An object of optional key/value flags
44+
* @property {DocumentStats} _stats An object of creation and access information
45+
*/
46+
47+
/**
48+
* @typedef JournalEntryPageImageData
49+
* @property {string} [caption] A caption for the image.
50+
*/
51+
52+
/**
53+
* @typedef JournalEntryPageTextData
54+
* @property {string} [content] The content of the JournalEntryPage in a format appropriate for its type.
55+
* @property {string} [markdown] The original markdown source, if applicable.
56+
* @property {number} format The format of the page's content, in CONST.JOURNAL_ENTRY_PAGE_FORMATS.
57+
*/
58+
59+
/**
60+
* @typedef JournalEntryPageData
61+
* @property {string|null} _id The _id which uniquely identifies this JournalEntryPage embedded document.
62+
* @property {string} name The text name of this page.
63+
* @property {'image'|'text'} type The type of this page.
64+
* @property {JournalEntryPageTitleData} title Data that control's the display of this page's title.
65+
* @property {JournalEntryPageImageData} image Data particular to image journal entry pages.
66+
* @property {JournalEntryPageTextData} text Data particular to text journal entry pages.
67+
* @property {JournalEntryPageVideoData} video Data particular to video journal entry pages.
68+
* @property {string} [src] The URI of the image or other external media to be used for this page.
69+
* @property {object} system System-specific data.
70+
* @property {string} [category] An optional category that this page belongs to.
71+
* @property {number} sort The numeric sort value which orders this page relative to its siblings.
72+
* @property {object} [ownership] An object which configures the ownership of this page.
73+
* @property {DocumentFlags} flags An object of optional key/value flags
74+
* @property {DocumentStats} _stats An object of creation and access information
75+
*/
76+
3377
// TODO: Figure out how to remove warnings
3478
/**
3579
* @global

module/pipelines/damage-customizer.mjs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { FU, systemPath } from '../helpers/config.mjs';
2222
/**
2323
* Displays a dialog to customize damage.
2424
*
25-
* @param {TableDamageData} damage - The damage object containing type and total.
25+
* @param {DamageData} damage - The damage object containing type and total.
2626
* @param {FUActor[]} targets - The specified targets.
2727
* @param {DamageOverrideCallback} callback - The function to call when the user confirms.
2828
* @param {() => void} onCancel - The function to call when the user cancels.
@@ -31,7 +31,7 @@ export async function DamageCustomizer(damage, targets, callback, onCancel) {
3131
// Create and render the dialog
3232
const result = await foundry.applications.api.DialogV2.input({
3333
window: {
34-
title: game.i18n.localize('FU.DamageCustomizer'),
34+
title: game.i18n.localize('FU.AutomationApplyDamage'),
3535
},
3636
position: {
3737
width: 440,
@@ -40,6 +40,7 @@ export async function DamageCustomizer(damage, targets, callback, onCancel) {
4040
content: await foundry.applications.handlebars.renderTemplate(systemPath('templates/dialog/dialog-damage-customizer.hbs'), {
4141
FU,
4242
damage,
43+
targets,
4344
}),
4445
rejectClose: false,
4546
ok: {
@@ -49,24 +50,32 @@ export async function DamageCustomizer(damage, targets, callback, onCancel) {
4950
render: (event, dialog) => {
5051
// Cache selectors
5152
const hrZeroCheckbox = dialog.element.querySelector('#hr-zero');
52-
const totalDamageSpan = dialog.element.querySelector('#total-damage');
5353
const damageTypeSelect = dialog.element.querySelector('#damage-type');
5454
const extraDamageInput = dialog.element.querySelector('#extra-damage');
55-
const totalDamageIcon = dialog.element.querySelector('#total-damage-icon');
5655
const checkAllButton = dialog.element.querySelector('#check-all');
5756
const checkNoneButton = dialog.element.querySelector('#check-none');
5857
const ignoreCheckboxes = dialog.element.querySelectorAll('.ignore');
5958

6059
// Function to update total damage and icons based on HR Zero status, and extra damage
60+
const totalDamageSection = dialog.element.querySelector('#total-damage');
61+
const baseDamageText = totalDamageSection.querySelector('#base');
62+
const hrDamageText = totalDamageSection.querySelector('#hr');
63+
const extraDamageText = totalDamageSection.querySelector('#extra');
64+
const totalDamageText = totalDamageSection.querySelector('#total');
65+
const damageTypeIcon = totalDamageSection.querySelector('#damage-type-icon');
66+
6167
function updateTotalDamage() {
6268
const extraDamage = parseInt(extraDamageInput.value, 10) || 0;
6369
const selectedDamageType = damageTypeSelect.value;
64-
const baseDamage = hrZeroCheckbox.checked ? damage.modifierTotal : damage.total;
65-
const totalDamage = baseDamage + extraDamage;
70+
const baseDamage = damage.modifiers[0].amount;
71+
const hrDamage = hrZeroCheckbox.checked ? 0 : damage.hr;
72+
const totalDamage = baseDamage + hrDamage + extraDamage;
6673

67-
totalDamageSpan.textContent = `${baseDamage} (Base) + ${extraDamage} (Extra Damage) = ${totalDamage} ${game.i18n.localize(FU.damageTypes[selectedDamageType])}`;
68-
// Update icons
69-
totalDamageIcon.classList.value = `icon ${FU.affIcon[selectedDamageType]}`;
74+
baseDamageText.textContent = baseDamage;
75+
hrDamageText.textContent = hrDamage;
76+
extraDamageText.textContent = extraDamage;
77+
totalDamageText.textContent = totalDamage;
78+
damageTypeIcon.classList.value = `fu-icon--sm glow ${FU.affIcon[selectedDamageType]}`;
7079
}
7180

7281
// Initial update

0 commit comments

Comments
 (0)