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