1import ReactMarkdown from 'react-markdown';
2
3import {
4  ClassDefinitionData,
5  GeneratedData,
6  PropData,
7} from '~/components/plugins/api/APIDataTypes';
8import { APISectionDeprecationNote } from '~/components/plugins/api/APISectionDeprecationNote';
9import { renderMethod } from '~/components/plugins/api/APISectionMethods';
10import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags';
11import { renderProp } from '~/components/plugins/api/APISectionProps';
12import {
13  CommentTextBlock,
14  getAPISectionHeader,
15  H3Code,
16  getTagData,
17  getTagNamesList,
18  mdComponents,
19  resolveTypeName,
20  STYLES_APIBOX,
21  STYLES_APIBOX_NESTED,
22  STYLES_NESTED_SECTION_HEADER,
23  TypeDocKind,
24} from '~/components/plugins/api/APISectionUtils';
25import { H2, H4, BOLD, P, CODE } from '~/ui/components/Text';
26
27export type APISectionClassesProps = {
28  data: GeneratedData[];
29};
30
31const classNamesMap: Record<string, string> = {
32  AccelerometerSensor: 'Accelerometer',
33  BarometerSensor: 'Barometer',
34  DeviceMotionSensor: 'DeviceMotion',
35  GyroscopeSensor: 'Gyroscope',
36  MagnetometerSensor: 'Magnetometer',
37} as const;
38
39const isProp = (child: PropData) =>
40  child.kind === TypeDocKind.Property &&
41  !child.overwrites &&
42  !child.name.startsWith('_') &&
43  !child.implementationOf;
44
45const isMethod = (child: PropData, allowOverwrites: boolean = false) =>
46  child.kind &&
47  [TypeDocKind.Method, TypeDocKind.Function, TypeDocKind.Accessor].includes(child.kind) &&
48  (allowOverwrites || !child.overwrites) &&
49  !child.name.startsWith('_') &&
50  !child?.implementationOf;
51
52const remapClass = (clx: ClassDefinitionData) => {
53  clx.isSensor = !!classNamesMap[clx.name] || Object.values(classNamesMap).includes(clx.name);
54  clx.name = classNamesMap[clx.name] ?? clx.name;
55
56  if (clx.isSensor && clx.extendedTypes) {
57    clx.extendedTypes = clx.extendedTypes.map(type => ({
58      ...type,
59      name: type.name === 'default' ? 'DeviceSensor' : type.name,
60    }));
61  }
62
63  return clx;
64};
65
66const renderClass = (clx: ClassDefinitionData, exposeInSidebar: boolean): JSX.Element => {
67  const { name, comment, type, extendedTypes, children, implementedTypes, isSensor } = clx;
68  const Header = getAPISectionHeader(exposeInSidebar);
69
70  const properties = children?.filter(isProp);
71  const methods = children
72    ?.filter(child => isMethod(child, isSensor))
73    .sort((a: PropData, b: PropData) => a.name.localeCompare(b.name));
74  const returnComment = getTagData('returns', comment);
75
76  return (
77    <div key={`class-definition-${name}`} css={[STYLES_APIBOX, STYLES_APIBOX_NESTED]}>
78      <APISectionDeprecationNote comment={comment} />
79      <APISectionPlatformTags comment={comment} prefix="Only for:" />
80      <H3Code tags={getTagNamesList(comment)}>
81        <CODE>{name}</CODE>
82      </H3Code>
83      {(extendedTypes?.length || implementedTypes?.length) && (
84        <P>
85          <BOLD>Type: </BOLD>
86          {type ? <CODE>{resolveTypeName(type)}</CODE> : 'Class'}
87          {extendedTypes?.length && (
88            <>
89              <span> extends </span>
90              {extendedTypes.map(extendedType => (
91                <CODE key={`extends-${extendedType.name}`}>{resolveTypeName(extendedType)}</CODE>
92              ))}
93            </>
94          )}
95          {implementedTypes?.length && (
96            <>
97              <span> implements </span>
98              {implementedTypes.map(implementedType => (
99                <CODE key={`implements-${implementedType.name}`}>
100                  {resolveTypeName(implementedType)}
101                </CODE>
102              ))}
103            </>
104          )}
105        </P>
106      )}
107      <CommentTextBlock comment={comment} includePlatforms={false} />
108      {returnComment && (
109        <>
110          <div css={STYLES_NESTED_SECTION_HEADER}>
111            <H4>Returns</H4>
112          </div>
113          <ReactMarkdown components={mdComponents}>{returnComment.text}</ReactMarkdown>
114        </>
115      )}
116      {properties?.length ? (
117        <>
118          <div css={STYLES_NESTED_SECTION_HEADER}>
119            <Header>{name} Properties</Header>
120          </div>
121          <div>
122            {properties.map(property =>
123              renderProp(property, property?.defaultValue, exposeInSidebar)
124            )}
125          </div>
126        </>
127      ) : null}
128      {methods?.length && (
129        <>
130          <div css={STYLES_NESTED_SECTION_HEADER}>
131            <Header>{name} Methods</Header>
132          </div>
133          {methods.map(method => renderMethod(method, { exposeInSidebar }))}
134        </>
135      )}
136    </div>
137  );
138};
139
140const APISectionClasses = ({ data }: APISectionClassesProps) => {
141  if (data?.length) {
142    const exposeInSidebar = data.length < 2;
143    return (
144      <>
145        <H2>Classes</H2>
146        {data.map(clx => renderClass(remapClass(clx), exposeInSidebar))}
147      </>
148    );
149  }
150  return null;
151};
152
153export default APISectionClasses;
154