First implementation on Slots UI

This commit is contained in:
Stefan Zermatten
2020-10-14 14:45:26 +02:00
parent d2cb86ac27
commit 51569592ab
12 changed files with 289 additions and 20 deletions

View File

@@ -33,11 +33,6 @@ let SlotSchema = new SimpleSchema({
type: String,
optional: true,
},
// How many properties have been selected to fill this slot
quantityFilled: {
type: SimpleSchema.Integer,
defaultValue: 0,
},
});
const ComputedOnlySlotSchema = new SimpleSchema({

View File

@@ -6,3 +6,4 @@ import '/imports/server/publications/experiences.js';
import '/imports/server/publications/users.js';
import '/imports/server/publications/icons.js';
import '/imports/server/publications/tabletops.js';
import '/imports/server/publications/slotFillers.js'

View File

@@ -0,0 +1,47 @@
import Libraries from '/imports/api/library/Libraries.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
Meteor.publish('slotFillers', function(slotId){
this.autorun(function (){
if (!this.userId) {
return this.ready();
}
// Get the slot
console.log({slotId});
let slot = CreatureProperties.findOne(slotId);
if (!slot){
return this.ready()
}
// Get all the ids of libraries the user can access
const user = Meteor.users.findOne(this.userId, {
fields: {subscribedLibraries: 1}
});
const subs = user && user.subscribedLibraries || [];
let libraryIds = Libraries.find({
$or: [
{owner: this.userId},
{writers: this.userId},
{readers: this.userId},
{_id: {$in: subs}},
]
}, {
fields: {_id: 1},
}).map(lib => lib._id);
console.log(libraryIds);
// Build a filter for nodes in those libraries that match the slot
let filter = {
'ancestors.id': {$in: libraryIds},
'tags': {$all: slot.slotTags},
};
if (slot.slotType){
filter.type = slot.slotType;
}
console.log(filter);
console.log(LibraryNodes.find(filter).fetch());
return LibraryNodes.find(filter);
});
});

View File

@@ -83,7 +83,7 @@
import FeaturesTab from '/imports/ui/creature/character/characterSheetTabs/FeaturesTab.vue';
import InventoryTab from '/imports/ui/creature/character/characterSheetTabs/InventoryTab.vue';
import SpellsTab from '/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue';
import PersonaTab from '/imports/ui/creature/character/characterSheetTabs/PersonaTab.vue';
import CharacterTab from '/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue';
import TreeTab from '/imports/ui/creature/character/characterSheetTabs/TreeTab.vue';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
@@ -94,7 +94,7 @@
FeaturesTab,
InventoryTab,
SpellsTab,
PersonaTab,
CharacterTab,
TreeTab,
},
props: {

View File

@@ -105,7 +105,7 @@
Spells
</v-tab>
<v-tab>
Persona
Character
</v-tab>
<v-tab>
Tree

View File

@@ -22,7 +22,7 @@
Spells
</v-tab>
<v-tab>
Persona
Character
</v-tab>
<v-tab>
Tree

View File

@@ -20,6 +20,13 @@
</v-card-text>
</v-card>
</div>
<div>
<v-card>
<v-card-text>
<slots :creature-id="creatureId" />
</v-card-text>
</v-card>
</div>
<div>
<v-card class="class-details">
<v-card-title
@@ -101,12 +108,14 @@ import Creatures from '/imports/api/creature/Creatures.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
import NoteCard from '/imports/ui/properties/components/persona/NoteCard.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
import Slots from '/imports/ui/creature/slots/Slots.vue';
export default {
components: {
ColumnLayout,
NoteCard,
Slots,
},
props: {
creatureId: {

View File

@@ -0,0 +1,92 @@
<template lang="html">
<dialog-base :color="model.color">
<template slot="toolbar">
<v-toolbar-title>
{{ model.name }}
</v-toolbar-title>
</template>
<div class="library-nodes">
<v-list>
<v-list-tile
v-for="node in libraryNodes"
:key="node._id"
:class="node._id === (selectedNode && selectedNode._id) && 'primary--text'"
@click="selectedNode = node"
>
<tree-node-view
:model="node"
:selected="node._id === (selectedNode && selectedNode._id)"
/>
</v-list-tile>
</v-list>
<h4
v-if="!libraryNodes.length"
class="ma-4"
>
Nothing suitable was found in your libraries
</h4>
</div>
<template slot="actions">
<v-spacer />
<v-btn
flat
@click="$store.dispatch('popDialogStack')"
>
Cancel
</v-btn>
<v-btn
flat
:disabled="!selectedNode"
@click="$store.dispatch('popDialogStack', selectedNode)"
>
Insert
</v-btn>
</template>
</dialog-base>
</template>
<script>
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
export default {
components: {
DialogBase,
TreeNodeView,
},
props:{
slotId: {
type: String,
required: true,
}
},
data(){return {
selectedNode: undefined,
}},
meteor: {
$subscribe: {
'slotFillers'(){
return [this.slotId]
},
},
model(){
return CreatureProperties.findOne(this.slotId);
},
libraryNodes(){
let filter = {
tags: {$all: this.model.slotTags},
};
if (this.model.slotType){
filter.type = this.model.slotType;
}
console.log(filter);
return LibraryNodes.find(filter);
},
}
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,115 @@
<template lang="html">
<div class="slots">
<div
v-for="slot in slots"
:key="slot._id"
class="slot"
>
<h3 class="layout row align-center">
{{ slot.name }}
<v-spacer />
<span v-if="slot.quantityExpected > 1">
{{ slot.children.length }} / {{ slot.quantityExpected }}
</span>
</h3>
<div
v-for="child in slot.children"
:key="child._id"
class="layout row"
:data-id="`slot-child-${child._id}`"
>
<tree-node-view
class="slotChild"
:model="child"
/>
<v-spacer />
<v-btn
icon
flat
small
@click="remove(child._id)"
>
<v-icon>delete</v-icon>
</v-btn>
</div>
<v-btn
v-if="slot.quantityExpected > slot.children.length"
icon
:data-id="`slot-add-button-${slot._id}`"
@click="fillSlot(slot._id)"
>
<v-icon>add</v-icon>
</v-btn>
</div>
</div>
</template>
<script>
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
import { softRemoveProperty, insertPropertyFromLibraryNode } from '/imports/api/creature/CreatureProperties.js';
export default {
components: {
TreeNodeView,
},
inject: {
context: { default: {} }
},
props: {
creatureId: {
type: String,
required: true,
},
},
methods: {
fillSlot(slotId){
this.$store.commit('pushDialogStack', {
component: 'slot-fill-dialog',
elementId: `slot-add-button-${slotId}`,
data: {slotId},
callback(node){
if(!node) return;
let newPropertyId = insertPropertyFromLibraryNode.call({
nodeId: node._id,
parentRef: {
'id': slotId,
'collection': 'creatureProperties',
},
});
return `slot-child-${newPropertyId}`;
}
});
},
remove(_id){
softRemoveProperty.call({_id});
}
},
meteor: {
slots(){
return CreatureProperties.find({
'ancestors.id': this.creatureId,
type: 'propertySlot',
$or: [
{slotConditionResult: true},
{slotConditionResult: {$exists: false}},
]
}, {
sort: {
order: 1,
name: 1,
},
}).map(slot => {
slot.children = CreatureProperties.find({
'parent.id': slot._id,
removed: {$ne: true},
}).fetch();
return slot;
});
},
},
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -13,6 +13,7 @@ import LibraryNodeDialog from '/imports/ui/library/LibraryNodeDialog.vue';
import MoveLibraryNodeDialog from '/imports/ui/library/MoveLibraryNodeDialog.vue'
import SelectCreaturesDialog from '/imports/ui/tabletop/SelectCreaturesDialog.vue';
import ShareDialog from '/imports/ui/sharing/ShareDialog.vue';
import SlotFillDialog from '/imports/ui/creature/slots/SlotFillDialog.vue';
import TierTooLowDialog from '/imports/ui/user/TierTooLowDialog.vue';
import UsernameDialog from '/imports/ui/user/UsernameDialog.vue';
@@ -32,6 +33,7 @@ export default {
MoveLibraryNodeDialog,
SelectCreaturesDialog,
ShareDialog,
SlotFillDialog,
TierTooLowDialog,
UsernameDialog,
};

View File

@@ -9,14 +9,31 @@
:error-messages="errors.name"
@change="change('name', ...arguments)"
/>
<form-section
name="Advanced"
standalone
>
<smart-combobox
label="Tags"
multiple
chips
deletable-chips
:value="model.tags"
@change="change('tags', ...arguments)"
/>
</form-section>
</div>
</div>
</template>
<script>
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
export default {
components: {
FormSection,
},
mixins: [propertyFormMixin],
};
</script>

View File

@@ -53,15 +53,6 @@
name="Advanced"
standalone
>
<text-field
label="Quantity filled"
type="number"
min="0"
hint="How many properties have already been selected to fill this slot"
:value="model.quantityFilled"
:error-messages="errors.quantityFilled"
@change="change('quantityFilled', ...arguments)"
/>
<div class="layout row wrap justify-space-between">
<smart-switch
label="Ignored"