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  H3Code,
15  getTagData,
16  getTagNamesList,
17  mdComponents,
18  resolveTypeName,
19  STYLES_APIBOX,
20  STYLES_APIBOX_NESTED,
21  TypeDocKind,
22  getCommentContent,
23  BoxSectionHeader,
24} from '~/components/plugins/api/APISectionUtils';
25import { H2, 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
69  const properties = children?.filter(isProp);
70  const methods = children
71    ?.filter(child => isMethod(child, isSensor))
72    .sort((a: PropData, b: PropData) => a.name.localeCompare(b.name));
73  const returnComment = getTagData('returns', comment);
74
75  return (
76    <div key={`class-definition-${name}`} css={[STYLES_APIBOX, STYLES_APIBOX_NESTED]}>
77      <APISectionDeprecationNote comment={comment} />
78      <APISectionPlatformTags comment={comment} prefix="Only for:" />
79      <H3Code tags={getTagNamesList(comment)}>
80        <CODE>{name}</CODE>
81      </H3Code>
82      {(extendedTypes?.length || implementedTypes?.length) && (
83        <P>
84          <BOLD>Type: </BOLD>
85          {type ? <CODE>{resolveTypeName(type)}</CODE> : 'Class'}
86          {extendedTypes?.length && (
87            <>
88              <span> extends </span>
89              {extendedTypes.map(extendedType => (
90                <CODE key={`extends-${extendedType.name}`}>{resolveTypeName(extendedType)}</CODE>
91              ))}
92            </>
93          )}
94          {implementedTypes?.length && (
95            <>
96              <span> implements </span>
97              {implementedTypes.map(implementedType => (
98                <CODE key={`implements-${implementedType.name}`}>
99                  {resolveTypeName(implementedType)}
100                </CODE>
101              ))}
102            </>
103          )}
104        </P>
105      )}
106      <CommentTextBlock comment={comment} includePlatforms={false} />
107      {returnComment && (
108        <>
109          <BoxSectionHeader text="Returns" />
110          <ReactMarkdown components={mdComponents}>
111            {getCommentContent(returnComment.content)}
112          </ReactMarkdown>
113        </>
114      )}
115      {properties?.length ? (
116        <>
117          <BoxSectionHeader text={`${name} Properties`} exposeInSidebar={exposeInSidebar} />
118          <div>
119            {properties.map(property =>
120              renderProp(property, property?.defaultValue, exposeInSidebar)
121            )}
122          </div>
123        </>
124      ) : null}
125      {methods?.length && (
126        <>
127          <BoxSectionHeader text={`${name} Methods`} exposeInSidebar={exposeInSidebar} />
128          {methods.map(method => renderMethod(method, { exposeInSidebar }))}
129        </>
130      )}
131    </div>
132  );
133};
134
135const APISectionClasses = ({ data }: APISectionClassesProps) => {
136  if (data?.length) {
137    const exposeInSidebar = data.length < 2;
138    return (
139      <>
140        <H2>Classes</H2>
141        {data.map(clx => renderClass(remapClass(clx), exposeInSidebar))}
142      </>
143    );
144  }
145  return null;
146};
147
148export default APISectionClasses;
149