diff --git a/lang/en.json b/lang/en.json index 36b68ad7..adaaf63f 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2399,7 +2399,11 @@ "environments": "Environments", "beastforms": "Beastforms", "features": "Features", - "items": "Items" + "items": "Items", + "weapons": "Weapons", + "armors": "Armors", + "consumables": "Consumables", + "loots": "Loots" } }, "Notifications": { diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index ba98ef61..cf20934e 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -425,8 +425,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl equipment = ['armor', 'weapon']; const presets = { - compendium: 'daggerheart', - folder: equipment.includes(type) ? 'equipments' : type, + folder: equipment.includes(type) ? `equipments.folders.${type}s` : type, render: { noFolder: true } diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index 0b3f8970..389e32f2 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -539,8 +539,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) static async viewCompendium(event, target) { const type = target.dataset.compendium ?? target.dataset.type; - const presets = { - compendium: 'daggerheart', + const presets = { folder: type, render: { noFolder: true diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index de9fe53d..b9135b46 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -577,28 +577,27 @@ export default function DHApplicationMixin(Base) { static async #browseItem(event, target) { const type = target.dataset.compendium ?? target.dataset.type; - const presets = {}; + const presets = { + render: { + noFolder: true + } + }; switch (type) { case 'loot': + presets.folder = 'equipments.folders.loots'; + break; case 'consumable': + presets.folder = 'equipments.folders.consumables'; + break; case 'armor': + presets.folder = 'equipments.folders.armors'; + break; case 'weapon': - presets.compendium = 'daggerheart'; - presets.folder = 'equipments'; - presets.render = { - noFolder: true - }; - presets.filter = { - type: { key: 'type', value: type, forced: true } - }; + presets.folder = 'equipments.folders.weapons'; break; case 'domainCard': - presets.compendium = 'daggerheart'; presets.folder = 'domains'; - presets.render = { - noFolder: true - }; presets.filter = { 'level.max': { key: 'level.max', value: this.document.system.levelData.level.current }, 'system.domain': { key: 'system.domain', value: this.document.system.domains } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 8eefd9cd..9c534c91 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -17,8 +17,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig; this.presets = options.presets; - if (this.presets?.compendium && this.presets?.folder) - ItemBrowser.selectFolder.call(this, null, null, this.presets.compendium, this.presets.folder); + if (this.presets?.folder) + ItemBrowser.selectFolder.call(this, null, null, this.presets.folder); } /** @inheritDoc */ @@ -115,7 +115,10 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { if (this.presets?.filter) { Object.entries(this.presets.filter).forEach( - ([k, v]) => (this.fieldFilter.find(c => c.name === k).value = v.value) + ([k, v]) => { + const filter = this.fieldFilter.find(c => c.name === k) + if(filter) filter.value = v.value; + } ); await this._onInputFilterBrowser(); } @@ -162,16 +165,34 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { : []; folders.push(folder); }); + folders.sort((a, b) => a.label.localeCompare(b.label)) return folders; } - static async selectFolder(_, target, compend, folder) { - const config = foundry.utils.deepClone(this.config), - compendium = compend ?? target.closest('[data-compendium-id]').dataset.compendiumId, - folderId = folder ?? target.dataset.folderId, - folderPath = `${compendium}.folders.${folderId}`, - folderData = foundry.utils.getProperty(config, folderPath); + static async selectFolder(_, target, folder) { + + let loadTimeout = ItemBrowser.toggleLoader(target, true); + + const folderId = folder ?? target.dataset.folderId, + folderData = foundry.utils.getProperty(this.config, folderId) ?? {}, + promises = []; + + game.packs.forEach(pack => { + promises.push( + new Promise(async resolve => { + const items = await pack.getDocuments({ type__in: folderData?.type }); + resolve(items); + }) + ) + }); + + Promise.all(promises).then(result => { + this.items = ItemBrowser.sortBy(result.flatMap(r => r), 'name'); + clearTimeout(loadTimeout); + ItemBrowser.toggleLoader(target, false); + this.render({ force: true }); + }); const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({ ...col, @@ -179,22 +200,13 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { })); this.selectedMenu = { - path: folderPath.split('.'), + path: folderId.split('.'), data: { ...folderData, columns: columns } }; - let items = []; - for (const key of folderData.keys) { - const comp = game.packs.get(`${compendium}.${key}`); - if (!comp) return; - items = items.concat(await comp.getDocuments({ type__in: folderData.type })); - } - - this.items = ItemBrowser.sortBy(items, 'name'); - if (target) { target .closest('.compendium-sidebar') @@ -202,8 +214,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { .forEach(element => element.classList.remove('is-selected')); target.classList.add('is-selected'); } - - this.render({ force: true }); } _replaceHTML(result, content, options) { @@ -211,6 +221,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { super._replaceHTML(result, content, options); } + static toggleLoader(target, state) { + if(!target) return; + const container = target.closest(".window-content"); + return setTimeout(() => { + container.classList.toggle("loader", state); + }, 100); + } + static expandContent(_, target) { const parent = target.parentElement; parent.classList.toggle('expanded'); diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index d5afe72e..23c8d53b 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -149,6 +149,104 @@ export const typeConfig = { } ] }, + weapons: { + columns: [ + { + key: 'system.secondary', + label: 'DAGGERHEART.UI.ItemBrowser.subtype', + format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-') + }, + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular' + } + ], + filters: [ + { + key: 'system.secondary', + label: 'DAGGERHEART.UI.ItemBrowser.subtype', + choices: [ + { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' }, + { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' } + ] + }, + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular', + choices: [ + { value: '1', label: '1' }, + { value: '2', label: '2' }, + { value: '3', label: '3' }, + { value: '4', label: '4' } + ] + }, + { + key: 'system.burden', + label: 'DAGGERHEART.GENERAL.burden', + field: 'system.api.models.items.DHWeapon.schema.fields.burden' + }, + { + key: 'system.attack.roll.trait', + label: 'DAGGERHEART.GENERAL.Trait.single', + field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' + }, + { + key: 'system.attack.range', + label: 'DAGGERHEART.GENERAL.range', + field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' + }, + { + key: 'system.itemFeatures', + label: 'DAGGERHEART.GENERAL.features', + choices: () => + Object.entries(CONFIG.DH.ITEM.weaponFeatures) + .map(([k, v]) => ({ value: k, label: v.label })), + operator: 'contains3' + } + ] + }, + armors: { + columns: [ + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular' + } + ], + filters: [ + { + key: 'system.tier', + label: 'DAGGERHEART.GENERAL.Tiers.singular', + choices: [ + { value: '1', label: '1' }, + { value: '2', label: '2' }, + { value: '3', label: '3' }, + { value: '4', label: '4' } + ] + }, + { + key: 'system.baseScore', + name: 'armor.min', + label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMin', + field: 'system.api.models.items.DHArmor.schema.fields.baseScore', + operator: 'gte' + }, + { + key: 'system.baseScore', + name: 'armor.max', + label: 'DAGGERHEART.UI.ItemBrowser.armorScoreMax', + field: 'system.api.models.items.DHArmor.schema.fields.baseScore', + operator: 'lte' + }, + { + key: 'system.itemFeatures', + label: 'DAGGERHEART.GENERAL.features', + choices: () => + Object.entries(CONFIG.DH.ITEM.armorFeatures) + .map(([k, v]) => ({ value: k, label: v.label })), + operator: 'contains3' + } + ] + }, features: { columns: [], filters: [] @@ -305,10 +403,10 @@ export const typeConfig = { }; export const compendiumConfig = { - daggerheart: { - id: 'daggerheart', - label: 'DAGGERHEART', - folders: { + // daggerheart: { + // id: 'daggerheart', + // label: 'DAGGERHEART', + // folders: { adversaries: { id: 'adversaries', keys: ['adversaries'], @@ -321,28 +419,56 @@ export const compendiumConfig = { keys: ['ancestries'], label: 'DAGGERHEART.UI.ItemBrowser.folders.ancestries', type: ['ancestry'], - folders: { + /* folders: { features: { id: 'features', keys: ['ancestries'], label: 'DAGGERHEART.UI.ItemBrowser.folders.features', type: ['feature'] } - } + } */ }, equipments: { id: 'equipments', keys: ['armors', 'weapons', 'consumables', 'loot'], label: 'DAGGERHEART.UI.ItemBrowser.folders.equipment', type: ['armor', 'weapon', 'consumable', 'loot'], - listType: 'items' + listType: 'items', + folders: { + weapons: { + id: 'weapons', + keys: ['weapons'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.weapons', + type: ['weapon'], + listType: 'weapons' + }, + armors: { + id: 'armors', + keys: ['armors'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.armors', + type: ['armor'], + listType: 'armors' + }, + consumables: { + id: 'consumables', + keys: ['consumables'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.consumables', + type: ['consumable'] + }, + loots: { + id: 'loots', + keys: ['loots'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.loots', + type: ['loot'] + } + } }, classes: { id: 'classes', keys: ['classes'], label: 'DAGGERHEART.UI.ItemBrowser.folders.classes', type: ['class'], - folders: { + /* folders: { features: { id: 'features', keys: ['classes'], @@ -356,7 +482,7 @@ export const compendiumConfig = { type: ['armor', 'weapon', 'consumable', 'loot'], listType: 'items' } - }, + }, */ listType: 'classes' }, subclasses: { @@ -378,14 +504,14 @@ export const compendiumConfig = { keys: ['communities'], label: 'DAGGERHEART.UI.ItemBrowser.folders.communities', type: ['community'], - folders: { + /* folders: { features: { id: 'features', keys: ['communities'], label: 'DAGGERHEART.UI.ItemBrowser.folders.features', type: ['feature'] } - } + } */ }, environments: { id: 'environments', @@ -399,15 +525,21 @@ export const compendiumConfig = { label: 'DAGGERHEART.UI.ItemBrowser.folders.beastforms', type: ['beastform'], listType: 'beastforms', - folders: { + /* folders: { features: { id: 'features', keys: ['beastforms'], label: 'DAGGERHEART.UI.ItemBrowser.folders.features', type: ['feature'] } - } + } */ + }, + features: { + id: 'features', + keys: ['features'], + label: 'DAGGERHEART.UI.ItemBrowser.folders.features', + type: ['feature'] } - } - } + // } + // } }; diff --git a/styles/less/global/global.less b/styles/less/global/global.less index cf8431b5..bf084751 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -23,4 +23,27 @@ color: var(--color-form-hint-hover); } } + + .loader { + div { + opacity: .5; + // transition: opacity .3s ease-in-out; + } + + &:before { + font-family: "Font Awesome 6 Pro"; + content: '\f110'; + position: absolute; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + animation: spinner 1.5s linear infinite; + } + } + + @keyframes spinner { + to { transform: rotate(360deg); } + } } \ No newline at end of file diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index ab9db27c..276d6e2c 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -71,6 +71,7 @@ } .compendium-results { + position: relative; padding: 16px; } @@ -101,10 +102,14 @@ .folder-list, .item-list-header, .item-header > div { - gap: 10px; cursor: pointer; } + .item-list-header, + .item-header > div { + gap: 10px; + } + .item-filter { display: flex; align-items: center; @@ -228,7 +233,8 @@ } .item-list-header, - .item-list { + .item-list, + .compendium-sidebar > .folder-list { overflow-y: auto; scrollbar-gutter: stable; scrollbar-width: thin; diff --git a/templates/ui/itemBrowser/sidebar.hbs b/templates/ui/itemBrowser/sidebar.hbs index 8df0aed3..f6c63328 100644 --- a/templates/ui/itemBrowser/sidebar.hbs +++ b/templates/ui/itemBrowser/sidebar.hbs @@ -1,32 +1,49 @@