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