Skip to content

Commit

Permalink
chore(docs): Use react-marked-renderer for markdown stuffs
Browse files Browse the repository at this point in the history
  • Loading branch information
mlaursen committed Oct 19, 2021
1 parent 9bdf6ba commit 93ebaa4
Show file tree
Hide file tree
Showing 14 changed files with 222 additions and 237 deletions.
3 changes: 1 addition & 2 deletions packages/documentation/package.json
Expand Up @@ -28,14 +28,14 @@
"fuse.js": "6.4.6",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"marked": "^3.0.7",
"mobile-detect": "^1.4.5",
"next": "11.1.2",
"prismjs": "^1.25.0",
"qs": "^6.10.0",
"react": "^17.0.2",
"react-dom": "^17.0.1",
"react-hook-form": "^7.17.2",
"react-marked-renderer": "^0.2.1",
"react-md": "^3.1.1",
"react-router-dom": "^5.3.0",
"react-swipeable": "^6.2.0",
Expand All @@ -49,7 +49,6 @@
"@types/formidable": "^1.2.4",
"@types/gtag.js": "^0.0.7",
"@types/js-cookie": "^3.0.0",
"@types/marked": "^3.0.1",
"@types/prismjs": "^1.16.6",
"@types/qs": "^6.9.7",
"@types/react-virtualized": "^9.21.13",
Expand Down
7 changes: 4 additions & 3 deletions packages/documentation/src/components/Blockquote.tsx
@@ -1,15 +1,16 @@
import React, { HTMLAttributes, ReactElement } from "react";
import cn from "classnames";
import { Text } from "@react-md/typography";

import styles from "./Blockquote.module.scss";

export default function Blockquote({
className,
children,
...props
}: HTMLAttributes<HTMLDivElement>): ReactElement {
return (
<blockquote {...props} className={cn("blockquote", className)}>
<Text>{children}</Text>
<blockquote {...props} className={cn(styles.blockquote, className)}>
{children}
</blockquote>
);
}
Expand Up @@ -25,7 +25,7 @@
}

// prismjs custom theme
// this is _basically_ my current vim theme, but not aas good since
// this is _basically_ my current vim theme, but not as good since
// Prism doesn't have as many tokenizers
:global {
.token {
Expand Down Expand Up @@ -95,8 +95,7 @@
color: $solarized-blue;
}

&:global(.language-ts) :global .token + .class-name,
&:global(.language-tsx) :global .token + .class-name {
&:global(.language-ts) :global .token + .class-name {
color: $solarized-base-1;
}

Expand Down
11 changes: 8 additions & 3 deletions packages/documentation/src/components/Code/CodeBlock.tsx
Expand Up @@ -6,11 +6,10 @@ import React, {
} from "react";
import cn from "classnames";

import { highlightCode } from "components/Markdown/utils";

import styles from "./CodeBlock.module.scss";
import Code from "./Code";
import LineNumbers from "./LineNumbers";
import { highlightCode } from "./utils";

export interface CodeBlockProps extends HTMLAttributes<HTMLPreElement> {
/**
Expand Down Expand Up @@ -64,14 +63,19 @@ export default forwardRef<HTMLPreElement, CodeBlockProps>(function CodeBlock(
language = "markdown",
children,
highlight = true,
lineNumbers = false,
lineNumbers: propLineNumbers,
suppressHydrationWarning = false,
...props
},
ref
) {
const code = typeof children === "string" ? children : "";

const lineNumbers =
propLineNumbers ??
(!/markup|markdown|shell/.test(language) &&
(code.match(/\r?\n/g) || []).length + 1 > 3);

let dangerouslySetInnerHTML: DOMAttributes<HTMLElement>["dangerouslySetInnerHTML"];
if (code && highlight) {
dangerouslySetInnerHTML = {
Expand All @@ -82,6 +86,7 @@ export default forwardRef<HTMLPreElement, CodeBlockProps>(function CodeBlock(
return (
<pre
{...props}
data-linenumbers={lineNumbers || undefined}
ref={ref}
className={cn(
styles.block,
Expand Down
21 changes: 21 additions & 0 deletions packages/documentation/src/components/Code/utils.ts
@@ -0,0 +1,21 @@
import { highlight, languages } from "prismjs";
import {
DangerouslyHighlightCode,
GetCodeLanguage,
} from "react-marked-renderer";

export const getLanguage: GetCodeLanguage = (lang, _rawCode) => {
// allow aliases
lang = lang === "sh" ? "shell" : lang;

// if the Prism doesn't support the language, default to nothing instead
// of crashing
if (!languages[lang]) {
return "none";
}

return lang;
};

export const highlightCode: DangerouslyHighlightCode = (code, lang) =>
highlight(code, languages[lang], lang);
Expand Up @@ -37,6 +37,8 @@ export default function CodePreview({
if (typeof content !== "string") {
content = `${JSON.stringify(content, null, 2)}\n`;
}

content = content.trim();
}

const code = useRef<HTMLPreElement | null>(null);
Expand Down
Expand Up @@ -11,13 +11,13 @@ $demo-padding-extra: 6rem;

padding: 0 $demo-padding-extra;

:global .code--counted {
[data-linenumbers] {
width: calc(100vw - #{$demo-padding-extra * 2});
}
}

@include rmd-utils-desktop-media {
:global .code--counted {
[data-linenumbers] {
$offset: $rmd-sheet-static-width + $toc-width + ($demo-padding-extra * 2);

width: calc(100vw - #{$offset});
Expand Down
@@ -1,6 +1,7 @@
import React, { ReactElement } from "react";
import { Button } from "@react-md/button";
import { isPersistentLayout, useLayoutConfig } from "@react-md/layout";
import { Text } from "@react-md/typography";

import CodeBlock from "components/Code/CodeBlock";
import Blockquote from "components/Blockquote";
Expand All @@ -22,8 +23,10 @@ export default function LayoutVisibility(): ReactElement {
<CodeBlock language="typescript">{code}</CodeBlock>
{isPersistentLayout(remaining.layout) && (
<Blockquote>
The visibility cannot be changed for persistent layouts so the buttons
will do nothing.
<Text>
The visibility cannot be changed for persistent layouts so the
buttons will do nothing.
</Text>
</Blockquote>
)}
<Button onClick={showNav}>Show</Button>
Expand Down
@@ -1,10 +1,5 @@
@use 'react-md' as *;

.p,
.list {
@include rmd-typography(subtitle-1);
}

.container {
@include rmd-utils-phone-media {
@each $headline in (1 2 3) {
Expand Down
91 changes: 17 additions & 74 deletions packages/documentation/src/components/Markdown/Markdown.tsx
@@ -1,100 +1,39 @@
/* eslint-disable react/no-danger */
import React, {
HTMLAttributes,
MutableRefObject,
ReactElement,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import cn from "classnames";
import { useRouter } from "next/router";
import { Markdown as MarkdownRenderer } from "react-marked-renderer";

import { getLanguage, highlightCode } from "components/Code/utils";

import styles from "./Markdown.module.scss";
import { markdownToHTML } from "./utils";
import { renderers } from "./renderers";
import { transformMarkdown } from "./utils";

function useMarkdownResolver(markdown: MarkdownProps["children"]): string {
/* eslint-disable react-hooks/rules-of-hooks */
// i will never swap between strings and promises
if (typeof markdown === "string") {
return markdown;
return transformMarkdown(markdown);
}

const [resolved, setResolved] = useState("");
useEffect(() => {
markdown().then((md) => {
if (typeof md === "string") {
setResolved(md);
setResolved(transformMarkdown(md));
} else if (typeof md.default === "string") {
setResolved(md.default);
setResolved(transformMarkdown(md.default));
}
});
}, [markdown]);

return resolved;
}

interface DangerHTML {
__html: string;
}

function useHTML(children: MarkdownChildren): DangerHTML {
const markdown = useMarkdownResolver(children);
const html = useMemo(
() => ({
__html: markdownToHTML(markdown),
}),
[markdown]
);

return html;
}

function useCustomMarkdownBehavior({
__html: html,
}: DangerHTML): MutableRefObject<HTMLDivElement | null> {
const ref = useRef<HTMLDivElement | null>(null);
const router = useRouter();
useEffect(() => {
const instance = ref.current;
if (!instance) {
return;
}

const { origin } = window.location;
const links = Array.from(
instance.querySelectorAll<HTMLAnchorElement>("a[href]")
);

links.forEach((link) => {
if (link.href.startsWith(origin)) {
link.onclick = (event) => {
event.preventDefault();
const href = link.href.replace(origin, "");

router.push(href).then((success) => {
if (success) {
const [, hash] = href.split("#");
if (hash) {
const el = document.getElementById(hash);
if (el && typeof el.focus === "function") {
el.focus();
return;
}
}

window.scrollTo(0, 0);
}
});
};
}
});
}, [html, router]);

return ref;
}

export type ResolveMarkdown = () => Promise<string | { default: string }>;
export type MarkdownChildren = string | ResolveMarkdown;

Expand All @@ -109,23 +48,27 @@ export default function Markdown({
disableSinglePMargin,
...props
}: MarkdownProps): ReactElement {
const html = useHTML(children);
const ref = useCustomMarkdownBehavior(html);
const markdown = useMarkdownResolver(children);

return (
<>
<div
{...props}
ref={ref}
className={cn(
styles.container,
{
[styles.marginless]: disableSinglePMargin,
},
className
)}
dangerouslySetInnerHTML={html}
/>
>
<MarkdownRenderer
markdown={markdown}
renderers={renderers}
getLanguage={getLanguage}
highlightCode={highlightCode}
/>
</div>
</>
);
}
@@ -0,0 +1,4 @@
.task {
list-style: none;
margin-left: -2.5rem;
}

0 comments on commit 93ebaa4

Please sign in to comment.