15b6cd93dSBartosz Kaszubowskiimport { css } from '@emotion/react'; 2c4c6b9d1SBartosz Kaszubowskiimport { theme, typography } from '@expo/styleguide'; 3586106d6SBartłomiej Klocekimport * as React from 'react'; 4586106d6SBartłomiej Klocek 5c4c6b9d1SBartosz Kaszubowskiimport { BASE_HEADING_LEVEL, Heading, HeadingType } from '~/common/headingManager'; 6586106d6SBartłomiej Klocekimport { paragraph } from '~/components/base/typography'; 7c4c6b9d1SBartosz Kaszubowskiimport { Tag } from '~/ui/components/Tag'; 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; 14c4c6b9d1SBartosz Kaszubowski display: flex; 15586106d6SBartłomiej Klocek text-decoration: none; 16586106d6SBartłomiej Klocek margin-bottom: 6px; 17586106d6SBartłomiej Klocek cursor: pointer; 18c4c6b9d1SBartosz Kaszubowski overflow: hidden; 19c4c6b9d1SBartosz Kaszubowski text-overflow: ellipsis; 20c4c6b9d1SBartosz Kaszubowski align-items: center; 21c4c6b9d1SBartosz Kaszubowski justify-content: space-between; 22586106d6SBartłomiej Klocek 23586106d6SBartłomiej Klocek :hover { 248bf05203SJon Samp color: ${theme.link.default}; 25586106d6SBartłomiej Klocek } 26586106d6SBartłomiej Klocek`; 27586106d6SBartłomiej Klocek 28586106d6SBartłomiej Klocekconst STYLES_LINK_HEADER = css` 298947d273SCedric van Putten font-family: ${typography.fontFaces.medium}; 30586106d6SBartłomiej Klocek`; 31586106d6SBartłomiej Klocek 32c4c6b9d1SBartosz Kaszubowskiconst STYLES_LINK_LABEL = css` 334510b2f2SBartosz Kaszubowski overflow: hidden; 344510b2f2SBartosz Kaszubowski text-overflow: ellipsis; 354510b2f2SBartosz Kaszubowski white-space: nowrap; 36586106d6SBartłomiej Klocek`; 37586106d6SBartłomiej Klocek 38c4c6b9d1SBartosz Kaszubowskiconst STYLES_LINK_CODE = css` 39c4c6b9d1SBartosz Kaszubowski font-family: ${typography.fontFaces.mono}; 40c4c6b9d1SBartosz Kaszubowski font-size: 13px; 41c4c6b9d1SBartosz Kaszubowski`; 42c4c6b9d1SBartosz Kaszubowski 43586106d6SBartłomiej Klocekconst STYLES_LINK_ACTIVE = css` 448bf05203SJon Samp color: ${theme.link.default}; 45586106d6SBartłomiej Klocek`; 46586106d6SBartłomiej Klocek 47586106d6SBartłomiej Klocekconst STYLES_TOOLTIP = css` 48c4c6b9d1SBartosz Kaszubowski ${typography.fontSizes[13]} 49586106d6SBartłomiej Klocek border-radius: 3px; 50586106d6SBartłomiej Klocek position: absolute; 51c4c6b9d1SBartosz Kaszubowski color: ${theme.text.default}; 52c4c6b9d1SBartosz Kaszubowski background-color: ${theme.background.secondary}; 538947d273SCedric van Putten font-family: ${typography.fontFaces.medium}; 54586106d6SBartłomiej Klocek max-width: 400px; 55c4c6b9d1SBartosz Kaszubowski border: 1px solid ${theme.border.default}; 56586106d6SBartłomiej Klocek padding: 3px 6px; 57586106d6SBartłomiej Klocek word-break: break-word; 58586106d6SBartłomiej Klocek word-wrap: normal; 59586106d6SBartłomiej Klocek display: inline-block; 60586106d6SBartłomiej Klocek`; 61586106d6SBartłomiej Klocek 62586106d6SBartłomiej Klocekconst STYLES_CODE_TOOLTIP = css` 638947d273SCedric van Putten font-family: ${typography.fontFaces.mono}; 64586106d6SBartłomiej Klocek font-size: 11px; 65586106d6SBartłomiej Klocek`; 66586106d6SBartłomiej Klocek 67c4c6b9d1SBartosz Kaszubowskiconst STYLES_TAG_CONTAINER = css` 68c4c6b9d1SBartosz Kaszubowski display: inline-flex; 69c4c6b9d1SBartosz Kaszubowski`; 70c4c6b9d1SBartosz Kaszubowski 71c4c6b9d1SBartosz Kaszubowskiconst NESTING_OFFSET = 12; 72586106d6SBartłomiej Klocek 73586106d6SBartłomiej Klocek/** 74586106d6SBartłomiej Klocek * Replaces `Module.someFunction(arguments: argType)` 75586106d6SBartłomiej Klocek * with `someFunction()` 76586106d6SBartłomiej Klocek */ 77586106d6SBartłomiej Klocekconst trimCodedTitle = (str: string) => { 78586106d6SBartłomiej Klocek const dotIdx = str.indexOf('.'); 79586106d6SBartłomiej Klocek if (dotIdx > 0) str = str.substring(dotIdx + 1); 80586106d6SBartłomiej Klocek 81586106d6SBartłomiej Klocek const parIdx = str.indexOf('('); 82586106d6SBartłomiej Klocek if (parIdx > 0) str = str.substring(0, parIdx + 1) + ')'; 83586106d6SBartłomiej Klocek 84586106d6SBartłomiej Klocek return str; 85586106d6SBartłomiej Klocek}; 86586106d6SBartłomiej Klocek 87586106d6SBartłomiej Klocek/** 88586106d6SBartłomiej Klocek * Determines if element is overflowing 89c4c6b9d1SBartosz Kaszubowski * (its children width exceeds container width) 90586106d6SBartłomiej Klocek * @param {HTMLElement} el element to check 91586106d6SBartłomiej Klocek */ 92586106d6SBartłomiej Klocekconst isOverflowing = (el: HTMLElement) => { 93c4c6b9d1SBartosz Kaszubowski if (!el || !el.children) { 94586106d6SBartłomiej Klocek return false; 95586106d6SBartłomiej Klocek } 96586106d6SBartłomiej Klocek 97c4c6b9d1SBartosz Kaszubowski const childrenWidth = Array.from(el.children).reduce((sum, child) => sum + child.scrollWidth, 0); 98fc13007cSBartosz Kaszubowski return childrenWidth >= el.scrollWidth - parseInt(el.style.paddingLeft, 10); 99586106d6SBartłomiej Klocek}; 100586106d6SBartłomiej Klocek 1011f61a071SBartosz Kaszubowskitype TooltipProps = React.PropsWithChildren<{ 102586106d6SBartłomiej Klocek isCode?: boolean; 103586106d6SBartłomiej Klocek topOffset: number; 1041f61a071SBartosz Kaszubowski}>; 105586106d6SBartłomiej Klocek 1061f61a071SBartosz Kaszubowskiconst Tooltip = ({ children, isCode, topOffset }: TooltipProps) => ( 107c4c6b9d1SBartosz Kaszubowski <div css={[STYLES_TOOLTIP, isCode && STYLES_CODE_TOOLTIP]} style={{ right: 24, top: topOffset }}> 108586106d6SBartłomiej Klocek {children} 109586106d6SBartłomiej Klocek </div> 110586106d6SBartłomiej Klocek); 111586106d6SBartłomiej Klocek 112586106d6SBartłomiej Klocektype SidebarLinkProps = { 113586106d6SBartłomiej Klocek heading: Heading; 114586106d6SBartłomiej Klocek isActive: boolean; 115586106d6SBartłomiej Klocek shortenCode: boolean; 116586106d6SBartłomiej Klocek onClick: (event: React.MouseEvent<HTMLAnchorElement>) => void; 117586106d6SBartłomiej Klocek}; 118586106d6SBartłomiej Klocek 119586106d6SBartłomiej Klocekconst DocumentationSidebarRightLink = React.forwardRef<HTMLAnchorElement, SidebarLinkProps>( 120586106d6SBartłomiej Klocek ({ heading, isActive, shortenCode, onClick }, ref) => { 121c4c6b9d1SBartosz Kaszubowski const { slug, level, title, type, tags } = heading; 122586106d6SBartłomiej Klocek 123586106d6SBartłomiej Klocek const isNested = level <= BASE_HEADING_LEVEL; 124586106d6SBartłomiej Klocek const isCode = type === HeadingType.InlineCode; 125586106d6SBartłomiej Klocek 126586106d6SBartłomiej Klocek const paddingLeft = NESTING_OFFSET * (level - BASE_HEADING_LEVEL) + 'px'; 127586106d6SBartłomiej Klocek const displayTitle = shortenCode && isCode ? trimCodedTitle(title) : title; 128586106d6SBartłomiej Klocek 129586106d6SBartłomiej Klocek const [tooltipVisible, setTooltipVisible] = React.useState(false); 130586106d6SBartłomiej Klocek const [tooltipOffset, setTooltipOffset] = React.useState(-20); 131586106d6SBartłomiej Klocek const onMouseOver = (event: React.MouseEvent<HTMLAnchorElement>) => { 132586106d6SBartłomiej Klocek setTooltipVisible(isOverflowing(event.currentTarget)); 133c4c6b9d1SBartosz Kaszubowski setTooltipOffset( 134c4c6b9d1SBartosz Kaszubowski event.currentTarget.getBoundingClientRect().top + event.currentTarget.offsetHeight 135c4c6b9d1SBartosz Kaszubowski ); 136586106d6SBartłomiej Klocek }; 137c4c6b9d1SBartosz Kaszubowski 138586106d6SBartłomiej Klocek const onMouseOut = () => { 139586106d6SBartłomiej Klocek setTooltipVisible(false); 140586106d6SBartłomiej Klocek }; 141586106d6SBartłomiej Klocek 142586106d6SBartłomiej Klocek return ( 143586106d6SBartłomiej Klocek <> 144586106d6SBartłomiej Klocek {tooltipVisible && ( 145586106d6SBartłomiej Klocek <Tooltip topOffset={tooltipOffset} isCode={isCode}> 146586106d6SBartłomiej Klocek {displayTitle} 147586106d6SBartłomiej Klocek </Tooltip> 148586106d6SBartłomiej Klocek )} 149586106d6SBartłomiej Klocek <a 150586106d6SBartłomiej Klocek ref={ref} 151586106d6SBartłomiej Klocek onMouseOver={onMouseOver} 152586106d6SBartłomiej Klocek onMouseOut={onMouseOut} 153586106d6SBartłomiej Klocek style={{ paddingLeft }} 154586106d6SBartłomiej Klocek href={'#' + slug} 155586106d6SBartłomiej Klocek onClick={onClick} 156c4c6b9d1SBartosz Kaszubowski css={[STYLES_LINK, isNested && STYLES_LINK_HEADER, isActive && STYLES_LINK_ACTIVE]}> 157c4c6b9d1SBartosz Kaszubowski <span css={[STYLES_LINK_LABEL, isCode && STYLES_LINK_CODE]}>{displayTitle}</span> 158c4c6b9d1SBartosz Kaszubowski {tags && tags.length ? ( 159c4c6b9d1SBartosz Kaszubowski <div css={STYLES_TAG_CONTAINER}> 160c4c6b9d1SBartosz Kaszubowski {tags.map(tag => ( 161*22ee2951SBartosz Kaszubowski <Tag name={tag} type="toc" key={`${displayTitle}-${tag}`} /> 162c4c6b9d1SBartosz Kaszubowski ))} 163c4c6b9d1SBartosz Kaszubowski </div> 164c4c6b9d1SBartosz Kaszubowski ) : undefined} 165586106d6SBartłomiej Klocek </a> 166586106d6SBartłomiej Klocek </> 167586106d6SBartłomiej Klocek ); 168586106d6SBartłomiej Klocek } 169586106d6SBartłomiej Klocek); 170586106d6SBartłomiej Klocek 171586106d6SBartłomiej Klocekexport default DocumentationSidebarRightLink; 172