Improved property viewers

Slot fill data included in library viewer
Breadcrumbs and children in lib view
breadcrumbs and children work on tree tab
This commit is contained in:
Stefan Zermatten
2023-06-13 12:48:35 +02:00
parent c580970d6d
commit 957aabcb82
10 changed files with 197 additions and 114 deletions

View File

@@ -13,7 +13,7 @@
<v-divider vertical /> <v-divider vertical />
<div <div
class="flex layout column" class="flex layout column"
style="background-color: inherit; overflow: hidden;" style="background-color: inherit; overflow: hidden; min-height: 100%;"
data-id="selected-node-card" data-id="selected-node-card"
> >
<slot name="detail" /> <slot name="detail" />

View File

@@ -46,6 +46,7 @@
:_id="selectedNodeId" :_id="selectedNodeId"
@removed="selectedNodeId = undefined" @removed="selectedNodeId = undefined"
@duplicated="id => selectedNodeId = id" @duplicated="id => selectedNodeId = id"
@select-sub-property="clickNode"
/> />
</template> </template>
</tree-detail-layout> </tree-detail-layout>

View File

@@ -4,11 +4,14 @@
:class="{'no-icons': noIcons}" :class="{'no-icons': noIcons}"
> >
<span <span
v-if="noLinks" v-if="noLinks || embedded || collection !== 'creatureProperties'"
> >
<v-icon> <v-icon v-if="collection === 'creatureProperties'">
mdi-account mdi-account
</v-icon> </v-icon>
<v-icon v-else-if="collection === 'libraryNodes'">
mdi-book-open-blank-variant
</v-icon>
</span> </span>
<a <a
v-else v-else
@@ -62,20 +65,29 @@
type: Object, type: Object,
required: true, required: true,
}, },
collection: {
type: String,
default: 'creatureProperties',
},
noLinks: Boolean, noLinks: Boolean,
noIcons: Boolean, noIcons: Boolean,
editing: Boolean, editing: Boolean,
embedded: Boolean,
}, },
computed:{ computed:{
props(){ props(){
return this.model.ancestors return this.model.ancestors
.slice(1) .slice(1)
.map(ref => fetchDocByRef(ref)) .map(ref => fetchDocByRef(ref))
.filter(prop => prop.type !== 'propertySlot'); .filter(prop => (this.collection !== 'creatureProperties' || prop.type !== 'propertySlot'));
}, },
}, },
methods: { methods: {
click(id){ click(id) {
if (this.embedded) {
this.$emit('select-sub-property', id);
return;
}
const store = this.$store; const store = this.$store;
// Check if there is a dialog open for this doc already // Check if there is a dialog open for this doc already
let dialogFound; let dialogFound;
@@ -92,9 +104,12 @@
// Pop dialogs until we get to it // Pop dialogs until we get to it
store.dispatch('popDialogStacks', dialogsToPop); store.dispatch('popDialogStacks', dialogsToPop);
} else { } else {
const component = this.collection === 'creatureProperties' ? 'creature-property-dialog'
: this.collection === 'libraryNodes' ? 'library-node-dialog'
: undefined;
// Otherwise open it as a new dialog // Otherwise open it as a new dialog
store.commit('pushDialogStack', { store.commit('pushDialogStack', {
component: 'creature-property-dialog', component,
elementId: `breadcrumb-${id}`, elementId: `breadcrumb-${id}`,
data: { data: {
_id: id, _id: id,
@@ -139,9 +154,6 @@
.breadcrumbs { .breadcrumbs {
margin-bottom: 16px; margin-bottom: 16px;
opacity: 0.8; opacity: 0.8;
}
.no-icons {
} }
</style> </style>

View File

@@ -16,12 +16,12 @@
<div <div
class="layout mb-4" class="layout mb-4"
> >
<template v-if="!embedded"> <breadcrumbs
<breadcrumbs :model="model"
:model="model" :editing="editing"
:editing="editing" :embedded="embedded"
/> @select-sub-property="selectSubProperty"
</template> />
<v-spacer /> <v-spacer />
<v-chip disabled> <v-chip disabled>
{{ typeName }} {{ typeName }}
@@ -43,31 +43,12 @@
@select-sub-property="selectSubProperty" @select-sub-property="selectSubProperty"
/> />
</div> </div>
<div v-else> <property-viewer
<component v-else
:is="model.type + 'Viewer'" :key="_id"
:key="_id" :model="model"
class="creature-property-viewer" @select-sub-property="selectSubProperty"
:model="model" />
/>
<v-row
v-show="!embedded && childrenLength"
class="mt-1"
dense
>
<property-field
name="Child properties"
:cols="{cols: 12}"
>
<creature-properties-tree
style="width: 100%;"
:root="{collection: 'creatureProperties', id: model._id}"
@length="childrenLength = $event"
@selected="selectSubProperty"
/>
</property-field>
</v-row>
</div>
</v-fade-transition> </v-fade-transition>
</template> </template>
<div <div
@@ -100,11 +81,7 @@ import Creatures from '/imports/api/creature/creatures/Creatures.js';
import PropertyToolbar from '/imports/client/ui/components/propertyToolbar.vue'; import PropertyToolbar from '/imports/client/ui/components/propertyToolbar.vue';
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue'; import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
import { getPropertyName } from '/imports/constants/PROPERTIES.js'; import { getPropertyName } from '/imports/constants/PROPERTIES.js';
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
import propertyFormIndex from '/imports/client/ui/properties/forms/shared/propertyFormIndex.js';
import PropertyForm from '/imports/client/ui/properties/PropertyForm.vue'; import PropertyForm from '/imports/client/ui/properties/PropertyForm.vue';
import propertyViewerIndex from '/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js';
import CreaturePropertiesTree from '/imports/client/ui/creature/creatureProperties/CreaturePropertiesTree.vue';
import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle.js'; import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { get, findLast } from 'lodash'; import { get, findLast } from 'lodash';
@@ -114,29 +91,15 @@ import { getHighestOrder } from '/imports/api/parenting/order.js';
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js'; import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
import Breadcrumbs from '/imports/client/ui/creature/creatureProperties/Breadcrumbs.vue'; import Breadcrumbs from '/imports/client/ui/creature/creatureProperties/Breadcrumbs.vue';
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js'; import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
import PropertyField from '/imports/client/ui/properties/viewers/shared/PropertyField.vue'; import PropertyViewer from '/imports/client/ui/properties/shared/PropertyViewer.vue';
let formIndex = {};
for (let key in propertyFormIndex){
formIndex[key + 'Form'] = propertyFormIndex[key];
}
let viewerIndex = {};
for (let key in propertyViewerIndex){
formIndex[key + 'Viewer'] = propertyViewerIndex[key];
}
export default { export default {
components: { components: {
...formIndex,
...viewerIndex,
PropertyForm, PropertyForm,
PropertyIcon,
DialogBase, DialogBase,
PropertyToolbar, PropertyToolbar,
CreaturePropertiesTree,
Breadcrumbs, Breadcrumbs,
PropertyField, PropertyViewer,
}, },
props: { props: {
_id: String, _id: String,
@@ -148,7 +111,6 @@ export default {
// CurrentId lags behind Id by one tick so that events fired by destroying // CurrentId lags behind Id by one tick so that events fired by destroying
// forms keyed to the old ID are applied before the new ID overwrites it // forms keyed to the old ID are applied before the new ID overwrites it
currentId: undefined, currentId: undefined,
childrenLength: 0,
}}, }},
meteor: { meteor: {
model(){ model(){

View File

@@ -67,7 +67,7 @@
<div <div
slot="detail" slot="detail"
data-id="selected-node-card" data-id="selected-node-card"
style="overflow: hidden;" style="overflow: hidden; min-height: 100%;"
> >
<library-node-dialog <library-node-dialog
:_id="selectedNodeId" :_id="selectedNodeId"

View File

@@ -14,6 +14,24 @@
@color-changed="value => change({path: ['color'], value})" @color-changed="value => change({path: ['color'], value})"
/> />
</template> </template>
<v-fade-transition>
<div
v-if="model"
class="layout mb-4"
>
<breadcrumbs
:model="model"
:editing="editing"
:embedded="embedded"
collection="libraryNodes"
@select-sub-property="selectSubProperty"
/>
<v-spacer />
<v-chip disabled>
{{ typeName }}
</v-chip>
</div>
</v-fade-transition>
<v-fade-transition <v-fade-transition
mode="out-in" mode="out-in"
> >
@@ -41,16 +59,13 @@
@add-child="addLibraryNode" @add-child="addLibraryNode"
@select-sub-property="selectSubProperty" @select-sub-property="selectSubProperty"
/> />
<component <property-viewer
:is="model.type + 'Viewer'" v-else-if="model"
v-else-if="model && !editing && $options.components[model.type + 'Viewer']"
:key="_id" :key="_id"
class="creature-property-viewer"
:model="model" :model="model"
collection="libraryNodes"
@select-sub-property="selectSubProperty"
/> />
<p v-else>
This property can't be viewed yet.
</p>
</v-fade-transition> </v-fade-transition>
<div <div
v-if="!embedded" v-if="!embedded"
@@ -96,9 +111,6 @@ import duplicateLibraryNode from '/imports/api/library/methods/duplicateLibraryN
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue'; import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
import PropertyToolbar from '/imports/client/ui/components/propertyToolbar.vue'; import PropertyToolbar from '/imports/client/ui/components/propertyToolbar.vue';
import { getPropertyName } from '/imports/constants/PROPERTIES.js'; import { getPropertyName } from '/imports/constants/PROPERTIES.js';
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
import propertyFormIndex from '/imports/client/ui/properties/forms/shared/propertyFormIndex.js';
import propertyViewerIndex from '/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js';
import { get } from 'lodash'; import { get } from 'lodash';
import { import {
assertDocEditPermission, assertDocCopyPermission assertDocEditPermission, assertDocCopyPermission
@@ -110,18 +122,16 @@ import copyLibraryNodeTo from '/imports/api/library/methods/copyLibraryNodeTo.js
import { getHighestOrder } from '/imports/api/parenting/order.js'; import { getHighestOrder } from '/imports/api/parenting/order.js';
import { getUserTier } from '/imports/api/users/patreon/tiers.js'; import { getUserTier } from '/imports/api/users/patreon/tiers.js';
import PropertyForm from '/imports/client/ui/properties/PropertyForm.vue'; import PropertyForm from '/imports/client/ui/properties/PropertyForm.vue';
let viewerIndex = {}; import PropertyViewer from '/imports/client/ui/properties/shared/PropertyViewer.vue';
for (let key in propertyViewerIndex){ import Breadcrumbs from '/imports/client/ui/creature/creatureProperties/Breadcrumbs.vue';
viewerIndex[key + 'Viewer'] = propertyViewerIndex[key];
}
export default { export default {
components: { components: {
PropertyToolbar, PropertyToolbar,
PropertyIcon, Breadcrumbs,
DialogBase, DialogBase,
PropertyForm, PropertyForm,
...viewerIndex, PropertyViewer,
}, },
props: { props: {
_id: String, _id: String,
@@ -140,6 +150,12 @@ export default {
currentId: undefined, currentId: undefined,
isLibraryForm: true, isLibraryForm: true,
}}, }},
computed: {
typeName(){
if (!this.model) return;
return getPropertyName(this.model.type)
},
},
watch: { watch: {
_id: { _id: {
immediate: true, immediate: true,
@@ -205,7 +221,7 @@ export default {
}); });
}, },
move(){ move(){
let that = this; const id = this._id;
this.$store.commit('pushDialogStack', { this.$store.commit('pushDialogStack', {
component: 'move-library-node-dialog', component: 'move-library-node-dialog',
elementId: 'property-toolbar-menu-button', elementId: 'property-toolbar-menu-button',
@@ -214,7 +230,7 @@ export default {
organizeDoc.call({ organizeDoc.call({
docRef: { docRef: {
collection: 'libraryNodes', collection: 'libraryNodes',
id: that._id, id,
}, },
parentRef: { parentRef: {
collection: 'libraryNodes', collection: 'libraryNodes',

View File

@@ -65,7 +65,7 @@
md="6" md="6"
> >
<smart-select <smart-select
label="Slot Fill Type" label="Slot fill type"
style="flex-basis: 300px;" style="flex-basis: 300px;"
clearable clearable
hint="The property type that this slot filler pretends to be when being searched for by a slot" hint="The property type that this slot filler pretends to be when being searched for by a slot"
@@ -80,7 +80,7 @@
md="6" md="6"
> >
<text-field <text-field
label="Slot Fill Quantity" label="Slot quantity filled"
type="number" type="number"
min="0" min="0"
hint="How many properties this counts as when filling a slot" hint="How many properties this counts as when filling a slot"

View File

@@ -1,11 +1,104 @@
<template lang="html"> <template lang="html">
<component <div
:is="model.type"
v-if="model && $options.components[model.type]" v-if="model && $options.components[model.type]"
:key="model._id"
class="property-viewer" class="property-viewer"
:model="model" >
/> <component
:is="model.type"
:key="model._id"
class="property-viewer"
:model="model"
/>
<v-row dense>
<template
v-if="collection == 'libraryNodes'"
>
<property-field
v-if="model.fillSlots || model.searchable"
name="Library Behavior"
>
<ul>
<li
v-if="model.fillSlots"
>
Can fill slots
</li>
<li v-if="model.searchable">
Searchable from character sheet
</li>
</ul>
</property-field>
<property-field
name="Slot fill type"
:value="slotFillTypeName"
/>
<property-field
name="Slot quantity filled"
:value="model.slotQuantityFilled"
/>
<property-field
name="Condition"
mono
:value="model.slotFillerCondition"
/>
<property-field
name="Condition Error Text"
:value="model.slotFillerConditionNote"
/>
<property-field
name="Library Tags"
:cols="{cols: 12}"
>
<div
v-if="model.libraryTags && model.libraryTags.length"
class="py-2"
>
<v-chip
v-for="(tag, index) in model.libraryTags"
:key="tag + index"
class="mr-1"
small
disabled
>
{{ tag }}
</v-chip>
</div>
</property-field>
</template>
<property-field
name="Tags"
:cols="{cols: 12}"
>
<div
v-if="model.tags && model.tags.length"
class="py-1"
>
<v-chip
v-for="(tag, index) in model.tags"
:key="tag + index"
class="mr-1"
disabled
small
>
{{ tag }}
</v-chip>
</div>
</property-field>
<property-field
v-show="childrenLength"
name="Child properties"
:cols="{cols: 12}"
>
<creature-properties-tree
style="width: 100%;"
:root="{collection, id: model._id}"
:collection="collection"
@length="childrenLength = $event"
@selected="selectSubProperty"
/>
</property-field>
</v-row>
</div>
<div v-else-if="model"> <div v-else-if="model">
This property can't be viewed yet. This property can't be viewed yet.
</div> </div>
@@ -13,15 +106,40 @@
<script lang="js"> <script lang="js">
import propertyViewerIndex from '/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js'; import propertyViewerIndex from '/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js';
import CreaturePropertiesTree from '/imports/client/ui/creature/creatureProperties/CreaturePropertiesTree.vue';
import PropertyField from '/imports/client/ui/properties/viewers/shared/PropertyField.vue';
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
export default { export default {
components: { components: {
...propertyViewerIndex, ...propertyViewerIndex,
CreaturePropertiesTree,
PropertyField,
}, },
props: { props: {
model: { model: {
type: Object, type: Object,
default: undefined default: undefined
}, },
collection: {
type: String,
default: 'creatureProperties'
},
},
data() {
return {
childrenLength: 0,
}
},
computed: {
slotFillTypeName() {
return getPropertyName(this.model.slotFillerType);
},
},
methods: {
selectSubProperty(_id) {
this.$emit('select-sub-property', _id);
},
}, },
} }
</script> </script>

View File

@@ -1,32 +1,6 @@
<template lang="html"> <template lang="html">
<div class="slot-filler-viewer"> <div class="slot-filler-viewer">
<v-row dense> <v-row dense>
<property-field
name="Type"
:value="slotTypeName"
/>
<property-field
name="Quantity filled"
:value="model.slotQuantityFilled"
/>
<property-field
v-if="!context.creatureId"
name="Condition"
mono
:value="model.slotFillerCondition"
/>
<property-field
v-if="model.picture"
name="Image"
:cols="{cols: 12}"
>
<v-img
:src="model.picture"
:height="200"
contain
class="slot-card-image"
/>
</property-field>
<property-field <property-field
v-if="model.description" v-if="model.description"
name="Description" name="Description"

View File

@@ -214,7 +214,7 @@ const PROPERTIES = Object.freeze({
export default PROPERTIES; export default PROPERTIES;
export function getPropertyName(type) { export function getPropertyName(type) {
return type && PROPERTIES[type] && PROPERTIES[type].name; return (type && PROPERTIES[type] && PROPERTIES[type].name) || type;
} }
export function getPropertyIcon(type) { export function getPropertyIcon(type) {