1import { css } from '@emotion/react'; 2import { LinkBase } from '@expo/styleguide'; 3import { ClipboardIcon, Link01SolidIcon } from '@expo/styleguide-icons'; 4import * as React from 'react'; 5import tippy from 'tippy.js'; 6 7import { AdditionalProps } from '~/common/headingManager'; 8import withHeadingManager, { 9 HeadingManagerProps, 10} from '~/components/page-higher-order/withHeadingManager'; 11 12type BaseProps = React.PropsWithChildren<{ 13 component: any; 14 className?: string; 15 style?: React.CSSProperties; 16}>; 17 18type EnhancedProps = React.PropsWithChildren<{ 19 // Sidebar heading level override 20 nestingLevel?: number; 21 additionalProps?: AdditionalProps; 22 id?: string; 23}>; 24 25const STYLES_PERMALINK_TARGET = css` 26 display: block; 27 position: absolute; 28 top: -46px; 29 visibility: hidden; 30`; 31 32const STYLES_PERMALINK_LINK = css` 33 position: relative; 34 color: inherit; 35 text-decoration: none !important; 36 37 /* Disable link when used in collapsible, to allow expand on click */ 38 details & { 39 pointer-events: none; 40 } 41`; 42 43const STYLED_PERMALINK_CONTENT = css` 44 display: inline; 45`; 46 47const STYLES_PERMALINK_ICON = css` 48 cursor: pointer; 49 vertical-align: middle; 50 display: inline-block; 51 width: 1.2em; 52 height: 1em; 53 padding: 0 0.2em; 54 visibility: hidden; 55 56 a:hover &, 57 a:focus-visible & { 58 visibility: visible; 59 } 60 61 svg { 62 width: 100%; 63 height: auto; 64 } 65`; 66 67const PermalinkBase = ({ component, children, className, ...rest }: BaseProps) => 68 React.cloneElement( 69 component, 70 { 71 className: [className, component.props.className || ''].join(' '), 72 ...rest, 73 }, 74 children 75 ); 76 77// @ts-ignore Jest ESM issue https://github.com/facebook/jest/issues/9430 78const { default: testTippy } = tippy; 79 80const PermalinkCopyIcon = ({ 81 slug, 82 onClick, 83}: { 84 slug: string; 85 onClick?: (event: React.MouseEvent<HTMLElement>) => void | undefined; 86}) => { 87 const tippyFunc = testTippy || tippy; 88 89 const [tooltipInstance, setTooltipInstance] = React.useState< 90 { setContent: (content: string) => void } | undefined 91 >(undefined); 92 93 React.useEffect(() => { 94 const tippyInstance = tippyFunc('#docs-anchor-permalink-copy-' + slug, { 95 content: 'Copy anchor link', 96 arrow: null, 97 offset: [0, 0], 98 hideOnClick: false, 99 }); 100 setTooltipInstance(tippyInstance[0]); 101 }, []); 102 103 const myOnClick = React.useCallback( 104 (event: React.MouseEvent<HTMLElement>) => { 105 event.preventDefault(); 106 const url = window.location.href.replace(/#.*/, '') + '#' + slug; 107 navigator.clipboard?.writeText(url); 108 tooltipInstance?.setContent('Copied!'); 109 onClick && onClick(event); 110 }, 111 [tooltipInstance, onClick] 112 ); 113 114 return ( 115 <span id={'docs-anchor-permalink-copy-' + slug} onClick={myOnClick} css={STYLES_PERMALINK_ICON}> 116 <ClipboardIcon className="icon-sm text-icon-default" /> 117 </span> 118 ); 119}; 120 121export { PermalinkCopyIcon }; 122 123const Permalink: React.FC<EnhancedProps> = withHeadingManager( 124 (props: EnhancedProps & HeadingManagerProps) => { 125 // NOTE(jim): Not the greatest way to generate permalinks. 126 // for now I've shortened the length of permalinks. 127 const component = props.children as JSX.Element; 128 const children = component.props.children || ''; 129 130 if (!props.nestingLevel) { 131 return children; 132 } 133 134 const heading = props.headingManager.addHeading( 135 children, 136 props.nestingLevel, 137 props.additionalProps, 138 props.id 139 ); 140 141 return ( 142 <PermalinkBase component={component} style={props.additionalProps?.style}> 143 <LinkBase css={STYLES_PERMALINK_LINK} href={'#' + heading.slug} ref={heading.ref}> 144 <span css={STYLES_PERMALINK_TARGET} id={heading.slug} /> 145 <span css={STYLED_PERMALINK_CONTENT}>{children}</span> 146 <span css={STYLES_PERMALINK_ICON}> 147 <Link01SolidIcon className="icon-sm text-icon-default" /> 148 </span> 149 <PermalinkCopyIcon slug={heading.slug} /> 150 </LinkBase> 151 </PermalinkBase> 152 ); 153 } 154); 155 156export default Permalink; 157