Overhaul of character action components, actions now consume resources

This commit is contained in:
Stefan Zermatten
2020-06-15 22:30:27 +02:00
parent dc18734d1f
commit 3f540d0f14
16 changed files with 502 additions and 179 deletions

View File

@@ -343,7 +343,45 @@ const adjustQuantity = new ValidatedMethod({
let currentProperty = CreatureProperties.findOne(_id); let currentProperty = CreatureProperties.findOne(_id);
// Check permissions // Check permissions
assertPropertyEditPermission(currentProperty, this.userId); assertPropertyEditPermission(currentProperty, this.userId);
adjustQuantityWork({property: currentProperty, operation, value}) adjustQuantityWork({property: currentProperty, operation, value});
recomputeCreatures(currentProperty);
},
});
const selectAmmoItem = new ValidatedMethod({
name: 'creatureProperties.selectAmmoItem',
validate: new SimpleSchema({
actionId: SimpleSchema.RegEx.Id,
itemId: SimpleSchema.RegEx.Id,
itemConsumedIndex: Number,
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({actionId, itemId, itemConsumedIndex}) {
let action = CreatureProperties.findOne(actionId);
// Check permissions
assertPropertyEditPermission(action, this.userId);
// Check that this index has a document to edit
let itemConsumed = action.resources.itemsConsumed[itemConsumedIndex];
if (!itemConsumed){
throw new Meteor.Error('Resouce not found',
'Could not set ammo, because the ammo document was not found');
}
let itemToLink = CreatureProperties.findOne(itemId);
if (!itemToLink){
throw new Meteor.Error('Item not found',
'Could not set ammo: the item was not found');
}
let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`;
CreatureProperties.update(actionId, {
$set: {[path]: itemId}
}, {
selector: action,
});
recomputeCreatures(action);
}, },
}); });
@@ -416,6 +454,7 @@ export {
updateProperty, updateProperty,
damageProperty, damageProperty,
adjustQuantity, adjustQuantity,
selectAmmoItem,
pushToProperty, pushToProperty,
pullFromProperty, pullFromProperty,
softRemoveProperty, softRemoveProperty,

View File

@@ -15,6 +15,8 @@ export default class ComputationMemo {
this.classes = {}; this.classes = {};
this.togglesById = {}; this.togglesById = {};
this.toggleIds = new Set(); this.toggleIds = new Set();
// Equipped items that might be used as ammo
this.equipmentById = {};
// Properties that have calculations, but don't impact other properties // Properties that have calculations, but don't impact other properties
this.endStepPropsById = {}; this.endStepPropsById = {};
// First note all the ids of all the toggles // First note all the ids of all the toggles
@@ -40,6 +42,10 @@ export default class ComputationMemo {
) { ) {
// Add all the stats // Add all the stats
this.addStat(prop); this.addStat(prop);
} else if (
prop.type === 'item'
) {
this.addEquipment(prop);
} else { } else {
return true; return true;
} }
@@ -185,6 +191,10 @@ export default class ComputationMemo {
}); });
return targets; return targets;
} }
addEquipment(prop){
prop = this.registerProperty(prop);
this.equipmentById[prop._id] = prop;
}
addEndStepProp(prop){ addEndStepProp(prop){
prop = this.registerProperty(prop); prop = this.registerProperty(prop);
this.endStepPropsById[prop._id] = prop; this.endStepPropsById[prop._id] = prop;

View File

@@ -37,6 +37,7 @@ function computeAction(prop, memo){
if (attConsumed.variableName){ if (attConsumed.variableName){
let stat = memo.statsByVariableName[attConsumed.variableName]; let stat = memo.statsByVariableName[attConsumed.variableName];
prop.resources.attributesConsumed[i].statId = stat && stat._id; prop.resources.attributesConsumed[i].statId = stat && stat._id;
prop.resources.attributesConsumed[i].statName = stat && stat.name;
let available = stat && stat.currentValue || 0; let available = stat && stat.currentValue || 0;
prop.resources.attributesConsumed[i].available = available; prop.resources.attributesConsumed[i].available = available;
if (available < attConsumed.quantity){ if (available < attConsumed.quantity){
@@ -45,7 +46,22 @@ function computeAction(prop, memo){
} }
}); });
// Items consumed // Items consumed
// TODO prop.resources.itemsConsumed.forEach((itemConsumed, i) => {
let item = itemConsumed.itemId && memo.equipmentById[itemConsumed.itemId];
prop.resources.itemsConsumed[i].itemId = item && item._id;
let available = item && item.quantity || 0;
prop.resources.itemsConsumed[i].available = available;
let name = item && item.name;
if (item && item.quantity !== 1 && item.plural){
name = item.plural;
}
prop.resources.itemsConsumed[i].itemName = name;
prop.resources.itemsConsumed[i].itemIcon = item && item.icon;
prop.resources.itemsConsumed[i].itemColor = item && item.color;
if (!item || available < itemConsumed.quantity){
prop.insufficientResources = true;
}
});
} }
function computeAttack(prop, memo){ function computeAttack(prop, memo){

View File

@@ -42,6 +42,7 @@ const calculationPropertyTypes = [
'proficiency', 'proficiency',
'classLevel', 'classLevel',
'toggle', 'toggle',
'item',
// End step types // End step types
'action', 'action',
'attack', 'attack',

View File

@@ -1,5 +1,6 @@
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
import { storedIconsSchema } from '/imports/api/icons/Icons.js'
/* /*
* Actions are things a character can do * Actions are things a character can do
@@ -133,6 +134,19 @@ const ComputedOnlyActionSchema = new SimpleSchema({
// This appears both in the computed and uncomputed schema because it can be // This appears both in the computed and uncomputed schema because it can be
// set by both a computation or a form // set by both a computation or a form
'resources.itemsConsumed.$.itemId': { 'resources.itemsConsumed.$.itemId': {
type: String,
regEx: SimpleSchema.RegEx.Id,
optional: true,
},
'resources.itemsConsumed.$.itemName': {
type: String,
optional: true,
},
'resources.itemsConsumed.$.itemIcon': {
type: storedIconsSchema,
optional: true,
},
'resources.itemsConsumed.$.itemColor': {
type: String, type: String,
optional: true, optional: true,
}, },
@@ -147,6 +161,12 @@ const ComputedOnlyActionSchema = new SimpleSchema({
regEx: SimpleSchema.RegEx.Id, regEx: SimpleSchema.RegEx.Id,
optional: true, optional: true,
}, },
'resources.attributesConsumed.$.statName': {
type: String,
optional: true,
},
// True if the uses left is zero, or any item or attribute consumed is
// insufficient
insufficientResources: { insufficientResources: {
type: Boolean, type: Boolean,
optional: true, optional: true,

View File

@@ -34,8 +34,7 @@
drag_handle drag_handle
</v-icon> </v-icon>
<!--{{node && node.order}}--> <!--{{node && node.order}}-->
<component <tree-node-view
:is="treeNodeView"
:model="node" :model="node"
:selected="selected" :selected="selected"
/> />
@@ -80,13 +79,13 @@
**/ **/
import { canBeParent } from '/imports/api/parenting/parenting.js'; import { canBeParent } from '/imports/api/parenting/parenting.js';
import { getPropertyIcon } from '/imports/constants/PROPERTIES.js'; import { getPropertyIcon } from '/imports/constants/PROPERTIES.js';
import treeNodeViewIndex from '/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js'; import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
export default { export default {
name: 'TreeNode', name: 'TreeNode',
components: { components: {
...treeNodeViewIndex TreeNodeView,
}, },
props: { props: {
node: Object, node: Object,
group: String, group: String,
@@ -100,10 +99,6 @@
expanded: false, expanded: false,
}}, }},
computed: { computed: {
treeNodeView(){
let type = this.node.type;
return treeNodeViewIndex[type] || treeNodeViewIndex.default;
},
hasChildren(){ hasChildren(){
return this.children && this.children.length || this.lazy && !this.expanded; return this.children && this.children.length || this.lazy && !this.expanded;
}, },

View File

@@ -1,7 +1,6 @@
<template lang="html"> <template lang="html">
<v-btn <v-btn
:loading="loading" :loading="loading"
:disabled="loading"
outline outline
style="width: 160px;" style="width: 160px;"
@click="rest" @click="rest"

View File

@@ -258,25 +258,16 @@
/> />
</div> </div>
<div <div
v-if="attacks.length" v-for="attack in attacks"
class="actions" :key="attack._id"
class="attacks"
> >
<v-card> <action-card
<v-list attack
two-line :model="attack"
subheader :data-id="attack._id"
> @click="clickProperty({_id: attack._id})"
<v-subheader>Attacks</v-subheader> />
<action-list-tile
v-for="attack in attacks"
:key="attack._id"
attack
:model="attack"
:data-id="attack._id"
@click="clickProperty({_id: attack._id})"
/>
</v-list>
</v-card>
</div> </div>
</column-layout> </column-layout>
</div> </div>
@@ -294,7 +285,6 @@
import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.vue'; import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.vue';
import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue'; import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue';
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue'; import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
import ActionListTile from '/imports/ui/properties/components/actions/ActionListTile.vue';
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue'; import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
import RestButton from '/imports/ui/creature/RestButton.vue'; import RestButton from '/imports/ui/creature/RestButton.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
@@ -337,7 +327,6 @@
SkillListTile, SkillListTile,
ResourceCard, ResourceCard,
SpellSlotListTile, SpellSlotListTile,
ActionListTile,
ActionCard, ActionCard,
}, },
props: { props: {
@@ -390,14 +379,7 @@
return getSkillOfType(this.creature, 'language'); return getSkillOfType(this.creature, 'language');
}, },
actions(){ actions(){
let props = getProperties(this.creature, {type: 'action'}).map(action => { return getProperties(this.creature, {type: 'action'});
action.children = getActiveProperties({
ancestorId: action._id,
options: {sort: {order: 1}},
});
return action;
});
return props;
}, },
attacks(){ attacks(){
let props = getProperties(this.creature, {type: 'attack'}).map(attack => { let props = getProperties(this.creature, {type: 'attack'}).map(attack => {

View File

@@ -160,7 +160,6 @@ export default {
}); });
}, },
push({path, value, ack}){ push({path, value, ack}){
console.log({path, value, ack});
pushToProperty.call({_id: this._id, path, value}, (error) =>{ pushToProperty.call({_id: this._id, path, value}, (error) =>{
if (error) console.warn(error); if (error) console.warn(error);
ack && ack(error && error.reason || error); ack && ack(error && error.reason || error);

View File

@@ -1,58 +1,106 @@
<template lang="html"> <template lang="html">
<v-card <v-card
class="action" ref="card"
@click="$emit('click')" class="action-card"
:class="cardClasses"
:elevation="hovering ? 8 : undefined"
> >
<v-card-title <div class="layout row align-center px-3">
primary-title
class="layout row pa-2"
>
<div class="avatar"> <div class="avatar">
<v-btn <v-btn
flat flat
icon icon
class="headline" outline
style="margin-left: -4px; font-size: 18px;"
color="primary"
:loading="doActionLoading"
:disabled="model.insufficientResources" :disabled="model.insufficientResources"
@click.stop="doAction" @click.stop="doAction"
> >
<template v-if="rollBonus"> <template v-if="attack && !rollBonusTooLong">
{{ rollBonus }} {{ rollBonus }}
</template> </template>
<v-icon v-else> <v-icon v-else>
$vuetify.icons.action {{ actionTypeIcon }}
</v-icon> </v-icon>
</v-btn> </v-btn>
</div> </div>
<div class="action-header flex"> <div
<div class="action-title"> class="action-header flex layout column justify-center pl-1"
style="height: 72px; cursor: pointer;"
@mouseover="hovering = true"
@mouseleave="hovering = false"
@click="$emit('click')"
>
<div
class="action-title my-1"
>
{{ model.name }} {{ model.name }}
</div> </div>
<div class="action-type"> <div class="action-sub-title layout row align-center">
action type text <div class="flex">
{{ model.actionType }}
</div>
<div v-if="model.uses">
{{ usesLeft }} uses
</div>
</div> </div>
</div> </div>
<div class="action-uses"> </div>
{{ usesLeft }}/{{ totalUses }} <div class="px-3 pb-3">
</div> <attribute-consumed-view
</v-card-title> v-for="attributeConsumed in model.resources.attributesConsumed"
<v-card-text :key="attributeConsumed._id"
v-if="childText" class="action-child"
class="action-details" :model="attributeConsumed"
v-html="childText" />
/> <item-consumed-view
v-for="itemConsumed in model.resources.itemsConsumed"
:key="itemConsumed._id"
class="action-child"
:model="itemConsumed"
:action="model"
/>
<v-divider
v-if="
model.resources.attributesConsumed.length ||
model.resources.itemsConsumed.length
"
class="my-2"
/>
<tree-node-view
v-for="child in children"
:key="child._id"
class="action-child"
:model="child"
/>
</div>
</v-card> </v-card>
</template> </template>
<script> <script>
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
import doAction from '/imports/api/creature/actions/doAction.js'; import doAction from '/imports/api/creature/actions/doAction.js';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
import AttributeConsumedView from '/imports/ui/properties/components/actions/AttributeConsumedView.vue';
import ItemConsumedView from '/imports/ui/properties/components/actions/ItemConsumedView.vue';
export default { export default {
components: {
TreeNodeView,
AttributeConsumedView,
ItemConsumedView,
},
inject: { inject: {
context: { context: {
default: {}, default: {},
}, },
theme: {
default: {
isDark: false,
},
},
}, },
props: { props: {
model: { model: {
@@ -63,48 +111,118 @@ export default {
type: Boolean, type: Boolean,
}, },
}, },
data(){return {
activated: undefined,
doActionLoading: false,
hovering: false,
}},
computed: { computed: {
hasClickListener(){ hasClickListener(){
return this.$listeners && this.$listeners.click return this.$listeners && this.$listeners.click
}, },
rollBonus(){ rollBonus(){
if (!this.attack) return;
return numberToSignedString(this.model.rollBonusResult); return numberToSignedString(this.model.rollBonusResult);
}, },
childText(){ rollBonusTooLong(){
let scope = this.context.creature && this.context.creature.variables; return this.rollBonus && this.rollBonus.length > 3;
if (!this.model.children || !this.model.children.length) return;
let textArray = [];
this.model.children.forEach(child => {
if (child.type === 'damage'){
let { result } = evaluateString(child.amount, scope);
textArray.push(`${result} ${child.damageType}`);
} else if (child.type === 'savingThrow'){
textArray.push(`DC ${child.dcResult} ${child.name}`);
}
});
return textArray.join(' ');
}, },
totalUses(){ totalUses(){
return Math.max(this.model.usesResult, 0); return Math.max(this.model.usesResult, 0);
}, },
usesLeft(){ usesLeft(){
return Math.max(this.model.usesResult - this.model.usesUsed, 0); return Math.max(this.model.usesResult - this.model.usesUsed, 0);
},
cardClasses() {
return {
'theme--dark': this.theme.isDark,
'theme--light': !this.theme.isDark,
'muted-text': this.model.insufficientResources,
'shrink': this.activated,
}
},
actionTypeIcon() {
return `$vuetify.icons.${this.model.actionType}`;
}, },
}, },
meteor: {
children(){
return getActiveProperties({
ancestorId: this.model._id,
filter: {'parent.id': this.model._id},
options: {sort: {order: 1}},
});
},
},
methods: { methods: {
click(e){ click(e){
this.$emit('click', e); this.$emit('click', e);
}, },
doAction(){ doAction(){
this.doActionLoading = true;
this.shwing();
doAction.call({actionId: this.model._id}, error => { doAction.call({actionId: this.model._id}, error => {
this.doActionLoading = false;
if (error){ if (error){
console.error(error); console.error(error);
} }
}); });
}, },
shwing(){
this.activated = true;
setTimeout(() => {
this.activated = undefined;
}, 300);
}
} }
} }
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.action-title {
font-size: 16px;
font-weight: 400;
height: 24px;
line-height: 24px;
position: relative;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: .3s cubic-bezier(.25,.8,.5,1);
width: 100%;
}
.action-sub-title {
color: #9e9e9e;
flex-grow: 0;
font-size: 12px;
line-height: 12px;
height: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: .3s cubic-bezier(.25,.8,.5,1);
width: 100%;
}
.action-child {
height: 32px;
}
.theme--light.muted-text {
color: rgba(0,0,0,.3) !important;
}
.theme--dark.muted-text {
color: hsla(0,0%,100%,.3) !important;
}
.action-card {
transition: transform 0.15s cubic;
}
</style>
<style lang="css">
.action-card.theme--light.muted-text .v-icon {
color: rgba(0,0,0,.3) !important;
}
.action-card.theme--dark.muted-text .v-icon {
color: hsla(0,0%,100%,.3) !important;
}
</style> </style>

View File

@@ -1,100 +0,0 @@
<template lang="html">
<v-list-tile
class="ability-list-tile"
v-on="hasClickListener ? {click} : {}"
>
<v-list-tile-action
v-if="attack"
>
<v-btn
flat
icon
class="headline"
:disabled="model.insufficientResources"
@click.stop="doAction"
>
{{ rollBonus }}
</v-btn>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>
{{ model.name }}
</v-list-tile-title>
<v-list-tile-sub-title
v-if="childText"
v-html="childText"
/>
</v-list-tile-content>
<v-list-tile-action v-if="model.usesResult">
<v-list-tile-action-text>
{{ usesLeft }}/{{ totalUses }}
</v-list-tile-action-text>
</v-list-tile-action>
</v-list-tile>
</template>
<script>
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
import doAction from '/imports/api/creature/actions/doAction.js';
export default {
inject: {
context: {
default: {},
},
},
props: {
model: {
type: Object,
required: true,
},
attack: {
type: Boolean,
},
},
computed: {
hasClickListener(){
return this.$listeners && this.$listeners.click
},
rollBonus(){
return numberToSignedString(this.model.rollBonusResult);
},
childText(){
let scope = this.context.creature && this.context.creature.variables;
if (!this.model.children || !this.model.children.length) return;
let textArray = [];
this.model.children.forEach(child => {
if (child.type === 'damage'){
let { result } = evaluateString(child.amount, scope);
textArray.push(`${result} ${child.damageType}`);
} else if (child.type === 'savingThrow'){
textArray.push(`DC ${child.dcResult} ${child.name}`);
}
});
return textArray.join(' ');
},
totalUses(){
return Math.max(this.model.usesResult, 0);
},
usesLeft(){
return Math.max(this.model.usesResult - this.model.usesUsed, 0);
},
},
methods: {
click(e){
this.$emit('click', e);
},
doAction(){
doAction.call({actionId: this.model._id}, error => {
if (error){
console.error(error);
}
});
},
}
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,34 @@
<template lang="html">
<div
class="layout row align-center justify-start"
:class="insufficient && 'error--text'"
>
<div
class="mr-2"
style="width: 24px; text-align: center;"
>
{{ model.quantity }}
</div>
<div
class="text-no-wrap text-truncate"
>
{{ model.statName }}
</div>
</div>
</template>
<script>
export default {
props: {
model: {
type: Object,
default: () => ({}),
},
},
computed: {
insufficient(){
return this.model.quantity > this.model.available;
},
},
}
</script>

View File

@@ -0,0 +1,108 @@
<template lang="html">
<div
:class="{
'theme--dark': theme.isDark,
'theme--light': !theme.isDark,
}"
>
<v-menu
transition="slide-y-transition"
lazy
>
<template #activator="{ on }">
<div
class="layout row align-center justify-start"
style="height: 100%;"
:class="{
'error--text': insufficient,
'clickable': context.editPermission,
}"
v-on="on"
>
<div
class="mr-2"
style="width: 24px; text-align: center;"
>
<template v-if="model.quantity === 1">
{{ model.available }}
</template>
<template v-else-if="model.quantity !== 0">
{{ model.available }} / {{ model.quantity }}
</template>
</div>
<svg-icon
v-if="model.itemIcon"
:shape="model.itemIcon.shape"
:color="model.itemColor"
/>
<div
class="text-no-wrap text-truncate flex"
>
<template v-if="model.itemId">
{{ model.itemName }}
</template>
<span
v-else
class="error--text"
>
Select ammo
</span>
</div>
<v-icon v-if="context.editPermission">
arrow_drop_down
</v-icon>
</div>
</template>
<select-item-to-consume
:action="action"
:item-consumed="model"
/>
</v-menu>
</div>
</template>
<script>
import SelectItemToConsume from '/imports/ui/properties/components/actions/SelectItemToConsume.vue';
export default {
components: {
SelectItemToConsume,
},
inject: {
context: {
default: {},
},
theme: {
default: {
isDark: false,
},
},
},
props: {
model: {
type: Object,
default: () => ({}),
},
action: {
type: Object,
required: true,
},
},
computed: {
insufficient(){
return this.model.quantity > this.model.available;
},
},
}
</script>
<style lang="css" scoped>
.clickable {
cursor: pointer;
}
.theme--light .clickable:hover {
background: rgba(0,0,0,.04);
}
.theme--dark .clickable:hover {
background: hsla(0,0%,100%,.08);
}
</style>

View File

@@ -0,0 +1,73 @@
<template lang="html">
<v-list v-if="items.length">
<v-list-tile
v-for="item in items"
:key="item._id"
@click="selectItem(item._id)"
>
<item-tree-node
:model="item"
:selected="itemConsumed.itemId === item._id"
/>
</v-list-tile>
</v-list>
<v-card v-else>
<v-card-text>
No equipped items found with the tag "{{ itemConsumed.tag }}"
</v-card-text>
</v-card>
</template>
<script>
import ItemTreeNode from '/imports/ui/properties/treeNodeViews/ItemTreeNode.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
import { selectAmmoItem } from '/imports/api/creature/CreatureProperties.js';
import { findIndex } from 'lodash';
export default {
components: {
ItemTreeNode
},
props: {
action: {
type: Object,
required: true,
},
itemConsumed: {
type: Object,
required: true,
},
},
meteor: {
items(){
return getActiveProperties({
ancestorId: this.action.ancestors[0].id,
filter: {
tags: this.itemConsumed.tag,
equipped: true,
},
options: {
fields: {equipped: false},
}
});
}
},
methods:{
selectItem(itemId){
let itemConsumedIndex = findIndex(
this.action.resources.itemsConsumed,
item => item._id === this.itemConsumed._id
);
selectAmmoItem.call({
actionId: this.action._id,
itemId,
itemConsumedIndex
}, error => {
if (error) console.error(error);
});
}
}
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -83,7 +83,6 @@
this.addResourceLoading = false; this.addResourceLoading = false;
}, },
addAttributesConsumed(){ addAttributesConsumed(){
console.log(AttributeConsumedSchema.clean({}));
this.addResourceLoading = true; this.addResourceLoading = true;
this.$emit('push', { this.$emit('push', {
path: ['attributesConsumed'], path: ['attributesConsumed'],

View File

@@ -0,0 +1,30 @@
<template lang="html">
<component
:is="treeNodeView"
:model="model"
:selected="selected"
/>
</template>
<script>
import treeNodeViewIndex from '/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js';
export default {
components: {
...treeNodeViewIndex
},
props: {
model: {
type: Object,
required: true,
},
selected: Boolean,
},
computed: {
treeNodeView(){
let type = this.model.type;
return treeNodeViewIndex[type] || treeNodeViewIndex.default;
},
}
}
</script>