From a1103f847a9d73a1ae86567e14fb4cc844435cda Mon Sep 17 00:00:00 2001 From: Vlyan Date: Tue, 18 Mar 2025 13:45:19 +0100 Subject: [PATCH] Refactoring renderCompendium Added "hide disabled source" option --- system/lang/en-en.json | 7 +- system/lang/es-es.json | 9 +- system/lang/fr-fr.json | 7 +- system/lang/it-it.json | 9 +- system/scripts/hooks.js | 305 ++++++++++------------- system/scripts/migration.js | 3 +- system/scripts/misc/l5r5e-multiselect.js | 2 + system/scripts/settings.js | 9 + 8 files changed, 167 insertions(+), 184 deletions(-) diff --git a/system/lang/en-en.json b/system/lang/en-en.json index 6777fc6..587ef37 100644 --- a/system/lang/en-en.json +++ b/system/lang/en-en.json @@ -35,8 +35,12 @@ "Hint": "Set the default height for 'Items' windows types (techniques, weapons...), in pixels" }, "Compendium": { + "HideDisabledSources": { + "Title": "[Compendium] Hide sources filter without reference", + "Hint": "Hide empty source with no elements in source filter." + }, "HideEmptySourcesFromPlayers": { - "Title": "[Compendium] Hide sources without a reference", + "Title": "[Compendium] Hide elements with empty reference", "Hint": "Basically require a reference to be set in order for players to view the content in compendiums" }, "AllowedOfficialSources": { @@ -91,6 +95,7 @@ "placeholder": "Filter Sources", "player_filter_label": "Player filter", "player_filter_tooltip": "Apply player filter", + "already_in_filter": "Already in filter", "sources_categories": { "rules": "Rules", "adventures": "Adventures", diff --git a/system/lang/es-es.json b/system/lang/es-es.json index 66a76bc..aeb8d3b 100644 --- a/system/lang/es-es.json +++ b/system/lang/es-es.json @@ -35,9 +35,13 @@ "Hint": "Set the default height for 'Items' windows types (techniques, weapons...), in pixels" }, "Compendium": { + "HideDisabledSources": { + "Title": "[Compendium] Hide sources filter without reference", + "Hint": "Hide empty source with no elements in source filter." + }, "HideEmptySourcesFromPlayers": { - "Title": "[Compendium] Hide sources without a reference", - "Hint": "Useful for hiding items etc. that are partially done" + "Title": "[Compendium] Hide elements with empty reference", + "Hint": "Basically require a reference to be set in order for players to view the content in compendiums" }, "AllowedOfficialSources": { "Title": "[Compendium] Available official resources", @@ -91,6 +95,7 @@ "placeholder": "Filter Sources", "player_filter_label": "Player filter", "player_filter_tooltip": "Apply player filter", + "already_in_filter": "Already in filter", "sources_categories": { "rules": "Rules", "adventures": "Adventures", diff --git a/system/lang/fr-fr.json b/system/lang/fr-fr.json index 6bcf5cf..0187e59 100644 --- a/system/lang/fr-fr.json +++ b/system/lang/fr-fr.json @@ -35,8 +35,12 @@ "Hint": "Définir la hauteur par défaut des fenêtres de type Item (techniques, armes...) en pixels" }, "Compendium": { + "HideDisabledSources": { + "Title": "[Compendium] Cacher les sources vides", + "Hint": "Cache les sources sans éléments du filtre source." + }, "HideEmptySourcesFromPlayers": { - "Title": "[Compendium] Cacher les sources sans référence", + "Title": "[Compendium] Cacher les éléments ayant une référence vide", "Hint": "Utile pour cacher les éléments qui sont partiellement terminés." }, "AllowedOfficialSources": { @@ -91,6 +95,7 @@ "placeholder": "Sources à filtrer", "player_filter_label": "Filtre joueur", "player_filter_tooltip": "Applique le filtre des joueurs", + "already_in_filter": "Filtre déjà appliqué", "sources_categories": { "rules": "Règles", "adventures": "Aventures", diff --git a/system/lang/it-it.json b/system/lang/it-it.json index c0e7db2..a598028 100644 --- a/system/lang/it-it.json +++ b/system/lang/it-it.json @@ -35,9 +35,13 @@ "Hint": "Set the default height for 'Items' windows types (techniques, weapons...), in pixels" }, "Compendium": { + "HideDisabledSources": { + "Title": "[Compendium] Hide sources filter without reference", + "Hint": "Hide empty source with no elements in source filter." + }, "HideEmptySourcesFromPlayers": { - "Title": "[Compendium] Hide sources without a reference", - "Hint": "Useful for hiding items etc. that are partially done" + "Title": "[Compendium] Hide elements with empty reference", + "Hint": "Basically require a reference to be set in order for players to view the content in compendiums" }, "AllowedOfficialSources": { "Title": "[Compendium] Available official resources", @@ -91,6 +95,7 @@ "placeholder": "Filter Sources", "player_filter_label": "Player filter", "player_filter_tooltip": "Apply player filter", + "already_in_filter": "Already in filter", "sources_categories": { "rules": "Rules", "adventures": "Adventures", diff --git a/system/scripts/hooks.js b/system/scripts/hooks.js index 11e5327..9860c6a 100644 --- a/system/scripts/hooks.js +++ b/system/scripts/hooks.js @@ -226,230 +226,183 @@ export default class HooksL5r5e { } /** - * Compendium display + * Compendium display (Add filters) */ static async renderCompendium(app, html, data) { if (app.collection.documentName === "Item") { const content = await app.collection.getDocuments(); - let sources_in_this_compendium = new Set([]); - let filters_to_show = { + const sourcesInThisCompendium = new Set([]); + const filtersToShow = { rank: false, rarity: false, source: false, ring: false, - } + }; + + // Cache + const header = html.find(".directory-header"); + const entries = html.find(".directory-item"); // Add additional data to the entries to make it faster to lookup. // Add Ring/rank/rarity information for (const document of content) { - const entry = html.find(`[data-document-id="${document.id}"]`); - if(document.system?.rank) { + const entry = entries.filter(`[data-document-id="${document.id}"]`); + + if (document.system?.rank) { + filtersToShow.rank = true; entry.data("rank", document.system.rank); - filters_to_show.rank = true; } - if(document.system?.source_reference) { - sources_in_this_compendium.add(document.system.source_reference.source); + if (document.system?.source_reference) { + filtersToShow.source = true; + sourcesInThisCompendium.add(document.system.source_reference.source); entry.data("source", document.system.source_reference); - filters_to_show.source = true; } - if(document.system?.ring) { + if (document.system?.ring) { + filtersToShow.ring = true; entry.data("ring", document.system.ring); - filters_to_show.ring = true } - if(document.system?.rarity) { + if (document.system?.rarity) { + filtersToShow.rarity = true; entry.data("rarity", document.system.rarity); - filters_to_show.rarity = true; } // Add ring/rank/rarity information on the item in the compendium view - if(document.system?.ring || document.system?.rarity || document.system?.ring) { - const ring_rarity_rank = await renderTemplate(`${CONFIG.l5r5e.paths.templates}compendium/ring-rarity-rank.html`, document.system); - entry.append(ring_rarity_rank); + if (document.system?.ring || document.system?.rarity || document.system?.rank) { + const ringRarityRank = await renderTemplate(`${CONFIG.l5r5e.paths.templates}compendium/ring-rarity-rank.html`, document.system); + entry.append(ringRarityRank); } } - //Setup what the player cannot see. - const officialContent = game.settings.get(CONFIG.l5r5e.namespace, "compendium-official-content-for-players"); - const unofficialContent = game.settings.get(CONFIG.l5r5e.namespace, "compendium-unofficial-content-for-players"); - const unavailableSourceForPlayers = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references") - .filter((element) => { - if(CONFIG.l5r5e.sourceReference[element]) { + // Setup filters + const officialContent = game.settings.get(CONFIG.l5r5e.namespace, "compendium-official-content-for-players"); + const unofficialContent = game.settings.get(CONFIG.l5r5e.namespace, "compendium-unofficial-content-for-players"); + const allCompendiumReferences = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references") + const hideEmptySourcesFromPlayers = game.settings.get(CONFIG.l5r5e.namespace, "compendium-hide-empty-sources-from-players"); + + const unavailableSourceForPlayers = allCompendiumReferences.filter((element) => { + if (CONFIG.l5r5e.sourceReference[element]) { return officialContent.length > 0 ? !officialContent.includes(element) : false; } return unofficialContent.length > 0 ? !unofficialContent.includes(element) : false; }); - // Create the function that will hide/show elements based on various factors - const header = html.find(".directory-header"); - const applyCompendiumFilter = function() { - const rank_filter = header.find(".rank-filter").find(".selected").data("rank"); - const user_filter = header.find("l5r5e-multi-select").val(); - const ring_filter = header.find(".ring-filter").find(".selected").data("ring"); - const rarity_filter = header.find(".rarity-filter").find(".selected").data("rarity"); + // Create filter function + const applyCompendiumFilter = () => { + const userFilter = header.find("l5r5e-multi-select").val(); + const rankFilter = header.find(".rank-filter .selected").data("rank"); + const ringFilter = header.find(".ring-filter .selected").data("ring"); + const rarityFilter = header.find(".rarity-filter .selected").data("rarity"); - $(html).find(".directory-item").each(function() { + entries.each(function () { const lineSource = $(this).data("source")?.source; - if(lineSource === null || lineSource === undefined) { - return; // We might have stuff in the compendium view that does not have a source (folders etc.) Ignore those. + + // We might have stuff in the compendium view that does not have a source (folders etc.) Ignore those. + if (lineSource === null || lineSource === undefined) { + return; } - let should_show = true; - if(unavailableSourceForPlayers.includes(lineSource)) { - if(game.user.isGM) { - should_show &= true; - $(this).addClass("not-for-players"); - $(this).attr("data-tooltip", game.i18n.localize("l5r5e.compendium.not_for_players")); - } - else { - should_show &= false; + let shouldShow = true; + + // Handle unavailable sources + if (unavailableSourceForPlayers.includes(lineSource)) { + if (game.user.isGM) { + shouldShow &= true; + $(this) + .addClass("not-for-players") + .attr("data-tooltip", game.i18n.localize("l5r5e.compendium.not_for_players")); + } else { + shouldShow &= false; } } - if(lineSource === "" && game.settings.get(CONFIG.l5r5e.namespace, "compendium-hide-empty-sources-from-players")) { - if(game.user.isGM) { - should_show &= true; - $(this).addClass("not-for-players"); - $(this).attr("data-tooltip", game.i18n.localize("l5r5e.compendium.not_for_players")) - } - else { - should_show &= false; + // Handle empty sources + if (lineSource === "" && hideEmptySourcesFromPlayers) { + if (game.user.isGM) { + shouldShow &= true; + $(this) + .addClass("not-for-players") + .attr("data-tooltip", game.i18n.localize("l5r5e.compendium.not_for_players")); + } else { + shouldShow &= false; } } - if(rank_filter) { - should_show &= $(this).data("rank") == rank_filter; + // Apply filters + if (rankFilter) { + shouldShow &= $(this).data("rank") == rankFilter; + } + if (userFilter.length) { + shouldShow &= userFilter.includes(lineSource); + } + if (ringFilter) { + shouldShow &= $(this).data("ring") == ringFilter; + } + if (rarityFilter >= 0) { + shouldShow &= $(this).data("rarity") == rarityFilter; } - if(user_filter.length) { - should_show &= user_filter.includes(lineSource); - } - - if(ring_filter) { - should_show &= $(this).data("ring") == ring_filter - } - - if(rarity_filter >= 0) { - should_show &= $(this).data("rarity") == rarity_filter - } - - if(should_show) { - $(this).show(); - } else { - $(this).hide(); - } + // Show or hide this entry based on the result + shouldShow ? $(this).show() : $(this).hide(); }); - } + }; - // Rank filter: Add the HTML element and on click handling - if (filters_to_show.rank) { - header.append(await renderTemplate(`${CONFIG.l5r5e.paths.templates}compendium/rank-filter.html`, {type: "rank", number:[1,2,3,4,5]})); - header.find(".rank-filter").children().each(function() { - $(this).on("click", (event, rank=$(this).data("rank")) => { - const already_selected = $(event.target).hasClass("selected"); - $(html).find(".rank-filter").children().each(function() { - $(this).removeClass("selected"); - }); - - // Only select valid values - if(rank) { - $(event.target).addClass("selected"); - } - // we click the same button to unselect - if(already_selected) { - $(event.target).removeClass("selected"); - } + // Filter setup + const addFilter = async (filterType, templateFile, templateData) => { + if (!filtersToShow[filterType]) { + return; + } + const filterTemplate = await renderTemplate( + `${CONFIG.l5r5e.paths.templates}compendium/${templateFile}.html`, + templateData + ); + header.append(filterTemplate); + header.find(`.${filterType}-filter`).children().each(function () { + $(this).on("click", (event) => { + const selected = $(event.target).hasClass("selected"); + header.find(`.${filterType}-filter`).children().removeClass("selected"); + $(event.target).toggleClass("selected", !selected); applyCompendiumFilter(); }); }); - } + }; - if(filters_to_show.rarity) { - header.append(await renderTemplate(`${CONFIG.l5r5e.paths.templates}compendium/rank-filter.html`, {type: "rarity", number:[0,1,2,3,4,5,6,7,8,9,10]})); - header.find(".rarity-filter").children().each(function() { - $(this).on("click", (event, rarity=$(this).data("rarity")) => { - const already_selected = $(event.target).hasClass("selected"); - $(html).find(".rarity-filter").children().each(function() { - $(this).removeClass("selected"); - }); - - // Only select valid values - if(Number.isInteger(rarity)) { - $(event.target).addClass("selected"); - } - // we click the same button to unselect - if(already_selected) { - $(event.target).removeClass("selected"); - } - - applyCompendiumFilter(); - }); - }); - } - - if(filters_to_show.ring) { - header.append(await renderTemplate(`${CONFIG.l5r5e.paths.templates}compendium/ring-filter.html`)); - header.find(".ring-filter").children().each(function() { - $(this).on("click", (event, ring=$(this).data("ringid")) => { - const already_selected = $(event.target).hasClass("selected"); - $(html).find(".ring-filter").children().each(function() { - $(this).removeClass("selected"); - }); - - if(ring) { // Do not keep the "reset" button highlighted - $(event.target).addClass("selected"); - } - // we click the same button to unselect - if(already_selected) { - $(event.target).removeClass("selected"); - } - - applyCompendiumFilter(); - }); - }); - } - - if(filters_to_show.source) { - // Setup the source select and add it to the document with change callback - const selectable_sources = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references") - .map((reference) => { - return { - disable: !sources_in_this_compendium.has(reference), - source: reference - } - }) - .map((reference) => { - return { - value: reference.source, - label: CONFIG.l5r5e.sourceReference[reference.source]?.label ?? reference.source, - translate: true, - group: CONFIG.l5r5e.sourceReference[reference.source]?.type.split(",")[0] ?? "l5r5e.multiselect.sources_categories.others", - disabled: reference.disable - } - }); + // Add Rank, Rarity, Ring Filters + await Promise.all([ + addFilter('rank' , 'rank-filter', { type: "rank", number: [1, 2, 3, 4, 5] }), + addFilter('rarity', 'rank-filter', { type: "rarity", number: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }), + addFilter('ring' , 'ring-filter', {}), + ]); + if (filtersToShow.source) { + // Build the source select + const selectableSources = allCompendiumReferences.map((reference) => ({ + value: reference, + label: CONFIG.l5r5e.sourceReference[reference]?.label ?? reference, + translate: true, + group: CONFIG.l5r5e.sourceReference[reference]?.type.split(",")[0] ?? "l5r5e.multiselect.sources_categories.others", + disabled: !sourcesInThisCompendium.has(reference) || (!game.user.isGM && unavailableSourceForPlayers.includes(reference)) + })); const filterSourcesBox = L5r5eHtmlMultiSelectElement.create({ name: "filter-sources", - options: selectable_sources, + options: selectableSources, localize: true, }); header.append(filterSourcesBox.outerHTML); - $("l5r5e-multi-select").on("change", (event) => { - applyCompendiumFilter(); - }); + $("l5r5e-multi-select").on("change", applyCompendiumFilter); - // If gm add a extra button to easily filter the content to see the same stuff as a player + // If gm add an extra button to easily filter the content to see the same stuff as a player if (game.user.isGM && unavailableSourceForPlayers.length > 0) { const buttonHTML = `' + + game.i18n.localize('l5r5e.multiselect.player_filter_label') + + '' - const filterPlayerView = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references") + const filterPlayerView = allCompendiumReferences .filter((item) => !unavailableSourceForPlayers.includes(item)) - .filter((item) => sources_in_this_compendium.has(item)); + .filter((item) => sourcesInThisCompendium.has(item)); $(buttonHTML).appendTo($(header).find("l5r5e-multi-select")).click(function() { header.find("l5r5e-multi-select")[0].value = filterPlayerView; @@ -457,18 +410,31 @@ export default class HooksL5r5e { } } - // TODO find a better way + // TODO: This delay is a workaround and should be addressed in another way. // This is ugly but if we hide the content too early then it won't be hidden for some reason. // Current guess is that the foundry search filter is doing something. // Adding a delay here so that we hide the content. This will fail on slow computers/network... setTimeout(() => { applyCompendiumFilter(); - }, 250) + }, 250); return false; } } + static updateCompendium(pack, documents, options, userId) { + documents.forEach((document) => { + const inc_reference = document?.system?.source_reference?.source?.trim(); + if (!!inc_reference) { + const references = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references"); + if (!references.includes(inc_reference)) { + references.push(inc_reference); + game.settings.set(CONFIG.l5r5e.namespace, "all-compendium-references", references); + } + } + }); + } + /** * DiceSoNice - Add L5R DicePresets */ @@ -530,19 +496,6 @@ export default class HooksL5r5e { } } - static updateCompendium(pack, documents, options, userId) { - documents.forEach((document) => { - const inc_reference = document?.system?.source_reference?.source; - if(inc_reference) { - const references = game.settings.get(CONFIG.l5r5e.namespace, "all-compendium-references"); - if(!references.includes(inc_reference)) { - references.push(inc_reference); - game.settings.set(CONFIG.l5r5e.namespace, "all-compendium-references", references); - } - } - }) - } - /** * Attempt to create a macro from the dropped data. Will use an existing macro if one exists. * @param {object} dropData The dropped data diff --git a/system/scripts/migration.js b/system/scripts/migration.js index a18bb3c..9397507 100644 --- a/system/scripts/migration.js +++ b/system/scripts/migration.js @@ -334,9 +334,8 @@ export class MigrationL5r5e { updateData["system.source_reference.page"] = bookReference[2]; } - // TODO uncomment before release // Delete the old key - //updateData["system.-=book_reference"] = null; + updateData["system.-=book_reference"] = null; } } // ***** End of 1.12.3 ***** diff --git a/system/scripts/misc/l5r5e-multiselect.js b/system/scripts/misc/l5r5e-multiselect.js index 6220de2..a126b47 100644 --- a/system/scripts/misc/l5r5e-multiselect.js +++ b/system/scripts/misc/l5r5e-multiselect.js @@ -117,6 +117,7 @@ export class L5r5eHtmlMultiSelectElement extends AbstractMultiSelectElement { } // Disable selected options + const hideDisabled = game.settings.get(CONFIG.l5r5e.namespace, "compendium-hide-disabled-sources"); for (const option of this.#select) { if (this._value.has(option.value)) { option.disabled = true; @@ -125,6 +126,7 @@ export class L5r5eHtmlMultiSelectElement extends AbstractMultiSelectElement { } if (this.#disabledValues.has(option.value)) { option.disabled = true; + option.hidden = hideDisabled; continue; } option.disabled = false; diff --git a/system/scripts/settings.js b/system/scripts/settings.js index 152a66a..934ab6e 100644 --- a/system/scripts/settings.js +++ b/system/scripts/settings.js @@ -98,6 +98,15 @@ export const RegisterSettings = function () { scope: "world", }); + game.settings.register(CONFIG.l5r5e.namespace, "compendium-hide-disabled-sources", { + name: "SETTINGS.Compendium.HideDisabledSources.Title", + hint: "SETTINGS.Compendium.HideDisabledSources.Hint", + type: Boolean, + default: true, + config: true, + scope: "world", + }); + /* ------------------------------------ */ /* Client preferences */ /* ------------------------------------ */