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, MONOSPACE, 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 <MONOSPACE weight="medium" css={!exposeInSidebar && STYLES_NOT_EXPOSED_HEADER}> 123 {name} 124 </MONOSPACE> 125 </HeaderComponent> 126 <P css={extractedComment && STYLES_ELEMENT_SPACING}> 127 {flags?.isOptional && <span css={STYLES_SECONDARY}>Optional • </span>} 128 <span css={STYLES_SECONDARY}>Type:</span>{' '} 129 {renderTypeOrSignatureType(type, extractedSignatures)} 130 {defaultValue && defaultValue !== UNKNOWN_VALUE ? ( 131 <span> 132 <span css={STYLES_SECONDARY}> • Default:</span>{' '} 133 <CODE>{defaultValue}</CODE> 134 </span> 135 ) : null} 136 </P> 137 <CommentTextBlock comment={extractedComment} includePlatforms={false} /> 138 <br /> 139 </div> 140 ); 141}; 142 143const APISectionProps = ({ data, defaultProps, header = 'Props' }: APISectionPropsProps) => { 144 const baseProp = data.find(prop => prop.name === header); 145 return data?.length ? ( 146 <> 147 {data?.length === 1 || header === 'Props' ? ( 148 <H2 key="props-header">{header}</H2> 149 ) : ( 150 <div> 151 {baseProp && <APISectionDeprecationNote comment={baseProp.comment} />} 152 <div css={STYLES_NESTED_SECTION_HEADER}> 153 <H4 key={`${header}-props-header`}>{header}</H4> 154 </div> 155 {baseProp && baseProp.comment ? <CommentTextBlock comment={baseProp.comment} /> : null} 156 </div> 157 )} 158 {data.map((propsDefinition: PropsDefinitionData) => 159 renderProps(propsDefinition, defaultProps, data?.length === 1 || header === 'Props') 160 )} 161 </> 162 ) : null; 163}; 164 165export default APISectionProps; 166