Implemented Reference properties
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||
|
||||
const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
name: 'creatureProperties.insertPropertyFromLibraryNode',
|
||||
@@ -54,6 +55,7 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
|
||||
// Fetch the library node and its decendents, provided they have not been
|
||||
// removed
|
||||
// TODO: Check permission to read the library this node is in
|
||||
let node = LibraryNodes.findOne({
|
||||
_id: nodeId,
|
||||
removed: {$ne: true},
|
||||
@@ -65,6 +67,9 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
removed: {$ne: true},
|
||||
}).fetch();
|
||||
|
||||
// Convert all references into actual nodes
|
||||
nodes = reifyNodeReferences(nodes);
|
||||
|
||||
// The root node is first in the array of nodes
|
||||
// It must get the first generated ID to prevent flickering
|
||||
nodes = [node, ...nodes];
|
||||
@@ -115,4 +120,95 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
},
|
||||
});
|
||||
|
||||
// Covert node references into actual nodes
|
||||
// TODO: check permissions for each library a reference node references
|
||||
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
||||
depth += 1;
|
||||
// New nodes added this function
|
||||
let newNodes = [];
|
||||
|
||||
// Filter out the reference nodes we replace
|
||||
let resultingNodes = nodes.filter(node => {
|
||||
|
||||
// We have already visited this ref and replaced it
|
||||
if (visitedRefs.has(node._id)) return false;
|
||||
|
||||
// Already replaced an ancestor node
|
||||
for (let i; i < node.ancestors.length; i++){
|
||||
if (visitedRefs.has(node.ancestors[i].id)) return false;
|
||||
}
|
||||
|
||||
// This isn't a reference node, continue as normal
|
||||
if (node.type !== 'reference') return true;
|
||||
|
||||
// We have gone too deep, keep the reference node as an error
|
||||
if (depth > 10){
|
||||
if (Meteor.isClient) console.warn('Reference depth limit exceeded');
|
||||
node.cache = {error: 'Reference depth limit exceeded'};
|
||||
return true;
|
||||
}
|
||||
|
||||
let referencedNode
|
||||
try {
|
||||
referencedNode = fetchDocByRef(node.ref);
|
||||
referencedNode.order = node.order;
|
||||
// We are definitely replacing this node, so add it to the list
|
||||
visitedRefs.add(node._id);
|
||||
} catch (e){
|
||||
node.cache = {error: e.reason || e.message || e.toString()};
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get all the descendants of the referenced node
|
||||
let descendents = LibraryNodes.find({
|
||||
'ancestors.id': referencedNode._id,
|
||||
removed: {$ne: true},
|
||||
}, {
|
||||
sort: {order: 1},
|
||||
}).fetch();
|
||||
|
||||
// We are adding the referenced node and its descendants
|
||||
let addedNodes = [referencedNode, ...descendents];
|
||||
|
||||
// re-map all the ancestors to parent the new sub-tree into our existing
|
||||
// node tree
|
||||
setLineageOfDocs({
|
||||
docArray: addedNodes,
|
||||
newAncestry: node.ancestors,
|
||||
oldParent: referencedNode.parent,
|
||||
});
|
||||
|
||||
// Remove all the looped references and descendents from the new nodes
|
||||
// We can't rely on the reify recursion to do this, since the IDs are
|
||||
// getting renewed before it is called
|
||||
addedNodes = addedNodes.filter(node => {
|
||||
// Exclude removed referenced
|
||||
if (visitedRefs.has(node._id)) return false;
|
||||
|
||||
// Exclude descendants of removed references
|
||||
for (let i; i < node.ancestors.length; i++){
|
||||
if (visitedRefs.has(node.ancestors[i].id)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Give the new referenced sub-tree new ids
|
||||
renewDocIds({
|
||||
docArray: addedNodes,
|
||||
});
|
||||
|
||||
// Reify the subtree as well with recursion
|
||||
addedNodes = reifyNodeReferences(addedNodes, visitedRefs, depth);
|
||||
|
||||
// Store the new nodes from this inner loop without altering the array
|
||||
// we are looping over
|
||||
newNodes.push(...addedNodes);
|
||||
});
|
||||
|
||||
// We are done filtering the array, we can add the new nodes to it
|
||||
resultingNodes.push(...newNodes);
|
||||
|
||||
return resultingNodes;
|
||||
}
|
||||
|
||||
export default insertPropertyFromLibraryNode;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
import '/imports/api/library/methods/index.js';
|
||||
import { updateReferenceNodeWork } from '/imports/api/library/methods/updateReferenceNode.js';
|
||||
|
||||
let LibraryNodes = new Mongo.Collection('libraryNodes');
|
||||
|
||||
@@ -76,7 +77,12 @@ const insertNode = new ValidatedMethod({
|
||||
run(libraryNode) {
|
||||
delete libraryNode._id;
|
||||
assertNodeEditPermission(libraryNode, this.userId);
|
||||
return LibraryNodes.insert(libraryNode);
|
||||
let nodeId = LibraryNodes.insert(libraryNode);
|
||||
if (libraryNode.type == 'reference'){
|
||||
libraryNode._id = nodeId;
|
||||
updateReferenceNodeWork(libraryNode, this.userId);
|
||||
}
|
||||
return nodeId;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -109,9 +115,14 @@ const updateLibraryNode = new ValidatedMethod({
|
||||
} else {
|
||||
modifier = {$set: {[pathString]: value}};
|
||||
}
|
||||
return LibraryNodes.update(_id, modifier, {
|
||||
let numUpdated = LibraryNodes.update(_id, modifier, {
|
||||
selector: {type: node.type},
|
||||
});
|
||||
if (node.type == 'reference'){
|
||||
node = LibraryNodes.findOne(_id);
|
||||
updateReferenceNodeWork(node, this.userId);
|
||||
}
|
||||
return numUpdated;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import '/imports/api/library/methods/duplicateLibraryNode.js';
|
||||
import '/imports/api/library/methods/updateReferenceNode.js';
|
||||
|
||||
67
app/imports/api/library/methods/updateReferenceNode.js
Normal file
67
app/imports/api/library/methods/updateReferenceNode.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||
import {
|
||||
assertDocEditPermission,
|
||||
assertViewPermission,
|
||||
} from '/imports/api/sharing/sharingPermissions.js';
|
||||
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||
|
||||
const updateReferenceNode = new ValidatedMethod({
|
||||
name: 'libraryNodes.updateReferenceNode',
|
||||
validate: new SimpleSchema({
|
||||
_id: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
}
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({_id}) {
|
||||
let userId = this.userId;
|
||||
let node = LibraryNodes.findOne(_id);
|
||||
assertDocEditPermission(node, userId);
|
||||
updateReferenceNodeWork(node, userId);
|
||||
},
|
||||
});
|
||||
|
||||
function writeCache(_id, cache){
|
||||
LibraryNodes.update(_id, {$set: {cache}}, {
|
||||
selector: {type: 'reference'},
|
||||
});
|
||||
}
|
||||
|
||||
function updateReferenceNodeWork(node, userId){
|
||||
let cache = {}
|
||||
if (!node.ref){
|
||||
writeCache(node._id, cache);
|
||||
return;
|
||||
}
|
||||
let doc, library;
|
||||
try {
|
||||
doc = fetchDocByRef(node.ref);
|
||||
if (doc.removed) throw 'Property has been deleted';
|
||||
if (doc.ancestors[0].id !== node.ancestors[0].id){
|
||||
library = fetchDocByRef(doc.ancestors[0]);
|
||||
assertViewPermission(library, userId)
|
||||
}
|
||||
} catch(e){
|
||||
cache = {error: e.reason || e.message || e.toString()}
|
||||
writeCache(node._id, cache);
|
||||
return;
|
||||
}
|
||||
cache = {
|
||||
node: {name: doc.name, type: doc.type},
|
||||
};
|
||||
if (library){
|
||||
cache.library = {name: library.name};
|
||||
}
|
||||
writeCache(node._id, cache);
|
||||
}
|
||||
|
||||
export default updateReferenceNode;
|
||||
export { updateReferenceNodeWork }
|
||||
47
app/imports/api/properties/References.js
Normal file
47
app/imports/api/properties/References.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
|
||||
let ReferenceSchema = new SimpleSchema({
|
||||
ref: {
|
||||
type: Object,
|
||||
defaultValue: {},
|
||||
},
|
||||
'ref.id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
'ref.collection': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
// Denormalised store of referenced property's details
|
||||
cache: {
|
||||
type: Object,
|
||||
defaultValue: {},
|
||||
},
|
||||
'cache.error': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'cache.node': {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
'cache.node.name': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'cache.node.type': {
|
||||
type: String,
|
||||
},
|
||||
'cache.library': {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
'cache.library.name': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
export { ReferenceSchema };
|
||||
@@ -15,6 +15,7 @@ import { FolderSchema } from '/imports/api/properties/Folders.js';
|
||||
import { ComputedOnlyItemSchema } from '/imports/api/properties/Items.js';
|
||||
import { ComputedOnlyNoteSchema } from '/imports/api/properties/Notes.js';
|
||||
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
||||
import { ComputedOnlyRollSchema } from '/imports/api/properties/Rolls.js';
|
||||
import { ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
|
||||
import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills.js';
|
||||
@@ -42,6 +43,7 @@ const propertySchemasIndex = {
|
||||
note: ComputedOnlyNoteSchema,
|
||||
proficiency: ProficiencySchema,
|
||||
propertySlot: ComputedOnlySlotSchema,
|
||||
reference: ReferenceSchema,
|
||||
roll: ComputedOnlyRollSchema,
|
||||
savingThrow: ComputedOnlySavingThrowSchema,
|
||||
skill: ComputedOnlySkillSchema,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { FolderSchema } from '/imports/api/properties/Folders.js';
|
||||
import { ComputedItemSchema } from '/imports/api/properties/Items.js';
|
||||
import { ComputedNoteSchema } from '/imports/api/properties/Notes.js';
|
||||
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
|
||||
import { ReferenceSchema } from '/imports/api/properties/References.js';
|
||||
import { ComputedRollSchema } from '/imports/api/properties/Rolls.js';
|
||||
import { ComputedSavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
|
||||
import { ComputedSkillSchema } from '/imports/api/properties/Skills.js';
|
||||
@@ -40,6 +41,7 @@ const propertySchemasIndex = {
|
||||
note: ComputedNoteSchema,
|
||||
proficiency: ProficiencySchema,
|
||||
propertySlot: ComputedSlotSchema,
|
||||
reference: ReferenceSchema,
|
||||
roll: ComputedRollSchema,
|
||||
savingThrow: ComputedSavingThrowSchema,
|
||||
skill: ComputedSkillSchema,
|
||||
|
||||
@@ -13,6 +13,7 @@ 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 { ReferenceSchema } from '/imports/api/properties/References.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';
|
||||
@@ -40,6 +41,7 @@ const propertySchemasIndex = {
|
||||
note: NoteSchema,
|
||||
proficiency: ProficiencySchema,
|
||||
propertySlot: SlotSchema,
|
||||
reference: ReferenceSchema,
|
||||
roll: RollSchema,
|
||||
savingThrow: SavingThrowSchema,
|
||||
skill: SkillSchema,
|
||||
|
||||
@@ -12,7 +12,7 @@ function assertIdValid(userId){
|
||||
function assertdocExists(doc){
|
||||
if (!doc){
|
||||
throw new Meteor.Error('Permission denied',
|
||||
'No such document exists');
|
||||
'Permission denied: No such document exists');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,11 @@ const PROPERTIES = Object.freeze({
|
||||
icon: '$vuetify.icons.roll',
|
||||
name: 'Roll'
|
||||
},
|
||||
reference: {
|
||||
icon: 'link',
|
||||
name: 'Reference',
|
||||
libraryOnly: true,
|
||||
},
|
||||
savingThrow: {
|
||||
icon: '$vuetify.icons.saving_throw',
|
||||
name: 'Saving throw'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template lang="html">
|
||||
<selectable-property-dialog
|
||||
:value="forcedType || type"
|
||||
no-library-only-props
|
||||
@input="e => type = e"
|
||||
>
|
||||
<creature-property-insert-form
|
||||
|
||||
@@ -14,6 +14,7 @@ import LibraryNodeCreationDialog from '/imports/ui/library/LibraryNodeCreationDi
|
||||
import LibraryNodeDialog from '/imports/ui/library/LibraryNodeDialog.vue';
|
||||
import MoveLibraryNodeDialog from '/imports/ui/library/MoveLibraryNodeDialog.vue'
|
||||
import SelectCreaturesDialog from '/imports/ui/tabletop/SelectCreaturesDialog.vue';
|
||||
import SelectLibraryNodeDialog from '/imports/ui/library/SelectLibraryNodeDialog.vue';
|
||||
import ShareDialog from '/imports/ui/sharing/ShareDialog.vue';
|
||||
import SlotDetailsDialog from '/imports/ui/creature/slots/SlotDetailsDialog.vue';
|
||||
import SlotFillDialog from '/imports/ui/creature/slots/SlotFillDialog.vue';
|
||||
@@ -37,7 +38,8 @@ export default {
|
||||
LibraryNodeDialog,
|
||||
MoveLibraryNodeDialog,
|
||||
SelectCreaturesDialog,
|
||||
ShareDialog,
|
||||
SelectLibraryNodeDialog,
|
||||
ShareDialog,
|
||||
SlotDetailsDialog,
|
||||
SlotFillDialog,
|
||||
TierTooLowDialog,
|
||||
|
||||
47
app/imports/ui/library/SelectLibraryNodeDialog.vue
Normal file
47
app/imports/ui/library/SelectLibraryNodeDialog.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template lang="html">
|
||||
<dialog-base>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Select Library Property
|
||||
</v-toolbar-title>
|
||||
<library-and-node
|
||||
slot="unwrapped-content"
|
||||
style="height: 100%;"
|
||||
selection
|
||||
@selected="val => node = val"
|
||||
/>
|
||||
<template slot="actions">
|
||||
<v-btn
|
||||
text
|
||||
color="primary"
|
||||
@click="$store.dispatch('popDialogStack')"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
color="primary"
|
||||
@click="$store.dispatch('popDialogStack', node)"
|
||||
>
|
||||
Select
|
||||
</v-btn>
|
||||
</template>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import LibraryAndNode from '/imports/ui/library/LibraryAndNode.vue';
|
||||
export default {
|
||||
components: {
|
||||
DialogBase,
|
||||
LibraryAndNode,
|
||||
},
|
||||
data(){return {
|
||||
node: undefined,
|
||||
};},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
69
app/imports/ui/properties/forms/ReferenceForm.vue
Normal file
69
app/imports/ui/properties/forms/ReferenceForm.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template lang="html">
|
||||
<div class="folder-form layout justify-start wrap">
|
||||
<v-text-field
|
||||
label="Linked Property"
|
||||
style="flex-basis: 300px;"
|
||||
readonly
|
||||
outlined
|
||||
persistent-hint
|
||||
:loading="linkLoading"
|
||||
:value="
|
||||
model.cache.node && model.cache.node.name ||
|
||||
model.ref && model.ref.id
|
||||
"
|
||||
:hint="model.cache.library && model.cache.library.name"
|
||||
:error-messages="model.cache.error || errors.ref"
|
||||
prepend-inner-icon="link"
|
||||
append-icon="refresh"
|
||||
data-id="change-ref"
|
||||
@click="changeReference"
|
||||
@click:prepend-inner="changeReference"
|
||||
@click:append="updateReferenceNode"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
|
||||
import updateReferenceNode from '/imports/api/library/methods/updateReferenceNode.js';
|
||||
|
||||
export default {
|
||||
mixins: [propertyFormMixin],
|
||||
data(){return {
|
||||
linkLoading: false,
|
||||
}},
|
||||
methods: {
|
||||
changeReference(){
|
||||
let that = this;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'select-library-node-dialog',
|
||||
elementId: 'change-ref',
|
||||
callback(node){
|
||||
if (!node) return;
|
||||
that.linkLoading = true;
|
||||
that.$emit('change', {
|
||||
path: ['ref'],
|
||||
value: {
|
||||
id: node._id,
|
||||
collection: 'libraryNodes',
|
||||
},
|
||||
ack(){
|
||||
that.linkLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
updateReferenceNode(){
|
||||
if (!this.model._id) return;
|
||||
this.linkLoading = true;
|
||||
updateReferenceNode.call({_id: this.model._id}, () => {
|
||||
this.linkLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -14,6 +14,7 @@ import FolderForm from '/imports/ui/properties/forms/FolderForm.vue';
|
||||
import ItemForm from '/imports/ui/properties/forms/ItemForm.vue';
|
||||
import NoteForm from '/imports/ui/properties/forms/NoteForm.vue';
|
||||
import ProficiencyForm from '/imports/ui/properties/forms/ProficiencyForm.vue';
|
||||
import ReferenceForm from '/imports/ui/properties/forms/ReferenceForm.vue';
|
||||
import RollForm from '/imports/ui/properties/forms/RollForm.vue';
|
||||
import SavingThrowForm from '/imports/ui/properties/forms/SavingThrowForm.vue';
|
||||
import SkillForm from '/imports/ui/properties/forms/SkillForm.vue';
|
||||
@@ -41,6 +42,7 @@ export default {
|
||||
note: NoteForm,
|
||||
proficiency: ProficiencyForm,
|
||||
propertySlot: SlotForm,
|
||||
reference: ReferenceForm,
|
||||
roll: RollForm,
|
||||
savingThrow: SavingThrowForm,
|
||||
skill: SkillForm,
|
||||
|
||||
@@ -9,33 +9,35 @@
|
||||
wrap
|
||||
fill-height
|
||||
>
|
||||
<v-flex
|
||||
v-for="(property, type) in PROPERTIES"
|
||||
:key="type"
|
||||
sm4
|
||||
xs6
|
||||
>
|
||||
<v-card
|
||||
hover
|
||||
style="height: 100%; overflow: hidden;"
|
||||
@click="$emit('select', type)"
|
||||
<template v-for="(property, type) in PROPERTIES">
|
||||
<v-flex
|
||||
v-if="!noLibraryOnlyProps || !property.libraryOnly"
|
||||
:key="type"
|
||||
sm4
|
||||
xs6
|
||||
>
|
||||
<div
|
||||
class="layout align-center justify-center"
|
||||
style="min-height: 70px;"
|
||||
<v-card
|
||||
hover
|
||||
style="height: 100%; overflow: hidden;"
|
||||
@click="$emit('select', type)"
|
||||
>
|
||||
<v-icon x-large>
|
||||
{{ property.icon }}
|
||||
</v-icon>
|
||||
</div>
|
||||
<h3
|
||||
class="subtitle pb-3"
|
||||
style="text-align: center;"
|
||||
>
|
||||
{{ property.name }}
|
||||
</h3>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<div
|
||||
class="layout align-center justify-center"
|
||||
style="min-height: 70px;"
|
||||
>
|
||||
<v-icon x-large>
|
||||
{{ property.icon }}
|
||||
</v-icon>
|
||||
</div>
|
||||
<h3
|
||||
class="subtitle pb-3"
|
||||
style="text-align: center;"
|
||||
>
|
||||
{{ property.name }}
|
||||
</h3>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
</template>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</div>
|
||||
@@ -43,8 +45,12 @@
|
||||
|
||||
<script lang="js">
|
||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
||||
|
||||
export default {
|
||||
data(){return {
|
||||
props: {
|
||||
noLibraryOnlyProps: Boolean,
|
||||
},
|
||||
data(){ return {
|
||||
PROPERTIES,
|
||||
};},
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
</v-toolbar-title>
|
||||
<property-selector
|
||||
slot="unwrapped-content"
|
||||
:no-library-only-props="noLibraryOnlyProps"
|
||||
@select="type => $emit('input', type)"
|
||||
/>
|
||||
</dialog-base>
|
||||
@@ -34,6 +35,7 @@ export default {
|
||||
PropertySelector,
|
||||
},
|
||||
props: {
|
||||
noLibraryOnlyProps: Boolean,
|
||||
value: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<template lang="html">
|
||||
<div class="layout align-center justify-start">
|
||||
<property-icon
|
||||
v-if="!hideIcon"
|
||||
class="mr-2"
|
||||
:model="model"
|
||||
:color="model.color"
|
||||
:class="selected && 'primary--text'"
|
||||
/>
|
||||
<div class="text-no-wrap text-truncate">
|
||||
{{ model.cache.node && model.cache.node.name || title }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import treeNodeViewMixin from '/imports/ui/properties/treeNodeViews/treeNodeViewMixin.js';
|
||||
|
||||
export default {
|
||||
mixins: [treeNodeViewMixin],
|
||||
}
|
||||
</script>
|
||||
@@ -4,6 +4,7 @@ import ItemTreeNode from '/imports/ui/properties/treeNodeViews/ItemTreeNode.vue'
|
||||
import DamageTreeNode from '/imports/ui/properties/treeNodeViews/DamageTreeNode.vue';
|
||||
import EffectTreeNode from '/imports/ui/properties/treeNodeViews/EffectTreeNode.vue';
|
||||
import ClassLevelTreeNode from '/imports/ui/properties/treeNodeViews/ClassLevelTreeNode.vue';
|
||||
import ReferenceTreeNode from '/imports/ui/properties/treeNodeViews/ReferenceTreeNode.vue';
|
||||
|
||||
export default {
|
||||
default: DefaultTreeNode,
|
||||
@@ -12,4 +13,5 @@ export default {
|
||||
damage: DamageTreeNode,
|
||||
effect: EffectTreeNode,
|
||||
item: ItemTreeNode,
|
||||
reference: ReferenceTreeNode,
|
||||
}
|
||||
|
||||
30
app/imports/ui/properties/viewers/ReferenceViewer.vue
Normal file
30
app/imports/ui/properties/viewers/ReferenceViewer.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template lang="html">
|
||||
<div class="reference-viewer">
|
||||
<property-field
|
||||
v-if="model.cache.error"
|
||||
name="Error"
|
||||
:value="model.cache.error"
|
||||
/>
|
||||
<property-field
|
||||
v-else-if="model.ref && model.ref.id"
|
||||
name="Linked Property"
|
||||
:value="model.cache.node && model.cache.node.name || model.ref.id"
|
||||
/>
|
||||
<property-field
|
||||
v-if="model.cache.library && model.cache.library.name"
|
||||
name="Library"
|
||||
:value="model.cache.library.name"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
|
||||
|
||||
export default {
|
||||
mixins: [propertyViewerMixin],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -14,6 +14,7 @@ import FolderViewer from '/imports/ui/properties/viewers/FolderViewer.vue';
|
||||
import ItemViewer from '/imports/ui/properties/viewers/ItemViewer.vue';
|
||||
import NoteViewer from '/imports/ui/properties/viewers/NoteViewer.vue';
|
||||
import ProficiencyViewer from '/imports/ui/properties/viewers/ProficiencyViewer.vue';
|
||||
import ReferenceViewer from '/imports/ui/properties/viewers/ReferenceViewer.vue';
|
||||
import RollViewer from '/imports/ui/properties/viewers/RollViewer.vue';
|
||||
import SkillViewer from '/imports/ui/properties/viewers/SkillViewer.vue';
|
||||
import SavingThrowViewer from '/imports/ui/properties/viewers/SavingThrowViewer.vue';
|
||||
@@ -42,6 +43,7 @@ export default {
|
||||
proficiency: ProficiencyViewer,
|
||||
propertySlot: SlotViewer,
|
||||
roll: RollViewer,
|
||||
reference: ReferenceViewer,
|
||||
savingThrow: SavingThrowViewer,
|
||||
slotFiller: SlotFillerViewer,
|
||||
skill: SkillViewer,
|
||||
|
||||
Reference in New Issue
Block a user