Item filters + restyle
parent
c218a2001e
commit
82277afd66
|
@ -307,12 +307,13 @@
|
||||||
li {
|
li {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
// margin: 4px 0;
|
|
||||||
height: @line_size;
|
height: @line_size;
|
||||||
border-bottom: 1px solid var(--color-border-dark-3);
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
&:nth-child(odd) {
|
||||||
|
background-color: rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.line {
|
.line {
|
||||||
|
|
|
@ -3,22 +3,7 @@
|
||||||
<section class="control-area flexcol">
|
<section class="control-area flexcol">
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="filtercontainer">
|
<FilterNameSort v-model="name" :filters="sorts"/>
|
||||||
<!-- Name filter. -->
|
|
||||||
<div class="filter">
|
|
||||||
<input type="text" name="compendiumBrowser.name" v-model="name" :placeholder="game.i18n.localize('Name')" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Sort -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label>{{ game.i18n.localize('Sort by:') }}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select class="sort" name="sortorder" v-model="sortBy">
|
|
||||||
<option v-for="(option, index) in sortOptions" :key="index" :value="option.value">{{ option.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="filtercontainer">
|
<div class="filtercontainer">
|
||||||
<h3>{{ game.i18n.localize('General') }}</h3>
|
<h3>{{ game.i18n.localize('General') }}</h3>
|
||||||
|
@ -44,6 +29,66 @@
|
||||||
:options="getOptions(CONFIG.DND5E.itemRarity)"
|
:options="getOptions(CONFIG.DND5E.itemRarity)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.activation">{{ game.i18n.localize('Activation') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="activation"
|
||||||
|
mode="tags"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(CONFIG.DND5E.abilityActivationTypes)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.damageType">{{ game.i18n.localize('Damage Type') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="damageType"
|
||||||
|
mode="tags"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(CONFIG.DND5E.damageTypes)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.uses">{{ game.i18n.localize('Has Limited Uses') }}</label>
|
||||||
|
<input type="checkbox" v-model="uses">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filtercontainer">
|
||||||
|
<h3>{{ game.i18n.localize('Subtypes') }}</h3>
|
||||||
|
<div class="filters" style="display: none;">
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.weaponTypes">{{ game.i18n.localize('Weapon Types') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="weaponTypes"
|
||||||
|
mode="tags"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(CONFIG.DND5E.weaponTypes)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.equipmentTypes">{{ game.i18n.localize('Equipment Types') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="equipmentTypes"
|
||||||
|
mode="tags"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(CONFIG.DND5E.equipmentTypes)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.consumableTypes">{{ game.i18n.localize('Consumable Types') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="consumableTypes"
|
||||||
|
mode="tags"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(CONFIG.DND5E.consumableTypes)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,7 +102,7 @@
|
||||||
<!-- Items results. -->
|
<!-- Items results. -->
|
||||||
<ul v-if="loaded" class="compendium-browser-results compendium-browser-items">
|
<ul v-if="loaded" class="compendium-browser-results compendium-browser-items">
|
||||||
<!-- Individual items entries. -->
|
<!-- Individual items entries. -->
|
||||||
<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">
|
<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, equipment, 'Item')" draggable="true">
|
||||||
<!-- Both the image and title have drag events. These are primarily separated so that -->
|
<!-- 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. -->
|
<!-- if a user drags the token, it will only show the token as their drag preview. -->
|
||||||
<img :src="equipment.img" />
|
<img :src="equipment.img" />
|
||||||
|
@ -65,15 +110,13 @@
|
||||||
<!-- First row is the title. -->
|
<!-- First row is the title. -->
|
||||||
<h4 class="name">{{ equipment.name }}</h4>
|
<h4 class="name">{{ equipment.name }}</h4>
|
||||||
<!-- Second row is supplemental info. -->
|
<!-- Second row is supplemental info. -->
|
||||||
<div class="tags flexrow">
|
<div class="tags">
|
||||||
<div v-if="equipment.system.properties.length">
|
<span v-if="equipment.system.rarity" class="rarity">
|
||||||
<span v-for="(prop, index) of equipment.system.properties" :key="prop">
|
{{ CONFIG.DND5E.itemRarity[equipment.system.rarity] }}<span v-if="equipment.system.properties.length">, </span>
|
||||||
{{ prop }}<span v-if="index != Object.keys(equipment.system.properties).length - 1">, </span>
|
</span>
|
||||||
</span>
|
<span v-if="equipment.system.properties.length" v-for="(prop, index) of equipment.system.properties" :key="prop">
|
||||||
</div>
|
{{ CONFIG.DND5E.itemProperties[prop].label }}<span v-if="index != Object.keys(equipment.system.properties).length - 1">, </span>
|
||||||
<div v-if="equipment.system.rarity && equipment.system.rarity !== 'common'">
|
</span>
|
||||||
<span class="rarity">{{ CONFIG.DND5E.itemRarity[equipment.system.rarity] }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -89,6 +132,7 @@ import { onUpdated } from 'vue';
|
||||||
// External components.
|
// External components.
|
||||||
import Slider from '@vueform/slider';
|
import Slider from '@vueform/slider';
|
||||||
import Multiselect from '@vueform/multiselect';
|
import Multiselect from '@vueform/multiselect';
|
||||||
|
import FilterNameSort from '@/components/dialogs/compendium-browser/filters/FilterNameSort.vue';
|
||||||
// Helper methods.
|
// Helper methods.
|
||||||
import {
|
import {
|
||||||
getPackIndex,
|
getPackIndex,
|
||||||
|
@ -105,7 +149,8 @@ export default {
|
||||||
// Imported components that need to be available in the <template>
|
// Imported components that need to be available in the <template>
|
||||||
components: {
|
components: {
|
||||||
Slider,
|
Slider,
|
||||||
Multiselect
|
Multiselect,
|
||||||
|
FilterNameSort
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
|
@ -133,22 +178,27 @@ export default {
|
||||||
lastIndex: 50,
|
lastIndex: 50,
|
||||||
totalRows: 0,
|
totalRows: 0,
|
||||||
},
|
},
|
||||||
|
sorts: {
|
||||||
|
sortBy: 'name',
|
||||||
|
direction: 'asc',
|
||||||
|
sortOptions: [
|
||||||
|
{ value: 'name', label: game.i18n.localize('Name') },
|
||||||
|
// { value: 'type', label: game.i18n.localize('Type') },
|
||||||
|
],
|
||||||
|
},
|
||||||
// Sorting.
|
// Sorting.
|
||||||
sortBy: 'name',
|
|
||||||
sortOptions: [
|
|
||||||
{ value: 'name', label: game.i18n.localize('Name') },
|
|
||||||
{ value: 'type', label: game.i18n.localize('Type') },
|
|
||||||
],
|
|
||||||
// Our list of pseudo documents returned from the compendium.
|
// Our list of pseudo documents returned from the compendium.
|
||||||
packIndex: [],
|
packIndex: [],
|
||||||
// Filters.
|
// Filters.
|
||||||
name: '',
|
name: '',
|
||||||
type: [],
|
type: [],
|
||||||
rarity: [],
|
rarity: [],
|
||||||
chakra: [],
|
activation: [],
|
||||||
recharge: [],
|
damageType: [],
|
||||||
bonuses: [],
|
uses: false,
|
||||||
powerUsage: [],
|
weaponTypes: [],
|
||||||
|
consumableTypes: [],
|
||||||
|
equipmentTypes: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -179,30 +229,17 @@ export default {
|
||||||
* Click event to reset our filters.
|
* Click event to reset our filters.
|
||||||
*/
|
*/
|
||||||
resetFilters() {
|
resetFilters() {
|
||||||
this.sortBy = 'name';
|
this.sorts.sortBy = 'name';
|
||||||
|
this.sorts.direction = 'asc';
|
||||||
this.name = '';
|
this.name = '';
|
||||||
this.type = [];
|
this.type = [];
|
||||||
this.rarity = [];
|
this.rarity = [];
|
||||||
this.chakra = [];
|
this.activation = [];
|
||||||
this.recharge = [];
|
this.damageType = [];
|
||||||
this.bonuses = [];
|
this.uses = false;
|
||||||
this.powerUsage = [];
|
this.weaponTypes = [];
|
||||||
},
|
this.consumableTypes = [];
|
||||||
getBonuses(equipment) {
|
this.equipmentTypes = [];
|
||||||
let bonuses = {};
|
|
||||||
for (let [prop, value] of Object.entries(equipment.system.attributes)) {
|
|
||||||
if (value.bonus) {
|
|
||||||
bonuses[prop] = value.bonus
|
|
||||||
}
|
|
||||||
else if (prop == 'attack') {
|
|
||||||
for (let [atkProp, atkValue] of Object.entries(value)) {
|
|
||||||
if (atkValue.bonus) {
|
|
||||||
bonuses[atkProp] = atkValue.bonus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bonuses;
|
|
||||||
},
|
},
|
||||||
getOptions(config) {
|
getOptions(config) {
|
||||||
const options = {};
|
const options = {};
|
||||||
|
@ -328,48 +365,27 @@ export default {
|
||||||
if (Array.isArray(this.rarity) && this.rarity.length > 0) {
|
if (Array.isArray(this.rarity) && this.rarity.length > 0) {
|
||||||
result = result.filter(entry => this.rarity.includes(entry.system.rarity));
|
result = result.filter(entry => this.rarity.includes(entry.system.rarity));
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(this.activation) && this.activation.length > 0) {
|
||||||
// Handle multiselect filters, which use arrays as their values.
|
result = result.filter(entry => entry.system.activation && this.activation.includes(entry.system.activation.type))
|
||||||
if (Array.isArray(this.chakra) && this.chakra.length > 0) {
|
|
||||||
// @todo chakra is misspelled in our data model. We need to fix that :(
|
|
||||||
result = result.filter(entry => this.chakra.includes(entry.system?.chackra));
|
|
||||||
}
|
}
|
||||||
if (Array.isArray(this.powerUsage) && this.powerUsage.length > 0) {
|
if (Array.isArray(this.damageType) && this.damageType.length > 0) {
|
||||||
result = result.filter(entry => this.powerUsage.includes(entry.system?.powerUsage?.value ?? 'other'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recharge.
|
|
||||||
if (Array.isArray(this.recharge) && this.recharge.length > 0) {
|
|
||||||
result = result.filter(entry => {
|
result = result.filter(entry => {
|
||||||
let allowEntry = false;
|
if (!entry.system.damage) return false;
|
||||||
for (let rechargeOption of this.rechargeOptions) {
|
const damageTypes = entry.system.damage.parts.map((d) => d[1]);
|
||||||
if (this.recharge.includes(rechargeOption.value)) {
|
return this.damageType.some((d) => damageTypes.includes(d));
|
||||||
const rechargeEntry = parseInt(entry.system?.recharge?.value ?? 0);
|
})
|
||||||
if (rechargeEntry >= rechargeOption.value && rechargeEntry <= rechargeOption.next) {
|
|
||||||
allowEntry = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allowEntry;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if (this.uses) {
|
||||||
// Bonus options.
|
result = result.filter(entry => entry.system.uses && entry.system.uses.max);
|
||||||
if (Array.isArray(this.bonuses) && this.bonuses.length > 0) {
|
}
|
||||||
result = result.filter(entry => {
|
if (Array.isArray(this.weaponTypes) && this.weaponTypes.length > 0) {
|
||||||
let allowEntry = false;
|
result = result.filter(entry => entry.system.type && this.weaponTypes.includes(entry.system.type.value));
|
||||||
for (let bonusOption of this.bonusOptions) {
|
}
|
||||||
if (this.bonuses.includes(bonusOption.value)) {
|
if (Array.isArray(this.consumableTypes) && this.consumableTypes.length > 0) {
|
||||||
const prop = this.foundry.utils.getProperty(entry, bonusOption.dataProp);
|
result = result.filter(entry => entry.system.type && this.consumableTypes.includes(entry.system.type.value));
|
||||||
if (Number.isNumeric(prop) && prop !== 0) {
|
}
|
||||||
allowEntry = true;
|
if (Array.isArray(this.equipmentTypes) && this.equipmentTypes.length > 0) {
|
||||||
break;
|
result = result.filter(entry => entry.system.type && this.equipmentTypes.includes(entry.system.type.value));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allowEntry;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reflow pager.
|
// Reflow pager.
|
||||||
|
@ -386,18 +402,13 @@ export default {
|
||||||
// Sort.
|
// Sort.
|
||||||
result = result.sort((a, b) => {
|
result = result.sort((a, b) => {
|
||||||
// Add sorts here.
|
// Add sorts here.
|
||||||
switch (this.sortBy) {
|
switch (this.sorts.sortBy) {
|
||||||
case 'type':
|
|
||||||
return a.type.localeCompare(b.type);
|
|
||||||
case 'chakra':
|
|
||||||
return (a.system?.chackra ?? '').localeCompare((b.system?.chackra ?? ''));
|
|
||||||
case 'usage':
|
|
||||||
return (a.system?.powerUsage?.value ?? '').localeCompare((b.system?.powerUsage?.value ?? ''));
|
|
||||||
case 'recharge':
|
|
||||||
return (a.system?.recharge?.value ?? 0) - (b.system?.recharge?.value ?? 0);
|
|
||||||
}
|
}
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
});
|
});
|
||||||
|
if (this.sorts.direction === "desc") {
|
||||||
|
result = result.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
// Return results.
|
// Return results.
|
||||||
return this.pager.totalRows > 0
|
return this.pager.totalRows > 0
|
||||||
|
@ -415,13 +426,13 @@ export default {
|
||||||
'dnd5e.items',
|
'dnd5e.items',
|
||||||
], [
|
], [
|
||||||
'system.activation.type',
|
'system.activation.type',
|
||||||
'system.armor.type',
|
|
||||||
'system.container',
|
'system.container',
|
||||||
'system.damage',
|
'system.damage',
|
||||||
'system.properties',
|
'system.properties',
|
||||||
'system.rarity',
|
'system.rarity',
|
||||||
'system.source.book',
|
'system.source.book',
|
||||||
'system.type'
|
'system.type',
|
||||||
|
'system.uses',
|
||||||
]).then(packIndex => {
|
]).then(packIndex => {
|
||||||
this.packIndex = packIndex.filter((e) => !e.system.container);
|
this.packIndex = packIndex.filter((e) => !e.system.container);
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
|
|
|
@ -38,7 +38,9 @@ export default {
|
||||||
game
|
game
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {},
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// updateValue(event) {
|
// updateValue(event) {
|
||||||
// this.valueMutable = event.target.value;
|
// this.valueMutable = event.target.value;
|
||||||
|
|
Loading…
Reference in New Issue