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