mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-16 05:31:07 +01:00
finish inventory tab and add inital template to projects tab
This commit is contained in:
parent
f33c40d8f8
commit
18e275a471
8 changed files with 376 additions and 4 deletions
|
|
@ -1893,7 +1893,8 @@
|
||||||
"domains": "Domains",
|
"domains": "Domains",
|
||||||
"downtime": "Downtime",
|
"downtime": "Downtime",
|
||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"partyMembers": "Party Members"
|
"partyMembers": "Party Members",
|
||||||
|
"projects": "Projects"
|
||||||
},
|
},
|
||||||
"Tiers": {
|
"Tiers": {
|
||||||
"singular": "Tier",
|
"singular": "Tier",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import DHBaseActorSheet from '../api/base-actor.mjs';
|
import DHBaseActorSheet from '../api/base-actor.mjs';
|
||||||
import { getDocFromElement } from '../../../helpers/utils.mjs';
|
import { getDocFromElement } from '../../../helpers/utils.mjs';
|
||||||
|
import { ItemBrowser } from '../../ui/itemBrowser.mjs';
|
||||||
|
import FilterMenu from '../../ux/filter-menu.mjs';
|
||||||
|
|
||||||
export default class Party extends DHBaseActorSheet {
|
export default class Party extends DHBaseActorSheet {
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
@ -16,7 +18,8 @@ export default class Party extends DHBaseActorSheet {
|
||||||
toggleHope: Party.#toggleHope,
|
toggleHope: Party.#toggleHope,
|
||||||
toggleHitPoints: Party.#toggleHitPoints,
|
toggleHitPoints: Party.#toggleHitPoints,
|
||||||
toggleStress: Party.#toggleStress,
|
toggleStress: Party.#toggleStress,
|
||||||
toggleArmorSlot: Party.#toggleArmorSlot
|
toggleArmorSlot: Party.#toggleArmorSlot,
|
||||||
|
tempBrowser: Party.#tempBrowser
|
||||||
},
|
},
|
||||||
dragDrop: [{ dragSelector: '.actors-section .inventory-item', dropSelector: null }]
|
dragDrop: [{ dragSelector: '.actors-section .inventory-item', dropSelector: null }]
|
||||||
};
|
};
|
||||||
|
|
@ -30,18 +33,67 @@ export default class Party extends DHBaseActorSheet {
|
||||||
template: 'systems/daggerheart/templates/sheets/actors/party/resources.hbs',
|
template: 'systems/daggerheart/templates/sheets/actors/party/resources.hbs',
|
||||||
scrollable: ['.resources']
|
scrollable: ['.resources']
|
||||||
},
|
},
|
||||||
|
projects: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs',
|
||||||
|
scrollable: ['.projects']
|
||||||
|
},
|
||||||
|
inventory: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/actors/party/inventory.hbs',
|
||||||
|
scrollable: ['.inventory']
|
||||||
|
},
|
||||||
notes: { template: 'systems/daggerheart/templates/sheets/actors/party/notes.hbs' }
|
notes: { template: 'systems/daggerheart/templates/sheets/actors/party/notes.hbs' }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static TABS = {
|
static TABS = {
|
||||||
primary: {
|
primary: {
|
||||||
tabs: [{ id: 'partyMembers' }, { id: 'resources' }, { id: 'notes' }],
|
tabs: [
|
||||||
|
{ id: 'partyMembers' },
|
||||||
|
{ id: 'resources' },
|
||||||
|
{ id: 'projects' },
|
||||||
|
{ id: 'inventory' },
|
||||||
|
{ id: 'notes' }
|
||||||
|
],
|
||||||
initial: 'partyMembers',
|
initial: 'partyMembers',
|
||||||
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async _onRender(context, options) {
|
||||||
|
await super._onRender(context, options);
|
||||||
|
this._createFilterMenus();
|
||||||
|
this._createSearchFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Prepare Context */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
|
||||||
|
context.inventory = {
|
||||||
|
currency: {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
|
||||||
|
coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
|
||||||
|
handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
|
||||||
|
bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
|
||||||
|
chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const homebrewCurrency = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).currency;
|
||||||
|
if (homebrewCurrency.enabled) {
|
||||||
|
context.inventory.currency = homebrewCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.inventory.length === 0) {
|
||||||
|
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
async _preparePartContext(partId, context, options) {
|
async _preparePartContext(partId, context, options) {
|
||||||
context = await super._preparePartContext(partId, context, options);
|
context = await super._preparePartContext(partId, context, options);
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
|
|
@ -148,6 +200,162 @@ export default class Party extends DHBaseActorSheet {
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens Compedium Browser
|
||||||
|
*/
|
||||||
|
static async #tempBrowser(_, target) {
|
||||||
|
new ItemBrowser().render({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the set of ContextMenu options for Consumable and Loot.
|
||||||
|
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
|
||||||
|
* @this {CharacterSheet}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
static #getItemContextOptions() {
|
||||||
|
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Filter Tracking */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently active search filter.
|
||||||
|
* @type {foundry.applications.ux.SearchFilter}
|
||||||
|
*/
|
||||||
|
#search = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently active search filter.
|
||||||
|
* @type {FilterMenu}
|
||||||
|
*/
|
||||||
|
#menu = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks which item IDs are currently displayed, organized by filter type and section.
|
||||||
|
* @type {{
|
||||||
|
* inventory: {
|
||||||
|
* search: Set<string>,
|
||||||
|
* menu: Set<string>
|
||||||
|
* },
|
||||||
|
* loadout: {
|
||||||
|
* search: Set<string>,
|
||||||
|
* menu: Set<string>
|
||||||
|
* },
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
#filteredItems = {
|
||||||
|
inventory: {
|
||||||
|
search: new Set(),
|
||||||
|
menu: new Set()
|
||||||
|
},
|
||||||
|
loadout: {
|
||||||
|
search: new Set(),
|
||||||
|
menu: new Set()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Search Inputs */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize search filter instances for the inventory and loadout sections.
|
||||||
|
*
|
||||||
|
* Sets up two {@link foundry.applications.ux.SearchFilter} instances:
|
||||||
|
* - One for the inventory, which filters items in the inventory grid.
|
||||||
|
* - One for the loadout, which filters items in the loadout/card grid.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_createSearchFilter() {
|
||||||
|
//Filters could be a application option if needed
|
||||||
|
const filters = [
|
||||||
|
{
|
||||||
|
key: 'inventory',
|
||||||
|
input: 'input[type="search"].search-inventory',
|
||||||
|
content: '[data-application-part="inventory"] .items-section',
|
||||||
|
callback: this._onSearchFilterInventory.bind(this)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { key, input, content, callback } of filters) {
|
||||||
|
const filter = new foundry.applications.ux.SearchFilter({
|
||||||
|
inputSelector: input,
|
||||||
|
contentSelector: content,
|
||||||
|
callback
|
||||||
|
});
|
||||||
|
filter.bind(this.element);
|
||||||
|
this.#search[key] = filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle invetory items search and filtering.
|
||||||
|
* @param {KeyboardEvent} event The keyboard input event.
|
||||||
|
* @param {string} query The input search string.
|
||||||
|
* @param {RegExp} rgx The regular expression query that should be matched against.
|
||||||
|
* @param {HTMLElement} html The container to filter items from.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
async _onSearchFilterInventory(_event, query, rgx, html) {
|
||||||
|
this.#filteredItems.inventory.search.clear();
|
||||||
|
|
||||||
|
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||||
|
const item = await getDocFromElement(li);
|
||||||
|
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
|
||||||
|
if (matchesSearch) this.#filteredItems.inventory.search.add(item.id);
|
||||||
|
const { menu } = this.#filteredItems.inventory;
|
||||||
|
li.hidden = !(menu.has(item.id) && matchesSearch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Filter Menus */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
_createFilterMenus() {
|
||||||
|
//Menus could be a application option if needed
|
||||||
|
const menus = [
|
||||||
|
{
|
||||||
|
key: 'inventory',
|
||||||
|
container: '[data-application-part="inventory"]',
|
||||||
|
content: '.items-section',
|
||||||
|
callback: this._onMenuFilterInventory.bind(this),
|
||||||
|
target: '.filter-button',
|
||||||
|
filters: FilterMenu.invetoryFilters
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
menus.forEach(m => {
|
||||||
|
const container = this.element.querySelector(m.container);
|
||||||
|
this.#menu[m.key] = new FilterMenu(container, m.target, m.filters, m.callback, {
|
||||||
|
contentSelector: m.content
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when filters change
|
||||||
|
* @param {PointerEvent} event
|
||||||
|
* @param {HTMLElement} html
|
||||||
|
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
|
||||||
|
*/
|
||||||
|
async _onMenuFilterInventory(_event, html, filters) {
|
||||||
|
this.#filteredItems.inventory.menu.clear();
|
||||||
|
|
||||||
|
for (const li of html.querySelectorAll('.inventory-item')) {
|
||||||
|
const item = await getDocFromElement(li);
|
||||||
|
|
||||||
|
const matchesMenu =
|
||||||
|
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
|
||||||
|
if (matchesMenu) this.#filteredItems.inventory.menu.add(item.id);
|
||||||
|
|
||||||
|
const { search } = this.#filteredItems.inventory;
|
||||||
|
li.hidden = !(search.has(item.id) && matchesMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
async _onDragStart(event) {
|
async _onDragStart(event) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,13 @@ export default class DhParty extends BaseDataActor {
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }),
|
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }),
|
||||||
notes: new fields.HTMLField()
|
notes: new fields.HTMLField(),
|
||||||
|
gold: new fields.SchemaField({
|
||||||
|
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
handfuls: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
73
styles/less/sheets/actors/party/inventory.less
Normal file
73
styles/less/sheets/actors/party/inventory.less
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
@import '../../../utils/colors.less';
|
||||||
|
@import '../../../utils/fonts.less';
|
||||||
|
|
||||||
|
.application.sheet.daggerheart.actor.dh-style.party {
|
||||||
|
.tab.inventory {
|
||||||
|
.search-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
position: relative;
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 5px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 50px;
|
||||||
|
background: light-dark(@dark-blue-10, @golden-10);
|
||||||
|
border: none;
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
outline: 2px solid light-dark(@dark, @golden);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:placeholder {
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
align-content: center;
|
||||||
|
height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
z-index: 1;
|
||||||
|
color: light-dark(@dark-blue-50, @beige-50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
|
||||||
|
padding: 20px 0;
|
||||||
|
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currency-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 10px 0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
color: light-dark(@dark, @beige);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
.application.sheet.daggerheart.actor.dh-style.party {
|
.application.sheet.daggerheart.actor.dh-style.party {
|
||||||
.tab {
|
.tab {
|
||||||
|
height: -webkit-fill-available;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
@import './actors/party/header.less';
|
@import './actors/party/header.less';
|
||||||
@import './actors/party/party-members.less';
|
@import './actors/party/party-members.less';
|
||||||
@import './actors/party/sheet.less';
|
@import './actors/party/sheet.less';
|
||||||
|
@import './actors/party/inventory.less';
|
||||||
@import './actors/party/resources.less';
|
@import './actors/party/resources.less';
|
||||||
|
|
||||||
@import './items/beastform.less';
|
@import './items/beastform.less';
|
||||||
|
|
|
||||||
78
templates/sheets/actors/party/inventory.hbs
Normal file
78
templates/sheets/actors/party/inventory.hbs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<section class='tab {{tabs.inventory.cssClass}} {{tabs.inventory.id}}' data-tab='{{tabs.inventory.id}}'
|
||||||
|
data-group='{{tabs.inventory.group}}'>
|
||||||
|
<div class="search-section">
|
||||||
|
<div class="search-bar">
|
||||||
|
<div class="icon">
|
||||||
|
<i class="fa-solid fa-magnifying-glass"></i>
|
||||||
|
</div>
|
||||||
|
<input type="search" name="search" class="search-inventory" placeholder="Search...">
|
||||||
|
</div>
|
||||||
|
<a class="filter-button">
|
||||||
|
<i class="fa-solid fa-filter"></i>
|
||||||
|
</a>
|
||||||
|
<a data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.compendiumBrowser'}}" data-action="tempBrowser">
|
||||||
|
<i class="fa-solid fa-book-atlas"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="currency-section">
|
||||||
|
<div class="input">
|
||||||
|
<span>{{localize this.inventory.currency.coins}}</span>
|
||||||
|
{{formInput systemFields.gold.fields.coins value=source.system.gold.coins enriched=source.system.gold.coins
|
||||||
|
localize=true toggled=true}}
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<span>{{localize this.inventory.currency.handfuls}}</span>
|
||||||
|
{{formInput systemFields.gold.fields.handfuls value=source.system.gold.handfuls
|
||||||
|
enriched=source.system.gold.handfuls localize=true toggled=true}}
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<span>{{localize this.inventory.currency.bags}}</span>
|
||||||
|
{{formInput systemFields.gold.fields.bags value=source.system.gold.bags enriched=source.system.gold.bags
|
||||||
|
localize=true toggled=true}}
|
||||||
|
</div>
|
||||||
|
<div class="input">
|
||||||
|
<span>{{localize this.inventory.currency.chests}}</span>
|
||||||
|
{{formInput systemFields.gold.fields.chests value=source.system.gold.chests
|
||||||
|
enriched=source.system.gold.chests localize=true toggled=true}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="items-section">
|
||||||
|
{{> 'daggerheart.inventory-items'
|
||||||
|
title='TYPES.Item.weapon'
|
||||||
|
type='weapon'
|
||||||
|
collection=document.itemTypes.weapon
|
||||||
|
isGlassy=true
|
||||||
|
canCreate=true
|
||||||
|
hideResources=true
|
||||||
|
hideControls=true
|
||||||
|
}}
|
||||||
|
{{> 'daggerheart.inventory-items'
|
||||||
|
title='TYPES.Item.armor'
|
||||||
|
type='armor'
|
||||||
|
collection=document.itemTypes.armor
|
||||||
|
isGlassy=true
|
||||||
|
canCreate=true
|
||||||
|
hideResources=true
|
||||||
|
hideControls=true
|
||||||
|
}}
|
||||||
|
{{> 'daggerheart.inventory-items'
|
||||||
|
title='TYPES.Item.consumable'
|
||||||
|
type='consumable'
|
||||||
|
collection=document.itemTypes.consumable
|
||||||
|
isGlassy=true
|
||||||
|
canCreate=true
|
||||||
|
hideControls=true
|
||||||
|
}}
|
||||||
|
{{> 'daggerheart.inventory-items'
|
||||||
|
title='TYPES.Item.loot'
|
||||||
|
type='loot'
|
||||||
|
collection=document.itemTypes.loot
|
||||||
|
isGlassy=true
|
||||||
|
canCreate=true
|
||||||
|
hideControls=true
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
4
templates/sheets/actors/party/projects.hbs
Normal file
4
templates/sheets/actors/party/projects.hbs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<section class='tab {{tabs.projects.cssClass}} {{tabs.projects.id}}' data-tab='{{tabs.projects.id}}'
|
||||||
|
data-group='{{tabs.projects.group}}'>
|
||||||
|
<h2>Soon tm</h2>
|
||||||
|
</section>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue