Files
DiceCloud/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue
2021-10-15 11:12:40 +02:00

337 lines
9.4 KiB
Vue

<template lang="html">
<dialog-base>
<template slot="toolbar">
<v-toolbar-title class="mr-4">
<template v-if="tab === 2">
New
</template>{{ typeName }}
</v-toolbar-title>
<v-spacer />
<v-slide-x-reverse-transition hide-on-leave>
<v-switch
v-if="tab === 0"
:input-value="showPropertyHelp"
append-icon="mdi-help"
hide-details
flat
@change="propertyHelpChanged"
/>
<text-field
v-if="tab === 1"
prepend-inner-icon="mdi-magnify"
regular
hide-details
:value="searchValue"
:debounce="400"
@change="searchChanged"
/>
</v-slide-x-reverse-transition>
</template>
<v-tabs
slot="toolbar-extension"
v-model="tab"
>
<v-tab :disabled="!!forcedType">
{{ typeName || 'Type' }}
</v-tab>
<v-tab :disabled="!type">
Create
</v-tab>
<v-tab :disabled="!type">
Library
</v-tab>
</v-tabs>
<v-tabs-items
slot="unwrapped-content"
v-model="tab"
>
<v-tab-item :disabled="!!forcedType">
<property-selector
no-library-only-props
:parent-type="parentDoc && parentDoc.type"
@select="e => type = e"
/>
</v-tab-item>
<v-tab-item :disabled="!type">
<v-card-text
v-if="!$slots['unwrapped-content']"
>
<component
:is="type"
v-if="type"
class="creature-property-form"
:model="model"
:errors="errors"
@change="change"
@push="push"
@pull="pull"
/>
</v-card-text>
</v-tab-item>
<v-tab-item :disabled="!type">
<v-expansion-panels
multiple
inset
>
<v-expansion-panel
v-for="libraryNode in libraryNodes"
:key="libraryNode._id"
:model="libraryNode"
:data-id="libraryNode._id"
>
<v-expansion-panel-header>
<template #default="{ open }">
<v-checkbox
v-model="selectedNodeIds"
class="my-0 py-0 mr-2 flex-grow-0"
hide-details
:value="libraryNode._id"
:disabled="!selectedNodeIds.includes(libraryNode._id) &&
selectedNodeIds.length >= 20"
@click.stop
/>
<v-layout column>
<tree-node-view :model="libraryNode" />
<div class="text-caption">
{{ libraryNames[libraryNode.ancestors[0].id ] }}
</div>
</v-layout>
<template v-if="open">
<v-spacer />
<v-btn
icon
class="flex-grow-0"
@click.stop="openPropertyDetails(libraryNode._id)"
>
<v-icon>mdi-window-restore</v-icon>
</v-btn>
</template>
</template>
</v-expansion-panel-header>
<v-expansion-panel-content>
<library-node-expansion-content :model="libraryNode" />
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<v-layout
justify-center
>
<v-fade-transition mode="out-in">
<div
v-if="currentLimit < countAll"
class="layout justify-center align-stretch"
>
<v-btn
v-if="currentLimit < countAll"
key="load-more-btn"
:loading="!$subReady.searchLibraryNodes"
color="accent"
class="ma-4"
@click="loadMore"
>
Load More
</v-btn>
</div>
</v-fade-transition>
</v-layout>
</v-tab-item>
</v-tabs-items>
<template slot="actions">
<v-btn
text
@click="$store.dispatch('popDialogStack')"
>
Cancel
</v-btn>
<v-spacer />
<v-btn
v-if="tab === 1"
text
color="primary"
:disabled="!valid"
@click="$store.dispatch('popDialogStack', model)"
>
create
</v-btn>
<v-btn
v-else-if="tab === 2"
text
color="primary"
:disabled="!selectedNodeIds.length"
@click="$store.dispatch('popDialogStack', selectedNodeIds)"
>
<template v-if="selectedNodeIds.length >= 15">
{{ selectedNodeIds.length }}/20
</template>
Insert
</v-btn>
</template>
</dialog-base>
</template>
<script lang="js">
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue';
import schemaFormMixin from '/imports/ui/properties/forms/shared/schemaFormMixin.js';
import propertyFormIndex from '/imports/ui/properties/forms/shared/propertyFormIndex.js';
import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js';
import Libraries from '/imports/api/library/Libraries.js';
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
import PropertySelector from '/imports/ui/properties/shared/PropertySelector.vue';
import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js';
const SKIP_LIBRARY_PROP_TYPES = ['note', 'damage', 'adjustment']
export default {
components: {
PropertySelector,
DialogBase,
TreeNodeView,
LibraryNodeExpansionContent,
...propertyFormIndex,
},
mixins: [schemaFormMixin],
props: {
forcedType: {
type: String,
default: undefined,
},
suggestedTypes: {
type: Array,
default: undefined,
},
suggestedType: {
type: String,
default: undefined,
},
parentDoc: {
type: Object,
default: undefined,
},
},
reactiveProvide: {
name: 'context',
include: ['debounceTime'],
},
data(){return {
selectedNodeIds: [],
type: this.forcedType || this.suggestedType,
model: {
type: this.type,
},
searchValue: undefined,
debounceTime: 0,
tab: 0,
};},
computed: {
typeName(){
return getPropertyName(this.type) || 'Property';
},
toolbarColor(){
return getThemeColor('secondary');
}
},
watch: {
type(newType){
this.changeType(newType);
},
},
mounted(){
this.changeType(this.type);
},
methods: {
propertyHelpChanged(value){
Meteor.users.setPreference.call({
preference: 'hidePropertySelectDialogHelp',
value: !value
}, error => {
if (!error) return;
console.error(error);
snackbar({
text: error.reason,
});
});
},
searchChanged(val, ack){
this._subs.searchLibraryNodes.setData('searchTerm', val);
this._subs.searchLibraryNodes.setData('limit', undefined);
this.selectedNode = undefined;
this.searchValue = val;
setTimeout(ack, 200);
},
loadMore(){
if (this.currentLimit >= this.countAll) return;
this._subs.searchLibraryNodes.setData('limit', this.currentLimit + 32);
},
insert(){
if (!this.selectedNodeIds.length) return;
this.$store.dispatch('popDialogStack', this.selectedNodeIds);
},
changeType(type){
this._subs.searchLibraryNodes.setData('type', type);
if (!type) return;
if (SKIP_LIBRARY_PROP_TYPES.includes(type)){
this.tab = 1;
} else {
this.tab = 2;
}
this.schema = propertySchemasIndex[type];
this.validationContext = this.schema.newContext();
let model = this.schema.clean({});
model.type = type;
this.model = model;
},
openPropertyDetails(id){
this.$store.commit('pushDialogStack', {
component: 'library-node-dialog',
elementId: id,
data: {
_id: id,
},
});
},
},
meteor: {
'$subscribe':{
'searchLibraryNodes': [],
'selectedLibraryNodes'(){
return [this.selectedNodeIds];
},
},
showPropertyHelp(){
let user = Meteor.user();
return !(user?.preferences?.hidePropertySelectDialogHelp)
},
currentLimit(){
return this._subs.searchLibraryNodes.data('limit') || 32;
},
countAll(){
return this._subs.searchLibraryNodes.data('countAll');
},
libraryNodes(){
return LibraryNodes.find({
_searchResult: true
},{
sort: {
'ancestors.0.id': 1,
name: 1,
order: 1,
},
});
},
libraryNames(){
let names = {};
Libraries.find().forEach(lib => names[lib._id] = lib.name)
return names;
}
}
};
</script>
<style lang="css" scoped>
</style>