Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { Metadata } from "next";
import { createRelativeLink } from "fumadocs-ui/mdx";
import { LLMCopyButton, ViewOptions } from "@/components/ai/page-actions";
import { ImageZoom } from "fumadocs-ui/components/image-zoom";
import ContactFooter, { type Contacts } from "@/lib/contact-footer";

export default async function Page(props: PageProps<"/docs/[[...slug]]">) {
const params = await props.params;
Expand Down Expand Up @@ -47,10 +48,11 @@ export default async function Page(props: PageProps<"/docs/[[...slug]]">) {
components={getMDXComponents({
// this allows you to link to other pages with relative file paths
a: createRelativeLink(source, page),
img: ImageZoom
img: ImageZoom,
})}
/>
</DocsBody>
<ContactFooter contacts={GetContacts(params.slug)} />
</DocsPage>
);
}
Expand All @@ -74,3 +76,20 @@ export async function generateMetadata(
},
};
}

function GetContacts(slug: string[] | undefined): Contacts {
if (slug == undefined || slug.length == 0) {
return undefined;
}

const page = source.getPage(slug);
if (!page) {
return undefined;
}

if (page.data.contacts) {
return page.data.contacts;
}

return GetContacts(slug?.slice(0, -1));
}
79 changes: 79 additions & 0 deletions lib/contact-footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { z } from "zod";
const ContactSchema = z
.object({
name: z.string(),
role: z.string().optional(),
email: z.email().optional(),
url: z.url().optional(),
})
.refine(
(x) => {
return EnsureMutuallyExclusive(x, ["email", "url"]);
},
{
error: "URL and email are mutually exclusive properties",
}
);

function EnsureMutuallyExclusive(x: { [key: string]: any }, keys: string[]) {
let keyCount = 0;
for (let i = 0; i < keys.length; i++) {
if (x[keys[i]] != undefined) {
keyCount++;
}
}
return keyCount <= 1;
}

export const ContactsSchema = z.array(ContactSchema).optional();

export type Contacts = z.infer<typeof ContactsSchema>;
export type Contact = z.infer<typeof ContactSchema>;

export default function ContactFooter(params: { contacts: Contacts }) {
if (params.contacts == undefined) {
return <></>;
}

return (
<div className="border-t py-6">
<h2 className="text-md font-bold">
{params.contacts.length > 1 ? "Contacts" : "Contact"}:
</h2>
<ul className="grid grid-cols-[repeat(auto-fit,minmax(--spacing(60),1fr))]">
{params.contacts.map((contact, index) => Contact(contact, index))}
</ul>
</div>
);
}

function Contact(contact: Contact, index: number) {
let contactLink: string | undefined = undefined;
if (contact.email) {
contactLink = "mailto:" + contact.email;
}
if (contact.url) {
contactLink = contact.url;
}

let textContainer;
if (contactLink) {
textContainer = (
<a
href={contactLink}
className="block underline underline-offset-3.5 decoration-fd-primary hover:opacity-80"
>
{contact.name}
</a>
);
} else {
textContainer = <span className="block">{contact.name}</span>;
}

return (
<li className="block m-1" key={index}>
{textContainer}{" "}
{contact.role && <div className="opacity-70 block">{contact.role}</div>}
</li>
);
}
5 changes: 4 additions & 1 deletion source.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import {
frontmatterSchema,
metaSchema,
} from "fumadocs-mdx/config";
import { ContactsSchema } from "./lib/contact-footer";

// You can customise Zod schemas for frontmatter and `meta.json` here
// see https://fumadocs.dev/docs/mdx/collections
export const docs = defineDocs({
dir: "content/docs",
docs: {
schema: frontmatterSchema,
schema: frontmatterSchema.extend({
contacts: ContactsSchema,
}),
postprocess: {
includeProcessedMarkdown: true,
},
Expand Down