import React, { useEffect, useRef } from 'react'; import { createStyledComponent } from '@cloudflare/style-container'; import { Main, Header, Div, P, Span } from '@cloudflare/elements'; import { Label } from '@cloudflare/component-label'; import { Trans } from '@cloudflare/intl-react'; import { Heading, Section } from './Heading'; import { DocumentationLink, Link } from '@cloudflare/component-link'; import { useIsMobile } from '@cloudflare/util-responsive'; import { Icon } from '@cloudflare/component-icon'; import { Button } from '@cloudflare/component-button'; import { Tooltip } from '@cloudflare/component-tooltip'; import { useLocation } from '@cloudflare/util-cl1-compat-router'; type PageWidth = 'narrow' | 'wide' | 'unbounded'; type PageHeaderProps = { title?: React.ReactNode; parentPageLabel?: React.ReactNode; description?: React.ReactNode; documentationLabel?: React.ReactNode; documentationHref?: string; control?: React.ReactNode; children?: React.ReactNode; centerHeader?: boolean; beta?: boolean; titleRef?: React.RefObject; parentPageLabelRef?: React.RefObject; navigation?: React.ReactNode; onDocumentationButtonClick?: () => void; documentationAsButton?: boolean; }; type Props = PageHeaderProps & { type?: PageWidth; className?: string; testId?: string; sidebar?: React.ReactNode; sidebarPosition?: 'outside' | 'inside' | 'left'; autofocus?: boolean; }; const maxWidthByType = { narrow: '64em', wide: '79em', unbounded: '100%' }; const maxPageTitles = 2; const PageHeader = ({ title, parentPageLabel, description, documentationLabel, documentationHref, centerHeader, control, children, beta, titleRef, parentPageLabelRef, navigation, onDocumentationButtonClick, documentationAsButton }: PageHeaderProps) => { const headerVisible = !!(title || description); const titlesCount = Math.min([title, description].filter(Boolean).length, 2); const isMobile = useIsMobile(); return ( <> {navigation && !isMobile &&
{navigation}
}
{parentPageLabel && (

{parentPageLabel}

)} {title && (
{isMobile && navigation && React.isValidElement(navigation) && ( )} {title} {beta && ( )}
)} {description && (

{description}

)} {documentationAsButton && !isMobile ? ( } Component={Span} buttonProps={{ width: 'fit-content' }} > ) : ( documentationHref && ( {documentationLabel ? ( documentationLabel ) : ( )} ) )}
{control}
{children}
); }; PageHeader.displayName = 'PageHeader'; // firstPage is used when dealing with focus. When navigating the dash, focus // jumps to the page content, but not when the dash is initially loaded. let firstPage = ''; // firstLoad is used to ensure focus is handled correctly if the user navigates // back to the first page that was loaded. let firstLoad = true; // Workaround for a bug where elements don't focus correctly. const maxFocusAttempts = 10; const focus = (el: HTMLElement | null, attempt: number = 0) => { el?.focus(); if ( typeof document !== 'undefined' && document.activeElement !== el && attempt < maxFocusAttempts ) { setTimeout(() => focus(el, attempt + 1), 10); } }; const Page = ({ title, parentPageLabel, description, documentationLabel, documentationHref, centerHeader, beta, testId, className, sidebar, type = 'wide', sidebarPosition = 'inside', autofocus = true, control, titleRef, parentPageLabelRef, children, navigation, onDocumentationButtonClick, documentationAsButton, ...props }: Props) => { const skipTargetRef = useRef(null); const { pathname } = useLocation(); useEffect(() => { // If autofocus is enabled, then focus will move to the title block when // the page is navigated to (but not when the dash is first loaded) if (autofocus) { if (!firstPage) { firstPage = pathname; } else if (firstPage !== pathname || !firstLoad) { focus(skipTargetRef.current); firstLoad = false; } } }, [pathname]); const sidebarInside = sidebarPosition === 'inside'; return (
{sidebar && sidebarInside && (
{sidebar}
)}
{!sidebarInside && sidebar}
); }; Page.displayName = 'Page'; export default createStyledComponent(() => { return { py: 4 }; }, Page);