309 lines
9.3 KiB
Vue
309 lines
9.3 KiB
Vue
<template>
|
|
<div class="property-form">
|
|
<v-row>
|
|
<v-col
|
|
cols="12"
|
|
class="d-flex flex-wrap-reverse justify-end"
|
|
style="gap: 8px"
|
|
>
|
|
<text-field
|
|
v-if="schemaHasName"
|
|
ref="focusFirst"
|
|
label="Name"
|
|
style="flex-basis: 320px;"
|
|
:value="model.name"
|
|
:error-messages="errors.name"
|
|
@change="(value, ack) => $emit('change', {path: ['name'], value, ack})"
|
|
/>
|
|
<icon-color-menu
|
|
:model="model"
|
|
@change="e => $emit('change', e)"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
<component
|
|
:is="model.type"
|
|
class="creature-property-form mb-4"
|
|
:model="model"
|
|
:errors="errors"
|
|
@change="e => $emit('change', e)"
|
|
@push="e => $emit('push', e)"
|
|
@pull="e => $emit('pull', e)"
|
|
>
|
|
<form-section
|
|
v-if="context.isLibraryForm"
|
|
name="Library"
|
|
>
|
|
<v-row
|
|
v-if="context.isLibraryForm"
|
|
dense
|
|
>
|
|
<v-col
|
|
cols="12"
|
|
md="6"
|
|
>
|
|
<smart-switch
|
|
label="Can fill slots"
|
|
:value="model.fillSlots"
|
|
:error-messages="errors.fillSlots"
|
|
@change="(value, ack) => $emit('change', {path: ['fillSlots'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
<v-col
|
|
cols="12"
|
|
md="6"
|
|
>
|
|
<smart-switch
|
|
label="Searchable from character sheet"
|
|
:value="model.searchable"
|
|
:error-messages="errors.searchable"
|
|
@change="(value, ack) => $emit('change', {path: ['searchable'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
<v-col
|
|
cols="12"
|
|
md="6"
|
|
>
|
|
<smart-select
|
|
label="Slot Fill Type"
|
|
style="flex-basis: 300px;"
|
|
clearable
|
|
hint="The property type that this slot filler pretends to be when being searched for by a slot"
|
|
:items="slotTypes"
|
|
:value="model.slotFillerType"
|
|
:error-messages="errors.slotFillerType"
|
|
@change="(value, ack) => $emit('change', {path: ['slotFillerType'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
<v-col
|
|
cols="12"
|
|
md="6"
|
|
>
|
|
<text-field
|
|
label="Slot Fill Quantity"
|
|
type="number"
|
|
min="0"
|
|
hint="How many properties this counts as when filling a slot"
|
|
:value="model.slotQuantityFilled"
|
|
:error-messages="errors.slotQuantityFilled"
|
|
@change="(value, ack) => $emit('change', {path: ['slotQuantityFilled'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
<v-col
|
|
cols="12"
|
|
md="6"
|
|
>
|
|
<text-field
|
|
v-if="context.isLibraryForm"
|
|
label="Condition"
|
|
hint="A caclulation to determine if this property can be added to a character"
|
|
placeholder="Always active"
|
|
:value="model.slotFillerCondition"
|
|
:error-messages="errors.slotFillerCondition"
|
|
@change="(value, ack) => $emit('change', {path: ['slotFillerCondition'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
<v-col
|
|
cols="12"
|
|
md="6"
|
|
>
|
|
<text-field
|
|
v-if="context.isLibraryForm"
|
|
label="Condition Error Text"
|
|
hint="Text to display if the condition isn't met"
|
|
placeholder="Always active"
|
|
:value="model.slotFillerConditionNote"
|
|
:error-messages="errors.slotFillerConditionNote"
|
|
@change="(value, ack) => $emit('change', {path: ['slotFillerConditionNote'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
<v-col
|
|
cols="12"
|
|
>
|
|
<smart-combobox
|
|
label="Library Tags"
|
|
multiple
|
|
small-chips
|
|
deletable-chips
|
|
hint="Used to let slots find this property in a library"
|
|
:value="model.libraryTags"
|
|
:error-messages="errors.libraryTags"
|
|
@change="(value, ack) => $emit('change', {path: ['libraryTags'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</form-section>
|
|
</component>
|
|
<v-divider
|
|
class="mt-10 mb-8"
|
|
/>
|
|
<v-row>
|
|
<v-col
|
|
cols="12"
|
|
>
|
|
<smart-combobox
|
|
label="Tags"
|
|
multiple
|
|
small-chips
|
|
deletable-chips
|
|
hint="Tags let other properties target this property with interactions"
|
|
:value="model.tags"
|
|
:error-messages="errors.tags"
|
|
@change="(value, ack) => $emit('change', {path: ['tags'], value, ack})"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row
|
|
class="mt-1"
|
|
dense
|
|
>
|
|
<v-col
|
|
cols="12"
|
|
class="d-flex flex-wrap-reverse justify-end"
|
|
style="gap: 8px"
|
|
>
|
|
<outlined-input
|
|
name="Child properties"
|
|
style="width: 100%"
|
|
class="pa-2 no-hover"
|
|
>
|
|
<creature-properties-tree
|
|
style="width: 100%;"
|
|
organize
|
|
:root="{collection, id: model._id}"
|
|
:collection="collection"
|
|
@selected="e => $emit('select-sub-property', e)"
|
|
/>
|
|
<v-btn
|
|
v-for="suggestion in suggestedChildren"
|
|
:key="suggestion.type"
|
|
:disabled="noChildInsert"
|
|
tile
|
|
plain
|
|
:data-id="`insert-${suggestion.type}-property-btn`"
|
|
@click="$event => $emit('add-child', {suggestedType: suggestion.type, elementId: `insert-${suggestion.type}-property-btn`})"
|
|
>
|
|
<v-icon left>
|
|
mdi-plus
|
|
</v-icon>
|
|
{{ suggestion.details.name }}
|
|
</v-btn>
|
|
<v-btn
|
|
:disabled="noChildInsert"
|
|
tile
|
|
plain
|
|
data-id="insert-any-property-btn"
|
|
@click="$event => $emit('add-child', {elementId: 'insert-any-property-btn'})"
|
|
>
|
|
<v-icon
|
|
v-if="!suggestedChildren.length"
|
|
left
|
|
>
|
|
mdi-plus
|
|
</v-icon>
|
|
{{ suggestedChildren.length ? '...Other' : 'Child' }}
|
|
</v-btn>
|
|
<div
|
|
v-if="noChildInsert"
|
|
class="ma-2 text--disabled"
|
|
>
|
|
Children can be added after this property is created
|
|
</div>
|
|
</outlined-input>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="js">
|
|
/*
|
|
All of the shared fields common to all properties go in this form,
|
|
property-specific forms are included as dynamic components
|
|
*/
|
|
import ComputedField from '/imports/client/ui/properties/forms/shared/ComputedField.vue';
|
|
import InlineComputationField from '/imports/client/ui/properties/forms/shared/InlineComputationField.vue';
|
|
import FormSection, { FormSections } from '/imports/client/ui/properties/forms/shared/FormSection.vue';
|
|
import propertyFormIndex from '/imports/client/ui/properties/forms/shared/propertyFormIndex.js';
|
|
import IconColorMenu from '/imports/client/ui/properties/forms/shared/IconColorMenu.vue';
|
|
import CreaturePropertiesTree from '/imports/client/ui/creature/creatureProperties/CreaturePropertiesTree.vue';
|
|
import OutlinedInput from '/imports/client/ui/properties/viewers/shared/OutlinedInput.vue';
|
|
import { getSuggestedChildren } from '/imports/constants/PROPERTIES.js';
|
|
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
|
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js';
|
|
|
|
const slotTypes = [];
|
|
for (let key in PROPERTIES) {
|
|
slotTypes.push({ text: PROPERTIES[key].name, value: key });
|
|
}
|
|
|
|
export default {
|
|
components: {
|
|
ComputedField,
|
|
InlineComputationField,
|
|
FormSection,
|
|
FormSections,
|
|
IconColorMenu,
|
|
CreaturePropertiesTree,
|
|
OutlinedInput,
|
|
...propertyFormIndex,
|
|
},
|
|
inject: {
|
|
context: { default: {} }
|
|
},
|
|
props: {
|
|
model: {
|
|
type: [Object, Array],
|
|
default: () => ({}),
|
|
},
|
|
collection: {
|
|
type: String,
|
|
default: 'creatureProperties'
|
|
},
|
|
errors: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
embedded: Boolean, // This dialog is embedded in a page
|
|
noChildInsert: Boolean, // Don't allow inserting of children in this form
|
|
},
|
|
data() {
|
|
return {
|
|
slotTypes,
|
|
};
|
|
},
|
|
computed: {
|
|
suggestedChildren() {
|
|
if (!this.model?.type) return [];
|
|
return getSuggestedChildren(this.model.type);
|
|
},
|
|
schemaHasName() {
|
|
if (!this.model?.type) return true;
|
|
const schema = propertySchemasIndex[this.model.type];
|
|
return schema.allowsKey('name');
|
|
}
|
|
},
|
|
mounted() {
|
|
// Don't autofocus on mobile, it brings up the on-screen keyboard
|
|
if (this.$vuetify.breakpoint.smAndDown) return;
|
|
|
|
setTimeout(() => {
|
|
if (this.$refs.focusFirst && this.$refs.focusFirst.focus) {
|
|
this.$refs.focusFirst.focus()
|
|
}
|
|
}, 300);
|
|
},
|
|
methods: {
|
|
selectSubProperty(_id){
|
|
this.$store.commit('pushDialogStack', {
|
|
component: 'creature-property-dialog',
|
|
elementId: `tree-node-${_id}`,
|
|
data: {
|
|
_id,
|
|
startInEditTab: this.editing,
|
|
},
|
|
});
|
|
},
|
|
},
|
|
}
|
|
</script>
|