diff --git a/apify-docs-theme/src/theme/Heading/index.jsx b/apify-docs-theme/src/theme/Heading/index.jsx new file mode 100644 index 000000000..bed905c13 --- /dev/null +++ b/apify-docs-theme/src/theme/Heading/index.jsx @@ -0,0 +1,85 @@ +import { useThemeConfig } from '@docusaurus/theme-common'; +import { translate } from '@docusaurus/Translate'; +import useBrokenLinks from '@docusaurus/useBrokenLinks'; +import clsx from 'clsx'; +import React, { useEffect } from 'react'; + +import { LinkIcon } from '@apify/ui-icons'; +import { useCopyToClipboard } from '@apify/ui-library'; + +import styles from './styles.module.css'; + +export default function Heading({ as: As, id, ...props }) { + const brokenLinks = useBrokenLinks(); + const { + navbar: { hideOnScroll }, + } = useThemeConfig(); + + const { isCopied, copyToClipboard } = useCopyToClipboard(); + + // Register the anchor ID so Docusaurus can scroll to it + useEffect(() => { + if (id) { + brokenLinks.collectAnchor(id); + + // Handle scroll on page load if this heading matches the URL hash + const hash = decodeURIComponent(window.location.hash.slice(1)); + if (hash === id) { + // Use setTimeout to ensure the page is fully rendered + setTimeout(() => { + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }, 100); + } + } + }, [id, brokenLinks]); + + // H1 headings and headings without an id shouldn't have the copy to clipboard button + if (As === 'h1' || !id) { + return ; + } + + const handleCopy = async (e) => { + e.preventDefault(); + const url = new URL(window.location.href); + url.hash = `#${id ?? ''}`; + window.location.hash = `#${id ?? ''}`; + await copyToClipboard(url.toString()); + }; + + const anchorTitle = translate( + { + id: 'theme.common.headingLinkTitle', + message: 'Direct link to {heading}', + description: 'Title for link to heading', + }, + { + heading: typeof props.children === 'string' ? props.children : id, + }, + ); + + return ( + + {props.children} + + + + + ); +} diff --git a/apify-docs-theme/src/theme/Heading/styles.module.css b/apify-docs-theme/src/theme/Heading/styles.module.css new file mode 100644 index 000000000..a94765a22 --- /dev/null +++ b/apify-docs-theme/src/theme/Heading/styles.module.css @@ -0,0 +1,47 @@ +.anchorWithStickyNavbar { + scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem); +} + +.anchorWithHideOnScrollNavbar { + scroll-margin-top: 0.5rem; +} + +.headingCopyIcon { + display: none; + position: relative; + left: .4rem; + color: var(--ifm-color-emphasis-700); + text-decoration: none; +} + +.headingCopyIcon svg { + stroke: var(--ifm-color-primary); + max-height: 1em !important; +} + +h2:hover .headingCopyIcon, +h3:hover .headingCopyIcon, +h4:hover .headingCopyIcon, +h5:hover .headingCopyIcon, +h6:hover .headingCopyIcon { + display: inline-block; +} + +.headingCopyIcon.copied { + display: inline-block !important; +} + +.headingCopyIcon.copied svg { + display: none; +} + +.headingCopyIcon.copied::after { + content: 'Copied!'; + font-size: 12px; + color: var(--ifm-font-color-secondary); + font-weight: 600; + margin-left: 8px; + vertical-align: middle; + line-height: 1; +} + diff --git a/package-lock.json b/package-lock.json index eb230c89b..e61c2c828 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ }, "apify-docs-theme": { "name": "@apify/docs-theme", - "version": "1.0.214", + "version": "1.0.216", "license": "ISC", "dependencies": { "@apify/docs-search-modal": "^1.3.3",