-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from murchinroom/feat-content
Add /blogs: Nuxt Content
- Loading branch information
Showing
18 changed files
with
5,251 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
public/blogs.test | ||
|
||
# Nuxt dev/build outputs | ||
.output | ||
.data | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<template> | ||
<div ref="breadcrumbContainer" class="relative"> | ||
<nav class="flex items-center space-x-2 max-w-screen-xl text-ellipsis overflow-scroll"> | ||
<template v-for="(item, index) in breadcrumbs" :key="index"> | ||
<div class="flex items-center"> | ||
|
||
<!-- for larger screens: show the full path --> | ||
<span class="max-md:hidden flex items-center"> | ||
<span v-if="index > 0" class="ml-0.5 mr-2">></span> | ||
<span @click="toggleTreeView(item._path)" :class="{'truncate max-w-xs mx-1 font-bold text-black cursor-default': item._path === $route.path, 'cursor-pointer text-gray-600 hover:text-gray-900 hover:underline': item._path !== $route.path}"> | ||
<Icon :name="item.children ? 'bx:bxs-folder-open' : 'bx:bxs-file'" class="mx-0.5 mb-1 inline-block align-middle"/> | ||
{{ item.title }} | ||
</span> | ||
</span> | ||
|
||
<!-- for small screens: fold the mid dirs --> | ||
<span class="md:hidden flex items-center" v-if="index === 0 || index >= breadcrumbs.length-2"> | ||
<span v-if="index > 0" class="ml-0.5 mr-2">></span> | ||
<span @click="toggleTreeView(item._path)" :class="{'truncate max-w-xs mx-1 font-bold text-black cursor-default': item._path === $route.path, 'cursor-pointer text-gray-600 hover:text-gray-900 hover:underline': item._path !== $route.path}"> | ||
<Icon :name="item.children ? 'bx:bxs-folder-open' : 'bx:bxs-file'" class="mr-0.5 mb-1 inline-block align-middle"/> | ||
<span v-if="index === 0 || index === breadcrumbs.length-1">{{ item.title }}</span> | ||
<span v-else>..</span> | ||
</span> | ||
</span> | ||
|
||
</div> | ||
</template> | ||
</nav> | ||
<div v-if="showTreeView" class="tree-panel absolute left-0 top-full mt-2 h-screen w-full bg-slate-50 shadow-md z-10 overflow-y-auto" @click.stop> | ||
<BlogsTreeView :items="navigation" :expandPath="expandPath" /> | ||
<button @click="showTreeView = false" class="absolute top-2 right-2 text-black bg-gray-200 rounded-full p-1">✖️</button> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import {defineComponent, ref, onMounted, onBeforeUnmount, watch} from 'vue'; | ||
import {useRoute} from 'vue-router'; | ||
export default defineComponent({ | ||
props: {navigation: {type: Array, required: true}}, | ||
setup(props) { | ||
const route = useRoute(); | ||
const breadcrumbs = ref([]); | ||
const showTreeView = ref(false); | ||
const expandPath = ref(''); | ||
const breadcrumbContainer = ref(null); | ||
const buildBreadcrumbs = (navigation) => { | ||
const pathParts = route.path.split('/').filter(Boolean); | ||
let currentLevel = navigation; | ||
breadcrumbs.value = pathParts.map(part => { | ||
const item = currentLevel.find(navItem => navItem._path.split('/').pop() === part); | ||
if (!item) return {title: part, _path: `/${part}`, children: []}; | ||
currentLevel = item.children || []; | ||
return item; | ||
}); | ||
}; | ||
const toggleTreeView = (path) => { | ||
expandPath.value = path; | ||
showTreeView.value = !showTreeView.value; | ||
}; | ||
const handleClickOutside = (event) => { | ||
if (breadcrumbContainer.value && !breadcrumbContainer.value.contains(event.target)) { | ||
showTreeView.value = false; | ||
} | ||
}; | ||
onMounted(() => { | ||
document.addEventListener('click', handleClickOutside); | ||
}); | ||
onBeforeUnmount(() => { | ||
document.removeEventListener('click', handleClickOutside); | ||
}); | ||
watch(() => props.navigation, (newNavigation) => { | ||
buildBreadcrumbs(newNavigation); | ||
}, {immediate: true}); | ||
return {breadcrumbs, showTreeView, expandPath, toggleTreeView, breadcrumbContainer}; | ||
} | ||
}); | ||
</script> | ||
|
||
<style scoped> | ||
nav { | ||
@apply flex items-center space-x-2; | ||
} | ||
.tree-panel { | ||
min-width: 80vw; | ||
max-width: 80vw; | ||
} | ||
span.truncate { | ||
/* min-width: 4rem; */ | ||
max-width: 36vw; /* Adjust the max-width as needed */ | ||
white-space: nowrap; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
} | ||
button { | ||
@apply text-black bg-gray-200 rounded-full p-1; | ||
} | ||
.absolute { | ||
width: auto; /* Remove fixed width */ | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<template> | ||
<div class="min-w-full p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 hover:-translate-y-1 duration-300 "> | ||
<h5 class="min-h-8 min-w-16 mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">{{ article?.title }}</h5> | ||
<p class="min-h-8 min-w-16 font-normal text-gray-700 dark:text-gray-400">{{ article?.description }}</p> | ||
<span class="min-h-8 min-w-16 font-mono">{{ article?.publishedAt }}</span> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
defineProps<{ | ||
article?: { | ||
title?: string, | ||
description?: string, | ||
publishedAt?: string, | ||
} | ||
}>() | ||
</script> | ||
|
||
<style scoped> | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<template> | ||
<div> | ||
<ContentNavigation v-slot="{ navigation }"> | ||
<div class="breadcrumb"> | ||
<BlogsBreadcrumb :navigation="navigation" /> | ||
</div> | ||
</ContentNavigation> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
</script> | ||
|
||
<style scoped> | ||
.breadcrumb { | ||
@apply flex items-center space-x-2; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<template> | ||
<div class="bg-black px-10 py-16 max-sm:py-10 mt-20 mx-auto max-w-5xl rounded-lg flex flex-col items-center text-center"> | ||
<div v-if="prevArticle || nextArticle"> | ||
<h2 class="text-white text-2xl md:text-4xl"> | ||
Continue reading... | ||
</h2> | ||
|
||
<p v-if="nextArticle" class="text-slate-500 mt-8 text-lg md:text-xl inline-flex items-center line-clamp-1"> | ||
<!-- <Icon class="h-8 m-2 text-slate-500" name="bx:bxs-chevron-left" size="24"/>--> | ||
<span class="mr-1">Next<span class="max-sm:hidden"> article</span>: </span> | ||
<a :href="nextArticle?._path" class="text-slate-100 max-w-xs line-clamp-1 hover:underline">{{ | ||
nextArticle.title | ||
}}</a> | ||
</p> | ||
|
||
<p v-if="prevArticle" class="text-slate-500 mt-4 text-lg md:text-xl inline-flex items-center line-clamp-1"> | ||
<!-- <Icon class="h-8 m-2 text-slate-500" name="bx:bxs-chevron-right" size="24"/>--> | ||
<span class="mr-1">Previous<span class="max-sm:hidden"> article</span>: </span> | ||
<a :href="prevArticle?._path" class="text-slate-100 max-w-xs line-clamp-1 hover:underline">{{ | ||
prevArticle.title | ||
}}</a> | ||
</p> | ||
</div> | ||
<div v-else> | ||
<h2 class="text-white text-2xl md:text-4xl"> | ||
Continue reading... | ||
</h2> | ||
|
||
<p class="text-slate-500 mt-8 text-lg md:text-xl inline-flex items-center line-clamp-1"> | ||
<!-- <Icon class="h-8 m-2 text-slate-500" name="bx:bxs-chevron-left" size="24"/>--> | ||
<span class="mr-1.5">Back<span class="max-sm:hidden"> to</span>: </span> | ||
<a href="/blogs" class="text-slate-100 max-w-xs line-clamp-1 hover:underline"> /blogs </a> | ||
</p> | ||
|
||
</div> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import {type ParsedContent} from "@nuxt/content"; | ||
interface ArticleMetadata { | ||
title?: string, | ||
_path?: string, | ||
// description?: string, | ||
// publishedAt?: string, | ||
} | ||
defineProps<{ | ||
prevArticle?: ArticleMetadata | ||
nextArticle?: ArticleMetadata | ||
}>() | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<template> | ||
<div class="toc"> | ||
<h2>{{ article?.title }}</h2> | ||
<p v-if="article?.description" class="description">{{ article.description }}</p> | ||
<p v-if="article?.publishedAt" class="published-at">{{ new Date(article.publishedAt).toLocaleDateString() }}</p> | ||
<ul> | ||
<li v-for="link in article?.body?.toc?.links" :key="link.id"> | ||
<a :href="'#' + link.id" @click.prevent="scrollToHeader(link.id)" :class="{'ml-4': link.depth === 3}"> | ||
{{ link.text }} | ||
</a> | ||
<ul v-if="link.children"> | ||
<li v-for="child in link.children" :key="child.id"> | ||
<a :href="'#' + child.id" @click.prevent="scrollToHeader(child.id)" class="ml-4"> | ||
{{ child.text }} | ||
</a> | ||
</li> | ||
</ul> | ||
</li> | ||
</ul> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import {type ParsedContent} from "@nuxt/content"; | ||
interface ArticleMetadata { | ||
title?: string; | ||
description?: string; | ||
publishedAt?: string; | ||
} | ||
defineProps<{ | ||
article?: ArticleMetadata & ParsedContent; | ||
}>(); | ||
const scrollToHeader = (id: string) => { | ||
const element = document.getElementById(id); | ||
if (element) { | ||
element.scrollIntoView({behavior: 'smooth'}); | ||
} | ||
}; | ||
</script> | ||
|
||
<style scoped> | ||
.toc { | ||
padding: 1rem; | ||
border: 1px solid #e0e0e0; | ||
border-radius: 8px; | ||
background-color: #f9f9f9; | ||
} | ||
h2 { | ||
font-size: 1.25rem; | ||
font-weight: bold; | ||
margin-bottom: 0.5rem; | ||
} | ||
.description { | ||
font-size: 1rem; | ||
color: #666; | ||
margin-bottom: 0.5rem; | ||
} | ||
.published-at { | ||
font-size: 0.875rem; | ||
color: #999; | ||
margin-bottom: 1rem; | ||
} | ||
ul { | ||
list-style-type: none; | ||
padding-left: 0; | ||
} | ||
a { | ||
text-decoration: none; | ||
color: #007bff; | ||
} | ||
a:hover { | ||
text-decoration: underline; | ||
} | ||
.ml-4 { | ||
margin-left: 1rem; | ||
} | ||
</style> |
Oops, something went wrong.