_includes/resources/channel/ChannelModels.tsx (83 lines of code) (raw):

import { Static, Type } from "@sinclair/typebox"; import { Resource, ResourceFrontmatter } from "../../../src/ResourceModels"; import { EleventyPage, LayoutProps } from "../../../src/models"; import { CHANNEL_RESOURCE, CHANNEL_RESOURCE_TYPE, } from "../../../src/resourceType"; import { IconField } from "../commonModels"; import path from "upath"; export const ChannelFrontmatter = Type.Intersect([ ResourceFrontmatter, Type.Optional(IconField), Type.Object({ hero: Type.Optional( Type.String({ description: "File name of the hero image", }), ), }), Type.Object({ subnav: Type.Optional( Type.Array( Type.Object({ title: Type.String({ description: "File name of the animated GIF, can be webm or gif", }), url: Type.String({ description: "URL to link to", }), target: Type.Optional( Type.String({ description: "Link target", }), ), }), ), ), }), ]); export type ChannelFrontmatter = Static<typeof ChannelFrontmatter>; export class Channel extends Resource<CHANNEL_RESOURCE_TYPE> implements ChannelFrontmatter { hero?: string; subnav?: ChannelFrontmatter["subnav"]; accent?: string; icon?: string; logo?: string; thumbnail?: string; static frontmatterSchema = ChannelFrontmatter; constructor({ data, page, }: { data: ChannelFrontmatter; page: EleventyPage; }) { super({ data, page, }); this.hero = data.hero; this.subnav = data.subnav; if (data.logo) { this.logo = path.join(page.url, data.logo); this.thumbnail = this.logo; } if (data.accent) { this.accent = data.accent; this.icon = data.icon; } } getThumbnail(): string { return this.logo!; } } // The following type is helpful for re-use in // channel homepage views. export type ChannelHomepageData = {} & LayoutProps & ChannelFrontmatter; export const isChannel = ( resource: Resource | undefined, ): resource is Channel => { return resource?.resourceType === CHANNEL_RESOURCE; };