UI work to improve look and feel of Viewers

This commit is contained in:
Stefan Zermatten
2021-10-17 23:28:39 +02:00
parent 247353f0ed
commit bc6c857b6b
13 changed files with 420 additions and 196 deletions

View File

@@ -1,6 +1,7 @@
import _variable from './computeByType/computeVariable.js'; import _variable from './computeByType/computeVariable.js';
import action from './computeByType/computeAction.js'; import action from './computeByType/computeAction.js';
import attribute from './computeByType/computeAttribute.js'; import attribute from './computeByType/computeAttribute.js';
import skill from './computeByType/computeSkill.js';
import slot from './computeByType/computeSlot.js'; import slot from './computeByType/computeSlot.js';
import container from './computeByType/computeContainer.js'; import container from './computeByType/computeContainer.js';
@@ -9,6 +10,7 @@ export default Object.freeze({
action, action,
attribute, attribute,
container, container,
skill,
slot, slot,
spell: action, spell: action,
}); });

View File

@@ -0,0 +1,28 @@
// If we compute this skill without a variable name, it just
// uses its base value, proficiency, and damage since no effects can target it
// If this skill does have a variable name, it is recomputed later
// by computeVariableAsSkill
export default function computeSkill(computation, node){
const prop = node.data;
prop.proficiency = prop.baseProficiency;
let profBonus = computation.scope['proficiencyBonus']?.value || 0;
// Multiply the proficiency bonus by the actual proficiency
if(prop.proficiency === 0.49){
// Round down proficiency bonus in the special case
profBonus = Math.floor(profBonus * 0.5);
} else {
profBonus = Math.ceil(profBonus * prop.proficiency);
}
const ability = computation.scope[prop.ability];
prop.abilityMod = ability?.modifier || 0;
const base = prop.baseValue?.value || 0;
let result = base + prop.abilityMod + profBonus;
if (Number.isFinite(result)){
result = Math.floor(result);
}
prop.value = result;
}

View File

@@ -15,6 +15,7 @@ export default function(){
assert.equal(scope('strength').modifier, 1); assert.equal(scope('strength').modifier, 1);
assert.equal(prop('referencesDexId').value, 4); assert.equal(prop('referencesDexId').value, 4);
assert.equal(prop('hitDiceId').constitutionMod, 5); assert.equal(prop('hitDiceId').constitutionMod, 5);
assert.equal(prop('overriddenDexId').overridden, true, 'override properties with the same variable name');
assert.equal( assert.equal(
prop('parseErrorId').baseValue.value, null, prop('parseErrorId').baseValue.value, null,
'Parse errors should null the value' 'Parse errors should null the value'
@@ -44,11 +45,22 @@ var testProperties = [
calculation: '12' calculation: '12'
}, },
}), }),
clean({
_id: 'overriddenDexId',
variableName: 'dexterity',
type: 'attribute',
attributeType: 'ability',
order: 1,
baseValue: {
calculation: '15'
},
}),
clean({ clean({
_id: 'dexterityId', _id: 'dexterityId',
variableName: 'dexterity', variableName: 'dexterity',
type: 'attribute', type: 'attribute',
attributeType: 'ability', attributeType: 'ability',
order: 2,
baseValue: { baseValue: {
calculation: '15' calculation: '15'
}, },

View File

@@ -20,6 +20,7 @@ let SkillSchema = createPropertySchema({
regEx: VARIABLE_NAME_REGEX, regEx: VARIABLE_NAME_REGEX,
min: 2, min: 2,
max: STORAGE_LIMITS.variableName, max: STORAGE_LIMITS.variableName,
optional: true,
}, },
// The variable name of the ability this skill relies on // The variable name of the ability this skill relies on
ability: { ability: {

View File

@@ -11,7 +11,7 @@ const unaryOperator = {
}, },
resolve(fn, node, scope, context){ resolve(fn, node, scope, context){
const {result: rightNode} = resolve(fn, node.right, scope, context); const {result: rightNode} = resolve(fn, node.right, scope, context);
if (rightNode.parseType !== 'number'){ if (rightNode.valueType !== 'number'){
return { return {
result: unaryOperator.create({ result: unaryOperator.create({
operator: node.operator, operator: node.operator,

View File

@@ -39,13 +39,15 @@
}, },
meteor: { meteor: {
children(){ children(){
return nodesToTree({ const children = nodesToTree({
collection: CreatureProperties, collection: CreatureProperties,
ancestorId: this.root.id, ancestorId: this.root.id,
filter: this.filter, filter: this.filter,
includeFilteredDocAncestors: true, includeFilteredDocAncestors: true,
includeFilteredDocDescendants: true, includeFilteredDocDescendants: true,
}); });
this.$emit('length', children.length);
return children;
}, },
}, },
methods: { methods: {

View File

@@ -41,28 +41,34 @@
This property can't be viewed yet. This property can't be viewed yet.
</p> </p>
</v-fade-transition> </v-fade-transition>
<template v-if="!editing && !embedded"> <property-field
<v-divider class="my-2" /> v-if="!editing && !embedded && childrenLength"
name="Child properties"
:cols="{cols: 12}"
>
<creature-properties-tree <creature-properties-tree
v-if="!editing" style="width: 100%;"
:root="{collection: 'creatureProperties', id: model._id}" :root="{collection: 'creatureProperties', id: model._id}"
@length="childrenLength = $event"
@selected="selectSubProperty" @selected="selectSubProperty"
/> />
<v-btn </property-field>
text
data-id="insert-creature-property-btn"
@click="addProperty"
>
<v-icon>mdi-plus</v-icon>
Property
</v-btn>
</template>
</template> </template>
<div <div
v-if="!embedded" v-if="!embedded"
slot="actions" slot="actions"
class="layout justify-end" class="layout"
> >
<v-btn
v-if="!editing && !embedded"
text
data-id="insert-creature-property-btn"
@click="addProperty"
>
<v-icon>mdi-plus</v-icon>
Property
</v-btn>
<v-spacer />
<v-btn <v-btn
text text
@click="$store.dispatch('popDialogStack')" @click="$store.dispatch('popDialogStack')"
@@ -99,6 +105,7 @@ 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/ui/creature/creatureProperties/Breadcrumbs.vue'; import Breadcrumbs from '/imports/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/ui/properties/viewers/shared/PropertyField.vue';
let formIndex = {}; let formIndex = {};
for (let key in propertyFormIndex){ for (let key in propertyFormIndex){
@@ -119,6 +126,7 @@ export default {
PropertyToolbar, PropertyToolbar,
CreaturePropertiesTree, CreaturePropertiesTree,
Breadcrumbs, Breadcrumbs,
PropertyField,
}, },
props: { props: {
_id: String, _id: String,
@@ -130,6 +138,7 @@ 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

@@ -70,8 +70,8 @@
/> />
</template> </template>
<template v-if="model.summary"> <template v-if="model.summary">
<property-description <markdown-text
:model="model.summary" :markdown="model.summary.value || model.summary.text"
/> />
</template> </template>
</div> </div>
@@ -84,14 +84,14 @@ import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import doAction from '/imports/api/engine/actions/doAction.js'; import doAction from '/imports/api/engine/actions/doAction.js';
import AttributeConsumedView from '/imports/ui/properties/components/actions/AttributeConsumedView.vue'; import AttributeConsumedView from '/imports/ui/properties/components/actions/AttributeConsumedView.vue';
import ItemConsumedView from '/imports/ui/properties/components/actions/ItemConsumedView.vue'; import ItemConsumedView from '/imports/ui/properties/components/actions/ItemConsumedView.vue';
import PropertyDescription from '/imports/ui/properties/viewers/shared/PropertyDescription.vue';
import PropertyIcon from '/imports/ui/properties/shared/PropertyIcon.vue'; import PropertyIcon from '/imports/ui/properties/shared/PropertyIcon.vue';
import MarkdownText from '/imports/ui/components/MarkdownText.vue';
export default { export default {
components: { components: {
AttributeConsumedView, AttributeConsumedView,
ItemConsumedView, ItemConsumedView,
PropertyDescription, MarkdownText,
PropertyIcon, PropertyIcon,
}, },
inject: { inject: {

View File

@@ -4,10 +4,10 @@
@click="click" @click="click"
> >
<div class="layout align-center"> <div class="layout align-center">
<v-card-title class="value text-h4"> <v-card-title class="value text-h4 flex-shrink-0">
{{ computedValue }} {{ computedValue }}
</v-card-title> </v-card-title>
<v-card-title class="name text-subtitle-1 text-truncate pl-0"> <v-card-title class="name text-subtitle-1 text-truncate d-block pl-0">
{{ model.name }} {{ model.name }}
</v-card-title> </v-card-title>
</div> </div>
@@ -28,14 +28,10 @@
return this.$listeners && !!this.$listeners.click return this.$listeners && !!this.$listeners.click
}, },
computedValue(){ computedValue(){
if (this.model.type === 'attribute'){ if (this.model.attributeType === 'modifier' || this.model.type === 'skill'){
if (this.model.attributeType === 'modifier'){ return numberToSignedString(this.model.value);
return numberToSignedString(this.model.value);
} else {
return this.model.value
}
} else { } else {
return this.model.value; return this.model.value
} }
} }
}, },

View File

@@ -1,10 +1,16 @@
<template lang="html"> <template lang="html">
<div class="attribute-viewer"> <div class="attribute-viewer">
<v-layout <v-row
column dense
align-center align="stretch"
justify="center"
justify-sm="start"
> >
<v-layout v-if="model.value !== undefined"> <property-field
:name="model.damage !== undefined ? 'Value / Total': 'Value'"
center
>
<v-spacer />
<div <div
class="text-h4 mr-3" class="text-h4 mr-3"
> >
@@ -17,93 +23,171 @@
{{ model.value }} {{ model.value }}
</div> </div>
</div> </div>
<v-spacer />
<increment-button <increment-button
v-if="context.creatureId" v-if="context.creatureId"
icon
large
outlined outlined
icon
tile
color="primary" color="primary"
:value="model.value" :value="model.value"
@change="damageProperty" @change="damageProperty"
> >
<v-icon>$vuetify.icons.abacus</v-icon> <v-icon>
$vuetify.icons.abacus
</v-icon>
</increment-button> </increment-button>
</v-layout> </property-field>
<div <property-field
v-if="model.modifier !== undefined" v-if="model.modifier !== undefined"
class="text-h6" name="Modifier"
center
:value="isFinite(model.modifier) ?
numberToSignedString(model.modifier) :
model.modifier"
> >
{{ numberToSignedString(model.modifier) }} <div class="text-h6">
</div> {{ numberToSignedString(model.modifier) }}
</v-layout> </div>
<div> </property-field>
<property-name :value="model.name" /> <property-field
<property-variable-name :value="model.variableName" /> name="Variable Name"
</div> :value="model.variableName"
<property-field
v-if="model.attributeType === 'hitDice' && model.hitDiceSize"
name="Hit dice size"
:value="model.hitDiceSize"
/>
<property-field
v-if="reset && model.attributeType !== 'hitDice'"
name="Reset"
:value="reset"
/>
<property-description
:model="model.description"
/>
<v-list>
<attribute-effect
v-for="effect in baseEffects"
:key="effect._id"
:model="effect"
:hide-breadcrumbs="effect._id === model._id"
:data-id="effect._id"
@click="effect._id !== model._id && clickEffect(effect._id)"
/> />
<attribute-effect <property-field
v-for="effect in effects" name="Type"
:key="effect._id" :value="attributeTypes[model.attributeType]"
:model="effect"
:data-id="effect._id"
@click="clickEffect(effect._id)"
/> />
</v-list> <property-field
v-if="model.attributeType === 'hitDice' && model.hitDiceSize"
name="Hit dice size"
:value="model.hitDiceSize"
/>
<property-field
v-if="model.attributeType === 'hitDice'"
name="Constitution modifier"
:value="isFinite(model.constitutionMod) ?
numberToSignedString(model.constitutionMod) :
model.constitutionMod"
/>
<property-field
v-if="model.attributeType === 'spellSlot' && model.spellSlotLevel"
name="Spell slot level"
:value="model.spellSlotLevel.value !== undefined ? model.spellSlotLevel.value : model.spellSlotLevel.calculation"
/>
<property-field
v-if="model.attributeType === 'ability' && model.proficiency !== undefined"
name="Proficiency"
>
<v-icon
style="height: 12px"
class="ml-1 mr-2"
>
{{ proficiencyIcon }}
</v-icon>
<div>
{{ proficiencyText[model.proficiency] }}
</div>
</property-field>
<property-field
v-if="reset && model.attributeType !== 'hitDice'"
name="Reset"
:value="reset"
/>
<property-field
v-if="model.overridden"
:cols="{cols: 6, md: 12}"
name="Overridden"
value="Overriden by another property with the same variable name"
/>
</v-row>
<v-row dense>
<property-description
label="Description"
:model="model.description"
/>
</v-row>
<v-row dense>
<property-field
v-if="baseEffects.length || effects.length"
:cols="{col: 12}"
name="Effects"
>
<v-list>
<attribute-effect
v-for="effect in baseEffects"
:key="effect._id"
:model="effect"
:hide-breadcrumbs="effect._id === model._id"
:data-id="effect._id"
@click="effect._id !== model._id && clickEffect(effect._id)"
/>
<attribute-effect
v-for="effect in effects"
:key="effect._id"
:model="effect"
:data-id="effect._id"
@click="clickEffect(effect._id)"
/>
</v-list>
</property-field>
</v-row>
</div> </div>
</template> </template>
<script lang="js"> <script lang="js">
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js' import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import AttributeEffect from '/imports/ui/properties/components/attributes/AttributeEffect.vue'; import AttributeEffect from '/imports/ui/properties/components/attributes/AttributeEffect.vue';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import IncrementButton from '/imports/ui/components/IncrementButton.vue'; import IncrementButton from '/imports/ui/components/IncrementButton.vue';
import getProficiencyIcon from '/imports/ui/utility/getProficiencyIcon.js';
export default { export default {
components: { components: {
AttributeEffect, AttributeEffect,
IncrementButton, IncrementButton,
}, },
mixins: [propertyViewerMixin], mixins: [propertyViewerMixin],
inject: { inject: {
context: { default: {} } context: { default: {} }
}, },
computed: { data(){return {
reset(){ attributeTypes: {
let reset = this.model.reset ability: 'Ability score',
if (reset === 'shortRest'){ stat: 'Stat',
return 'Reset on a short rest'; modifier: 'Modifier',
} else if (reset === 'longRest'){ hitDice: 'Hit dice',
return 'Reset on a long rest'; healthBar: 'Health bar',
} resource: 'Resource',
spellSlot: 'Spell slot',
utility: 'Utility',
},
proficiencyText: {
0: 'Not proficient',
1: 'Proficient',
0.49: 'Half proficiency bonus rounded down',
0.5: 'Half proficiency bonus rounded up',
2: 'Double proficiency bonus',
},
}},
computed: {
reset(){
let reset = this.model.reset
if (reset === 'shortRest'){
return 'Reset on a short rest';
} else if (reset === 'longRest'){
return 'Reset on a long rest';
}
return undefined; return undefined;
} },
}, proficiencyIcon(){
methods: { return getProficiencyIcon(this.model.proficiency);
numberToSignedString, },
},
methods: {
numberToSignedString,
clickEffect(id){ clickEffect(id){
this.$store.commit('pushDialogStack', { this.$store.commit('pushDialogStack', {
component: 'creature-property-dialog', component: 'creature-property-dialog',
@@ -118,7 +202,7 @@
value: value value: value
}); });
}, },
}, },
meteor: { meteor: {
baseEffects(){ baseEffects(){
if (this.context.creatureId && this.model.variableName){ if (this.context.creatureId && this.model.variableName){
@@ -155,20 +239,20 @@
} }
}, },
}, },
} }
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.ability-value { .ability-value {
font-weight: 600; font-weight: 600;
font-size: 24px !important; font-size: 24px !important;
color: rgba(0, 0, 0, 0.54); color: rgba(0, 0, 0, 0.54);
} }
.mod, .ability-value { .mod, .ability-value {
text-align: center; text-align: center;
width: 100%; width: 100%;
} }
.attribute-value { .attribute-value {
text-align: center; text-align: center;
} }
</style> </style>

View File

@@ -1,84 +1,114 @@
<template lang="html"> <template lang="html">
<div class="skill-viewer"> <div class="skill-viewer">
<v-layout <v-row
column dense
align-center justify="center"
justify-sm="start"
> >
<div <property-field
v-if="model.value !== undefined" v-if="model.value !== undefined"
class="text-h4 layout align-center" center
large
name="Roll bonus"
:value="isFinite(model.value) ?
numberToSignedString(model.value) :
model.value"
/>
<property-field
v-if="model.proficiency !== undefined"
name="Proficiency"
> >
<v-icon class="mr-4"> <v-icon
style="height: 12px"
class="ml-1 mr-2"
>
{{ icon }} {{ icon }}
</v-icon> </v-icon>
<div v-if="isFinite(model.value)"> <div>
{{ numberToSignedString(model.value) }} {{ proficiencyText[model.proficiency] }}
</div> </div>
</div> </property-field>
</v-layout> <property-field
<property-name :value="model.name" /> name="Variable Name"
<property-variable-name :value="model.variableName" /> :value="model.variableName"
<property-field />
name="Ability" <property-field
:value="model.ability" name="Ability"
/> :value="model.ability"
<property-field />
name="Type" <property-field
:value="model.skillType" name="Skill type"
/> :value="model.skillType"
<property-field />
name="Base value" </v-row>
:value="model.baseValue"
/>
<property-field
name="Base proficiency"
:value="model.baseProficiency"
/>
<property-description <property-description
:string="model.description" :string="model.description"
:calculations="model.descriptionCalculations" :calculations="model.descriptionCalculations"
:inactive="model.inactive" :inactive="model.inactive"
/> />
<v-row
<attribute-effect v-if="baseEffects.length || ability || effects.length"
v-for="effect in baseEffects" dense
:key="effect._id" >
:model="effect" <property-field
:hide-breadcrumbs="effect._id === model._id" :cols="{col: 12}"
:data-id="effect._id" name="Effects"
@click="effect._id !== model._id && clickEffect(effect._id)" >
/> <v-list style="width: 100%">
<attribute-effect <attribute-effect
v-if="ability" v-for="effect in baseEffects"
:key="ability._id" :key="effect._id === model._id ? 'this_base' : effect._id"
:model="ability" :model="effect"
:data-id="ability._id" :hide-breadcrumbs="effect._id === model._id"
@click="clickEffect(ability._id)" :data-id="effect._id"
/> @click="effect._id !== model._id && clickEffect(effect._id)"
<attribute-effect />
v-for="effect in effects" <attribute-effect
:key="effect._id" v-if="ability"
:model="effect" :key="ability._id"
:data-id="effect._id" :model="ability"
@click="clickEffect(effect._id)" :data-id="ability._id"
/> @click="clickEffect(ability._id)"
<skill-proficiency />
v-for="proficiency in baseProficiencies" <attribute-effect
:key="proficiency._id" v-for="effect in effects"
:model="proficiency" :key="effect._id"
:proficiency-bonus="proficiencyBonus" :model="effect"
:hide-breadcrumbs="proficiency._id === model._id" :data-id="effect._id"
:data-id="proficiency._id" @click="clickEffect(effect._id)"
@click="clickEffect(proficiency._id)" />
/> </v-list>
<skill-proficiency </property-field>
v-for="proficiency in proficiencies" </v-row>
:key="proficiency._id" <v-row
:model="proficiency" v-if="baseProficiencies.length || proficiencies.length"
:proficiency-bonus="proficiencyBonus" dense
:data-id="proficiency._id" >
@click="clickEffect(proficiency._id)" <property-field
/> :cols="{col: 12}"
name="Proficiencies"
>
<v-list style="width: 100%">
<skill-proficiency
v-for="proficiency in baseProficiencies"
:key="proficiency._id"
:model="proficiency"
:proficiency-bonus="proficiencyBonus"
:hide-breadcrumbs="proficiency._id === model._id"
:data-id="proficiency._id"
@click="clickEffect(proficiency._id)"
/>
<skill-proficiency
v-for="proficiency in proficiencies"
:key="proficiency._id"
:model="proficiency"
:proficiency-bonus="proficiencyBonus"
:data-id="proficiency._id"
@click="clickEffect(proficiency._id)"
/>
</v-list>
</property-field>
</v-row>
</div> </div>
</template> </template>
@@ -100,6 +130,15 @@ export default {
inject: { inject: {
context: { default: {} } context: { default: {} }
}, },
data(){return {
proficiencyText: {
0: 'Not proficient',
1: 'Proficient',
0.49: 'Half proficiency bonus rounded down',
0.5: 'Half proficiency bonus rounded up',
2: 'Double proficiency bonus',
},
}},
computed: { computed: {
displayedModifier(){ displayedModifier(){
let mod = this.model.value; let mod = this.model.value;
@@ -139,23 +178,23 @@ export default {
name: 'Skill base value', name: 'Skill base value',
operation: 'base', operation: 'base',
calculation: prop.baseValueCalculation, calculation: prop.baseValueCalculation,
result: prop.baseValue, amount: {value: prop.baseValue?.value},
stats: [prop.variableName], stats: [prop.variableName],
ancestors: prop.ancestors, ancestors: prop.ancestors,
}) ).filter(effect => effect.result); }) ).filter(effect => effect.amount?.value);
} else { } else {
return []; return [];
} }
}, },
effects(){ effects(){
if (this.context.creatureId){ if (this.context.creatureId && this.model.variableName){
let creatureId = this.context.creatureId; let creatureId = this.context.creatureId;
return CreatureProperties.find({ return CreatureProperties.find({
'ancestors.id': creatureId, 'ancestors.id': creatureId,
stats: this.model.variableName, stats: this.model.variableName,
type: 'effect', type: 'effect',
removed: {$ne: true}, removed: {$ne: true},
}); }).fetch();
} else { } else {
return []; return [];
} }
@@ -189,7 +228,7 @@ export default {
type: 'proficiency', type: 'proficiency',
removed: {$ne: true}, removed: {$ne: true},
inactive: {$ne: true}, inactive: {$ne: true},
}); }).fetch();
} else { } else {
return []; return [];
} }
@@ -211,7 +250,7 @@ export default {
_id: abilityProp._id, _id: abilityProp._id,
name: abilityProp.name, name: abilityProp.name,
operation: 'base', operation: 'base',
result: abilityProp.modifier, amount: {value: abilityProp.modifier},
stats: [this.model.variableName], stats: [this.model.variableName],
ancestors: abilityProp.ancestors, ancestors: abilityProp.ancestors,
} }

View File

@@ -1,21 +1,32 @@
<template lang="html"> <template lang="html">
<markdown-text <property-field
v-if="model" v-if="model"
:markdown="model.value" :name="label"
/> :cols="{col: 12}"
>
<markdown-text
:markdown="model.value || model.text"
/>
</property-field>
</template> </template>
<script lang="js"> <script lang="js">
import MarkdownText from '/imports/ui/components/MarkdownText.vue'; import MarkdownText from '/imports/ui/components/MarkdownText.vue';
import PropertyField from '/imports/ui/properties/viewers/shared/PropertyField.vue';
export default { export default {
components: { components: {
MarkdownText, MarkdownText,
PropertyField,
}, },
props: { props: {
model: { model: {
type: Object, type: Object,
default: undefined, default: undefined,
},
label: {
type: String,
default: undefined,
}, },
}, },
} }

View File

@@ -1,14 +1,42 @@
<template lang="html"> <template lang="html">
<div v-if="value !== undefined || $slots.default"> <v-col
<div class="text-caption"> v-if="value !== undefined || ($slots.default && $slots.default.length)"
{{ name }} v-bind="cols"
</div> class="mb-2"
<p class="ml-2 subheading"> >
<slot> <v-sheet
{{ value }} outlined
</slot> rounded
</p> class="pa-2 layout column align-start fill-height"
</div> >
<v-sheet
v-if="name"
class="text-caption px-1 name"
style="margin-top: -18px;"
>
{{ name }}
</v-sheet>
<div
class="flex-grow-1 layout align-center"
style="width: 100%;"
>
<div
class="layout align-center"
:class="{
'text-body-1': !large,
'text-h4': large,
'justify-center': center,
}"
style="overflow-x: auto;"
v-bind="$attrs"
>
<slot>
{{ value }}
</slot>
</div>
</div>
</v-sheet>
</v-col>
</template> </template>
<script lang="js"> <script lang="js">
@@ -16,9 +44,21 @@ export default {
props: { props: {
name: String, name: String,
value: [String, Number, Boolean], value: [String, Number, Boolean],
} center: Boolean,
large: Boolean,
cols: {
type: Object,
default: () => ({cols: 12, sm: 6, md: 4}),
},
},
} }
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.name {
color: rgba(0,0,0,.6);
}
.theme--dark .name {
color: rgba(255,255,255,.7);
}
</style> </style>