From 10be2f691748c9fc4c17b933fcf7df30dfed3dff Mon Sep 17 00:00:00 2001 From: Vlad Moroz Date: Wed, 20 Nov 2024 18:12:44 +0100 Subject: [PATCH 1/3] Place Quick Nav after the main demo in the DOM --- docs/src/components/demo/Demo.css | 9 ++++ .../components/quick-nav/rehypeQuickNav.mjs | 54 +++++++++++++++---- docs/src/mdx-components.tsx | 4 +- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/docs/src/components/demo/Demo.css b/docs/src/components/demo/Demo.css index 358e4e5858..9caf537613 100644 --- a/docs/src/components/demo/Demo.css +++ b/docs/src/components/demo/Demo.css @@ -3,6 +3,15 @@ background-color: var(--color-content); border: 1px solid var(--color-gray-200); border-radius: var(--radius-md); + + /* Coordinate the placement of Quick Nav that follows the main Demo */ + &[data-before-quick-nav] { + float: left; + width: 100%; + & + * + * { + clear: left; + } + } } .DemoPlayground { diff --git a/docs/src/components/quick-nav/rehypeQuickNav.mjs b/docs/src/components/quick-nav/rehypeQuickNav.mjs index c588eafe5c..2b60be15da 100644 --- a/docs/src/components/quick-nav/rehypeQuickNav.mjs +++ b/docs/src/components/quick-nav/rehypeQuickNav.mjs @@ -6,6 +6,7 @@ const TITLE = 'QuickNav.Title'; const LIST = 'QuickNav.List'; const ITEM = 'QuickNav.Item'; const LINK = 'QuickNav.Link'; +const DOC_DEMO = 'Demo'; const DOC_SUBTITLE = 'Subtitle'; /** @@ -21,11 +22,6 @@ const DOC_SUBTITLE = 'Subtitle'; */ export function rehypeQuickNav() { return (tree, file) => { - const h1 = tree.children.find( - /** @param {{ tagName: string; }} node */ - (node) => node.tagName === 'h1', - ); - /** @type {TocEntry[]} */ const toc = file.data.toc; const root = createMdxElement({ @@ -37,10 +33,50 @@ export function rehypeQuickNav() { return; } - // Place quick nav after the `` that immediately follows the first `

`, - // or after the first `

` if a matching `` wasn't found. - let index = tree.children.indexOf(h1) + 2; // Adding "2" because there's also a line break below h1 - index = tree.children[index]?.name === DOC_SUBTITLE ? index + 1 : index; + // Determine the placement of Quick Nav up next + + /** @type {{ tagName?: string; name?: string; attributes?: Record[] }[]} */ + const contentNodes = tree.children.filter( + /** @param {{ type?: string; value?: string; }} child */ + (child) => { + return ( + // Filter out import statements + child.type !== 'mdxjsEsm' && + // Filter out plain line breaks + child.value !== '\n' + ); + }, + ); + + const h1 = tree.children.find( + /** @param {{ tagName: string; }} node */ + (node) => node.tagName === 'h1', + ); + + const subtitle = contentNodes.find((node, i) => { + const prev = contentNodes[i - 1]; + return node.name === DOC_SUBTITLE && prev === h1; + }); + + let nodeBefore = contentNodes.find((node, i) => { + const prev = contentNodes[i - 1]; + return node.name === DOC_DEMO && prev === subtitle; + }); + + // Add a styling hook if a `` element was found + if (nodeBefore) { + nodeBefore.attributes ??= []; + nodeBefore.attributes.push({ + type: 'mdxJsxAttribute', + name: 'data-before-quick-nav', + value: '', + }); + } else { + // Otherwise, place the Quick Nav node after a fallback + nodeBefore = subtitle ?? h1; + } + + const index = tree.children.indexOf(nodeBefore) + 1; tree.children.splice(index, 0, root); }; } diff --git a/docs/src/mdx-components.tsx b/docs/src/mdx-components.tsx index 15e0a69949..f5ba7ef46d 100644 --- a/docs/src/mdx-components.tsx +++ b/docs/src/mdx-components.tsx @@ -66,7 +66,9 @@ export const mdxComponents: MDXComponents = { td: Table.Cell, // Custom components - Demo: (props) => , + Demo: (props) => ( + + ), QuickNav, AttributesTable: (props) => , CssVariablesTable: (props) => , From a3ea46c49acc3c48d0e7477940eeb662d2fd15c9 Mon Sep 17 00:00:00 2001 From: Vlad Moroz Date: Fri, 13 Dec 2024 13:25:25 +0100 Subject: [PATCH 2/3] Update --- docs/src/components/quick-nav/rehypeQuickNav.mjs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/src/components/quick-nav/rehypeQuickNav.mjs b/docs/src/components/quick-nav/rehypeQuickNav.mjs index 2b60be15da..092af901ad 100644 --- a/docs/src/components/quick-nav/rehypeQuickNav.mjs +++ b/docs/src/components/quick-nav/rehypeQuickNav.mjs @@ -1,5 +1,9 @@ // @ts-check +/** + * @import {Nodes} from 'hast' + */ import { createMdxElement } from 'docs/src/mdx/createMdxElement.mjs'; +import { toString } from 'hast-util-to-string'; const ROOT = 'QuickNav.Root'; const TITLE = 'QuickNav.Title'; @@ -29,7 +33,7 @@ export function rehypeQuickNav() { children: toc.flatMap(getNodeFromEntry).filter(Boolean), }); - if (!toc.length) { + if (!toc?.length) { return; } @@ -37,13 +41,12 @@ export function rehypeQuickNav() { /** @type {{ tagName?: string; name?: string; attributes?: Record[] }[]} */ const contentNodes = tree.children.filter( - /** @param {{ type?: string; value?: string; }} child */ + /** @param {Nodes} child */ (child) => { return ( - // Filter out import statements - child.type !== 'mdxjsEsm' && - // Filter out plain line breaks - child.value !== '\n' + // Filter out nodes that don't produce text + (('children' in child || 'value' in child) && toString(child).trim()) || + ('name' in child && child.name === 'Demo') ); }, ); From b6438bbf9c419a7ea637b422afc5317c95d2d53e Mon Sep 17 00:00:00 2001 From: Vlad Moroz Date: Fri, 13 Dec 2024 13:31:03 +0100 Subject: [PATCH 3/3] Fix toString --- docs/src/components/quick-nav/rehypeQuickNav.mjs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/components/quick-nav/rehypeQuickNav.mjs b/docs/src/components/quick-nav/rehypeQuickNav.mjs index 092af901ad..2f4e349991 100644 --- a/docs/src/components/quick-nav/rehypeQuickNav.mjs +++ b/docs/src/components/quick-nav/rehypeQuickNav.mjs @@ -43,11 +43,11 @@ export function rehypeQuickNav() { const contentNodes = tree.children.filter( /** @param {Nodes} child */ (child) => { - return ( - // Filter out nodes that don't produce text - (('children' in child || 'value' in child) && toString(child).trim()) || - ('name' in child && child.name === 'Demo') - ); + // Filter out nodes that don't produce texts + const hasChildren = 'children' in child && child.children?.length; + const isText = child.type === 'text'; + const isDemo = 'name' in child && child.name === 'Demo'; + return ((hasChildren || isText) && toString(child).trim()) || isDemo; }, );