15b6cd93dSBartosz Kaszubowskiimport { css } from '@emotion/react'; 2c4c6b9d1SBartosz Kaszubowskiimport { theme, typography } from '@expo/styleguide'; 3*c730ceaaSBartosz Kaszubowskiimport Link from 'next/link'; 4586106d6SBartłomiej Klocekimport * as React from 'react'; 5586106d6SBartłomiej Klocek 6c4c6b9d1SBartosz Kaszubowskiimport { BASE_HEADING_LEVEL, Heading, HeadingType } from '~/common/headingManager'; 7c4c6b9d1SBartosz Kaszubowskiimport { Tag } from '~/ui/components/Tag'; 8db57e342SBartosz Kaszubowskiimport { MONOSPACE, CALLOUT } from '~/ui/components/Text'; 9586106d6SBartłomiej Klocek 10586106d6SBartłomiej Klocekconst STYLES_LINK = css` 118bf05203SJon Samp transition: 50ms ease color; 12c4c6b9d1SBartosz Kaszubowski display: flex; 13586106d6SBartłomiej Klocek text-decoration: none; 14586106d6SBartłomiej Klocek margin-bottom: 6px; 15586106d6SBartłomiej Klocek cursor: pointer; 16c4c6b9d1SBartosz Kaszubowski overflow: hidden; 17c4c6b9d1SBartosz Kaszubowski text-overflow: ellipsis; 18c4c6b9d1SBartosz Kaszubowski align-items: center; 19c4c6b9d1SBartosz Kaszubowski justify-content: space-between; 20586106d6SBartłomiej Klocek`; 21586106d6SBartłomiej Klocek 22c4c6b9d1SBartosz Kaszubowskiconst STYLES_LINK_LABEL = css` 234510b2f2SBartosz Kaszubowski overflow: hidden; 244510b2f2SBartosz Kaszubowski text-overflow: ellipsis; 254510b2f2SBartosz Kaszubowski white-space: nowrap; 26db57e342SBartosz Kaszubowski color: ${theme.text.secondary}; 27db57e342SBartosz Kaszubowski 28db57e342SBartosz Kaszubowski :hover { 29db57e342SBartosz Kaszubowski color: ${theme.link.default}; 30db57e342SBartosz Kaszubowski } 31586106d6SBartłomiej Klocek`; 32586106d6SBartłomiej Klocek 33db57e342SBartosz Kaszubowskiconst STYLES_LINK_MONOSPACE = css` 34db57e342SBartosz Kaszubowski ${typography.fontSizes[13]} 35c4c6b9d1SBartosz Kaszubowski`; 36c4c6b9d1SBartosz Kaszubowski 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; 44c4c6b9d1SBartosz Kaszubowski background-color: ${theme.background.secondary}; 45586106d6SBartłomiej Klocek max-width: 400px; 46c4c6b9d1SBartosz Kaszubowski border: 1px solid ${theme.border.default}; 47586106d6SBartłomiej Klocek padding: 3px 6px; 48586106d6SBartłomiej Klocek display: inline-block; 49586106d6SBartłomiej Klocek`; 50586106d6SBartłomiej Klocek 51db57e342SBartosz Kaszubowskiconst STYLES_TOOLTIP_TEXT = css` 52db57e342SBartosz Kaszubowski ${typography.fontSizes[13]} 53db57e342SBartosz Kaszubowski color: ${theme.text.default}; 54db57e342SBartosz Kaszubowski word-break: break-word; 55db57e342SBartosz Kaszubowski word-wrap: normal; 56db57e342SBartosz Kaszubowski`; 57db57e342SBartosz Kaszubowski 58db57e342SBartosz Kaszubowskiconst STYLES_TOOLTIP_CODE = css` 59db57e342SBartosz Kaszubowski ${typography.fontSizes[12]} 60586106d6SBartłomiej Klocek`; 61586106d6SBartłomiej Klocek 62c4c6b9d1SBartosz Kaszubowskiconst STYLES_TAG_CONTAINER = css` 63c4c6b9d1SBartosz Kaszubowski display: inline-flex; 64c4c6b9d1SBartosz Kaszubowski`; 65c4c6b9d1SBartosz Kaszubowski 66c4c6b9d1SBartosz Kaszubowskiconst NESTING_OFFSET = 12; 67586106d6SBartłomiej Klocek 68586106d6SBartłomiej Klocek/** 69586106d6SBartłomiej Klocek * Replaces `Module.someFunction(arguments: argType)` 70586106d6SBartłomiej Klocek * with `someFunction()` 71586106d6SBartłomiej Klocek */ 72586106d6SBartłomiej Klocekconst trimCodedTitle = (str: string) => { 73586106d6SBartłomiej Klocek const dotIdx = str.indexOf('.'); 74586106d6SBartłomiej Klocek if (dotIdx > 0) str = str.substring(dotIdx + 1); 75586106d6SBartłomiej Klocek 76586106d6SBartłomiej Klocek const parIdx = str.indexOf('('); 77586106d6SBartłomiej Klocek if (parIdx > 0) str = str.substring(0, parIdx + 1) + ')'; 78586106d6SBartłomiej Klocek 79586106d6SBartłomiej Klocek return str; 80586106d6SBartłomiej Klocek}; 81586106d6SBartłomiej Klocek 82586106d6SBartłomiej Klocek/** 83586106d6SBartłomiej Klocek * Determines if element is overflowing 84c4c6b9d1SBartosz Kaszubowski * (its children width exceeds container width) 85586106d6SBartłomiej Klocek * @param {HTMLElement} el element to check 86586106d6SBartłomiej Klocek */ 87586106d6SBartłomiej Klocekconst isOverflowing = (el: HTMLElement) => { 88c4c6b9d1SBartosz Kaszubowski if (!el || !el.children) { 89586106d6SBartłomiej Klocek return false; 90586106d6SBartłomiej Klocek } 91586106d6SBartłomiej Klocek 92c4c6b9d1SBartosz Kaszubowski const childrenWidth = Array.from(el.children).reduce((sum, child) => sum + child.scrollWidth, 0); 93*c730ceaaSBartosz Kaszubowski const indent = parseInt(window.getComputedStyle(el).paddingLeft, 10); 94*c730ceaaSBartosz Kaszubowski return childrenWidth >= el.scrollWidth - indent; 95586106d6SBartłomiej Klocek}; 96586106d6SBartłomiej Klocek 971f61a071SBartosz Kaszubowskitype TooltipProps = React.PropsWithChildren<{ 98586106d6SBartłomiej Klocek isCode?: boolean; 99586106d6SBartłomiej Klocek topOffset: number; 1001f61a071SBartosz Kaszubowski}>; 101586106d6SBartłomiej Klocek 102db57e342SBartosz Kaszubowskiconst Tooltip = ({ children, isCode, topOffset }: TooltipProps) => { 103db57e342SBartosz Kaszubowski const ContentWrapper = isCode ? MONOSPACE : CALLOUT; 104db57e342SBartosz Kaszubowski return ( 105db57e342SBartosz Kaszubowski <div css={STYLES_TOOLTIP} style={{ right: 24, top: topOffset }}> 106db57e342SBartosz Kaszubowski <ContentWrapper css={[STYLES_TOOLTIP_TEXT, isCode && STYLES_TOOLTIP_CODE]}> 107586106d6SBartłomiej Klocek {children} 108db57e342SBartosz Kaszubowski </ContentWrapper> 109586106d6SBartłomiej Klocek </div> 110586106d6SBartłomiej Klocek ); 111db57e342SBartosz Kaszubowski}; 112586106d6SBartłomiej Klocek 113586106d6SBartłomiej Klocektype SidebarLinkProps = { 114586106d6SBartłomiej Klocek heading: Heading; 115586106d6SBartłomiej Klocek isActive: boolean; 116586106d6SBartłomiej Klocek shortenCode: boolean; 117586106d6SBartłomiej Klocek onClick: (event: React.MouseEvent<HTMLAnchorElement>) => void; 118586106d6SBartłomiej Klocek}; 119586106d6SBartłomiej Klocek 120586106d6SBartłomiej Klocekconst DocumentationSidebarRightLink = React.forwardRef<HTMLAnchorElement, SidebarLinkProps>( 121586106d6SBartłomiej Klocek ({ heading, isActive, shortenCode, onClick }, ref) => { 122c4c6b9d1SBartosz Kaszubowski const { slug, level, title, type, tags } = heading; 123586106d6SBartłomiej Klocek 124586106d6SBartłomiej Klocek const isCode = type === HeadingType.InlineCode; 125db57e342SBartosz Kaszubowski const paddingLeft = NESTING_OFFSET * (level - BASE_HEADING_LEVEL); 126586106d6SBartłomiej Klocek const displayTitle = shortenCode && isCode ? trimCodedTitle(title) : title; 127586106d6SBartłomiej Klocek 128586106d6SBartłomiej Klocek const [tooltipVisible, setTooltipVisible] = React.useState(false); 129586106d6SBartłomiej Klocek const [tooltipOffset, setTooltipOffset] = React.useState(-20); 130586106d6SBartłomiej Klocek const onMouseOver = (event: React.MouseEvent<HTMLAnchorElement>) => { 131586106d6SBartłomiej Klocek setTooltipVisible(isOverflowing(event.currentTarget)); 132c4c6b9d1SBartosz Kaszubowski setTooltipOffset( 133c4c6b9d1SBartosz Kaszubowski event.currentTarget.getBoundingClientRect().top + event.currentTarget.offsetHeight 134c4c6b9d1SBartosz Kaszubowski ); 135586106d6SBartłomiej Klocek }; 136c4c6b9d1SBartosz Kaszubowski 137586106d6SBartłomiej Klocek const onMouseOut = () => { 138586106d6SBartłomiej Klocek setTooltipVisible(false); 139586106d6SBartłomiej Klocek }; 140586106d6SBartłomiej Klocek 141db57e342SBartosz Kaszubowski const TitleElement = isCode ? MONOSPACE : CALLOUT; 142db57e342SBartosz Kaszubowski 143586106d6SBartłomiej Klocek return ( 144586106d6SBartłomiej Klocek <> 145586106d6SBartłomiej Klocek {tooltipVisible && ( 146586106d6SBartłomiej Klocek <Tooltip topOffset={tooltipOffset} isCode={isCode}> 147586106d6SBartłomiej Klocek {displayTitle} 148586106d6SBartłomiej Klocek </Tooltip> 149586106d6SBartłomiej Klocek )} 150*c730ceaaSBartosz Kaszubowski <Link 151586106d6SBartłomiej Klocek ref={ref} 152586106d6SBartłomiej Klocek onMouseOver={onMouseOver} 153586106d6SBartłomiej Klocek onMouseOut={onMouseOut} 154586106d6SBartłomiej Klocek href={'#' + slug} 155586106d6SBartłomiej Klocek onClick={onClick} 156db57e342SBartosz Kaszubowski css={[STYLES_LINK, paddingLeft && { paddingLeft }]}> 157db57e342SBartosz Kaszubowski <TitleElement 158db57e342SBartosz Kaszubowski css={[ 159db57e342SBartosz Kaszubowski STYLES_LINK_LABEL, 160db57e342SBartosz Kaszubowski isCode && STYLES_LINK_MONOSPACE, 161db57e342SBartosz Kaszubowski isActive && STYLES_LINK_ACTIVE, 162db57e342SBartosz Kaszubowski ]}> 163db57e342SBartosz Kaszubowski {displayTitle} 164db57e342SBartosz Kaszubowski </TitleElement> 165c4c6b9d1SBartosz Kaszubowski {tags && tags.length ? ( 166c4c6b9d1SBartosz Kaszubowski <div css={STYLES_TAG_CONTAINER}> 167c4c6b9d1SBartosz Kaszubowski {tags.map(tag => ( 16822ee2951SBartosz Kaszubowski <Tag name={tag} type="toc" key={`${displayTitle}-${tag}`} /> 169c4c6b9d1SBartosz Kaszubowski ))} 170c4c6b9d1SBartosz Kaszubowski </div> 171c4c6b9d1SBartosz Kaszubowski ) : undefined} 172*c730ceaaSBartosz Kaszubowski </Link> 173586106d6SBartłomiej Klocek </> 174586106d6SBartłomiej Klocek ); 175586106d6SBartłomiej Klocek } 176586106d6SBartłomiej Klocek); 177586106d6SBartłomiej Klocek 178586106d6SBartłomiej Klocekexport default DocumentationSidebarRightLink; 179