Started working on getting creature property insertion working
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
|
||||
import propertySchemas from '/imports/api/properties/propertySchemas.js';
|
||||
import Libraries from '/imports/api/library/Libraries.js';
|
||||
import Creature from '/imports/api/creature/Creatures.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import getModifierFields from '/imports/api/getModifierFields.js';
|
||||
import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js';
|
||||
|
||||
let CreatureProperties = new Mongo.Collection('creatureProperties');
|
||||
|
||||
let CreaturePropertySchema = new SimpleSchema({
|
||||
type: {
|
||||
type: String,
|
||||
allowedValues: Object.keys(propertySchemas),
|
||||
allowedValues: Object.keys(propertySchemasIndex),
|
||||
},
|
||||
charId: {
|
||||
type: String,
|
||||
@@ -20,9 +19,9 @@ let CreaturePropertySchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
for (let key in propertySchemas){
|
||||
for (let key in propertySchemasIndex){
|
||||
let schema = new SimpleSchema({});
|
||||
schema.extend(propertySchemas[key]);
|
||||
schema.extend(propertySchemasIndex[key]);
|
||||
schema.extend(CreaturePropertySchema);
|
||||
schema.extend(ChildSchema);
|
||||
CreatureProperties.attachSchema(schema, {
|
||||
@@ -30,6 +29,28 @@ for (let key in propertySchemas){
|
||||
});
|
||||
}
|
||||
|
||||
function getCreature(property){
|
||||
if (!property) throw new Meteor.Error('No property provided');
|
||||
let creature = Creatures.findOne(property.ancestors[0].id);
|
||||
if (!creature) throw new Meteor.Error('Creature does not exist');
|
||||
return creature;
|
||||
}
|
||||
|
||||
function assertPropertyEditPermission(property, userId){
|
||||
let creature = getCreature(property);
|
||||
return assertEditPermission(creature, userId);
|
||||
}
|
||||
|
||||
const insertProperty = new ValidatedMethod({
|
||||
name: 'CreatureProperties.methods.insert',
|
||||
validate: null,
|
||||
run(creatureProperty) {
|
||||
assertPropertyEditPermission(creatureProperty, this.userId);
|
||||
return CreatureProperties.insert(creatureProperty);
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
const adjustAttribute = new ValidatedMethod({
|
||||
name: 'Attributes.methods.adjust',
|
||||
mixins: [
|
||||
@@ -73,6 +94,7 @@ const adjustAttribute = new ValidatedMethod({
|
||||
}
|
||||
},
|
||||
});
|
||||
*/
|
||||
|
||||
export default CreatureProperties;
|
||||
export { CreaturePropertySchema};
|
||||
export { CreaturePropertySchema, insertProperty };
|
||||
|
||||
@@ -50,38 +50,10 @@ let CreatureSchema = new SimpleSchema({
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
race: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
picture: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
personality: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
ideals: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
bonds: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
flaws: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
backstory: {
|
||||
type: String,
|
||||
optional: true
|
||||
},
|
||||
|
||||
// Mechanics
|
||||
deathSave: {
|
||||
@@ -105,8 +77,6 @@ let CreatureSchema = new SimpleSchema({
|
||||
defaultValue: "pc",
|
||||
allowedValues: ["pc", "npc", "monster"],
|
||||
},
|
||||
|
||||
// Computed
|
||||
variables: {
|
||||
type: Object,
|
||||
blackbox: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
|
||||
import librarySchemas from '/imports/api/library/librarySchemas.js';
|
||||
import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js';
|
||||
import Libraries from '/imports/api/library/Libraries.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
@@ -11,14 +11,14 @@ let LibraryNodes = new Mongo.Collection('libraryNodes');
|
||||
let LibraryNodeSchema = new SimpleSchema({
|
||||
type: {
|
||||
type: String,
|
||||
allowedValues: Object.keys(librarySchemas),
|
||||
allowedValues: Object.keys(propertySchemasIndex),
|
||||
},
|
||||
});
|
||||
|
||||
for (let key in librarySchemas){
|
||||
for (let key in propertySchemasIndex){
|
||||
let schema = new SimpleSchema({});
|
||||
schema.extend(LibraryNodeSchema);
|
||||
schema.extend(librarySchemas[key]);
|
||||
schema.extend(propertySchemasIndex[key]);
|
||||
schema.extend(ChildSchema);
|
||||
schema.extend(SoftRemovableSchema);
|
||||
LibraryNodes.attachSchema(schema, {
|
||||
@@ -112,36 +112,6 @@ const softRemoveLibraryNode = new ValidatedMethod({
|
||||
}
|
||||
});
|
||||
|
||||
function libraryNodesToTree(ancestorId){
|
||||
// Store a dict of all the nodes
|
||||
let nodeIndex = {};
|
||||
let nodeList = [];
|
||||
LibraryNodes.find({
|
||||
'ancestors.id': ancestorId,
|
||||
removed: {$ne: true},
|
||||
}, {
|
||||
sort: {order: 1}
|
||||
}).forEach( node => {
|
||||
let treeNode = {
|
||||
node: node,
|
||||
children: [],
|
||||
};
|
||||
nodeIndex[node._id] = treeNode;
|
||||
nodeList.push(treeNode);
|
||||
});
|
||||
// Create a forest of trees
|
||||
let forest = [];
|
||||
// Either the node is a child of another node, or in the forest as a root
|
||||
nodeList.forEach(node => {
|
||||
if (nodeIndex[node.node.parent.id]){
|
||||
nodeIndex[node.node.parent.id].children.push(node);
|
||||
} else {
|
||||
forest.push(node);
|
||||
}
|
||||
});
|
||||
return forest;
|
||||
}
|
||||
|
||||
export default LibraryNodes;
|
||||
export {
|
||||
LibraryNodeSchema,
|
||||
|
||||
@@ -165,3 +165,33 @@ export function getName(doc){
|
||||
if (ancestors[i].name) return ancestors[i].name;
|
||||
}
|
||||
}
|
||||
|
||||
export function nodesToTree({collection, ancestorId}){
|
||||
// Store a dict of all the nodes
|
||||
let nodeIndex = {};
|
||||
let nodeList = [];
|
||||
collection.find({
|
||||
'ancestors.id': ancestorId,
|
||||
removed: {$ne: true},
|
||||
}, {
|
||||
sort: {order: 1}
|
||||
}).forEach( node => {
|
||||
let treeNode = {
|
||||
node: node,
|
||||
children: [],
|
||||
};
|
||||
nodeIndex[node._id] = treeNode;
|
||||
nodeList.push(treeNode);
|
||||
});
|
||||
// Create a forest of trees
|
||||
let forest = [];
|
||||
// Either the node is a child of another node, or in the forest as a root
|
||||
nodeList.forEach(node => {
|
||||
if (nodeIndex[node.node.parent.id]){
|
||||
nodeIndex[node.node.parent.id].children.push(node);
|
||||
} else {
|
||||
forest.push(node);
|
||||
}
|
||||
});
|
||||
return forest;
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { ActionSchema } from '/imports/api/properties/Actions.js';
|
||||
import { AttackSchema } from '/imports/api/properties/Attacks.js';
|
||||
import { AttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||
import { StoredBuffSchema } from '/imports/api/properties/Buffs.js';
|
||||
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||
import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js';
|
||||
import { EffectSchema } from '/imports/api/properties/Effects.js';
|
||||
import { ExperienceSchema } from '/imports/api/properties/Experiences.js';
|
||||
import { FeatureSchema } from '/imports/api/properties/Features.js';
|
||||
import { FolderSchema } from '/imports/api/properties/Folders.js';
|
||||
import { NoteSchema } from '/imports/api/properties/Notes.js';
|
||||
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||
import { RollSchema } from '/imports/api/properties/Rolls.js';
|
||||
import { SavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
|
||||
import { SkillSchema } from '/imports/api/properties/Skills.js';
|
||||
import { SpellListSchema } from '/imports/api/properties/SpellLists.js';
|
||||
import { SpellSchema } from '/imports/api/properties/Spells.js';
|
||||
import { ContainerSchema } from '/imports/api/properties/Containers.js';
|
||||
import { ItemSchema } from '/imports/api/properties/Items.js';
|
||||
|
||||
const propertySchemas = {
|
||||
action: ActionSchema,
|
||||
attack: AttackSchema,
|
||||
attribute: AttributeSchema,
|
||||
buff: StoredBuffSchema,
|
||||
classLevel: ClassLevelSchema,
|
||||
damageMultiplier: DamageMultiplierSchema,
|
||||
effect: EffectSchema,
|
||||
experience: ExperienceSchema,
|
||||
feature: FeatureSchema,
|
||||
folder: FolderSchema,
|
||||
note: NoteSchema,
|
||||
proficiency: ProficiencySchema,
|
||||
roll: RollSchema,
|
||||
savingThrow: SavingThrowSchema,
|
||||
skill: SkillSchema,
|
||||
spellList: SpellListSchema,
|
||||
spell: SpellSchema,
|
||||
container: ContainerSchema,
|
||||
item: ItemSchema,
|
||||
};
|
||||
|
||||
export default propertySchemas;
|
||||
@@ -19,7 +19,7 @@ import { SpellSchema } from '/imports/api/properties/Spells.js';
|
||||
import { ContainerSchema } from '/imports/api/properties/Containers.js';
|
||||
import { ItemSchema } from '/imports/api/properties/Items.js';
|
||||
|
||||
const librarySchemas = {
|
||||
const propertySchemasIndex = {
|
||||
action: ActionSchema,
|
||||
attack: AttackSchema,
|
||||
attribute: AttributeSchema,
|
||||
@@ -42,4 +42,4 @@ const librarySchemas = {
|
||||
any: new SimpleSchema({}),
|
||||
};
|
||||
|
||||
export default librarySchemas;
|
||||
export default propertySchemasIndex;
|
||||
@@ -1,7 +1,7 @@
|
||||
import Creatures from "/imports/api/creature/Creatures.js";
|
||||
import creatureCollections from '/imports/api/creature/creatureCollections.js';
|
||||
import Creatures from '/imports/api/creature/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
|
||||
|
||||
Meteor.publish("singleCharacter", function(charId){
|
||||
Meteor.publish('singleCharacter', function(charId){
|
||||
userId = this.userId;
|
||||
var char = Creatures.findOne({
|
||||
_id: charId,
|
||||
@@ -9,15 +9,15 @@ Meteor.publish("singleCharacter", function(charId){
|
||||
{readers: userId},
|
||||
{writers: userId},
|
||||
{owner: userId},
|
||||
{"settings.viewPermission": "public"},
|
||||
{'settings.viewPermission': 'public'},
|
||||
],
|
||||
});
|
||||
if (char){
|
||||
return [
|
||||
Creatures.find({_id: charId}),
|
||||
...creatureCollections.map(
|
||||
collection => collection.find({charId})
|
||||
)
|
||||
CreatureProperties.find({
|
||||
'ancestors.id': charId,
|
||||
}),
|
||||
];
|
||||
} else {
|
||||
return [];
|
||||
@@ -25,8 +25,8 @@ Meteor.publish("singleCharacter", function(charId){
|
||||
});
|
||||
|
||||
DDPRateLimiter.addRule({
|
||||
name: "singleCharacter",
|
||||
type: "subscription",
|
||||
name: 'singleCharacter',
|
||||
type: 'subscription',
|
||||
userId: null,
|
||||
connectionId(){ return true; },
|
||||
}, 8, 10000, function(reply, ruleInput){
|
||||
@@ -35,7 +35,7 @@ DDPRateLimiter.addRule({
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.publish("singleCharacterName", function(charId){
|
||||
Meteor.publish('singleCharacterName', function(charId){
|
||||
userId = this.userId;
|
||||
return Creatures.find({
|
||||
_id: charId,
|
||||
@@ -43,9 +43,9 @@ Meteor.publish("singleCharacterName", function(charId){
|
||||
{readers: userId},
|
||||
{writers: userId},
|
||||
{owner: userId},
|
||||
{"settings.viewPermission": "public"},
|
||||
{'settings.viewPermission': 'public'},
|
||||
],
|
||||
}, {
|
||||
fields:{"name": 1}
|
||||
fields:{'name': 1}
|
||||
});
|
||||
});
|
||||
|
||||
81
app/imports/ui/creature/CreatureTreeContainer.vue
Normal file
81
app/imports/ui/creature/CreatureTreeContainer.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template lang="html">
|
||||
<v-card-text style="width: initial; max-width: 100%; min-width: 320px;">
|
||||
<tree-node-list
|
||||
v-if="creatureChildren"
|
||||
:children="creatureChildren"
|
||||
:group="creature && creature._id"
|
||||
:organize="organize"
|
||||
:selected-node-id="selectedNodeId"
|
||||
@selected="e => $emit('selected', e)"
|
||||
@reordered="reordered"
|
||||
@reorganized="reorganized"
|
||||
/>
|
||||
</v-card-text>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Creatures from '/imports/api/creature/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
|
||||
import { nodesToTree } from '/imports/api/parenting/parenting.js'
|
||||
import TreeNodeList from '/imports/ui/components/tree/TreeNodeList.vue';
|
||||
import { organizeDoc, reorderDoc } from '/imports/api/parenting/organizeMethods.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TreeNodeList,
|
||||
},
|
||||
props: {
|
||||
creatureId: String,
|
||||
organize: Boolean,
|
||||
selectedNodeId: String,
|
||||
},
|
||||
meteor: {
|
||||
$subscribe: {
|
||||
'creature': [this.creatureId],
|
||||
},
|
||||
creature(){
|
||||
return Creatures.findOne(this.creatureId);
|
||||
},
|
||||
creatureChildren(){
|
||||
if (!this.creature) return;
|
||||
return nodesToTree({collection: CreatureProperties, ancestorId: this.creatureId});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
reordered({doc, newIndex}){
|
||||
reorderDoc.call({
|
||||
docRef: {
|
||||
id: doc._id,
|
||||
collection: 'creatureProperties',
|
||||
},
|
||||
order: newIndex,
|
||||
});
|
||||
},
|
||||
reorganized({doc, parent, newIndex}){
|
||||
let parentRef;
|
||||
if (parent){
|
||||
parentRef = {
|
||||
id: parent._id,
|
||||
collection: 'creatureProperties',
|
||||
};
|
||||
} else {
|
||||
parentRef = {
|
||||
id: this.creatureId,
|
||||
collection: 'creatures',
|
||||
};
|
||||
}
|
||||
organizeDoc.call({
|
||||
docRef: {
|
||||
id: doc._id,
|
||||
collection: 'creatureProperties',
|
||||
},
|
||||
parentRef,
|
||||
order: newIndex,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -13,12 +13,6 @@
|
||||
v-model="tab"
|
||||
centered
|
||||
>
|
||||
<v-tab>
|
||||
Stats
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Features
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Tree
|
||||
</v-tab>
|
||||
@@ -26,14 +20,8 @@
|
||||
</v-toolbar>
|
||||
<v-content v-if="$subReady.singleCharacter">
|
||||
<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"/>
|
||||
<tree-tab :character-id="creatureId"/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-content>
|
||||
@@ -48,20 +36,16 @@
|
||||
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
|
||||
import { mapMutations } from "vuex";
|
||||
import { theme } from '/imports/ui/theme.js';
|
||||
import StatsTab from '/imports/ui/creature/character/StatsTab.vue';
|
||||
import FeaturesTab from '/imports/ui/creature/character/FeaturesTab.vue';
|
||||
import CharacterTreeView from '/imports/ui/creature/character/CharacterTreeView.vue';
|
||||
import TreeTab from '/imports/ui/creature/character/TreeTab.vue';
|
||||
import { recomputeCreature } from '/imports/api/creature/creatureComputation.js'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
showMenuButton: Boolean,
|
||||
charId: String,
|
||||
creatureId: String,
|
||||
},
|
||||
components: {
|
||||
StatsTab,
|
||||
FeaturesTab,
|
||||
CharacterTreeView,
|
||||
TreeTab,
|
||||
},
|
||||
data(){return {
|
||||
theme,
|
||||
@@ -71,19 +55,19 @@
|
||||
...mapMutations([
|
||||
"toggleDrawer",
|
||||
]),
|
||||
recompute(charId){
|
||||
recomputeCreature.call({charId});
|
||||
recompute(creatureId){
|
||||
recomputeCreature.call({creatureId});
|
||||
},
|
||||
isDarkColor,
|
||||
},
|
||||
meteor: {
|
||||
$subscribe: {
|
||||
'singleCharacter'(){
|
||||
return [this.charId];
|
||||
return [this.creatureId];
|
||||
},
|
||||
},
|
||||
character(){
|
||||
return Creatures.findOne(this.charId) || {};
|
||||
return Creatures.findOne(this.creatureId) || {};
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<template lang="html">
|
||||
<v-treeview :items="treeItems" transition>
|
||||
<template slot="label" slot-scope="{ item, open }">
|
||||
<em>{{item.collection}}</em>:
|
||||
<span v-if="item.collection === 'attributes' || item.collection === 'skills'">
|
||||
{{item.name}} {{item.value}}
|
||||
</span>
|
||||
<span v-else-if="item.collection === 'effects'">
|
||||
{{item.name}}: {{item.stat}} {{item.operation}} {{item.result}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{item.name}}
|
||||
</span>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import creatureCollections from '/imports/api/creature/creatureCollections.js';
|
||||
export default {
|
||||
name: 'character-tree-view',
|
||||
props: {
|
||||
charId: String,
|
||||
},
|
||||
computed: {
|
||||
treeItems(){
|
||||
let docsByParent = this.characterDocsByParent;
|
||||
let buildDoc = (oldDoc) => {
|
||||
let doc = {...oldDoc};
|
||||
if (docsByParent[doc._id]){
|
||||
doc.children = [];
|
||||
for (let group in docsByParent[doc._id]){
|
||||
if (group === 'ungrouped'){
|
||||
doc.children.push(...docsByParent[doc._id][group].map(buildDoc))
|
||||
} else {
|
||||
doc.children.push({
|
||||
name: group,
|
||||
type: 'group',
|
||||
children: docsByParent[doc._id][group].map(buildDoc),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
return buildDoc({
|
||||
_id: 'unparented',
|
||||
}).children;
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
characterDocsByParent(){
|
||||
let parentIndex = {unparented: {}};
|
||||
for (collection of creatureCollections){
|
||||
collection.find({
|
||||
charId: this.charId
|
||||
}, {
|
||||
sort: {order: 1}
|
||||
}).forEach(doc => {
|
||||
doc.collection = collection._name;
|
||||
let parentId = doc.parent && doc.parent.id;
|
||||
let parentGroup = (doc.parent && doc.parent.group) || 'ungrouped'
|
||||
if (parentId && parentId !== this.charId){
|
||||
if (!parentIndex[parentId]){
|
||||
parentIndex[parentId] = {};
|
||||
}
|
||||
if (!parentIndex[parentId][parentGroup]){
|
||||
parentIndex[parentId][parentGroup] = [];
|
||||
}
|
||||
parentIndex[parentId][parentGroup].push(doc);
|
||||
} else {
|
||||
if (!parentIndex.unparented[parentGroup]){
|
||||
parentIndex.unparented[parentGroup] = [];
|
||||
}
|
||||
parentIndex.unparented[parentGroup].push(doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
return parentIndex;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
109
app/imports/ui/creature/character/TreeTab.vue
Normal file
109
app/imports/ui/creature/character/TreeTab.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template lang="html">
|
||||
<div class="tree-tab">
|
||||
<v-card class="ma-4 layout row" data-id="creature-tree-card">
|
||||
<div>
|
||||
<v-toolbar dense flat>
|
||||
<v-spacer/>
|
||||
<v-switch
|
||||
label="Organize"
|
||||
class="mx-3"
|
||||
v-model="organize"
|
||||
style="flex-grow: 0; height: 32px;"
|
||||
/>
|
||||
</v-toolbar>
|
||||
<creature-tree-container
|
||||
:creature-id="creatureId"
|
||||
:organize="organize"
|
||||
@selected="e => selected = e"
|
||||
:selected-node-id="selected"
|
||||
/>
|
||||
</div>
|
||||
<v-divider vertical/>
|
||||
<div style="width: 100%; background-color: inherit;" data-id="selected-node-card">
|
||||
<v-toolbar dense flat>
|
||||
<property-icon :type="selectedProperty && selectedProperty.type" class="mr-2"/>
|
||||
<div class="title">
|
||||
{{getPropertyName(selectedProperty && selectedProperty.type)}}
|
||||
</div>
|
||||
<v-spacer/>
|
||||
<v-btn flat icon @click="editCreatureProperty" v-if="selectedProperty">
|
||||
<v-icon>create</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<property-viewer :model="selectedProperty"/>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-card>
|
||||
<v-btn fixed fab bottom right
|
||||
color="primary"
|
||||
@click="insertCreatureProperty"
|
||||
data-id="insert-creature-property-fab"
|
||||
>
|
||||
<v-icon>add</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CreatureTreeContainer from '/imports/ui/creature/CreatureTreeContainer.vue';
|
||||
import CreatureProperties, { insertProperty } from '/imports/api/creature/CreatureProperties.js';
|
||||
import PropertyViewer from '/imports/ui/properties/PropertyViewer.vue';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
import PropertyIcon from '/imports/ui/properties/PropertyIcon.vue';
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CreatureTreeContainer,
|
||||
PropertyViewer,
|
||||
PropertyIcon,
|
||||
},
|
||||
data(){ return {
|
||||
organize: false,
|
||||
selected: undefined,
|
||||
};},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
insertCreatureProperty(){
|
||||
let that = this;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-creation-dialog',
|
||||
elementId: 'insert-creature-property-fab',
|
||||
callback(creatureProperty){
|
||||
if (!creatureProperty) return;
|
||||
creatureProperty.parent = {collection: "creatures", id: that.creatureId};
|
||||
creatureProperty.ancestors = [ {collection: "creatures", id: that.creatureId}];
|
||||
setDocToLastOrder({collection: CreatureProperties, doc: creatureProperty});
|
||||
let creaturePropertyId = insertProperty.call(creatureProperty);
|
||||
return creaturePropertyId;
|
||||
}
|
||||
});
|
||||
},
|
||||
editCreatureProperty(){
|
||||
let that = this;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'library-node-edit-dialog',
|
||||
elementId: 'selected-node-card',
|
||||
data: {_id: this.selected},
|
||||
});
|
||||
},
|
||||
getPropertyName,
|
||||
},
|
||||
meteor: {
|
||||
selectedProperty(){
|
||||
return CreatureProperties.findOne({
|
||||
_id: this.selected,
|
||||
removed: {$ne: true}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template lang="html">
|
||||
<selectable-property-dialog v-model="type">
|
||||
<creature-property-insert-form
|
||||
:type="type"
|
||||
:property-name="getPropertyName(type)"
|
||||
@back="type = undefined"
|
||||
/>
|
||||
</selectable-property-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectablePropertyDialog from '/imports/ui/properties/components/SelectablePropertyDialog.vue';
|
||||
import CreaturePropertyInsertForm from '/imports/ui/creature/creatureProperties/CreaturePropertyInsertForm.vue';
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
|
||||
export default {
|
||||
data() { return {
|
||||
type: undefined,
|
||||
};},
|
||||
components: {
|
||||
SelectablePropertyDialog,
|
||||
CreaturePropertyInsertForm,
|
||||
},
|
||||
methods: {
|
||||
getPropertyName,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template lang="html">
|
||||
<dialog-base :override-back-button="() => $emit('back')">
|
||||
<div slot="toolbar">Add {{propertyName}}</div>
|
||||
<component
|
||||
v-if="type"
|
||||
stored
|
||||
:is="type"
|
||||
class="creature-property-form"
|
||||
:model="model"
|
||||
:errors="errors"
|
||||
@change="change"
|
||||
@push="push"
|
||||
@pull="pull"
|
||||
/>
|
||||
<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 propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js';
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import propertyFormIndex from '/imports/ui/properties/forms/shared/propertyFormIndex.js';
|
||||
import schemaFormMixin from '/imports/ui/properties/forms/shared/schemaFormMixin.js';
|
||||
export default {
|
||||
components: {
|
||||
...propertyFormIndex,
|
||||
DialogBase,
|
||||
},
|
||||
mixins: [schemaFormMixin],
|
||||
data(){return {
|
||||
model: {
|
||||
type: this.type,
|
||||
},
|
||||
schema: undefined,
|
||||
validationContext: undefined,
|
||||
};},
|
||||
props: {
|
||||
propertyName: String,
|
||||
type: String,
|
||||
},
|
||||
watch: {
|
||||
type(newType){
|
||||
if (!newType) return;
|
||||
this.schema = propertySchemasIndex[newType];
|
||||
this.validationContext = this.schema.newContext();
|
||||
let model = this.schema.clean({});
|
||||
model.type = newType;
|
||||
this.model = model;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -1,6 +1,7 @@
|
||||
import AttributeDialog from '/imports/ui/properties/attributes/AttributeDialog.vue';
|
||||
import AttributeDialogContainer from '/imports/ui/properties/attributes/AttributeDialogContainer.vue';
|
||||
import AttributeCreationDialog from '/imports/ui/properties/attributes/AttributeCreationDialog.vue';
|
||||
import CreaturePropertyCreationDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue';
|
||||
import FeatureCreationDialog from '/imports/ui/properties/features/FeatureCreationDialog.vue';
|
||||
import FeatureDialogContainer from '/imports/ui/properties/features/FeatureDialogContainer.vue';
|
||||
import LibraryCreationDialog from '/imports/ui/library/LibraryCreationDialog.vue';
|
||||
@@ -12,6 +13,7 @@ export default {
|
||||
AttributeDialog,
|
||||
AttributeDialogContainer,
|
||||
AttributeCreationDialog,
|
||||
CreaturePropertyCreationDialog,
|
||||
FeatureCreationDialog,
|
||||
FeatureDialogContainer,
|
||||
LibraryCreationDialog,
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
<script>
|
||||
import Libraries from '/imports/api/library/Libraries.js';
|
||||
import LibraryNodes, { libraryNodesToTree } from '/imports/api/library/LibraryNodes.js';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||
import { nodesToTree } from '/imports/api/parenting/parenting.js'
|
||||
import TreeNodeList from '/imports/ui/components/tree/TreeNodeList.vue';
|
||||
import { organizeDoc, reorderDoc } from '/imports/api/parenting/organizeMethods.js';
|
||||
|
||||
@@ -37,7 +38,7 @@
|
||||
},
|
||||
libraryChildren(){
|
||||
if (!this.library) return;
|
||||
return libraryNodesToTree(this.library._id);
|
||||
return nodesToTree({collection: LibraryNodes, ancestorId: this.library._id});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,57 +1,31 @@
|
||||
<template lang="html">
|
||||
<transition-group name="slide">
|
||||
<dialog-base v-show="step == 1" class="step-1" key="left">
|
||||
<div slot="toolbar">Add Library Content</div>
|
||||
<property-selector @select="setType"/>
|
||||
</dialog-base>
|
||||
<selectable-property-dialog v-model="type">
|
||||
<library-node-insert-form
|
||||
v-show="step == 2"
|
||||
class="step-2"
|
||||
key="right"
|
||||
:type="type"
|
||||
:property-name="getPropertyName(type)"
|
||||
@back="step = 1"
|
||||
@back="type = undefined"
|
||||
/>
|
||||
</transition-group>
|
||||
</selectable-property-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import PropertySelector from '/imports/ui/properties/PropertySelector.vue';
|
||||
import SelectablePropertyDialog from '/imports/ui/properties/components/SelectablePropertyDialog.vue';
|
||||
import LibraryNodeInsertForm from '/imports/ui/library/LibraryNodeInsertForm.vue';
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
|
||||
export default {
|
||||
data() { return {
|
||||
type: undefined,
|
||||
step: 1,
|
||||
};},
|
||||
components: {
|
||||
DialogBase,
|
||||
PropertySelector,
|
||||
SelectablePropertyDialog,
|
||||
LibraryNodeInsertForm,
|
||||
},
|
||||
methods: {
|
||||
setType(property){
|
||||
this.type = property;
|
||||
this.step = 2;
|
||||
},
|
||||
getPropertyName,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
pullFromLibraryNode,
|
||||
softRemoveLibraryNode,
|
||||
} from '/imports/api/library/LibraryNodes.js';
|
||||
import librarySchemas from '/imports/api/library/librarySchemas.js';
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
import PropertyIcon from '/imports/ui/properties/PropertyIcon.vue';
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import librarySchemas from '/imports/api/library/librarySchemas.js';
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import propertyFormIndex from '/imports/ui/properties/forms/shared/propertyFormIndex.js';
|
||||
import schemaFormMixin from '/imports/ui/properties/forms/shared/schemaFormMixin.js';
|
||||
import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
...propertyFormIndex,
|
||||
@@ -49,7 +50,8 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
type(newType){
|
||||
this.schema = librarySchemas[newType];
|
||||
if (!newType) return;
|
||||
this.schema = propertySchemasIndex[newType];
|
||||
this.validationContext = this.schema.newContext();
|
||||
let model = this.schema.clean({});
|
||||
model.type = newType;
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
|
||||
const characterTransform = function(char){
|
||||
char.url = `\/character\/${char._id}\/${char.urlName || "-"}`;
|
||||
char.color = getColorClass(char.color);
|
||||
char.initial = char.name[0] || "?";
|
||||
return char;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<character-sheet show-menu-button :char-id="$route.params.id" />
|
||||
<character-sheet show-menu-button :creature-id="$route.params.id" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<template lang="html">
|
||||
<transition-group name="slide">
|
||||
<dialog-base v-show="!value" class="step-1" key="left">
|
||||
<div slot="toolbar">Add Library Content</div>
|
||||
<property-selector @select="property => $emit('input', property)"/>
|
||||
</dialog-base>
|
||||
<div
|
||||
v-show="value"
|
||||
class="step-2"
|
||||
style="height: 100%;"
|
||||
key="right"
|
||||
>
|
||||
<slot/>
|
||||
</div>
|
||||
</transition-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import PropertySelector from '/imports/ui/properties/PropertySelector.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
DialogBase,
|
||||
PropertySelector,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<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>
|
||||
@@ -31,8 +31,7 @@ RouterFactory.configure(factory => {
|
||||
component: Home,
|
||||
},{
|
||||
path: '/characterList',
|
||||
//component: CharacterList,
|
||||
component: NotImplemented,
|
||||
component: CharacterList,
|
||||
},{
|
||||
path: '/library',
|
||||
component: Libraries,
|
||||
@@ -41,12 +40,11 @@ RouterFactory.configure(factory => {
|
||||
component: Library,
|
||||
},{
|
||||
path: '/character/:id/:urlName',
|
||||
//component: CharacterSheetPage,
|
||||
component: NotImplemented,
|
||||
component: CharacterSheetPage,
|
||||
//component: NotImplemented,
|
||||
},{
|
||||
path: '/character/:id',
|
||||
//component: CharacterSheetPage,
|
||||
component: NotImplemented,
|
||||
component: CharacterSheetPage,
|
||||
},{
|
||||
path: '/sign-in',
|
||||
component: SignIn,
|
||||
|
||||
Reference in New Issue
Block a user