Initial item filters + restyling
parent
dc08ae7ea5
commit
ea35cbcadf
|
@ -1,4 +1,5 @@
|
|||
@color_1: #bbb;
|
||||
@line_size: 48px;
|
||||
|
||||
#compendium {
|
||||
.directory-footer {
|
||||
|
@ -117,6 +118,10 @@
|
|||
.small-select {
|
||||
width: 40px;
|
||||
}
|
||||
.direction {
|
||||
flex: 0;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
footer {
|
||||
|
@ -182,6 +187,107 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.item-browser {
|
||||
li {
|
||||
.item-image {
|
||||
max-width: @line_size;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
flex: 2;
|
||||
}
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: @line_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
.feat-browser {
|
||||
li {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
margin: 2px 0;
|
||||
height: @line_size;
|
||||
.item-image {
|
||||
max-width: @line_size;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
flex: 2;
|
||||
}
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: @line_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
.spell-browser {
|
||||
li {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
margin: 2px 0;
|
||||
height: @line_size;
|
||||
.item-image {
|
||||
max-width: @line_size;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
flex: 2;
|
||||
}
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: @line_size;
|
||||
}
|
||||
}
|
||||
.spell {
|
||||
.spell-level {
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
max-width: 18px;
|
||||
height: @line_size;
|
||||
}
|
||||
.spell-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
font-weight: 900;
|
||||
max-width: 100px;
|
||||
height: @line_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
.browser {
|
||||
height: 100%;
|
||||
overflow-y: hidden !important;
|
||||
|
@ -196,184 +302,73 @@
|
|||
display: inline-block;
|
||||
min-width: 15px;
|
||||
}
|
||||
}
|
||||
.item-browser {
|
||||
li {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
line-height: 16px;
|
||||
margin: 2px 0;
|
||||
height: 32px;
|
||||
.item-image {
|
||||
max-width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
flex: 2;
|
||||
}
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.feat-browser {
|
||||
li {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
line-height: 16px;
|
||||
margin: 2px 0;
|
||||
height: 32px;
|
||||
.item-image {
|
||||
max-width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
flex: 2;
|
||||
}
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.spell-browser {
|
||||
li {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
line-height: 16px;
|
||||
margin: 2px 0;
|
||||
height: 32px;
|
||||
.item-image {
|
||||
max-width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
flex: 2;
|
||||
}
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
.spell {
|
||||
.spell-level {
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
max-width: 18px;
|
||||
height: 32px;
|
||||
}
|
||||
.spell-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
font-weight: 900;
|
||||
max-width: 100px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.browser {
|
||||
li:hover {
|
||||
.line {
|
||||
h4 {
|
||||
text-shadow: 0 0 8px var(--color-shadow-primary);
|
||||
|
||||
.list-area {
|
||||
li {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
// margin: 4px 0;
|
||||
height: @line_size;
|
||||
border-bottom: 1px solid var(--color-border-dark-3);
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.line {
|
||||
h4 {
|
||||
text-shadow: 0 0 8px var(--color-shadow-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
flex: 0 0 @line_size;
|
||||
width: @line_size;
|
||||
height: @line_size;
|
||||
object-fit: cover;
|
||||
object-position: 50% 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: auto 0 auto 8px;
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tags {
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.details {
|
||||
flex: 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
max-width: 32px;
|
||||
height: 32px;
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
line-height: 25px;
|
||||
margin: auto 0 auto 8px;
|
||||
|
||||
.tags {
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.details {
|
||||
flex: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.rarity {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.item {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
line-height: 16px;
|
||||
margin: 2px 0;
|
||||
height: 32px;
|
||||
.feat-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
height: @line_size;
|
||||
}
|
||||
.item-tags {
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
text-transform: capitalize;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.actor {
|
||||
cursor: default;
|
||||
vertical-align: middle;
|
||||
line-height: 64px;
|
||||
margin: 4px 0;
|
||||
.image {
|
||||
max-width: 64px;
|
||||
height: 64px;
|
||||
img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
height: @line_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="npc-browser browser flexrow">
|
||||
<div class="actor-browser browser flexrow">
|
||||
<section class="control-area flexcol">
|
||||
<div class="controls">
|
||||
<div class="filtercontainer">
|
||||
|
@ -12,9 +12,13 @@
|
|||
<div class="form-group">
|
||||
<label>{{ game.i18n.localize('Sort by:') }}</label>
|
||||
<div class="form-fields">
|
||||
<select class="sort" name="sortorder" v-model="sortBy">
|
||||
<select class="sort" v-model="sortBy">
|
||||
<option v-for="(option, index) in sortOptions" :key="index" :value="option.value">{{ option.label }}</option>
|
||||
</select>
|
||||
<a class="direction" data-direction="asc" @click="changeDirection()">
|
||||
<i class="fa-solid fa-sort-numeric-up" v-if="direction === 'asc'"></i>
|
||||
<i class="fa-solid fa-sort-numeric-down-alt" v-if="direction !== 'asc'"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -149,16 +153,13 @@
|
|||
<li v-for="(entry, entryKey) in entries" :key="entryKey" :class="`flexrow draggable compendium-browser-row${entryKey >= pager.lastIndex - 1 && entryKey < pager.totalRows - 1 ? ' compendium-browser-row-observe': ''} document actor`" :data-document-id="entry._id" @click="openDocument(entry.uuid)" @dragstart="startDrag($event, entry, 'Actor')" draggable="true">
|
||||
<!-- Both the image and title have drag events. These are primarily separated so that -->
|
||||
<!-- if a user drags the token, it will only show the token as their drag preview. -->
|
||||
<div class="image">
|
||||
<img :src="entry.img ?? 'icons/svg/mystery-man.svg'"/>
|
||||
</div>
|
||||
<img :src="entry.img ?? 'icons/svg/mystery-man.svg'"/>
|
||||
<div class="line">
|
||||
<!-- First row is the title. -->
|
||||
<h4 class="name">[{{ game.dnd5e.utils.formatCR(entry.system.details.cr) }}] {{ entry.name }}</h4>
|
||||
<!-- Second row is supplemental info. -->
|
||||
<div class="tags flexrow">
|
||||
<div class="numbers flexrow">
|
||||
<!-- <span class="cr" :data-tooltip="game.i18n.localize('Challenge rating')">CR {{ game.dnd5e.utils.formatCR(entry.system.details.cr) }}</span> -->
|
||||
<div class="flexrow">
|
||||
<span class="hp"><span class="bold">HP:</span> {{ entry.system.attributes.hp.max }}</span>
|
||||
<span class="ac"><span class="bold">AC:</span> {{ entry.system.attributes.ac.flat }}</span>
|
||||
</div>
|
||||
|
@ -226,6 +227,7 @@ export default {
|
|||
},
|
||||
// Sorting.
|
||||
sortBy: 'name',
|
||||
direction: 'asc',
|
||||
sortOptions: [
|
||||
{ value: 'name', label: game.i18n.localize('Name') },
|
||||
{ value: 'cr', label: game.i18n.localize('Challenge Rating') },
|
||||
|
@ -278,6 +280,7 @@ export default {
|
|||
*/
|
||||
resetFilters() {
|
||||
this.sortBy = 'name';
|
||||
this.direction = 'asc';
|
||||
this.name = '';
|
||||
this.crRange = [0, 30];
|
||||
this.legact = '';
|
||||
|
@ -289,13 +292,17 @@ export default {
|
|||
this.size = [];
|
||||
this.creatureType = [];
|
||||
},
|
||||
changeDirection() {
|
||||
if (this.direction === "asc") this.direction = "desc";
|
||||
else this.direction = "asc";
|
||||
},
|
||||
/**
|
||||
* Get multiselect options.
|
||||
*/
|
||||
getOptions(config) {
|
||||
const options = {};
|
||||
for (let [key, value] of Object.entries(config)) {
|
||||
options[key] = value.label;
|
||||
options[key] = value.label ?? value;
|
||||
}
|
||||
return options;
|
||||
},
|
||||
|
@ -372,8 +379,7 @@ export default {
|
|||
if (this.pager.lastIndex == 0) {
|
||||
this.pager.lastIndex = this.pager.perPage - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.pager.totalRows = 0;
|
||||
}
|
||||
|
||||
|
@ -381,14 +387,16 @@ export default {
|
|||
result = result.sort((a, b) => {
|
||||
// Add sorts here.
|
||||
switch (this.sortBy) {
|
||||
case 'name':
|
||||
return a.name.localeCompare(b.name);
|
||||
case 'cr':
|
||||
return a.system.details.cr - b.system.details.cr;
|
||||
case 'size':
|
||||
return a.system.traits.size.localeCompare(b.system.traits.size);
|
||||
}
|
||||
// If no sorts match, sort by CR.
|
||||
return a.system.details.cr - b.system.details.cr;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
if (this.direction === "desc") {
|
||||
result = result.reverse();
|
||||
}
|
||||
|
||||
// Return results.
|
||||
return this.pager.totalRows > 0
|
||||
|
|
|
@ -19,6 +19,33 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filtercontainer">
|
||||
<h3>{{ game.i18n.localize('General') }}</h3>
|
||||
<div class="filters">
|
||||
<div class="filter">
|
||||
<label class="unit-title" for="compendiumBrowser.type">{{ game.i18n.localize('Type') }}</label>
|
||||
<Multiselect
|
||||
v-model="type"
|
||||
mode="tags"
|
||||
:searchable="false"
|
||||
:create-option="false"
|
||||
:options="getOptions(itemTypes)"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<label class="unit-title" for="compendiumBrowser.rarity">{{ game.i18n.localize('Rarity') }}</label>
|
||||
<Multiselect
|
||||
v-model="rarity"
|
||||
mode="tags"
|
||||
:class="{ 'rarity': true }"
|
||||
:searchable="false"
|
||||
:create-option="false"
|
||||
:options="getOptions(CONFIG.DND5E.itemRarity)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<!-- Reset. -->
|
||||
|
@ -33,28 +60,19 @@
|
|||
<li v-for="(equipment, equipmentKey) in entries" :key="equipmentKey" :class="`flexrow draggable compendium-browser-row${equipmentKey >= pager.lastIndex - 1 && equipmentKey < pager.totalRows - 1 ? ' compendium-browser-row-observe': ''} document item`" :data-document-id="equipment._id" @click="openDocument(equipment.uuid, 'Item')" @dragstart="startDrag($event, entry, 'Actor')" draggable="true">
|
||||
<!-- Both the image and title have drag events. These are primarily separated so that -->
|
||||
<!-- if a user drags the token, it will only show the token as their drag preview. -->
|
||||
<div class="image">
|
||||
<img :src="equipment.img" />
|
||||
</div>
|
||||
<img :src="equipment.img" />
|
||||
<div class="line">
|
||||
<!-- First row is the title. -->
|
||||
<h4 class="name">{{ equipment.name }}</h4>
|
||||
<!-- Second row is supplemental info. -->
|
||||
<div class="grid equipment-grid">
|
||||
<div class="equipment-bonus flexrow" :data-tooltip="game.i18n.localize('ARCHMAGE.bonuses')" data-tooltip-direction="RIGHT" v-if="equipment.system.attributes">
|
||||
<span class="bonus" v-for="(bonus, bonusProp) in getBonuses(equipment)" :key="bonusProp">
|
||||
<span class="bonus-label">{{localizeEquipmentBonus(bonusProp)}} </span>
|
||||
<span class="bonus-value">{{numberFormat(bonus, 0, true)}}</span>
|
||||
<div class="tags flexrow">
|
||||
<div v-if="equipment.system.properties.length">
|
||||
<span v-for="(prop, index) of equipment.system.properties" :key="prop">
|
||||
{{ prop }}<span v-if="index != Object.keys(equipment.system.properties).length - 1">, </span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="equipment-usage" v-if="equipment.system?.powerUsage?.value" :data-tooltip="game.i18n.localize('ARCHMAGE.GROUPS.powerUsage')">
|
||||
{{ '' }}
|
||||
</div>
|
||||
<div class="equipment-chakra" :data-tooltip="game.i18n.localize('ARCHMAGE.chakra')" v-if="equipment.system.chackra">
|
||||
{{localize(`ARCHMAGE.CHAKRA.${equipment.system.chackra}Label`)}}
|
||||
</div>
|
||||
<div class="equipment-recharge" :data-tooltip="game.i18n.localize('ARCHMAGE.recharge')">
|
||||
{{ `${equipment.system?.recharge?.value > 0 ? Number(equipment.system.recharge.value) + '+' : ''}`}}
|
||||
<div v-if="equipment.system.rarity && equipment.system.rarity !== 'common'">
|
||||
<span class="rarity">{{ CONFIG.DND5E.itemRarity[equipment.system.rarity] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -125,6 +143,8 @@ export default {
|
|||
packIndex: [],
|
||||
// Filters.
|
||||
name: '',
|
||||
type: [],
|
||||
rarity: [],
|
||||
chakra: [],
|
||||
recharge: [],
|
||||
bonuses: [],
|
||||
|
@ -161,6 +181,8 @@ export default {
|
|||
resetFilters() {
|
||||
this.sortBy = 'name';
|
||||
this.name = '';
|
||||
this.type = [];
|
||||
this.rarity = [];
|
||||
this.chakra = [];
|
||||
this.recharge = [];
|
||||
this.bonuses = [];
|
||||
|
@ -182,8 +204,25 @@ export default {
|
|||
}
|
||||
return bonuses;
|
||||
},
|
||||
getOptions(config) {
|
||||
const options = {};
|
||||
for (let [key, value] of Object.entries(config)) {
|
||||
options[key] = value.label ?? value;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
itemTypes() {
|
||||
return {
|
||||
consumable: game.i18n.localize("ITEM.TypeConsumable"),
|
||||
container: game.i18n.localize("ITEM.TypeContainer"),
|
||||
equipment: game.i18n.localize("ITEM.TypeEquipment"),
|
||||
loot: game.i18n.localize("ITEM.TypeLoot"),
|
||||
tool: game.i18n.localize("ITEM.TypeTool"),
|
||||
weapon: game.i18n.localize("ITEM.TypeWeapon"),
|
||||
};
|
||||
},
|
||||
bonusOptions() {
|
||||
return [
|
||||
{
|
||||
|
@ -283,6 +322,13 @@ export default {
|
|||
result = result.filter(entry => entry.name.toLocaleLowerCase().includes(name));
|
||||
}
|
||||
|
||||
if (Array.isArray(this.type) && this.type.length > 0) {
|
||||
result = result.filter(entry => this.type.includes(entry.type));
|
||||
}
|
||||
if (Array.isArray(this.rarity) && this.rarity.length > 0) {
|
||||
result = result.filter(entry => this.rarity.includes(entry.system.rarity));
|
||||
}
|
||||
|
||||
// Handle multiselect filters, which use arrays as their values.
|
||||
if (Array.isArray(this.chakra) && this.chakra.length > 0) {
|
||||
// @todo chakra is misspelled in our data model. We need to fix that :(
|
||||
|
@ -339,12 +385,10 @@ export default {
|
|||
|
||||
// Sort.
|
||||
result = result.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
// Sort.
|
||||
result = result.sort((a, b) => {
|
||||
// Add sorts here.
|
||||
switch (this.sortBy) {
|
||||
case 'type':
|
||||
return a.type.localeCompare(b.type);
|
||||
case 'chakra':
|
||||
return (a.system?.chackra ?? '').localeCompare((b.system?.chackra ?? ''));
|
||||
case 'usage':
|
||||
|
@ -372,12 +416,14 @@ export default {
|
|||
], [
|
||||
'system.activation.type',
|
||||
'system.armor.type',
|
||||
'system.container',
|
||||
'system.damage',
|
||||
'system.properties',
|
||||
'system.rarity',
|
||||
'system.source.book',
|
||||
'system.type'
|
||||
]).then(packIndex => {
|
||||
this.packIndex = packIndex;
|
||||
this.packIndex = packIndex.filter((e) => !e.system.container);
|
||||
this.loaded = true;
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue