144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import { trpc } from '~/app/_trpc/Client'
|
|
import type { RouterOutputs } from '~/server/routers/_app'
|
|
import {
|
|
AUTOCOMPLETE_CURSOR_MARKER,
|
|
linkSuggestionsToAutocomplete,
|
|
type AutocompleteTriggerConfig,
|
|
type InternalLinkSuggestion,
|
|
type MdeAutocompleteSuggestion,
|
|
} from '~/app/_components/Form/Fields/InternalLinkTextarea'
|
|
import MdxEditorPreview from './MdxEditorPreview'
|
|
|
|
function internalLinkSuggestions(params: {
|
|
posts?: RouterOutputs['blog']['list'],
|
|
projects?: RouterOutputs['projectv2']['listWithStack'],
|
|
}): InternalLinkSuggestion[] {
|
|
const postLinks = params.posts?.map((post) => ({
|
|
label: post.title,
|
|
href: `/blog/${post.slug}`,
|
|
group: 'Blog',
|
|
})) ?? []
|
|
|
|
const projectLinks = params.projects?.map((project) => ({
|
|
label: project.title,
|
|
href: `/projects#${project.id}`,
|
|
group: 'Project',
|
|
})) ?? []
|
|
|
|
return [...postLinks, ...projectLinks]
|
|
}
|
|
|
|
const mdxAutocompleteSuggestions: MdeAutocompleteSuggestion[] = [
|
|
{
|
|
label: 'Lead',
|
|
value: `<Lead>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Lead>`,
|
|
detail: 'Intro paragraph with larger muted text.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'Callout note',
|
|
value: `<Callout title="Heads up" variant="note">\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Callout>`,
|
|
detail: 'Highlighted note block.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'Callout tip',
|
|
value: `<Callout title="Tip" variant="tip">\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Callout>`,
|
|
detail: 'Highlighted tip block.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'Callout warning',
|
|
value: `<Callout title="Careful" variant="warning">\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Callout>`,
|
|
detail: 'Highlighted warning block.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'ButtonLink',
|
|
value: `<ButtonLink href="${AUTOCOMPLETE_CURSOR_MARKER}">\nView projects\n</ButtonLink>`,
|
|
detail: 'Button-styled internal or external link.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'Figure',
|
|
value: `<Figure\n src="${AUTOCOMPLETE_CURSOR_MARKER}"\n alt="Describe the image"\n caption="Optional caption"\n/>`,
|
|
detail: 'Image with optional caption.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'PullQuote',
|
|
value: `<PullQuote>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</PullQuote>`,
|
|
detail: 'Large emphasized quote.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'TagList',
|
|
value: `<TagList tags={[${AUTOCOMPLETE_CURSOR_MARKER}]} />`,
|
|
detail: 'Inline list of tag badges.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'Badge',
|
|
value: `<Badge variant="outline">${AUTOCOMPLETE_CURSOR_MARKER}</Badge>`,
|
|
detail: 'Small inline label.',
|
|
group: 'Component',
|
|
trigger: '<',
|
|
},
|
|
{
|
|
label: 'Image',
|
|
value: ``,
|
|
detail: 'Markdown image',
|
|
group: 'Markdown',
|
|
trigger: '!',
|
|
},
|
|
]
|
|
|
|
const mdxTriggerConfigs: AutocompleteTriggerConfig[] = [
|
|
{
|
|
trigger: '[[',
|
|
label: 'Internal links',
|
|
isQueryValid: (query) => !query.includes(']'),
|
|
},
|
|
{
|
|
trigger: '<',
|
|
label: 'MDX components',
|
|
isQueryValid: (query) => !/[\s>]/.test(query),
|
|
},
|
|
{
|
|
trigger: '!',
|
|
label: 'Markdown',
|
|
isQueryValid: (query) => !/[\s\)]/.test(query),
|
|
},
|
|
]
|
|
|
|
/**
|
|
* Shared props for an MDX-aware `MdeFormField`: internal-link + component
|
|
* autocomplete, trigger configs, and a live MDX preview. Used by every admin
|
|
* form that edits MDX content (blog, project, cv entry).
|
|
*/
|
|
export function useMdxEditorFieldProps() {
|
|
const posts = trpc.blog.list.useQuery(undefined, { refetchInterval: 5000 })
|
|
const projects = trpc.projectv2.listWithStack.useQuery()
|
|
|
|
const autocompleteSuggestions = [
|
|
...linkSuggestionsToAutocomplete(internalLinkSuggestions({ posts: posts.data, projects: projects.data })),
|
|
...mdxAutocompleteSuggestions,
|
|
]
|
|
|
|
return {
|
|
autocompleteSuggestions,
|
|
triggerConfigs: mdxTriggerConfigs,
|
|
renderPreview: (source: string) => <MdxEditorPreview source={source} />,
|
|
}
|
|
}
|