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 CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
|
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
|
||||||
import { assertUserHasPaidBenefits } from '/imports/api/users/patreon/tiers.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/removeCreature.js';
|
||||||
import '/imports/api/creature/restCreature.js';
|
import '/imports/api/creature/restCreature.js';
|
||||||
|
|
||||||
@@ -198,24 +199,29 @@ const insertCreature = new ValidatedMethod({
|
|||||||
let creatureId = Creatures.insert({
|
let creatureId = Creatures.insert({
|
||||||
owner: this.userId,
|
owner: this.userId,
|
||||||
});
|
});
|
||||||
CreatureProperties.insert({
|
|
||||||
slotTags: ['base'],
|
// Insert the default properties
|
||||||
quantityExpected: 1,
|
// Not batchInsert because we want the properties cleaned by the schema
|
||||||
type: 'propertySlot',
|
let baseId;
|
||||||
name: 'Base',
|
defaultCharacterProperties(creatureId).forEach(prop => {
|
||||||
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.',
|
let id = CreatureProperties.insert(prop);
|
||||||
hideWhenFull: true,
|
if (prop.name === 'Ruleset'){
|
||||||
parent: {collection: 'creatures', id: creatureId},
|
baseId = id;
|
||||||
ancestors: [{collection: 'creatures', id: creatureId}],
|
}
|
||||||
order: 0,
|
|
||||||
tags: [],
|
|
||||||
spaceLeft: 1,
|
|
||||||
totalFilled: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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();
|
this.unblock();
|
||||||
return creatureId;
|
return creatureId;
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateCreature = new ValidatedMethod({
|
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 { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||||
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
|
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||||
|
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.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'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equipping or unequipping an item will also change its parent
|
// Equipping or unequipping an item will also change its parent
|
||||||
const equipItem = new ValidatedMethod({
|
const equipItem = new ValidatedMethod({
|
||||||
@@ -50,8 +35,9 @@ const equipItem = new ValidatedMethod({
|
|||||||
}, {
|
}, {
|
||||||
selector: {type: 'item'},
|
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);
|
let parentRef = getParentRefByTag(creature._id, tag);
|
||||||
|
if (!parentRef) parentRef = {id: creature._id, collection: 'creatures'};
|
||||||
|
|
||||||
organizeDoc.call({
|
organizeDoc.call({
|
||||||
docRef: {
|
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 { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||||
import { getAncestry } from '/imports/api/parenting/parenting.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({
|
const insertProperty = new ValidatedMethod({
|
||||||
name: 'creatureProperties.insert',
|
name: 'creatureProperties.insert',
|
||||||
validate: null,
|
validate: new SimpleSchema({
|
||||||
|
creatureProperty: {
|
||||||
|
type: Object,
|
||||||
|
blackbox: true,
|
||||||
|
},
|
||||||
|
parentRef: RefSchema,
|
||||||
|
}).validator(),
|
||||||
mixins: [RateLimiterMixin],
|
mixins: [RateLimiterMixin],
|
||||||
rateLimit: {
|
rateLimit: {
|
||||||
numRequests: 5,
|
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}){
|
export function insertPropertyWork({property, creature}){
|
||||||
delete property._id;
|
delete property._id;
|
||||||
let _id = CreatureProperties.insert(property);
|
let _id = CreatureProperties.insert(property);
|
||||||
@@ -63,3 +153,4 @@ export function insertPropertyWork({property, creature}){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default insertProperty;
|
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',
|
inventory: 'inventory',
|
||||||
equipment: 'equipment',
|
equipment: 'equipment',
|
||||||
carried: 'carried',
|
carried: 'carried',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default INVENTORY_TAGS;
|
export default BUILT_IN_TAGS;
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:error-messages="errors"
|
:error-messages="errors"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
filled
|
outlined
|
||||||
v-on="on"
|
v-on="on"
|
||||||
@focus="focused = true"
|
@focus="focused = true"
|
||||||
@blur="focused = false"
|
@blur="focused = false"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
:menu-props="{auto: true, lazy: true}"
|
:menu-props="{auto: true, lazy: true}"
|
||||||
:search-input.sync="searchInput"
|
:search-input.sync="searchInput"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
filled
|
outlined
|
||||||
@change="customChange"
|
@change="customChange"
|
||||||
@focus="focused = true"
|
@focus="focused = true"
|
||||||
@blur="focused = false"
|
@blur="focused = false"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
:value="safeValue"
|
:value="safeValue"
|
||||||
:menu-props="{auto: true, lazy: true}"
|
:menu-props="{auto: true, lazy: true}"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
filled
|
outlined
|
||||||
@change="change"
|
@change="change"
|
||||||
@focus="focused = true"
|
@focus="focused = true"
|
||||||
@blur="focused = false"
|
@blur="focused = false"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
:value="safeValue"
|
:value="safeValue"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:auto-grow="autoGrow"
|
:auto-grow="autoGrow"
|
||||||
filled
|
outlined
|
||||||
@input="input"
|
@input="input"
|
||||||
@focus="focused = true"
|
@focus="focused = true"
|
||||||
@blur="focused = false"
|
@blur="focused = false"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
:error-messages="errors"
|
:error-messages="errors"
|
||||||
:value="safeValue"
|
:value="safeValue"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:filled="!regular"
|
:outlined="!regular"
|
||||||
@input="input"
|
@input="input"
|
||||||
@focus="focused = true"
|
@focus="focused = true"
|
||||||
@blur="focused = false"
|
@blur="focused = false"
|
||||||
|
|||||||
@@ -5,6 +5,12 @@
|
|||||||
:light="!isDark"
|
:light="!isDark"
|
||||||
:flat="flat"
|
:flat="flat"
|
||||||
>
|
>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
@click="back"
|
||||||
|
>
|
||||||
|
<v-icon>arrow_back</v-icon>
|
||||||
|
</v-btn>
|
||||||
<property-icon
|
<property-icon
|
||||||
:model="model"
|
:model="model"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
@@ -163,6 +169,9 @@ export default {
|
|||||||
colorChanged(value){
|
colorChanged(value){
|
||||||
this.$emit('color-changed', value);
|
this.$emit('color-changed', value);
|
||||||
},
|
},
|
||||||
|
back(){
|
||||||
|
this.$store.dispatch('popDialogStack');
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ let lastSnackbarId = 0;
|
|||||||
|
|
||||||
function snackbar(data) {
|
function snackbar(data) {
|
||||||
globalState.queue.push({
|
globalState.queue.push({
|
||||||
data,
|
data, //{text OR content, callback, callbackName} // content is logContent
|
||||||
id: ++lastSnackbarId,
|
id: ++lastSnackbarId,
|
||||||
enqueuedAt: new Date(),
|
enqueuedAt: new Date(),
|
||||||
shown: false,
|
shown: false,
|
||||||
|
|||||||
@@ -42,24 +42,12 @@
|
|||||||
<v-switch
|
<v-switch
|
||||||
label="Show spells tab"
|
label="Show spells tab"
|
||||||
:input-value="!model.settings.hideSpellsTab"
|
:input-value="!model.settings.hideSpellsTab"
|
||||||
@change="value => {
|
@change="changeHideSpellsTab"
|
||||||
$emit('change', {path: ['settings','hideSpellsTab'], value: !value});
|
|
||||||
$store.commit(
|
|
||||||
'setTabForCharacterSheet',
|
|
||||||
{id: model._id, tab: 0}
|
|
||||||
);
|
|
||||||
}"
|
|
||||||
/>
|
/>
|
||||||
<v-switch
|
<v-switch
|
||||||
label="Show tree tab"
|
label="Show tree tab"
|
||||||
:input-value="model.settings.showTreeTab"
|
:input-value="model.settings.showTreeTab"
|
||||||
@change="value => {
|
@change="changeShowTreeTab"
|
||||||
$emit('change', {path: ['settings','showTreeTab'], value: !!value});
|
|
||||||
$store.commit(
|
|
||||||
'setTabForCharacterSheet',
|
|
||||||
{id: model._id, tab: 0}
|
|
||||||
);
|
|
||||||
}"
|
|
||||||
/>
|
/>
|
||||||
<text-field
|
<text-field
|
||||||
label="Hit Dice reset multiplier"
|
label="Hit Dice reset multiplier"
|
||||||
@@ -129,6 +117,34 @@ export default {
|
|||||||
},
|
},
|
||||||
disabled: Boolean,
|
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>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
import Creatures from '/imports/api/creature/Creatures.js';
|
import Creatures from '/imports/api/creature/Creatures.js';
|
||||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||||
import removeCreature from '/imports/api/creature/removeCreature.js';
|
import removeCreature from '/imports/api/creature/removeCreature.js';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -61,12 +62,12 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
remove(){
|
remove(){
|
||||||
|
this.$router.push('/characterList');
|
||||||
|
this.$store.dispatch('popDialogStack');
|
||||||
removeCreature.call({charId: this.id}, (error) => {
|
removeCreature.call({charId: this.id}, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} else {
|
snackbar({text: error.message || error.toString()});
|
||||||
this.$router.push('/characterList');
|
|
||||||
this.$store.dispatch('popDialogStack');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
<script lang="js">
|
<script lang="js">
|
||||||
import LabeledFab from '/imports/ui/components/LabeledFab.vue';
|
import LabeledFab from '/imports/ui/components/LabeledFab.vue';
|
||||||
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
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 CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
@@ -176,9 +176,24 @@
|
|||||||
collection: CreatureProperties,
|
collection: CreatureProperties,
|
||||||
ancestorId: creatureId
|
ancestorId: creatureId
|
||||||
}) + 1;
|
}) + 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,
|
creatureProperty,
|
||||||
parentRef: {collection: 'creatures', id: creatureId},
|
creatureId,
|
||||||
|
tag: tagDetails.tag,
|
||||||
|
tagDefaultName: tagDetails.name,
|
||||||
});
|
});
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,9 +156,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted(){
|
||||||
if (this.$store.state.showBuildDialog){
|
if (this.$store.state.showDetailsDialog){
|
||||||
this.$store.commit('setShowBuildDialog', false);
|
this.$store.commit('setShowDetailsDialog', false);
|
||||||
this.showSlotDialog();
|
this.showCharacterForm();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
|||||||
import ContainerCard from '/imports/ui/properties/components/inventory/ContainerCard.vue';
|
import ContainerCard from '/imports/ui/properties/components/inventory/ContainerCard.vue';
|
||||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||||
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||||
import { getParentRefByTag } from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||||
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
|
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -183,10 +183,10 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
equipmentParentRef(){
|
equipmentParentRef(){
|
||||||
return getParentRefByTag(this.creatureId, INVENTORY_TAGS.equipment);
|
return getParentRefByTag(this.creatureId, BUILT_IN_TAGS.equipment);
|
||||||
},
|
},
|
||||||
carriedParentRef(){
|
carriedParentRef(){
|
||||||
return getParentRefByTag(this.creatureId, INVENTORY_TAGS.carried);
|
return getParentRefByTag(this.creatureId, BUILT_IN_TAGS.carried);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -434,7 +434,8 @@
|
|||||||
return getProperties(this.creature, {type: 'buff', applied: true});
|
return getProperties(this.creature, {type: 'buff', applied: true});
|
||||||
},
|
},
|
||||||
attacks(){
|
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({
|
attack.children = CreatureProperties.find({
|
||||||
'ancestors.id': attack._id,
|
'ancestors.id': attack._id,
|
||||||
removed: {$ne: true},
|
removed: {$ne: true},
|
||||||
@@ -444,7 +445,6 @@
|
|||||||
});
|
});
|
||||||
return attack;
|
return attack;
|
||||||
});
|
});
|
||||||
return props;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
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>
|
||||||
<template v-if="model">
|
<template v-if="model">
|
||||||
|
<template v-if="!editing && !embedded">
|
||||||
|
<breadcrumbs :model="model" />
|
||||||
|
</template>
|
||||||
<v-fade-transition
|
<v-fade-transition
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
>
|
>
|
||||||
@@ -44,6 +47,14 @@
|
|||||||
:root="{collection: 'creatureProperties', id: model._id}"
|
:root="{collection: 'creatureProperties', id: model._id}"
|
||||||
@selected="selectSubProperty"
|
@selected="selectSubProperty"
|
||||||
/>
|
/>
|
||||||
|
<v-btn
|
||||||
|
text
|
||||||
|
data-id="insert-creature-property-btn"
|
||||||
|
@click="addProperty"
|
||||||
|
>
|
||||||
|
<v-icon>add</v-icon>
|
||||||
|
Property
|
||||||
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<div
|
||||||
@@ -83,6 +94,9 @@ import { assertEditPermission } from '/imports/api/creature/creaturePermissions.
|
|||||||
import { get, findLast } from 'lodash';
|
import { get, findLast } from 'lodash';
|
||||||
import equipItem from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
import equipItem from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
||||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.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 = {};
|
let formIndex = {};
|
||||||
for (let key in propertyFormIndex){
|
for (let key in propertyFormIndex){
|
||||||
@@ -102,6 +116,7 @@ export default {
|
|||||||
DialogBase,
|
DialogBase,
|
||||||
PropertyToolbar,
|
PropertyToolbar,
|
||||||
CreaturePropertiesTree,
|
CreaturePropertiesTree,
|
||||||
|
Breadcrumbs,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
_id: String,
|
_id: String,
|
||||||
@@ -224,6 +239,30 @@ export default {
|
|||||||
elementId: `tree-node-${_id}`,
|
elementId: `tree-node-${_id}`,
|
||||||
data: {_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;
|
pointer-events: initial;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
transition: all .3s ease;
|
||||||
}
|
}
|
||||||
.dialog > * {
|
.dialog > * {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
let dialogStack = {};
|
let dialogStack = {};
|
||||||
dialogStack.dialogs = [];
|
dialogStack.dialogs = [];
|
||||||
|
|
||||||
@@ -25,7 +27,7 @@ const dialogStackStore = {
|
|||||||
if (!state.dialogs.length){
|
if (!state.dialogs.length){
|
||||||
throw new Meteor.Error('can\'t replace dialog if no dialogs are open');
|
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,
|
_id,
|
||||||
component,
|
component,
|
||||||
data,
|
data,
|
||||||
@@ -57,8 +59,24 @@ const dialogStackStore = {
|
|||||||
} else {
|
} else {
|
||||||
context.commit('popDialogStackMutation', result);
|
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;
|
export default dialogStackStore;
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
'setTabForCharacterSheet',
|
'setTabForCharacterSheet',
|
||||||
{id: result, tab: 4}
|
{id: result, tab: 4}
|
||||||
);
|
);
|
||||||
this.$store.commit('setShowBuildDialog', true);
|
this.$store.commit('setShowDetailsDialog', true);
|
||||||
this.$router.push({ path: `/character/${result}`});
|
this.$router.push({ path: `/character/${result}`});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
class="step-1"
|
class="step-1"
|
||||||
>
|
>
|
||||||
<v-toolbar-title slot="toolbar">
|
<v-toolbar-title slot="toolbar">
|
||||||
Add Library Content
|
Property Type
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
<property-selector
|
<property-selector
|
||||||
slot="unwrapped-content"
|
slot="unwrapped-content"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const store = new Vuex.Store({
|
|||||||
rightDrawer: undefined,
|
rightDrawer: undefined,
|
||||||
pageTitle: undefined,
|
pageTitle: undefined,
|
||||||
characterSheetTabs: {},
|
characterSheetTabs: {},
|
||||||
showBuildDialog: false,
|
showDetailsDialog: false,
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
// ...
|
// ...
|
||||||
@@ -45,8 +45,8 @@ const store = new Vuex.Store({
|
|||||||
setTabForCharacterSheet(state, {tab, id}){
|
setTabForCharacterSheet(state, {tab, id}){
|
||||||
Vue.set(state.characterSheetTabs, id, tab);
|
Vue.set(state.characterSheetTabs, id, tab);
|
||||||
},
|
},
|
||||||
setShowBuildDialog(state, value){
|
setShowDetailsDialog(state, value){
|
||||||
state.showBuildDialog = value;
|
state.showDetailsDialog = value;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user