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