Overhauled inventory tab again. Closer in functionality to V1
This commit is contained in:
@@ -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, {
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<v-btn
|
||||
v-bind="$attrs"
|
||||
v-on="on"
|
||||
@click.stop
|
||||
>
|
||||
<slot>
|
||||
<v-icon>add</v-icon>
|
||||
|
||||
@@ -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(){
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
113
app/imports/ui/properties/components/inventory/ItemList.vue
Normal file
113
app/imports/ui/properties/components/inventory/ItemList.vue
Normal 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>
|
||||
102
app/imports/ui/properties/components/inventory/ItemListTile.vue
Normal file
102
app/imports/ui/properties/components/inventory/ItemListTile.vue
Normal 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>
|
||||
@@ -6,6 +6,7 @@
|
||||
>
|
||||
<draggable
|
||||
v-model="computedSpells"
|
||||
style="min-height: 24px;"
|
||||
:group="`spell-list`"
|
||||
ghost-class="ghost"
|
||||
draggable=".item"
|
||||
|
||||
Reference in New Issue
Block a user