It works! Sort of!

pull/12/head
Matt Smith 2024-03-29 21:17:37 -05:00
parent 2327f2cdea
commit 5b0877382f
7 changed files with 4027 additions and 242 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,10 @@
display: block; display: block;
} }
} }
.compendium-browser-mount {
flex: 1;
overflow: hidden;
}
.compendium-browser { .compendium-browser {
overflow-y: hidden !important; overflow-y: hidden !important;
max-width: 1100px; max-width: 1100px;

View File

@ -14,8 +14,8 @@
have been opened at least once. have been opened at least once.
--> -->
<Tab group="primary" :tab="tabs.primary.creatures" classes="container container--bottom flexrow"> <Tab group="primary" :tab="tabs.primary.creatures" classes="container container--bottom flexrow">
<Stub>Creatures</Stub> <!-- <Stub>Creatures</Stub> -->
<!-- <CompendiumBrowserCreatures v-if="tabs.primary.creatures.active || tabs.primary.creatures.opened" :tab="tabs.primary.creatures"/> --> <CompendiumBrowserCreatures v-if="tabs.primary.creatures.active || tabs.primary.creatures.opened" :tab="tabs.primary.creatures"/>
</Tab> </Tab>
<Tab group="primary" :tab="tabs.primary.powers" classes="container container--bottom flexrow"> <Tab group="primary" :tab="tabs.primary.powers" classes="container container--bottom flexrow">
@ -37,7 +37,7 @@
import Tabs from '@/components/parts/Tabs.vue'; import Tabs from '@/components/parts/Tabs.vue';
import Tab from '@/components/parts/Tab.vue'; import Tab from '@/components/parts/Tab.vue';
import Stub from '@/components/dialogs/compendium-browser/stub.vue'; import Stub from '@/components/dialogs/compendium-browser/stub.vue';
// import CompendiumBrowserCreatures from '@/components/dialogs/compendium-browser/CompendiumBrowserCreatures.vue'; import CompendiumBrowserCreatures from '@/components/dialogs/compendium-browser/CompendiumBrowserCreatures.vue';
// import CompendiumBrowserPowers from '@/components/dialogs/compendium-browser/CompendiumBrowserPowers.vue'; // import CompendiumBrowserPowers from '@/components/dialogs/compendium-browser/CompendiumBrowserPowers.vue';
// import CompendiumBrowserItems from '@/components/dialogs/compendium-browser/CompendiumBrowserItems.vue'; // import CompendiumBrowserItems from '@/components/dialogs/compendium-browser/CompendiumBrowserItems.vue';
@ -48,7 +48,7 @@ export default {
Tabs, Tabs,
Tab, Tab,
Stub, Stub,
// CompendiumBrowserCreatures, CompendiumBrowserCreatures,
// CompendiumBrowserPowers, // CompendiumBrowserPowers,
// CompendiumBrowserItems // CompendiumBrowserItems
}, },

View File

@ -1,104 +1,105 @@
<template> <template>
<section class="section section--sidebar flexcol filters"> <div class="npc-browser browser flexrow">
<!-- Sort --> <section class="control-area">
<div class="unit unit--input"> <div class="filtercontainer">
<label for="compendiumBrowser.sort" class="unit-title">{{ localize('ARCHMAGE.sort') }}</label> <!-- Name filter. -->
<select class="sort" name="compendiumBrowser.sort" 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.name">{{ game.i18n.localize('Name') }}</label>
</select> <input type="text" name="compendiumBrowser.name" v-model="name" placeholder="Hydra"/>
</div>
<!-- Level range slider. -->
<div class="unit unit--input">
<label class="unit-title" for="compendiumBrowser.level">{{ localize('ARCHMAGE.level') }}</label>
<div class="level-range flexrow">
<div class="level-label"><span>{{ levelRange[0] }}</span><span v-if="levelRange[0] !== levelRange[1]"> - {{ levelRange[1] }}</span></div>
<div class="level-input slider-wrapper flexrow">
<Slider v-model="levelRange" :min="1" :max="15" :tooltips="false"/>
</div> </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>
<!-- Reset. -->
<button type="reset" @click="resetFilters()">{{ game.i18n.localize('Reset Filters') }}</button>
</div> </div>
</div>
<!-- Name filter. -->
<div class="unit unit--input"> <!-- Level range slider. -->
<label class="unit-title" for="compendiumBrowser.name">{{ localize('ARCHMAGE.name') }}</label> <!-- <div class="unit unit--input">
<input type="text" name="compendiumBrowser.name" v-model="name" placeholder="Hydra"/> <label class="unit-title" for="compendiumBrowser.level">{{ game.i18n.localize('ARCHMAGE.level') }}</label>
</div> <div class="level-range flexrow">
<div class="level-label"><span>{{ levelRange[0] }}</span><span v-if="levelRange[0] !== levelRange[1]"> - {{ levelRange[1] }}</span></div>
<!-- Type filter. --> <div class="level-input slider-wrapper flexrow">
<div class="unit unit--input"> <Slider v-model="levelRange" :min="1" :max="15" :tooltips="false"/>
<label class="unit-title" for="compendiumBrowser.type">{{ localize('ARCHMAGE.type') }}</label> </div>
<Multiselect </div>
v-model="type" </div> -->
mode="tags"
:searchable="false" <!-- Type filter. -->
:create-option="false" <!-- <div class="unit unit--input">
:options="CONFIG.ARCHMAGE.creatureTypes" <label class="unit-title" for="compendiumBrowser.type">{{ game.i18n.localize('ARCHMAGE.type') }}</label>
/> <Multiselect
</div> v-model="type"
mode="tags"
<!-- Role filter. --> :searchable="false"
<div class="unit unit--input"> :create-option="false"
<label class="unit-title" for="compendiumBrowser.role">{{ localize('ARCHMAGE.role') }}</label> :options="CONFIG.ARCHMAGE.creatureTypes"
<Multiselect />
v-model="role" </div> -->
mode="tags"
:searchable="false" <!-- Role filter. -->
:create-option="false" <!-- <div class="unit unit--input">
:options="CONFIG.ARCHMAGE.creatureRoles" <label class="unit-title" for="compendiumBrowser.role">{{ game.i18n.localize('ARCHMAGE.role') }}</label>
/> <Multiselect
</div> v-model="role"
mode="tags"
<!-- Size filter. --> :searchable="false"
<div class="unit unit--input"> :create-option="false"
<label class="unit-title" for="compendiumBrowser.size">{{ localize('ARCHMAGE.size') }}</label> :options="CONFIG.ARCHMAGE.creatureRoles"
<Multiselect />
v-model="size" </div> -->
mode="tags"
:searchable="false" <!-- Size filter. -->
:create-option="false" <!-- <div class="unit unit--input">
:options="CONFIG.ARCHMAGE.creatureSizes" <label class="unit-title" for="compendiumBrowser.size">{{ game.i18n.localize('ARCHMAGE.size') }}</label>
/> <Multiselect
</div> v-model="size"
mode="tags"
<!-- Reset. --> :searchable="false"
<div class="unit unit--input flexrow"> :create-option="false"
<button type="reset" @click="resetFilters()">{{ localize('Reset') }}</button> :options="CONFIG.ARCHMAGE.creatureSizes"
</div> />
</div> -->
</section>
</section>
<div class="section section--no-overflow">
<!-- Creatures results. --> <div class="list-area flexcol">
<section class="section section--creatures section--main flexcol"> <!-- Creatures results. -->
<ul v-if="loaded" class="compendium-browser-results compendium-browser-creatures"> <!-- <section class="section section--npcs section--main flexcol"> -->
<ul v-if="loaded" class="compendium-browser-results compendium-browser-npcs">
<!-- Individual creature entries. --> <!-- Individual creature entries. -->
<li v-for="(entry, entryKey) in entries" :key="entryKey" :class="`creature-summary compendium-browser-row${entryKey >= pager.lastIndex - 1 && entryKey < pager.totalRows - 1 ? ' compendium-browser-row-observe': ''} flexrow document actor`" :data-document-id="entry._id" @click="openDocument(entry.uuid)"> <li v-for="(entry, entryKey) in entries" :key="entryKey" :class="`npc 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 --> <!-- 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="entry.img" @dragstart="startDrag($event, entry, 'Actor')" draggable="true"/> <div class="npc-image">
<div class="flexcol creature-contents" @dragstart="startDrag($event, entry, 'Actor')" draggable="true"> <img :src="entry.img ?? 'icons/svg/mystery-man.svg'"/>
</div>
<div class="npc-line">
<!-- First row is the title. --> <!-- First row is the title. -->
<div class="creature-title-wrapper"> <div class="npc-name">
<strong class="creature-title"><span v-if="entry.system?.attributes?.level?.value">[{{ entry.system.attributes.level.value }}]</span> {{ entry?.name }}</strong> <a>{{ entry.name }}</a>
</div> </div>
<!-- Second row is supplemental info. --> <!-- Second row is supplemental info. -->
<div class="grid creature-grid"> <div class="npc-tags">
<div class="creature-defenses" :data-tooltip="localize('ARCHMAGE.defenses')"> <span class="cr" :data-tooltip="game.i18n.localize('Challenge rating')">{{ entry.system.details.cr }}</span>
<span><strong>{{ localize('ARCHMAGE.CHAT.HP') }}:</strong> {{ entry.system.attributes.hp.max }}</span> <span class="size">{{ entry.system.traits.size }}</span>
<span><strong>{{ localize('ARCHMAGE.ac.key') }}:</strong> {{ entry.system.attributes.ac.value }}</span> <span class="type">{{ entry.system.details.type.value }}</span>
<span><strong>{{ localize('ARCHMAGE.pd.key') }}:</strong> {{ entry.system.attributes.pd.value }}</span>
<span><strong>{{ localize('ARCHMAGE.md.key') }}:</strong> {{ entry.system.attributes.md.value }}</span>
</div>
<div class="creature-type" :data-tooltip="localize('ARCHMAGE.type')">{{ CONFIG.ARCHMAGE.creatureTypes[entry?.system?.details?.type?.value] }}</div>
<div class="creature-role" :data-tooltip="localize('ARCHMAGE.role')">{{ CONFIG.ARCHMAGE.creatureRoles[entry?.system?.details?.role?.value] }}</div>
<div class="creature-size" :data-tooltip="localize('ARCHMAGE.size')">{{ CONFIG.ARCHMAGE.creatureSizes[entry?.system?.details?.size?.value] }}</div>
</div> </div>
</div> </div>
</li> </li>
</ul> </ul>
<div v-else class="compendium-browser-loading"><p><i class="fas fa-circle-notch fa-spin"></i>Please wait, loading...</p></div> <div v-else class="compendium-browser-loading"><p><i class="fas fa-circle-notch fa-spin"></i>Please wait, loading...</p></div>
</section> <!-- </section> -->
</div>
</div> </div>
</template> </template>
@ -114,7 +115,6 @@ import {
getActorModuleArt, getActorModuleArt,
openDocument, openDocument,
startDrag, startDrag,
localize
} from '@/methods/Helpers.js'; } from '@/methods/Helpers.js';
export default { export default {
@ -131,7 +131,6 @@ export default {
getActorModuleArt, getActorModuleArt,
openDocument, openDocument,
startDrag, startDrag,
localize,
// Foundry base props and methods. // Foundry base props and methods.
CONFIG, CONFIG,
game, game,
@ -149,21 +148,18 @@ export default {
totalRows: 0, totalRows: 0,
}, },
// Sorting. // Sorting.
sortBy: 'level', sortBy: 'name',
sortOptions: [ sortOptions: [
{ value: 'level', label: game.i18n.localize('ARCHMAGE.level') }, { value: 'name', label: game.i18n.localize('Name') },
{ value: 'name', label: game.i18n.localize('ARCHMAGE.name') }, { value: 'cr', label: game.i18n.localize('Challenge Rating') },
{ value: 'type', label: game.i18n.localize('ARCHMAGE.type') }, { value: 'size', label: game.i18n.localize('Size') },
{ value: 'role', label: game.i18n.localize('ARCHMAGE.role') },
{ value: 'size', label: game.i18n.localize('ARCHMAGE.size') },
], ],
// Our list of pseudo documents returned from the compendium. // Our list of pseudo documents returned from the compendium.
packIndex: [], packIndex: [],
// Filters. // Filters.
name: '', name: '',
levelRange: [1, 15], // @todo partial CRs like 1/4.
type: [], crRange: [1, 30],
role: [],
size: [], size: [],
} }
}, },
@ -195,18 +191,13 @@ export default {
* Click event to reset our filters. * Click event to reset our filters.
*/ */
resetFilters() { resetFilters() {
this.sortBy = 'level'; this.sortBy = 'name';
this.name = ''; this.name = '';
this.levelRange = [1, 15]; this.crRange = [1, 30];
this.type = [];
this.role = [];
this.size = []; this.size = [];
}, },
}, },
computed: { computed: {
nightmode() {
return game.settings.get("archmage", "nightmode") ? 'nightmode' : '';
},
entries() { entries() {
// Build our results array. Exit early if the length is 0. // Build our results array. Exit early if the length is 0.
let result = this.packIndex; let result = this.packIndex;
@ -221,24 +212,18 @@ export default {
result = result.filter(entry => entry.name.toLocaleLowerCase().includes(name)); result = result.filter(entry => entry.name.toLocaleLowerCase().includes(name));
} }
// Filter by level range. // // Filter by level range.
if (this.levelRange.length == 2) { // if (this.crRange.length == 2) {
result = result.filter(entry => // result = result.filter(entry =>
entry.system.attributes.level.value >= this.levelRange[0] && // entry.system.attributes.level.value >= this.crRange[0] &&
entry.system.attributes.level.value <= this.levelRange[1] // entry.system.attributes.level.value <= this.crRange[1]
); // );
} // }
// Handle multiselect filters, which use arrays as their values. // Handle multiselect filters, which use arrays as their values.
if (Array.isArray(this.type) && this.type.length > 0) { // if (Array.isArray(this.size) && this.size.length > 0) {
result = result.filter(entry => this.type.includes(entry.system.details.type.value)); // result = result.filter(entry => this.size.includes(entry.system.details.size.value));
} // }
if (Array.isArray(this.role) && this.role.length > 0) {
result = result.filter(entry => this.role.includes(entry.system.details.role.value));
}
if (Array.isArray(this.size) && this.size.length > 0) {
result = result.filter(entry => this.size.includes(entry.system.details.size.value));
}
// Reflow pager. // Reflow pager.
if (result.length > this.pager.perPage) { if (result.length > this.pager.perPage) {
@ -256,14 +241,10 @@ export default {
switch (this.sortBy) { switch (this.sortBy) {
case 'name': case 'name':
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);
case 'type':
return a.system.details?.type?.value.localeCompare(b.system.details?.type?.value);
case 'role':
return a.system.details?.role?.value.localeCompare(b.system.details?.role?.value);
case 'size': case 'size':
return a.system.details?.size?.value.localeCompare(b.system.details?.size?.value); return a.system.traits.size.localeCompare(b.system.traits.size);
} }
return a.system.attributes.level.value - b.system.attributes.level.value; return a.system.details.cr - b.system.details.cr;
}); });
// Return results. // Return results.
@ -279,21 +260,17 @@ export default {
// Load the pack index with the fields we need. // Load the pack index with the fields we need.
getPackIndex([ getPackIndex([
'archmage.srd-Monsters', 'dnd5e.monsters',
'archmage.animal-companions', // insert additional packs as needed.
'archmage.necromancer-summons',
], [ ], [
'system.attributes.level', 'img',
'system.attributes.hp.max', 'system.details.cr',
'system.attributes.ac.value', 'system.details.type',
'system.attributes.pd.value', 'system.traits.size'
'system.attributes.md.value', // insert additional properties as needed.
'system.details.role.value',
'system.details.size.value',
'system.details.type.value'
]).then(packIndex => { ]).then(packIndex => {
// Restore the pack art. // Restore the pack art.
if (game.archmage.system?.moduleArt?.map?.size > 0) { if (!game.dnd5e?.moduleArt?.suppressArt && game.dnd5e?.moduleArt?.map?.size > 0) {
for (let record of packIndex) { for (let record of packIndex) {
record.img = getActorModuleArt(record); record.img = getActorModuleArt(record);
} }
@ -318,7 +295,7 @@ export default {
// Adjust our observers whenever the results of the compendium browser // Adjust our observers whenever the results of the compendium browser
// are updated. // are updated.
onUpdated(() => { onUpdated(() => {
const target = document.querySelector('.compendium-browser-creatures .compendium-browser-row-observe'); const target = document.querySelector('.compendium-browser-npcs .compendium-browser-row-observe');
if (target) { if (target) {
this.observer.observe(target); this.observer.observe(target);
} }
@ -332,7 +309,7 @@ export default {
} }
</script> </script>
<style lang="scss"> <style lang="less">
// Import our component styles. // Import our component styles.
@import "@vueform/slider/themes/default.css"; @import "@vueform/slider/themes/default.css";
@import "@vueform/multiselect/themes/default.css"; @import "@vueform/multiselect/themes/default.css";

View File

@ -3,10 +3,6 @@ export function getSafeValue(property, defaultValue) {
return defaultValue; return defaultValue;
} }
export function localize(key) {
return game.i18n.localize(key);
}
export function cssClass(string) { export function cssClass(string) {
return encodeURIComponent( return encodeURIComponent(
string.trim().toLowerCase() string.trim().toLowerCase()
@ -46,7 +42,7 @@ export function getActorModuleArt(actor) {
// UUID doesn't exactly match the format used in the map currently. // UUID doesn't exactly match the format used in the map currently.
const actorMapId = actor.uuid.replace('.Actor', ''); const actorMapId = actor.uuid.replace('.Actor', '');
// Retrieve the art from the map, or fallback to the actor image. // Retrieve the art from the map, or fallback to the actor image.
const art = game.archmage.system.moduleArt.map.get(actorMapId); const art = game.dnd5e.moduleArt.map.get(actorMapId);
return art?.actor ?? actor.img; return art?.actor ?? actor.img;
} }