feat: New tree navigation replaces browse navigation

pull/5340/head
myml 2 years ago
parent e3d94f7177
commit 40f875e085

@ -23,6 +23,15 @@
v-toolbar-title.subtitle-1 {{$t('admin:navigation.mode')}}
v-list(nav, two-line)
v-list-item-group(v-model='config.mode', mandatory, :color='$vuetify.theme.dark ? `teal lighten-3` : `teal`')
v-list-item(value='NEWTREE')
v-list-item-avatar
img(src='/_assets/svg/icon-tree-structure-dotted.svg', alt='Site Tree')
v-list-item-content
v-list-item-title {{$t('admin:navigation.modeNewSiteTree.title')}}
v-list-item-subtitle {{$t('admin:navigation.modeNewSiteTree.description')}}
v-list-item-avatar
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `NEWTREE` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
v-icon(v-else, :color='config.mode === `NEWTREE` ? `teal` : `grey lighten-3`') mdi-check-circle
v-list-item(value='TREE')
v-list-item-avatar
img(src='/_assets/svg/icon-tree-structure-dotted.svg', alt='Site Tree')

@ -23,6 +23,15 @@
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='flex: 1 1 100%;'
@click='switchMode(`tree`)'
)
v-icon(left) mdi-file-tree
.body-2.text-none {{$t('common:sidebar.tree')}}
v-btn.ml-3(
v-else-if='currentMode === `tree`'
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='flex: 1 1 100%;'
@click='switchMode(`custom`)'
)
v-icon(left) mdi-navigation
@ -43,6 +52,29 @@
v-list-item-title {{ item.l }}
v-divider.my-2(v-else-if='item.k === `divider`')
v-subheader.pl-4(v-else-if='item.k === `header`') {{ item.l }}
//-> Tree Navigation
v-treeview(
v-else-if='currentMode === `tree`'
activatable
open-on-click
:color='"white"'
:active='treeDefaultActive'
:open='treeDefaultOpen'
:items='treeItems'
:load-children='fetchTreeChild'
@update:active='activeTreeItem'
)
template(v-slot:prepend="{ item, open }")
v-icon(v-if="!item.children") mdi-text-box
v-icon(v-else-if="open") mdi-folder-open
v-icon(v-else) mdi-folder
template(v-slot:label="{ item }")
div(class='tree-item')
a(v-if="!item.children" :href="'/'+item.locale+'/'+item.path")
span {{item.name}}
span(v-else) {{item.name}}
//-> Browse
v-list.py-2(v-else-if='currentMode === `browse`', dense, :class='color', :dark='dark')
template(v-if='currentParent.id > 0')
@ -102,7 +134,10 @@ export default {
title: '/ (root)'
},
parents: [],
loadedCache: []
loadedCache: [],
treeItems: [],
treeDefaultOpen: [],
treeDefaultActive: [],
}
},
computed: {
@ -116,6 +151,9 @@ export default {
if (mode === `browse` && this.loadedCache.length < 1) {
this.loadFromCurrentPath()
}
if (mode === 'tree') {
this.fetchTreeRoot();
}
},
async fetchBrowseItems (item) {
this.$store.commit(`loadingStart`, 'browse-load')
@ -219,7 +257,95 @@ export default {
},
goHome () {
window.location.assign(siteLangs.length > 0 ? `/${this.locale}/home` : '/')
}
},
pageItem2TreeItem(item,level) {
if (item.isFolder) {
return { id: item.id, level: level, pageId: item.pageId, path: item.path, locale: item.locale, name: item.title, children: [] }
} else {
return { id: item.id, level: level, path: item.path, locale: item.locale, name: item.title }
}
},
activeTreeItem(id) {
const find = (items) => {
for(const item of items) {
if(item.id == id) {
return item
}
if(item.children && item.children.length) {
const v = find(item.children)
if(v) {
return v
}
}
}
}
const item = find(this.treeItems)
if(item) {
if(!this.treeDefaultActive.includes(item.id)) {
location.href = `/${item.locale}/${item.path}`
} else {
setTimeout(() => {
const el = document.querySelector(".v-treeview-node--active")
el.scrollIntoViewIfNeeded()
})
}
}
},
async fetchTreeChild(parent) {
const items = await this.fetchPages(parent.id)
parent.children = []
if(parent.pageId){
parent.children.push({
id: parent.pageId,level: parent.level+1, path: parent.path, locale: parent.locale, name: parent.name
})
}
parent.children.push(
...items.map(item => this.pageItem2TreeItem(item, parent.level+1))
)
this.checkTreeDefaultOpen(parent.children);
},
async fetchTreeRoot(){
const children = await this.fetchPages(0)
this.treeItems = children.map(item => this.pageItem2TreeItem(item, 0))
this.checkTreeDefaultOpen(this.treeItems, 0);
},
async checkTreeDefaultOpen(items){
const item = items.find(item => item.children && this.path.startsWith(item.path))
if(item) {
setTimeout(()=>{
this.treeDefaultOpen.push(item.id)
})
}
const active = items.find(item => item.path == this.path)
if(active) {
this.treeDefaultActive.push(active.id)
}
},
async fetchPages(id) {
const resp = await this.$apollo.query({
query: gql`
query($parent: Int, $locale: String!) {
pages {
tree(parent: $parent, mode: ALL, locale: $locale) {
id
path
title
isFolder
pageId
parent
locale
}
}
}
`,
fetchPolicy: 'cache-first',
variables: {
parent: id,
locale: this.locale
}
})
return _.get(resp, 'data.pages.tree', [])
},
},
mounted () {
this.currentParent.title = `/ ${this.$t('common:sidebar.root')}`
@ -227,12 +353,37 @@ export default {
this.currentMode = 'browse'
} else if (this.navMode === 'STATIC') {
this.currentMode = 'custom'
} else if (this.navMode === 'NEWTREE') {
this.currentMode = 'tree'
} else {
this.currentMode = window.localStorage.getItem('navPref') || 'custom'
}
if (this.currentMode === 'browse') {
this.loadFromCurrentPath()
}
if (this.currentMode === "tree") {
this.fetchTreeRoot();
}
}
}
</script>
<style lang="scss">
.v-treeview{
.tree-item {
font-weight: 500;
line-height: 1rem;
font-size: 0.8rem;
}
a {
text-decoration: none;
}
&.theme--dark{
a {
color: white;
}
}
}
</style>

@ -75,6 +75,7 @@ type NavigationConfig {
enum NavigationMode {
NONE
TREE
NEWTREE
MIXED
STATIC
}

Loading…
Cancel
Save