Compare commits
12 Commits
2.0-beta.2
...
2.0-beta.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2150bd6da4 | ||
|
|
e1df145675 | ||
|
|
1eb78756ac | ||
|
|
ce9b9199ec | ||
|
|
cfb1414494 | ||
|
|
4abd689c9f | ||
|
|
f0e443fba2 | ||
|
|
52e7deedc6 | ||
|
|
15d593db79 | ||
|
|
e30754ef26 | ||
|
|
255ac529b3 | ||
|
|
c8b5ada5b9 |
@@ -7,7 +7,8 @@ import SharingSchema from '/imports/api/sharing/SharingSchema.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { assertUserHasPaidBenefits } from '/imports/api/users/patreon/tiers.js';
|
||||
|
||||
import defaultCharacterProperties from '/imports/api/creature/defaultCharacterProperties.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import '/imports/api/creature/removeCreature.js';
|
||||
import '/imports/api/creature/restCreature.js';
|
||||
|
||||
@@ -198,24 +199,29 @@ const insertCreature = new ValidatedMethod({
|
||||
let creatureId = Creatures.insert({
|
||||
owner: this.userId,
|
||||
});
|
||||
CreatureProperties.insert({
|
||||
slotTags: ['base'],
|
||||
quantityExpected: 1,
|
||||
type: 'propertySlot',
|
||||
name: 'Base',
|
||||
description: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base, your sheet will be empty.',
|
||||
hideWhenFull: true,
|
||||
parent: {collection: 'creatures', id: creatureId},
|
||||
ancestors: [{collection: 'creatures', id: creatureId}],
|
||||
order: 0,
|
||||
tags: [],
|
||||
spaceLeft: 1,
|
||||
totalFilled: 0,
|
||||
|
||||
// Insert the default properties
|
||||
// Not batchInsert because we want the properties cleaned by the schema
|
||||
let baseId;
|
||||
defaultCharacterProperties(creatureId).forEach(prop => {
|
||||
let id = CreatureProperties.insert(prop);
|
||||
if (prop.name === 'Ruleset'){
|
||||
baseId = id;
|
||||
}
|
||||
});
|
||||
|
||||
if (Meteor.isServer){
|
||||
// Insert the 5e ruleset as the default base
|
||||
insertPropertyFromLibraryNode.call({
|
||||
nodeId: 'iHbhfcg3AL5isSWbw',
|
||||
parentRef: {id: baseId, collection: 'creatureProperties'},
|
||||
order: 0.5,
|
||||
});
|
||||
}
|
||||
|
||||
this.unblock();
|
||||
return creatureId;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const updateCreature = new ValidatedMethod({
|
||||
|
||||
@@ -7,23 +7,8 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
|
||||
|
||||
export function getParentRefByTag(creatureId, tag){
|
||||
let prop = CreatureProperties.findOne({
|
||||
'ancestors.id': creatureId,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
tags: tag,
|
||||
}, {
|
||||
sort: {order: 1},
|
||||
});
|
||||
if (prop){
|
||||
return {id: prop._id, collection: 'creatureProperties'};
|
||||
} else {
|
||||
return {id: creatureId, collection: 'creatures'};
|
||||
}
|
||||
}
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
|
||||
// Equipping or unequipping an item will also change its parent
|
||||
const equipItem = new ValidatedMethod({
|
||||
@@ -50,8 +35,9 @@ const equipItem = new ValidatedMethod({
|
||||
}, {
|
||||
selector: {type: 'item'},
|
||||
});
|
||||
let tag = equipped ? INVENTORY_TAGS.equipment : INVENTORY_TAGS.carried;
|
||||
let tag = equipped ? BUILT_IN_TAGS.equipment : BUILT_IN_TAGS.carried;
|
||||
let parentRef = getParentRefByTag(creature._id, tag);
|
||||
if (!parentRef) parentRef = {id: creature._id, collection: 'creatures'};
|
||||
|
||||
organizeDoc.call({
|
||||
docRef: {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
|
||||
export default function getParentRefByTag(creatureId, tag){
|
||||
let prop = CreatureProperties.findOne({
|
||||
'ancestors.id': creatureId,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
tags: tag,
|
||||
}, {
|
||||
sort: {order: 1},
|
||||
});
|
||||
return prop && {id: prop._id, collection: 'creatureProperties'};
|
||||
}
|
||||
@@ -2,16 +2,26 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
import { getAncestry } from '/imports/api/parenting/parenting.js';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
||||
|
||||
const insertProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.insert',
|
||||
validate: null,
|
||||
validate: new SimpleSchema({
|
||||
creatureProperty: {
|
||||
type: Object,
|
||||
blackbox: true,
|
||||
},
|
||||
parentRef: RefSchema,
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
@@ -42,6 +52,86 @@ const insertProperty = new ValidatedMethod({
|
||||
},
|
||||
});
|
||||
|
||||
const insertPropertyAsChildOfTag = new ValidatedMethod({
|
||||
name: 'creatureProperties.insertAsChildOfTag',
|
||||
validate: new SimpleSchema({
|
||||
creatureProperty: {
|
||||
type: Object,
|
||||
blackbox: true,
|
||||
},
|
||||
creatureId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
},
|
||||
tag: {
|
||||
type: String,
|
||||
max: 20,
|
||||
},
|
||||
tagDefaultName: {
|
||||
type: String,
|
||||
max: 20,
|
||||
optional: true,
|
||||
},
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({creatureProperty, creatureId, tag, tagDefaultName}) {
|
||||
let parentRef = getParentRefByTag(creatureId, tag);
|
||||
|
||||
if (!parentRef){
|
||||
// Use the creature as the parent and mark that we need to insert the folder first later
|
||||
var insertFolderFirst = true;
|
||||
parentRef = {id: creatureId, collection: 'creatures'};
|
||||
}
|
||||
|
||||
// get the new ancestry for the properties
|
||||
let {parentDoc, ancestors} = getAncestry({parentRef});
|
||||
|
||||
// Check permission to edit
|
||||
let rootCreature;
|
||||
if (parentRef.collection === 'creatures'){
|
||||
rootCreature = parentDoc;
|
||||
} else if (parentRef.collection === 'creatureProperties'){
|
||||
rootCreature = getRootCreatureAncestor(parentDoc);
|
||||
} else {
|
||||
throw `${parentRef.collection} is not a valid parent collection`
|
||||
}
|
||||
assertEditPermission(rootCreature, this.userId);
|
||||
|
||||
// Add the folder first if we need to
|
||||
if (insertFolderFirst){
|
||||
let order = getHighestOrder({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: parentRef.id,
|
||||
}) + 1;
|
||||
let id = CreatureProperties.insert({
|
||||
type: 'folder',
|
||||
name: tagDefaultName || (tag.charAt(0).toUpperCase() + tag.slice(1)),
|
||||
tags: [tag],
|
||||
parent: parentRef,
|
||||
ancestors: [parentRef],
|
||||
order,
|
||||
});
|
||||
// Make the folder our new parent
|
||||
let newParentRef = {id, collection: 'creatureProperties'};
|
||||
ancestors = [parentRef, newParentRef];
|
||||
parentRef = newParentRef;
|
||||
creatureProperty.order = order + 1;
|
||||
}
|
||||
|
||||
creatureProperty.parent = parentRef;
|
||||
creatureProperty.ancestors = ancestors;
|
||||
|
||||
return insertPropertyWork({
|
||||
property: creatureProperty,
|
||||
creature: rootCreature,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export function insertPropertyWork({property, creature}){
|
||||
delete property._id;
|
||||
let _id = CreatureProperties.insert(property);
|
||||
@@ -63,3 +153,4 @@ export function insertPropertyWork({property, creature}){
|
||||
}
|
||||
|
||||
export default insertProperty;
|
||||
export { insertPropertyAsChildOfTag };
|
||||
|
||||
47
app/imports/api/creature/defaultCharacterProperties.js
Normal file
47
app/imports/api/creature/defaultCharacterProperties.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
|
||||
export default function defaultCharacterProperties(creatureId){
|
||||
if (!creatureId) throw 'creatureId is required';
|
||||
const creatureRef = {collection: 'creatures', id: creatureId};
|
||||
let randomSrc = DDP.randomStream('defaultProperties');
|
||||
const inventoryId = randomSrc.id();
|
||||
const inventoryRef = {collection: 'creatureProperties', id: inventoryId};
|
||||
return [
|
||||
{
|
||||
type: 'propertySlot',
|
||||
name: 'Ruleset',
|
||||
description: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base, your sheet will be empty.',
|
||||
slotTags: ['base'],
|
||||
tags: [],
|
||||
quantityExpected: 1,
|
||||
hideWhenFull: true,
|
||||
spaceLeft: 1,
|
||||
totalFilled: 0,
|
||||
order: 0,
|
||||
parent: creatureRef,
|
||||
ancestors: [creatureRef],
|
||||
}, {
|
||||
_id: inventoryId,
|
||||
type: 'folder',
|
||||
name: 'Inventory',
|
||||
tags: [BUILT_IN_TAGS.inventory],
|
||||
order: 1,
|
||||
parent: creatureRef,
|
||||
ancestors: [creatureRef],
|
||||
}, {
|
||||
type: 'folder',
|
||||
name: 'Equipment',
|
||||
tags: [BUILT_IN_TAGS.equipment],
|
||||
order: 2,
|
||||
parent: inventoryRef,
|
||||
ancestors: [creatureRef, inventoryRef],
|
||||
}, {
|
||||
type: 'folder',
|
||||
name: 'Carried',
|
||||
tags: [BUILT_IN_TAGS.carried],
|
||||
order: 3,
|
||||
parent: inventoryRef,
|
||||
ancestors: [creatureRef, inventoryRef],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
const INVENTORY_TAGS = Object.freeze({
|
||||
const BUILT_IN_TAGS = Object.freeze({
|
||||
inventory: 'inventory',
|
||||
equipment: 'equipment',
|
||||
carried: 'carried',
|
||||
});
|
||||
|
||||
export default INVENTORY_TAGS;
|
||||
export default BUILT_IN_TAGS;
|
||||
@@ -16,7 +16,7 @@
|
||||
:loading="loading"
|
||||
:error-messages="errors"
|
||||
:disabled="isDisabled"
|
||||
filled
|
||||
outlined
|
||||
v-on="on"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:menu-props="{auto: true, lazy: true}"
|
||||
:search-input.sync="searchInput"
|
||||
:disabled="isDisabled"
|
||||
filled
|
||||
outlined
|
||||
@change="customChange"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:value="safeValue"
|
||||
:menu-props="{auto: true, lazy: true}"
|
||||
:disabled="isDisabled"
|
||||
filled
|
||||
outlined
|
||||
@change="change"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:value="safeValue"
|
||||
:disabled="isDisabled"
|
||||
:auto-grow="autoGrow"
|
||||
filled
|
||||
outlined
|
||||
@input="input"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:error-messages="errors"
|
||||
:value="safeValue"
|
||||
:disabled="isDisabled"
|
||||
:filled="!regular"
|
||||
:outlined="!regular"
|
||||
@input="input"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
:light="!isDark"
|
||||
:flat="flat"
|
||||
>
|
||||
<v-btn
|
||||
icon
|
||||
@click="back"
|
||||
>
|
||||
<v-icon>arrow_back</v-icon>
|
||||
</v-btn>
|
||||
<property-icon
|
||||
:model="model"
|
||||
class="mr-2"
|
||||
@@ -163,6 +169,9 @@ export default {
|
||||
colorChanged(value){
|
||||
this.$emit('color-changed', value);
|
||||
},
|
||||
back(){
|
||||
this.$store.dispatch('popDialogStack');
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -6,7 +6,7 @@ let lastSnackbarId = 0;
|
||||
|
||||
function snackbar(data) {
|
||||
globalState.queue.push({
|
||||
data,
|
||||
data, //{text OR content, callback, callbackName} // content is logContent
|
||||
id: ++lastSnackbarId,
|
||||
enqueuedAt: new Date(),
|
||||
shown: false,
|
||||
|
||||
@@ -42,24 +42,12 @@
|
||||
<v-switch
|
||||
label="Show spells tab"
|
||||
:input-value="!model.settings.hideSpellsTab"
|
||||
@change="value => {
|
||||
$emit('change', {path: ['settings','hideSpellsTab'], value: !value});
|
||||
$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{id: model._id, tab: 0}
|
||||
);
|
||||
}"
|
||||
@change="changeHideSpellsTab"
|
||||
/>
|
||||
<v-switch
|
||||
label="Show tree tab"
|
||||
:input-value="model.settings.showTreeTab"
|
||||
@change="value => {
|
||||
$emit('change', {path: ['settings','showTreeTab'], value: !!value});
|
||||
$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{id: model._id, tab: 0}
|
||||
);
|
||||
}"
|
||||
@change="changeShowTreeTab"
|
||||
/>
|
||||
<text-field
|
||||
label="Hit Dice reset multiplier"
|
||||
@@ -129,6 +117,34 @@ export default {
|
||||
},
|
||||
disabled: Boolean,
|
||||
},
|
||||
methods: {
|
||||
changeShowTreeTab(value){
|
||||
this.$emit('change', {
|
||||
path: ['settings','showTreeTab'],
|
||||
value: !!value
|
||||
});
|
||||
let currentTab = this.$store.getters.tabById(this.model._id);
|
||||
if (!value && currentTab === 5){
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{id: this.model._id, tab: 4}
|
||||
);
|
||||
}
|
||||
},
|
||||
changeHideSpellsTab(value){
|
||||
this.$emit('change', {
|
||||
path: ['settings','hideSpellsTab'],
|
||||
value: !value
|
||||
});
|
||||
let currentTab = this.$store.getters.tabById(this.model._id);
|
||||
if (!value && currentTab === 3){
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{id: this.model._id, tab: 4}
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
import Creatures from '/imports/api/creature/Creatures.js';
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import removeCreature from '/imports/api/creature/removeCreature.js';
|
||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -61,12 +62,12 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
remove(){
|
||||
this.$router.push('/characterList');
|
||||
this.$store.dispatch('popDialogStack');
|
||||
removeCreature.call({charId: this.id}, (error) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
this.$router.push('/characterList');
|
||||
this.$store.dispatch('popDialogStack');
|
||||
snackbar({text: error.message || error.toString()});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<script lang="js">
|
||||
import LabeledFab from '/imports/ui/components/LabeledFab.vue';
|
||||
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
||||
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
||||
import insertProperty, { insertPropertyAsChildOfTag } from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
@@ -176,9 +176,24 @@
|
||||
collection: CreatureProperties,
|
||||
ancestorId: creatureId
|
||||
}) + 1;
|
||||
let id = insertProperty.call({
|
||||
|
||||
let tagDetails;
|
||||
switch (type){
|
||||
case 'item':
|
||||
tagDetails = {tag: 'carried', name: 'Carried'};
|
||||
break;
|
||||
case 'container':
|
||||
tagDetails = {tag: 'inventory', name: 'Inventory'};
|
||||
break;
|
||||
default:
|
||||
tagDetails = {tag: `${type}s`};
|
||||
break;
|
||||
}
|
||||
let id = insertPropertyAsChildOfTag.call({
|
||||
creatureProperty,
|
||||
parentRef: {collection: 'creatures', id: creatureId},
|
||||
creatureId,
|
||||
tag: tagDetails.tag,
|
||||
tagDefaultName: tagDetails.name,
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -156,9 +156,9 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted(){
|
||||
if (this.$store.state.showBuildDialog){
|
||||
this.$store.commit('setShowBuildDialog', false);
|
||||
this.showSlotDialog();
|
||||
if (this.$store.state.showDetailsDialog){
|
||||
this.$store.commit('setShowDetailsDialog', false);
|
||||
this.showCharacterForm();
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
|
||||
@@ -104,8 +104,8 @@ import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import ContainerCard from '/imports/ui/properties/components/inventory/ContainerCard.vue';
|
||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||
import { getParentRefByTag } from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
||||
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
|
||||
export default {
|
||||
@@ -183,10 +183,10 @@ export default {
|
||||
});
|
||||
},
|
||||
equipmentParentRef(){
|
||||
return getParentRefByTag(this.creatureId, INVENTORY_TAGS.equipment);
|
||||
return getParentRefByTag(this.creatureId, BUILT_IN_TAGS.equipment);
|
||||
},
|
||||
carriedParentRef(){
|
||||
return getParentRefByTag(this.creatureId, INVENTORY_TAGS.carried);
|
||||
return getParentRefByTag(this.creatureId, BUILT_IN_TAGS.carried);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -434,7 +434,8 @@
|
||||
return getProperties(this.creature, {type: 'buff', applied: true});
|
||||
},
|
||||
attacks(){
|
||||
let props = getProperties(this.creature, {type: 'attack'}).map(attack => {
|
||||
let props = getProperties(this.creature, {type: 'attack'})
|
||||
return props && props.map(attack => {
|
||||
attack.children = CreatureProperties.find({
|
||||
'ancestors.id': attack._id,
|
||||
removed: {$ne: true},
|
||||
@@ -444,7 +445,6 @@
|
||||
});
|
||||
return attack;
|
||||
});
|
||||
return props;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
78
app/imports/ui/creature/creatureProperties/Breadcrumbs.vue
Normal file
78
app/imports/ui/creature/creatureProperties/Breadcrumbs.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template lang="html">
|
||||
<div class="breadcrumbs layout align-center wrap">
|
||||
<template v-for="(prop, index) in props">
|
||||
<v-icon
|
||||
v-if="index !== 0"
|
||||
:key="index"
|
||||
>
|
||||
chevron_right
|
||||
</v-icon>
|
||||
<a
|
||||
:key="prop._id"
|
||||
:data-id="`breadcrumb-${prop._id}`"
|
||||
@click="click(prop._id)"
|
||||
>
|
||||
<tree-node-view :model="prop" />
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TreeNodeView,
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
props(){
|
||||
return this.model.ancestors
|
||||
.slice(1)
|
||||
.map(ref => fetchDocByRef(ref))
|
||||
.filter(prop => prop.type !== 'propertySlot');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
click(id){
|
||||
let store = this.$store;
|
||||
// Check if there is a dialog open for this doc already
|
||||
let dialogFound;
|
||||
let dialogsToPop = 0;
|
||||
store.state.dialogStack.dialogs.forEach(dialog => {
|
||||
if (dialog.data && dialog.data._id === id){
|
||||
dialogFound = true;
|
||||
dialogsToPop = 0;
|
||||
} else {
|
||||
dialogsToPop += 1;
|
||||
}
|
||||
});
|
||||
if (dialogFound){
|
||||
// Pop dialogs until we get to it
|
||||
store.dispatch('popDialogStacks', dialogsToPop);
|
||||
} else {
|
||||
// Otherwise open it as a new dialog
|
||||
store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `breadcrumb-${id}`,
|
||||
data: {_id: id},
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.breadcrumbs {
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,9 @@
|
||||
/>
|
||||
</template>
|
||||
<template v-if="model">
|
||||
<template v-if="!editing && !embedded">
|
||||
<breadcrumbs :model="model" />
|
||||
</template>
|
||||
<v-fade-transition
|
||||
mode="out-in"
|
||||
>
|
||||
@@ -44,6 +47,14 @@
|
||||
:root="{collection: 'creatureProperties', id: model._id}"
|
||||
@selected="selectSubProperty"
|
||||
/>
|
||||
<v-btn
|
||||
text
|
||||
data-id="insert-creature-property-btn"
|
||||
@click="addProperty"
|
||||
>
|
||||
<v-icon>add</v-icon>
|
||||
Property
|
||||
</v-btn>
|
||||
</template>
|
||||
</template>
|
||||
<div
|
||||
@@ -83,6 +94,9 @@ import { assertEditPermission } from '/imports/api/creature/creaturePermissions.
|
||||
import { get, findLast } from 'lodash';
|
||||
import equipItem from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
||||
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
||||
import Breadcrumbs from '/imports/ui/creature/creatureProperties/Breadcrumbs.vue';
|
||||
|
||||
let formIndex = {};
|
||||
for (let key in propertyFormIndex){
|
||||
@@ -102,6 +116,7 @@ export default {
|
||||
DialogBase,
|
||||
PropertyToolbar,
|
||||
CreaturePropertiesTree,
|
||||
Breadcrumbs,
|
||||
},
|
||||
props: {
|
||||
_id: String,
|
||||
@@ -224,6 +239,30 @@ export default {
|
||||
elementId: `tree-node-${_id}`,
|
||||
data: {_id},
|
||||
});
|
||||
},
|
||||
addProperty(){
|
||||
let parentPropertyId = this.model._id;
|
||||
// Open the dialog to insert the property
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-creation-dialog',
|
||||
elementId: 'insert-creature-property-btn',
|
||||
callback(creatureProperty){
|
||||
if (!creatureProperty) return;
|
||||
// Get order and parent
|
||||
let parentRef = {
|
||||
id: parentPropertyId,
|
||||
collection: 'creatureProperties',
|
||||
};
|
||||
creatureProperty.order = getHighestOrder({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: parentRef.id,
|
||||
}) + 0.5;
|
||||
|
||||
// Insert the property
|
||||
let id = insertProperty.call({creatureProperty, parentRef});
|
||||
return `tree-node-${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -279,6 +279,7 @@
|
||||
pointer-events: initial;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
transition: all .3s ease;
|
||||
}
|
||||
.dialog > * {
|
||||
height: 100%;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
let dialogStack = {};
|
||||
dialogStack.dialogs = [];
|
||||
|
||||
@@ -25,7 +27,7 @@ const dialogStackStore = {
|
||||
if (!state.dialogs.length){
|
||||
throw new Meteor.Error('can\'t replace dialog if no dialogs are open');
|
||||
}
|
||||
state.dialogs.$set(0, {
|
||||
Vue.set(state.dialogs, state.dialogs.length - 1, {
|
||||
_id,
|
||||
component,
|
||||
data,
|
||||
@@ -57,8 +59,24 @@ const dialogStackStore = {
|
||||
} else {
|
||||
context.commit('popDialogStackMutation', result);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
popDialogStacks(context, quantity){
|
||||
if (quantity <= 0) return;
|
||||
let iterationsLeft = quantity;
|
||||
let intervalId = setInterval(() => {
|
||||
if (history && history.state && history.state.openDialogs){
|
||||
context.commit('setCurrentResult');
|
||||
history.back();
|
||||
} else {
|
||||
context.commit('popDialogStackMutation');
|
||||
}
|
||||
iterationsLeft -= 1;
|
||||
if (iterationsLeft === 0){
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 150);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default dialogStackStore;
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
'setTabForCharacterSheet',
|
||||
{id: result, tab: 4}
|
||||
);
|
||||
this.$store.commit('setShowBuildDialog', true);
|
||||
this.$store.commit('setShowDetailsDialog', true);
|
||||
this.$router.push({ path: `/character/${result}`});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
class="step-1"
|
||||
>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Add Library Content
|
||||
Property Type
|
||||
</v-toolbar-title>
|
||||
<property-selector
|
||||
slot="unwrapped-content"
|
||||
|
||||
@@ -13,7 +13,7 @@ const store = new Vuex.Store({
|
||||
rightDrawer: undefined,
|
||||
pageTitle: undefined,
|
||||
characterSheetTabs: {},
|
||||
showBuildDialog: false,
|
||||
showDetailsDialog: false,
|
||||
},
|
||||
getters: {
|
||||
// ...
|
||||
@@ -45,8 +45,8 @@ const store = new Vuex.Store({
|
||||
setTabForCharacterSheet(state, {tab, id}){
|
||||
Vue.set(state.characterSheetTabs, id, tab);
|
||||
},
|
||||
setShowBuildDialog(state, value){
|
||||
state.showBuildDialog = value;
|
||||
setShowDetailsDialog(state, value){
|
||||
state.showDetailsDialog = value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user