Implement a button to create items of a specific subtype in the player inventory (closes #120)
This commit is contained in:
parent
b3e699bc32
commit
96f5b17785
9 changed files with 73 additions and 21 deletions
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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`);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
<div class="item-group__header">
|
||||||
<h2>{{filter.label}}</h2>
|
<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}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue