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