Significantly improved tree view of libraries

This commit is contained in:
Stefan Zermatten
2019-07-30 12:49:20 +02:00
parent da561bfc83
commit 3460cbf5a0
5 changed files with 74 additions and 32 deletions

View File

@@ -94,16 +94,25 @@ const updateNode = new ValidatedMethod({
function libraryNodesToTree(ancestorId){
// Store a dict of all the nodes
let nodeIndex = {};
LibraryNodes.find({'ancestors.id': ancestorId}).forEach( node => {
node.children = [];
nodeIndex[node._id] = node;
let nodeList = [];
LibraryNodes.find({
'ancestors.id': ancestorId
}, {
sort: {order: 1}
}).forEach( node => {
let treeNode = {
node: node,
children: [],
};
nodeIndex[node._id] = treeNode;
nodeList.push(treeNode);
});
// Create a forest of trees
let forest = [];
// Either the node is a child of another node, or in the forest as a root
nodeList.forEach(node => {
if (nodeIndex[node.parent.id]){
nodeIndex[node.parent.id].children.push(node);
if (nodeIndex[node.node.parent.id]){
nodeIndex[node.node.parent.id].children.push(node);
} else {
forest.push(node);
}
@@ -112,4 +121,4 @@ function libraryNodesToTree(ancestorId){
}
export default LibraryNodes;
export { LibraryNodeSchema, insertNode, updateNode };
export { LibraryNodeSchema, insertNode, updateNode, libraryNodesToTree };

View File

@@ -5,18 +5,24 @@
small icon
:class="showExpanded ? 'rotate-90' : null"
@click="expanded = !expanded"
:disabled="!hasChildren && !showEmpty"
:disabled="!hasChildren && !organize"
>
<v-icon v-if="hasChildren || showEmpty">chevron_right</v-icon>
<v-icon v-if="hasChildren || organize">chevron_right</v-icon>
</v-btn>
<div>{{name}}</div>
<v-icon class="handle mr-2" v-if="organize">reorder</v-icon>
<div>
<span class="mr-2 subheading">{{node && node.order}}</span>
{{node && node.name}}
</div>
</div>
<v-expand-transition>
<div v-if="showExpanded">
<tree-node-list
:node="node"
:children="computedChildren"
:group="group"
:show-empty="showEmpty"
:show-empty="organize"
@moved="e => $emit('moved', e)"
/>
</div>
</v-expand-transition>
@@ -30,12 +36,8 @@
* the tree view shows off the full character structure, and where each part of
* character comes from.
**/
import draggable from 'vuedraggable';
export default {
name: 'tree-node',
components: {
draggable,
},
beforeCreate() {
this.$options.components.TreeNodeList = require('./TreeNodeList.vue').default
},
@@ -43,9 +45,9 @@
expanded: false,
}},
props: {
name: String,
node: Object,
group: String,
showEmpty: Boolean,
organize: Boolean,
children: Array,
getChildren: Function,
},
@@ -54,7 +56,7 @@
return this.children && this.children.length || this.lazy && !this.expanded;
},
showExpanded(){
return this.expanded && (this.showEmpty || this.hasChildren)
return this.expanded && (this.organize || this.hasChildren)
},
computedChildren(){
let children = [];
@@ -79,6 +81,9 @@
box-shadow: -2px 0px 0px 0px #808080;
margin-left: 23px;
}
.handle {
cursor: move;
}
.empty .drag-area {
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
}

View File

@@ -1,21 +1,25 @@
<template lang="html">
<draggable
:list="children"
:value="children"
class="drag-area layout column"
@change="change"
:group="group"
:animation="200"
ghost-class="ghost"
draggable=".item"
handle=".handle"
>
<tree-node
v-for="child in children"
v-bind="child"
:node="child.node"
:children="child.children"
:group="group"
:key="child._id || child.name"
:showEmpty="showEmpty"
:key="child.node && (child.node._id || child.node.name)"
:organize="organize"
:lazy="lazy"
class="item"
@dragstart.native="e => e.dataTransfer.setData('cow', child.name)"
@moved="e => $emit('moved', e)"
@dragstart.native="e => e.dataTransfer.setData('cow', child.node && child.node.name)"
/>
</draggable>
</template>
@@ -32,8 +36,9 @@
expanded: false,
}},
props: {
node: Object,
group: String,
showEmpty: Boolean,
organize: Boolean,
lazy: Boolean,
children: {
type: Array,
@@ -45,7 +50,14 @@
return this.children && this.children.length;
},
showExpanded(){
return this.expanded && (this.showEmpty || this.hasChildren)
return this.expanded && (this.organize || this.hasChildren)
},
},
methods: {
change({added, removed, moved}){
if (removed) return;
let newIndex = (added || moved).newIndex;
this.$emit('moved', {parent: this.node, newIndex});
},
},
};

View File

@@ -1,13 +1,19 @@
<template lang="html">
<v-card-text>
<tree-node-list v-if="libraryChildren" :children="libraryChildren" :group="library && library._id"/>
<tree-node-list
v-if="libraryChildren"
:children="libraryChildren"
:group="library && library._id"
:organize="organize"
@moved="moved"
/>
<template v-else>This library is empty</template>
</v-card-text>
</template>
<script>
import Libraries from '/imports/api/library/Libraries.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import LibraryNodes, { libraryNodesToTree } from '/imports/api/library/LibraryNodes.js';
import TreeNodeList from '/imports/ui/components/tree/TreeNodeList.vue';
export default {
@@ -16,6 +22,7 @@
},
props: {
libraryId: String,
organize: Boolean,
},
meteor: {
$subscribe: {
@@ -26,11 +33,12 @@
},
libraryChildren(){
if (!this.library) return;
return LibraryNodes.find({
"parent.id": this.library._id
}, {
sort: {order: 1},
});
return libraryNodesToTree(this.library._id);
},
},
methods: {
moved(e){
console.log(e)
},
},
};

View File

@@ -1,9 +1,14 @@
<template lang="html">
<toolbar-layout>
<span slot="toolbar">{{library && library.name || 'Library'}}</span>
<template slot="toolbar">
{{library && library.name || 'Library'}}
<v-spacer/>
<v-switch v-model="organize" label="Sort" style="flex-grow: 0;"/>
</template>
<v-card class="ma-4">
<library-contents-container
:library-id="$route.params.id"
:organize="organize"
/>
</v-card>
<v-btn fixed fab bottom right
@@ -28,6 +33,9 @@
ToolbarLayout,
LibraryContentsContainer,
},
data(){ return {
organize: false,
};},
methods: {
insertLibraryNode(){
let that = this;