1import { css } from '@emotion/react';
2import { theme, spacing, UndoIcon, iconSize } from '@expo/styleguide';
3import React from 'react';
4import ReactMarkdown from 'react-markdown';
5
6import { InlineCode } from '~/components/base/code';
7import { LI, UL } from '~/components/base/list';
8import { H2, H3Code, H4, H4Code } from '~/components/plugins/Headings';
9import {
10  MethodDefinitionData,
11  MethodSignatureData,
12  PropData,
13  TypeSignaturesData,
14} from '~/components/plugins/api/APIDataTypes';
15import { APISectionDeprecationNote } from '~/components/plugins/api/APISectionDeprecationNote';
16import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags';
17import {
18  CommentTextBlock,
19  getTagNamesList,
20  listParams,
21  mdComponents,
22  renderParams,
23  resolveTypeName,
24  STYLES_APIBOX,
25  STYLES_APIBOX_NESTED,
26  STYLES_NESTED_SECTION_HEADER,
27  STYLES_NOT_EXPOSED_HEADER,
28} from '~/components/plugins/api/APISectionUtils';
29
30export type APISectionMethodsProps = {
31  data: (MethodDefinitionData | PropData)[];
32  apiName?: string;
33  header?: string;
34};
35
36export type RenderMethodOptions = {
37  apiName?: string;
38  header?: string;
39  exposeInSidebar?: boolean;
40};
41
42export const renderMethod = (
43  method: MethodDefinitionData | PropData,
44  { apiName, exposeInSidebar = true }: RenderMethodOptions = {}
45): JSX.Element[] => {
46  const signatures = method.signatures || (method as PropData)?.type?.declaration?.signatures || [];
47  const HeaderComponent = exposeInSidebar ? H3Code : H4Code;
48  return signatures.map(
49    ({ name, parameters, comment, type }: MethodSignatureData | TypeSignaturesData) => (
50      <div
51        key={`method-signature-${method.name || name}-${parameters?.length || 0}`}
52        css={[STYLES_APIBOX, !exposeInSidebar && STYLES_APIBOX_NESTED]}>
53        <APISectionDeprecationNote comment={comment} />
54        <APISectionPlatformTags comment={comment} prefix="Only for:" firstElement />
55        <HeaderComponent tags={getTagNamesList(comment)}>
56          <InlineCode css={!exposeInSidebar ? STYLES_NOT_EXPOSED_HEADER : undefined}>
57            {apiName && `${apiName}.`}
58            {`${method.name || name}(${parameters ? listParams(parameters) : ''})`}
59          </InlineCode>
60        </HeaderComponent>
61        {parameters && renderParams(parameters)}
62        <CommentTextBlock comment={comment} includePlatforms={false} />
63        {resolveTypeName(type) !== 'undefined' && (
64          <>
65            <div css={STYLES_NESTED_SECTION_HEADER}>
66              <H4>Returns</H4>
67            </div>
68            <UL hideBullets>
69              <LI>
70                <UndoIcon
71                  color={theme.icon.secondary}
72                  size={iconSize.small}
73                  css={returnIconStyles}
74                />
75                <InlineCode>{resolveTypeName(type)}</InlineCode>
76              </LI>
77            </UL>
78            {comment?.returns && (
79              <ReactMarkdown components={mdComponents}>{comment.returns}</ReactMarkdown>
80            )}
81          </>
82        )}
83      </div>
84    )
85  );
86};
87
88const APISectionMethods = ({ data, apiName, header = 'Methods' }: APISectionMethodsProps) =>
89  data?.length ? (
90    <>
91      <H2 key="methods-header">{header}</H2>
92      {data.map((method: MethodDefinitionData | PropData) =>
93        renderMethod(method, { apiName, header })
94      )}
95    </>
96  ) : null;
97
98const returnIconStyles = css({
99  transform: 'rotate(180deg)',
100  marginRight: spacing[2],
101  verticalAlign: 'middle',
102});
103
104export default APISectionMethods;
105