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