ENH: Inventory

master
U~man 2020-07-02 12:10:14 +02:00
parent c07bbb864f
commit dad790090e
14 changed files with 529 additions and 281 deletions

View File

@ -75,6 +75,7 @@
"OSE.category.attributes": "Attributes",
"OSE.category.inventory": "Inventory",
"OSE.category.abilities": "Abilities",
"OSE.category.spells": "Spells",
"OSE.category.notes": "Notes",
@ -92,18 +93,25 @@
"OSE.Setting.VariableWeaponDamage": "Variable Weapon Damage",
"OSE.Setting.VariableWeaponDamageHint": "Weapons have different damage dice",
"OSE.ItemWeight": "Weight",
"OSE.ItemCost": "Cost",
"OSE.ItemQuantity": "Quantity",
"OSE.ItemRoll": "Roll",
"OSE.WeaponDamage": "Damage",
"OSE.WeaponMelee": "Melee",
"OSE.WeaponMissile": "Missile",
"OSE.WeaponSlow": "Slow",
"OSE.SpellRange": "Range",
"OSE.SpellClass": "Class",
"OSE.SpellDuration": "Duration",
"OSE.SpellLevel": "Level",
"OSE.ArmorAC": "AC",
"OSE.ArmorAAC": "AAC"
"OSE.items.Equip": "Equip",
"OSE.items.Unequip": "Unequip",
"OSE.items.Misc": "Misc",
"OSE.items.Weapons": "Weapons",
"OSE.items.Armors": "Armors",
"OSE.items.Weight": "Wght.",
"OSE.items.Qualities": "Qualities",
"OSE.items.Notes": "Notes",
"OSE.items.Cost": "Cost",
"OSE.items.Quantity": "Qt.",
"OSE.items.Roll": "Roll",
"OSE.items.Damage": "Damage",
"OSE.items.Melee": "Melee",
"OSE.items.Missile": "Missile",
"OSE.items.Slow": "Slow",
"OSE.spells.Range": "Range",
"OSE.spells.Class": "Class",
"OSE.spells.Duration": "Duration",
"OSE.spells.Level": "Level",
"OSE.items.ArmorAC": "AC",
"OSE.items.ArmorAAC": "AAC"
}

View File

@ -2,87 +2,116 @@ import { OseActor } from "./entity.js";
import { OseEntityTweaks } from "../dialog/entity-tweaks.js";
export class OseActorSheet extends ActorSheet {
constructor(...args) {
super(...args);
constructor(...args) {
super(...args);
}
/* -------------------------------------------- */
getData() {
const data = super.getData();
data.config = CONFIG.OSE;
// Settings
data.config.ascendingAC = game.settings.get("ose", "ascendingAC");
// Prepare owned items
this._prepareItems(data);
return data;
}
/**
* Organize and classify Owned Items for Character sheets
* @private
*/
_prepareItems(data) {
// Partition items by category
let [inventory, weapons, armors, abilities, spells] = data.items.reduce(
(arr, item) => {
// Classify items into types
if (item.type === "item") arr[0].push(item);
else if (item.type === "weapon") arr[1].push(item);
else if (item.type === "armor") arr[2].push(item);
else if (item.type === "ability") arr[3].push(item);
else if (item.type === "spell") arr[4].push(item);
return arr;
},
[[], [], [], [], []]
);
// Assign and return
data.inventory = inventory;
data.weapons = weapons;
data.armors = armors;
data.spells = spells;
data.abilities = abilities;
}
activateListeners(html) {
html.find(".saving-throw .attribute-name a").click((ev) => {
let actorObject = this.actor;
let element = event.currentTarget;
let save = element.parentElement.parentElement.dataset.save;
actorObject.rollSave(save, { event: event });
});
super.activateListeners(html);
}
// Override to set resizable initial size
async _renderInner(...args) {
const html = await super._renderInner(...args);
this.form = html[0];
// Resize resizable classes
let resizable = html.find(".resizable");
if (resizable.length == 0) {
return;
}
/* -------------------------------------------- */
resizable.each((_, el) => {
let heightDelta = this.position.height - this.options.height;
el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`;
});
return html;
}
getData() {
const data = super.getData();
async _onResize(event) {
super._onResize(event);
let html = $(event.path);
let resizable = html.find(".resizable");
resizable.each((_, el) => {
let heightDelta = this.position.height - this.options.height;
el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`;
});
}
data.config = CONFIG.OSE;
// Settings
data.config.ascendingAC = game.settings.get('ose', 'ascendingAC');
_onConfigureActor(event) {
event.preventDefault();
new OseEntityTweaks(this.actor, {
top: this.position.top + 40,
left: this.position.left + (this.position.width - 400) / 2,
}).render(true);
}
return data;
}
activateListeners(html) {
html.find('.saving-throw .attribute-name a').click(ev => {
let actorObject = this.actor;
let element = event.currentTarget;
let save = element.parentElement.parentElement.dataset.save;
actorObject.rollSave(save, { event: event });
})
super.activateListeners(html);
}
// Override to set resizable initial size
async _renderInner(...args) {
const html = await super._renderInner(...args);
this.form = html[0];
// Resize resizable classes
let resizable = html.find('.resizable');
if (resizable.length == 0) {
return;
}
resizable.each((_, el) => {
let heightDelta = this.position.height - (this.options.height);
el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`;
});
return html;
}
async _onResize(event) {
super._onResize(event);
let html = $(event.path);
let resizable = html.find('.resizable');
resizable.each((_, el) => {
let heightDelta = this.position.height - (this.options.height);
el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`;
});
}
_onConfigureActor(event) {
event.preventDefault();
new OseEntityTweaks(this.actor, {
top: this.position.top + 40,
left: this.position.left + (this.position.width - 400) / 2,
}).render(true);
}
/**
* Extend and override the sheet header buttons
* @override
*/
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
// Token Configuration
const canConfigure = game.user.isGM || this.actor.owner;
if (this.options.editable && canConfigure) {
buttons = [
{
label: 'Tweaks',
class: 'configure-actor',
icon: 'fas fa-dice',
onclick: (ev) => this._onConfigureActor(ev),
},
].concat(buttons);
}
return buttons;
/**
* Extend and override the sheet header buttons
* @override
*/
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
// Token Configuration
const canConfigure = game.user.isGM || this.actor.owner;
if (this.options.editable && canConfigure) {
buttons = [
{
label: "Tweaks",
class: "configure-actor",
icon: "fas fa-dice",
onclick: (ev) => this._onConfigureActor(ev),
},
].concat(buttons);
}
return buttons;
}
}

View File

@ -42,57 +42,44 @@ export class OseActorSheetCharacter extends OseActorSheet {
for (let [a, score] of Object.entries(data.data.scores)) {
data.data.scores[a].label = game.i18n.localize(`OSE.scores.${a}`);
}
// Prepare owned items
this._prepareItems(data);
// Settings
data.config.individualInit = game.settings.get('ose', 'individualInit');
data.config.variableWeaponDamage = game.settings.get(
"ose",
"variableWeaponDamage"
);
data.config.ascendingAC = game.settings.get("ose", "ascendingAC");
data.config.individualInit = game.settings.get("ose", "individualInit");
return data;
}
/**
* Organize and classify Owned Items for Character sheets
* @private
*/
_prepareItems(data) {
// Partition items by category
let [inventory, abilities, spells] = data.items.reduce(
(arr, item) => {
// Classify items into types
if (item.type === "item") arr[0].push(item);
if (item.type === "ability") arr[1].push(item);
else if (item.type === "spell") arr[2].push(item);
return arr;
},
[[], [], [], []]
);
// Assign and return
data.inventory = inventory;
data.spells = spells;
data.abilities = abilities;
}
/* -------------------------------------------- */
_onItemSummary(event) {
event.preventDefault();
let li = $(event.currentTarget).parents(".item"),
item = this.actor.getOwnedItem(li.data("item-id")),
description = TextEditor.enrichHTML(item.data.data.description);
item = this.actor.getOwnedItem(li.data("item-id")),
description = TextEditor.enrichHTML(item.data.data.description);
// Toggle summary
if ( li.hasClass("expanded") ) {
let summary = li.parents('.item-entry').children(".item-summary");
if (li.hasClass("expanded")) {
let summary = li.parents(".item-entry").children(".item-summary");
summary.slideUp(200, () => summary.remove());
} else {
let div = $(`<div class="item-summary">${description}</div>`);
li.parents('.item-entry').append(div.hide());
li.parents(".item-entry").append(div.hide());
div.slideDown(200);
}
li.toggleClass("expanded");
}
async _onQtChange(event) {
event.preventDefault();
const itemId = event.currentTarget.closest(".item").dataset.itemId;
const item = this.actor.getOwnedItem(itemId);
return item.update({ "data.quantity.value": parseInt(event.target.value) });
}
/**
* Activate event listeners using the prepared sheet HTML
* @param html {HTML} The prepared HTML object ready to be rendered into the DOM
@ -128,16 +115,34 @@ export class OseActorSheetCharacter extends OseActorSheet {
return this.actor.createOwnedItem(itemData);
});
//Toggle Equipment
html.find(".item-toggle").click(async (ev) => {
const li = $(ev.currentTarget).parents(".item");
const item = this.actor.getOwnedItem(li.data("itemId"));
await this.actor.updateOwnedItem({
_id: li.data("itemId"),
data: {
equipped: !item.data.data.equipped,
},
});
});
html
.find(".quantity input")
.click((ev) => ev.target.select())
.change(this._onQtChange.bind(this));
// Item summaries
html.find('.item .item-name h4').click(event => this._onItemSummary(event));
html
.find(".item .item-name h4")
.click((event) => this._onItemSummary(event));
html.find('.ability-score .attribute-name a').click(ev => {
html.find(".ability-score .attribute-name a").click((ev) => {
let actorObject = this.actor;
let element = event.currentTarget;
let score = element.parentElement.parentElement.dataset.score;
actorObject.rollCheck(score, { event: event });
})
});
// Handle default listeners last so system listeners are triggered first
super.activateListeners(html);

View File

@ -39,39 +39,12 @@ export class OseActorSheetMonster extends OseActorSheet {
getData() {
const data = super.getData();
// Prepare owned items
this._prepareItems(data);
// Settings
data.config.morale = game.settings.get('ose', 'morale');
return data;
}
/**
* Organize and classify Owned Items for Character sheets
* @private
*/
_prepareItems(data) {
// Partition items by category
let [inventory, abilities, spells] = data.items.reduce(
(arr, item) => {
// Classify items into types
if (item.type === "item") arr[0].push(item);
if (item.type === "ability") arr[1].push(item);
else if (item.type === "spell") arr[2].push(item);
return arr;
},
[[], [], [], []]
);
// Assign and return
data.inventory = inventory;
data.spells = spells;
data.abilities = abilities;
}
_onItemSummary(event) {
event.preventDefault();
let li = $(event.currentTarget).parents(".item"),

View File

@ -7,6 +7,7 @@ export const preloadHandlebarsTemplates = async function () {
//Sheet tabs
'systems/ose/templates/actors/partials/character-header.html',
'systems/ose/templates/actors/partials/character-attributes-tab.html',
'systems/ose/templates/actors/partials/character-abilities-tab.html',
'systems/ose/templates/actors/partials/character-spells-tab.html',
'systems/ose/templates/actors/partials/character-inventory-tab.html',

View File

@ -35,7 +35,7 @@ export const registerSettings = function () {
config: true
});
game.settings.register('ose', 'variableDamage', {
game.settings.register('ose', 'variableWeaponDamage', {
name: game.i18n.localize('OSE.Setting.VariableWeaponDamage'),
hint: game.i18n.localize('OSE.Setting.VariableWeaponDamageHint'),
default: false,

View File

@ -129,57 +129,7 @@
}
.editor {
height: 300px;
}
.inventory {
.item-entry {
padding: 0;
margin: 0;
list-style: none;
.item {
.item-image {
flex: 0 0 24px;
height: 24px;
background-size: cover;
}
.item-name {
line-height: 24px;
height: 24px;
overflow: hidden;
h4 {
text-indent: 4px;
margin: 0;
cursor: pointer;
&:hover {
color: whitesmoke;
background: linear-gradient(
45deg,
rgba(0, 0, 0, 0.5),
transparent
);
}
}
}
.item-controls {
line-height: 24px;
flex: 0 0 32px;
margin: 0 3px;
.fas {
color: $colorTan;
font-size: 12px;
&:hover {
color: $colorDark;
}
}
}
}
.item-summary {
font-size: 12px;
padding: 0 4px;
}
&:nth-child(odd) {
background: rgba(0, 0, 0, 0.1);
}
}
padding: 4px;
}
}
}

View File

@ -18,13 +18,111 @@
/* Sheet Body */
/* ----------------------------------------- */
.sheet-body {
}
.abilities {
.panel-content {
height: 250px;
.inventory {
overflow: auto;
height: 520px;
.items-section {
.header-field {
margin: 0;
}
}
.item-titles {
text-align: center;
margin-top: 1px;
padding-top: 2px;
.item-name {
text-align: left;
text-indent: 8px;
}
font-weight: 300;
font-size: 12px;
background: $colorDark;
color: white;
}
.item-list {
list-style: none;
margin: 0;
padding: 0;
li {
padding: 0 2px;
}
.item-header {
@extend %header-field !optional;
padding: 0px;
margin-bottom: 0px;
}
.item-entry {
&:nth-child(even) {
background: rgba(0, 0, 0, 0.1);
}
}
.item {
line-height: 30px;
height: 30px;
overflow: hidden;
}
.item-equipped {
grid-area: item-equipped;
justify-self: center;
}
.item-name {
text-indent: 8px;
text-align: left;
overflow: hidden;
height: 30px;
margin: 0;
line-height: 30px;
.item-image {
flex-basis: 30px;
flex-grow: 0;
background-size: contain;
background-repeat: no-repeat;
&:hover {
background-image: url("/icons/svg/d20-grey.svg") !important;
cursor: pointer;
}
}
h4 {
margin: 0;
}
}
}
.field-longer {
text-indent: 8px;
text-align: left;
flex-basis: 150px;
font-size: 12px;
flex-grow: 0;
}
.field-long {
flex-basis: 65px;
flex-grow: 0;
text-align: center;
font-size: 12px;
}
.field-short {
font-size: 12px;
flex-basis: 45px;
flex-grow: 0;
text-align: center;
&.quantity {
margin: 4px 0;
display: flex;
input {
border-bottom: none;
}
}
}
.item-controls {
font-size: 12px;
flex-basis: 60px;
flex-grow: 0;
text-align: right;
margin-right: 4px;
.item-unequipped {
color: rgba(0, 0, 0, 0.2);
}
}
}
}

View File

@ -1,6 +1,6 @@
.ose.actor.monster {
min-height: 565px;
min-width: 460px;
min-height: 565px;
min-width: 460px;
.sheet-body {
.editor {
height: 300px;
@ -13,9 +13,61 @@
}
}
.attribute-row {
padding: 2px;
.abilities {
margin: 2px;
padding: 2px;
.abilities {
margin: 2px;
}
}
.inventory {
.item-entry {
padding: 0;
margin: 0;
list-style: none;
.item {
.item-image {
flex: 0 0 30px;
height: 30px;
background-size: cover;
}
.item-name {
line-height: 30px;
height: 30px;
overflow: hidden;
h4 {
text-indent: 4px;
margin: 0;
cursor: pointer;
&:hover {
color: whitesmoke;
background: linear-gradient(
45deg,
rgba(0, 0, 0, 0.5),
transparent
);
}
}
}
.item-controls {
line-height: 30px;
flex: 0 0 32px;
margin: 0 3px;
.fas {
color: $colorTan;
font-size: 12px;
&:hover {
color: $colorDark;
}
}
}
}
.item-summary {
font-size: 13px;
padding: 0 4px;
}
&:nth-child(odd) {
background: rgba(0, 0, 0, 0.1);
}
}
}
}

View File

@ -13,10 +13,12 @@
"max": 20
},
"ac": {
"naked": 0,
"value": 0,
"mod": 0
},
"aac": {
"naked": 0,
"value": 0,
"mod": 0
},
@ -141,7 +143,7 @@
"max": 0
},
"cost": 0,
"weight": 0
"weight": 80
},
"weapon": {
"description": "",
@ -151,6 +153,7 @@
"missile": true,
"ranged": true,
"cost": 0,
"equipped": false,
"weight": 0
},
"armor": {
@ -158,6 +161,7 @@
"ac": 9,
"aac": 10,
"cost": 0,
"equipped": false,
"weight": 0
},
"spell": {

View File

@ -12,6 +12,9 @@
<a class="item" data-tab="inventory">
{{localize "OSE.category.inventory"}}
</a>
<a class="item" data-tab="abilities">
{{localize "OSE.category.abilities"}}
</a>
{{#if data.spells.enabled}}
<a class="item" data-tab="spells">
{{localize "OSE.category.spells"}}
@ -30,6 +33,9 @@
<div class="tab" data-group="primary" data-tab="inventory">
{{> "systems/ose/templates/actors/partials/character-inventory-tab.html"}}
</div>
<div class="tab" data-group="primary" data-tab="abilities">
{{> "systems/ose/templates/actors/partials/character-abilities-tab.html"}}
</div>
{{#if data.spells.enabled}}
<div class="tab" data-group="primary" data-tab="spells">
{{> "systems/ose/templates/actors/partials/character-spells-tab.html"}}

View File

@ -0,0 +1,46 @@
<div class="flex3 panel abilities resizable" data-base-size="250">
<div class="panel-title">
<h4>{{localize 'OSE.panel.abilities'}}</h4>
<div class="item-controls">
{{#if owner}}
<a
class="item-control item-create"
title='{{localize "OSE.Add"}}'
data-type="ability"
><i class="fas fa-plus"></i
></a>
{{/if}}
</div>
</div>
<ul class="panel-content inventory resizable" data-base-size="225">
<div class="">
{{#each abilities as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
<div
class="item-image"
style="background-image: url({{item.img}})"
></div>
<h4 title="{{item.name}}">
{{item.name~}}
</h4>
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-edit" title='{{localize "Ose.Edit"}}'
><i class="fas fa-edit"></i
></a>
<a
class="item-control item-delete"
title='{{localize "Ose.Delete"}}'
><i class="fas fa-trash"></i
></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</div>
</ul>
</div>

View File

@ -111,42 +111,6 @@
</li>
</ul>
</div>
{{!-- Skills and abilities --}}
<div class="flex3 panel abilities resizable" data-base-size="250">
<div class="panel-title">
<h4>{{localize 'OSE.panel.abilities'}}</h4>
<div class="item-controls">
{{#if owner}}
<a class="item-control item-create" title='{{localize "OSE.Add"}}' data-type="ability"><i
class="fas fa-plus"></i></a>
{{/if}}
</div>
</div>
<ul class="panel-content inventory resizable" data-base-size="225">
<div class="">
{{#each abilities as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
<div class="item-image" style="background-image: url({{item.img}})"></div>
<h4 title="{{item.name}}">
{{item.name~}}
</h4>
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-edit" title='{{localize "Ose.Edit"}}'><i
class="fas fa-edit"></i></a>
<a class="item-control item-delete" title='{{localize "Ose.Delete"}}'><i
class="fas fa-trash"></i></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</div>
</ul>
</div>
{{!-- Saving throws --}}
<div class="attribute-group">
<ul class="attributes">

View File

@ -1,29 +1,141 @@
<section class="inventory resizable" data-base-size="320">
<div class="">
{{#each inventory as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
<div
class="item-image"
style="background-image: url({{item.img}})"
></div>
<h4 title="{{item.name}}">
{{item.name~}}
</h4>
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-edit" title='{{localize "Ose.Edit"}}'
><i class="fas fa-edit"></i
></a>
<a class="item-control item-delete" title='{{localize "Ose.Delete"}}'
><i class="fas fa-trash"></i
></a>
{{/if}}
</div>
<div>
<li class="item-titles flexrow">
<div class="item-name">{{localize "OSE.items.Weapons"}}</div>
{{#if config.variableWeaponDamage}}
<div class="field-short">{{localize "OSE.items.Damage"}}</div>
{{/if}}
<div class="field-longer">{{localize "OSE.items.Qualities"}}</div>
<div class="field-short">{{localize "OSE.items.Weight"}}</div>
<div class="item-controls">
<a class="item-control item-create" data-type="weapon"><i class="fa fa-plus"></i> {{localize "OSE.Add"}}</a>
</div>
</li>
{{/each}}
<ol class="item-list">
{{#each weapons as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
<div class="item-image" style="background-image: url({{item.img}})"></div>
<a>
<h4 title="{{item.name}}">
{{item.name~}}
</h4>
</a>
</div>
{{#if config.variableWeaponDamage}}
<div class="field-short">
{{item.data.damage}}
</div>
{{/if}}
<div class="field-longer">
{{item.data.qualities}}
</div>
<div class="field-short">
{{item.data.weight}}
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-toggle {{#unless item.data.equipped}}item-unequipped{{/unless}}"
title='{{localize "OSE.items.Equip"}}'>
<i class="fas fa-tshirt"></i>
</a>
<a class="item-control item-edit" title='{{localize "OSE.Edit"}}'><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title='{{localize "OSE.Delete"}}'><i class="fas fa-trash"></i></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</ol>
</div>
<div>
<li class="item-titles flexrow">
<div class="item-name">{{localize "OSE.items.Armors"}}</div>
{{#if config.ascendingAC}}
<div class="field-short">{{localize "OSE.items.ArmorAAC"}}</div>
{{else}}
<div class="field-short">{{localize "OSE.items.ArmorAC"}}</div>
{{/if}}
<div class="field-short">{{localize "OSE.items.Weight"}}</div>
<div class="item-controls">
<a class="item-control item-create" data-type="armor"><i class="fa fa-plus"></i> {{localize "OSE.Add"}}</a>
</div>
</li>
<ol class="item-list">
{{#each armors as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
<div class="item-image" style="background-image: url({{item.img}})"></div>
<a>
<h4 title="{{item.name}}">
{{item.name~}}
</h4>
</a>
</div>
<div class="field-short">
{{#if config.ascendingAC}}
{{item.data.aac}}
{{else}}
{{item.data.ac}}
{{/if}}
</div>
<div class="field-short">
{{item.data.weight}}
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-toggle {{#unless item.data.equipped}}item-unequipped{{/unless}}"
title='{{localize "OSE.items.Equip"}}'>
<i class="fas fa-tshirt"></i>
</a>
<a class="item-control item-edit" title='{{localize "OSE.Edit"}}'><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title='{{localize "OSE.Delete"}}'><i class="fas fa-trash"></i></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</ol>
</div>
<div class="">
<li class="item-titles flexrow">
<div class="item-name">{{localize "OSE.items.Misc"}}</div>
<div class="field-short">{{localize "OSE.items.Quantity"}}</div>
<div class="field-short">{{localize "OSE.items.Weight"}}</div>
<div class="item-controls">
<a class="item-control item-create" data-type="item"><i class="fa fa-plus"></i> {{localize "OSE.Add"}}</a>
</div>
</li>
<ol class="item-list">
{{#each inventory as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
<div class="item-image" style="background-image: url({{item.img}})"></div>
<a>
<h4 title="{{item.name}}">
{{item.name~}}
</h4>
</a>
</div>
<div class="field-short quantity">
<input value="{{item.data.quantity.value}}" type="text"
placeholder="0" />{{#if item.data.quantity.max}}<span> / {{item.data.quantity.max}}</span>{{/if}}
</div>
<div class="field-short">
{{item.data.weight}}
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-edit" title='{{localize "OSE.Edit"}}'><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title='{{localize "OSE.Delete"}}'><i class="fas fa-trash"></i></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</ol>
</div>
</section>