Overhauled inventory tab again. Closer in functionality to V1

This commit is contained in:
Stefan Zermatten
2020-08-22 00:36:17 +02:00
parent 46501f2759
commit 6275cbd400
8 changed files with 310 additions and 37 deletions

View File

@@ -7,6 +7,7 @@ export default function getActiveProperties({
options = {sort: {order: 1}},
includeUntoggled = false,
includeUnprepared = false,
includeUnequipped = false,
excludeAncestors,
}){
filter = getActivePropertyFilter({
@@ -14,6 +15,7 @@ export default function getActiveProperties({
filter,
includeUntoggled,
includeUnprepared,
includeUnequipped,
excludeAncestors,
});
return CreatureProperties.find(filter, options).fetch();
@@ -24,6 +26,7 @@ export function getActivePropertyFilter({
filter = {},
includeUntoggled = false,
includeUnprepared = false,
includeUnequipped = false,
excludeAncestors = [],
}){
if (!ancestorId){
@@ -34,17 +37,20 @@ export function getActivePropertyFilter({
'ancestors.id': ancestorId,
$or: [
{disabled: true}, // Everything can be disabled
{equipped: false}, // Items can be equipped
{applied: false}, // Buffs can be applied
],
};
if (!includeUnequipped){
disabledAncestorsFilter.$or.push({type: 'item', equipped: {$ne: true}});
}
if (!includeUntoggled){
disabledAncestorsFilter.$or.push({toggleResult: false});
}
if (!includeUnprepared){
disabledAncestorsFilter.$or.push({
prepared: false,
alwaysPrepared: false
type: 'spell',
prepared: {$ne: true},
alwaysPrepared: {$ne: true}
});
}
let disabledAncestorIds = CreatureProperties.find(disabledAncestorsFilter, {

View File

@@ -154,6 +154,14 @@ export function updateParent({docRef, parentRef}){
// Get the parent and its ancestry
let {parentDoc, parent, ancestors} = getAncestry({parentRef});
// Check that the doc isn't its own ancestor
ancestors.forEach(ancestor => {
if (docRef.id === ancestor.id){
throw new Meteor.Error('invalid parenting',
'A doc can\'t be its own ancestor')
}
});
// If the doc and its parent are in the same collection, apply the allowed
// parent rules based on type
if (docRef.collection === parentRef.collection){

View File

@@ -11,6 +11,7 @@
<v-btn
v-bind="$attrs"
v-on="on"
@click.stop
>
<slot>
<v-icon>add</v-icon>

View File

@@ -2,26 +2,32 @@
<div class="inventory">
<column-layout wide-columns>
<div>
<toolbar-card :color="$vuetify.theme.secondary">
<v-spacer slot="toolbar" />
<v-switch
v-if="context.editPermission !== false"
slot="toolbar"
v-model="organize"
label="Organize"
class="justify-end"
/>
<toolbar-card
:color="context.creature.color"
>
<v-toolbar-title slot="toolbar">
Equipped
</v-toolbar-title>
<v-card-text class="px-0">
<creature-properties-tree
:root="{collection: 'creatures', id: creatureId}"
:filter="{
type: {$in: ['item']},
'ancestors.id': {$nin: containerIds}
}"
:organize="organize"
group="inventory"
@selected="e => clickProperty(e)"
@reorganized="({doc}) => setEquipped(doc, false)"
<item-list
equipment
:items="equippedItems"
:parent-ref="{id: creatureId, collection: 'creatures'}"
/>
</v-card-text>
</toolbar-card>
</div>
<div>
<toolbar-card
:color="context.creature.color"
>
<v-toolbar-title slot="toolbar">
Carried
</v-toolbar-title>
<v-card-text class="px-0">
<item-list
:items="carriedItems"
:parent-ref="{id: creatureId, collection: 'creatures'}"
/>
</v-card-text>
</toolbar-card>
@@ -32,7 +38,6 @@
>
<container-card
:model="container"
:organize="organize"
/>
</div>
</column-layout>
@@ -42,23 +47,27 @@
<script>
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
import CreaturePropertiesTree from '/imports/ui/creature/creatureProperties/CreaturePropertiesTree.vue';
import ContainerCard from '/imports/ui/properties/components/inventory/ContainerCard.vue';
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
import { updateProperty } from '/imports/api/creature/CreatureProperties.js';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
export default {
components: {
ColumnLayout,
CreaturePropertiesTree,
ContainerCard,
ToolbarCard,
ItemList,
},
inject: {
context: { default: {} }
},
props: {
creatureId: String,
creatureId: {
type: String,
required: true,
},
},
data(){ return {
organize: false,
@@ -85,6 +94,26 @@ export default {
sort: {order: 1},
});
},
carriedItems(){
return getActiveProperties({
ancestorId: this.creatureId,
includeUnequipped: true,
filter: {
type: 'item',
equipped: {$ne: true},
'parent.id': this.creatureId
},
});
},
equippedItems(){
return getActiveProperties({
ancestorId: this.creatureId,
filter: {
type: 'item',
equipped: true,
},
});
},
},
computed: {
containerIds(){

View File

@@ -11,12 +11,9 @@
<v-spacer />
</template>
<v-card-text class="px-0">
<creature-properties-tree
:root="{collection: 'creatureProperties', id: model._id}"
:filter="{type: {$in: ['container', 'item', 'folder']}}"
:organize="organize"
group="inventory"
@selected="e => clickProperty(e)"
<item-list
:items="items"
:parent-ref="{id: model._id, collection: 'creatureProperties'}"
/>
</v-card-text>
</toolbar-card>
@@ -24,16 +21,19 @@
<script>
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
import CreaturePropertiesTree from '/imports/ui/creature/creatureProperties/CreaturePropertiesTree.vue';
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
export default {
components: {
ToolbarCard,
CreaturePropertiesTree,
ItemList,
},
props: {
model: Object,
organize: Boolean,
model: {
type: Object,
required: true,
},
},
methods: {
clickContainer(_id){
@@ -50,7 +50,20 @@ export default {
data: {_id},
});
},
}
},
meteor: {
items(){
return getActiveProperties({
ancestorId: this.model._id,
includeUnequipped: true,
filter: {
type: {$in: ['item', 'container']},
'parent.id': this.model._id,
equipped: {$ne: true},
},
});
},
}
};
</script>

View File

@@ -0,0 +1,113 @@
<template lang="html">
<v-list
two-line
dense
class="item-list"
>
<draggable
v-model="dataItems"
style="min-height: 24px;"
:group="`item-list`"
ghost-class="ghost"
draggable=".item"
handle=".handle"
:animation="200"
@change="change"
>
<item-list-tile
v-for="item in dataItems"
:key="item._id"
class="item"
:data-id="`item-list-tile-${item._id}`"
:model="item"
@click="clickProperty(item._id)"
/>
</draggable>
</v-list>
</template>
<script>
import draggable from 'vuedraggable';
import ItemListTile from '/imports/ui/properties/components/inventory/ItemListTile.vue';
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
import { updateProperty } from '/imports/api/creature/CreatureProperties.js';
export default {
components: {
draggable,
ItemListTile,
},
props: {
items: {
type: Array,
default: () => [],
},
parentRef: {
type: Object,
required: true,
},
preparingSpells: Boolean,
equipment: Boolean,
},
data(){ return {
dataItems: [],
}},
computed: {
levels(){
let levels = new Set();
this.items.forEach(item => levels.add(item.level));
return levels;
},
},
watch: {
items(value){
this.dataItems = value;
}
},
mounted(){
this.dataItems = this.items;
},
methods: {
clickProperty(_id){
this.$store.commit('pushDialogStack', {
component: 'creature-property-dialog',
elementId: `item-list-tile-${_id}`,
data: {_id},
});
},
change({added, moved}){
let event = added || moved;
if (event){
// If this item is now adjacent to another, set the order accordingly
let order;
let before = this.dataItems[event.newIndex - 1];
let after = this.dataItems[event.newIndex + 1];
if (before && before._id){
order = before.order + 0.5;
} else if (after && after._id) {
order = after.order - 0.5;
} else {
order = -0.5;
}
let doc = event.element;
organizeDoc.call({
docRef: {
id: doc._id,
collection: 'creatureProperties',
},
parentRef: this.parentRef,
order,
});
if (doc.type === 'item' && doc.equipped != this.equipment){
updateProperty.call({
_id: doc._id,
path: ['equipped'],
value: !!this.equipment,
});
}
}
setTimeout(() => this.dataItems = this.items, 0);
},
}
}
</script>

View File

@@ -0,0 +1,102 @@
<template lang="html">
<v-list-tile
class="item"
v-on="hasClickListener ? {click} : {}"
>
<v-list-tile-avatar class="item-avatar">
<property-icon
class="mr-2"
:model="model"
:color="model.color"
/>
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title>
{{ title }}
</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<increment-button
v-if="context.creature && model.showIncrement"
icon
flat
color="primary"
:value="model.quantity"
@change="changeQuantity"
>
<v-icon>
$vuetify.icons.abacus
</v-icon>
</increment-button>
</v-list-tile-action>
<v-list-tile-action>
<v-icon
style="height: 100%; width: 40px; cursor: move;"
class="handle"
>
drag_indicator
</v-icon>
</v-list-tile-action>
</v-list-tile>
</template>
<script>
import treeNodeViewMixin from '/imports/ui/properties/treeNodeViews/treeNodeViewMixin.js';
import PROPERTIES from '/imports/constants/PROPERTIES.js';
import { adjustQuantity } from '/imports/api/creature/CreatureProperties.js';
import IncrementButton from '/imports/ui/components/IncrementButton.vue';
export default {
components:{
IncrementButton,
},
mixins: [treeNodeViewMixin],
inject: {
context: { default: {} }
},
props: {
preparingSpells: Boolean,
},
computed: {
hasClickListener(){
return this.$listeners && !!this.$listeners.click;
},
title(){
let model = this.model;
if (!model) return;
if (model.quantity !== 1){
if (model.plural){
return `${model.quantity} ${model.plural}`;
} else if (model.name){
return `${model.quantity} ${model.name}`;
}
} else if (model.name) {
return model.name;
}
let prop = PROPERTIES[model.type]
return prop && prop.name;
}
},
methods: {
click(e){
this.$emit('click', e);
},
changeQuantity({type, value}) {
adjustQuantity.call({
_id: this.model._id,
operation: type,
value: value
});
}
},
}
</script>
<style lang="css" scoped>
.item-avatar {
min-width: 32px;
}
.item {
background-color: inherit;
}
</style>

View File

@@ -6,6 +6,7 @@
>
<draggable
v-model="computedSpells"
style="min-height: 24px;"
:group="`spell-list`"
ghost-class="ghost"
draggable=".item"