Files
DiceCloud/app/imports/client/ui/properties/PropertyForm.vue
2023-06-07 14:19:06 +02:00

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>