1import { css } from '@emotion/react'; 2import { theme, spacing, UndoIcon, iconSize } from '@expo/styleguide'; 3import ReactMarkdown from 'react-markdown'; 4 5import { H2, H3Code, H4, H4Code } from '~/components/plugins/Headings'; 6import { APIDataType } from '~/components/plugins/api/APIDataType'; 7import { 8 AccessorDefinitionData, 9 MethodDefinitionData, 10 MethodSignatureData, 11 PropData, 12 TypeSignaturesData, 13} from '~/components/plugins/api/APIDataTypes'; 14import { APISectionDeprecationNote } from '~/components/plugins/api/APISectionDeprecationNote'; 15import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags'; 16import { 17 CommentTextBlock, 18 getMethodName, 19 getTagNamesList, 20 mdComponents, 21 renderParams, 22 resolveTypeName, 23 STYLES_APIBOX, 24 STYLES_APIBOX_NESTED, 25 STYLES_NESTED_SECTION_HEADER, 26 STYLES_NOT_EXPOSED_HEADER, 27 TypeDocKind, 28} from '~/components/plugins/api/APISectionUtils'; 29import { LI, UL, CODE } from '~/ui/components/Text'; 30 31export type APISectionMethodsProps = { 32 data: (MethodDefinitionData | PropData)[]; 33 apiName?: string; 34 header?: string; 35 exposeInSidebar?: boolean; 36}; 37 38export type RenderMethodOptions = { 39 apiName?: string; 40 header?: string; 41 exposeInSidebar?: boolean; 42}; 43 44export const renderMethod = ( 45 method: MethodDefinitionData | AccessorDefinitionData | PropData, 46 { apiName, exposeInSidebar = true }: RenderMethodOptions = {} 47): JSX.Element[] => { 48 const signatures = 49 (method as MethodDefinitionData).signatures || 50 (method as PropData)?.type?.declaration?.signatures || 51 (method as AccessorDefinitionData)?.getSignature || 52 []; 53 const HeaderComponent = exposeInSidebar ? H3Code : H4Code; 54 return signatures.map( 55 ({ name, parameters, comment, type }: MethodSignatureData | TypeSignaturesData) => ( 56 <div 57 key={`method-signature-${method.name || name}-${parameters?.length || 0}`} 58 css={[STYLES_APIBOX, STYLES_APIBOX_NESTED]}> 59 <APISectionDeprecationNote comment={comment} /> 60 <APISectionPlatformTags comment={comment} prefix="Only for:" /> 61 <HeaderComponent tags={getTagNamesList(comment)}> 62 <CODE css={!exposeInSidebar ? STYLES_NOT_EXPOSED_HEADER : undefined}> 63 {getMethodName(method as MethodDefinitionData, apiName, name, parameters)} 64 </CODE> 65 </HeaderComponent> 66 {parameters && parameters.length > 0 && ( 67 <> 68 {renderParams(parameters)} 69 <br /> 70 </> 71 )} 72 <CommentTextBlock comment={comment} includePlatforms={false} /> 73 {resolveTypeName(type) !== 'undefined' && ( 74 <> 75 <div css={STYLES_NESTED_SECTION_HEADER}> 76 <H4>Returns</H4> 77 </div> 78 <UL css={STYLES_NO_BULLET_LIST}> 79 <LI> 80 <UndoIcon 81 color={theme.icon.secondary} 82 size={iconSize.small} 83 css={returnIconStyles} 84 /> 85 <APIDataType typeDefinition={type} /> 86 </LI> 87 </UL> 88 <> 89 <br /> 90 <ReactMarkdown components={mdComponents}>{comment?.returns ?? ''}</ReactMarkdown> 91 </> 92 </> 93 )} 94 </div> 95 ) 96 ); 97}; 98 99const APISectionMethods = ({ 100 data, 101 apiName, 102 header = 'Methods', 103 exposeInSidebar = true, 104}: APISectionMethodsProps) => 105 data?.length ? ( 106 <> 107 <H2 key="methods-header">{header}</H2> 108 {data.map((method: MethodDefinitionData | PropData) => 109 renderMethod(method, { apiName, header, exposeInSidebar }) 110 )} 111 </> 112 ) : null; 113 114const returnIconStyles = css({ 115 transform: 'rotate(180deg)', 116 marginRight: spacing[2], 117 verticalAlign: 'middle', 118}); 119 120export default APISectionMethods; 121 122export const APIMethod = ({ 123 name, 124 comment, 125 returnTypeName, 126 isProperty = false, 127 isReturnTypeReference = false, 128 exposeInSidebar = false, 129 parameters = [], 130 platforms = [], 131}: { 132 exposeInSidebar?: boolean; 133 name: string; 134 comment: string; 135 returnTypeName: string; 136 isProperty: boolean; 137 isReturnTypeReference: boolean; 138 platforms: ('Android' | 'iOS' | 'Web')[]; 139 parameters: { 140 name: string; 141 comment?: string; 142 typeName: string; 143 isReference?: boolean; 144 }[]; 145}): JSX.Element[] => { 146 const parsedParameters = parameters.map(param => ({ 147 name: param.name, 148 type: { name: param.typeName, type: param.isReference ? 'reference' : 'literal' }, 149 comment: { 150 text: param.comment, 151 }, 152 })); 153 return renderMethod( 154 { 155 name, 156 signatures: [ 157 { 158 name, 159 parameters: parsedParameters, 160 comment: { 161 text: comment, 162 tags: platforms.map(text => ({ 163 tag: 'platform', 164 text, 165 })), 166 }, 167 type: { name: returnTypeName, type: isReturnTypeReference ? 'reference' : 'literal' }, 168 }, 169 ], 170 kind: isProperty ? TypeDocKind.Property : TypeDocKind.Function, 171 }, 172 { exposeInSidebar } 173 ); 174}; 175 176const STYLES_NO_BULLET_LIST = css({ 177 listStyle: 'none', 178 marginLeft: spacing[2], 179}); 180