project -> blog

This commit is contained in:
lightling 2024-06-25 20:34:08 -04:00
parent 3f9cf6a057
commit 424a8d0837
10 changed files with 72 additions and 72 deletions

View file

@ -1,6 +1,6 @@
import { Router } from 'vue-router' import { Router } from 'vue-router'
import { BlogEntry } from '../content/templates/blog-list'
import { GalleryEntry } from '../content/templates/gallery-list' import { GalleryEntry } from '../content/templates/gallery-list'
import { ProjectListingInfo } from '../content/templates/project-list'
import { TemplateType } from '../content/templates/templateType' import { TemplateType } from '../content/templates/templateType'
import { WarningModal } from './warnings' import { WarningModal } from './warnings'
@ -39,10 +39,10 @@ export type MarkdownDefinition = ContentfulRouteDefintion & {
} }
/** /**
* Defines the config for a route using the `project-list` {@link TemplateType} * Defines the config for a route using the `blog-list` {@link TemplateType}
*/ */
export type ProjectListDefinition = ConfigfulRouteDefinition & { export type BlogListDefinition = ConfigfulRouteDefinition & {
template: 'project-list' template: 'blog-list'
view: { view: {
stylesheetUrls: string[] stylesheetUrls: string[]
} }
@ -63,7 +63,7 @@ export type GalleryListDefinition = ConfigfulRouteDefinition & {
*/ */
export type RouteDefinition = export type RouteDefinition =
| MarkdownDefinition | MarkdownDefinition
| ProjectListDefinition | BlogListDefinition
| GalleryListDefinition | GalleryListDefinition
/** /**
@ -77,9 +77,9 @@ export type RouteCollection = { [key: string]: RouteDefinition }
export interface RoutedWindow extends Window { export interface RoutedWindow extends Window {
/** refers to a template's primary route config; may briefly refer to sub config until a view is fully resolved */ /** refers to a template's primary route config; may briefly refer to sub config until a view is fully resolved */
routeConfig: RouteDefinition routeConfig: RouteDefinition
/** refers to a template's sub config in the case of child routes under a template (e.g. project-view under project-list) */ /** refers to a template's sub config in the case of child routes under a template (e.g. blog-view under blog-list) */
routeSubConfig: any routeSubConfig: any
/** refers to content config for various view routes (e.g. {@link GalleryEntry}, {@link ProjectListingInfo}, etc.) */ /** refers to content config for various view routes (e.g. {@link GalleryEntry}, {@link BlogEntry}, etc.) */
routeContentConfig: any routeContentConfig: any
/** vue-router instance */ /** vue-router instance */
router?: Router router?: Router

View file

@ -1,5 +1,5 @@
/** /**
* Defines a tag used by entries in a gallery-list/project-list, * Defines a tag used by entries in a gallery-list/blog-list,
* used for filtering entries from view * used for filtering entries from view
*/ */
export type EntryTag = { export type EntryTag = {
@ -19,11 +19,11 @@ export type EntryTag = {
} }
/** /**
* Defines the list of tags in a gallery-list/project-list, * Defines the list of tags in a gallery-list/blog-list,
* a key-value object where the value is the entry, * a key-value object where the value is the entry,
* and the key represents the id of a tag; * and the key represents the id of a tag;
* the id of a tag must be unique, * the id of a tag must be unique,
* and the ids specified in a gallery/project entry must match * and the ids specified in a gallery/blog entry must match
* the ids specified in `EntryTagCollection` in order for them to work effectively * the ids specified in `EntryTagCollection` in order for them to work effectively
*/ */
export type EntryTagCollection = { [id: string]: EntryTag } export type EntryTagCollection = { [id: string]: EntryTag }

View file

@ -3,22 +3,22 @@ import type { EntryTagCollection } from '../entryTag'
import type { EntryWithConfig, EntryWithContent } from './shared' import type { EntryWithConfig, EntryWithContent } from './shared'
/** /**
* This describes aditional information about a project. * This describes aditional information about a blog entry
* to display when listing the project on the portfolio page. * to display when listing the entry on the blog-list page.
* At minimum, the title should be specified. * At minimum, the title should be specified.
*/ */
export type ProjectListingInfo = { export type BlogEntry = {
date?: DateRange | string | number date?: DateRange | string | number
/**[Supports Markdown] /**[Supports Markdown]
* Information to summarize a project * Information to summarize an entry
*/ */
description?: string description?: string
/** /**
* Tags that correspond to project filters on the portfolio page if defined * Tags that correspond to filters on the blog-list page if defined
*/ */
tags?: string[] tags?: string[]
/**[Supports Markdown] /**[Supports Markdown]
* The title of the project * The title of the blog entry
*/ */
title: string title: string
thumbnail?: { thumbnail?: {
@ -26,13 +26,13 @@ export type ProjectListingInfo = {
} }
} }
export type ProjectEntries = { [key: string]: export type BlogEntries = { [key: string]:
& EntryWithContent & EntryWithContent
& EntryWithConfig<ProjectListingInfo> & EntryWithConfig<BlogEntry>
} }
export type ProjectList = { export type BlogList = {
projects: ProjectEntries entries: BlogEntries
tags?: EntryTagCollection tags?: EntryTagCollection
removeFromView?: boolean removeFromView?: boolean
} }

View file

@ -4,5 +4,5 @@
*/ */
export type TemplateType = export type TemplateType =
| 'markdown' | 'markdown'
| 'project-list' | 'blog-list'
| 'gallery-list' | 'gallery-list'

View file

@ -8,6 +8,6 @@ export * from './content/dateRange'
export * from './content/entryTag' export * from './content/entryTag'
export * from './content/link' export * from './content/link'
export * from './content/templates/blog-list'
export * from './content/templates/gallery-list' export * from './content/templates/gallery-list'
export * from './content/templates/project-list'
export * from './content/templates/templateType' export * from './content/templates/templateType'

View file

@ -5,21 +5,21 @@ import type {
GalleryListDefinition, GalleryListDefinition,
HeaderEntry, HeaderEntry,
Link, Link,
ProjectListDefinition, BlogListDefinition,
RouteDefinition, RouteDefinition,
SiteGlobals, SiteGlobals,
TemplateType, TemplateType,
} from '@goldenwere/mackenzii-types' } from '@goldenwere/mackenzii-types'
const markdownBody = () => import ('./views/markdown/markdown.vue') const markdownBody = () => import ('./views/markdown/markdown.vue')
const projectListBody = () => import ('./views/project/project-list.vue') const blogListBody = () => import ('./views/blog/blog-list.vue')
const projectViewBody = () => import ('./views/project/project-view.vue') const blogViewBody = () => import ('./views/blog/blog-view.vue')
const galleryListBody = () => import ('./views/gallery/gallery-list.vue') const galleryListBody = () => import ('./views/gallery/gallery-list.vue')
const galleryViewBody = () => import ('./views/gallery/gallery-view.vue') const galleryViewBody = () => import ('./views/gallery/gallery-view.vue')
export const templates: Record<TemplateType, () => Promise<any>> = { export const templates: Record<TemplateType, () => Promise<any>> = {
'markdown': markdownBody, 'markdown': markdownBody,
'project-list': projectListBody, 'blog-list': blogListBody,
'gallery-list': galleryListBody, 'gallery-list': galleryListBody,
} }
@ -33,11 +33,11 @@ export const createRoutes = (): RouteRecordRaw[] => {
component: templates[routes[route].template], component: templates[routes[route].template],
} }
if (routes[route].template === 'project-list') { if (routes[route].template === 'blog-list') {
routeRecord.push({ routeRecord.push({
name: `${routes[route].id}: View Project`, name: `${routes[route].id}: View Blog`,
path: `${route}/view`, path: `${route}/view`,
component: projectViewBody, component: blogViewBody,
props: route => ({ id: route.query.id }), props: route => ({ id: route.query.id }),
}) })
} else if (routes[route].template === 'gallery-list') { } else if (routes[route].template === 'gallery-list') {
@ -129,10 +129,10 @@ export const initializeRouteStore = (routerRoutes: readonly RouteRecordRaw[]) =>
...routerRoutes.find(other => other.path === route) as RouteRecordRaw, ...routerRoutes.find(other => other.path === route) as RouteRecordRaw,
...routes[route] as RouteDefinition, ...routes[route] as RouteDefinition,
} }
if (routes[route].template === 'project-list' || routes[route].template === 'gallery-list') { if (routes[route].template === 'blog-list' || routes[route].template === 'gallery-list') {
routeStore._routes[`${route}/view`] = { routeStore._routes[`${route}/view`] = {
...routerRoutes.find(other => other.path === `${route}/view`) as RouteRecordRaw, ...routerRoutes.find(other => other.path === `${route}/view`) as RouteRecordRaw,
...(routes[route] as ProjectListDefinition | GalleryListDefinition).view, ...(routes[route] as BlogListDefinition | GalleryListDefinition).view,
} as any } as any
} }
}) })

View file

@ -121,7 +121,7 @@ export const fetchAndParseYaml = async <T>(path: string) => {
/** /**
* Fetches, sanitizes, and parses Markdown files * Fetches, sanitizes, and parses Markdown files
* @param path the path of the markdown document to load * @param path the path of the markdown document to load
* @returns the project parsed from the markdown document * @returns the content parsed from the markdown document
*/ */
export const fetchAndParseMarkdown = async (path: string) => { export const fetchAndParseMarkdown = async (path: string) => {
const document = await fetchAndReturnText(path) const document = await fetchAndReturnText(path)

View file

@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import type { import type {
ProjectList, BlogList,
ProjectListingInfo, BlogEntry,
ProjectListDefinition, BlogListDefinition,
} from '@goldenwere/mackenzii-types' } from '@goldenwere/mackenzii-types'
import { fetchAndParseYaml, fetchConfig } from 'src/utilities/fetch' import { fetchAndParseYaml, fetchConfig } from 'src/utilities/fetch'
@ -11,27 +11,27 @@ import { getCurrentRoute } from 'src/utilities/vuetils'
import { useRouteStore } from 'src/routes' import { useRouteStore } from 'src/routes'
import FilterPanel from 'src/components/shared/filter-panel.vue' import FilterPanel from 'src/components/shared/filter-panel.vue'
import ProjectTile from './project-tile.vue' import BlogTile from './blog-tile.vue'
/** /**
* A wrapper around {@link GalleryEntries} for the app's use only which adds additional fields * A wrapper around {@link GalleryEntries} for the app's use only which adds additional fields
* in order for the app to effectively display the entries. * in order for the app to effectively display the entries.
*/ */
type ProjectDisplayedEntries = { [idOrTitle: string]: ProjectListingInfo & { type BlogDisplayedEntries = { [idOrTitle: string]: BlogEntry & {
/** /**
* specifies whether the entry is hidden by the tags selected by a visitor * specifies whether the entry is hidden by the tags selected by a visitor
*/ */
isHidden?: boolean isHidden?: boolean
}} }}
const projectIds = ref([] as string[]) const entryIds = ref([] as string[])
const projects = ref({} as ProjectDisplayedEntries) const entries = ref({} as BlogDisplayedEntries)
const ready = ref(false) const ready = ref(false)
const currentRoute = getCurrentRoute() const currentRoute = getCurrentRoute()
const routeStore = useRouteStore() const routeStore = useRouteStore()
const routeConfig = routeStore._routes[currentRoute.path] as ProjectListDefinition const routeConfig = routeStore._routes[currentRoute.path] as BlogListDefinition
const config = ref(null as ProjectList | null) const config = ref(null as BlogList | null)
const projectViewPath = computed(() => `${currentRoute.path}/view`) const viewPath = computed(() => `${currentRoute.path}/view`)
/** /**
* Handler for a tag being selected; * Handler for a tag being selected;
@ -40,22 +40,22 @@ const projectViewPath = computed(() => `${currentRoute.path}/view`)
*/ */
const onToggledTagsChanged = (tagsToggled: string[]) => { const onToggledTagsChanged = (tagsToggled: string[]) => {
if (tagsToggled.length < 1) { if (tagsToggled.length < 1) {
Object.keys(projects.value).forEach(entryId => { Object.keys(entries.value).forEach(entryId => {
projects.value[entryId].isHidden = false entries.value[entryId].isHidden = false
}) })
} else { } else {
Object.keys(projects.value).forEach(entryId => { Object.keys(entries.value).forEach(entryId => {
projects.value[entryId].isHidden = !projects.value[entryId].tags?.some(own => tagsToggled.includes(own)) entries.value[entryId].isHidden = !entries.value[entryId].tags?.some(own => tagsToggled.includes(own))
}) })
} }
} }
onMounted(async () => { onMounted(async () => {
config.value = await fetchAndParseYaml<ProjectList>(routeConfig.config) config.value = await fetchAndParseYaml<BlogList>(routeConfig.config)
projectIds.value = Object.keys(config.value.projects) entryIds.value = Object.keys(config.value.entries)
for (let i = 0; i < projectIds.value.length; ++i) { for (let i = 0; i < entryIds.value.length; ++i) {
const id = projectIds.value[i] const id = entryIds.value[i]
projects.value[id] = await fetchConfig(config.value.projects[id]) entries.value[id] = await fetchConfig(config.value.entries[id])
} }
document.title = routeConfig.fullTitle document.title = routeConfig.fullTitle
ready.value = true ready.value = true
@ -63,20 +63,20 @@ onMounted(async () => {
</script> </script>
<template lang="pug"> <template lang="pug">
.template.project-list .template.blog-list
Transition Transition
.projects( .blog(
v-if='ready' v-if='ready'
) )
Transition( Transition(
v-for='id in projectIds' v-for='id in entryIds'
) )
ProjectTile( BlogTile(
v-if='!projects[id].isHidden || !config.removeFromView' v-if='!entries[id].isHidden || !config.removeFromView'
v-bind='projects[id]' v-bind='entries[id]'
:class='{ hidden: projects[id].isHidden && !config.removeFromView }' :class='{ hidden: entries[id].isHidden && !config.removeFromView }'
:id='id' :id='id'
:viewPath='projectViewPath' :viewPath='viewPath'
:isInternal='true' :isInternal='true'
) )
Transition Transition

View file

@ -3,14 +3,14 @@ import { computed } from 'vue'
import { marked } from 'marked' import { marked } from 'marked'
import { getFormattedDate } from 'src/utilities/parse' import { getFormattedDate } from 'src/utilities/parse'
import type { import type {
ProjectListingInfo, BlogEntry,
} from '@goldenwere/mackenzii-types' } from '@goldenwere/mackenzii-types'
const props = defineProps<{ const props = defineProps<{
id: string, id: string,
viewPath: string, viewPath: string,
isInternal: boolean, isInternal: boolean,
} & ProjectListingInfo>() } & BlogEntry>()
const { thumbnail } = props const { thumbnail } = props
const description = computed(() => marked.parse(props.description || '')) const description = computed(() => marked.parse(props.description || ''))
@ -40,7 +40,7 @@ mixin embedText
v-if='date' v-if='date'
) )
span {{ date }} span {{ date }}
.project-embed .blog-embed
.thumbnail-wrapper( .thumbnail-wrapper(
v-if='!!thumbnail' v-if='!!thumbnail'
) )

View file

@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import type { import type {
ProjectList, BlogList,
ProjectListingInfo, BlogEntry,
ProjectListDefinition, BlogListDefinition,
RoutedWindow, RoutedWindow,
} from '@goldenwere/mackenzii-types' } from '@goldenwere/mackenzii-types'
@ -20,19 +20,19 @@ const emits = defineEmits<{
}>() }>()
const ready = ref(false) const ready = ref(false)
const info = ref(null! as ProjectListingInfo) const info = ref(null! as BlogEntry)
const content = ref('') const content = ref('')
const currentRoute = getCurrentRoute() const currentRoute = getCurrentRoute()
const routeStore = useRouteStore() const routeStore = useRouteStore()
const routeConfig = routeStore._routes[currentRoute.path.substring(0, currentRoute.path.length - 5)] as ProjectListDefinition const routeConfig = routeStore._routes[currentRoute.path.substring(0, currentRoute.path.length - 5)] as BlogListDefinition
const routeSubConfig = routeStore._routes[currentRoute.path] const routeSubConfig = routeStore._routes[currentRoute.path]
onMounted(async () => { onMounted(async () => {
const config = await fetchAndParseYaml<ProjectList>(routeConfig.config) const config = await fetchAndParseYaml<BlogList>(routeConfig.config)
info.value = await fetchConfig(config.projects[currentRoute.query.id as string]) info.value = await fetchConfig(config.entries[currentRoute.query.id as string])
const md = await fetchContent(config.projects[currentRoute.query.id as string]) const md = await fetchContent(config.entries[currentRoute.query.id as string])
content.value = md content.value = md
document.title = routeSubConfig.fullTitle?.replace('$PROJECT', info.value.title) document.title = routeSubConfig.fullTitle?.replace('$ENTRY', info.value.title)
routeStore.setBreadcrumbs(currentRoute, info.value.title) routeStore.setBreadcrumbs(currentRoute, info.value.title)
window.routeConfig = {...routeConfig} window.routeConfig = {...routeConfig}
window.routeSubConfig = {...routeSubConfig} window.routeSubConfig = {...routeSubConfig}
@ -44,7 +44,7 @@ onMounted(async () => {
</script> </script>
<template lang="pug"> <template lang="pug">
.template.project-view .template.blog-view
Transition Transition
article( article(
v-if='ready' v-if='ready'