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