add blog feature to docs (#101)
All checks were successful
ci/woodpecker/push/docs Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

### 📖 Summary

- adds blog index page with card layout
- loads markdown pages in `posts/**/*.md`

### 📑 Test Plan

 CI pipeline tests (Default)

### 💬 Details

_No response_

### 📚 Additional Notes

_No response_

Reviewed-on: #101
This commit is contained in:
OCram85 2024-06-04 16:32:12 +02:00
parent 666c9503b7
commit 8e8febb936
9 changed files with 276 additions and 2 deletions

View File

@ -14,6 +14,7 @@ function getItems(version) {
return [
{ text: 'Home', link: '/' },
{ text: 'Blog', link: '/blog' },
{ text: 'Guide', link: '/guide/about' },
{
text: nver,

View File

@ -0,0 +1,51 @@
<script setup>
import Posts from './Posts.vue'
</script>
<template>
<div class="blog-container">
<h1>Blog</h1>
<h2> Featured Articles</h2>
<Posts featured/>
<h2>📰 All Posts</h2>
<Posts/>
</div>
</template>
<style scoped>
.blog-container {
margin: auto;
width: 100%;
max-width: 1280px;
padding: 0 64px;
}
h1, h2, h3 {
position: relative;
font-weight: 600;
outline: none;
}
h1 {
letter-spacing: -0.02em;
line-height: 40px;
font-size: 32px;
margin-top: 32px;
}
h2 {
margin: 40px 0 16px;
border-top: 1px solid var(--vp-c-divider);
padding-top: 24px;
letter-spacing: -0.02em;
line-height: 32px;
font-size: 24px;
}
h3 {
margin: 32px 0 0;
letter-spacing: -0.01em;
font-size: 20px;
}
</style>

View File

@ -0,0 +1,142 @@
<script setup>
import { computed } from 'vue'
import { withBase } from 'vitepress'
import Badge from 'vitepress/dist/client/theme-default/components/VPBadge.vue'
const props = defineProps({
image: {
type: String,
default: 'placeholder.jpg',
},
title: {
type: String,
required: true,
},
tag: {
type: String,
default: 'no-tag',
},
url: {
type: String,
required: true,
},
featured: {
type: Boolean,
default: false,
},
date: {
type: String,
default: '2020-01-01'
}
})
const normTitle = computed(() => {
if (props.title.length > 50) {
return props.title.slice(0, 50) + '...'
} else {
return props.title
}
})
const normImage = computed(() => {
if (props.image !== null) return withBase(props.image)
else {
return withBase('placeholder.jpg')
}
})
const normTag = computed(() => {
if (props.tag !== null) return props.tag.toUpperCase()
else {
return 'EMPTY-TAG'
}
})
</script>
<template>
<article class="card">
<div class="card-header">
<img :src="normImage" />
</div>
<div class="card-content">
<div class="badge-container">
<Badge :text="normTag" type="tip" />
</div>
<h3>{{ normTitle }}</h3>
<a :href="url">Read More</a>
</div>
</article>
</template>
<style scoped>
.card {
margin: 15px;
padding: 0px;
background-color: var(--vp-c-bg-soft);
border-color: var(--vp-c-brand-2);
border-style: solid;
border-width: 1px;
border-radius: 10px;
width: 325px;
/*height: 450px; */
overflow: hidden;
box-shadow: 0px 8px 16px var(--vp-c-brand-soft);
position: relative;
top: 0;
}
.card:hover {
border-color: var(--vp-c-brand-1);
filter: brightness(125%);
transition: 0.25s;
box-shadow: 0px 16px 32px var(--vp-c-brand-soft);
/*top: -10px;*/
}
.card-header {
width: 100%;
height: 200px;
}
.card-header img {
width: 100%;
height: 100%;
object-fit: cover;
}
.card-content {
padding: 10px;
height: 200px;
}
.card-content h3 {
margin-top: 5px;
height: 90px;
}
.badge-container {
margin: 15px;
}
h3 {
position: relative;
font-weight: 600;
outline: none;
margin: 32px 0 0;
letter-spacing: -0.01em;
line-height: 28px;
font-size: 20px;
}
a {
font-weight: 500;
color: var(--vp-c-brand-1);
text-decoration: underline;
text-underline-offset: 2px;
transition: color 0.025s, opacity 0.25s;
}
a:hover {
color: var(--vp-c-brand-2);
}
</style>

View File

@ -0,0 +1,53 @@
<script setup>
import { data } from './posts.data.js'
import Post from './Post.vue'
const props = defineProps({
tagFilter: {
type: String,
default: 'none',
},
featured: {
type: Boolean,
default: false,
},
})
const posts = data.filter((el) => {
if (!props.featured) {
if (props.tagFilter.toLowerCase() === 'none') {
return true
} else if (props.tagFilter.toLowerCase() === el.frontmatter.tag.toLowerCase()) {
return true
} else {
return false
}
} else {
if (el.frontmatter.featured) {
return true
} else return false
}
})
</script>
<template>
<section class="card-container">
<Post
:image="post.frontmatter.image"
:title="post.frontmatter.title"
:tag="post.frontmatter.tag"
:url="post.url"
:date="post.frontmatter.date"
v-for="post in posts"
/>
</section>
</template>
<style scoped>
.card-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
flex-direction: row;
}
</style>

View File

@ -0,0 +1,10 @@
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/**/*.md', {
excerpt: false,
transform(raw) {
return raw.sort((a, b) => {
return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
})
}
})

View File

@ -3,6 +3,8 @@ import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import './style.css'
import Blog from './components/Blog.vue'
/** @type {import('vitepress').Theme} */
export default {
extends: DefaultTheme,
@ -12,6 +14,6 @@ export default {
})
},
enhanceApp({ app, router, siteData }) {
// ...
}
app.component('blog', Blog)
},
}

3
docs/blog.md Normal file
View File

@ -0,0 +1,3 @@
---
layout: blog
---

View File

@ -0,0 +1,12 @@
---
author: OCram85
title: 'Working on Arkanum 2.0.0'
tag: 'roadmap'
image: 'screens/screen1.png'
date: '2024-06-06'
---
# Working on Arkanum 2.0
Lorem ipsum dolor sit amet consectetur adipisicing elit. Rerum dolorum ab optio, dolore saepe accusamus
au

BIN
docs/public/placeholder.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB