Added search to library tree views

This commit is contained in:
Stefan Zermatten
2021-08-02 00:29:56 +02:00
parent 1e38295164
commit 754eecc2da
6 changed files with 123 additions and 58 deletions

View File

@@ -0,0 +1,81 @@
<template lang="html">
<v-combobox
v-model="filterTerms"
:items="filterOptions"
prepend-inner-icon="mdi-magnify"
hide-no-data
hide-selected
multiple
clearable
small-chips
deletable-chips
/>
</template>
<script lang="js">
export default {
props: {
value: {
type: Object,
default: undefined,
},
},
data(){return {
filterTerms: [],
filterOptions: [
{text: 'Actions', value: 'action'},
{text: 'Attacks', value: 'attack'},
{text: 'Attributes', value: 'attribute'},
{text: 'Buffs', value: 'buff'},
{text: 'Class Levels', value: 'classLevel'},
{text: 'Damage Multipliers', value: 'damageMultiplier'},
{text: 'Effects', value: 'effect'},
{text: 'Experiences', value: 'experience'},
{text: 'Features', value: 'feature'},
{text: 'Folders', value: 'folder'},
{text: 'Notes', value: 'note'},
{text: 'Proficiencies', value: 'proficiency'},
{text: 'Rolls', value: 'roll'},
{text: 'Saving Throws', value: 'savingThrow'},
{text: 'Skills', value: 'skill'},
{text: 'Spell Lists', value: 'spellList'},
{text: 'Spells', value: 'spell'},
{text: 'Containers', value: 'container'},
{text: 'Items', value: 'item'},
],
}},
computed: {
filter(){
if (!this.filterTerms.length) return;
let typeFilters = [];
let nameFilters = [];
this.filterTerms.forEach(filter => {
if (filter.value){
typeFilters.push(filter.value);
} else {
// escape string
let term = filter.replace( /[-/\\^$*+?.()|[\]{}]/g, '\\$&' );
var reg = new RegExp( '.*' + term + '.*', 'i' );
nameFilters.push(reg)
}
});
let filter = {};
if (typeFilters.length){
filter.type = {$in: typeFilters};
}
if (nameFilters.length){
filter.name = {$in: nameFilters};
}
return filter;
},
},
watch:{
filter(value){
this.$emit('input', value);
}
}
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -23,19 +23,11 @@
:disabled="organizeDisabled" :disabled="organizeDisabled"
style="flex-grow: 0; height: 32px;" style="flex-grow: 0; height: 32px;"
/> />
<v-combobox <tree-search-input
ref="searchBox" ref="searchBox"
slot="extension" slot="extension"
v-model="filterString" v-model="filter"
:items="filterOptions"
prepend-inner-icon="mdi-magnify"
class="mx-4" class="mx-4"
hide-no-data
hide-selected
multiple
clearable
small-chips
deletable-chips
/> />
</v-toolbar> </v-toolbar>
<creature-properties-tree <creature-properties-tree
@@ -65,13 +57,14 @@
import TreeDetailLayout from '/imports/ui/components/TreeDetailLayout.vue'; import TreeDetailLayout from '/imports/ui/components/TreeDetailLayout.vue';
import CreaturePropertiesTree from '/imports/ui/creature/creatureProperties/CreaturePropertiesTree.vue'; import CreaturePropertiesTree from '/imports/ui/creature/creatureProperties/CreaturePropertiesTree.vue';
import CreaturePropertyDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue'; import CreaturePropertyDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue';
import TreeSearchInput from '/imports/ui/components/tree/TreeSearchInput.vue';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { getPropertyName } from '/imports/constants/PROPERTIES.js'; import { getPropertyName } from '/imports/constants/PROPERTIES.js';
export default { export default {
components: { components: {
TreeDetailLayout, TreeDetailLayout,
TreeSearchInput,
CreaturePropertiesTree, CreaturePropertiesTree,
CreaturePropertyDialog, CreaturePropertyDialog,
}, },
@@ -89,50 +82,8 @@
organizeDisabled: false, organizeDisabled: false,
selectedNodeId: undefined, selectedNodeId: undefined,
fab: false, fab: false,
filterString: '', filter: undefined,
filterOptions: [
{text: 'Actions', value: 'action'},
{text: 'Attacks', value: 'attack'},
{text: 'Attributes', value: 'attribute'},
{text: 'Buffs', value: 'buff'},
{text: 'Class Levels', value: 'classLevel'},
{text: 'Damage Multipliers', value: 'damageMultiplier'},
{text: 'Effects', value: 'effect'},
{text: 'Experiences', value: 'experience'},
{text: 'Features', value: 'feature'},
{text: 'Folders', value: 'folder'},
{text: 'Notes', value: 'note'},
{text: 'Proficiencies', value: 'proficiency'},
{text: 'Rolls', value: 'roll'},
{text: 'Saving Throws', value: 'savingThrow'},
{text: 'Skills', value: 'skill'},
{text: 'Spell Lists', value: 'spellList'},
{text: 'Spells', value: 'spell'},
{text: 'Containers', value: 'container'},
{text: 'Items', value: 'item'},
],
};}, };},
computed: {
filter(){
if (!this.filterString.length) return;
let typeFilters = [];
let nameFilters = [];
this.filterString.forEach(filter => {
if (filter.value){
typeFilters.push(filter.value);
} else {
// escape string
let term = filter.replace( /[-/\\^$*+?.()|[\]{}]/g, '\\$&' );
var reg = new RegExp( '.*' + term + '.*', 'i' );
nameFilters.push(reg)
}
});
return {$or: [
{type: {$in: typeFilters}},
{name: {$in: nameFilters}},
]};
},
},
watch: { watch: {
filter(filter){ filter(filter){
if (filter) { if (filter) {

View File

@@ -28,7 +28,10 @@
type: Object, type: Object,
default: undefined, default: undefined,
}, },
filter: Object, filter: {
type: Object,
default: undefined,
},
group: { group: {
type: String, type: String,
default: 'creatureProperties' default: 'creatureProperties'

View File

@@ -25,9 +25,16 @@
class="mx-3" class="mx-3"
style="flex-grow: 0; height: 32px;" style="flex-grow: 0; height: 32px;"
/> />
<tree-search-input
ref="searchBox"
slot="extension"
v-model="filter"
class="mx-4"
/>
<insert-library-node-button <insert-library-node-button
v-if="libraryId && canEditLibrary" v-if="libraryId && canEditLibrary"
style="bottom: -32px" slot="extension"
style="bottom: -24px"
fab fab
:library-id="libraryId" :library-id="libraryId"
:selected-node-id="selectedNodeId" :selected-node-id="selectedNodeId"
@@ -43,6 +50,7 @@
:organize-mode="organize" :organize-mode="organize"
:selected-node="selectedNode" :selected-node="selectedNode"
should-subscribe should-subscribe
:filter="filter"
@selected="clickNode" @selected="clickNode"
/> />
</div> </div>
@@ -52,6 +60,7 @@
:organize-mode="organize" :organize-mode="organize"
:selected-node="selectedNode" :selected-node="selectedNode"
style="overflow-y: auto; padding: 12px;" style="overflow-y: auto; padding: 12px;"
:filter="filter"
@selected="clickNode" @selected="clickNode"
/> />
</div> </div>
@@ -82,6 +91,7 @@ import { getPropertyName } from '/imports/constants/PROPERTIES.js';
import isDarkColor from '/imports/ui/utility/isDarkColor.js'; import isDarkColor from '/imports/ui/utility/isDarkColor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getThemeColor from '/imports/ui/utility/getThemeColor.js'; import getThemeColor from '/imports/ui/utility/getThemeColor.js';
import TreeSearchInput from '/imports/ui/components/tree/TreeSearchInput.vue';
export default { export default {
components: { components: {
@@ -90,6 +100,7 @@ export default {
LibraryNodeDialog, LibraryNodeDialog,
LibraryContentsContainer, LibraryContentsContainer,
InsertLibraryNodeButton, InsertLibraryNodeButton,
TreeSearchInput,
}, },
props: { props: {
selection: Boolean, selection: Boolean,
@@ -101,6 +112,7 @@ export default {
data(){ return { data(){ return {
organize: false, organize: false,
selectedNodeId: undefined, selectedNodeId: undefined,
filter: undefined,
};}, };},
computed: { computed: {
isToolbarDark(){ isToolbarDark(){

View File

@@ -47,6 +47,7 @@
:organize-mode="organizeMode && editPermission(library)" :organize-mode="organizeMode && editPermission(library)"
:edit-mode="editMode" :edit-mode="editMode"
:selected-node="selectedNode" :selected-node="selectedNode"
:filter="filter"
should-subscribe should-subscribe
@selected="e => $emit('selected', e)" @selected="e => $emit('selected', e)"
/> />
@@ -87,6 +88,10 @@ export default {
type: Object, type: Object,
default: undefined, default: undefined,
}, },
filter: {
type: Object,
default: undefined,
},
}, },
data(){ return { data(){ return {
expandedLibrary: [], expandedLibrary: [],

View File

@@ -38,13 +38,20 @@
TreeNodeList, TreeNodeList,
}, },
props: { props: {
libraryId: String, libraryId: {
type: String,
default: undefined,
},
organizeMode: Boolean, organizeMode: Boolean,
selectedNode: { selectedNode: {
type: Object, type: Object,
default: undefined, default: undefined,
}, },
shouldSubscribe: Boolean, shouldSubscribe: Boolean,
filter: {
type: Object,
default: undefined,
},
}, },
data(){return { data(){return {
slowShouldSubscribe: this.shouldSubscribe, slowShouldSubscribe: this.shouldSubscribe,
@@ -79,7 +86,13 @@
}, },
libraryChildren(){ libraryChildren(){
if (!this.library) return; if (!this.library) return;
return nodesToTree({collection: LibraryNodes, ancestorId: this.library._id}); return nodesToTree({
collection: LibraryNodes,
ancestorId: this.library._id,
filter: this.filter,
includeFilteredDocAncestors: true,
includeFilteredDocDescendants: true,
});
}, },
}, },
methods: { methods: {