1import { css } from '@emotion/core'; 2import * as React from 'react'; 3 4import { AdditionalProps } from '~/common/headingManager'; 5import PermalinkIcon from '~/components/icons/Permalink'; 6import withHeadingManager from '~/components/page-higher-order/withHeadingManager'; 7 8type BaseProps = { 9 component: any; 10 className?: string; 11}; 12 13type EnhancedProps = { 14 children: React.ReactNode; 15 nestingLevel?: number; 16 additionalProps?: AdditionalProps; 17 customIconStyle?: React.CSSProperties; 18 id?: string; 19}; 20 21const STYLES_PERMALINK = css` 22 position: relative; 23`; 24 25const STYLES_PERMALINK_TARGET = css` 26 display: block; 27 position: absolute; 28 top: -100px; 29 visibility: hidden; 30`; 31 32const STYLES_PERMALINK_LINK = css` 33 color: inherit; 34 text-decoration: inherit; 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: text-top; 49 display: inline-block; 50 width: 1.2em; 51 height: 1.2em; 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: React.FC<BaseProps> = ({ component, children, className, ...rest }) => 66 React.cloneElement( 67 component, 68 { 69 className: [className, component.props.className || ''].join(' '), 70 ...rest, 71 }, 72 children 73 ); 74 75/** 76 * Props: 77 * - children: Title or component containing title text 78 * - nestingLevel: Sidebar heading level override 79 * - additionalProps: Additional properties passed to component 80 */ 81const Permalink: React.FC<EnhancedProps> = withHeadingManager(props => { 82 // NOTE(jim): Not the greatest way to generate permalinks. 83 // for now I've shortened the length of permalinks. 84 const component = props.children as JSX.Element; 85 const children = component.props.children || ''; 86 87 let permalinkKey = props.id; 88 89 const heading = props.headingManager.addHeading( 90 children, 91 props.nestingLevel, 92 props.additionalProps 93 ); 94 95 if (!permalinkKey) { 96 permalinkKey = heading.slug; 97 } 98 99 return ( 100 <PermalinkBase component={component} data-components-heading> 101 <div css={STYLES_PERMALINK} ref={heading.ref}> 102 <span css={STYLES_PERMALINK_TARGET} id={permalinkKey} /> 103 <a css={STYLES_PERMALINK_LINK} href={'#' + permalinkKey}> 104 <span css={STYLED_PERMALINK_CONTENT}>{children}</span> 105 <span css={STYLES_PERMALINK_ICON} style={props.customIconStyle}> 106 <PermalinkIcon /> 107 </span> 108 </a> 109 </div> 110 </PermalinkBase> 111 ); 112}); 113 114export default Permalink; 115