Added big slot cards to build tab, improved build tab
This commit is contained in:
@@ -44,8 +44,8 @@ const restCreature = new ValidatedMethod({
|
||||
let filter = {
|
||||
'ancestors.id': creatureId,
|
||||
reset: resetFilter,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
};
|
||||
// update all attribute's damage
|
||||
filter.type = 'attribute';
|
||||
|
||||
@@ -6,7 +6,7 @@ import writeErrors from './computation/writeComputation/writeErrors.js';
|
||||
|
||||
export default function computeCreature(creatureId){
|
||||
if (Meteor.isClient) return;
|
||||
console.log('compute')
|
||||
// console.log('compute ' + creatureId);
|
||||
const computation = buildCreatureComputation(creatureId);
|
||||
computeComputation(computation, creatureId);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { debounce } from 'lodash';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import computeCreature from './computeCreature';
|
||||
|
||||
const COMPUTE_DEBOUNCE_TIME = 100; // ms
|
||||
export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.}
|
||||
|
||||
export function loadCreature(creatureId, subscription) {
|
||||
@@ -40,7 +42,7 @@ class LoadedCreature {
|
||||
|
||||
const compute = debounce(Meteor.bindEnvironment(() => {
|
||||
computeCreature(creatureId);
|
||||
}), 100);
|
||||
}), COMPUTE_DEBOUNCE_TIME);
|
||||
|
||||
self.properties = new Map();
|
||||
// Observe all creature properties which are needed for computation
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
props: {
|
||||
active: Boolean,
|
||||
dark: Boolean,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:class="{
|
||||
'empty': !hasChildren,
|
||||
}"
|
||||
:data-id="`tree-node-${node._id}`"
|
||||
:data-id="`build-tree-node-${node._id}`"
|
||||
>
|
||||
<div
|
||||
class="layout align-center justify-start tree-node-title"
|
||||
@@ -50,7 +50,6 @@
|
||||
<tree-node-view
|
||||
v-else
|
||||
:model="node"
|
||||
:hide-icon="node.type === 'propertySlot'"
|
||||
/>
|
||||
<template v-if="condenseChild">
|
||||
<span class="mr-4">:</span>
|
||||
@@ -69,6 +68,7 @@
|
||||
<build-tree-node-list
|
||||
v-if="showExpanded"
|
||||
:children="computedChildren"
|
||||
@selected="e => $emit('selected', e)"
|
||||
/>
|
||||
<div v-else>
|
||||
<div
|
||||
@@ -119,15 +119,22 @@
|
||||
},
|
||||
},
|
||||
data(){return {
|
||||
expanded: this.node._descendantCanFill || (
|
||||
this.node.type === 'propertySlot' &&
|
||||
this. node.quantityExpected?.value === 0 ||
|
||||
(this.node.quantityExpected?.value > 1 && this.node.spaceLeft > 0)
|
||||
),
|
||||
expanded: false,
|
||||
/* expand if there's a slot needing attention:
|
||||
this.node._descendantCanFill || (
|
||||
this.node.type === 'propertySlot' &&
|
||||
this. node.quantityExpected?.value === 0 ||
|
||||
(this.node.quantityExpected?.value > 1 && this.node.spaceLeft > 0)
|
||||
)
|
||||
*/
|
||||
}},
|
||||
computed: {
|
||||
condenseChild(){
|
||||
return this.node.type === 'propertySlot' && this.children.length === 1;
|
||||
return this.node.type === 'propertySlot' &&
|
||||
this.children.length === 1 &&
|
||||
this.children[0].node.type !== 'propertySlot' &&
|
||||
this.node.quantityExpected &&
|
||||
this.node.quantityExpected.value === 1;
|
||||
},
|
||||
isSlot(){
|
||||
return this.node.type === 'propertySlot';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
icon
|
||||
:data-id="`slot-add-button-${model._id}`"
|
||||
class="slot-add-button accent--text"
|
||||
@click="fillSlot()"
|
||||
@click.stop="fillSlot()"
|
||||
>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
<template lang="html">
|
||||
<v-container fluid>
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="8" lg="6" style="height: 100%;">
|
||||
<v-card style="height: calc(100vh - 120px); overflow: auto;">
|
||||
<v-col cols="12">
|
||||
<slot-cards-to-fill :creature-id="creatureId" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="8"
|
||||
lg="6"
|
||||
>
|
||||
<v-card class="pb-4">
|
||||
<v-card-title>Slots</v-card-title>
|
||||
<build-tree-node-list :children="slotBuildTree" />
|
||||
<build-tree-node-list
|
||||
:children="slotBuildTree"
|
||||
class="mx-2"
|
||||
@selected="_id => propertyClicked({_id, prefix: 'build-tree-node-'})"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" lg="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
md="4"
|
||||
lg="6"
|
||||
>
|
||||
<v-card class="class-details mb-2">
|
||||
<v-card-title
|
||||
v-if="creature.variables.level"
|
||||
@@ -75,29 +92,6 @@
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
<toolbar-card
|
||||
data-id="slot-card"
|
||||
@toolbarclick="showSlotDialog"
|
||||
>
|
||||
<template slot="toolbar">
|
||||
<v-toolbar-title>
|
||||
Build
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
<v-toolbar-title>
|
||||
<v-icon
|
||||
small
|
||||
style="width: 16px;"
|
||||
class="mr-1"
|
||||
>
|
||||
mdi-pencil
|
||||
</v-icon>
|
||||
</v-toolbar-title>
|
||||
</template>
|
||||
<v-card-text style="background-color: inherit;">
|
||||
<slots :creature-id="creatureId" />
|
||||
</v-card-text>
|
||||
</toolbar-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
@@ -106,11 +100,9 @@
|
||||
<script lang="js">
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import Slots from '/imports/ui/creature/slots/Slots.vue';
|
||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import BuildTreeNodeList from '/imports/ui/creature/buildTree/BuildTreeNodeList.vue';
|
||||
import SlotCardsToFill from '/imports/ui/creature/slots/SlotCardsToFill.vue';
|
||||
|
||||
function traverse(tree, callback, parents = []){
|
||||
tree.forEach(node => {
|
||||
@@ -121,10 +113,8 @@ function traverse(tree, callback, parents = []){
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColumnLayout,
|
||||
Slots,
|
||||
ToolbarCard,
|
||||
BuildTreeNodeList,
|
||||
SlotCardsToFill,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
@@ -214,6 +204,13 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
propertyClicked({_id, prefix}){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `${prefix}${_id}`,
|
||||
data: {_id},
|
||||
});
|
||||
},
|
||||
addExperience(){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'experience-insert-dialog',
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
<script lang="js">
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import Slots from '/imports/ui/creature/slots/Slots.vue';
|
||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import NoteCard from '/imports/ui/properties/components/persona/NoteCard.vue';
|
||||
import CreatureSummary from '/imports/ui/creature/character/CreatureSummary.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
|
||||
70
app/imports/ui/creature/slots/SlotCard.vue
Normal file
70
app/imports/ui/creature/slots/SlotCard.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<v-card
|
||||
v-if="model"
|
||||
v-bind="$attrs"
|
||||
:data-id="`slot-card-${model._id}`"
|
||||
:style="`border: solid 1px ${accentColor};`"
|
||||
hover
|
||||
class="slot-card d-flex flex-column"
|
||||
style="max-width: 400px;"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<card-highlight
|
||||
:active="hover"
|
||||
/>
|
||||
<v-card-title>
|
||||
{{ model.name }}
|
||||
</v-card-title>
|
||||
<v-card-text v-if="model.description">
|
||||
{{ model.description.value }}
|
||||
</v-card-text>
|
||||
<v-spacer />
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
color="primary"
|
||||
@click.stop="$emit('ignore')"
|
||||
>
|
||||
Skip
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CardHighlight from '/imports/ui/components/CardHighlight.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CardHighlight,
|
||||
},
|
||||
inject: {
|
||||
theme: {
|
||||
default: {
|
||||
isDark: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data(){ return {
|
||||
hover: false,
|
||||
}},
|
||||
computed: {
|
||||
accentColor(){
|
||||
if (this.theme.isDark){
|
||||
return this.$vuetify.theme.themes.dark.primary;
|
||||
} else {
|
||||
return this.$vuetify.theme.themes.light.primary;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
104
app/imports/ui/creature/slots/SlotCardsToFill.vue
Normal file
104
app/imports/ui/creature/slots/SlotCardsToFill.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="slots-to-fill">
|
||||
<v-slide-y-transition
|
||||
group
|
||||
class="d-flex flex-row flex-wrap"
|
||||
>
|
||||
<slot-card
|
||||
v-for="slot in slots"
|
||||
:key="slot._id"
|
||||
:model="slot"
|
||||
class="ma-1"
|
||||
hover
|
||||
@ignore="ignoreSlot(slot._id)"
|
||||
@click="fillSlot(slot._id)"
|
||||
/>
|
||||
</v-slide-y-transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import SlotCard from '/imports/ui/creature/slots/SlotCard.vue';
|
||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SlotCard,
|
||||
},
|
||||
inject: {
|
||||
context: { default: {} }
|
||||
},
|
||||
methods: {
|
||||
ignoreSlot(_id){
|
||||
updateCreatureProperty.call({
|
||||
_id,
|
||||
path: ['ignored'],
|
||||
value: true
|
||||
}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
snackbar(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
fillSlot(slotId){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'slot-fill-dialog',
|
||||
elementId: `slot-card-${slotId}`,
|
||||
data: {
|
||||
slotId,
|
||||
creatureId: this.context.creatureId,
|
||||
},
|
||||
callback(nodeIds){
|
||||
if (!nodeIds || !nodeIds.length) return;
|
||||
let newPropertyId = insertPropertyFromLibraryNode.call({
|
||||
nodeIds,
|
||||
parentRef: {
|
||||
'id': slotId,
|
||||
'collection': 'creatureProperties',
|
||||
},
|
||||
}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
snackbar(error);
|
||||
}
|
||||
});
|
||||
return `slot-child-${newPropertyId}`;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
slots(){
|
||||
return CreatureProperties.find({
|
||||
type: 'propertySlot',
|
||||
'ancestors.id': this.context.creatureId,
|
||||
ignored: { $ne: true },
|
||||
$and: [
|
||||
{
|
||||
$or: [
|
||||
{'slotCondition.value': {$nin: [false, 0, '']}},
|
||||
{'slotCondition.value': {$exists: false}},
|
||||
]
|
||||
},{
|
||||
$or: [
|
||||
{ quantityExpected: {exists: false} },
|
||||
{ 'quantityExpected.value': 0 },
|
||||
{spaceLeft: {$gt: 0}},
|
||||
]
|
||||
},
|
||||
],
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,36 +0,0 @@
|
||||
<template lang="html">
|
||||
<dialog-base>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Build
|
||||
</v-toolbar-title>
|
||||
<slots
|
||||
:creature-id="creatureId"
|
||||
show-hidden-slots
|
||||
/>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import Slots from '/imports/ui/creature/slots/Slots.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DialogBase,
|
||||
Slots,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
reactiveProvide: {
|
||||
name: 'context',
|
||||
include: ['creatureId'],
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -1,160 +0,0 @@
|
||||
<template lang="html">
|
||||
<div class="slots">
|
||||
<div
|
||||
v-for="slot in slots"
|
||||
:key="slot._id"
|
||||
class="slot"
|
||||
>
|
||||
<h3 class="layout align-center">
|
||||
{{ slot.name }}
|
||||
<v-spacer />
|
||||
<span v-if="slot.quantityExpected && slot.quantityExpected.value > 1">
|
||||
{{ slot.totalFilled }} / {{ slot.quantityExpected.value }}
|
||||
</span>
|
||||
</h3>
|
||||
<v-list v-if="slot.children.length">
|
||||
<v-list-item
|
||||
v-for="child in slot.children"
|
||||
:key="child._id"
|
||||
:data-id="`slot-child-${child._id}`"
|
||||
@click="clickSlotChild(child)"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<tree-node-view
|
||||
class="slotChild"
|
||||
:model="child"
|
||||
/>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-btn
|
||||
icon
|
||||
small
|
||||
@click.stop="remove(child)"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-btn
|
||||
v-if="!slot.quantityExpected || !slot.quantityExpected.value || slot.spaceLeft"
|
||||
icon
|
||||
:data-id="`slot-add-button-${slot._id}`"
|
||||
class="slot-add-button"
|
||||
style="background-color: inherit;"
|
||||
@click="fillSlot(slot)"
|
||||
>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||
import softRemoveProperty from '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
|
||||
import restoreProperty from '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
|
||||
import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TreeNodeView,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
showHiddenSlots: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clickSlotChild({_id}){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `slot-child-${_id}`,
|
||||
data: {_id},
|
||||
});
|
||||
},
|
||||
fillSlot(slot){
|
||||
let slotId = slot._id;
|
||||
let creatureId = this.creatureId;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'slot-fill-dialog',
|
||||
elementId: `slot-add-button-${slotId}`,
|
||||
data: {
|
||||
slotId,
|
||||
creatureId,
|
||||
},
|
||||
callback(nodeIds){
|
||||
if (!nodeIds || !nodeIds.length) return;
|
||||
let newPropertyId = insertPropertyFromLibraryNode.call({
|
||||
nodeIds,
|
||||
parentRef: {
|
||||
'id': slotId,
|
||||
'collection': 'creatureProperties',
|
||||
},
|
||||
});
|
||||
return `slot-child-${newPropertyId}`;
|
||||
}
|
||||
});
|
||||
},
|
||||
remove(model){
|
||||
softRemoveProperty.call({_id: model._id});
|
||||
snackbar({
|
||||
text: `Deleted ${getPropertyTitle(model)}`,
|
||||
callbackName: 'undo',
|
||||
callback(){
|
||||
restoreProperty.call({_id: model._id});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
slots(){
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'propertySlot',
|
||||
$or: [
|
||||
{'slotCondition.value': {$nin: [false, 0, '']}},
|
||||
{'slotCondition.value': {$exists: false}},
|
||||
],
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
}, {
|
||||
sort: {order: 1}
|
||||
}).map(slot => {
|
||||
if (
|
||||
!this.showHiddenSlots &&
|
||||
(slot.quantityExpected && slot.quantityExpected.value) === 0 &&
|
||||
slot.hideWhenFull
|
||||
){
|
||||
slot.children = []
|
||||
} else {
|
||||
slot.children = CreatureProperties.find({
|
||||
'parent.id': slot._id,
|
||||
removed: {$ne: true},
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
}).fetch();
|
||||
}
|
||||
return slot;
|
||||
}).filter(slot => !( // Hide full and ignored slots
|
||||
!this.showHiddenSlots && (
|
||||
slot.hideWhenFull &&
|
||||
(slot.quantityExpected && slot.quantityExpected.value) > 0 &&
|
||||
slot.spaceLeft <= 0 ||
|
||||
slot.ignored
|
||||
)
|
||||
));
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -18,7 +18,6 @@ const MoveLibraryNodeDialog = () => import('/imports/ui/library/MoveLibraryNodeD
|
||||
const SelectCreaturesDialog = () => import('/imports/ui/tabletop/SelectCreaturesDialog.vue');
|
||||
const SelectLibraryNodeDialog = () => import('/imports/ui/library/SelectLibraryNodeDialog.vue');
|
||||
const ShareDialog = () => import('/imports/ui/sharing/ShareDialog.vue');
|
||||
const SlotDetailsDialog = () => import('/imports/ui/creature/slots/SlotDetailsDialog.vue');
|
||||
const SlotFillDialog = () => import('/imports/ui/creature/slots/SlotFillDialog.vue');
|
||||
const TierTooLowDialog = () => import('/imports/ui/user/TierTooLowDialog.vue');
|
||||
const TransferOwnershipDialog = () => import('/imports/ui/sharing/TransferOwnershipDialog.vue');
|
||||
@@ -45,7 +44,6 @@ export default {
|
||||
SelectCreaturesDialog,
|
||||
SelectLibraryNodeDialog,
|
||||
ShareDialog,
|
||||
SlotDetailsDialog,
|
||||
SlotFillDialog,
|
||||
TierTooLowDialog,
|
||||
TransferOwnershipDialog,
|
||||
|
||||
Reference in New Issue
Block a user