-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(lib, src): add Article component and integrate with MDX, refacto…
…r related components
- Loading branch information
Showing
13 changed files
with
1,582 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { createElement, useEffect, useState } from 'react'; | ||
import { Note, Tip, Steps, Step } from '@stubbycms/ui'; | ||
import { highlight } from 'sugar-high'; | ||
import { removeFrontMatter, slugify } from '../utils'; | ||
import remarkGfm from 'remark-gfm'; | ||
import { MDXRemote } from 'next-mdx-remote/rsc'; | ||
|
||
const mdxOptions = { | ||
mdxOptions: { | ||
remarkPlugins: [remarkGfm], | ||
rehypePlugins: [], | ||
}, | ||
}; | ||
|
||
const CustomLink = (props: { href: string; children: React.ReactNode }) => { | ||
let href = props.href; | ||
|
||
if (href.startsWith('#')) { | ||
return <a {...props} />; | ||
} | ||
|
||
return ( | ||
<a | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
{...props} | ||
/> | ||
); | ||
}; | ||
|
||
const Code = ({ children, ...props }: { children: string }) => { | ||
let codeHTML = highlight(children); | ||
return ( | ||
<code | ||
dangerouslySetInnerHTML={{ __html: codeHTML }} | ||
{...props} | ||
/> | ||
); | ||
}; | ||
|
||
function createHeading(level: number) { | ||
const Heading = ({ children }: { children: string }) => { | ||
let slug = slugify(children); | ||
return createElement( | ||
`h${level}`, | ||
{ id: slug }, | ||
[ | ||
createElement('a', { | ||
href: `#${slug}`, | ||
key: `link-${slug}`, | ||
className: 'anchor', | ||
}), | ||
], | ||
children, | ||
); | ||
}; | ||
|
||
Heading.displayName = `Heading${level}`; | ||
|
||
return Heading; | ||
} | ||
|
||
export const ArticleComponents: Record<string, React.ComponentType<any>> = { | ||
h1: createHeading(1), | ||
h2: createHeading(2), | ||
h3: createHeading(3), | ||
h4: createHeading(4), | ||
h5: createHeading(5), | ||
h6: createHeading(6), | ||
a: CustomLink, | ||
code: Code, | ||
Note, | ||
Tip, | ||
Steps, | ||
Step, | ||
}; | ||
|
||
export function Article(props: { | ||
components?: Record<string, React.ComponentType>; | ||
source: string; | ||
}) { | ||
const [mdxContent, setMdxContent] = useState<React.ReactNode>(null); | ||
|
||
useEffect(() => { | ||
MDXRemote({ | ||
source: removeFrontMatter(props.source as string), | ||
components: { ...ArticleComponents, ...(props.components || {}) }, | ||
options: mdxOptions, | ||
}).then(setMdxContent); | ||
}, [props.source, props.components]); | ||
|
||
return <article className="prose">{mdxContent}</article>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { highlight } from 'sugar-high'; | ||
|
||
export const Code = ({ children, ...props }: { children: string }) => { | ||
let codeHTML = highlight(children); | ||
return ( | ||
<code | ||
dangerouslySetInnerHTML={{ __html: codeHTML }} | ||
{...props} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import _slugify from 'slugify'; | ||
|
||
/** | ||
* Removes the front matter from a given content string. | ||
* | ||
* The front matter is expected to be enclosed within triple dashes (`---`). | ||
* If front matter is found, it is removed from the content string. | ||
* If no front matter is found, the original content string is returned. | ||
* | ||
* @param content - The content string from which to remove the front matter. | ||
* @returns The content string without the front matter. | ||
*/ | ||
export const removeFrontMatter = (content: string) => { | ||
let frontMatter = content.match(/---([\s\S]*?)---/); | ||
if (frontMatter) { | ||
return content.replace(frontMatter[0], ''); | ||
} | ||
return content; | ||
}; | ||
|
||
export const slugify = (text: string) => { | ||
return _slugify(text, { lower: true, strict: true }); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { NavLink } from 'react-router'; | ||
|
||
export const components = [ | ||
{ | ||
name: 'Tabs', | ||
description: 'A tab system that allows users to switch between different sections of content.', | ||
route: '/components/tabs', | ||
}, | ||
{ | ||
name: 'Callouts', | ||
description: | ||
'A callout is a small piece of text that is used to "call out" a feature or highlight a specific piece of information.', | ||
route: '/components/callouts', | ||
}, | ||
{ | ||
name: 'Accordion', | ||
description: | ||
'An accordion is a vertically stacked list of items that utilizes show/hide functionality.', | ||
route: '/components/accordion', | ||
}, | ||
{ | ||
name: 'Steps', | ||
description: | ||
'A step component is used to guide users through a process or workflow in a linear fashion.', | ||
route: '/components/steps', | ||
}, | ||
{ | ||
name: 'Article', | ||
description: 'A component that renders markdown content with custom components.', | ||
route: '/components/article', | ||
}, | ||
].sort((a, b) => a.name.localeCompare(b.name)) as { | ||
name: string; | ||
description: string; | ||
route: string; | ||
}[]; | ||
|
||
export const SideNav = () => { | ||
return ( | ||
<div className="min-w-[220px]"> | ||
<div className="sticky top-10"> | ||
<h1 className="text-xs font-bold mb-4 uppercase tracking-widest">Components</h1> | ||
<div className="flex flex-col gap-2"> | ||
{components.map((component) => ( | ||
<NavLink | ||
to={component.route} | ||
key={component.name} | ||
className={({ isActive }) => { | ||
const classNames = | ||
'text-gray-700 hover:text-gray-900 px-4 py-1.5 hover:bg-slate-100 -ml-4 rounded-full'; | ||
return isActive ? `${classNames} bg-slate-100 font-medium` : classNames; | ||
}} | ||
> | ||
{component.name} | ||
</NavLink> | ||
))} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
import { Outlet } from 'react-router'; | ||
import { SideNav } from '../components/SideNav'; | ||
|
||
export function ComponentsLayout() { | ||
return ( | ||
<div className="container mx-auto max-w-6xl"> | ||
<div className="h-20"></div> | ||
<Outlet /> | ||
<div className="container mx-auto max-w-6xl flex pt-10 gap-10"> | ||
<SideNav /> | ||
<div className="flex-1"> | ||
<Outlet /> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Article } from '../../lib'; | ||
// @ts-ignore | ||
import article from '../samples/article.mdx?raw'; | ||
|
||
export const ArticleSamples = () => { | ||
return ( | ||
<> | ||
<h1 className="text-4xl font-bold mb-6">Privacy-first programming</h1> | ||
<Article source={article}></Article> | ||
</> | ||
); | ||
}; |
Oops, something went wrong.