1import { 2 DefaultPropsDefinitionData, 3 PropData, 4 PropsDefinitionData, 5 TypeDefinitionData, 6} from '~/components/plugins/api/APIDataTypes'; 7import { APISectionDeprecationNote } from '~/components/plugins/api/APISectionDeprecationNote'; 8import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags'; 9import { 10 CommentTextBlock, 11 getCommentContent, 12 getCommentOrSignatureComment, 13 getTagData, 14 getTagNamesList, 15 H3Code, 16 H4Code, 17 renderTypeOrSignatureType, 18 resolveTypeName, 19 STYLES_APIBOX, 20 STYLES_APIBOX_NESTED, 21 STYLES_ELEMENT_SPACING, 22 STYLES_NESTED_SECTION_HEADER, 23 STYLES_NOT_EXPOSED_HEADER, 24 STYLES_SECONDARY, 25 TypeDocKind, 26} from '~/components/plugins/api/APISectionUtils'; 27import { CODE, H2, H3, H4, LI, P, UL } from '~/ui/components/Text'; 28 29export type APISectionPropsProps = { 30 data: PropsDefinitionData[]; 31 defaultProps?: DefaultPropsDefinitionData; 32 header?: string; 33}; 34 35const UNKNOWN_VALUE = '...'; 36 37const extractDefaultPropValue = ( 38 { comment, name }: PropData, 39 defaultProps?: DefaultPropsDefinitionData 40): string | undefined => { 41 const annotationDefault = getTagData('default', comment); 42 if (annotationDefault) { 43 return getCommentContent(annotationDefault.content); 44 } 45 return defaultProps?.type?.declaration?.children?.filter( 46 (defaultProp: PropData) => defaultProp.name === name 47 )[0]?.defaultValue; 48}; 49 50const renderInheritedProp = (ip: TypeDefinitionData) => { 51 return ( 52 <LI key={`inherited-prop-${ip.name}-${ip.type}`}> 53 <CODE>{resolveTypeName(ip)}</CODE> 54 </LI> 55 ); 56}; 57 58const renderInheritedProps = ( 59 data: PropsDefinitionData | undefined, 60 exposeInSidebar?: boolean 61): JSX.Element | undefined => { 62 const inheritedData = data?.type?.types ?? data?.extendedTypes ?? []; 63 const inheritedProps = 64 inheritedData.filter((ip: TypeDefinitionData) => ip.type === 'reference') ?? []; 65 if (inheritedProps.length) { 66 return ( 67 <> 68 {exposeInSidebar ? <H3>Inherited Props</H3> : <H4>Inherited Props</H4>} 69 <UL>{inheritedProps.map(renderInheritedProp)}</UL> 70 </> 71 ); 72 } 73 return undefined; 74}; 75 76const getPropsBaseTypes = (def: PropsDefinitionData) => { 77 if (def.kind === TypeDocKind.TypeAlias) { 78 const baseTypes = def?.type?.types 79 ? def.type.types?.filter((t: TypeDefinitionData) => t.declaration) 80 : [def.type]; 81 return baseTypes.map(def => def?.declaration?.children); 82 } else if (def.kind === TypeDocKind.Interface) { 83 return def.children?.filter(child => !child.inheritedFrom) ?? []; 84 } 85 return []; 86}; 87 88const renderProps = ( 89 def: PropsDefinitionData, 90 defaultValues?: DefaultPropsDefinitionData, 91 exposeInSidebar?: boolean 92): JSX.Element => { 93 const propsDeclarations = getPropsBaseTypes(def) 94 .flat() 95 .filter((dec, i, arr) => arr.findIndex(t => t?.name === dec?.name) === i); 96 97 return ( 98 <div key={`props-definition-${def.name}`}> 99 {propsDeclarations?.map(prop => 100 prop 101 ? renderProp(prop, extractDefaultPropValue(prop, defaultValues), exposeInSidebar) 102 : null 103 )} 104 {renderInheritedProps(def, exposeInSidebar)} 105 </div> 106 ); 107}; 108 109export const renderProp = ( 110 { comment, name, type, flags, signatures }: PropData, 111 defaultValue?: string, 112 exposeInSidebar?: boolean 113) => { 114 const HeaderComponent = exposeInSidebar ? H3Code : H4Code; 115 const extractedSignatures = signatures || type?.declaration?.signatures; 116 const extractedComment = getCommentOrSignatureComment(comment, extractedSignatures); 117 return ( 118 <div key={`prop-entry-${name}`} css={[STYLES_APIBOX, STYLES_APIBOX_NESTED]}> 119 <APISectionDeprecationNote comment={extractedComment} /> 120 <APISectionPlatformTags comment={comment} prefix="Only for:" /> 121 <HeaderComponent tags={getTagNamesList(comment)}> 122 <CODE css={!exposeInSidebar ? STYLES_NOT_EXPOSED_HEADER : undefined}>{name}</CODE> 123 </HeaderComponent> 124 <P css={extractedComment && STYLES_ELEMENT_SPACING}> 125 {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional • </span>} 126 <span css={STYLES_SECONDARY}>Type:</span>{' '} 127 {renderTypeOrSignatureType(type, extractedSignatures)} 128 {defaultValue && defaultValue !== UNKNOWN_VALUE ? ( 129 <span> 130 <span css={STYLES_SECONDARY}> • Default:</span>{' '} 131 <CODE>{defaultValue}</CODE> 132 </span> 133 ) : null} 134 </P> 135 <CommentTextBlock comment={extractedComment} includePlatforms={false} /> 136 </div> 137 ); 138}; 139 140const APISectionProps = ({ data, defaultProps, header = 'Props' }: APISectionPropsProps) => { 141 const baseProp = data.find(prop => prop.name === header); 142 return data?.length ? ( 143 <> 144 {data?.length === 1 || header === 'Props' ? ( 145 <H2 key="props-header">{header}</H2> 146 ) : ( 147 <div> 148 {baseProp && <APISectionDeprecationNote comment={baseProp.comment} />} 149 <div css={STYLES_NESTED_SECTION_HEADER}> 150 <H4 key={`${header}-props-header`}>{header}</H4> 151 </div> 152 {baseProp && baseProp.comment ? <CommentTextBlock comment={baseProp.comment} /> : null} 153 </div> 154 )} 155 {data.map((propsDefinition: PropsDefinitionData) => 156 renderProps(propsDefinition, defaultProps, data?.length === 1 || header === 'Props') 157 )} 158 </> 159 ) : null; 160}; 161 162export default APISectionProps; 163