Breadcrumbs keep longest path, browsing breadcrumbs with left/right arrows, highlight current.
This commit is contained in:
parent
5386508e28
commit
b25d0fc14b
|
@ -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>
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user