diff --git a/libs/types/src/config/globals.d.ts b/libs/types/src/config/globals.d.ts index 03ca58f..dc2c046 100644 --- a/libs/types/src/config/globals.d.ts +++ b/libs/types/src/config/globals.d.ts @@ -1,4 +1,5 @@ import type { HeaderEntry } from './navigation' +import type { MediaEntryTag } from '../content/entryTag' import type { RouteCollection } from './routing' import type { SiteThemeList } from './themes' import type { WarningModal } from './warnings' @@ -12,5 +13,6 @@ export type SiteGlobals = { id: string stylesheetUrls: string[] themes: SiteThemeList + tags?: string | MediaEntryTag[] warning: WarningModal } diff --git a/libs/types/src/content/entryTag.d.ts b/libs/types/src/content/entryTag.d.ts index 9b7fa7c..a17bf76 100644 --- a/libs/types/src/content/entryTag.d.ts +++ b/libs/types/src/content/entryTag.d.ts @@ -2,7 +2,11 @@ * Defines a tag used by entries in a gallery-list/article-list, * used for filtering entries from view */ -export type EntryTag = { +export type MediaEntryTag = { + /** + * specifies the id for the tag + */ + tagId: string /** * specifies a category that the tag belongs to * in order to optionally organize them in the view; @@ -11,19 +15,13 @@ export type EntryTag = { * placed before any other sections formed by categories (if any) */ category?: string + /** + * can be used to describe a tag when hovering over it in the UI + */ + description?: string /** * specifies the name that the tag will appear as in the DOM; * if not specified, the id of the tag will be used in its place */ displayName?: string } - -/** - * Defines the list of tags in a gallery-list/article-list, - * a key-value object where the value is the entry, - * and the key represents the id of a tag; - * the id of a tag must be unique, - * and the ids specified in a gallery/article entry must match - * the ids specified in `EntryTagCollection` in order for them to work effectively - */ -export type EntryTagCollection = { [id: string]: EntryTag } diff --git a/libs/types/src/content/templates/shared.d.ts b/libs/types/src/content/templates/shared.d.ts index 940a5f3..0f449c8 100644 --- a/libs/types/src/content/templates/shared.d.ts +++ b/libs/types/src/content/templates/shared.d.ts @@ -1,5 +1,4 @@ import type { DateRange } from '../dateRange' -import type { EntryTagCollection } from '../entryTag' /** * Defines entries that are already fetched or are embedded directly in the list. @@ -40,22 +39,6 @@ export type ListWithEntries<T> = { */ export type ResolvedListEntries<T> = ListEntries<Promise<T>> -/** - * Defines a list that supports tagging the entries - */ -export type ListWithTags<T> = { - /** - * the tags to use for filtering entries - */ - tags?: EntryTagCollection - /** - * whether or not tag filtering removes entries completely from view; - * if false, they will apply a class selector instead - * in order to manually style (CSS filtering/opacity/etc.) - */ - removeFromView?: boolean -} & ListWithEntries<T> - /** * Defines a list that has warnings on its entries */ diff --git a/projects/frontend/src/components/shared/filter-panel.vue b/projects/frontend/src/components/shared/filter-panel.vue index c8c57d0..4291c1e 100644 --- a/projects/frontend/src/components/shared/filter-panel.vue +++ b/projects/frontend/src/components/shared/filter-panel.vue @@ -1,27 +1,28 @@ <script setup lang="ts"> -import { computed } from 'vue' -import type { EntryTagCollection } from '@goldenwere/mackenzii-types' +import { computed, ref, onMounted } from 'vue' +import type { MediaEntryTag } from '@goldenwere/mackenzii-types' +import { fetchAndParseConfig } from 'src/utilities/fetch' const props = defineProps<{ - tags: EntryTagCollection + tags: string | MediaEntryTag[] }>() const emits = defineEmits<{ (e: 'toggledTagsChanged', value: string[]): void }>() +const tagsLoaded = ref([] as MediaEntryTag[]) const tagsByCategory = computed(() => { - const value: { [category: string]: Record<string, string> } = { 'NoCategory': {}} + const value: { [category: string]: MediaEntryTag[] } = { 'NoCategory': []} - Object.keys(props.tags).forEach(id => { - const tag = props.tags![id] + tagsLoaded.value.forEach((tag) => { if (!!tag.category) { if (!value[tag.category]) { - value[tag.category] = {} + value[tag.category] = [] } - value[tag.category][id] = tag.displayName || id + value[tag.category].push(tag) } else { - value['NoCategory'][id] = tag.displayName || id + value['NoCategory'].push(tag) } }) @@ -44,6 +45,7 @@ const onToggleTag = (event: Event, tagId: string) => { tagsToggled.splice(index, 1) } } + console.log(tagsToggled) emits('toggledTagsChanged', tagsToggled) } @@ -53,6 +55,12 @@ const onToggleTag = (event: Event, tagId: string) => { const resetTags = () => { tagsToggled = [] } + +onMounted(async () => { + tagsLoaded.value = Array.isArray(props.tags) + ? tagsLoaded.value = props.tags + : await fetchAndParseConfig<MediaEntryTag[]>(props.tags) +}) </script> <template lang="pug"> @@ -66,17 +74,17 @@ const resetTags = () => { v-if='category !== "NoCategory"' ) {{ category }} .input.labeled-checkbox( - v-for='(tagDisplayName, tagId) in tags' - :id='tagId' + v-for='tag in tags' + :id='tag.tagId' ) label( - :for='`${tagId}-toggle`' - ) {{ tagDisplayName }} + :for='`${tag.tagId}-toggle`' + ) {{ tag.displayName || tag.tagId }} input( type='checkbox' - :name='`${tagId}-toggle`' - :id='`${tagId}-toggle`' - @input='onToggleTag($event, tagId)' + :name='`${tag.tagId}-toggle`' + :id='`${tag.tagId}-toggle`' + @input='onToggleTag($event, tag.tagId)' ) </template> diff --git a/projects/frontend/src/main.ts b/projects/frontend/src/main.ts index 642b0dc..6338ca6 100644 --- a/projects/frontend/src/main.ts +++ b/projects/frontend/src/main.ts @@ -21,7 +21,7 @@ export const createApp = ViteSSG( // the root component main, // vue-router options - { routes: createRoutes(globals as SiteGlobals) }, + { routes: createRoutes(globals as unknown as SiteGlobals) }, // function to have custom setups async ({ app, router, routes, isClient, initialState }) => { const hljsResolved: HLJSApi = await hljs as any @@ -44,6 +44,6 @@ export const createApp = ViteSSG( } app.use(createPinia()) - initializeRouteStore(routes, globals as SiteGlobals) + initializeRouteStore(routes, globals as unknown as SiteGlobals) }, ) diff --git a/projects/frontend/src/views/shared/media-list.vue b/projects/frontend/src/views/shared/media-list.vue index 8e88552..01748d6 100644 --- a/projects/frontend/src/views/shared/media-list.vue +++ b/projects/frontend/src/views/shared/media-list.vue @@ -1,8 +1,7 @@ <script setup lang="ts"> -import { computed, onMounted, ref } from 'vue' +import { computed, onMounted, useTemplateRef, ref, toRaw } from 'vue' import type { ConfigfulRouteDefinition, - ListWithTags, ListWithWarnings, MediaEntry, ResolvedListEntries, @@ -16,7 +15,6 @@ import GalleryTile from '../gallery/gallery-tile.vue' import ArticleTile from '../article/article-tile.vue' type MediaList = - & ListWithTags<MediaEntry> & ListWithWarnings<MediaEntry> /** @@ -41,6 +39,7 @@ const viewPath = computed(() => `${currentRoute.path}/view`) const config = ref(null as MediaList | null) const entries = ref({} as DisplayedEntries) +const entriesRefs = useTemplateRef('tiles' as any) const hasWarnings = ref(false) const hideWarnings = defineModel('hideWarnings', { type: Boolean }) const template = ref(routeConfig.template) @@ -117,8 +116,8 @@ onMounted(async () => { ) Transition FilterPanel( - v-if='ready && !!config.tags' - :tags='config.tags' + v-if='ready && globalConfig.tags' + :tags='globalConfig.tags' @toggledTagsChanged='onToggledTagsChanged($event)' ) Transition @@ -130,7 +129,8 @@ onMounted(async () => { v-for='(entry, id) in entries' ) GalleryTile( - v-if='template === "gallery-list" && (!entry.isHidden || !config.removeFromView)' + v-if='template === "gallery-list"' + ref='tiles' :class='{ hidden: entry.isHidden && !config.removeFromView }' :hideWarnings='hideWarnings' :id='id' @@ -139,7 +139,8 @@ onMounted(async () => { :entry='entry' ) ArticleTile( - v-else-if='template === "article-list" && (!entry.isHidden || !config.removeFromView)' + v-else-if='template === "article-list"' + ref='tiles' :class='{ hidden: entry.isHidden && !config.removeFromView }' :hideWarnings='hideWarnings' :id='id'