Skip to content

Commit

Permalink
Merge branch 'toc-upgrade'
Browse files Browse the repository at this point in the history
  • Loading branch information
HugoRCD committed Dec 5, 2024
2 parents ae83bc5 + 40a9edf commit 29884ce
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 47 deletions.
2 changes: 1 addition & 1 deletion app/components/layout/LetterCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
</script>

<template>
<div class="flex size-full max-w-7xl flex-1 flex-col justify-between gap-3 rounded-sm border-2 border-main p-4 sm:p-6">
<div class="flex size-full max-w-7xl flex-1 flex-col justify-between gap-3 rounded-sm sm:border-2 border-main p-4 sm:p-6">
<slot />
</div>
</template>
Expand Down
73 changes: 65 additions & 8 deletions app/components/toc/Index.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,78 @@
<script setup lang="ts">
import type { TocLink } from '@nuxt/content'
const { title = 'Table of Contents', links = [] } = defineProps<{
title?: string
const { links = [] } = defineProps<{
links: TocLink[]
}>()
const isOpen = ref(false)
const isMobile = ref(false)
const target = ref(null)
const { activeHeadings, updateHeadings } = useScrollspy()
const nuxtApp = useNuxtApp()
const checkMobile = () => {
isMobile.value = window.innerWidth < 640
}
const handleInteraction = (event: 'enter' | 'leave' | 'click') => {
if (isMobile.value) {
if (event === 'click') {
isOpen.value = !isOpen.value
}
} else {
isOpen.value = event === 'enter'
}
}
onMounted(() => {
checkMobile()
window.addEventListener('resize', checkMobile)
})
onUnmounted(() => {
window.removeEventListener('resize', checkMobile)
})
nuxtApp.hooks.hookOnce('page:finish', () => {
updateHeadings([
...document.querySelectorAll('h2'),
...document.querySelectorAll('h3')
])
})
onClickOutside(target, event => {
if (isOpen.value) isOpen.value = false
})
</script>

<template>
<div class="max-sm:hidden fixed z-50 scale-[0.3] hover:scale-100 transition-transform duration-200 ease-in-out right-4 top-1/2 -translate-y-1/2 origin-right">
<div class="bg-primary p-4 shadow-md w-fit rounded-md mx-auto border border-secondary/20">
<div
class="fixed z-50 scale-[0.8] sm:scale-[0.6] transition-all duration-300 ease-in-out right-2 top-1/2 -translate-y-1/2 origin-right"
:class="[
!isMobile && 'hover:scale-100'
]"
>
<div
ref="target"
class="rounded-md mx-auto transition-all duration-300 ease-in-out"
:class="[
isOpen ? 'bg-primary shadow-md border border-secondary/20 p-4' : 'p-0 border-transparent'
]"
@mouseenter="handleInteraction('enter')"
@mouseleave="handleInteraction('leave')"
@click="isMobile && handleInteraction('click')"
>
<nav class="overflow-y-auto">
<div>
<button v-if="links?.length" tabindex="-1" class="group flex w-full items-center gap-1.5 lg:cursor-text lg:select-text">
<span class="truncate text-sm/6 font-newsreader italic font-light font-semibold">{{ title }}</span>
</button>
<TocLinks :links />
<TocLinks
:links
:is-hover="isOpen"
:active-headings
:is-mobile
/>
</div>
</nav>
</div>
Expand Down
107 changes: 72 additions & 35 deletions app/components/toc/Links.vue
Original file line number Diff line number Diff line change
@@ -1,50 +1,87 @@
<script setup lang="ts">
import type { TocLink } from '@nuxt/content'
import { useScrollspy, useNuxtApp, useRouter } from '#imports'
const { activeHeadings, updateHeadings } = useScrollspy()
type ContentTocProps = {
title?: string
const { links = [], isHover, isMobile } = defineProps<{
links: TocLink[]
active?: string
}
const { title = 'Table of Contents', links = [] } = defineProps<ContentTocProps>()
isHover: boolean
activeHeadings: string[]
isMobile: boolean
}>()
const nuxtApp = useNuxtApp()
const router = useRouter()
nuxtApp.hooks.hookOnce('page:finish', () => {
updateHeadings([
...document.querySelectorAll('h2'),
...document.querySelectorAll('h3')
])
})
const emit = defineEmits(['move'])
const scrollToHeading = (id: string): void => {
const encodedId = encodeURIComponent(id)
router.push(`#${encodedId}`)
emit('move', id)
if (!isMobile || (isMobile && isHover)) {
const encodedId = encodeURIComponent(id)
router.push(`#${encodedId}`)
}
}
</script>

<template>
<ul v-if="links?.length">
<li v-for="link in links" :key="link.text" :class="link.depth === 3 ? 'ml-3' : ''">
<a
class="block truncate text-xs/6"
:class="activeHeadings.includes(link.id) ? 'text-accent' : 'text-tertiary hover:text-gray-700 dark:hover:text-gray-200'"
:href="`#${link.id}`"
@click.prevent="scrollToHeading(link.id)"
>
{{ link.text }}
</a>
<div>
<Transition
enter-active-class="transition-all duration-100 ease-in-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-all duration-100 ease-in-out"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
mode="out-in"
>
<ul v-if="links?.length && isHover" class="space-y-2">
<li
v-for="link in links"
:key="link.text"
:class="[
link.depth === 3 ? 'ml-4' : ''
]"
>
<a
class="block truncate text-xs/6 transition-all duration-500 ease-in-out"
:class="activeHeadings.includes(link.id) ? 'text-accent' : 'text-tertiary hover:text-gray-700 dark:hover:text-gray-200'"
:href="`#${link.id}`"
@click.prevent="scrollToHeading(link.id)"
>
{{ link.text }}
</a>

<Links
v-if="link.children"
:links="link.children"
:is-hover
:active-headings
:is-mobile
/>
</li>
</ul>
<div v-else-if="links?.length" class="space-y-4">
<div
v-for="link in links"
:key="link.text"
class="flex flex-col items-end gap-1"
>
<div
class="h-[4px] rounded-full transition-all duration-500 ease-in-out"
:class="[
activeHeadings.includes(link.id)
? 'w-12 bg-accent'
: 'w-8 bg-secondary/20',
link.depth === 3 ? 'w-6' : '',
isMobile ? 'cursor-default' : 'cursor-pointer'
]"
/>

<Links v-if="link.children" :links="link.children" />
</li>
</ul>
<Links
v-if="link.children"
:links="link.children"
:is-hover
:active-headings
:is-mobile
/>
</div>
</div>
</Transition>
</div>
</template>
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default defineNuxtConfig({
'@nuxt/image',
'@nuxtjs/seo',
'@nuxt/scripts',
'@vueuse/nuxt',
],

runtimeConfig: {
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
},
"devDependencies": {
"@hrcd/eslint-config": "edge",
"@nuxt/content": "^2.13.4",
"@nuxt/image": "^1.8.1",
"@nuxt/content": "2.13.4",
"@nuxt/image": "1.8.1",
"@nuxt/scripts": "0.9.5",
"@nuxthq/studio": "2.2.1",
"@nuxtjs/seo": "2.0.2",
"@vueuse/nuxt": "12.0.0",
"automd": "0.3.12",
"eslint": "9.16.0",
"mockline": "^0.6.0",
"mockline": "0.6.0",
"nuxt": "3.14.1592",
"resend": "4.0.1",
"vue": "3.5.13",
Expand Down

0 comments on commit 29884ce

Please sign in to comment.