Implement a button to create items of a specific subtype in the player inventory (closes #120)

This commit is contained in:
Oliver-Akins 2024-03-22 23:35:04 -06:00
parent b3e699bc32
commit 96f5b17785
9 changed files with 73 additions and 21 deletions

View file

@ -49,7 +49,8 @@
"v2": { "v2": {
"stat-not-chosen": "Select a dice to see the {name} skills", "stat-not-chosen": "Select a dice to see the {name} skills",
"skill-roll-locked": "@dotdungeon.trainingLevel.locked", "skill-roll-locked": "@dotdungeon.trainingLevel.locked",
"toggle-item-information": "Toggle the item's extra information visibility" "toggle-item-information": "Toggle the item's extra information visibility",
"create-item": "Create @TYPES.Item.{type}"
} }
}, },
"item": { "item": {

View file

@ -1,6 +1,7 @@
export class DotDungeonActor extends Actor { export class DotDungeonActor extends Actor {
async createEmbeddedItem(defaults, opts = {}) { async createEmbeddedItem(defaults, opts = {}) {
let items = await this.createEmbeddedDocuments(`Item`, defaults); let items = await this.createEmbeddedDocuments(`Item`, defaults);
if (!Array.isArray(items)) items = items ? [items] : [];
if (items.length == 0) { if (items.length == 0) {
throw new Error(`Failed to create any items`); throw new Error(`Failed to create any items`);
}; };

View file

@ -3,7 +3,7 @@ import { createArray } from "./createArray.mjs";
import { detailsExpanded } from "./detailsExpanded.mjs"; import { detailsExpanded } from "./detailsExpanded.mjs";
import { objectValue } from "./objectValue.mjs"; import { objectValue } from "./objectValue.mjs";
import { toFriendlyDuration } from "./toFriendlyDuration.mjs"; import { toFriendlyDuration } from "./toFriendlyDuration.mjs";
import { localizer } from "../utils/localizer.mjs"; import { handlebarsLocalizer } from "../utils/localizer.mjs";
import { options } from "./options.mjs"; import { options } from "./options.mjs";
export default { export default {
@ -14,7 +14,7 @@ export default {
"dd-toFriendlyDuration": toFriendlyDuration, "dd-toFriendlyDuration": toFriendlyDuration,
"dd-objectValue": objectValue, "dd-objectValue": objectValue,
"dd-expanded": detailsExpanded, "dd-expanded": detailsExpanded,
"dd-i18n": localizer, "dd-i18n": handlebarsLocalizer,
"dd-options": options, "dd-options": options,
// Simple helpers // Simple helpers

View file

@ -136,6 +136,7 @@ export class PlayerSheetv2 extends GenericActorSheet {
filters[type] = { filters[type] = {
label: localizer(`TYPES.Item.${type}`), label: localizer(`TYPES.Item.${type}`),
active: !this._itemTypesHidden.has(type), active: !this._itemTypesHidden.has(type),
createLabel: localizer(`dotdungeon.sheet.actor.v2.create-item`, {type}),
}; };
}; };
return filters; return filters;

View file

@ -1,3 +1,4 @@
import { localizer } from "../utils/localizer.mjs";
import DOTDUNGEON from "../config.mjs"; import DOTDUNGEON from "../config.mjs";
export class GenericActorSheet extends ActorSheet { export class GenericActorSheet extends ActorSheet {
@ -127,11 +128,11 @@ export class GenericActorSheet extends ActorSheet {
async genericEmbeddedCreate($event) { async genericEmbeddedCreate($event) {
const data = $event.currentTarget.dataset; const data = $event.currentTarget.dataset;
if (!this[`createCustom${data.embeddedCreate}`]) { if (!this[`createCustom${data.embeddedCreate}`]) {
this.createEmbeddedItem({ this.actor.createEmbeddedItem({
type: data.embeddedCreate, type: data.embeddedCreate,
name: localizer( name: localizer(
`dotdungeon.default.name`, `dotdungeon.default.name`,
{ document: `Actor`, type: data.embeddedCreate } { document: `Item`, type: data.embeddedCreate }
), ),
}); });
} else { } else {

View file

@ -1,8 +1,14 @@
import { localizerConfig } from "../config.mjs"; import { localizerConfig } from "../config.mjs";
export function localizer(key, args = {}, depth = 0) { export function handlebarsLocalizer(key, ...args) {
let data = args[0]
if (args.length === 1) data = args[0].hash;
if (key instanceof Handlebars.SafeString) key = key.toString(); if (key instanceof Handlebars.SafeString) key = key.toString();
const localized = localizer(key, data);
return localized;
};
export function localizer(key, args = {}, depth = 0) {
/** @type {string} */ /** @type {string} */
let localized = game.i18n.format(key, args); let localized = game.i18n.format(key, args);
const subkeys = localized.matchAll(localizerConfig.subKeyPattern); const subkeys = localized.matchAll(localizerConfig.subKeyPattern);

View file

@ -27,6 +27,40 @@
} }
} }
.item-group {
&__header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center
}
&__create-item {
@include material.elevate(2);
color: var(--inventory-create-item-font-color);
&:hover {
@include material.elevate(4);
}
&:focus-visible, &:active {
@include material.elevate(8);
}
}
&__list {
&--material {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
&--untyped {
display: flex;
gap: 8px;
flex-direction: column;
}
}
}
.panel { .panel {
padding: 8px; padding: 8px;
border-radius: 4px; border-radius: 4px;
@ -54,7 +88,7 @@
background: hsl( from currentColor / 50% ); // probably gonna need color-mix background: hsl( from currentColor / 50% ); // probably gonna need color-mix
} }
&:focus-visible { &:focus-visible, &:active {
border-width: 2px; border-width: 2px;
border-color: currentColor; border-color: currentColor;
background: var(--elevation-8dp-bg); background: var(--elevation-8dp-bg);
@ -94,18 +128,6 @@
} }
} }
.material-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.untyped-list {
display: flex;
gap: 8px;
flex-direction: column;
}
.material { .material {
@include material.elevate(1); @include material.elevate(1);
padding: 8px; padding: 8px;
@ -174,6 +196,7 @@
display: flex; display: flex;
gap: 8px; gap: 8px;
align-items: center; align-items: center;
justify-content: space-between;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
@ -188,6 +211,7 @@
} }
&__name { &__name {
flex-grow: 1; flex-grow: 1;
cursor: inherit;
font-family: var(--inventory-item-name-font); font-family: var(--inventory-item-name-font);
font-size: var(--inventory-item-name-font-size); font-size: var(--inventory-item-name-font-size);
} }

View file

@ -48,6 +48,9 @@ $body-font: sans-serif;
--skill-training-select-text-color: #{$on-surface}; --skill-training-select-text-color: #{$on-surface};
--skill-roll-button-text-color: #{$on-surface}; --skill-roll-button-text-color: #{$on-surface};
/* General variables for inventory tab */
--inventory-create-item-font-color: white;
/* Common Inventory Item Variables */ /* Common Inventory Item Variables */
--inventory-item-name-font: #{$body-font}; --inventory-item-name-font: #{$body-font};
--inventory-item-name-font-size: 0.875rem; --inventory-item-name-font-size: 0.875rem;

View file

@ -7,9 +7,24 @@
{{#each computed.itemFilters as | filter |}} {{#each computed.itemFilters as | filter |}}
{{#if filter.active}} {{#if filter.active}}
<section class="item-list__group"> <section class="item-list__group">
<h2>{{filter.label}}</h2> <div class="item-group__header">
<h2>{{filter.label}}</h2>
<button
type="button"
class="item-group__create-item reduced-padding equal-padding"
data-embedded-create="{{@key}}"
aria-label="{{filter.createLabel}}"
data-tooltip="{{filter.createLabel}}"
data-tooltip-direction="LEFT"
>
<div aria-hidden="true" class="icon icon--16">
{{{ @root.icons.create }}}
</div>
</button>
</div>
<hr> <hr>
<div class="{{@key}}-list"> <div class="item-group__list--{{@key}}">
{{#each (lookup @root.items @key) as | item |}} {{#each (lookup @root.items @key) as | item |}}
{{> (concat "dotdungeon.pc.v2." @../key) @root item=item}} {{> (concat "dotdungeon.pc.v2." @../key) @root item=item}}
{{else}} {{else}}