1import { css } from '@emotion/react'; 2import { borderRadius, breakpoints, shadows, spacing, theme, typography } from '@expo/styleguide'; 3import type { ComponentProps, ComponentType } from 'react'; 4import { Fragment } from 'react'; 5import ReactMarkdown from 'react-markdown'; 6import remarkGfm from 'remark-gfm'; 7 8import { APIDataType } from './APIDataType'; 9 10import { HeadingType } from '~/common/headingManager'; 11import { Code as PrismCodeBlock } from '~/components/base/code'; 12import { 13 CommentContentData, 14 CommentData, 15 MethodDefinitionData, 16 MethodParamData, 17 MethodSignatureData, 18 PropData, 19 TypeDefinitionData, 20 TypePropertyDataFlags, 21 TypeSignaturesData, 22} from '~/components/plugins/api/APIDataTypes'; 23import { APISectionPlatformTags } from '~/components/plugins/api/APISectionPlatformTags'; 24import { Callout } from '~/ui/components/Callout'; 25import { Cell, HeaderCell, Row, Table, TableHead } from '~/ui/components/Table'; 26import { tableWrapperStyle } from '~/ui/components/Table/Table'; 27import { Tag } from '~/ui/components/Tag'; 28import { 29 A, 30 BOLD, 31 CODE, 32 H4, 33 LI, 34 OL, 35 P, 36 RawH3, 37 RawH4, 38 UL, 39 createPermalinkedComponent, 40 DEMI, 41 CALLOUT, 42 createTextComponent, 43} from '~/ui/components/Text'; 44import { TextElement } from '~/ui/components/Text/types'; 45 46const isDev = process.env.NODE_ENV === 'development'; 47 48export enum TypeDocKind { 49 Namespace = 4, 50 Enum = 8, 51 Variable = 32, 52 Function = 64, 53 Class = 128, 54 Interface = 256, 55 Property = 1024, 56 Method = 2048, 57 Parameter = 32768, 58 Accessor = 262144, 59 TypeAlias = 4194304, 60} 61 62export type MDComponents = ComponentProps<typeof ReactMarkdown>['components']; 63 64const getInvalidLinkMessage = (href: string) => 65 `Using "../" when linking other packages in doc comments produce a broken link! Please use "./" instead. Problematic link:\n\t${href}`; 66 67export const mdComponents: MDComponents = { 68 blockquote: ({ children }) => <Callout>{children}</Callout>, 69 code: ({ children, className }) => 70 className ? ( 71 <PrismCodeBlock className={className}>{children}</PrismCodeBlock> 72 ) : ( 73 <CODE css={css({ display: 'inline' })}>{children}</CODE> 74 ), 75 h1: ({ children }) => <H4>{children}</H4>, 76 ul: ({ children }) => <UL css={STYLES_ELEMENT_SPACING}>{children}</UL>, 77 ol: ({ children }) => <OL css={STYLES_ELEMENT_SPACING}>{children}</OL>, 78 li: ({ children }) => <LI>{children}</LI>, 79 a: ({ href, children }) => { 80 if ( 81 href?.startsWith('../') && 82 !href?.startsWith('../..') && 83 !href?.startsWith('../react-native') 84 ) { 85 if (isDev) { 86 throw new Error(getInvalidLinkMessage(href)); 87 } else { 88 console.warn(getInvalidLinkMessage(href)); 89 } 90 } 91 return <A href={href}>{children}</A>; 92 }, 93 p: ({ children }) => (children ? <P css={STYLES_ELEMENT_SPACING}>{children}</P> : null), 94 strong: ({ children }) => <BOLD>{children}</BOLD>, 95 span: ({ children }) => (children ? <span>{children}</span> : null), 96 table: ({ children }) => <Table>{children}</Table>, 97 thead: ({ children }) => <TableHead>{children}</TableHead>, 98 tr: ({ children }) => <Row>{children}</Row>, 99 th: ({ children }) => <HeaderCell>{children}</HeaderCell>, 100 td: ({ children }) => <Cell>{children}</Cell>, 101}; 102 103export const mdComponentsNoValidation: MDComponents = { 104 ...mdComponents, 105 a: ({ href, children }) => <A href={href}>{children}</A>, 106}; 107 108const nonLinkableTypes = [ 109 'ColorValue', 110 'Component', 111 'ComponentClass', 112 'PureComponent', 113 'E', 114 'EventSubscription', 115 'Listener', 116 'NativeSyntheticEvent', 117 'ParsedQs', 118 'ServiceActionResult', 119 'T', 120 'TaskOptions', 121 'Uint8Array', 122 // React & React Native 123 'React.FC', 124 'ForwardRefExoticComponent', 125 'StyleProp', 126 'HTMLInputElement', 127 // Cross-package permissions management 128 'RequestPermissionMethod', 129 'GetPermissionMethod', 130 'Options', 131 'PermissionHookBehavior', 132]; 133 134/** 135 * List of type names that should not be visible in the docs. 136 */ 137const omittableTypes = [ 138 // Internal React type that adds `ref` prop to the component 139 'RefAttributes', 140]; 141 142/** 143 * Map of internal names/type names that should be replaced with something more developer-friendly. 144 */ 145const replaceableTypes: Partial<Record<string, string>> = { 146 ForwardRefExoticComponent: 'Component', 147}; 148 149const hardcodedTypeLinks: Record<string, string> = { 150 AVPlaybackSource: '/versions/latest/sdk/av/#avplaybacksource', 151 AVPlaybackStatus: '/versions/latest/sdk/av/#avplaybackstatus', 152 AVPlaybackStatusToSet: '/versions/latest/sdk/av/#avplaybackstatustoset', 153 Blob: 'https://developer.mozilla.org/en-US/docs/Web/API/Blob', 154 Date: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date', 155 DeviceSensor: '/versions/latest/sdk/sensors', 156 Element: 'https://www.typescriptlang.org/docs/handbook/jsx.html#function-component', 157 Error: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error', 158 ExpoConfig: 159 'https://github.com/expo/expo/blob/main/packages/%40expo/config-types/src/ExpoConfig.ts', 160 File: 'https://developer.mozilla.org/en-US/docs/Web/API/File', 161 FileList: 'https://developer.mozilla.org/en-US/docs/Web/API/FileList', 162 Manifest: '/versions/latest/sdk/constants/#manifest', 163 MediaTrackSettings: 'https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings', 164 MessageEvent: 'https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent', 165 Omit: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys', 166 Pick: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys', 167 Partial: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype', 168 Promise: 169 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise', 170 SyntheticEvent: 171 'https://beta.reactjs.org/reference/react-dom/components/common#react-event-object', 172 View: 'https://reactnative.dev/docs/view', 173 ViewProps: 'https://reactnative.dev/docs/view#props', 174 ViewStyle: 'https://reactnative.dev/docs/view-style-props', 175 WebGL2RenderingContext: 'https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext', 176 WebGLFramebuffer: 'https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer', 177}; 178 179const renderWithLink = (name: string, type?: string) => { 180 const replacedName = replaceableTypes[name] ?? name; 181 182 if (name.includes('.')) return name; 183 184 return nonLinkableTypes.includes(replacedName) ? ( 185 replacedName + (type === 'array' ? '[]' : '') 186 ) : ( 187 <A 188 href={hardcodedTypeLinks[replacedName] || `#${replacedName.toLowerCase()}`} 189 key={`type-link-${replacedName}`}> 190 {replacedName} 191 {type === 'array' && '[]'} 192 </A> 193 ); 194}; 195 196const renderUnion = (types: TypeDefinitionData[]) => 197 types 198 .map(type => resolveTypeName(type)) 199 .map((valueToRender, index) => ( 200 <span key={`union-type-${index}`}> 201 {valueToRender} 202 {index + 1 !== types.length && ' | '} 203 </span> 204 )); 205 206export const resolveTypeName = ( 207 typeDefinition: TypeDefinitionData 208): string | JSX.Element | (string | JSX.Element)[] => { 209 if (!typeDefinition) { 210 return 'undefined'; 211 } 212 213 const { 214 elements, 215 elementType, 216 name, 217 type, 218 types, 219 typeArguments, 220 declaration, 221 value, 222 queryType, 223 operator, 224 objectType, 225 indexType, 226 } = typeDefinition; 227 228 try { 229 if (name) { 230 if (type === 'reference') { 231 if (typeArguments) { 232 if (name === 'Record' || name === 'React.ComponentProps') { 233 return ( 234 <> 235 {name}< 236 {typeArguments.map((type, index) => ( 237 <span key={`record-type-${index}`}> 238 {resolveTypeName(type)} 239 {index !== typeArguments.length - 1 ? ', ' : null} 240 </span> 241 ))} 242 > 243 </> 244 ); 245 } else { 246 return ( 247 <> 248 {renderWithLink(name)} 249 < 250 {typeArguments.map((type, index) => ( 251 <span key={`${name}-nested-type-${index}`}> 252 {resolveTypeName(type)} 253 {index !== typeArguments.length - 1 ? ', ' : null} 254 </span> 255 ))} 256 > 257 </> 258 ); 259 } 260 } else { 261 return renderWithLink(name); 262 } 263 } else { 264 return name; 265 } 266 } else if (elementType?.name) { 267 if (elementType.type === 'reference') { 268 return renderWithLink(elementType.name, type); 269 } else if (type === 'array') { 270 return elementType.name + '[]'; 271 } 272 return elementType.name + type; 273 } else if (elementType?.declaration) { 274 if (type === 'array') { 275 const { parameters, type: paramType } = elementType.declaration.indexSignature || {}; 276 if (parameters && paramType) { 277 return `{ [${listParams(parameters)}]: ${resolveTypeName(paramType)} }`; 278 } 279 } 280 return elementType.name + type; 281 } else if (type === 'union' && types?.length) { 282 return renderUnion(types); 283 } else if (elementType && elementType.type === 'union' && elementType?.types?.length) { 284 const unionTypes = elementType?.types || []; 285 return ( 286 <> 287 ({renderUnion(unionTypes)}){type === 'array' && '[]'} 288 </> 289 ); 290 } else if (declaration?.signatures) { 291 const baseSignature = declaration.signatures[0]; 292 if (baseSignature?.parameters?.length) { 293 return ( 294 <> 295 ( 296 {baseSignature.parameters?.map((param, index) => ( 297 <span key={`param-${index}-${param.name}`}> 298 {param.name}: {resolveTypeName(param.type)} 299 {index + 1 !== baseSignature.parameters?.length && ', '} 300 </span> 301 ))} 302 ) {'=>'} {resolveTypeName(baseSignature.type)} 303 </> 304 ); 305 } else { 306 return ( 307 <> 308 {'() =>'} {resolveTypeName(baseSignature.type)} 309 </> 310 ); 311 } 312 } else if (type === 'reflection' && declaration?.children) { 313 return ( 314 <> 315 {'{\n'} 316 {declaration?.children.map((child: PropData, i) => ( 317 <span key={`reflection-${name}-${i}`}> 318 {' '} 319 {child.name + ': '} 320 {resolveTypeName(child.type)} 321 {i + 1 !== declaration?.children?.length ? ', ' : null} 322 {'\n'} 323 </span> 324 ))} 325 {'}'} 326 </> 327 ); 328 } else if (type === 'tuple' && elements) { 329 return ( 330 <> 331 [ 332 {elements.map((elem, i) => ( 333 <span key={`tuple-${name}-${i}`}> 334 {resolveTypeName(elem)} 335 {i + 1 !== elements.length ? ', ' : null} 336 </span> 337 ))} 338 ] 339 </> 340 ); 341 } else if (type === 'query' && queryType) { 342 return queryType.name; 343 } else if (type === 'literal' && typeof value === 'boolean') { 344 return `${value}`; 345 } else if (type === 'literal' && (value || (typeof value === 'number' && value === 0))) { 346 return `'${value}'`; 347 } else if (type === 'intersection' && types) { 348 return types 349 .filter(({ name }) => !omittableTypes.includes(name ?? '')) 350 .map((value, index, array) => ( 351 <span key={`intersection-${name}-${index}`}> 352 {resolveTypeName(value)} 353 {index + 1 !== array.length && ' & '} 354 </span> 355 )); 356 } else if (type === 'indexedAccess') { 357 return `${objectType?.name}['${indexType?.value}']`; 358 } else if (type === 'typeOperator') { 359 return operator || 'undefined'; 360 } else if (type === 'intrinsic') { 361 return name || 'undefined'; 362 } else if (value === null) { 363 return 'null'; 364 } 365 return 'undefined'; 366 } catch (e) { 367 console.warn('Type resolve has failed!', e); 368 return 'undefined'; 369 } 370}; 371 372export const parseParamName = (name: string) => (name.startsWith('__') ? name.substr(2) : name); 373 374export const renderParamRow = ({ 375 comment, 376 name, 377 type, 378 flags, 379 defaultValue, 380}: MethodParamData): JSX.Element => { 381 const defaultData = getTagData('default', comment); 382 const initValue = parseCommentContent( 383 defaultValue || (defaultData ? getCommentContent(defaultData.content) : '') 384 ); 385 return ( 386 <Row key={`param-${name}`}> 387 <Cell> 388 <BOLD>{parseParamName(name)}</BOLD> 389 {renderFlags(flags, initValue)} 390 </Cell> 391 <Cell> 392 <APIDataType typeDefinition={type} /> 393 </Cell> 394 <Cell> 395 <CommentTextBlock 396 comment={comment} 397 afterContent={renderDefaultValue(initValue)} 398 emptyCommentFallback="-" 399 /> 400 </Cell> 401 </Row> 402 ); 403}; 404 405export const ParamsTableHeadRow = () => ( 406 <TableHead> 407 <Row> 408 <HeaderCell>Name</HeaderCell> 409 <HeaderCell>Type</HeaderCell> 410 <HeaderCell>Description</HeaderCell> 411 </Row> 412 </TableHead> 413); 414 415const InheritPermalink = createPermalinkedComponent( 416 createTextComponent( 417 TextElement.SPAN, 418 css({ fontSize: 'inherit', fontWeight: 'inherit', color: 'inherit' }) 419 ), 420 { baseNestingLevel: 2 } 421); 422 423export const BoxSectionHeader = ({ 424 text, 425 exposeInSidebar, 426}: { 427 text: string; 428 exposeInSidebar?: boolean; 429}) => { 430 const TextWrapper = exposeInSidebar ? InheritPermalink : Fragment; 431 return ( 432 <CALLOUT theme="secondary" weight="medium" css={STYLES_NESTED_SECTION_HEADER}> 433 <TextWrapper>{text}</TextWrapper> 434 </CALLOUT> 435 ); 436}; 437 438export const renderParams = (parameters: MethodParamData[]) => ( 439 <Table> 440 <ParamsTableHeadRow /> 441 <tbody>{parameters?.map(renderParamRow)}</tbody> 442 </Table> 443); 444 445export const listParams = (parameters: MethodParamData[]) => 446 parameters ? parameters?.map(param => parseParamName(param.name)).join(', ') : ''; 447 448export const renderDefaultValue = (defaultValue?: string) => 449 defaultValue && defaultValue !== '...' ? ( 450 <div css={defaultValueContainerStyle}> 451 <DEMI>Default:</DEMI> <CODE>{defaultValue}</CODE> 452 </div> 453 ) : undefined; 454 455export const renderTypeOrSignatureType = ( 456 type?: TypeDefinitionData, 457 signatures?: MethodSignatureData[] | TypeSignaturesData[], 458 allowBlock: boolean = false 459) => { 460 if (signatures && signatures.length) { 461 return ( 462 <CODE key={`signature-type-${signatures[0].name}`}> 463 ( 464 {signatures?.map(({ parameters }) => 465 parameters?.map(param => ( 466 <span key={`signature-param-${param.name}`}> 467 {param.name} 468 {param.flags?.isOptional && '?'}: {resolveTypeName(param.type)} 469 </span> 470 )) 471 )} 472 ) => {signatures[0].type ? resolveTypeName(signatures[0].type) : 'void'} 473 </CODE> 474 ); 475 } else if (type) { 476 if (allowBlock) { 477 return <APIDataType typeDefinition={type} />; 478 } 479 return <CODE key={`signature-type-${type.name}`}>{resolveTypeName(type)}</CODE>; 480 } 481 return undefined; 482}; 483 484export const renderFlags = (flags?: TypePropertyDataFlags, defaultValue?: string) => 485 (flags?.isOptional || defaultValue) && ( 486 <> 487 <br /> 488 <span css={STYLES_OPTIONAL}>(optional)</span> 489 </> 490 ); 491 492export const renderIndexSignature = (kind: TypeDocKind) => 493 kind === TypeDocKind.Parameter && ( 494 <> 495 <br /> 496 <A 497 css={STYLES_OPTIONAL} 498 href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures" 499 openInNewTab 500 isStyled> 501 (index signature) 502 </A> 503 </> 504 ); 505 506export type CommentTextBlockProps = { 507 comment?: CommentData; 508 components?: MDComponents; 509 beforeContent?: JSX.Element; 510 afterContent?: JSX.Element; 511 includePlatforms?: boolean; 512 inlineHeaders?: boolean; 513 emptyCommentFallback?: string; 514}; 515 516export const parseCommentContent = (content?: string): string => 517 content && content.length ? content.replace(/*/g, '*').replace(/\t/g, '') : ''; 518 519export const getCommentOrSignatureComment = ( 520 comment?: CommentData, 521 signatures?: MethodSignatureData[] | TypeSignaturesData[] 522) => comment || (signatures && signatures[0]?.comment); 523 524export const getTagData = (tagName: string, comment?: CommentData) => 525 getAllTagData(tagName, comment)?.[0]; 526 527export const getAllTagData = (tagName: string, comment?: CommentData) => 528 comment?.blockTags?.filter(tag => tag.tag.substring(1) === tagName); 529 530export const getTagNamesList = (comment?: CommentData) => 531 comment && [ 532 ...(getAllTagData('platform', comment)?.map(platformData => 533 getCommentContent(platformData.content) 534 ) || []), 535 ...(getTagData('deprecated', comment) ? ['deprecated'] : []), 536 ...(getTagData('experimental', comment) ? ['experimental'] : []), 537 ]; 538 539export const getMethodName = ( 540 method: MethodDefinitionData, 541 apiName?: string, 542 name?: string, 543 parameters?: MethodParamData[] 544) => { 545 const isProperty = method.kind === TypeDocKind.Property && !parameters?.length; 546 const methodName = ((apiName && `${apiName}.`) ?? '') + (method.name || name); 547 if (!isProperty) { 548 return `${methodName}(${parameters ? listParams(parameters) : ''})`; 549 } 550 551 return methodName; 552}; 553 554export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); 555 556const PARAM_TAGS_REGEX = /@tag-\S*/g; 557 558const getParamTags = (shortText?: string) => { 559 if (!shortText || !shortText.includes('@tag-')) { 560 return undefined; 561 } 562 return Array.from(shortText.matchAll(PARAM_TAGS_REGEX), match => match[0]); 563}; 564 565export const getCommentContent = (content: CommentContentData[]) => { 566 return content 567 .map(entry => entry.text) 568 .join('') 569 .trim(); 570}; 571 572export const CommentTextBlock = ({ 573 comment, 574 beforeContent, 575 afterContent, 576 includePlatforms = true, 577 inlineHeaders = false, 578 emptyCommentFallback, 579}: CommentTextBlockProps) => { 580 const content = comment && comment.summary ? getCommentContent(comment.summary) : undefined; 581 582 if (emptyCommentFallback && (!comment || !content || !content.length)) { 583 return <>{emptyCommentFallback}</>; 584 } 585 586 const paramTags = content ? getParamTags(content) : undefined; 587 const parsedContent = ( 588 <ReactMarkdown components={mdComponents} remarkPlugins={[remarkGfm]}> 589 {parseCommentContent(paramTags ? content?.replaceAll(PARAM_TAGS_REGEX, '') : content)} 590 </ReactMarkdown> 591 ); 592 593 const examples = getAllTagData('example', comment); 594 const exampleText = examples?.map((example, index) => ( 595 <Fragment key={'example-' + index}> 596 {inlineHeaders ? ( 597 <div css={STYLES_EXAMPLE_IN_TABLE}> 598 <BOLD>Example</BOLD> 599 </div> 600 ) : ( 601 <BoxSectionHeader text="Example" /> 602 )} 603 <ReactMarkdown components={mdComponents}>{getCommentContent(example.content)}</ReactMarkdown> 604 </Fragment> 605 )); 606 607 const see = getTagData('see', comment); 608 const seeText = see && ( 609 <Callout> 610 <ReactMarkdown components={mdComponents}> 611 {`**See:** ` + getCommentContent(see.content)} 612 </ReactMarkdown> 613 </Callout> 614 ); 615 616 const hasPlatforms = (getAllTagData('platform', comment)?.length || 0) > 0; 617 618 return ( 619 <> 620 {includePlatforms && hasPlatforms && ( 621 <APISectionPlatformTags comment={comment} prefix="Only for:" /> 622 )} 623 {paramTags && ( 624 <> 625 <BOLD>Only for: </BOLD> 626 {paramTags.map(tag => ( 627 <Tag key={tag} name={tag.split('-')[1]} /> 628 ))} 629 </> 630 )} 631 {beforeContent} 632 {parsedContent} 633 {afterContent} 634 {seeText} 635 {exampleText} 636 </> 637 ); 638}; 639 640const getMonospaceHeader = (element: ComponentType<any>) => { 641 const level = parseInt(element?.displayName?.replace(/\D/g, '') ?? '0', 10); 642 return createPermalinkedComponent(element, { 643 baseNestingLevel: level !== 0 ? level : undefined, 644 sidebarType: HeadingType.InlineCode, 645 }); 646}; 647 648export const H3Code = getMonospaceHeader(RawH3); 649export const H4Code = getMonospaceHeader(RawH4); 650 651export const getComponentName = (name?: string, children: PropData[] = []) => { 652 if (name && name !== 'default') return name; 653 const ctor = children.filter((child: PropData) => child.name === 'constructor')[0]; 654 return ctor?.signatures?.[0]?.type?.name ?? 'default'; 655}; 656 657export const STYLES_APIBOX = css({ 658 borderRadius: borderRadius.md, 659 borderWidth: 1, 660 borderStyle: 'solid', 661 borderColor: theme.border.default, 662 padding: spacing[5], 663 boxShadow: shadows.xs, 664 marginBottom: spacing[6], 665 overflowX: 'hidden', 666 667 h3: { 668 marginBottom: spacing[2], 669 }, 670 671 'h2, h3, h4': { 672 marginTop: 0, 673 }, 674 675 th: { 676 color: theme.text.secondary, 677 padding: `${spacing[3]}px ${spacing[4]}px`, 678 }, 679 680 li: { 681 marginBottom: 0, 682 }, 683 684 [`.css-${tableWrapperStyle.name}`]: { 685 boxShadow: 'none', 686 marginBottom: 0, 687 }, 688 689 [`@media screen and (max-width: ${breakpoints.medium + 124}px)`]: { 690 paddingInline: spacing[4], 691 }, 692}); 693 694export const STYLES_APIBOX_NESTED = css({ 695 boxShadow: 'none', 696 marginBottom: spacing[4], 697 padding: `${spacing[4]}px ${spacing[5]}px 0`, 698 699 h4: { 700 marginTop: 0, 701 }, 702}); 703 704export const STYLES_APIBOX_WRAPPER = css({ 705 marginBottom: spacing[4], 706 padding: `${spacing[4]}px ${spacing[5]}px 0`, 707 708 [`.css-${tableWrapperStyle.name}:last-child`]: { 709 marginBottom: spacing[4], 710 }, 711}); 712 713export const STYLE_APIBOX_NO_SPACING = css({ marginBottom: -spacing[5] }); 714 715export const STYLES_NESTED_SECTION_HEADER = css({ 716 display: 'flex', 717 borderTop: `1px solid ${theme.border.default}`, 718 borderBottom: `1px solid ${theme.border.default}`, 719 margin: `${spacing[4]}px -${spacing[5]}px ${spacing[4]}px`, 720 padding: `${spacing[2.5]}px ${spacing[5]}px`, 721 backgroundColor: theme.background.subtle, 722 723 h4: { 724 ...typography.fontSizes[16], 725 fontWeight: 600, 726 marginBottom: 0, 727 marginTop: 0, 728 color: theme.text.secondary, 729 }, 730}); 731 732export const STYLES_NOT_EXPOSED_HEADER = css({ 733 marginBottom: spacing[1], 734 display: 'inline-block', 735 736 code: { 737 marginBottom: 0, 738 }, 739}); 740 741export const STYLES_OPTIONAL = css({ 742 color: theme.text.secondary, 743 fontSize: '90%', 744 paddingTop: 22, 745}); 746 747export const STYLES_SECONDARY = css({ 748 color: theme.text.secondary, 749 fontSize: '90%', 750 fontWeight: 600, 751}); 752 753const defaultValueContainerStyle = css({ 754 marginTop: spacing[2], 755 marginBottom: spacing[2], 756 757 '&:last-child': { 758 marginBottom: 0, 759 }, 760}); 761 762const STYLES_EXAMPLE_IN_TABLE = css({ 763 margin: `${spacing[2]}px 0`, 764}); 765 766export const STYLES_ELEMENT_SPACING = css({ 767 marginBottom: spacing[4], 768}); 769