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