Migrating UI for new data structures
This commit is contained in:
@@ -1,42 +1,68 @@
|
||||
<template lang="html">
|
||||
<div class="action-form">
|
||||
<div class="layout column align-center">
|
||||
<icon-picker
|
||||
label="Icon"
|
||||
:value="model.icon"
|
||||
:error-messages="errors.icon"
|
||||
@change="change('icon', ...arguments)"
|
||||
/>
|
||||
</div>
|
||||
<text-field
|
||||
ref="focusFirst"
|
||||
label="Name"
|
||||
:value="model.name"
|
||||
:error-messages="errors.name"
|
||||
@change="change('name', ...arguments)"
|
||||
/>
|
||||
<smart-select
|
||||
label="Action type"
|
||||
:items="actionTypes"
|
||||
:value="model.actionType"
|
||||
:error-messages="errors.actionType"
|
||||
:menu-props="{auto: true, lazy: true}"
|
||||
:hint="actionTypeHints[model.actionType]"
|
||||
@change="change('actionType', ...arguments)"
|
||||
/>
|
||||
<v-row
|
||||
justify="center"
|
||||
class="mb-3"
|
||||
>
|
||||
<v-col cols="1">
|
||||
<icon-color-menu
|
||||
:model="model"
|
||||
:errors="errors"
|
||||
@change="e => $emit('change', e)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<text-field
|
||||
ref="focusFirst"
|
||||
label="Name"
|
||||
:value="model.name"
|
||||
:error-messages="errors.name"
|
||||
@change="change('name', ...arguments)"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<smart-select
|
||||
label="Action type"
|
||||
:items="actionTypes"
|
||||
:value="model.actionType"
|
||||
:error-messages="errors.actionType"
|
||||
:menu-props="{auto: true, lazy: true}"
|
||||
:hint="actionTypeHints[model.actionType]"
|
||||
@change="change('actionType', ...arguments)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<computed-field
|
||||
label="Attack Roll Bonus"
|
||||
hint="The bonus to attack if this action has an attack roll"
|
||||
:model="model.rollBonus"
|
||||
:error-messages="errors.rollBonus"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['rollBonus', ...path], value, ack})"
|
||||
/>
|
||||
<v-slide-x-transition mode="out-in">
|
||||
<v-switch
|
||||
v-if="!isAttack"
|
||||
label="Attack roll"
|
||||
:value="attackSwitch"
|
||||
@change="e => attackSwitch = e"
|
||||
/>
|
||||
<computed-field
|
||||
v-else
|
||||
label="To Hit"
|
||||
prefix="1d20 + "
|
||||
hint="The bonus to attack if this action has an attack roll"
|
||||
:model="model.attackRoll"
|
||||
:error-messages="errors.attackRoll"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['attackRoll', ...path], value, ack})"
|
||||
/>
|
||||
</v-slide-x-transition>
|
||||
|
||||
<inline-computation-field
|
||||
label="Summary"
|
||||
hint="This will appear in the action card in the character sheet"
|
||||
hint="This will appear in the action card in the character sheet, summarise what the action does"
|
||||
:model="model.summary"
|
||||
:error-messages="errors.summary"
|
||||
@change="({path, value, ack}) =>
|
||||
@@ -45,7 +71,7 @@
|
||||
|
||||
<inline-computation-field
|
||||
label="Description"
|
||||
hint="The rest of the description that doesn't fit in the summary goes here"
|
||||
hint="This text will be displayed in the log when the action is taken"
|
||||
:model="model.description"
|
||||
:error-messages="errors.description"
|
||||
@change="({path, value, ack}) =>
|
||||
@@ -82,25 +108,36 @@
|
||||
@change="change('target', ...arguments)"
|
||||
/>
|
||||
-->
|
||||
<div class="layout wrap">
|
||||
<computed-field
|
||||
label="Uses"
|
||||
hint="How many times this action can be used before needing to be reset"
|
||||
:model="model.uses"
|
||||
:error-messages="errors.uses"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['uses', ...path], value, ack})"
|
||||
/>
|
||||
<text-field
|
||||
label="Uses used"
|
||||
type="number"
|
||||
hint="How many times this action has already been used: should be 0 in most cases"
|
||||
style="flex-basis: 300px;"
|
||||
:value="model.usesUsed"
|
||||
:error-messages="errors.uses"
|
||||
@change="change('usesUsed', ...arguments)"
|
||||
/>
|
||||
</div>
|
||||
<v-row dense>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<computed-field
|
||||
label="Uses"
|
||||
hint="How many times this action can be used before needing to be reset"
|
||||
class="mr-2"
|
||||
:model="model.uses"
|
||||
:error-messages="errors.uses"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['uses', ...path], value, ack})"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<text-field
|
||||
label="Uses used"
|
||||
type="number"
|
||||
hint="How many times this action has already been used: should be 0 in most cases"
|
||||
style="flex-basis: 300px;"
|
||||
:value="model.usesUsed"
|
||||
:error-messages="errors.uses"
|
||||
@change="change('usesUsed', ...arguments)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<smart-select
|
||||
label="Reset"
|
||||
clearable
|
||||
@@ -121,12 +158,14 @@
|
||||
import FormSection, {FormSections} from '/imports/ui/properties/forms/shared/FormSection.vue';
|
||||
import ResourcesForm from '/imports/ui/properties/forms/ResourcesForm.vue';
|
||||
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
|
||||
import IconColorMenu from '/imports/ui/properties/forms/shared/IconColorMenu.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormSection,
|
||||
FormSections,
|
||||
ResourcesForm,
|
||||
IconColorMenu,
|
||||
},
|
||||
mixins: [propertyFormMixin],
|
||||
data(){
|
||||
@@ -176,6 +215,7 @@
|
||||
value: 'longRest',
|
||||
}
|
||||
],
|
||||
attackSwitch: false,
|
||||
};
|
||||
data.actionTypeHints = {};
|
||||
data.actionTypes.forEach(type => {
|
||||
@@ -183,6 +223,11 @@
|
||||
});
|
||||
return data;
|
||||
},
|
||||
computed: {
|
||||
isAttack(){
|
||||
return this.attackSwitch || !!this.model.attackRoll?.calculation
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
<template lang="html">
|
||||
<div class="adjustment-form">
|
||||
<div class="layout">
|
||||
<smart-combobox
|
||||
label="Attribute"
|
||||
hint="The attribute this adjustment will apply to"
|
||||
style="flex-basis: 300px;"
|
||||
:items="attributeList"
|
||||
:value="model.stat"
|
||||
:error-messages="errors.stat"
|
||||
@change="change('stat', ...arguments)"
|
||||
/>
|
||||
<computed-field
|
||||
label="Amount"
|
||||
hint="The amount of damage to apply to the selected stat, can be a calculation or roll. Negative values will restore the selected from previous damage. If the operation is set, this is the final value of the stat instead."
|
||||
style="flex-basis: 300px;"
|
||||
:model="model.amount"
|
||||
:error-messages="errors.amount"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['amount', ...path], value, ack})"
|
||||
/>
|
||||
</div>
|
||||
<v-row dense>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<smart-combobox
|
||||
label="Attribute"
|
||||
hint="The attribute this adjustment will apply to"
|
||||
style="flex-basis: 300px;"
|
||||
:items="attributeList"
|
||||
:value="model.stat"
|
||||
:error-messages="errors.stat"
|
||||
@change="change('stat', ...arguments)"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<computed-field
|
||||
label="Amount"
|
||||
:hint="model.operation === 'set' ? setHint : damageHint"
|
||||
style="flex-basis: 300px;"
|
||||
:model="model.amount"
|
||||
:error-messages="errors.amount"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['amount', ...path], value, ack})"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<smart-select
|
||||
label="Operation"
|
||||
class="mx-1"
|
||||
@@ -69,6 +79,8 @@ export default {
|
||||
{text: 'Damage', value: 'increment'},
|
||||
{text: 'Set', value: 'set'},
|
||||
],
|
||||
damageHint: 'The amount of damage to apply to the selected stat, can be a calculation or roll. Negative values will restore the selected from previous damage. If the operation is set, this is the final value of the stat instead.',
|
||||
setHint: 'The value of the stat after applying this adjustment. The stat\'s value can\'t exceed its total',
|
||||
}},
|
||||
computed: {
|
||||
targetOptions(){
|
||||
@@ -88,11 +100,8 @@ export default {
|
||||
text: 'Self',
|
||||
value: 'self',
|
||||
}, {
|
||||
text: 'Roll once for each target',
|
||||
value: 'each',
|
||||
}, {
|
||||
text: 'Roll once and apply to every target',
|
||||
value: 'every',
|
||||
text: 'Target',
|
||||
value: 'target',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -13,22 +13,31 @@
|
||||
$emit('change', {path: ['baseValue', ...path], value, ack})"
|
||||
/>
|
||||
</div>
|
||||
<div class="layout wrap">
|
||||
<text-field
|
||||
label="Name"
|
||||
:value="model.name"
|
||||
:error-messages="errors.name"
|
||||
@change="change('name', ...arguments)"
|
||||
/>
|
||||
<text-field
|
||||
label="Variable name"
|
||||
:value="model.variableName"
|
||||
style="flex-basis: 300px;"
|
||||
hint="Use this name in calculations to reference this attribute"
|
||||
:error-messages="errors.variableName"
|
||||
@change="change('variableName', ...arguments)"
|
||||
/>
|
||||
</div>
|
||||
<v-row dense>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<text-field
|
||||
label="Name"
|
||||
:value="model.name"
|
||||
:error-messages="errors.name"
|
||||
@change="change('name', ...arguments)"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<text-field
|
||||
label="Variable name"
|
||||
:value="model.variableName"
|
||||
hint="Use this name in calculations to reference this attribute"
|
||||
:error-messages="errors.variableName"
|
||||
@change="change('variableName', ...arguments)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<smart-select
|
||||
label="Type"
|
||||
:items="attributeTypes"
|
||||
|
||||
68
app/imports/ui/properties/forms/shared/IconColorMenu.vue
Normal file
68
app/imports/ui/properties/forms/shared/IconColorMenu.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template lang="html">
|
||||
<v-menu offset-y>
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-badge
|
||||
icon="mdi-pencil"
|
||||
overlap
|
||||
>
|
||||
<v-btn
|
||||
icon
|
||||
:color="model.color"
|
||||
outlined
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<property-icon
|
||||
:model="model"
|
||||
:color="model.color"
|
||||
/>
|
||||
</v-btn>
|
||||
</v-badge>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-list-item-title>
|
||||
<icon-picker
|
||||
label="Icon"
|
||||
:value="model.icon"
|
||||
:error-messages="errors.icon"
|
||||
@change="(value, ack) =>$emit('change', {path: ['icon'], value, ack})"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-item-title>
|
||||
<color-picker
|
||||
:value="model.color"
|
||||
@input="value =>$emit('change', {path: ['color'], value})"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import PropertyIcon from '/imports/ui/properties/shared/PropertyIcon.vue';
|
||||
import ColorPicker from '/imports/ui/components/ColorPicker.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PropertyIcon,
|
||||
ColorPicker,
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
errors: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -4,14 +4,23 @@
|
||||
*/
|
||||
import { get, toPath } from 'lodash';
|
||||
|
||||
function resolvePath(model, path){
|
||||
function resolvePath(model, path, set){
|
||||
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);
|
||||
let objectPath = arrayPath.slice(0, -1);
|
||||
let object = model;
|
||||
// Ensure that nested objects exist before navigating them
|
||||
objectPath.forEach(pathKey => {
|
||||
let newObject = object[pathKey];
|
||||
if (!newObject){
|
||||
newObject = {};
|
||||
set(object, pathKey, newObject);
|
||||
}
|
||||
object = newObject;
|
||||
});
|
||||
return {object, key};
|
||||
}
|
||||
|
||||
@@ -41,7 +50,8 @@ const schemaFormMixin = {
|
||||
methods: {
|
||||
// Sets the value at the given path
|
||||
change({path, value, ack}){
|
||||
let {object, key} = resolvePath(this.model, path);
|
||||
let {object, key} = resolvePath(this.model, path, this.$set);
|
||||
|
||||
this.$set(object, key, value);
|
||||
if (ack) ack();
|
||||
},
|
||||
@@ -54,7 +64,7 @@ const schemaFormMixin = {
|
||||
if (ack) ack();
|
||||
},
|
||||
pull({path, ack}){
|
||||
let {object, key} = resolvePath(this.model, path);
|
||||
let {object, key} = resolvePath(this.model, path, this.$set);
|
||||
if (!object || !object.splice){
|
||||
throw `${path.join('.')} is ${object}, doesnt have "splice"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user