/* Source: https://github.com/shuding/nextra/blob/core/packages/nextra-theme-docs/src/mdx-components.tsx */ import { HashtagIcon } from '@heroicons/react/24/outline'; import Slugger from 'github-slugger'; import Link from 'next/link'; import { useRouter } from 'next/router'; import React, { ComponentProps, ReactElement, useEffect, useRef } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import 'intersection-observer'; import clsxm from '@/lib/clsxm'; import { IS_BROWSER } from '@/lib/constants'; import { Anchor } from '.'; import { useSetActiveAnchor } from '../contexts'; let observer: IntersectionObserver; let setActiveAnchor: ReturnType; const slugs = new WeakMap(); if (IS_BROWSER) { observer ||= new IntersectionObserver( (entries) => { setActiveAnchor((f) => { const ret = { ...f }; for (const entry of entries) { if (entry?.rootBounds && slugs.has(entry.target)) { const [slug, index, depth, text] = slugs.get(entry.target); const aboveHalfViewport = entry.boundingClientRect.y + entry.boundingClientRect.height <= entry.rootBounds.y + entry.rootBounds.height; const insideHalfViewport = entry.intersectionRatio > 0; ret[slug] = { index, aboveHalfViewport, insideHalfViewport, depth, text, }; } } let activeSlug = ''; let smallestIndexInViewport = Infinity; let largestIndexAboveViewport = -1; for (const s in ret) { ret[s].isActive = false; if ( ret[s].insideHalfViewport && ret[s].index < smallestIndexInViewport ) { smallestIndexInViewport = ret[s].index; activeSlug = s; } if ( smallestIndexInViewport === Infinity && ret[s].aboveHalfViewport && ret[s].index > largestIndexAboveViewport ) { largestIndexAboveViewport = ret[s].index; activeSlug = s; } } if (ret[activeSlug]) ret[activeSlug].isActive = true; return ret; }); }, { rootMargin: '0px 0px -50%', threshold: [0, 1], } ); } // Anchor links const createHeaderLink = ( Tag: `h${2 | 3 | 4 | 5 | 6}`, context: { index: number } ) => function HeaderLink({ children, ...props }: ComponentProps<'h2'>): ReactElement { setActiveAnchor = useSetActiveAnchor(); const obRef = useRef(null); const id = Slugger.slug(children); const depth = Number(Tag.slice(1)); const { pathname } = useRouter(); useEffect(() => { if (!obRef.current) return; slugs.set(obRef.current, [ id, (context.index += 1), depth, String(children), ]); if (obRef.current) observer.observe(obRef.current); return () => { observer.disconnect(); slugs.delete(obRef.current!); setActiveAnchor((f) => { const ret = { ...f }; delete ret[id!]; return ret; }); }; }, []); return (
{children}
); }; const A = ({ href = '', ...props }) => ( ); export const getComponents = ( { isRawLayout = false, }: { isRawLayout?: boolean; } = { isRawLayout: false, } ) => { if (isRawLayout) { return { a: A }; } const context = { index: 0 }; return { h1: (props: ComponentProps<'h1'>) =>

, h2: createHeaderLink('h2', context), h3: createHeaderLink('h3', context), h4: createHeaderLink('h4', context), h5: createHeaderLink('h5', context), h6: createHeaderLink('h6', context), ul: (props: ComponentProps<'ul'>) => (
    ), ol: (props: ComponentProps<'ol'>) => (
      ), li: (props: ComponentProps<'li'>) =>
    1. , blockquote: (props: ComponentProps<'blockquote'>) => (
      ), hr: (props: ComponentProps<'hr'>) => (
      ), a: A, table: (props: ComponentProps<'table'>) => (
      ), p: (props: ComponentProps<'p'>) => (

      ), tr: (props: ComponentProps<'tr'>) => (

      ), th: (props: ComponentProps<'th'>) => { const isDescription = props.children === 'Description'; const isPropOrDefault = props.children === 'Prop' || props.children === 'Default'; return (
      ); }, td: (props: ComponentProps<'td'>) => ( ), code: (props: ComponentProps<'code'>) => ( ), }; };