1*5b6cd93dSBartosz Kaszubowskiimport { css } from '@emotion/react'; 2d3745f8fSJon Sampimport { theme, palette } from '@expo/styleguide'; 3586106d6SBartłomiej Klocekimport * as React from 'react'; 4586106d6SBartłomiej Klocek 5586106d6SBartłomiej Klocekimport { BASE_HEADING_LEVEL, Heading, HeadingType } from '../common/headingManager'; 6586106d6SBartłomiej Klocek 7586106d6SBartłomiej Klocekimport { paragraph } from '~/components/base/typography'; 8586106d6SBartłomiej Klocekimport * as Constants from '~/constants/theme'; 9586106d6SBartłomiej Klocek 10586106d6SBartłomiej Klocekconst STYLES_LINK = css` 11586106d6SBartłomiej Klocek ${paragraph} 128bf05203SJon Samp color: ${theme.text.secondary}; 138bf05203SJon Samp transition: 50ms ease color; 14586106d6SBartłomiej Klocek font-size: 14px; 15586106d6SBartłomiej Klocek display: block; 16586106d6SBartłomiej Klocek text-decoration: none; 17586106d6SBartłomiej Klocek margin-bottom: 6px; 18586106d6SBartłomiej Klocek cursor: pointer; 19586106d6SBartłomiej Klocek 20586106d6SBartłomiej Klocek :hover { 218bf05203SJon Samp color: ${theme.link.default}; 22586106d6SBartłomiej Klocek } 23586106d6SBartłomiej Klocek`; 24586106d6SBartłomiej Klocek 25586106d6SBartłomiej Klocekconst STYLES_LINK_HEADER = css` 26586106d6SBartłomiej Klocek font-family: ${Constants.fontFamilies.demi}; 27586106d6SBartłomiej Klocek`; 28586106d6SBartłomiej Klocek 29586106d6SBartłomiej Klocekconst STYLES_LINK_CODE = css` 30586106d6SBartłomiej Klocek font-family: ${Constants.fontFamilies.mono}; 31586106d6SBartłomiej Klocek font-size: 13px; 324510b2f2SBartosz Kaszubowski overflow: hidden; 334510b2f2SBartosz Kaszubowski text-overflow: ellipsis; 344510b2f2SBartosz Kaszubowski white-space: nowrap; 35586106d6SBartłomiej Klocek`; 36586106d6SBartłomiej Klocek 37586106d6SBartłomiej Klocekconst STYLES_LINK_ACTIVE = css` 388bf05203SJon Samp color: ${theme.link.default}; 39586106d6SBartłomiej Klocek`; 40586106d6SBartłomiej Klocek 41586106d6SBartłomiej Klocekconst STYLES_TOOLTIP = css` 42586106d6SBartłomiej Klocek border-radius: 3px; 43586106d6SBartłomiej Klocek position: absolute; 44d3745f8fSJon Samp background-color: ${palette.dark.white}; 45586106d6SBartłomiej Klocek font-family: ${Constants.fontFamilies.demi}; 46586106d6SBartłomiej Klocek max-width: 400px; 47586106d6SBartłomiej Klocek border: 1px solid black; 48586106d6SBartłomiej Klocek padding: 3px 6px; 49586106d6SBartłomiej Klocek letter-spacing: normal; 50586106d6SBartłomiej Klocek line-height: 1.4; 51586106d6SBartłomiej Klocek word-break: break-word; 52586106d6SBartłomiej Klocek word-wrap: normal; 53586106d6SBartłomiej Klocek font-size: 12px; 54586106d6SBartłomiej Klocek 55586106d6SBartłomiej Klocek display: inline-block; 56586106d6SBartłomiej Klocek`; 57586106d6SBartłomiej Klocek 58586106d6SBartłomiej Klocekconst STYLES_CODE_TOOLTIP = css` 59586106d6SBartłomiej Klocek font-family: ${Constants.fontFamilies.mono}; 60586106d6SBartłomiej Klocek font-size: 11px; 61586106d6SBartłomiej Klocek`; 62586106d6SBartłomiej Klocek 63586106d6SBartłomiej Klocekconst NESTING_OFFSET = 16; 64586106d6SBartłomiej Klocek 65586106d6SBartłomiej Klocek/** 66586106d6SBartłomiej Klocek * Replaces `Module.someFunction(arguments: argType)` 67586106d6SBartłomiej Klocek * with `someFunction()` 68586106d6SBartłomiej Klocek */ 69586106d6SBartłomiej Klocekconst trimCodedTitle = (str: string) => { 70586106d6SBartłomiej Klocek const dotIdx = str.indexOf('.'); 71586106d6SBartłomiej Klocek if (dotIdx > 0) str = str.substring(dotIdx + 1); 72586106d6SBartłomiej Klocek 73586106d6SBartłomiej Klocek const parIdx = str.indexOf('('); 74586106d6SBartłomiej Klocek if (parIdx > 0) str = str.substring(0, parIdx + 1) + ')'; 75586106d6SBartłomiej Klocek 76586106d6SBartłomiej Klocek return str; 77586106d6SBartłomiej Klocek}; 78586106d6SBartłomiej Klocek 79586106d6SBartłomiej Klocek/** 80586106d6SBartłomiej Klocek * Determines if element is overflowing 81586106d6SBartłomiej Klocek * (its width exceeds container width) 82586106d6SBartłomiej Klocek * @param {HTMLElement} el element to check 83586106d6SBartłomiej Klocek */ 84586106d6SBartłomiej Klocekconst isOverflowing = (el: HTMLElement) => { 85586106d6SBartłomiej Klocek if (!el) { 86586106d6SBartłomiej Klocek return false; 87586106d6SBartłomiej Klocek } 88586106d6SBartłomiej Klocek 89586106d6SBartłomiej Klocek return el.clientWidth < el.scrollWidth; 90586106d6SBartłomiej Klocek}; 91586106d6SBartłomiej Klocek 92586106d6SBartłomiej Klocektype TooltipProps = { 93586106d6SBartłomiej Klocek isCode?: boolean; 94586106d6SBartłomiej Klocek topOffset: number; 95586106d6SBartłomiej Klocek}; 96586106d6SBartłomiej Klocek 97586106d6SBartłomiej Klocekconst Tooltip: React.FC<TooltipProps> = ({ children, isCode, topOffset }) => ( 98586106d6SBartłomiej Klocek <div css={[STYLES_TOOLTIP, isCode && STYLES_CODE_TOOLTIP]} style={{ right: 20, top: topOffset }}> 99586106d6SBartłomiej Klocek {children} 100586106d6SBartłomiej Klocek </div> 101586106d6SBartłomiej Klocek); 102586106d6SBartłomiej Klocek 103586106d6SBartłomiej Klocektype SidebarLinkProps = { 104586106d6SBartłomiej Klocek heading: Heading; 105586106d6SBartłomiej Klocek isActive: boolean; 106586106d6SBartłomiej Klocek shortenCode: boolean; 107586106d6SBartłomiej Klocek onClick: (event: React.MouseEvent<HTMLAnchorElement>) => void; 108586106d6SBartłomiej Klocek}; 109586106d6SBartłomiej Klocek 110586106d6SBartłomiej Klocekconst DocumentationSidebarRightLink = React.forwardRef<HTMLAnchorElement, SidebarLinkProps>( 111586106d6SBartłomiej Klocek ({ heading, isActive, shortenCode, onClick }, ref) => { 112586106d6SBartłomiej Klocek const { slug, level, title, type } = heading; 113586106d6SBartłomiej Klocek 114586106d6SBartłomiej Klocek const isNested = level <= BASE_HEADING_LEVEL; 115586106d6SBartłomiej Klocek const isCode = type === HeadingType.InlineCode; 116586106d6SBartłomiej Klocek 117586106d6SBartłomiej Klocek const paddingLeft = NESTING_OFFSET * (level - BASE_HEADING_LEVEL) + 'px'; 118586106d6SBartłomiej Klocek const displayTitle = shortenCode && isCode ? trimCodedTitle(title) : title; 119586106d6SBartłomiej Klocek 120586106d6SBartłomiej Klocek const [tooltipVisible, setTooltipVisible] = React.useState(false); 121586106d6SBartłomiej Klocek const [tooltipOffset, setTooltipOffset] = React.useState(-20); 122586106d6SBartłomiej Klocek const onMouseOver = (event: React.MouseEvent<HTMLAnchorElement>) => { 123586106d6SBartłomiej Klocek setTooltipVisible(isOverflowing(event.currentTarget)); 124586106d6SBartłomiej Klocek setTooltipOffset(event.currentTarget.getBoundingClientRect().top + 25); 125586106d6SBartłomiej Klocek }; 126586106d6SBartłomiej Klocek const onMouseOut = () => { 127586106d6SBartłomiej Klocek setTooltipVisible(false); 128586106d6SBartłomiej Klocek }; 129586106d6SBartłomiej Klocek 130586106d6SBartłomiej Klocek return ( 131586106d6SBartłomiej Klocek <> 132586106d6SBartłomiej Klocek {tooltipVisible && ( 133586106d6SBartłomiej Klocek <Tooltip topOffset={tooltipOffset} isCode={isCode}> 134586106d6SBartłomiej Klocek {displayTitle} 135586106d6SBartłomiej Klocek </Tooltip> 136586106d6SBartłomiej Klocek )} 137586106d6SBartłomiej Klocek <a 138586106d6SBartłomiej Klocek ref={ref} 139586106d6SBartłomiej Klocek onMouseOver={onMouseOver} 140586106d6SBartłomiej Klocek onMouseOut={onMouseOut} 141586106d6SBartłomiej Klocek style={{ paddingLeft }} 142586106d6SBartłomiej Klocek href={'#' + slug} 143586106d6SBartłomiej Klocek onClick={onClick} 144586106d6SBartłomiej Klocek css={[ 145586106d6SBartłomiej Klocek STYLES_LINK, 146586106d6SBartłomiej Klocek isNested && STYLES_LINK_HEADER, 147586106d6SBartłomiej Klocek isCode && STYLES_LINK_CODE, 148586106d6SBartłomiej Klocek isActive && STYLES_LINK_ACTIVE, 149586106d6SBartłomiej Klocek ]}> 150586106d6SBartłomiej Klocek {displayTitle} 151586106d6SBartłomiej Klocek </a> 152586106d6SBartłomiej Klocek </> 153586106d6SBartłomiej Klocek ); 154586106d6SBartłomiej Klocek } 155586106d6SBartłomiej Klocek); 156586106d6SBartłomiej Klocek 157586106d6SBartłomiej Klocekexport default DocumentationSidebarRightLink; 158