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