Finished implementing new slot fill dialog, including ability to test slots
This commit is contained in:
@@ -1,310 +0,0 @@
|
|||||||
<template lang="html">
|
|
||||||
<dialog-base
|
|
||||||
:color="model.color"
|
|
||||||
dark-body
|
|
||||||
>
|
|
||||||
<template slot="toolbar">
|
|
||||||
<v-toolbar-title>
|
|
||||||
{{ model.name }}
|
|
||||||
</v-toolbar-title>
|
|
||||||
<v-spacer />
|
|
||||||
<text-field
|
|
||||||
prepend-inner-icon="mdi-magnify"
|
|
||||||
regular
|
|
||||||
hide-details
|
|
||||||
:value="searchValue"
|
|
||||||
:debounce="300"
|
|
||||||
@change="searchChanged"
|
|
||||||
@keyup.enter="insert"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<p
|
|
||||||
v-if="model.description"
|
|
||||||
class="description"
|
|
||||||
>
|
|
||||||
{{ model.description }}
|
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
class="library-nodes"
|
|
||||||
>
|
|
||||||
<v-fade-transition mode="out-in">
|
|
||||||
<div v-if="libraryNodes && libraryNodes.length">
|
|
||||||
<v-container fluid>
|
|
||||||
<v-row
|
|
||||||
dense
|
|
||||||
align="center"
|
|
||||||
align-md="start"
|
|
||||||
>
|
|
||||||
<v-col
|
|
||||||
v-for="node in libraryNodes"
|
|
||||||
:key="node._id"
|
|
||||||
cols="12"
|
|
||||||
sm="6"
|
|
||||||
md="4"
|
|
||||||
>
|
|
||||||
<v-card
|
|
||||||
hover
|
|
||||||
ripple
|
|
||||||
class="slot-card layout column justify-end"
|
|
||||||
:class="{'selected': node._id === (selectedNode && selectedNode._id)}"
|
|
||||||
:dark="node._id === (selectedNode && selectedNode._id)"
|
|
||||||
@click="selectedNode = node"
|
|
||||||
>
|
|
||||||
<v-img
|
|
||||||
v-if="node.picture"
|
|
||||||
:src="node.picture"
|
|
||||||
:height="200"
|
|
||||||
contain
|
|
||||||
class="slot-card-image"
|
|
||||||
/>
|
|
||||||
<v-card-title primary-title>
|
|
||||||
<tree-node-view
|
|
||||||
class="mr-2 text-h6 mb-0"
|
|
||||||
:class="{'theme--dark': node._id === (selectedNode && selectedNode._id)}"
|
|
||||||
:hide-icon="node.picture"
|
|
||||||
:model="node"
|
|
||||||
:color="node.color"
|
|
||||||
/>
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text
|
|
||||||
v-if="node.description"
|
|
||||||
class="pt-0"
|
|
||||||
>
|
|
||||||
<property-description
|
|
||||||
class="slot-card-text line-clamp"
|
|
||||||
:string="node.description"
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="countAll"
|
|
||||||
class="ma-4"
|
|
||||||
>
|
|
||||||
<h4 v-if="numFiltered">
|
|
||||||
Requirements of {{ numFiltered }} library properties were not met.
|
|
||||||
</h4>
|
|
||||||
<h4 v-else>
|
|
||||||
Nothing suitable was found in your libraries.
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="$subReady.slotFillers"
|
|
||||||
class="ma-4"
|
|
||||||
>
|
|
||||||
<h4>
|
|
||||||
Nothing suitable was found in your libraries
|
|
||||||
<span v-if="searchValue">
|
|
||||||
matching "{{ searchValue }}"
|
|
||||||
</span>
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
This slot requires a {{ slotPropertyTypeName }}
|
|
||||||
<template v-if="model.slotTags.length == 1">
|
|
||||||
with the tag <code>{{ model.slotTags[0] }}</code>,
|
|
||||||
</template>
|
|
||||||
<template v-else-if="model.slotTags.length > 1">
|
|
||||||
with the following tags:
|
|
||||||
<span
|
|
||||||
v-for="(tag, index) in model.slotTags"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<code>{{ tag }}</code>,
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<span v-if="model.spaceLeft">
|
|
||||||
that fills less than {{ model.spaceLeft }} {{ model.spaceLeft == 1 && 'slot' || 'slots' }}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</v-fade-transition>
|
|
||||||
<v-fade-transition mode="out-in">
|
|
||||||
<div
|
|
||||||
v-if="!$subReady.slotFillers"
|
|
||||||
key="character-loading"
|
|
||||||
class="fill-height layout justify-center align-center"
|
|
||||||
>
|
|
||||||
<v-progress-circular
|
|
||||||
indeterminate
|
|
||||||
color="primary"
|
|
||||||
size="64"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-fade-transition>
|
|
||||||
<v-fade-transition mode="out-in">
|
|
||||||
<div
|
|
||||||
v-if="currentLimit < countAll"
|
|
||||||
class="layout justify-center align-stretch"
|
|
||||||
>
|
|
||||||
<v-btn
|
|
||||||
:loading="!$subReady.slotFillers"
|
|
||||||
class="primary"
|
|
||||||
@click="loadMore"
|
|
||||||
>
|
|
||||||
Load More
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</v-fade-transition>
|
|
||||||
</div>
|
|
||||||
<template slot="actions">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
@click="$store.dispatch('popDialogStack')"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
:disabled="!selectedNode"
|
|
||||||
@click="insert"
|
|
||||||
>
|
|
||||||
Insert
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</dialog-base>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="js">
|
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
|
||||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
|
||||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
|
||||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
|
||||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
|
||||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
|
||||||
import PropertyDescription from '/imports/ui/properties/viewers/shared/PropertyDescription.vue'
|
|
||||||
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
DialogBase,
|
|
||||||
TreeNodeView,
|
|
||||||
PropertyDescription,
|
|
||||||
},
|
|
||||||
props:{
|
|
||||||
slotId: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
creatureId: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data(){return {
|
|
||||||
selectedNode: undefined,
|
|
||||||
searchValue: undefined,
|
|
||||||
numFiltered: 0,
|
|
||||||
}},
|
|
||||||
computed: {
|
|
||||||
slotPropertyTypeName(){
|
|
||||||
if (!this.model) return;
|
|
||||||
if (!this.model.slotType) return 'property';
|
|
||||||
let propName = getPropertyName(this.model.slotType);
|
|
||||||
return propName && propName.toLowerCase();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
reactiveProvide: {
|
|
||||||
name: 'context',
|
|
||||||
include: ['creatureId'],
|
|
||||||
},
|
|
||||||
methods:{
|
|
||||||
getTitle(model){
|
|
||||||
if (!model) return;
|
|
||||||
if (model.name) return model.name;
|
|
||||||
let prop = PROPERTIES[model.type]
|
|
||||||
return prop && prop.name;
|
|
||||||
},
|
|
||||||
searchChanged(val, ack){
|
|
||||||
this._subs['slotFillers'].setData('searchTerm', val);
|
|
||||||
this._subs['slotFillers'].setData('limit', undefined);
|
|
||||||
this.selectedNode = undefined;
|
|
||||||
this.searchValue = val;
|
|
||||||
setTimeout(ack, 200);
|
|
||||||
},
|
|
||||||
loadMore(){
|
|
||||||
if (this.currentLimit >= this.countAll) return;
|
|
||||||
this._subs['slotFillers'].setData('limit', this.currentLimit + 50);
|
|
||||||
},
|
|
||||||
insert(){
|
|
||||||
if (!this.selectedNode) return;
|
|
||||||
this.$store.dispatch('popDialogStack', this.selectedNode);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
meteor: {
|
|
||||||
$subscribe: {
|
|
||||||
'slotFillers'(){
|
|
||||||
return [this.slotId]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
model(){
|
|
||||||
return CreatureProperties.findOne(this.slotId);
|
|
||||||
},
|
|
||||||
creature(){
|
|
||||||
return Creatures.findOne(this.creatureId);
|
|
||||||
},
|
|
||||||
currentLimit(){
|
|
||||||
return this._subs['slotFillers'].data('limit') || 50;
|
|
||||||
},
|
|
||||||
countAll(){
|
|
||||||
return this._subs['slotFillers'].data('countAll');
|
|
||||||
},
|
|
||||||
libraryNodes(){
|
|
||||||
let filter = {
|
|
||||||
removed: {$ne: true},
|
|
||||||
};
|
|
||||||
if (this.model.slotTags && this.model.slotTags.length){
|
|
||||||
filter.tags = {$all: this.model.slotTags};
|
|
||||||
}
|
|
||||||
if (this.model.slotType){
|
|
||||||
filter.$or = [{
|
|
||||||
type: this.model.slotType
|
|
||||||
},{
|
|
||||||
type: 'slotFiller',
|
|
||||||
slotFillerType: this.model.slotType,
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
let nodes = LibraryNodes.find(filter, {
|
|
||||||
sort: {name: 1, order: 1}
|
|
||||||
}).fetch();
|
|
||||||
let totalNodes = nodes.length;
|
|
||||||
// Filter out slotFillers whose condition isn't met or are too big to fit
|
|
||||||
// the quantity to fill
|
|
||||||
nodes = nodes.filter(node => {
|
|
||||||
if (node.slotFillerCondition){
|
|
||||||
let {result} = evaluateString({
|
|
||||||
string: node.slotFillerCondition,
|
|
||||||
scope: this.creature.variables,
|
|
||||||
fn: 'reduce',
|
|
||||||
});
|
|
||||||
if (!result.value) return false;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
node.type === 'slotFiller' &&
|
|
||||||
this.model.spaceLeft > 0 &&
|
|
||||||
node.slotQuantityFilled > this.model.spaceLeft
|
|
||||||
){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
this.numFiltered = totalNodes - nodes.length;
|
|
||||||
if (nodes.length === 1) this.selectedNode = nodes[0];
|
|
||||||
return nodes;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.slot-card-text.line-clamp {
|
|
||||||
-webkit-line-clamp: 5;
|
|
||||||
}
|
|
||||||
.slot-card.selected {
|
|
||||||
background: #8E1B1B;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -60,8 +60,8 @@
|
|||||||
v-if="libraryNode._disabledByAlreadyAdded"
|
v-if="libraryNode._disabledByAlreadyAdded"
|
||||||
class="my-0 py-0"
|
class="my-0 py-0"
|
||||||
hide-details
|
hide-details
|
||||||
:value="true"
|
:input-value="true"
|
||||||
:disabled="true"
|
disabled
|
||||||
/>
|
/>
|
||||||
<v-checkbox
|
<v-checkbox
|
||||||
v-else
|
v-else
|
||||||
@@ -160,26 +160,24 @@
|
|||||||
<v-btn
|
<v-btn
|
||||||
text
|
text
|
||||||
color="primary"
|
color="primary"
|
||||||
:disabled="!selectedNodeIds.length"
|
:disabled="!dummySlot && !selectedNodeIds.length"
|
||||||
@click="$store.dispatch('popDialogStack', selectedNodeIds)"
|
@click="$store.dispatch('popDialogStack', selectedNodeIds)"
|
||||||
>
|
>
|
||||||
<template v-if="model.spaceLeft">
|
<template v-if="model.spaceLeft">
|
||||||
{{ totalQuantitySelected }} / {{ model.spaceLeft }}
|
{{ totalQuantitySelected }} / {{ model.spaceLeft }}
|
||||||
</template>
|
</template>
|
||||||
Insert
|
<template v-if="slotId">
|
||||||
|
Insert
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Close Test
|
||||||
|
</template>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</dialog-base>
|
</dialog-base>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* Enforce unique in slot/unique in character selection rules
|
|
||||||
* Fix the dialog callback for multiple property inserting
|
|
||||||
* Show the dialog in library view to test slots
|
|
||||||
* Delete the old slot fill dialog
|
|
||||||
*/
|
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||||
@@ -192,6 +190,7 @@ import Libraries from '/imports/api/library/Libraries.js';
|
|||||||
import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue';
|
import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue';
|
||||||
import PropertyTags from '/imports/ui/properties/viewers/shared/PropertyTags.vue';
|
import PropertyTags from '/imports/ui/properties/viewers/shared/PropertyTags.vue';
|
||||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||||
|
import { clone } from 'lodash';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -204,11 +203,15 @@ export default {
|
|||||||
props:{
|
props:{
|
||||||
slotId: {
|
slotId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: undefined,
|
||||||
},
|
},
|
||||||
creatureId: {
|
creatureId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: undefined,
|
||||||
|
},
|
||||||
|
dummySlot: {
|
||||||
|
type: Object,
|
||||||
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data(){return {
|
||||||
@@ -287,9 +290,17 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
model(){
|
model(){
|
||||||
return CreatureProperties.findOne(this.slotId);
|
if (this.slotId){
|
||||||
|
return CreatureProperties.findOne(this.slotId);
|
||||||
|
} else if (this.dummySlot) {
|
||||||
|
let model = clone(this.dummySlot)
|
||||||
|
model.quantityExpectedResult = +model.quantityExpected;
|
||||||
|
model.spaceLeft = model.quantityExpectedResult;
|
||||||
|
return model;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
creature(){
|
creature(){
|
||||||
|
if (!this.creatureId) return {variables: {}};
|
||||||
return Creatures.findOne(this.creatureId);
|
return Creatures.findOne(this.creatureId);
|
||||||
},
|
},
|
||||||
currentLimit(){
|
currentLimit(){
|
||||||
@@ -300,7 +311,7 @@ export default {
|
|||||||
},
|
},
|
||||||
alreadyAdded(){
|
alreadyAdded(){
|
||||||
let added = new Set();
|
let added = new Set();
|
||||||
if (this.model.unique) return added;
|
if (!this.model.unique) return added;
|
||||||
let ancestorId;
|
let ancestorId;
|
||||||
if (this.model.unique === 'uniqueInSlot'){
|
if (this.model.unique === 'uniqueInSlot'){
|
||||||
ancestorId = this.model._id;
|
ancestorId = this.model._id;
|
||||||
@@ -310,6 +321,7 @@ export default {
|
|||||||
CreatureProperties.find({
|
CreatureProperties.find({
|
||||||
'ancestors.id': ancestorId,
|
'ancestors.id': ancestorId,
|
||||||
libraryNodeId: {$exists: true},
|
libraryNodeId: {$exists: true},
|
||||||
|
removed: {$ne: true},
|
||||||
}, {
|
}, {
|
||||||
fields: {libraryNodeId: 1},
|
fields: {libraryNodeId: 1},
|
||||||
}).forEach(prop => {
|
}).forEach(prop => {
|
||||||
@@ -382,7 +394,13 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.disabledNodeCount = disabledNodeCount;
|
this.disabledNodeCount = disabledNodeCount;
|
||||||
if (activeNodeCount === 1) this.selectedNodeIds = [lastActiveNodeId];
|
if (
|
||||||
|
activeNodeCount === 1 &&
|
||||||
|
this.$subReady.slotFillers &&
|
||||||
|
this.currentLimit >= this.countAll
|
||||||
|
) {
|
||||||
|
this.selectedNodeIds = [lastActiveNodeId]
|
||||||
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ export default {
|
|||||||
slotId,
|
slotId,
|
||||||
creatureId,
|
creatureId,
|
||||||
},
|
},
|
||||||
callback(node){
|
callback(nodeIds){
|
||||||
if(!node) return;
|
if (!nodeIds || !nodeIds.length) return;
|
||||||
let newPropertyId = insertPropertyFromLibraryNode.call({
|
let newPropertyId = insertPropertyFromLibraryNode.call({
|
||||||
nodeIds: [node._id],
|
nodeIds,
|
||||||
parentRef: {
|
parentRef: {
|
||||||
'id': slotId,
|
'id': slotId,
|
||||||
'collection': 'creatureProperties',
|
'collection': 'creatureProperties',
|
||||||
|
|||||||
@@ -92,6 +92,29 @@
|
|||||||
/>
|
/>
|
||||||
<calculation-error-list :errors="model.slotConditionErrors" />
|
<calculation-error-list :errors="model.slotConditionErrors" />
|
||||||
|
|
||||||
|
<smart-select
|
||||||
|
label="Unique"
|
||||||
|
style="flex-basis: 300px;"
|
||||||
|
clearable
|
||||||
|
hint="Do the properties that fill this slot need to be unique?"
|
||||||
|
:items="uniqueOptions"
|
||||||
|
:value="model.unique"
|
||||||
|
:error-messages="errors.unique"
|
||||||
|
@change="change('unique', ...arguments)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-layout justify-center>
|
||||||
|
<v-btn
|
||||||
|
v-if="context.isLibraryForm"
|
||||||
|
color="accent"
|
||||||
|
class="ma-2 mb-4"
|
||||||
|
data-id="test-slot-button"
|
||||||
|
@click="testSlot"
|
||||||
|
>
|
||||||
|
Test Slot
|
||||||
|
</v-btn>
|
||||||
|
</v-layout>
|
||||||
|
|
||||||
<text-area
|
<text-area
|
||||||
label="Description"
|
label="Description"
|
||||||
:value="model.description"
|
:value="model.description"
|
||||||
@@ -122,16 +145,6 @@
|
|||||||
@change="change('ignored', ...arguments)"
|
@change="change('ignored', ...arguments)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<smart-select
|
|
||||||
label="Unique"
|
|
||||||
style="flex-basis: 300px;"
|
|
||||||
clearable
|
|
||||||
hint="Do the properties that fill this slot need to be unique?"
|
|
||||||
:items="uniqueOptions"
|
|
||||||
:value="model.unique"
|
|
||||||
:error-messages="errors.unique"
|
|
||||||
@change="change('unique', ...arguments)"
|
|
||||||
/>
|
|
||||||
<smart-combobox
|
<smart-combobox
|
||||||
label="Tags"
|
label="Tags"
|
||||||
hint="This slot's own tags which will be used to fill other slots"
|
hint="This slot's own tags which will be used to fill other slots"
|
||||||
@@ -158,6 +171,9 @@
|
|||||||
CalculationErrorList,
|
CalculationErrorList,
|
||||||
},
|
},
|
||||||
mixins: [propertyFormMixin],
|
mixins: [propertyFormMixin],
|
||||||
|
inject: {
|
||||||
|
context: { default: {} }
|
||||||
|
},
|
||||||
data(){
|
data(){
|
||||||
let slotTypes = [];
|
let slotTypes = [];
|
||||||
for (let key in PROPERTIES){
|
for (let key in PROPERTIES){
|
||||||
@@ -199,6 +215,16 @@
|
|||||||
ack: this.acknowledgeAddResult,
|
ack: this.acknowledgeAddResult,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
testSlot(){
|
||||||
|
if (!this.context.isLibraryForm) return;
|
||||||
|
this.$store.commit('pushDialogStack', {
|
||||||
|
component: 'slot-fill-dialog',
|
||||||
|
elementId: 'test-slot-button',
|
||||||
|
data: {
|
||||||
|
dummySlot: this.model,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user