Ability Score filters + restyling
parent
549fa19a4c
commit
855cdcdc1b
|
@ -92,7 +92,7 @@ export class CompendiumBrowserVueApplication extends Application {
|
||||||
// Reactivate the listeners if we need to.
|
// Reactivate the listeners if we need to.
|
||||||
if (!this.vueListenersActive) {
|
if (!this.vueListenersActive) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.activateVueListeners($(this.form), true);
|
this.activateVueListeners(true);
|
||||||
}, 150);
|
}, 150);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -112,7 +112,7 @@ export class CompendiumBrowserVueApplication extends Application {
|
||||||
this.vueRoot = this.vueApp.mount(`[data-appid="${this.appId}"] .compendium-browser-mount`);
|
this.vueRoot = this.vueApp.mount(`[data-appid="${this.appId}"] .compendium-browser-mount`);
|
||||||
// @todo Find a better solution than a timeout.
|
// @todo Find a better solution than a timeout.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.activateVueListeners($(this.form), false);
|
this.activateVueListeners();
|
||||||
}, 150);
|
}, 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,37 +142,14 @@ export class CompendiumBrowserVueApplication extends Application {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate additional listeners on the rendered Vue app.
|
* Activate additional listeners on the rendered Vue app.
|
||||||
* @param {jQuery} html
|
|
||||||
*/
|
*/
|
||||||
activateVueListeners(html, repeat = false) {
|
activateVueListeners(repeat = false) {
|
||||||
if (!this.options.editable) {
|
this.vueListenersActive = true;
|
||||||
html.find("input,select,textarea").attr("disabled", true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (html.find(".archmage-v2-vue").length > 0) {
|
|
||||||
this.vueListenersActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place one-time executions after this line.
|
// Place one-time executions after this line.
|
||||||
if (repeat) return;
|
if (repeat) return;
|
||||||
|
|
||||||
// Input listeners.
|
// Input listeners.
|
||||||
let inputs = '.section input[type="text"], .section input[type="number"]';
|
this.element.find(".filtercontainer h3").click(async (ev) => await $(ev.target.nextElementSibling).toggle(100));
|
||||||
html.on("focus", inputs, (event) => this._onFocus(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle focus events.
|
|
||||||
*
|
|
||||||
* @param {*} event
|
|
||||||
*/
|
|
||||||
_onFocus(event) {
|
|
||||||
let target = event.currentTarget;
|
|
||||||
setTimeout(function () {
|
|
||||||
if (target == document.activeElement) {
|
|
||||||
$(target).trigger("select");
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,69 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.control-area {
|
.control-area {
|
||||||
position: sticky;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
|
||||||
flex: 2;
|
flex: 2;
|
||||||
|
.controls {
|
||||||
|
overflow: hidden auto;
|
||||||
|
.filtercontainer {
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 2px;
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
dt {
|
||||||
|
display: inline-block;
|
||||||
|
width: 40%;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
display: inline-block;
|
||||||
|
width: 58%;
|
||||||
|
margin-left: 0;
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.multiselect {
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 3px;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 32px;
|
||||||
|
margin: 2px 0;
|
||||||
|
label {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.small-input {
|
||||||
|
width: calc(100% - 44px);
|
||||||
|
height: 27px;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 3px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.small-select {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
flex: 0;
|
||||||
|
padding: 1rem 0 3px;
|
||||||
|
}
|
||||||
button {
|
button {
|
||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
border: 1px solid #bbb;
|
border: 1px solid #bbb;
|
||||||
|
@ -71,60 +130,6 @@
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
.filtercontainer {
|
|
||||||
border: 1px solid #bbb;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
padding: 2px;
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
dl {
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
|
||||||
dt {
|
|
||||||
display: inline-block;
|
|
||||||
width: 40%;
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
display: inline-block;
|
|
||||||
width: 58%;
|
|
||||||
margin-left: 0;
|
|
||||||
select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.multiselect {
|
|
||||||
border: 1px solid #bbb;
|
|
||||||
border-radius: 3px;
|
|
||||||
vertical-align: middle;
|
|
||||||
line-height: 32px;
|
|
||||||
margin: 2px 0;
|
|
||||||
label {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.small-input {
|
|
||||||
width: calc(100% - 44px);
|
|
||||||
height: 27px;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
border: 1px solid #444;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 0 3px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.small-select {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.list-area {
|
.list-area {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
@ -134,17 +139,11 @@
|
||||||
flex: 0;
|
flex: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.browser {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: hidden !important;
|
|
||||||
.window-content {
|
|
||||||
overflow-y: hidden !important;
|
|
||||||
}
|
|
||||||
ul {
|
ul {
|
||||||
float: right;
|
float: right;
|
||||||
display: block;
|
display: block;
|
||||||
min-width: 335px;
|
// min-width: 335px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -153,6 +152,10 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
li {
|
li {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -178,6 +181,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.browser {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: hidden !important;
|
||||||
|
.window-content {
|
||||||
|
overflow-y: hidden !important;
|
||||||
|
}
|
||||||
.spacer {
|
.spacer {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 5px;
|
min-width: 5px;
|
||||||
|
@ -328,6 +338,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.npc-line {
|
||||||
|
.npc-name {
|
||||||
|
text-shadow: 0 0 8px var(--color-shadow-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.settings {
|
.settings {
|
||||||
|
|
|
@ -1,79 +1,98 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="npc-browser browser flexrow">
|
<div class="npc-browser browser flexrow">
|
||||||
<section class="control-area">
|
<section class="control-area flexcol">
|
||||||
<div class="filtercontainer">
|
<div class="controls">
|
||||||
<!-- Name filter. -->
|
<div class="filtercontainer">
|
||||||
<div class="filter">
|
<!-- Name filter. -->
|
||||||
<input type="text" name="compendiumBrowser.name" v-model="name" :placeholder="game.i18n.localize('Name')" />
|
<div class="filter">
|
||||||
|
<input type="text" name="compendiumBrowser.name" v-model="name" :placeholder="game.i18n.localize('Name')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sort -->
|
||||||
|
<dl class="sorter">
|
||||||
|
<dt>{{ game.i18n.localize('Sort by:') }}</dt>
|
||||||
|
<dd>
|
||||||
|
<select class="sort" name="sortorder" v-model="sortBy">
|
||||||
|
<option v-for="(option, index) in sortOptions" :key="index" :value="option.value">{{ option.label }}</option>
|
||||||
|
</select>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sort -->
|
<div class="filtercontainer">
|
||||||
<dl class="sorter">
|
<h3>{{ game.i18n.localize('General') }}</h3>
|
||||||
<dt>{{ game.i18n.localize('Sort by:') }}</dt>
|
<div class="filters">
|
||||||
<dd>
|
<!-- Level range slider. -->
|
||||||
<select class="sort" name="sortorder" v-model="sortBy">
|
<div class="filter">
|
||||||
<option v-for="(option, index) in sortOptions" :key="index" :value="option.value">{{ option.label }}</option>
|
<label class="unit-title" for="compendiumBrowser.level">{{ game.i18n.localize('Challenge Rating') }}</label>
|
||||||
</select>
|
<div class="level-range flexrow">
|
||||||
</dd>
|
<div class="level-label"><span>{{ crRange[0] }}</span><span v-if="crRange[0] !== crRange[1]"> - {{ crRange[1] }}</span></div>
|
||||||
</dl>
|
<div class="level-input slider-wrapper flexrow">
|
||||||
|
<Slider v-model="crRange" :min="0" :max="30" :tooltips="false"/>
|
||||||
<!-- Reset. -->
|
</div>
|
||||||
<button type="reset" @click="resetFilters()">{{ game.i18n.localize('Reset Filters') }}</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Size filter. -->
|
||||||
<div class="filtercontainer">
|
<div class="filter">
|
||||||
<h3>{{ game.i18n.localize('General') }}</h3>
|
<label class="unit-title" for="compendiumBrowser.size">{{ game.i18n.localize('Size') }}</label>
|
||||||
<!-- Level range slider. -->
|
<Multiselect
|
||||||
<div class="filter">
|
v-model="size"
|
||||||
<label class="unit-title" for="compendiumBrowser.level">{{ game.i18n.localize('Challenge Rating') }}</label>
|
mode="tags"
|
||||||
<div class="level-range flexrow">
|
:searchable="false"
|
||||||
<div class="level-label"><span>{{ crRange[0] }}</span><span v-if="crRange[0] !== crRange[1]"> - {{ crRange[1] }}</span></div>
|
:create-option="false"
|
||||||
<div class="level-input slider-wrapper flexrow">
|
:options="getOptions(CONFIG.DND5E.actorSizes)"
|
||||||
<Slider v-model="crRange" :min="0" :max="30" :tooltips="false"/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.legact">{{ game.i18n.localize('Legendary Actions') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="legact"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(yesNo)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.legres">{{ game.i18n.localize('Legendary Resistances') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="legres"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(yesNo)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter">
|
||||||
|
<label class="unit-title" for="compendiumBrowser.creatureType">{{ game.i18n.localize('Creature Type') }}</label>
|
||||||
|
<Multiselect
|
||||||
|
v-model="creatureType"
|
||||||
|
mode="tags"
|
||||||
|
:searchable="false"
|
||||||
|
:create-option="false"
|
||||||
|
:options="getOptions(CONFIG.DND5E.creatureTypes)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Size filter. -->
|
|
||||||
<div class="filter">
|
<div class="filtercontainer">
|
||||||
<label class="unit-title" for="compendiumBrowser.size">{{ game.i18n.localize('Size') }}</label>
|
<h3>{{ game.i18n.localize('Ability Scores') }}</h3>
|
||||||
<Multiselect
|
<div class="filters">
|
||||||
v-model="size"
|
<div v-for="(ability, key) in abilities" class="filter">
|
||||||
mode="tags"
|
<label class="unit-title" for="compendiumBrowser.str">{{ ability.label }}</label>
|
||||||
:searchable="false"
|
<div class="level-range flexrow">
|
||||||
:create-option="false"
|
<div class="level-label"><span>{{ ability.range[0] }}</span><span v-if="ability.range[0] !== ability.range[1]"> - {{ ability.range[1] }}</span></div>
|
||||||
:options="getOptions(CONFIG.DND5E.actorSizes)"
|
<div class="level-input slider-wrapper flexrow">
|
||||||
/>
|
<Slider v-model="abilities[key].range" :min="1" :max="30" :tooltips="false"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter">
|
</div>
|
||||||
<label class="unit-title" for="compendiumBrowser.legact">{{ game.i18n.localize('Legendary Actions') }}</label>
|
</div>
|
||||||
<Multiselect
|
</div>
|
||||||
v-model="legact"
|
|
||||||
:searchable="false"
|
|
||||||
:create-option="false"
|
|
||||||
:options="getOptions(yesNo)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="filter">
|
|
||||||
<label class="unit-title" for="compendiumBrowser.legres">{{ game.i18n.localize('Legendary Resistances') }}</label>
|
|
||||||
<Multiselect
|
|
||||||
v-model="legres"
|
|
||||||
:searchable="false"
|
|
||||||
:create-option="false"
|
|
||||||
:options="getOptions(yesNo)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="filter">
|
|
||||||
<label class="unit-title" for="compendiumBrowser.creatureType">{{ game.i18n.localize('Creature Type') }}</label>
|
|
||||||
<Multiselect
|
|
||||||
v-model="creatureType"
|
|
||||||
mode="tags"
|
|
||||||
:searchable="false"
|
|
||||||
:create-option="false"
|
|
||||||
:options="getOptions(CONFIG.DND5E.creatureTypes)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<footer>
|
||||||
|
<!-- Reset. -->
|
||||||
|
<button type="reset" @click="resetFilters()">{{ game.i18n.localize('Reset Filters') }}</button>
|
||||||
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="list-area flexcol">
|
<div class="list-area flexcol">
|
||||||
|
@ -89,9 +108,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="npc-line">
|
<div class="npc-line">
|
||||||
<!-- First row is the title. -->
|
<!-- First row is the title. -->
|
||||||
<div class="npc-name">
|
<div class="npc-name">[{{ game.dnd5e.utils.formatCR(entry.system.details.cr) }}] {{ entry.name }}</div>
|
||||||
<a>[{{ game.dnd5e.utils.formatCR(entry.system.details.cr) }}] {{ entry.name }}</a>
|
|
||||||
</div>
|
|
||||||
<!-- Second row is supplemental info. -->
|
<!-- Second row is supplemental info. -->
|
||||||
<div class="npc-tags flexrow">
|
<div class="npc-tags flexrow">
|
||||||
<div class="numbers flexrow">
|
<div class="numbers flexrow">
|
||||||
|
@ -147,6 +164,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const abilities = {};
|
||||||
|
Object.entries(CONFIG.DND5E.abilities).forEach(([k, v]) => abilities[k] = { label: v.label, range: [1, 30] });
|
||||||
return {
|
return {
|
||||||
// Props used for infinite scroll and pagination.
|
// Props used for infinite scroll and pagination.
|
||||||
observer: null,
|
observer: null,
|
||||||
|
@ -171,6 +190,7 @@ export default {
|
||||||
// Mixed decimals and ints aren't supported by the slider, so
|
// Mixed decimals and ints aren't supported by the slider, so
|
||||||
// just use 0 for all CRs below 1.
|
// just use 0 for all CRs below 1.
|
||||||
crRange: [0, 30],
|
crRange: [0, 30],
|
||||||
|
abilities,
|
||||||
legact: '',
|
legact: '',
|
||||||
legres: '',
|
legres: '',
|
||||||
size: [],
|
size: [],
|
||||||
|
@ -267,6 +287,14 @@ export default {
|
||||||
result = result.filter(entry => this.creatureType.includes(entry.system.details.type.value));
|
result = result.filter(entry => this.creatureType.includes(entry.system.details.type.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.entries(this.abilities)
|
||||||
|
.forEach(([k, v]) => {
|
||||||
|
result = result.filter((entry) =>
|
||||||
|
Number(entry.system.abilities[k].value) >= v.range[0] &&
|
||||||
|
Number(entry.system.abilities[k].value) <= v.range[1]
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Reflow pager.
|
// Reflow pager.
|
||||||
if (result.length > this.pager.perPage) {
|
if (result.length > this.pager.perPage) {
|
||||||
this.pager.totalRows = result.length;
|
this.pager.totalRows = result.length;
|
||||||
|
|
Loading…
Reference in New Issue