Added effect editing component, abstracted out operation icons

This commit is contained in:
Stefan Zermatten
2019-02-04 14:30:04 +02:00
parent e3e7b76f02
commit c6e7f8eeb6
6 changed files with 232 additions and 15 deletions

View File

@@ -42,6 +42,7 @@
import AttributeCard from '/imports/ui/components/AttributeCard.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/components/EffectEdit.Story.vue';
import HealthBar from '/imports/ui/components/HealthBar.Story.vue';
import HitDiceListTile from '/imports/ui/components/HitDiceListTile.Story.vue';
import IconSearch from '/imports/ui/components/IconSearch.Story.vue';
@@ -54,6 +55,7 @@
AttributeCard,
ColumnLayout,
DialogStack,
EffectEdit,
HealthBar,
HitDiceListTile,
IconSearch,

View File

@@ -9,7 +9,7 @@
>
<v-layout row align-center class="net-effect">
<v-icon class="black--text icon">
{{getIcon(effect.operation, effect.value)}}
{{getEffectIcon(effect.operation, effect.value)}}
</v-icon>
<div class="value display-1 pr-2" v-if="showValue(effect.operation)">
{{getValue(effect.operation, effect.value)}}
@@ -32,6 +32,7 @@
<script>
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import getEffectIcon from '/imports/ui/utility/getEffectIcon.js';
const SORT_INDEX = {
"base": 1,
"add": 2,
@@ -66,20 +67,7 @@
click(event){
this.$emit('click', event);
},
getIcon(op, value){
switch(op) {
case 'base': return 'forward';
case 'add': return value < 0 ? 'remove' : 'add';
case 'mul': return 'clear';
case 'min': return 'unfold_less';
case 'max': return 'unfold_more';
case 'advantage': return 'arrow_upward';
case 'disadvantage': return 'arrow_downward';
case 'passiveAdd': return value < 0 ? 'remove_circle_outline' : 'add_circle_outline';
case 'fail': return 'block';
case 'conditional': return '*' ;
}
},
getEffectIcon,
getOperation(op, value){
switch(op) {
case 'base': return 'Base value';

View File

@@ -0,0 +1,51 @@
<template lang="html">
<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)"/>
</template>
</v-card-text>
</template>
<script>
import EffectEdit from '/imports/ui/components/EffectEdit.vue';
export default {
data(){ return {
effects: [
{
operation: 'add',
calculation: '2',
stat: '',
},
{
operation: 'mul',
calculation: '2',
stat: 'strength',
},
{
operation: 'base',
calculation: 'strength + 4',
result: 6,
stat: 'dexterity',
},
],
stats: [
{name: "Strength", variableName: "strength"},
],
}},
components: {
EffectEdit,
},
methods: {
change(index, e){
for (let i in e){
this.effects[index][i] = e[i];
console.log({e, effect: this.effects[index]})
}
},
},
};
</script>
<style lang="css" scoped>
</style>

View File

@@ -0,0 +1,154 @@
<template lang="html">
<v-layout row wrap class="effect-edit py-4 px-2">
<!-- Operation -->
<v-flex class="ma-1">
<v-select
label="Operation"
color="accent"
append-icon="arrow_drop_down"
:items="operations"
:value="this.effect.operation"
@change="operation => $emit('change', {operation})"
>
<v-icon
class="black--text icon"
slot="prepend"
:class="iconClass"
>
{{displayedIcon}}
</v-icon>
<template slot="item" slot-scope="item">
<v-icon class="black--text icon mr-2">
{{getEffectIcon(item.item.value, 1)}}
</v-icon>
{{item.item.text}}
</template>
</v-select>
</v-flex>
<!-- Value -->
<v-flex class="ma-1">
<v-text-field
label="Value"
:persistent-hint="needsValue"
:value="needsValue ? (effect.calculation) : ' '"
:disabled="!needsValue"
:hint="!isFinite(effect.calculation) && effect.result ? effect.result + '' : '' "
@change="calculation => $emit('change', {calculation})"
/>
</v-flex>
<!-- Stat -->
<v-flex class="ma-1">
<v-autocomplete
label="Stat"
color="accent"
append-icon="arrow_drop_down"
item-text="name"
item-value="variableName"
:value="effect.stat"
:items="stats"
@change="stat => $emit('change', {stat})"
/>
</v-flex>
</v-layout>
</template>
<script>
import getEffectIcon from '/imports/ui/utility/getEffectIcon.js';
const ICON_SPIN_DURATION = 300;
export default {
props: {
effect: {
type: Object,
default: {},
},
stats: {
type: Array,
},
},
data(){ return {
displayedIcon: 'add',
iconClass: '',
operations: [
{value: 'base', text: 'Base Value'},
{value: 'add', text: 'Add'},
{value: 'mul', text: 'Multiply'},
{value: 'min', text: 'Minimum'},
{value: 'max', text: 'Maximum'},
{value: 'advantage', text: 'Advantage'},
{value: 'disadvantage', text: 'Disadvantage'},
{value: 'passiveAdd', text: 'Passive Bonus'},
{value: 'fail', text: 'Fail'},
{value: 'conditional', text: 'Conditional Benefit'},
],
}},
computed: {
needsValue(){
switch(this.effect.operation) {
case 'base': return true;
case 'add': return true;
case 'mul': return true;
case 'min': return true;
case 'max': return true;
case 'advantage': return false;
case 'disadvantage': return false;
case 'passiveAdd': return true;
case 'fail': return false;
case 'conditional': return true;
}
},
},
methods: {
getEffectIcon,
},
watch: {
'effect.operation': {
immediate: true,
handler(newValue, oldValue, e){
let newIcon = getEffectIcon(newValue, 1);
if (!oldValue){
// Skip animation
this.displayedIcon = newIcon;
} else {
this.iconClass="leaving";
setTimeout(() => {
this.displayedIcon = newIcon;
this.iconClass="arriving";
requestAnimationFrame(() => {
this.iconClass="";
});
}, ICON_SPIN_DURATION / 2);
}
},
},
}
};
</script>
<style lang="css" scoped>
.icon {
min-width: 30px;
transition: transform 0.15s linear, opacity 0.15s ease;
transform-origin: 18px center;
margin-left: -12px;
}
.icon.leaving {
transform: translateY(-24px);
opacity: 0;
}
.icon.arriving {
transform: translateY(24px);
opacity: 0;
transition: none;
}
.hidden {
visibility: hidden;
}
.flex {
width: 220px;
}
</style>

View File

@@ -68,6 +68,10 @@
return `left:${left}px; top:${top}px;`;
},
enter(target, done){
if (!target.attributes['data-element-id']){
done();
return;
}
let elementId = target.attributes['data-element-id'].value;
let source = document.getElementById(elementId);
// Get the original styles so we can repair them later
@@ -104,6 +108,10 @@
if (target.attributes['data-return-element-id']) {
elementId = target.attributes['data-return-element-id'].value;
} else {
if (!target.attributes['data-element-id']){
done();
return;
}
elementId = target.attributes['data-element-id'].value;
}
let source = document.getElementById(elementId);

View File

@@ -0,0 +1,14 @@
export default function getEffectIcon(op, value){
switch(op) {
case 'base': return 'forward';
case 'add': return value < 0 ? 'remove' : 'add';
case 'mul': return 'clear';
case 'min': return 'unfold_more';
case 'max': return 'unfold_less';
case 'advantage': return 'arrow_upward';
case 'disadvantage': return 'arrow_downward';
case 'passiveAdd': return value < 0 ? 'remove_circle_outline' : 'add_circle_outline';
case 'fail': return 'block';
case 'conditional': return '*' ;
}
};