All properties added to the sheet now use the type/library/create UX

This commit is contained in:
Stefan Zermatten
2021-08-01 23:28:04 +02:00
parent 758cb2f8bc
commit 1e38295164
8 changed files with 397 additions and 321 deletions

View File

@@ -1,50 +1,127 @@
<template lang="html">
<selectable-property-dialog
:value="type"
no-library-only-props
@input="e => type = e"
>
<dialog-base
:override-back-button="back"
<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"
>
<template slot="toolbar">
<v-toolbar-title class="mr-4">
<template v-if="customProperty">
New
</template>{{ typeName }}
</v-toolbar-title>
<v-slide-x-transition>
<text-field
v-if="!customProperty"
prepend-inner-icon="mdi-magnify"
regular
hide-details
:value="searchValue"
:debounce="400"
@change="searchChanged"
/>
</v-slide-x-transition>
<v-scale-transition>
<v-btn
v-if="!customProperty"
fab
small
elevation="0"
class="mr-2"
color="accent"
@click="customProperty = true"
<v-tab :disabled="!!forcedType">
{{ typeName || 'Type' }}
</v-tab>
<v-tab :disabled="!type">
Library
</v-tab>
<v-tab :disabled="!type">
Create
</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-expansion-panels
multiple
inset
>
<v-expansion-panel
v-for="libraryNode in libraryNodes"
:key="libraryNode._id"
:model="libraryNode"
:data-id="libraryNode._id"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-scale-transition>
</template>
<v-slide-x-transition
class="unwrapped-content"
leave-absolute
>
<div
v-if="customProperty"
key="custom-property-form"
<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-tab-item :disabled="!type">
<v-card-text
v-if="!$slots['unwrapped-content']"
>
<component
:is="type"
@@ -56,113 +133,43 @@
@push="push"
@pull="pull"
/>
</div>
<div
v-else
key="library-search"
>
<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"
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>
<v-spacer />
<v-btn
v-if="open"
icon
class="flex-grow-0"
@click.stop="openPropertyDetails(libraryNode._id)"
>
<v-icon>mdi-pencil</v-icon>
</v-btn>
</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>
</div>
</v-slide-x-transition>
<template slot="actions">
<v-btn
text
@click="$store.dispatch('popDialogStack')"
>
Cancel
</v-btn>
<v-spacer />
<v-btn
v-if="customProperty"
text
color="primary"
:disabled="!valid"
@click="$store.dispatch('popDialogStack', model)"
>
create
</v-btn>
<v-btn
v-else
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>
</selectable-property-dialog>
</v-card-text>
</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 === 2"
text
color="primary"
:disabled="!valid"
@click="$store.dispatch('popDialogStack', model)"
>
create
</v-btn>
<v-btn
v-else-if="tab === 1"
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 SelectablePropertyDialog from '/imports/ui/properties/shared/SelectablePropertyDialog.vue';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
@@ -172,14 +179,19 @@
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: {
...propertyFormIndex,
SelectablePropertyDialog,
PropertySelector,
DialogBase,
TreeNodeView,
LibraryNodeExpansionContent,
...propertyFormIndex,
},
mixins: [schemaFormMixin],
props: {
@@ -187,6 +199,10 @@
type: String,
default: undefined,
},
suggestedTypes: {
type: Array,
default: undefined,
},
suggestedType: {
type: String,
default: undefined,
@@ -207,13 +223,16 @@
type: this.type,
},
searchValue: undefined,
customProperty: false,
debounceTime: 0,
tab: 0,
};},
computed: {
typeName(){
return getPropertyName(this.type) || 'Property';
},
toolbarColor(){
return getThemeColor('secondary');
}
},
watch: {
type(newType){
@@ -224,14 +243,18 @@
this.changeType(this.type);
},
methods: {
back(){
if (this.customProperty){
this.customProperty = false;
} else if (this.forcedType){
this.$store.dispatch('popDialogStack');
} else {
this.type = undefined;
}
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);
@@ -251,6 +274,11 @@
changeType(type){
this._subs.searchLibraryNodes.setData('type', type);
if (!type) return;
if (SKIP_LIBRARY_PROP_TYPES.includes(type)){
this.tab = 2;
} else {
this.tab = 1;
}
this.schema = propertySchemasIndex[type];
this.validationContext = this.schema.newContext();
let model = this.schema.clean({});
@@ -265,12 +293,16 @@
_id: id,
},
});
}
},
},
meteor: {
'$subscribe':{
'searchLibraryNodes': [],
},
showPropertyHelp(){
let user = Meteor.user();
return !(user?.preferences?.hidePropertySelectDialogHelp)
},
currentLimit(){
return this._subs.searchLibraryNodes.data('limit') || 32;
},