From 45717816a2cd4a0336e2710257561d9352e62d7b Mon Sep 17 00:00:00 2001 From: Yifei Gao Date: Wed, 3 Jul 2024 01:05:04 +0800 Subject: [PATCH] [feature] support video link with headings --- gatsby/lib/parse-content.mjs | 29 +++++++++++++++++++++++------ src/components/VideoLink.js | 26 ++++++++++++++++++++++++++ src/layouts/ChapterLayout.js | 2 ++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 src/components/VideoLink.js diff --git a/gatsby/lib/parse-content.mjs b/gatsby/lib/parse-content.mjs index c46524a..94e52a7 100644 --- a/gatsby/lib/parse-content.mjs +++ b/gatsby/lib/parse-content.mjs @@ -11,9 +11,13 @@ import { toString } from 'hast-util-to-string'; import { rehypeCodesplit } from './codesplit.mjs'; +function isHeading(node) { + return node.type === 'element' && /^h[1-6]$/i.test(node.tagName); +} + export function parseContent(html) { const replaceMedia = () => (tree) => { - visit(tree, { tagName: 'div' }, (node) => { + visit(tree, { tagName: 'div' }, (node, index, parent) => { if ( node.properties.className && Array.isArray(node.properties.className) && @@ -44,6 +48,19 @@ export function parseContent(html) { node.tagName = 'embed-example'; } + if ( + node.properties.dataType === 'video-link' && + node.properties.dataTitle && + index > 2 && + isHeading(parent.children[index - 2]) + ) { + node.tagName = 'video-link'; + + // move the video-link node inside the last adjacent heading + parent.children[index - 2].children.push(node); + parent.children.splice(index, 1); + } + if ( node.properties.dataType === 'note' || node.properties.dataType === 'exercise' || @@ -151,11 +168,6 @@ export function parseContent(html) { const description = paragraphs.join(' ').trim().substring(0, 150); const transformedAst = unified() - .use(replaceMedia) - .use(externalLinkInNewTab) - .use(rehypeCodesplit) - .use(rehypeHighlight) - .use(rehypeSlug) .use(rehypeAutolinkHeadings, { behavior: 'wrap', test: ['h2', 'h3'], @@ -163,6 +175,11 @@ export function parseContent(html) { class: 'heading-link', }, }) + .use(replaceMedia) + .use(externalLinkInNewTab) + .use(rehypeCodesplit) + .use(rehypeHighlight) + .use(rehypeSlug) .use(rehypeKatex) .runSync(ast); diff --git a/src/components/VideoLink.js b/src/components/VideoLink.js new file mode 100644 index 0000000..987a3d1 --- /dev/null +++ b/src/components/VideoLink.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { FaYoutube } from 'react-icons/fa'; + +const VideoLink = (props) => { + return ( +
+ + + {props['data-title']} + + {props.children && ( +
+
+ {props.children} +
+
+ )} +
+
+ ); +}; + +export default VideoLink; diff --git a/src/layouts/ChapterLayout.js b/src/layouts/ChapterLayout.js index ad97d03..f3265d2 100644 --- a/src/layouts/ChapterLayout.js +++ b/src/layouts/ChapterLayout.js @@ -8,6 +8,7 @@ import SideNavLayout from './SideNavLayout'; import PrevNextButtons from '../components/PrevNextButtons'; import Image from '../components/Image'; import Example from '../components/Example'; +import VideoLink from '../components/VideoLink'; const renderAst = ({ ast, images }) => { visit(ast, { tagName: 'img' }, (node) => { @@ -29,6 +30,7 @@ const renderAst = ({ ast, images }) => { components: { 'gatsby-image': Image, 'embed-example': Example, + 'video-link': VideoLink, }, });