xref: /expo/docs/components/Permalink.tsx (revision eac5feda)
1import { css } from '@emotion/core';
2import * as React from 'react';
3
4import { AdditionalProps } from '~/common/headingManager';
5import PermalinkIcon from '~/components/icons/Permalink';
6import withHeadingManager from '~/components/page-higher-order/withHeadingManager';
7
8type BaseProps = {
9  component: any;
10  className?: string;
11};
12
13type EnhancedProps = {
14  children: React.ReactNode;
15  nestingLevel?: number;
16  additionalProps?: AdditionalProps;
17  customIconStyle?: React.CSSProperties;
18  id?: string;
19};
20
21class Permalink extends React.Component<BaseProps> {
22  render() {
23    const { component, className, children, ...rest } = this.props;
24    return React.cloneElement(
25      component,
26      {
27        className: [className, component.props.className || ''].join(' '),
28        ...rest,
29      },
30      children
31    );
32  }
33}
34
35const STYLES_CONTAINER = css`
36  position: relative;
37
38  .anchor-icon {
39    cursor: pointer;
40    position: absolute;
41    top: 8px;
42    width: 20px;
43    height: 20px;
44    visibility: hidden;
45  }
46
47  :hover {
48    .anchor-icon {
49      visibility: visible;
50    }
51  }
52`;
53
54const STYLES_CONTAINER_ANCHOR = css`
55  position: absolute;
56  top: 0;
57  left: -24px;
58  width: 20px;
59  height: 20px;
60`;
61
62const STYLES_CONTAINER_TARGET = css`
63  display: block;
64  position: absolute;
65  top: -100px;
66  visibility: hidden;
67`;
68
69/**
70 * Props:
71 * - children: Title or component containing title text
72 * - nestingLevel: Sidebar heading level override
73 * - additionalProps: Additional properties passed to component
74 */
75export default withHeadingManager<EnhancedProps>(props => {
76  // NOTE(jim): Not the greatest way to generate permalinks.
77  // for now I've shortened the length of permalinks.
78  const component = props.children as JSX.Element;
79  const children = component.props.children || '';
80
81  let permalinkKey = props.id;
82
83  const heading = props.headingManager.addHeading(
84    children,
85    props.nestingLevel,
86    props.additionalProps
87  );
88
89  if (!permalinkKey) {
90    permalinkKey = heading.slug;
91  }
92
93  return (
94    <Permalink component={component} data-components-heading>
95      <div css={STYLES_CONTAINER} ref={heading.ref}>
96        <span id={permalinkKey} css={STYLES_CONTAINER_TARGET} />
97        <a
98          style={props.customIconStyle}
99          href={'#' + permalinkKey}
100          className="permalink"
101          css={STYLES_CONTAINER_ANCHOR}>
102          <PermalinkIcon />
103        </a>
104        <div className="permalink-child">{children}</div>
105      </div>
106    </Permalink>
107  );
108});
109