From 208e8192b448b0970aba2218acdb67ca308686e9 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 9 Jun 2026 23:38:29 +0200 Subject: [PATCH] Toggle pills --- module/applications/ui/countdownEdit.mjs | 6 +- module/applications/ui/countdowns.mjs | 93 +++++++++++++++++-- module/config/flagsConfig.mjs | 3 +- module/config/generalConfig.mjs | 22 ++--- module/data/countdowns.mjs | 32 ++++++- module/data/fields/action/countdownField.mjs | 4 +- module/systemRegistration/handlebars.mjs | 1 + module/systemRegistration/migrations.mjs | 4 +- module/systemRegistration/settings.mjs | 5 +- styles/less/ui/countdown/countdown.less | 25 ++++- templates/ui/countdowns.hbs | 56 ----------- .../ui/{ => countdowns}/countdown-edit.hbs | 2 +- templates/ui/countdowns/countdowns-view.hbs | 22 +++++ templates/ui/countdowns/parts/countdowns.hbs | 40 ++++++++ 14 files changed, 223 insertions(+), 92 deletions(-) delete mode 100644 templates/ui/countdowns.hbs rename templates/ui/{ => countdowns}/countdown-edit.hbs (99%) create mode 100644 templates/ui/countdowns/countdowns-view.hbs create mode 100644 templates/ui/countdowns/parts/countdowns.hbs diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 5974e1f2..0765e9fb 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -35,7 +35,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio static PARTS = { countdowns: { - template: 'systems/daggerheart/templates/ui/countdown-edit.hbs', + template: 'systems/daggerheart/templates/ui/countdowns/countdown-edit.hbs', scrollable: ['.expanded-view', '.edit-content'] } }; @@ -45,7 +45,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio context.isGM = game.user.isGM; context.ownershipDefaultOptions = CONFIG.DH.GENERAL.basicOwnershiplevels; context.defaultOwnership = this.data.defaultOwnership; - context.countdownBaseTypes = CONFIG.DH.GENERAL.countdownBaseTypes; + context.countdownTypes = CONFIG.DH.GENERAL.countdownType; context.countdownProgressionTypes = CONFIG.DH.GENERAL.countdownProgressionTypes; context.countdownLoopingTypes = CONFIG.DH.GENERAL.countdownLoopingTypes; context.hideNewCountdowns = this.hideNewCountdowns; @@ -62,7 +62,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio const randomizeValid = !new Roll(countdown.progress.startFormula ?? '').isDeterministic; acc[key] = { ...countdown, - typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownBaseTypes[countdown.type].label), + typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownType[countdown.type].label), progress: { ...countdown.progress, typeName: game.i18n.localize( diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 2a2a113e..e9804b6a 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -11,9 +11,14 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; */ export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) { + previusCountdownData = null; + countdownChangeAnimationTimeout = null; + constructor(options = {}) { super(options); + this.previusCountdownData = + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns; this.setupHooks(); } @@ -32,6 +37,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application }, actions: { toggleViewMode: DhCountdowns.#onToggleViewMode, + onToggleCountdownTypes: DhCountdowns.#onToggleCountdownTypes, editCountdowns: DhCountdowns.#onEditCountdowns, loopCountdown: DhCountdowns.#onLoopCountdown, decreaseCountdown: (_, target) => this.editCountdown(false, target), @@ -48,7 +54,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application static PARTS = { resources: { root: true, - template: 'systems/daggerheart/templates/ui/countdowns.hbs' + template: 'systems/daggerheart/templates/ui/countdowns/countdowns-view.hbs' } }; @@ -76,16 +82,14 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE); } - /** @override */ - async _prepareContext(options) { - const context = await super._prepareContext(options); - context.isGM = game.user.isGM; - - context.iconOnly = - game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === - CONFIG.DH.GENERAL.countdownAppMode.iconOnly; + _getCountdownData() { const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - context.countdowns = this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => { + const typeModes = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownTypeModes) ?? []; + + return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => { + if (!typeModes.includes(countdown.type)) + return acc; + const playersWithAccess = game.users.reduce((acc, user) => { const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown); if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { @@ -118,6 +122,27 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application }; return acc; }, {}); + } + + /** @override */ + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.isGM = game.user.isGM; + this.previusCountdownData = + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns; + + context.iconOnly = + game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === + CONFIG.DH.GENERAL.countdownAppMode.iconOnly; + + const userCountdownTypes = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownTypeModes) ?? []; + context.typeToggles = Object.values(CONFIG.DH.GENERAL.countdownType).map(type => ({ + type: type.id, + label: game.i18n.localize(type.label), + active: userCountdownTypes.includes(type.id) + })); + + context.countdowns = this._getCountdownData(); return context; } @@ -158,6 +183,36 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application this.render(); } + static async #onToggleCountdownTypes(event, target) { + const currentTypes = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownTypeModes) ?? []; + const { type } = target.dataset; + const newTypes = event.shiftKey ? + [type] : + currentTypes.includes(type) ? currentTypes.filter(x => x !== type) : [...currentTypes, type]; + await game.user.setFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownTypeModes, newTypes); + + for (const type of Object.keys(CONFIG.DH.GENERAL.countdownType)) { + const toggleElement = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`); + if (newTypes.includes(type)) + toggleElement.classList.remove('inactive'); + + else + toggleElement.classList.add('inactive'); + } + + const updatedCountdownsElement = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/countdowns/parts/countdowns.hbs', + { + countdowns: this._getCountdownData(), + iconOnly: game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === + CONFIG.DH.GENERAL.countdownAppMode.iconOnly, + isGM: game.user.isGM + } + ); + + this.element.querySelector('#countdowns .countdowns-container').innerHTML = updatedCountdownsElement; + } + static async #onEditCountdowns() { new game.system.api.applications.ui.CountdownEdit().render(true); } @@ -212,6 +267,10 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application } static async gmSetSetting(data) { + // if(Object.keys(data).some(x => x.includes('countdowns'))) { + // this.previusCountdownData = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns; + // } + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data); game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.Refresh, @@ -270,6 +329,20 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application }); } + /** + * + * @param {*} context + * @param {*} options + */ + async performChangeAnimations(changedCountdowns) { + if (this.countdownChangeAnimationTimeout) + clearTimeout(this.countdownChangeAnimationTimeout); + + this.countdownChangeAnimationTimeout = setTimeout(() => { + + }, 3000); + } + async _onRender(context, options) { await super._onRender(context, options); this.element.hidden = !game.user.isGM && this.#getCountdowns().length === 0; diff --git a/module/config/flagsConfig.mjs b/module/config/flagsConfig.mjs index 817ac89d..cb05bd72 100644 --- a/module/config/flagsConfig.mjs +++ b/module/config/flagsConfig.mjs @@ -24,7 +24,8 @@ export const itemAttachmentSource = 'attachmentSource'; export const userFlags = { welcomeMessage: 'welcome-message', - countdownMode: 'countdown-mode' + countdownMode: 'countdown-mode', + countdownTypeModes: 'countdown-type-modes' }; export const combatToggle = 'combat-toggle-origin'; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 6fc85ec5..e39be829 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -935,17 +935,6 @@ export const simpleOwnershiplevels = { ...basicOwnershiplevels }; -export const countdownBaseTypes = { - narrative: { - id: 'narrative', - label: 'DAGGERHEART.APPLICATIONS.Countdown.types.narrative' - }, - encounter: { - id: 'encounter', - label: 'DAGGERHEART.APPLICATIONS.Countdown.types.encounter' - } -}; - export const countdownLoopingTypes = { noLooping: { id: 'noLooping', @@ -970,6 +959,17 @@ export const countdownAppMode = { iconOnly: 'icon-only' }; +export const countdownType = { + shortterm: { + id: 'shortterm', + label: 'Short' + }, + longterm: { + id: 'longterm', + label: 'Long' + } +}; + export const sceneRangeMeasurementSetting = { disable: { id: 'disable', diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index 54971d34..ab71ca28 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -13,6 +13,33 @@ export default class DhCountdowns extends foundry.abstract.DataModel { }) }; } + + handleChange() { + const previousCountdowns = foundry.ui.countdowns.previusCountdownData; + const changedCountdowns = Object.values(this.countdowns).reduce((acc, [key, countdown]) => { + const previousCountdown = previousCountdowns[key]; + if (!previousCountdown || (previousCountdown.current !== countdown.current)) { + acc.push(key); + } + + return acc; + }, []); + + foundry.ui.performChangeAnimations(changedCountdowns); + } + + migrateData(source) { + for (const countdown of source.countdowns) { + switch (countdown.type) { + case 'narrative': + countdown.type = CONFIG.DH.GENERAL.countdownType.longterm.id; + break; + case 'encounter': + countdown.type = CONFIG.DH.GENERAL.countdownType.shortterm.id; + break; + } + } + } } export class DhCountdown extends foundry.abstract.DataModel { @@ -21,7 +48,8 @@ export class DhCountdown extends foundry.abstract.DataModel { return { type: new fields.StringField({ required: true, - choices: CONFIG.DH.GENERAL.countdownBaseTypes, + choices: CONFIG.DH.GENERAL.countdownType, + initial: CONFIG.DH.GENERAL.countdownType.shortterm.id, label: 'DAGGERHEART.GENERAL.type' }), name: new fields.StringField({ @@ -85,7 +113,7 @@ export class DhCountdown extends foundry.abstract.DataModel { : undefined; return { - type: type ?? CONFIG.DH.GENERAL.countdownBaseTypes.narrative.id, + type: type ?? CONFIG.DH.GENERAL.countdownType.shortterm.id, name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.newCountdown'), img: 'icons/magic/time/hourglass-yellow-green.webp', ownership: ownership, diff --git a/module/data/fields/action/countdownField.mjs b/module/data/fields/action/countdownField.mjs index 96d9dd91..366de6df 100644 --- a/module/data/fields/action/countdownField.mjs +++ b/module/data/fields/action/countdownField.mjs @@ -8,8 +8,8 @@ export default class CountdownField extends fields.ArrayField { ...game.system.api.data.countdowns.DhCountdown.defineSchema(), type: new fields.StringField({ required: true, - choices: CONFIG.DH.GENERAL.countdownBaseTypes, - initial: CONFIG.DH.GENERAL.countdownBaseTypes.encounter.id, + choices: CONFIG.DH.GENERAL.countdownType, + initial: CONFIG.DH.GENERAL.countdownType.shortterm.id, label: 'DAGGERHEART.GENERAL.type' }), name: new fields.StringField({ diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 1b08e0ad..bf315358 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -50,6 +50,7 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', + 'systems/daggerheart/templates/ui/countdowns/parts/countdowns.hbs', 'systems/daggerheart/templates/scene/dh-config.hbs', 'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs', 'systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs' diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index b718a127..ec546c92 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -178,8 +178,8 @@ export async function runMigrations() { await countdownSettings.updateSource({ countdowns: { - ...getCountdowns(countdownSettings.narrative, CONFIG.DH.GENERAL.countdownBaseTypes.narrative.id), - ...getCountdowns(countdownSettings.encounter, CONFIG.DH.GENERAL.countdownBaseTypes.encounter.id) + ...getCountdowns(countdownSettings.narrative, 'narrative'), + ...getCountdowns(countdownSettings.encounter, 'encounter') } }); await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSettings); diff --git a/module/systemRegistration/settings.mjs b/module/systemRegistration/settings.mjs index a66323c7..1a985274 100644 --- a/module/systemRegistration/settings.mjs +++ b/module/systemRegistration/settings.mjs @@ -199,7 +199,10 @@ const registerNonConfigSettings = () => { game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, { scope: 'world', config: false, - type: DhCountdowns + type: DhCountdowns, + onChange: value => { + value.handleChange(); + } }); game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.CompendiumBrowserSettings, { diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 63e539ba..307af858 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -65,12 +65,31 @@ padding: 0 0.5rem; overflow: hidden; font-size: var(--font-size-13); + .window-title { font-family: @font-body; - flex: 1; } - .header-control + .header-control { - margin-left: 8px; + + + .header-type-toggles { + flex: 1; + display: flex; + align-items: center; + gap: 4px; + + .header-type { + flex: 1; + border: 1px solid @beige; + border-radius: 12px; + padding: 2px 4px; + text-align: center; + background-color: @dark-blue; + color: @beige; + + &.inactive { + opacity: 0.4; + } + } } } diff --git a/templates/ui/countdowns.hbs b/templates/ui/countdowns.hbs deleted file mode 100644 index 4a77dfd7..00000000 --- a/templates/ui/countdowns.hbs +++ /dev/null @@ -1,56 +0,0 @@ -
-
- - {{localize "DAGGERHEART.UI.Countdowns.title"}} - {{#if isGM}} - - - - {{/if}} - - - -
-
- {{#each countdowns as | countdown id |}} -
-
- -
- {{#unless ../iconOnly}} -
{{countdown.name}}
- {{/unless}} -
-
- {{#if countdown.editable}}{{/if}} -
- {{countdown.progress.current}}/{{countdown.progress.start}} -
- {{#if countdown.editable}}{{/if}} -
-
- {{#if (not ../iconOnly)}} - {{#if (and countdown.noPlayerAccess @root.isGM)}} - - {{/if}} - {{#unless (eq countdown.progress.looping "noLooping")}} - - - - {{#if (eq countdown.progress.looping "increasing")}} - - {{else if (eq countdown.progress.looping "decreasing")}} - - {{/if}} - - - {{/unless}} - {{/if}} -
-
-
-
-
- {{/each}} -
-
\ No newline at end of file diff --git a/templates/ui/countdown-edit.hbs b/templates/ui/countdowns/countdown-edit.hbs similarity index 99% rename from templates/ui/countdown-edit.hbs rename to templates/ui/countdowns/countdown-edit.hbs index 6b7d22d4..04b8a811 100644 --- a/templates/ui/countdown-edit.hbs +++ b/templates/ui/countdowns/countdown-edit.hbs @@ -80,7 +80,7 @@
diff --git a/templates/ui/countdowns/countdowns-view.hbs b/templates/ui/countdowns/countdowns-view.hbs new file mode 100644 index 00000000..8bfa3fd5 --- /dev/null +++ b/templates/ui/countdowns/countdowns-view.hbs @@ -0,0 +1,22 @@ +
+
+ + {{localize "DAGGERHEART.UI.Countdowns.title"}} +
+ {{#each typeToggles as |type|}} + {{type.label}} + {{/each}} +
+ {{#if isGM}} + + + + {{/if}} + + + +
+
+ {{> "systems/daggerheart/templates/ui/countdowns/parts/countdowns.hbs" }} +
+
\ No newline at end of file diff --git a/templates/ui/countdowns/parts/countdowns.hbs b/templates/ui/countdowns/parts/countdowns.hbs new file mode 100644 index 00000000..f919d3fc --- /dev/null +++ b/templates/ui/countdowns/parts/countdowns.hbs @@ -0,0 +1,40 @@ +{{#each countdowns as | countdown id |}} +
+
+ +
+ {{#unless ../iconOnly}} +
{{countdown.name}}
+ {{/unless}} +
+
+ {{#if countdown.editable}}{{/if}} +
+ {{countdown.progress.current}}/{{countdown.progress.start}} +
+ {{#if countdown.editable}}{{/if}} +
+
+ {{#if (not ../iconOnly)}} + {{#if (and countdown.noPlayerAccess @root.isGM)}} + + {{/if}} + {{#unless (eq countdown.progress.looping "noLooping")}} + + + + {{#if (eq countdown.progress.looping "increasing")}} + + {{else if (eq countdown.progress.looping "decreasing")}} + + {{/if}} + + + {{/unless}} + {{/if}} +
+
+
+
+
+{{/each}} \ No newline at end of file