1import { css } from '@emotion/react'; 2import { LinkBase } from '@expo/styleguide'; 3import * as React from 'react'; 4 5import { AdditionalProps } from '~/common/headingManager'; 6import PermalinkIcon from '~/components/icons/Permalink'; 7import withHeadingManager, { 8 HeadingManagerProps, 9} from '~/components/page-higher-order/withHeadingManager'; 10 11type BaseProps = React.PropsWithChildren<{ 12 component: any; 13 className?: string; 14 style?: React.CSSProperties; 15}>; 16 17type EnhancedProps = React.PropsWithChildren<{ 18 // Sidebar heading level override 19 nestingLevel?: number; 20 additionalProps?: AdditionalProps; 21 id?: string; 22}>; 23 24const STYLES_PERMALINK_TARGET = css` 25 display: block; 26 position: absolute; 27 top: -46px; 28 visibility: hidden; 29`; 30 31const STYLES_PERMALINK_LINK = css` 32 position: relative; 33 color: inherit; 34 text-decoration: none !important; 35 36 /* Disable link when used in collapsible, to allow expand on click */ 37 details & { 38 pointer-events: none; 39 } 40`; 41 42const STYLED_PERMALINK_CONTENT = css` 43 display: inline; 44`; 45 46const STYLES_PERMALINK_ICON = css` 47 cursor: pointer; 48 vertical-align: middle; 49 display: inline-block; 50 width: 1.2em; 51 height: 1em; 52 padding: 0 0.2em; 53 visibility: hidden; 54 55 a:hover & { 56 visibility: visible; 57 } 58 59 svg { 60 width: 100%; 61 height: auto; 62 } 63`; 64 65const PermalinkBase = ({ component, children, className, ...rest }: BaseProps) => 66 React.cloneElement( 67 component, 68 { 69 className: [className, component.props.className || ''].join(' '), 70 ...rest, 71 }, 72 children 73 ); 74 75const Permalink: React.FC<EnhancedProps> = withHeadingManager( 76 (props: EnhancedProps & HeadingManagerProps) => { 77 // NOTE(jim): Not the greatest way to generate permalinks. 78 // for now I've shortened the length of permalinks. 79 const component = props.children as JSX.Element; 80 const children = component.props.children || ''; 81 82 if (!props.nestingLevel) { 83 return children; 84 } 85 86 const heading = props.headingManager.addHeading( 87 children, 88 props.nestingLevel, 89 props.additionalProps, 90 props.id 91 ); 92 93 return ( 94 <PermalinkBase component={component} style={props.additionalProps?.style}> 95 <LinkBase css={STYLES_PERMALINK_LINK} href={'#' + heading.slug} ref={heading.ref}> 96 <span css={STYLES_PERMALINK_TARGET} id={heading.slug} /> 97 <span css={STYLED_PERMALINK_CONTENT}>{children}</span> 98 <span css={STYLES_PERMALINK_ICON}> 99 <PermalinkIcon /> 100 </span> 101 </LinkBase> 102 </PermalinkBase> 103 ); 104 } 105); 106 107export default Permalink; 108