diff --git a/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx b/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx index 52710f088139..6092df125130 100644 --- a/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/SkipToContent/index.tsx @@ -5,40 +5,14 @@ * LICENSE file in the root directory of this source tree. */ -import React, {useRef} from 'react'; -import {useHistory} from '@docusaurus/router'; +import React from 'react'; import Translate from '@docusaurus/Translate'; -import {useLocationChange} from '@docusaurus/theme-common'; +import {useSkipToContent} from '@docusaurus/theme-common'; import styles from './styles.module.css'; -function programmaticFocus(el: HTMLElement) { - el.setAttribute('tabindex', '-1'); - el.focus(); - el.removeAttribute('tabindex'); -} - export default function SkipToContent(): JSX.Element { - const containerRef = useRef(null); - const {action} = useHistory(); - const handleSkip = (e: React.MouseEvent) => { - e.preventDefault(); - - const targetElement: HTMLElement | null = - document.querySelector('main:first-of-type') || - document.querySelector('.main-wrapper'); - - if (targetElement) { - programmaticFocus(targetElement); - } - }; - - useLocationChange(({location}) => { - if (containerRef.current && !location.hash && action === 'PUSH') { - programmaticFocus(containerRef.current); - } - }); - + const {containerRef, handleSkip} = useSkipToContent(); return (
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 4ee294d41cac..38a0486fc10c 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -57,6 +57,8 @@ export { useLayoutDocsSidebar, } from './utils/docsUtils'; +export {useSkipToContent} from './utils/a11yUtils'; + export {useTitleFormatter} from './utils/generalUtils'; export {usePluralForm} from './utils/usePluralForm'; diff --git a/packages/docusaurus-theme-common/src/utils/a11yUtils.ts b/packages/docusaurus-theme-common/src/utils/a11yUtils.ts new file mode 100644 index 000000000000..9e1d0cf797c6 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/a11yUtils.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import type React from 'react'; +import {useCallback, useRef} from 'react'; +import {useHistory} from '@docusaurus/router'; +import {useLocationChange} from './useLocationChange'; + +function programmaticFocus(el: HTMLElement) { + el.setAttribute('tabindex', '-1'); + el.focus(); + el.removeAttribute('tabindex'); +} + +export function useSkipToContent() { + const containerRef = useRef(null); + const {action} = useHistory(); + const handleSkip = useCallback((e: React.MouseEvent) => { + e.preventDefault(); + + const targetElement: HTMLElement | null = + document.querySelector('main:first-of-type') || + document.querySelector('.main-wrapper'); + + if (targetElement) { + programmaticFocus(targetElement); + } + }, []); + + useLocationChange(({location}) => { + if (containerRef.current && !location.hash && action === 'PUSH') { + programmaticFocus(containerRef.current); + } + }); + + return {containerRef, handleSkip}; +}