mackenzii/projects/frontend/src/routes.ts
2024-06-25 20:34:08 -04:00

146 lines
5 KiB
TypeScript

import { defineStore } from 'pinia'
import { type RouteLocationNormalizedLoaded, type RouteRecordRaw } from 'vue-router'
import { routes, siteGlobals } from 'content/routes.js'
import type {
GalleryListDefinition,
HeaderEntry,
Link,
BlogListDefinition,
RouteDefinition,
SiteGlobals,
TemplateType,
} from '@goldenwere/mackenzii-types'
const markdownBody = () => import ('./views/markdown/markdown.vue')
const blogListBody = () => import ('./views/blog/blog-list.vue')
const blogViewBody = () => import ('./views/blog/blog-view.vue')
const galleryListBody = () => import ('./views/gallery/gallery-list.vue')
const galleryViewBody = () => import ('./views/gallery/gallery-view.vue')
export const templates: Record<TemplateType, () => Promise<any>> = {
'markdown': markdownBody,
'blog-list': blogListBody,
'gallery-list': galleryListBody,
}
export const createRoutes = (): RouteRecordRaw[] => {
const routeRecord: RouteRecordRaw[] = []
Object.keys(routes).forEach(route => {
const toPush: RouteRecordRaw = {
name: routes[route].id,
path: route,
component: templates[routes[route].template],
}
if (routes[route].template === 'blog-list') {
routeRecord.push({
name: `${routes[route].id}: View Blog`,
path: `${route}/view`,
component: blogViewBody,
props: route => ({ id: route.query.id }),
})
} else if (routes[route].template === 'gallery-list') {
toPush.props = route => ({ variants: (route.query.v as string || '').split(';') })
routeRecord.push({
name: `${routes[route].id}: View Entry`,
path: `${route}/view`,
component: galleryViewBody,
props: route => ({ variants: (route.query.v as string || '').split(';') }),
})
}
routeRecord.push(toPush)
})
return routeRecord
}
export const useRouteStore = defineStore('routeStore', {
state: () => ({
_header: [] as HeaderEntry[],
_routes: {} as Record<string, RouteRecordRaw & RouteDefinition>,
_globals: {} as SiteGlobals,
_routesAlreadyWarned: {} as Record<string, boolean>,
_breadcrumbs: [] as Link[]
}),
actions: {
/**
* Determines whether a route has showed a warning or not
* @param route the route to check
* @returns `true` if previously warned, `false` otherwise
*/
doesRouteRememberWarning(route: string) {
return this._routesAlreadyWarned[route]
},
/**
* Remembers whether a route showed a warning or not
* @param route the route to remember
*/
rememberRouteWarning(route: string) {
this._routesAlreadyWarned[route] = true
},
/**
* Sets the breadcrumbs array in the store
* @param route the current route
* @param resolvedTitle a title related to dynamic content loaded from a list-based template when in the view for that list
*/
setBreadcrumbs(route: RouteLocationNormalizedLoaded, resolvedTitle?: string) {
// breadcrumb shouldn't show on home page
if (route.path === '/') {
this._breadcrumbs = []
return
}
// split '/my/path' into '', 'my', 'path'
const split = route.path.split('/')
// adjust for root; '', 'my', 'path' becomes '/', 'my', 'path'
split[0] = '/'
// compile paths from the split through some array functions
const paths = [] as string[]
split.forEach((val, i) => {
// get the paths leading up to the current index plus the next one
const sliced = split.slice(0, i + 1)
// reduce converts '/', 'my', 'path' to a breadcrumb trail '/', '/my', '/my/path'
// the conditional '/' check prevents accidentally prepending '//' to each breadcrumb
paths.push(sliced.reduce((prev, curr) => prev === '/' ? `/${curr}` : `${prev}/${curr}`))
})
// map paths into breadcrumb info
this._breadcrumbs = paths.map((path, index) => {
return {
href: path,
// the tail end should be the resolvedTitle from a view route if specified,
// otherwise use the title in the route definition
caption: index === paths.length - 1 && !!resolvedTitle ? resolvedTitle : this._routes[path]?.title,
}
})
}
},
})
export const initializeRouteStore = (routerRoutes: readonly RouteRecordRaw[]) => {
const routeStore = useRouteStore()
Object.keys(routes).forEach(route => {
routeStore._routes[route] = {
...routerRoutes.find(other => other.path === route) as RouteRecordRaw,
...routes[route] as RouteDefinition,
}
if (routes[route].template === 'blog-list' || routes[route].template === 'gallery-list') {
routeStore._routes[`${route}/view`] = {
...routerRoutes.find(other => other.path === `${route}/view`) as RouteRecordRaw,
...(routes[route] as BlogListDefinition | GalleryListDefinition).view,
} as any
}
})
routeStore._globals = siteGlobals
}
export type RouteStoreDefinition = Omit<
ReturnType<typeof useRouteStore>,
keyof ReturnType<typeof defineStore>
>