Added simple feature UI components and insertion dialog
This commit is contained in:
@@ -36,6 +36,9 @@ let creatureSchema = schema({
|
||||
level: {type: SimpleSchema.Integer, defaultValue: 0},
|
||||
type: {type: String, defaultValue: "pc", allowedValues: ["pc", "npc", "monster"]},
|
||||
|
||||
//computed
|
||||
variables: {type: Object, blackbox: true},
|
||||
|
||||
//permissions
|
||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||
readers: {type: Array, defaultValue: [], index: 1},
|
||||
|
||||
@@ -94,9 +94,48 @@ function writeCreature(char) {
|
||||
writeSkills(char);
|
||||
writeDamageMultipliers(char);
|
||||
writeEffects(char);
|
||||
Creatures.update(char.id, {$set: {level: char.level}});
|
||||
writeCreatureDoc(char);
|
||||
};
|
||||
|
||||
function writeCreatureDoc(char) {
|
||||
// Store all the variables, using the same priority as computation evaluation
|
||||
// Attributes
|
||||
let variables = {};
|
||||
for (let key in char.atts){
|
||||
variables[key] = char.atts[key].result;
|
||||
if (
|
||||
char.atts[key].attributeType === 'ability' &&
|
||||
!variables.hasOwnProperty(key + 'Mod')
|
||||
){
|
||||
variables[key + 'Mod'] = char.atts[key].mod;
|
||||
}
|
||||
}
|
||||
for (let key in char.skills){
|
||||
if (!variables.hasOwnProperty(key)){
|
||||
variables[key] = char.skills[key].result;
|
||||
}
|
||||
}
|
||||
// Damage Multipliers
|
||||
for (let key in char.dms){
|
||||
if (!variables.hasOwnProperty(key)){
|
||||
variables[key] = char.dms[key].result;
|
||||
}
|
||||
}
|
||||
// Class levels
|
||||
for (let key in char.classes){
|
||||
if (!variables.hasOwnProperty(key + 'Level')){
|
||||
variables[key + 'Level'] = char.classes[key].level;
|
||||
}
|
||||
}
|
||||
// Creature level
|
||||
if (!variables.hasOwnProperty('level')){
|
||||
variables['level'] = char.level;
|
||||
}
|
||||
|
||||
// Write the creature
|
||||
Creatures.update(char.id, {$set: {level: char.level, variables}});
|
||||
}
|
||||
|
||||
/*
|
||||
* Write all the attributes from the in-memory char object to the Attirbute docs
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ import {makeChild} from "/imports/api/parenting.js";
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import schema from '/imports/api/schema.js';
|
||||
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
|
||||
import OrderSchema from "/imports/api/creature/subSchemas/OrderSchema.js";
|
||||
import { canEditCreature } from '/imports/api/creature/creaturePermission.js';
|
||||
import { recomputeCreatureById } from '/imports/api/creature/creatureComputation.js'
|
||||
import { getHighestOrder } from '/imports/api/order.js';
|
||||
@@ -29,12 +30,6 @@ let attributeSchema = schema({
|
||||
regEx: /^\w*[a-z]\w*$/i,
|
||||
index: 1,
|
||||
},
|
||||
// Attributes need to store their order to keep the sheet consistent
|
||||
order: {
|
||||
type: SimpleSchema.Integer,
|
||||
// Indexed because we update order in bulk using the current order as a query
|
||||
index: 1,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
allowedValues: [
|
||||
@@ -58,6 +53,10 @@ let attributeSchema = schema({
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
defaultValue: true,
|
||||
},
|
||||
// The computed modifier, provided the attribute is an ability
|
||||
mod: {
|
||||
type: SimpleSchema.Integer,
|
||||
@@ -82,6 +81,8 @@ let attributeSchema = schema({
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
// Attributes need to store their order to keep the sheet consistent
|
||||
order: OrderSchema(),
|
||||
color: ColorSchema(),
|
||||
});
|
||||
|
||||
@@ -108,10 +109,9 @@ const insertAttribute = new ValidatedMethod({
|
||||
|
||||
validate: schema({
|
||||
attribute: {
|
||||
type: Object,
|
||||
blackbox: true,
|
||||
type: attributeSchema.omit('order', 'parent'),
|
||||
},
|
||||
}).validator(),
|
||||
}).validator({ clean: true }),
|
||||
|
||||
run({attribute}) {
|
||||
const charId = attribute.charId;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import schema from '/imports/api/schema.js';
|
||||
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
|
||||
import OrderSchema from "/imports/api/creature/subSchemas/OrderSchema.js";
|
||||
import { canEditCreature } from '/imports/api/creature/creaturePermission.js';
|
||||
import { recomputeCreatureById } from '/imports/api/creature/creatureComputation.js'
|
||||
import { getHighestOrder } from '/imports/api/order.js';
|
||||
import {makeParent} from "/imports/api/parenting.js";
|
||||
|
||||
let Features = new Mongo.Collection("features");
|
||||
@@ -13,17 +17,58 @@ let featureSchema = schema({
|
||||
used: {type: SimpleSchema.Integer, defaultValue: 0},
|
||||
reset: {
|
||||
type: String,
|
||||
allowedValues: ["manual", "longRest", "shortRest"],
|
||||
defaultValue: "manual",
|
||||
allowedValues: ["longRest", "shortRest"],
|
||||
optional: true,
|
||||
},
|
||||
enabled: {type: Boolean, defaultValue: true},
|
||||
alwaysEnabled:{type: Boolean, defaultValue: true},
|
||||
order: {
|
||||
type: SimpleSchema.Integer,
|
||||
// Indexed because we update order in bulk using the current order as a query
|
||||
index: 1,
|
||||
defaultValue: 0,
|
||||
},
|
||||
order: OrderSchema(),
|
||||
color: ColorSchema(),
|
||||
});
|
||||
|
||||
Features.attachSchema(featureSchema);
|
||||
Features.attachSchema(ColorSchema);
|
||||
|
||||
//Features.attachBehaviour("softRemovable");
|
||||
makeParent(Features, ["name", "enabled"]); //parents of effects and attacks
|
||||
|
||||
const insertFeature = new ValidatedMethod({
|
||||
|
||||
name: "Features.methods.insert",
|
||||
|
||||
validate: schema({
|
||||
feature: {
|
||||
type: featureSchema.omit('order', 'parent'),
|
||||
},
|
||||
}).validator({clean: true}),
|
||||
|
||||
run({feature}) {
|
||||
const charId = feature.charId;
|
||||
if (canEditCreature(charId, this.userId)){
|
||||
// Set order
|
||||
feature.order = getHighestOrder({
|
||||
collection: Features,
|
||||
charId,
|
||||
}) + 1;
|
||||
|
||||
// Set parent
|
||||
feature.parent = {
|
||||
id: charId,
|
||||
collection: 'Creatures',
|
||||
};
|
||||
|
||||
// Insert
|
||||
let featureId = Features.insert(feature);
|
||||
recomputeCreatureById(charId);
|
||||
return featureId;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default Features;
|
||||
export { insertFeature }
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import schema from '/imports/api/schema.js';
|
||||
|
||||
const ColorSchema = ({optional = false} = {}) => ({
|
||||
type: String,
|
||||
defaultValue: "#9E9E9E",
|
||||
|
||||
6
app/imports/api/creature/subSchemas/OrderSchema.js
Normal file
6
app/imports/api/creature/subSchemas/OrderSchema.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const OrderSchema = () => ({
|
||||
type: Number,
|
||||
index: true,
|
||||
});
|
||||
|
||||
export default OrderSchema;
|
||||
@@ -47,6 +47,7 @@
|
||||
import DialogStack from '/imports/ui/dialogStack/DialogStack.Story.vue';
|
||||
import EffectEdit from '/imports/ui/components/EffectEdit.Story.vue';
|
||||
import EffectEditExpansionList from '/imports/ui/components/EffectEditExpansionList.Story.vue';
|
||||
import FeatureCard from '/imports/ui/components/FeatureCard.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';
|
||||
@@ -64,6 +65,7 @@
|
||||
DialogStack,
|
||||
EffectEdit,
|
||||
EffectEditExpansionList,
|
||||
FeatureCard,
|
||||
HealthBar,
|
||||
HitDiceListTile,
|
||||
IconSearch,
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
>
|
||||
<v-tab>
|
||||
Stats
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Features
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Tree
|
||||
@@ -25,6 +28,9 @@
|
||||
<v-tabs-items v-model="tab">
|
||||
<v-tab-item>
|
||||
<stats-tab :char-id="character._id"/>
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<features-tab :char-id="character._id"/>
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<character-tree-view :char-id="character._id"/>
|
||||
@@ -43,6 +49,7 @@
|
||||
import { mapMutations } from "vuex";
|
||||
import { theme } from '/imports/ui/theme.js';
|
||||
import StatsTab from '/imports/ui/character/StatsTab.vue';
|
||||
import FeaturesTab from '/imports/ui/character/FeaturesTab.vue';
|
||||
import CharacterTreeView from '/imports/ui/character/CharacterTreeView.vue';
|
||||
import { recomputeCreature } from '/imports/api/creature/creatureComputation.js'
|
||||
|
||||
@@ -53,6 +60,7 @@
|
||||
},
|
||||
components: {
|
||||
StatsTab,
|
||||
FeaturesTab,
|
||||
CharacterTreeView,
|
||||
},
|
||||
data(){return {
|
||||
|
||||
73
app/imports/ui/character/FeaturesTab.vue
Normal file
73
app/imports/ui/character/FeaturesTab.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template lang="html">
|
||||
<div class="features">
|
||||
<column-layout>
|
||||
<div v-for="feature in features" :key="feature._id">
|
||||
<feature-card
|
||||
v-bind="feature"
|
||||
:data-id="feature._id"
|
||||
/>
|
||||
</div>
|
||||
</column-layout>
|
||||
|
||||
<v-btn fixed fab bottom right
|
||||
color="primary"
|
||||
@click="insertFeature"
|
||||
data-id="insert-feature-fab"
|
||||
>
|
||||
<v-icon>add</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Creatures from '/imports/api/creature/Creatures.js';
|
||||
import Features from '/imports/api/creature/properties/Features.js';
|
||||
import { insertFeature } from '/imports/api/creature/properties/Features.js';
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import FeatureCard from '/imports/ui/components/FeatureCard.vue';
|
||||
import { evaluateComputation, evaluateString } from '/imports/ui/utility/evaluate.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
charId: String,
|
||||
},
|
||||
components: {
|
||||
ColumnLayout,
|
||||
FeatureCard,
|
||||
},
|
||||
meteor: {
|
||||
features(){
|
||||
let char = Creatures.findOne(this.charId, {fields: {variables: 1}});
|
||||
if (!char) return [];
|
||||
let vars = char.variables;
|
||||
return Features.find({
|
||||
charId: this.charId,
|
||||
}, {
|
||||
sort: {order: 1},
|
||||
}).map(f => {
|
||||
f.uses = evaluateComputation(f.uses, vars);
|
||||
f.description = evaluateString(f.description, vars);
|
||||
return f;
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
insertFeature(){
|
||||
const charId = this.charId;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'feature-creation-dialog',
|
||||
elementId: 'insert-feature-fab',
|
||||
callback(feature){
|
||||
if (!feature) return;
|
||||
feature.charId = charId;
|
||||
let featureId = insertFeature.call({feature});
|
||||
return featureId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -9,16 +9,15 @@
|
||||
@change="change"
|
||||
:debounce-time="0"
|
||||
/>
|
||||
<div slot="actions">
|
||||
<v-spacer/>
|
||||
<v-btn
|
||||
flat
|
||||
:disabled="!valid"
|
||||
@click="$store.dispatch('popDialogStack', attribute)"
|
||||
>
|
||||
Insert Attribute
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-spacer slot="actions"/>
|
||||
<v-btn
|
||||
flat
|
||||
slot="actions"
|
||||
:disabled="!valid"
|
||||
@click="$store.dispatch('popDialogStack', attribute)"
|
||||
>
|
||||
Insert Attribute
|
||||
</v-btn>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
@@ -26,7 +25,6 @@
|
||||
import AttributeEdit from '/imports/ui/components/AttributeEdit.vue';
|
||||
import Attributes from '/imports/api/creature/properties/Attributes.js';
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
/>
|
||||
<smart-select
|
||||
label="Reset"
|
||||
append-icon="arrow_drop_down"
|
||||
clearable
|
||||
:items="resetOptions"
|
||||
:value="attribute.reset"
|
||||
|
||||
51
app/imports/ui/components/FeatureCard.Story.vue
Normal file
51
app/imports/ui/components/FeatureCard.Story.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template lang="html">
|
||||
<column-layout>
|
||||
<div
|
||||
v-for="(feature, index) in features"
|
||||
:key="index"
|
||||
>
|
||||
<feature-card
|
||||
v-bind="feature"
|
||||
/>
|
||||
</div>
|
||||
</column-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import FeatureCard from '/imports/ui/components/FeatureCard.vue';
|
||||
|
||||
export default {
|
||||
dontWrap: true,
|
||||
components: {
|
||||
ColumnLayout,
|
||||
FeatureCard,
|
||||
},
|
||||
data(){return {
|
||||
features: [
|
||||
{
|
||||
name: 'Feature 1',
|
||||
enabled: true,
|
||||
alwaysEnabled: true,
|
||||
description: `
|
||||
|
||||
blah blah, with
|
||||
|
||||
spacing
|
||||
`,
|
||||
color: '#f44336',
|
||||
}, {
|
||||
name: 'Feature 2',
|
||||
enabled: false,
|
||||
alwaysEnabled: false,
|
||||
description: `Short Description`,
|
||||
uses: 5,
|
||||
used: 2,
|
||||
},
|
||||
],
|
||||
}},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css' scoped>
|
||||
</style>
|
||||
60
app/imports/ui/components/FeatureCard.vue
Normal file
60
app/imports/ui/components/FeatureCard.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template lang="html">
|
||||
<toolbar-card :color="color" @click="$emit('click')">
|
||||
<span slot="toolbar">
|
||||
{{name}}
|
||||
</span>
|
||||
<v-spacer slot="toolbar"/>
|
||||
<v-checkbox
|
||||
hide-details
|
||||
class="shrink"
|
||||
v-if="!alwaysEnabled"
|
||||
:value="enabled"
|
||||
@change="enabled => $emit('change', {enabled})"
|
||||
slot="toolbar"
|
||||
/>
|
||||
<v-card-text>
|
||||
{{description}}
|
||||
</v-card-text>
|
||||
<v-card-actions v-if="uses">
|
||||
<v-spacer/>
|
||||
<v-btn
|
||||
flat
|
||||
:disabled="uses - used <= 0"
|
||||
@click="$emit('used')"
|
||||
>
|
||||
Use
|
||||
</v-btn>
|
||||
<v-btn
|
||||
flat
|
||||
:disabled="!used"
|
||||
@click="$emit('reset')"
|
||||
>
|
||||
Reset
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</toolbar-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
charId: String,
|
||||
name: String,
|
||||
description: String,
|
||||
uses: Number,
|
||||
used: Number,
|
||||
reset: String,
|
||||
color: String,
|
||||
enabled: Boolean,
|
||||
alwaysEnabled: Boolean,
|
||||
},
|
||||
components: {
|
||||
ToolbarCard,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
78
app/imports/ui/components/FeatureCreationDialog.vue
Normal file
78
app/imports/ui/components/FeatureCreationDialog.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template lang="html">
|
||||
<dialog-base>
|
||||
<div slot="toolbar">
|
||||
New Feature
|
||||
</div>
|
||||
<feature-edit
|
||||
:feature="feature"
|
||||
:errors="errors"
|
||||
@change="change"
|
||||
:debounce-time="0"
|
||||
/>
|
||||
<v-spacer slot="actions"/>
|
||||
<v-btn
|
||||
flat
|
||||
slot="actions"
|
||||
:disabled="!valid"
|
||||
@click="$store.dispatch('popDialogStack', feature)"
|
||||
>
|
||||
Insert Feature
|
||||
</v-btn>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FeatureEdit from '/imports/ui/components/FeatureEdit.vue';
|
||||
import Features from '/imports/api/creature/properties/Features.js';
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FeatureEdit,
|
||||
DialogBase,
|
||||
},
|
||||
data(){ return {
|
||||
feature: {
|
||||
name: 'New Feature',
|
||||
description: null,
|
||||
uses: null,
|
||||
used: 0,
|
||||
reset: null,
|
||||
enabled: true,
|
||||
alwaysEnabled: true,
|
||||
color: '#9E9E9E',
|
||||
},
|
||||
valid: true,
|
||||
}},
|
||||
methods: {
|
||||
change(update, ack){
|
||||
for (key in update){
|
||||
this.feature[key] = update[key];
|
||||
}
|
||||
if (ack) ack();
|
||||
},
|
||||
},
|
||||
created(){
|
||||
this.validationContext = Features.simpleSchema().newContext();
|
||||
},
|
||||
computed: {
|
||||
errors(){
|
||||
this.valid = true;
|
||||
let cleanAtt = this.validationContext.clean(this.feature)
|
||||
this.validationContext.validate(cleanAtt, {keys: [
|
||||
'name', 'description', 'uses', 'used', 'reset', 'enabled',
|
||||
'alwaysEnabled', 'color',
|
||||
]});
|
||||
let errors = {};
|
||||
this.validationContext.validationErrors().forEach(error => {
|
||||
if (this.valid) this.valid = false;
|
||||
errors[error.name] = Features.simpleSchema().messageForError(error);
|
||||
});
|
||||
return errors;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
113
app/imports/ui/components/FeatureEdit.vue
Normal file
113
app/imports/ui/components/FeatureEdit.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template lang="html">
|
||||
<div>
|
||||
<text-field
|
||||
label="Name"
|
||||
:value="feature.name"
|
||||
@change="(name, ack) => $emit('change', {name}, ack)"
|
||||
:error-messages="errors.name"
|
||||
:debounce-time="debounceTime"
|
||||
/>
|
||||
<text-field
|
||||
label="Used"
|
||||
type="number"
|
||||
:value="feature.used"
|
||||
@change="(used, ack) => $emit('change', {used}, ack)"
|
||||
:error-messages="errors.used"
|
||||
:debounce-time="debounceTime"
|
||||
/>
|
||||
<text-field
|
||||
label="Uses"
|
||||
:value="feature.uses"
|
||||
@change="(uses, ack) => $emit('change', {uses}, ack)"
|
||||
:error-messages="errors.uses"
|
||||
:debounce-time="debounceTime"
|
||||
/>
|
||||
<smart-select
|
||||
label="Reset"
|
||||
clearable
|
||||
:items="resetOptions"
|
||||
:value="feature.reset"
|
||||
:error-messages="errors.reset"
|
||||
:menu-props="{auto: true, lazy: true}"
|
||||
@change="(reset, ack) => $emit('change', {reset}, ack)"
|
||||
:debounce-time="debounceTime"
|
||||
/>
|
||||
<smart-select
|
||||
label="Enabled"
|
||||
:items="enabledOptions"
|
||||
:value="enabledStatus"
|
||||
:error-messages="errors.enabled || errors.alwaysEnabled"
|
||||
:menu-props="{auto: true, lazy: true}"
|
||||
@change="changeEnabled"
|
||||
:debounce-time="debounceTime"
|
||||
/>
|
||||
<text-area
|
||||
label="Description"
|
||||
:value="feature.description"
|
||||
:error-messages="errors.description"
|
||||
@change="(description, ack) => $emit('change', {description}, ack)"
|
||||
:debounce-time="debounceTime"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
feature: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
errors: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
debounceTime: Number,
|
||||
},
|
||||
data(){ return{
|
||||
resetOptions: [
|
||||
{
|
||||
text: 'Short rest',
|
||||
value: 'shortRest',
|
||||
}, {
|
||||
text: 'Long rest',
|
||||
value: 'longRest',
|
||||
}
|
||||
],
|
||||
enabledOptions: [
|
||||
{
|
||||
text: 'Always enabled',
|
||||
value: 'always',
|
||||
}, {
|
||||
text: 'Enabled',
|
||||
value: 'enabled',
|
||||
}, {
|
||||
text: 'Disabled',
|
||||
value: 'disabled',
|
||||
}
|
||||
],
|
||||
}},
|
||||
computed: {
|
||||
enabledStatus(){
|
||||
if (!this.feature) return;
|
||||
if (this.feature.alwaysEnabled) return 'always';
|
||||
if (this.feature.enabled) return 'enabled';
|
||||
return 'disabled';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeEnabled(value, ack){
|
||||
if (value === 'always'){
|
||||
this.$emit('change', {enabled: true, alwaysEnabled: true}, ack);
|
||||
} else if (value === 'enabled'){
|
||||
this.$emit('change', {enabled: true, alwaysEnabled: false}, ack);
|
||||
} else if (value === 'disabled'){
|
||||
this.$emit('change', {enabled: false, alwaysEnabled: false}, ack);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
38
app/imports/ui/components/ToolbarCard.vue
Normal file
38
app/imports/ui/components/ToolbarCard.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template lang="html">
|
||||
<v-card>
|
||||
<v-toolbar
|
||||
flat
|
||||
style="transform: none;"
|
||||
@click="$emit('click')"
|
||||
:color="color"
|
||||
:dark="isDark"
|
||||
>
|
||||
<slot name="toolbar"/>
|
||||
</v-toolbar>
|
||||
<div>
|
||||
<slot/>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default(){
|
||||
return this.$vuetify.theme.secondary;
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isDark(){
|
||||
return isDarkColor(this.color);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@
|
||||
:loading="loading"
|
||||
:error-messages="errors"
|
||||
:value="safeValue"
|
||||
:auto-grow="autoGrow"
|
||||
@input="input"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
@@ -15,5 +16,11 @@
|
||||
|
||||
export default {
|
||||
mixins: [SmartInput],
|
||||
props: {
|
||||
autoGrow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</template>
|
||||
</v-toolbar>
|
||||
<v-card-text id="base-dialog-body" v-scroll:#base-dialog-body="onScroll">
|
||||
<v-tabs-items :value="isEditing ? 1 : 0">
|
||||
<v-tabs-items :value="isEditing ? 1 : 0" touchless>
|
||||
<v-tab-item>
|
||||
<slot/>
|
||||
</v-tab-item>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import AttributeDialog from '/imports/ui/components/AttributeDialog.vue';
|
||||
import AttributeDialogContainer from '/imports/ui/components/AttributeDialogContainer.vue';
|
||||
import AttributeCreationDialog from '/imports/ui/components/AttributeCreationDialog.vue';
|
||||
import FeatureCreationDialog from '/imports/ui/components/FeatureCreationDialog.vue';
|
||||
import SkillDialogContainer from '/imports/ui/components/SkillDialogContainer.vue';
|
||||
|
||||
export default {
|
||||
AttributeDialog,
|
||||
AttributeDialogContainer,
|
||||
AttributeCreationDialog,
|
||||
FeatureCreationDialog,
|
||||
SkillDialogContainer,
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
z-index: 4;
|
||||
}
|
||||
.dialog-sizer {
|
||||
position: relative;
|
||||
|
||||
29
app/imports/ui/utility/evaluate.js
Normal file
29
app/imports/ui/utility/evaluate.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Computations resolve to numbers
|
||||
// vars is a dict of variables to substitute
|
||||
function evaluateComputation(string, vars){
|
||||
if (!string) return string;
|
||||
// Replace all the string variables with numbers if possible
|
||||
let substitutedString = string.replace(
|
||||
/\w*[a-z]\w*/gi,
|
||||
sub => vars.hasOwnProperty(sub) ? vars[sub] : sub
|
||||
);
|
||||
|
||||
// Evaluate the expression to a number or return it as is.
|
||||
try {
|
||||
return math.eval(substitutedString);
|
||||
} catch (e){
|
||||
return substitutedString;
|
||||
}
|
||||
};
|
||||
|
||||
// Strings can have computations in bracers like so: {computation}
|
||||
// vars is a dict of variables to substitute
|
||||
function evaluateString(string, vars){
|
||||
if (!string) return string;
|
||||
// Compute everything inside bracers
|
||||
return string.replace(/\{([^\{\}]*)\}/g, function(match, p1){
|
||||
return evaluateComputation(p1, vars);
|
||||
});
|
||||
}
|
||||
|
||||
export { evaluateComputation, evaluateString };
|
||||
62
app/package-lock.json
generated
62
app/package-lock.json
generated
@@ -1406,37 +1406,6 @@
|
||||
"requires": {
|
||||
"inherits": "~2.0.1",
|
||||
"readable-stream": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"stream-http": {
|
||||
@@ -1449,37 +1418,6 @@
|
||||
"readable-stream": "^2.3.3",
|
||||
"to-arraybuffer": "^1.0.0",
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
|
||||
@@ -2,5 +2,5 @@ import SimpleSchema from 'simpl-schema';
|
||||
import schema from '/imports/api/schema.js';
|
||||
|
||||
if (Meteor.isDevelopment){
|
||||
SimpleSchema.debug = true
|
||||
//SimpleSchema.debug = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user