mirror of https://github.com/nextcloud/bookmarks
UI: Implement folder tree in AppContentList
fixes #1756 Signed-off-by: Marcel Klehr <mklehr@gmx.net>
This commit is contained in:
parent
fad74190a7
commit
9f5ac85458
|
@ -225,6 +225,7 @@ export default {
|
|||
align-content: start;
|
||||
gap: 10px;
|
||||
padding: 0 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.folder--gridview,
|
||||
|
|
|
@ -285,6 +285,7 @@ export default {
|
|||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-bottom: var(--color-border) 1px solid;
|
||||
}
|
||||
|
||||
.controls h2 {
|
||||
|
@ -302,7 +303,11 @@ export default {
|
|||
|
||||
.controls h2 > .material-design-icon {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.controls .action-item {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.controls.wide {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
- Copyright (c) 2022. The Nextcloud Bookmarks contributors.
|
||||
-
|
||||
- This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<AppContentList :show-details="showDetails" @update:show-details="$emit('update:show-details', $event)">
|
||||
<TreeFolder v-for="folder in rootFolder.children"
|
||||
:key="folder.id"
|
||||
:folder="folder"
|
||||
@select="onSelect($event)" />
|
||||
</AppContentList>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppContentList from '@nextcloud/vue/dist/Components/AppContentList'
|
||||
import TreeFolder from './TreeFolder'
|
||||
import { privateRoutes } from '../router'
|
||||
|
||||
export default {
|
||||
name: 'FolderOverview',
|
||||
components: {
|
||||
TreeFolder,
|
||||
AppContentList,
|
||||
},
|
||||
props: {
|
||||
showDetails: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
rootFolder() {
|
||||
return this.$store.getters.getFolder(-1)[0]
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSelect(folder) {
|
||||
this.$router.push({ name: privateRoutes.FOLDER, params: { folder } })
|
||||
this.$emit('update:show-details', true)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.app-content-list {
|
||||
padding: 5px;
|
||||
padding-top: 45px;
|
||||
}
|
||||
</style>
|
|
@ -21,14 +21,11 @@
|
|||
<HomeIcon :fill-color="colorMainText" />
|
||||
</h2>
|
||||
</div>
|
||||
<div v-for="folder of items" :key="folder.id" class="treefolder">
|
||||
<div class="treefolder__title" @click="folder.children && onSelect(folder.id)">
|
||||
<h3>
|
||||
<FolderIcon :fill-color="colorPrimaryElement" />
|
||||
{{ folder.title }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<TreeFolder v-for="folder of items"
|
||||
:key="folder.id"
|
||||
:folder="folder"
|
||||
:show-children="false"
|
||||
@select="folder.children && onSelect(folder.id)" />
|
||||
<div class="actions">
|
||||
<button class="button" @click="onSubmit">
|
||||
{{ t('bookmarks', 'Choose folder') }}
|
||||
|
@ -43,11 +40,17 @@ import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
|||
import FolderIcon from 'vue-material-design-icons/Folder'
|
||||
import ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft'
|
||||
import HomeIcon from 'vue-material-design-icons/Home'
|
||||
import TreeFolder from './TreeFolder'
|
||||
|
||||
export default {
|
||||
name: 'FolderPicker',
|
||||
components: {
|
||||
Actions, ActionButton, FolderIcon, ArrowLeftIcon, HomeIcon,
|
||||
TreeFolder,
|
||||
Actions,
|
||||
ActionButton,
|
||||
FolderIcon,
|
||||
ArrowLeftIcon,
|
||||
HomeIcon,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
|
@ -108,39 +111,6 @@ export default {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.treefolder__title .material-design-icon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.treefolder__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
margin: 0 -10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.treefolder__title * {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
{
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.treefolder__title:hover,
|
||||
.treefolder__title:focus {
|
||||
background: var(--color-background-dark);
|
||||
}
|
||||
|
||||
.treefolder__title > h3 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.folderpicker .actions {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
<!--
|
||||
- Copyright (c) 2022. The Nextcloud Bookmarks contributors.
|
||||
-
|
||||
- This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="{treefolder:true, active}">
|
||||
<div class="treefolder__title" @click="$emit('select', folder.id)">
|
||||
<h3>
|
||||
<FolderIcon v-if="!childrenShown"
|
||||
class="treefolder__icon-hover"
|
||||
:fill-color="colorPrimaryElement"
|
||||
@click.stop="folder.children.length && showChildren && (childrenShown = true)" />
|
||||
<FolderOpenIcon v-else
|
||||
class="treefolder__icon-hover"
|
||||
:fill-color="colorPrimaryElement"
|
||||
@click.stop="folder.children.length && (childrenShown = false)" />
|
||||
{{ folder.title }}
|
||||
</h3>
|
||||
</div>
|
||||
<div v-if="showChildren && childrenShown" class="treefolder__children">
|
||||
<TreeFolder v-for="f in folder.children"
|
||||
:key="f.id"
|
||||
:folder="f"
|
||||
@select="$emit('select', $event)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FolderIcon from 'vue-material-design-icons/Folder'
|
||||
import FolderOpenIcon from 'vue-material-design-icons/FolderOpen'
|
||||
import { privateRoutes } from '../router'
|
||||
export default {
|
||||
name: 'TreeFolder',
|
||||
components: { FolderIcon, FolderOpenIcon },
|
||||
props: {
|
||||
folder: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
showChildren: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childrenShown: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
active() {
|
||||
return this.$route.params.folder === this.folder.id
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
if (this.$route.name === privateRoutes.FOLDER
|
||||
&& (this.$route.params.folder === this.folder.id
|
||||
|| this.folder.children.find(f => f.id === this.$route.params.folder))
|
||||
) {
|
||||
this.childrenShown = true
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.treefolder__title .material-design-icon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.treefolder__icon-hover:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.treefolder__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
margin: 0 -10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.treefolder__title * {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.treefolder.active > .treefolder__title,
|
||||
.treefolder__title:hover,
|
||||
.treefolder__title:focus {
|
||||
background: var(--color-background-dark);
|
||||
}
|
||||
|
||||
.treefolder__title > h3 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.treefolder__children .treefolder__title {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.treefolder__children .treefolder__children .treefolder__title {
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.treefolder__children .treefolder__children .treefolder__children .treefolder__title {
|
||||
padding-left: 75px;
|
||||
}
|
||||
|
||||
.treefolder__children .treefolder__children .treefolder__children .treefolder__children .treefolder__title {
|
||||
padding-left: 100px;
|
||||
}
|
||||
|
||||
.treefolder__children .treefolder__children .treefolder__children .treefolder__children .treefolder__children .treefolder__title {
|
||||
padding-left: 125px;
|
||||
}
|
||||
</style>
|
|
@ -7,7 +7,10 @@
|
|||
<template>
|
||||
<Content app-name="bookmarks">
|
||||
<Navigation />
|
||||
<AppContent>
|
||||
<AppContent :show-details.sync="showDetails">
|
||||
<template v-if="isFolderView && !smallScreen && folders.length" #list>
|
||||
<FolderOverview :show-details.sync="showDetails" />
|
||||
</template>
|
||||
<Controls />
|
||||
<BookmarksList />
|
||||
</AppContent>
|
||||
|
@ -24,6 +27,7 @@
|
|||
import Content from '@nextcloud/vue/dist/Components/Content'
|
||||
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
|
||||
import Navigation from './Navigation'
|
||||
import FolderOverview from './FolderOverview'
|
||||
import BookmarksList from './BookmarksList'
|
||||
import Controls from './Controls'
|
||||
import SidebarBookmark from './SidebarBookmark'
|
||||
|
@ -44,6 +48,7 @@ export default {
|
|||
Navigation,
|
||||
Content,
|
||||
AppContent,
|
||||
FolderOverview,
|
||||
Controls,
|
||||
BookmarksList,
|
||||
SidebarBookmark,
|
||||
|
@ -54,6 +59,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
newBookmark: false,
|
||||
showDetails: false,
|
||||
smallScreen: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -63,6 +70,9 @@ export default {
|
|||
tags() {
|
||||
return this.$store.state.tags
|
||||
},
|
||||
isFolderView() {
|
||||
return this.$route.name === privateRoutes.FOLDER || this.$route.name === privateRoutes.HOME
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -70,6 +80,10 @@ export default {
|
|||
},
|
||||
|
||||
async created() {
|
||||
const mediaQuery = window.matchMedia('(max-width: 1024px)')
|
||||
this.smallScreen = mediaQuery.matches
|
||||
mediaQuery.addEventListener('change', this.onWindowFormatChange)
|
||||
|
||||
if (OCA.Search) {
|
||||
// legacy search pre nc v20
|
||||
this.search = new window.OCA.Search(this.onSearch, this.onResetSearch)
|
||||
|
@ -188,6 +202,10 @@ export default {
|
|||
const dataEl = resDocument.querySelector('data')
|
||||
return dataEl.firstElementChild.textContent
|
||||
},
|
||||
|
||||
onWindowFormatChange(mediaQuery) {
|
||||
this.smallScreen = mediaQuery.matches
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -197,7 +215,7 @@ export default {
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
@media only screen and (max-width: 720px) {
|
||||
#app-content {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue