Breadcrumbs keep longest path, browsing breadcrumbs with left/right arrows, highlight current.

This commit is contained in:
Leo Vasanko 2023-11-06 18:51:51 +00:00
parent 5386508e28
commit b25d0fc14b
5 changed files with 78 additions and 27 deletions

View File

@ -1,3 +1,16 @@
<template>
<LoginModal />
<header>
<HeaderMain ref="headerMain">
<HeaderSelected :path="path.pathList" />
</HeaderMain>
<BreadCrumb :path="path.pathList" />
</header>
<main>
<RouterView :path="path.pathList" />
</main>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
@ -123,14 +136,3 @@ onUnmounted(() => {
}) })
export type { Path } export type { Path }
</script> </script>
<template>
<LoginModal />
<header>
<HeaderMain ref="headerMain"><HeaderSelected :path="path.pathList" /></HeaderMain>
<BreadCrumb :path="path.pathList" />
</header>
<main>
<RouterView :path="path.pathList" />
</main>
</template>

View File

@ -35,6 +35,7 @@
} }
.breadcrumb { .breadcrumb {
font-size: 1.7em; font-size: 1.7em;
flex-shrink: 10;
} }
} }
@media screen and (min-width: 800px) and (--webkit-min-device-pixel-ratio: 2) { @media screen and (min-width: 800px) and (--webkit-min-device-pixel-ratio: 2) {

View File

@ -1,18 +1,68 @@
<template> <template>
<div class="breadcrumb"> <nav
<a href="#/"><component :is="home" /></a> class="breadcrumb"
<template v-for="(location, index) in props.path" :key="index"> aria-label="Breadcrumb"
<a :href="`/#/${props.path.slice(0, index + 1).join('/')}/`">{{ location }}</a> tabindex="0"
@keyup.left.stop="move(-1)"
@keyup.right.stop="move(1)"
@focus="move(0)"
>
<a href="#/"
:ref="el => setLinkRef(0, el)"
:class="{ current: !!isCurrent(0) }"
:aria-current="isCurrent(0)"
>
<component :is="home" />
</a>
<template v-for="(location, index) in longest" :key="index">
<a :href="`/#/${longest.slice(0, index + 1).join('/')}/`"
:class="{ current: !!isCurrent(index + 1) }"
:aria-current="isCurrent(index + 1)"
@click.prevent="navigate(index + 1)"
:ref="el => setLinkRef(index + 1, el)"
>{{ location }}</a>
</template> </template>
</div> </nav>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import home from '@/assets/svg/home.svg' import home from '@/assets/svg/home.svg'
import { onBeforeUpdate, ref, watchEffect } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const links = [] as Array<HTMLElement>
const setLinkRef = (index: number, el: any) => { if (el) links[index] = el }
onBeforeUpdate(() => { links.length = 1 }) // 1 to keep home
const props = defineProps<{ const props = defineProps<{
path: Array<string> path: Array<string>
}>() }>()
const longest = ref<Array<string>>([])
watchEffect(() => {
const longcut = longest.value.slice(0, props.path.length)
const same = longcut.every((value, index) => value === props.path[index])
if (!same) longest.value = props.path
else if (props.path.length > longcut.length) {
longest.value = longcut.concat(props.path.slice(longcut.length))
}
})
const isCurrent = (index: number) => index == props.path.length ? 'location' : undefined
const navigate = (index: number) => {
links[index].focus()
router.replace(`/${longest.value.slice(0, index).join('/')}`)
}
const move = (dir: number) => {
const index = props.path.length + dir
if (index < 0 || index > longest.value.length) return
navigate(index)
}
</script> </script>
<style> <style>
@ -93,10 +143,8 @@ const props = defineProps<{
.breadcrumb a:focus:nth-child(even) { .breadcrumb a:focus:nth-child(even) {
background: var(--breadcrumb-hover-background-even); background: var(--breadcrumb-hover-background-even);
} }
.breadcrumb a:hover { .breadcrumb a:hover { color: var(--breadcrumb-hover-color) }
color: var(--breadcrumb-hover-color); .breadcrumb a:hover svg { fill: var(--breadcrumb-hover-color) }
} .breadcrumb a.current { color: var(--accent-color) }
.breadcrumb a:hover svg { .breadcrumb a.current svg { fill: var(--accent-color) }
fill: var(--breadcrumb-hover-color);
}
</style> </style>

View File

@ -36,7 +36,7 @@ defineExpose({
@click="() => documentStore.fileExplorer.newFolder()" @click="() => documentStore.fileExplorer.newFolder()"
/> />
<slot></slot> <slot></slot>
<div class="spacer"></div> <div class="spacer smallgap"></div>
<template v-if="showSearchInput"> <template v-if="showSearchInput">
<input <input
ref="search" ref="search"
@ -66,6 +66,9 @@ defineExpose({
.spacer { .spacer {
flex-grow: 1; flex-grow: 1;
} }
.smallgap {
margin-left: 2em;
}
input[type='search'] { input[type='search'] {
background: var(--primary-background); background: var(--primary-background);
color: var(--primary-color); color: var(--primary-color);

View File

@ -142,10 +142,7 @@ const download = async () => {
} }
</script> </script>
<style scoped> <style>
.smallgap {
margin-left: 2em;
}
.select-text { .select-text {
color: var(--accent-color); color: var(--accent-color);
text-wrap: nowrap; text-wrap: nowrap;