Added effect editing component, abstracted out operation icons
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
51
app/imports/ui/components/EffectEdit.Story.vue
Normal file
51
app/imports/ui/components/EffectEdit.Story.vue
Normal 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>
|
||||
154
app/imports/ui/components/EffectEdit.vue
Normal file
154
app/imports/ui/components/EffectEdit.vue
Normal 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>
|
||||
@@ -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);
|
||||
|
||||
14
app/imports/ui/utility/getEffectIcon.js
Normal file
14
app/imports/ui/utility/getEffectIcon.js
Normal 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 '*' ;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user