Library attribute insert form complete
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"undef": false,
|
"undef": false,
|
||||||
"esversion": 6
|
"esversion": 9,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,14 @@ let AttributeSchema = schema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
defaultValue: 'New Attribute',
|
||||||
},
|
},
|
||||||
// The technical, lowercase, single-word name used in formulae
|
// The technical, lowercase, single-word name used in formulae
|
||||||
variableName: {
|
variableName: {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
|
min: 3,
|
||||||
|
defaultValue: 'newAttribute',
|
||||||
},
|
},
|
||||||
// How it is displayed and computed is determined by type
|
// How it is displayed and computed is determined by type
|
||||||
type: {
|
type: {
|
||||||
@@ -46,6 +49,7 @@ let AttributeSchema = schema({
|
|||||||
'spellSlot', // Level 1, 2, 3... spell slots
|
'spellSlot', // Level 1, 2, 3... spell slots
|
||||||
'utility', // Aren't displayed, Jump height, Carry capacity
|
'utility', // Aren't displayed, Jump height, Carry capacity
|
||||||
],
|
],
|
||||||
|
defaultValue: 'stat',
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
// The starting value, before effects
|
// The starting value, before effects
|
||||||
|
|||||||
@@ -65,10 +65,7 @@ function assertNodeEditPermission(node, userId){
|
|||||||
|
|
||||||
const insertNode = new ValidatedMethod({
|
const insertNode = new ValidatedMethod({
|
||||||
name: 'LibraryNodes.methods.insert',
|
name: 'LibraryNodes.methods.insert',
|
||||||
mixins: [
|
validate: null,
|
||||||
simpleSchemaMixin,
|
|
||||||
],
|
|
||||||
schema: LibraryNodeSchema,
|
|
||||||
run(libraryNode) {
|
run(libraryNode) {
|
||||||
assertNodeEditPermission(libraryNode, this.userId);
|
assertNodeEditPermission(libraryNode, this.userId);
|
||||||
return LibraryNodes.insert(libraryNode);
|
return LibraryNodes.insert(libraryNode);
|
||||||
|
|||||||
@@ -2,29 +2,37 @@
|
|||||||
* Forms that take in a schema and a model of the current data, manages smart
|
* 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
|
* inputs, and sends update events when valid data model changes must occur
|
||||||
*/
|
*/
|
||||||
export default function schemaFormMixin(schema){
|
const schemaFormMixin = {
|
||||||
return {
|
data(){ return {
|
||||||
data(){ return {
|
valid: true,
|
||||||
valid: true,
|
};},
|
||||||
};},
|
computed: {
|
||||||
created(){
|
errors(){
|
||||||
this.validationContext = schema.newContext();
|
this.valid = true;
|
||||||
|
if (!this.model){
|
||||||
|
throw new Error("this.model must be set");
|
||||||
|
}
|
||||||
|
if (!this.validationContext) return {};
|
||||||
|
let cleanModel = this.validationContext.clean(this.model, {
|
||||||
|
getAutoValues: false,
|
||||||
|
});
|
||||||
|
this.validationContext.validate(cleanModel);
|
||||||
|
let errors = {};
|
||||||
|
this.validationContext.validationErrors().forEach(error => {
|
||||||
|
if (this.valid) this.valid = false;
|
||||||
|
errors[error.name] = this.schema.messageForError(error);
|
||||||
|
});
|
||||||
|
return errors;
|
||||||
},
|
},
|
||||||
computed: {
|
},
|
||||||
errors(){
|
methods: {
|
||||||
this.valid = true;
|
change(modifier, ack){
|
||||||
if (!this.model){
|
for (let key in modifier){
|
||||||
throw new Error("this.model must be set");
|
this.$set(this.model, key, modifier[key])
|
||||||
}
|
}
|
||||||
let cleanModel = this.validationContext.clean(this.model);
|
if (ack) ack();
|
||||||
this.validationContext.validate(cleanModel);
|
|
||||||
let errors = {};
|
|
||||||
this.validationContext.validationErrors().forEach(error => {
|
|
||||||
if (this.valid) this.valid = false;
|
|
||||||
errors[error.name] = Attributes.simpleSchema().messageForError(error);
|
|
||||||
});
|
|
||||||
return errors;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default schemaFormMixin;
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
<div>
|
<div>
|
||||||
<text-field
|
<text-field
|
||||||
label="Name"
|
label="Name"
|
||||||
:value="attribute.name"
|
:value="model.name"
|
||||||
@change="(name, ack) => $emit('change', {name}, ack)"
|
@change="(name, ack) => $emit('change', {name}, ack)"
|
||||||
:error-messages="errors.name"
|
:error-messages="errors.name"
|
||||||
:debounce-time="debounceTime"
|
:debounce-time="debounceTime"
|
||||||
/>
|
/>
|
||||||
<text-field
|
<text-field
|
||||||
label="Variable name"
|
label="Variable name"
|
||||||
:value="attribute.variableName"
|
:value="model.variableName"
|
||||||
@change="(variableName, ack) => $emit('change', {variableName}, ack)"
|
@change="(variableName, ack) => $emit('change', {variableName}, ack)"
|
||||||
hint="Use this name in formulae to reference this attribute"
|
hint="Use this name in formulae to reference this attribute"
|
||||||
:error-messages="errors.variableName"
|
:error-messages="errors.variableName"
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
<text-field
|
<text-field
|
||||||
label="Base Value"
|
label="Base Value"
|
||||||
type="number"
|
type="number"
|
||||||
:value="attribute.baseValue"
|
:value="model.baseValue"
|
||||||
@change="(baseValue, ack) => $emit('change', {baseValue: +baseValue}, ack)"
|
@change="(baseValue, ack) => $emit('change', {baseValue}, ack)"
|
||||||
hint="This is the value of the attribute before effects are applied"
|
hint="This is the value of the attribute before effects are applied"
|
||||||
:error-messages="errors.baseValue"
|
:error-messages="errors.baseValue"
|
||||||
:debounce-time="debounceTime"
|
:debounce-time="debounceTime"
|
||||||
@@ -27,15 +27,15 @@
|
|||||||
<text-field
|
<text-field
|
||||||
label="Damage"
|
label="Damage"
|
||||||
type="number"
|
type="number"
|
||||||
:value="-attribute.adjustment"
|
:value="damage"
|
||||||
@change="(damage, ack) => $emit('change', {adjustment: -damage || null}, ack)"
|
@change="(damage, ack) => $emit('change', {adjustment: -damage || damage}, ack)"
|
||||||
:error-messages="errors.adjustment"
|
:error-messages="errors.adjustment"
|
||||||
:debounce-time="debounceTime"
|
:debounce-time="debounceTime"
|
||||||
/>
|
/>
|
||||||
<smart-select
|
<smart-select
|
||||||
label="Type"
|
label="Type"
|
||||||
:items="attributeTypes"
|
:items="attributeTypes"
|
||||||
:value="attribute.type"
|
:value="model.type"
|
||||||
:error-messages="errors.type"
|
:error-messages="errors.type"
|
||||||
:menu-props="{auto: true, lazy: true}"
|
:menu-props="{auto: true, lazy: true}"
|
||||||
@change="(type, ack) => $emit('change', {type}, ack)"
|
@change="(type, ack) => $emit('change', {type}, ack)"
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
/>
|
/>
|
||||||
<v-switch
|
<v-switch
|
||||||
label="Allow decimal values"
|
label="Allow decimal values"
|
||||||
:value="attribute.decimal"
|
:value="model.decimal"
|
||||||
:error-messages="errors.decimal"
|
:error-messages="errors.decimal"
|
||||||
@change="e => $emit('change', {decimal: !!e})"
|
@change="e => $emit('change', {decimal: !!e})"
|
||||||
/>
|
/>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
label="Reset"
|
label="Reset"
|
||||||
clearable
|
clearable
|
||||||
:items="resetOptions"
|
:items="resetOptions"
|
||||||
:value="attribute.reset"
|
:value="model.reset"
|
||||||
:error-messages="errors.reset"
|
:error-messages="errors.reset"
|
||||||
:menu-props="{auto: true, lazy: true}"
|
:menu-props="{auto: true, lazy: true}"
|
||||||
@change="(reset, ack) => $emit('change', {reset}, ack)"
|
@change="(reset, ack) => $emit('change', {reset}, ack)"
|
||||||
@@ -60,9 +60,9 @@
|
|||||||
<text-field
|
<text-field
|
||||||
label="Reset Multiplier"
|
label="Reset Multiplier"
|
||||||
type="number"
|
type="number"
|
||||||
:value="attribute.resetMultiplier"
|
:value="model.resetMultiplier"
|
||||||
:error-messages="errors.resetMultiplier"
|
:error-messages="errors.resetMultiplier"
|
||||||
@change="(resetMultiplier, ack) => $emit('change', {resetMultiplier: +resetMultiplier}, ack)"
|
@change="(resetMultiplier, ack) => $emit('change', {resetMultiplier}, ack)"
|
||||||
hint="Some attributes, like hit dice, only reset by half their total on a long rest"
|
hint="Some attributes, like hit dice, only reset by half their total on a long rest"
|
||||||
:debounce-time="debounceTime"
|
:debounce-time="debounceTime"
|
||||||
/>
|
/>
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
attribute: {
|
model: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
@@ -82,6 +82,11 @@
|
|||||||
},
|
},
|
||||||
debounceTime: Number,
|
debounceTime: Number,
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
damage(){
|
||||||
|
return this.model.adjustment && -this.model.adjustment
|
||||||
|
},
|
||||||
|
},
|
||||||
data(){ return{
|
data(){ return{
|
||||||
attributeTypes: [
|
attributeTypes: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<dialog-base :override-back-button="step === 2 ? back : undefined">
|
<transition-group name="slide">
|
||||||
<div slot="toolbar">{{ step === 2 ? `Add ${property.name}` : 'Add Library Content'}}</div>
|
<dialog-base v-show="step == 1" class="step-1" key="left">
|
||||||
<v-window v-model="step" slot="unwrapped-content" style="height: 100%;">
|
<div slot="toolbar">Add Library Content</div>
|
||||||
<v-window-item :value="1">
|
<property-selector @select="setProperty"/>
|
||||||
<property-selector @select="setProperty"/>
|
</dialog-base>
|
||||||
</v-window-item>
|
<library-node-insert-form
|
||||||
<v-window-item :value="2">
|
v-show="step == 2"
|
||||||
<v-card-text>
|
class="step-2"
|
||||||
<library-node-form :type="property && property.type"/>
|
key="right"
|
||||||
</v-card-text>
|
:type="property && property.type"
|
||||||
</v-window-item>
|
:property-name="property && property.name"
|
||||||
</v-window>
|
@back="step = 1"
|
||||||
|
/>
|
||||||
<div slot="actions" v-if="step === 2" class="layout row justify-end">
|
</transition-group>
|
||||||
<v-btn flat>Insert</v-btn>
|
|
||||||
</div>
|
|
||||||
</dialog-base>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||||
import PropertySelector from '/imports/ui/creature/properties/PropertySelector.vue';
|
import PropertySelector from '/imports/ui/creature/properties/PropertySelector.vue';
|
||||||
import LibraryNodeForm from '/imports/ui/library/LibraryNodeForm.vue';
|
import LibraryNodeInsertForm from '/imports/ui/library/LibraryNodeInsertForm.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() { return {
|
data() { return {
|
||||||
@@ -31,12 +28,9 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
DialogBase,
|
DialogBase,
|
||||||
PropertySelector,
|
PropertySelector,
|
||||||
LibraryNodeForm,
|
LibraryNodeInsertForm,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
back(){
|
|
||||||
this.step = 1;
|
|
||||||
},
|
|
||||||
setProperty(property){
|
setProperty(property){
|
||||||
this.property = property;
|
this.property = property;
|
||||||
this.step = 2;
|
this.step = 2;
|
||||||
@@ -46,4 +40,16 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
.slide-enter-active, .slide-leave-active {
|
||||||
|
transition: transform .3s ease;
|
||||||
|
}
|
||||||
|
.slide-enter-active.step-1, .slide-leave-active.step-1{
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.slide-enter.step-1, .slide-leave-to.step-1 {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
.slide-enter.step-2, .slide-leave-to.step-2 {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
<template lang="html">
|
|
||||||
<component v-if="type" :is="type" class="library-node-form"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import propertyFormIndex from '/imports/ui/creature/properties/propertyFormIndex.js';
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
components: propertyFormIndex,
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
</style>
|
|
||||||
67
app/imports/ui/library/LibraryNodeInsertForm.vue
Normal file
67
app/imports/ui/library/LibraryNodeInsertForm.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<dialog-base :override-back-button="() => $emit('back')">
|
||||||
|
<div slot="toolbar">Add {{propertyName}}</div>
|
||||||
|
<v-card-text>
|
||||||
|
<component
|
||||||
|
v-if="type"
|
||||||
|
:is="type"
|
||||||
|
class="library-node-form"
|
||||||
|
:model="model"
|
||||||
|
:errors="errors"
|
||||||
|
@change="change"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
<div
|
||||||
|
slot="actions"
|
||||||
|
class="layout row justify-end"
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
flat
|
||||||
|
:disabled="!valid"
|
||||||
|
@click="$store.dispatch('popDialogStack', model)"
|
||||||
|
>Insert</v-btn>
|
||||||
|
</div>
|
||||||
|
</dialog-base>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import librarySchemas from '/imports/api/library/librarySchemas.js';
|
||||||
|
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||||
|
import propertyFormIndex from '/imports/ui/creature/properties/propertyFormIndex.js';
|
||||||
|
import schemaFormMixin from '/imports/ui/components/forms/schemaFormMixin.js';
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
...propertyFormIndex,
|
||||||
|
DialogBase,
|
||||||
|
},
|
||||||
|
mixins: [schemaFormMixin],
|
||||||
|
data(){return {
|
||||||
|
model: {
|
||||||
|
libraryNodeType: this.type,
|
||||||
|
},
|
||||||
|
schema: undefined,
|
||||||
|
validationContext: undefined,
|
||||||
|
};},
|
||||||
|
props: {
|
||||||
|
propertyName: String,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
type(newType){
|
||||||
|
this.schema = librarySchemas[newType];
|
||||||
|
this.validationContext = this.schema.newContext();
|
||||||
|
let model = this.schema.clean({});
|
||||||
|
model.libraryNodeType = newType;
|
||||||
|
this.model = model;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
insert(){
|
||||||
|
console.log(this.model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
</style>
|
||||||
@@ -33,6 +33,8 @@
|
|||||||
elementId: 'insert-library-node-fab',
|
elementId: 'insert-library-node-fab',
|
||||||
callback(libraryNode){
|
callback(libraryNode){
|
||||||
if (!libraryNode) return;
|
if (!libraryNode) return;
|
||||||
|
console.log({libraryNode});
|
||||||
|
throw "TODO: give this library node ancestry before inserting it"
|
||||||
let libraryNodeId = insertNode.call(libraryNode);
|
let libraryNodeId = insertNode.call(libraryNode);
|
||||||
return libraryNodeId;
|
return libraryNodeId;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user