Completed Action form, effects form, reworked form event api

This commit is contained in:
Stefan Zermatten
2019-07-22 12:43:57 +02:00
parent baa1c0967c
commit dfa302a4a9
17 changed files with 512 additions and 142 deletions

View File

@@ -14,6 +14,13 @@ import updateSchemaMixin from '/imports/api/creature/mixins/updateSchemaMixin.js
let Buffs = new Mongo.Collection('buffs');
let BuffSchema = new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
autoValue(){
if (!this.isSet) return Random.id();
}
},
name: {
type: String,
optional: true,
@@ -23,9 +30,8 @@ let BuffSchema = new SimpleSchema({
optional: true,
},
duration: {
type: SimpleSchema.Integer,
type: String,
optional: true,
min: 0,
},
});

View File

@@ -18,6 +18,13 @@ let Effects = new Mongo.Collection('effects');
* that modify their final value or presentation in some way
*/
let EffectSchema = schema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
autoValue(){
if (!this.isSet) return Random.id();
}
},
name: {
type: String,
optional: true,

View File

@@ -7,8 +7,7 @@ const AdjustmentSchema = new SimpleSchema({
type: String,
regEx: SimpleSchema.RegEx.Id,
autoValue(){
if (this.isSet) return;
return Random.id();
if (!this.isSet) return Random.id();
}
},
// The roll that determines how much to damage the attribute

View File

@@ -45,7 +45,7 @@
import ColorPicker from '/imports/ui/components/ColorPicker.Story.vue';
import ColumnLayout from "/imports/ui/components/ColumnLayout.Story.vue";
import DialogStack from '/imports/ui/dialogStack/DialogStack.Story.vue';
import EffectEdit from '/imports/ui/creature/properties/effects/EffectEdit.Story.vue';
import EffectForm from '/imports/ui/creature/properties/effects/EffectForm.Story.vue';
import EffectEditExpansionList from '/imports/ui/creature/properties/effects/EffectEditExpansionList.Story.vue';
import FeatureCard from '/imports/ui/creature/properties/features/FeatureCard.Story.vue';
import HealthBar from '/imports/ui/creature/properties/attributes/HealthBar.Story.vue';
@@ -66,7 +66,7 @@
ColorPicker,
ColumnLayout,
DialogStack,
EffectEdit,
EffectForm,
EffectEditExpansionList,
FeatureCard,
HealthBar,

View File

@@ -2,6 +2,18 @@
* Forms that take in a schema and a model of the current data, manages smart
* inputs, and sends update events when valid data model changes must occur
*/
import { get, toPath } from 'lodash';
function resolvePath(model, path){
let arrayPath = toPath(path);
if (arrayPath.length === 1){
return { object: model, key: arrayPath[0] };
}
let objectPath = arrayPath.slice(0, -1);
let key = arrayPath.slice(-1);
let object = get(model, objectPath);
return {object, key};
};
const schemaFormMixin = {
data(){ return {
valid: true,
@@ -26,26 +38,19 @@ const schemaFormMixin = {
},
},
methods: {
change(modifier, ack){
for (let key in modifier){
this.$set(this.model, key, modifier[key])
}
// Sets the value at the given path
change({path, value, ack}){
let {object, key} = resolvePath(this.model, path);
this.$set(object, key, value);
if (ack) ack();
},
push(modifier, ack){
for (let key in modifier){
this.model[key].push(modifier[key]);
}
push({path, value, ack}){
get(this.model, path).push(value);
if (ack) ack();
},
changeAtIndex(field, index, modifier, ack){
for (let key in modifier){
this.$set(this.model[field][index], key, modifier[key])
}
if (ack) ack();
},
removeAtIndex(field, index, ack){
this.model[field].splice(index, 1);
pull({path, ack}){
let {object, key} = resolvePath(this.model, path);
object.splice(key, 1);
if (ack) ack();
},
},

View File

@@ -8,6 +8,7 @@
@input="input"
@focus="focused = true"
@blur="focused = false"
box
/>
</template>

View File

@@ -3,7 +3,7 @@
<text-field
label="Name"
:value="model.name"
@change="(name, ack) => $emit('change', {name}, ack)"
@change="(value, ack) => $emit('change', {path: ['name'], value, ack})"
:error-messages="errors.name"
:debounce-time="debounceTime"
/>
@@ -13,7 +13,7 @@
:value="model.type"
:error-messages="errors.type"
:menu-props="{auto: true, lazy: true}"
@change="(type, ack) => $emit('change', {type}, ack)"
@change="(value, ack) => $emit('change', {path: ['type'], value, ack})"
:hint="actionTypeHints[model.type]"
:debounce-time="debounceTime"
/>
@@ -26,7 +26,7 @@
:value="model.target"
:error-messages="errors.target"
:menu-props="{auto: true, lazy: true}"
@change="(target, ack) => $emit('change', {target}, ack)"
@change="(value, ack) => $emit('change', {path: ['target'], value, ack})"
:debounce-time="debounceTime"
/>
<div class="layout row wrap">
@@ -35,7 +35,7 @@
hint="How many times this action can be used before needing to be reset"
style="flex-basis: 300px;"
:value="model.uses"
@change="(uses, ack) => $emit('change', {uses}, ack)"
@change="(value, ack) => $emit('change', {path: ['uses'], value, ack})"
:error-messages="errors.uses"
:debounce-time="debounceTime"
/>
@@ -45,7 +45,7 @@
hint="How many times this action has already been used"
style="flex-basis: 300px;"
:value="model.usesUsed"
@change="(uses, ack) => $emit('change', {uses}, ack)"
@change="(value, ack) => $emit('change', {path: ['usesUsed'], value, ack})"
:error-messages="errors.uses"
:debounce-time="debounceTime"
/>
@@ -58,11 +58,11 @@
:value="model.reset"
:error-messages="errors.reset"
:menu-props="{auto: true, lazy: true}"
@change="(reset, ack) => $emit('change', {reset}, ack)"
@change="(value, ack) => $emit('change', {path: ['reset'], value, ack})"
:debounce-time="debounceTime"
/>
</form-section>
<form-section name="Adjustments">
<form-section name="Damage and Adjustments">
<div class="caption">
Adjustments can be used to automatically spend resources or deal
damage when taking an action.
@@ -71,9 +71,22 @@
<adjustment-list-form
:model="model.adjustments"
:parent-target="model.target"
@push="(adjustments, ack) => $emit('push', {adjustments}, ack)"
@changeAtIndex="(index, modifier, ack) => $emit('changeAtIndex', 'adjustments', index, modifier, ack)"
@removeAtIndex="(index, ack) => $emit('removeAtIndex', 'adjustments', index, ack)"
@change="({path, value, ack}) => $emit('change', {path: ['adjustments', ...path], value, ack})"
@push="({path, value, ack}) => $emit('push', {path: ['adjustments', ...path], value, ack})"
@pull="({path, ack}) => $emit('pull', {path: ['adjustments', ...path], ack})"
/>
</form-section>
<form-section name="Buffs">
<div class="caption">
Buffs apply temporary effects to characters when taking an action.
</div>
<buff-list-form
:model="model.buffs"
:parent-target="model.target"
:stored="stored"
@change="({path, value, ack}) => $emit('change', {path: ['buffs', ...path], value, ack})"
@push="({path, value, ack}) => $emit('push', {path: ['buffs', ...path], value, ack})"
@pull="({path, ack}) => $emit('pull', {path: ['buffs', ...path], ack})"
/>
</form-section>
</form-sections>
@@ -83,14 +96,19 @@
<script>
import FormSection, {FormSections} from '/imports/ui/components/forms/FormSection.vue';
import AdjustmentListForm from '/imports/ui/creature/properties/adjustments/AdjustmentListForm.vue';
import BuffListForm from '/imports/ui/creature/properties/buffs/BuffListForm.vue';
export default {
components: {
FormSection,
FormSections,
AdjustmentListForm,
BuffListForm,
},
props: {
stored: {
type: Boolean,
},
model: {
type: Object,
default: () => ({}),

View File

@@ -1,43 +1,36 @@
<template lang="html">
<div class="layout row align-center">
<div style="flex-grow: 1;">
<div class="layout row wrap">
<text-field
label="Attribute"
hint="The attribute this adjustment will apply to"
style="flex-basis: 300px;"
:value="model.stat"
@change="(stat, ack) => $emit('change', {stat}, ack)"
:error-messages="errors.stat"
:debounce-time="debounceTime"
/>
<text-field
label="Damage"
hint="The amount of damage to apply to the selected stat, can be a calculation or roll"
style="flex-basis: 300px;"
:value="model.damage"
@change="(damage, ack) => $emit('change', {damage}, ack)"
:error-messages="errors.damage"
:debounce-time="debounceTime"
/>
</div>
<smart-select
v-if="parentTarget !== 'self'"
label="Target"
:hint="targetOptionHint"
:items="targetOptions"
:value="model.target"
:error-messages="errors.target"
:menu-props="{auto: true, lazy: true}"
@change="(target, ack) => $emit('change', {target}, ack)"
<div>
<div class="layout row">
<text-field
label="Attribute"
hint="The attribute this adjustment will apply to"
style="flex-basis: 300px;"
:value="model.stat"
@change="(value, ack) => $emit('change', {path: ['stat'], value, ack})"
:error-messages="errors.stat"
:debounce-time="debounceTime"
/>
<text-field
label="Damage"
hint="The amount of damage to apply to the selected stat, can be a calculation or roll"
style="flex-basis: 300px;"
:value="model.damage"
@change="(value, ack) => $emit('change', {path: ['damage'], value, ack})"
:error-messages="errors.damage"
:debounce-time="debounceTime"
/>
</div>
<div>
<v-btn outline icon large class="ma-3" @click="$emit('remove')">
<v-icon>delete</v-icon>
</v-btn>
</div>
<smart-select
v-if="parentTarget !== 'self'"
label="Target"
:hint="targetOptionHint"
:items="targetOptions"
:value="model.target"
:error-messages="errors.target"
:menu-props="{auto: true, lazy: true}"
@change="(value, ack) => $emit('change', {path: ['target'], value, ack})"
:debounce-time="debounceTime"
/>
</div>
</template>

View File

@@ -10,9 +10,13 @@
class="mt-4"
:model="adjustment"
:parent-target="parentTarget"
@change="(modifier, ack) => $emit('changeAtIndex', i, modifier, ack)"
@remove="(ack) => $emit('removeAtIndex', i, ack)"
@change="({path, value, ack}) => $emit('change', {path: [i, ...path], value, ack})"
/>
<div>
<v-btn outline icon large class="ma-3" @click="$emit('pull', {path: [i]})">
<v-icon>delete</v-icon>
</v-btn>
</div>
</div>
</v-slide-x-transition>
<div class="layout row justify-end">
@@ -46,7 +50,11 @@
},
addAdjustment(){
this.addAdjustmentLoading = true;
this.$emit('push', AdjustmentSchema.clean({}), this.acknowledgeAddAdjustment);
this.$emit('push', {
path: [],
value: AdjustmentSchema.clean({}),
ack: this.acknowledgeAddAdjustment,
});
},
},
props: {

View File

@@ -6,7 +6,7 @@
type="number"
class="base-value-field text-xs-center large-format no-flex"
:value="model.baseValue"
@change="(baseValue, ack) => $emit('change', {baseValue}, ack)"
@change="(value, ack) => $emit('change', {path: ['baseValue'], value, ack})"
hint="This is the value of the attribute before effects are applied"
:error-messages="errors.baseValue"
:debounce-time="debounceTime"
@@ -16,7 +16,7 @@
<text-field
label="Name"
:value="model.name"
@change="(name, ack) => $emit('change', {name}, ack)"
@change="(value, ack) => $emit('change', {path: ['name'], value, ack})"
:error-messages="errors.name"
:debounce-time="debounceTime"
/>
@@ -24,7 +24,7 @@
label="Variable name"
:value="model.variableName"
style="flex-basis: 300px;"
@change="(variableName, ack) => $emit('change', {variableName}, ack)"
@change="(value, ack) => $emit('change', {path: ['variableName'], value, ack})"
hint="Use this name in formulae to reference this attribute"
:error-messages="errors.variableName"
:debounce-time="debounceTime"
@@ -36,7 +36,7 @@
:value="model.type"
:error-messages="errors.type"
:menu-props="{auto: true, lazy: true}"
@change="(type, ack) => $emit('change', {type}, ack)"
@change="(value, ack) => $emit('change', {path: ['type'], value, ack})"
:hint="attributeTypeHints[model.type]"
:debounce-time="debounceTime"
/>
@@ -47,7 +47,7 @@
class="no-flex"
:value="model.decimal"
:error-messages="errors.decimal"
@change="e => $emit('change', {decimal: !!e})"
@change="e => $emit('change', $emit('change', {path: ['decimal'], value: !!e, ack}))"
/>
<div class="layout row justify-center" style="align-self: stretch;">
<text-field
@@ -57,7 +57,7 @@
style="max-width: 300px;"
hint="The attribute's final value is reduced by this amount"
:value="model.damage"
@change="(damage, ack) => $emit('change', {damage}, ack)"
@change="(value, ack) => $emit('change', {path: ['damage'], value, ack})"
:error-messages="errors.adjustment"
:debounce-time="debounceTime"
/>
@@ -72,7 +72,7 @@
:value="model.reset"
:error-messages="errors.reset"
:menu-props="{auto: true, lazy: true}"
@change="(reset, ack) => $emit('change', {reset}, ack)"
@change="(value, ack) => $emit('change', {path: ['reset'], value, ack})"
:debounce-time="debounceTime"
/>
<text-field
@@ -81,7 +81,7 @@
style="flex-basis: 400px;"
:value="model.resetMultiplier"
:error-messages="errors.resetMultiplier"
@change="(resetMultiplier, ack) => $emit('change', {resetMultiplier}, ack)"
@change="(value, ack) => $emit('change', {path: ['resetMultiplier'], value, ack})"
hint="Some attributes, like hit dice, only reset by half their total on a long rest"
:debounce-time="debounceTime"
/>

View File

@@ -0,0 +1,113 @@
<template lang="html">
<div class="buff-form">
<text-field
label="Name"
:value="model.name"
@change="(value, ack) => $emit('change', {path: ['name'], value, ack})"
:error-messages="errors.name"
:debounce-time="debounceTime"
/>
<text-area
label="Description"
:value="model.description"
@change="(value, ack) => $emit('change', {path: ['description'], value, ack})"
:error-messages="errors.description"
:debounce-time="debounceTime"
/>
<text-field
label="Duration"
hint="How long the buff lasts"
:value="model.duration"
@change="(value, ack) => $emit('change', {path: ['duration'], value, ack})"
:error-messages="errors.duration"
:debounce-time="debounceTime"
/>
<div v-if="stored">
<smart-select
v-if="parentTarget !== 'self'"
label="Target"
:hint="targetOptionHint"
:items="targetOptions"
:value="model.target"
:error-messages="errors.target"
:menu-props="{auto: true, lazy: true}"
@change="(value, ack) => $emit('change', {path: ['target'], value, ack})"
:debounce-time="debounceTime"
/>
<effect-list-form
:model="model.effects"
@change="({path, value, ack}) => $emit('change', {path: ['effects', ...path], value, ack})"
@push="({path, value, ack}) => $emit('push', {path: ['effects', ...path], value, ack})"
@pull="({path, ack}) => $emit('pull', {path: ['effects', ...path], ack})"
/>
</div>
</div>
</template>
<script>
import EffectListForm from '/imports/ui/creature/properties/effects/EffectListForm.vue';
export default {
props: {
stored: Boolean,
model: {
type: Object,
default: () => ({}),
},
errors: {
type: Object,
default: () => ({}),
},
parentTarget: {
type: String,
},
debounceTime: Number,
},
components: {
EffectListForm,
},
computed: {
targetOptions(){
if (this.parentTarget === 'singleTarget') {
return [
{
text: 'Self',
value: 'self',
}, {
text: 'Target',
value: 'every',
},
];
} else {
return [
{
text: 'Self',
value: 'self',
}, {
text: 'Roll once for each target',
value: 'each',
}, {
text: 'Roll once and apply to every target',
value: 'every',
},
];
}
},
targetOptionHint(){
let hints = {
self: 'The buff will be applied to the character taking the action',
target: 'The buff will be applied to the target of the action',
each: 'The buff will be rolled separately for each of the targets of the action',
every: 'The buff will be rolled once and applied to each of the targets of the action',
};
if (this.parentTarget === 'singleTarget'){
hints.each = hints.target;
hints.every = hints.target;
}
return hints[this.model.target];
}
}
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,77 @@
<template lang="html">
<div>
<v-slide-x-transition group>
<div
v-for="(buff, i) in model"
:key="buff._id || i"
>
<v-divider v-if="i !== 0"/>
<div class="layout row align-center">
<div style="flex-grow: 1;">
<buff-form
class="mt-4"
:model="buff"
:parent-target="parentTarget"
:stored="stored"
@change="({path, value, ack}) => $emit('change', {path: [i, ...path], value, ack})"
@push="({path, value, ack}) => $emit('push', {path: [i, ...path], value, ack})"
@pull="({path, ack}) => $emit('pull', {path: [i, ...path], ack})"
/>
</div>
<v-btn outline icon large class="ma-3" @click="$emit('pull', {path: [i]})">
<v-icon>delete</v-icon>
</v-btn>
</div>
</div>
</v-slide-x-transition>
<div class="layout row justify-end">
<v-btn
:loading="addBuffLoading"
:disabled="addBuffLoading"
outline
@click="addBuff"
>
<v-icon>add</v-icon>
Add Buff
</v-btn>
</div>
</div>
</template>
<script>
import BuffForm from '/imports/ui/creature/properties/buffs/BuffForm.vue';
import {StoredBuffSchema, AppliedBuffSchema} from '/imports/api/creature/properties/Buffs.js';
export default {
components: {
BuffForm,
},
props: {
stored: Boolean,
model: {
type: Array,
default: () => ([]),
},
parentTarget: {
type: String,
},
debounceTime: Number,
},
data(){return {
addBuffLoading: false,
}},
methods: {
acknowledgeAddBuff(){
this.addBuffLoading = false;
},
addBuff(){
this.addBuffLoading = true;
let schema = this.stored ? StoredBuffSchema : AppliedBuffSchema;
this.$emit('push', {path: [], value: schema.clean({}), ack: this.acknowledgeAddBuff});
},
},
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -14,7 +14,7 @@
:class="{'primary--text': expanded === index}"
v-bind="effect"
/>
<effect-edit
<effect-form
:effect="effect"
:stats="stats"
@change="({set, ack}) => $emit('change', {set, ack, effectId: effect._id, index})"
@@ -24,7 +24,7 @@
</template>
<script>
import EffectEdit from '/imports/ui/creature/properties/effects/EffectEdit.vue';
import EffectForm from '/imports/ui/creature/properties/effects/EffectForm.vue';
import EffectListTile from '/imports/ui/creature/properties/effects/EffectListTile.vue';
export default {
props: {
@@ -32,7 +32,7 @@
stats: Array,
},
components: {
EffectEdit,
EffectForm,
EffectListTile,
},
data(){ return {

View File

@@ -2,13 +2,13 @@
<v-card-text>
<template v-for="(effect, index) in effects">
<v-divider v-if="index != 0"/>
<effect-edit :key="index" :effect="effect" :stats="stats" @change="e => change(index, e)"/>
<effect-form :key="index" :effect="effect" :stats="stats" @change="e => change(index, e)"/>
</template>
</v-card-text>
</template>
<script>
import EffectEdit from '/imports/ui/creature/properties/effects/EffectEdit.vue';
import EffectForm from '/imports/ui/creature/properties/effects/EffectForm.vue';
export default {
data(){ return {
effects: [
@@ -34,7 +34,7 @@ export default {
],
}},
components: {
EffectEdit,
EffectForm,
},
methods: {
change(index, e){

View File

@@ -1,57 +1,49 @@
<template lang="html">
<v-layout row wrap class="effect-edit py-4 px-2">
<!-- Operation -->
<v-flex class="ma-1">
<smart-select
label="Operation"
append-icon="arrow_drop_down"
:menu-props="{transition: 'slide-y-transition', lazy: true}"
:items="operations"
:value="this.effect.operation"
@change="(operation, ack) => $emit('change', {set: {operation}, ack})"
>
<div class="layout row wrap justify-start effect-form">
<smart-select
label="Operation"
append-icon="arrow_drop_down"
class="mx-2"
:menu-props="{transition: 'slide-y-transition', lazy: true}"
:items="operations"
:value="model.operation"
@change="(value, ack) => $emit('change', {path: ['operation'], value, ack})"
>
<v-icon
class="icon"
slot="prepend"
:class="iconClass"
>{{displayedIcon}}</v-icon>
<template slot="item" slot-scope="item">
<v-icon
class="icon"
slot="prepend"
:class="iconClass"
>{{displayedIcon}}</v-icon>
<template slot="item" slot-scope="item">
<v-icon
class="icon mr-2"
>{{getEffectIcon(item.item.value, 1)}}</v-icon>
{{item.item.text}}
</template>
</smart-select>
</v-flex>
class="icon mr-2"
>{{getEffectIcon(item.item.value, 1)}}</v-icon>
{{item.item.text}}
</template>
</smart-select>
<!-- Value -->
<v-flex class="ma-1">
<text-field
label="Value"
:persistent-hint="needsValue"
:value="needsValue ? (effect.calculation) : ' '"
:disabled="!needsValue"
:hint="!isFinite(effect.calculation) && effect.result ? effect.result + '' : '' "
@change="(calculation, ack) => $emit('change', {set: {calculation}, ack})"
/>
</v-flex>
<text-field
label="Value"
class="mr-2"
:persistent-hint="needsValue"
:value="needsValue ? (model.calculation) : ' '"
:disabled="!needsValue"
:hint="!isFinite(model.calculation) && model.result ? model.result + '' : '' "
@change="(value, ack) => $emit('change', {path: ['calculation'], value, ack})"
/>
<!-- Stat -->
<v-flex class="ma-1">
<v-autocomplete
label="Stat"
append-icon="arrow_drop_down"
item-text="name"
item-value="variableName"
:menu-props="{transition: 'slide-y-transition', lazy: true}"
:value="effect.stat"
:items="stats"
@input="stat => $emit('change', {set: {stat}, ack: () => {} })"
/>
</v-flex>
</v-layout>
<text-field
label="Stat"
class="mr-2"
append-icon="arrow_drop_down"
item-text="name"
item-value="variableName"
:menu-props="{transition: 'slide-y-transition', lazy: true}"
:value="model.stat"
:items="stats"
@change="(value, ack) => $emit('change', {path: ['stat'], value, ack})"
/>
</div>
</template>
<script>
@@ -60,7 +52,7 @@
const ICON_SPIN_DURATION = 300;
export default {
props: {
effect: {
model: {
type: Object,
default: () => ({}),
},
@@ -86,7 +78,7 @@
}},
computed: {
needsValue(){
switch(this.effect.operation) {
switch(this.model.operation) {
case 'base': return true;
case 'add': return true;
case 'mul': return true;
@@ -104,7 +96,7 @@
getEffectIcon,
},
watch: {
'effect.operation': {
'model.operation': {
immediate: true,
handler(newValue, oldValue, e){
let newIcon = getEffectIcon(newValue, 1);
@@ -149,7 +141,7 @@
.hidden {
visibility: hidden;
}
.flex {
width: 220px;
.effect-form > div {
flex-basis: 220px;
}
</style>

View File

@@ -0,0 +1,151 @@
<template lang="html">
<div>
<v-slide-x-transition group>
<div
v-for="(effect, i) in model"
:key="effect._id || i"
>
<v-divider v-if="i !== 0"/>
<div class="layout row align-center">
<div style="flex-grow: 1;">
<effect-form
class="mt-4"
:model="effect"
:parent-target="parentTarget"
:stored="stored"
@change="({path, value, ack}) => $emit('change', {path: [i, ...path], value, ack})"
@pull="(ack) => $emit('pull', {path: [i], ack})"
/>
</div>
<v-btn outline icon large class="ma-3" @click="$emit('pull', {path: [i]})">
<v-icon>delete</v-icon>
</v-btn>
</div>
</div>
</v-slide-x-transition>
<div class="layout row justify-end">
<v-btn
:loading="addEffectLoading"
:disabled="addEffectLoading"
outline
@click="addEffect"
>
<v-icon>add</v-icon>
Add Effect
</v-btn>
</div>
</div>
</template>
<script>
import EffectForm from '/imports/ui/creature/properties/effects/EffectForm.vue';
import { EffectSchema } from '/imports/api/creature/properties/Effects.js';
export default {
components: {
EffectForm,
},
props: {
stored: Boolean,
model: {
type: Array,
default: () => ([]),
},
parentTarget: {
type: String,
},
debounceTime: Number,
},
data(){return {
addEffectLoading: false,
}},
methods: {
acknowledgeAddEffect(){
this.addEffectLoading = false;
},
addEffect(){
this.addEffectLoading = true;
this.$emit('push', {
path: [],
value: EffectSchema.clean({}),
ack: this.acknowledgeAddEffect,
});
},
},
}
</script>
<style lang="css" scoped>
</style>
<template lang="html">
<div>
<v-slide-x-transition group>
<div
v-for="(effect, i) in model"
:key="effect._id || i"
>
<v-divider v-if="i !== 0"/>
<div class="layout row align-center">
<div style="flex-grow: 1;">
<effect-form
class="mt-4"
:model="effect"
@change="({path, value, ack}) => $emit('change', {path: [i, ...path], value, ack})"
/>
</div>
<v-btn outline icon large class="ma-3" @click="$emit('pull', {path: [i]})">
<v-icon>delete</v-icon>
</v-btn>
</div>
</div>
</v-slide-x-transition>
<div class="layout row justify-end">
<v-btn
:loading="addEffectLoading"
:disabled="addEffectLoading"
outline
@click="addEffect"
>
<v-icon>add</v-icon>
Add Effect
</v-btn>
</div>
</div>
</template>
<script>
import EffectForm from '/imports/ui/creature/properties/effects/EffectForm.vue';
import { EffectSchema } from '/imports/api/creature/properties/Effects.js';
export default {
components: {
EffectForm,
},
props: {
model: {
type: Array,
default: () => ([]),
},
debounceTime: Number,
},
data(){return {
addEffectLoading: false,
}},
methods: {
acknowledgeAddEffect(){
this.addEffectLoading = false;
},
addEffect(){
this.addEffectLoading = true;
this.$emit('push', {
path: [],
value: EffectSchema.clean({}),
ack: this.acknowledgeAddEffect,
});
},
},
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -3,14 +3,14 @@
<div slot="toolbar">Add {{propertyName}}</div>
<component
v-if="type"
stored
:is="type"
class="library-node-form"
:model="model"
:errors="errors"
@change="change"
@push="push"
@changeAtIndex="changeAtIndex"
@removeAtIndex="removeAtIndex"
@pull="pull"
/>
<div
slot="actions"