146 lines
5 KiB
TypeScript
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>
|
|
>
|
|
|