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 { APIDataType } from '~/components/plugins/api/APIDataType';
10import {
11  MethodDefinitionData,
12  MethodSignatureData,
13  PropData,
14  TypeSignaturesData,
15} from '~/components/plugins/api/APIDataTypes';
16import { APISectionDeprecationNote } from '~/components/plugins/api/APISectionDeprecationNote';
17import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags';
18import {
19  CommentTextBlock,
20  getMethodName,
21  getTagNamesList,
22  mdComponents,
23  renderParams,
24  resolveTypeName,
25  STYLES_APIBOX,
26  STYLES_APIBOX_NESTED,
27  STYLES_NESTED_SECTION_HEADER,
28  STYLES_NOT_EXPOSED_HEADER,
29  TypeDocKind,
30} from '~/components/plugins/api/APISectionUtils';
31
32export type APISectionMethodsProps = {
33  data: (MethodDefinitionData | PropData)[];
34  apiName?: string;
35  header?: string;
36  exposeInSidebar?: boolean;
37};
38
39export type RenderMethodOptions = {
40  apiName?: string;
41  header?: string;
42  exposeInSidebar?: boolean;
43};
44
45export const renderMethod = (
46  method: MethodDefinitionData | PropData,
47  { apiName, exposeInSidebar = true }: RenderMethodOptions = {}
48): JSX.Element[] => {
49  const signatures = method.signatures || (method as PropData)?.type?.declaration?.signatures || [];
50  const HeaderComponent = exposeInSidebar ? H3Code : H4Code;
51  return signatures.map(
52    ({ name, parameters, comment, type }: MethodSignatureData | TypeSignaturesData) => (
53      <div
54        key={`method-signature-${method.name || name}-${parameters?.length || 0}`}
55        css={[STYLES_APIBOX, !exposeInSidebar && STYLES_APIBOX_NESTED]}>
56        <APISectionDeprecationNote comment={comment} />
57        <APISectionPlatformTags comment={comment} prefix="Only for:" />
58        <HeaderComponent tags={getTagNamesList(comment)}>
59          <InlineCode css={!exposeInSidebar ? STYLES_NOT_EXPOSED_HEADER : undefined}>
60            {getMethodName(method as MethodDefinitionData, apiName, name, parameters)}
61          </InlineCode>
62        </HeaderComponent>
63        {parameters && parameters.length > 0 && renderParams(parameters)}
64        <CommentTextBlock comment={comment} includePlatforms={false} />
65        {resolveTypeName(type) !== 'undefined' && (
66          <>
67            <div css={STYLES_NESTED_SECTION_HEADER}>
68              <H4>Returns</H4>
69            </div>
70            <UL hideBullets>
71              <LI>
72                <UndoIcon
73                  color={theme.icon.secondary}
74                  size={iconSize.small}
75                  css={returnIconStyles}
76                />
77                <APIDataType typeDefinition={type} />
78              </LI>
79            </UL>
80            {comment?.returns && (
81              <ReactMarkdown components={mdComponents}>{comment.returns}</ReactMarkdown>
82            )}
83          </>
84        )}
85      </div>
86    )
87  );
88};
89
90const APISectionMethods = ({
91  data,
92  apiName,
93  header = 'Methods',
94  exposeInSidebar = true,
95}: APISectionMethodsProps) =>
96  data?.length ? (
97    <>
98      <H2 key="methods-header">{header}</H2>
99      {data.map((method: MethodDefinitionData | PropData) =>
100        renderMethod(method, { apiName, header, exposeInSidebar })
101      )}
102    </>
103  ) : null;
104
105const returnIconStyles = css({
106  transform: 'rotate(180deg)',
107  marginRight: spacing[2],
108  verticalAlign: 'middle',
109});
110
111export default APISectionMethods;
112
113export const APIMethod = ({
114  name,
115  comment,
116  returnTypeName,
117  isProperty = false,
118  isReturnTypeReference = false,
119  exposeInSidebar = false,
120  parameters = [],
121}: {
122  exposeInSidebar?: boolean;
123  name: string;
124  comment: string;
125  returnTypeName: string;
126  isProperty: boolean;
127  isReturnTypeReference: boolean;
128  parameters: {
129    name: string;
130    comment?: string;
131    typeName: string;
132    isReference?: boolean;
133  }[];
134}): JSX.Element[] => {
135  const parsedParameters = parameters.map(param => ({
136    name: param.name,
137    type: { name: param.typeName, type: param.isReference ? 'reference' : 'literal' },
138    comment: {
139      text: param.comment,
140    },
141  }));
142  return renderMethod(
143    {
144      name,
145      signatures: [
146        {
147          name,
148          parameters: parsedParameters,
149          comment: {
150            text: comment,
151          },
152          type: { name: returnTypeName, type: isReturnTypeReference ? 'reference' : 'literal' },
153        },
154      ],
155      kind: isProperty ? TypeDocKind.Property : TypeDocKind.Function,
156    },
157    { exposeInSidebar }
158  );
159};
160