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 LocationAccuracy: 'Accuracy', 149 LocationGeofencingRegionState: 'GeofencingRegionState', 150 LocationActivityType: 'ActivityType', 151}; 152 153const hardcodedTypeLinks: Record<string, string> = { 154 AVPlaybackSource: '/versions/latest/sdk/av/#avplaybacksource', 155 AVPlaybackStatus: '/versions/latest/sdk/av/#avplaybackstatus', 156 AVPlaybackStatusToSet: '/versions/latest/sdk/av/#avplaybackstatustoset', 157 Blob: 'https://developer.mozilla.org/en-US/docs/Web/API/Blob', 158 Date: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date', 159 DeviceSensor: '/versions/latest/sdk/sensors', 160 Element: 'https://www.typescriptlang.org/docs/handbook/jsx.html#function-component', 161 Error: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error', 162 ExpoConfig: 163 'https://github.com/expo/expo/blob/main/packages/%40expo/config-types/src/ExpoConfig.ts', 164 File: 'https://developer.mozilla.org/en-US/docs/Web/API/File', 165 FileList: 'https://developer.mozilla.org/en-US/docs/Web/API/FileList', 166 Manifest: '/versions/latest/sdk/constants/#manifest', 167 MediaTrackSettings: 'https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings', 168 MessageEvent: 'https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent', 169 Omit: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys', 170 Pick: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys', 171 Partial: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype', 172 Promise: 173 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise', 174 SyntheticEvent: 'https://react.dev/reference/react-dom/components/common#react-event-object', 175 View: 'https://reactnative.dev/docs/view', 176 ViewProps: 'https://reactnative.dev/docs/view#props', 177 ViewStyle: 'https://reactnative.dev/docs/view-style-props', 178 WebGL2RenderingContext: 'https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext', 179 WebGLFramebuffer: 'https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer', 180}; 181 182const renderWithLink = (name: string, type?: string) => { 183 const replacedName = replaceableTypes[name] ?? name; 184 185 if (name.includes('.')) return name; 186 187 return nonLinkableTypes.includes(replacedName) ? ( 188 replacedName + (type === 'array' ? '[]' : '') 189 ) : ( 190 <A 191 href={hardcodedTypeLinks[replacedName] || `#${replacedName.toLowerCase()}`} 192 key={`type-link-${replacedName}`}> 193 {replacedName} 194 {type === 'array' && '[]'} 195 </A> 196 ); 197}; 198 199const renderUnion = (types: TypeDefinitionData[]) => 200 types 201 .map(type => resolveTypeName(type)) 202 .map((valueToRender, index) => ( 203 <span key={`union-type-${index}`}> 204 {valueToRender} 205 {index + 1 !== types.length && ' | '} 206 </span> 207 )); 208 209export const resolveTypeName = ( 210 typeDefinition: TypeDefinitionData 211): string | JSX.Element | (string | JSX.Element)[] => { 212 if (!typeDefinition) { 213 return 'undefined'; 214 } 215 216 const { 217 elements, 218 elementType, 219 name, 220 type, 221 types, 222 typeArguments, 223 declaration, 224 value, 225 queryType, 226 operator, 227 objectType, 228 indexType, 229 } = typeDefinition; 230 231 try { 232 if (name) { 233 if (type === 'reference') { 234 if (typeArguments) { 235 if (name === 'Record' || name === 'React.ComponentProps') { 236 return ( 237 <> 238 {name}< 239 {typeArguments.map((type, index) => ( 240 <span key={`record-type-${index}`}> 241 {resolveTypeName(type)} 242 {index !== typeArguments.length - 1 ? ', ' : null} 243 </span> 244 ))} 245 > 246 </> 247 ); 248 } else { 249 return ( 250 <> 251 {renderWithLink(name)} 252 < 253 {typeArguments.map((type, index) => ( 254 <span key={`${name}-nested-type-${index}`}> 255 {resolveTypeName(type)} 256 {index !== typeArguments.length - 1 ? ', ' : null} 257 </span> 258 ))} 259 > 260 </> 261 ); 262 } 263 } else { 264 return renderWithLink(name); 265 } 266 } else { 267 return name; 268 } 269 } else if (elementType?.name) { 270 if (elementType.type === 'reference') { 271 return renderWithLink(elementType.name, type); 272 } else if (type === 'array') { 273 return elementType.name + '[]'; 274 } 275 return elementType.name + type; 276 } else if (elementType?.declaration) { 277 if (type === 'array') { 278 const { parameters, type: paramType } = elementType.declaration.indexSignature || {}; 279 if (parameters && paramType) { 280 return `{ [${listParams(parameters)}]: ${resolveTypeName(paramType)} }`; 281 } 282 } 283 return elementType.name + type; 284 } else if (type === 'union' && types?.length) { 285 return renderUnion(types); 286 } else if (elementType && elementType.type === 'union' && elementType?.types?.length) { 287 const unionTypes = elementType?.types || []; 288 return ( 289 <> 290 ({renderUnion(unionTypes)}){type === 'array' && '[]'} 291 </> 292 ); 293 } else if (declaration?.signatures) { 294 const baseSignature = declaration.signatures[0]; 295 if (baseSignature?.parameters?.length) { 296 return ( 297 <> 298 ( 299 {baseSignature.parameters?.map((param, index) => ( 300 <span key={`param-${index}-${param.name}`}> 301 {param.name}: {resolveTypeName(param.type)} 302 {index + 1 !== baseSignature.parameters?.length && ', '} 303 </span> 304 ))} 305 ) {'=>'} {resolveTypeName(baseSignature.type)} 306 </> 307 ); 308 } else { 309 return ( 310 <> 311 {'() =>'} {resolveTypeName(baseSignature.type)} 312 </> 313 ); 314 } 315 } else if (type === 'reflection' && declaration?.children) { 316 return ( 317 <> 318 {'{\n'} 319 {declaration?.children.map((child: PropData, i) => ( 320 <span key={`reflection-${name}-${i}`}> 321 {' '} 322 {child.name + ': '} 323 {resolveTypeName(child.type)} 324 {i + 1 !== declaration?.children?.length ? ', ' : null} 325 {'\n'} 326 </span> 327 ))} 328 {'}'} 329 </> 330 ); 331 } else if (type === 'tuple' && elements) { 332 return ( 333 <> 334 [ 335 {elements.map((elem, i) => ( 336 <span key={`tuple-${name}-${i}`}> 337 {resolveTypeName(elem)} 338 {i + 1 !== elements.length ? ', ' : null} 339 </span> 340 ))} 341 ] 342 </> 343 ); 344 } else if (type === 'query' && queryType) { 345 return queryType.name; 346 } else if (type === 'literal' && typeof value === 'boolean') { 347 return `${value}`; 348 } else if (type === 'literal' && (value || (typeof value === 'number' && value === 0))) { 349 return `'${value}'`; 350 } else if (type === 'intersection' && types) { 351 return types 352 .filter(({ name }) => !omittableTypes.includes(name ?? '')) 353 .map((value, index, array) => ( 354 <span key={`intersection-${name}-${index}`}> 355 {resolveTypeName(value)} 356 {index + 1 !== array.length && ' & '} 357 </span> 358 )); 359 } else if (type === 'indexedAccess') { 360 return `${objectType?.name}['${indexType?.value}']`; 361 } else if (type === 'typeOperator') { 362 return operator || 'undefined'; 363 } else if (type === 'intrinsic') { 364 return name || 'undefined'; 365 } else if (value === null) { 366 return 'null'; 367 } 368 return 'undefined'; 369 } catch (e) { 370 console.warn('Type resolve has failed!', e); 371 return 'undefined'; 372 } 373}; 374 375export const parseParamName = (name: string) => (name.startsWith('__') ? name.substr(2) : name); 376 377export const renderParamRow = ({ 378 comment, 379 name, 380 type, 381 flags, 382 defaultValue, 383}: MethodParamData): JSX.Element => { 384 const defaultData = getTagData('default', comment); 385 const initValue = parseCommentContent( 386 defaultValue || (defaultData ? getCommentContent(defaultData.content) : '') 387 ); 388 return ( 389 <Row key={`param-${name}`}> 390 <Cell> 391 <BOLD>{parseParamName(name)}</BOLD> 392 {renderFlags(flags, initValue)} 393 </Cell> 394 <Cell> 395 <APIDataType typeDefinition={type} /> 396 </Cell> 397 <Cell> 398 <CommentTextBlock 399 comment={comment} 400 afterContent={renderDefaultValue(initValue)} 401 emptyCommentFallback="-" 402 /> 403 </Cell> 404 </Row> 405 ); 406}; 407 408export const ParamsTableHeadRow = () => ( 409 <TableHead> 410 <Row> 411 <HeaderCell>Name</HeaderCell> 412 <HeaderCell>Type</HeaderCell> 413 <HeaderCell>Description</HeaderCell> 414 </Row> 415 </TableHead> 416); 417 418const InheritPermalink = createPermalinkedComponent( 419 createTextComponent( 420 TextElement.SPAN, 421 css({ fontSize: 'inherit', fontWeight: 'inherit', color: 'inherit' }) 422 ), 423 { baseNestingLevel: 2 } 424); 425 426export const BoxSectionHeader = ({ 427 text, 428 exposeInSidebar, 429}: { 430 text: string; 431 exposeInSidebar?: boolean; 432}) => { 433 const TextWrapper = exposeInSidebar ? InheritPermalink : Fragment; 434 return ( 435 <CALLOUT theme="secondary" weight="medium" css={STYLES_NESTED_SECTION_HEADER}> 436 <TextWrapper>{text}</TextWrapper> 437 </CALLOUT> 438 ); 439}; 440 441export const renderParams = (parameters: MethodParamData[]) => ( 442 <Table> 443 <ParamsTableHeadRow /> 444 <tbody>{parameters?.map(renderParamRow)}</tbody> 445 </Table> 446); 447 448export const listParams = (parameters: MethodParamData[]) => 449 parameters ? parameters?.map(param => parseParamName(param.name)).join(', ') : ''; 450 451export const renderDefaultValue = (defaultValue?: string) => 452 defaultValue && defaultValue !== '...' ? ( 453 <div css={defaultValueContainerStyle}> 454 <DEMI>Default:</DEMI> <CODE>{defaultValue}</CODE> 455 </div> 456 ) : undefined; 457 458export const renderTypeOrSignatureType = ( 459 type?: TypeDefinitionData, 460 signatures?: MethodSignatureData[] | TypeSignaturesData[], 461 allowBlock: boolean = false 462) => { 463 if (signatures && signatures.length) { 464 return ( 465 <CODE key={`signature-type-${signatures[0].name}`}> 466 ( 467 {signatures?.map(({ parameters }) => 468 parameters?.map(param => ( 469 <span key={`signature-param-${param.name}`}> 470 {param.name} 471 {param.flags?.isOptional && '?'}: {resolveTypeName(param.type)} 472 </span> 473 )) 474 )} 475 ) => {signatures[0].type ? resolveTypeName(signatures[0].type) : 'void'} 476 </CODE> 477 ); 478 } else if (type) { 479 if (allowBlock) { 480 return <APIDataType typeDefinition={type} />; 481 } 482 return <CODE key={`signature-type-${type.name}`}>{resolveTypeName(type)}</CODE>; 483 } 484 return undefined; 485}; 486 487export const renderFlags = (flags?: TypePropertyDataFlags, defaultValue?: string) => 488 (flags?.isOptional || defaultValue) && ( 489 <> 490 <br /> 491 <span css={STYLES_OPTIONAL}>(optional)</span> 492 </> 493 ); 494 495export const renderIndexSignature = (kind: TypeDocKind) => 496 kind === TypeDocKind.Parameter && ( 497 <> 498 <br /> 499 <A 500 css={STYLES_OPTIONAL} 501 href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures" 502 openInNewTab 503 isStyled> 504 (index signature) 505 </A> 506 </> 507 ); 508 509export type CommentTextBlockProps = { 510 comment?: CommentData; 511 components?: MDComponents; 512 beforeContent?: JSX.Element; 513 afterContent?: JSX.Element; 514 includePlatforms?: boolean; 515 inlineHeaders?: boolean; 516 emptyCommentFallback?: string; 517}; 518 519export const parseCommentContent = (content?: string): string => 520 content && content.length ? content.replace(/*/g, '*').replace(/\t/g, '') : ''; 521 522export const getCommentOrSignatureComment = ( 523 comment?: CommentData, 524 signatures?: MethodSignatureData[] | TypeSignaturesData[] 525) => comment || (signatures && signatures[0]?.comment); 526 527export const getTagData = (tagName: string, comment?: CommentData) => 528 getAllTagData(tagName, comment)?.[0]; 529 530export const getAllTagData = (tagName: string, comment?: CommentData) => 531 comment?.blockTags?.filter(tag => tag.tag.substring(1) === tagName); 532 533export const getTagNamesList = (comment?: CommentData) => 534 comment && [ 535 ...(getAllTagData('platform', comment)?.map(platformData => 536 getCommentContent(platformData.content) 537 ) || []), 538 ...(getTagData('deprecated', comment) ? ['deprecated'] : []), 539 ...(getTagData('experimental', comment) ? ['experimental'] : []), 540 ]; 541 542export const getMethodName = ( 543 method: MethodDefinitionData, 544 apiName?: string, 545 name?: string, 546 parameters?: MethodParamData[] 547) => { 548 const isProperty = method.kind === TypeDocKind.Property && !parameters?.length; 549 const methodName = ((apiName && `${apiName}.`) ?? '') + (method.name || name); 550 if (!isProperty) { 551 return `${methodName}(${parameters ? listParams(parameters) : ''})`; 552 } 553 554 return methodName; 555}; 556 557export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); 558 559const PARAM_TAGS_REGEX = /@tag-\S*/g; 560 561const getParamTags = (shortText?: string) => { 562 if (!shortText || !shortText.includes('@tag-')) { 563 return undefined; 564 } 565 return Array.from(shortText.matchAll(PARAM_TAGS_REGEX), match => match[0]); 566}; 567 568export const getCommentContent = (content: CommentContentData[]) => { 569 return content 570 .map(entry => entry.text) 571 .join('') 572 .trim(); 573}; 574 575export const CommentTextBlock = ({ 576 comment, 577 beforeContent, 578 afterContent, 579 includePlatforms = true, 580 inlineHeaders = false, 581 emptyCommentFallback, 582}: CommentTextBlockProps) => { 583 const content = comment && comment.summary ? getCommentContent(comment.summary) : undefined; 584 585 if (emptyCommentFallback && (!comment || !content || !content.length)) { 586 return <>{emptyCommentFallback}</>; 587 } 588 589 const paramTags = content ? getParamTags(content) : undefined; 590 const parsedContent = ( 591 <ReactMarkdown components={mdComponents} remarkPlugins={[remarkGfm]}> 592 {parseCommentContent(paramTags ? content?.replaceAll(PARAM_TAGS_REGEX, '') : content)} 593 </ReactMarkdown> 594 ); 595 596 const examples = getAllTagData('example', comment); 597 const exampleText = examples?.map((example, index) => ( 598 <Fragment key={'example-' + index}> 599 {inlineHeaders ? ( 600 <div css={STYLES_EXAMPLE_IN_TABLE}> 601 <BOLD>Example</BOLD> 602 </div> 603 ) : ( 604 <BoxSectionHeader text="Example" /> 605 )} 606 <ReactMarkdown components={mdComponents}>{getCommentContent(example.content)}</ReactMarkdown> 607 </Fragment> 608 )); 609 610 const see = getTagData('see', comment); 611 const seeText = see && ( 612 <Callout> 613 <ReactMarkdown components={mdComponents}> 614 {`**See:** ` + getCommentContent(see.content)} 615 </ReactMarkdown> 616 </Callout> 617 ); 618 619 const hasPlatforms = (getAllTagData('platform', comment)?.length || 0) > 0; 620 621 return ( 622 <> 623 {includePlatforms && hasPlatforms && ( 624 <APISectionPlatformTags comment={comment} prefix="Only for:" /> 625 )} 626 {paramTags && ( 627 <> 628 <BOLD>Only for: </BOLD> 629 {paramTags.map(tag => ( 630 <Tag key={tag} name={tag.split('-')[1]} /> 631 ))} 632 </> 633 )} 634 {beforeContent} 635 {parsedContent} 636 {afterContent} 637 {seeText} 638 {exampleText} 639 </> 640 ); 641}; 642 643const getMonospaceHeader = (element: ComponentType<any>) => { 644 const level = parseInt(element?.displayName?.replace(/\D/g, '') ?? '0', 10); 645 return createPermalinkedComponent(element, { 646 baseNestingLevel: level !== 0 ? level : undefined, 647 sidebarType: HeadingType.InlineCode, 648 }); 649}; 650 651export const H3Code = getMonospaceHeader(RawH3); 652export const H4Code = getMonospaceHeader(RawH4); 653 654export const getComponentName = (name?: string, children: PropData[] = []) => { 655 if (name && name !== 'default') return name; 656 const ctor = children.filter((child: PropData) => child.name === 'constructor')[0]; 657 return ctor?.signatures?.[0]?.type?.name ?? 'default'; 658}; 659 660export const STYLES_APIBOX = css({ 661 borderRadius: borderRadius.md, 662 borderWidth: 1, 663 borderStyle: 'solid', 664 borderColor: theme.border.default, 665 padding: spacing[5], 666 boxShadow: shadows.xs, 667 marginBottom: spacing[6], 668 overflowX: 'hidden', 669 670 h3: { 671 marginBottom: spacing[2.5], 672 }, 673 674 'h2, h3, h4': { 675 marginTop: 0, 676 }, 677 678 th: { 679 color: theme.text.secondary, 680 padding: `${spacing[3]}px ${spacing[4]}px`, 681 }, 682 683 li: { 684 marginBottom: 0, 685 }, 686 687 [`.css-${tableWrapperStyle.name}`]: { 688 boxShadow: 'none', 689 marginBottom: 0, 690 }, 691 692 [`@media screen and (max-width: ${breakpoints.medium + 124}px)`]: { 693 paddingInline: spacing[4], 694 }, 695}); 696 697export const STYLES_APIBOX_NESTED = css({ 698 boxShadow: 'none', 699 marginBottom: spacing[4], 700 padding: `${spacing[4]}px ${spacing[5]}px 0`, 701 702 h4: { 703 marginTop: 0, 704 }, 705}); 706 707export const STYLES_APIBOX_WRAPPER = css({ 708 marginBottom: spacing[4], 709 padding: `${spacing[4]}px ${spacing[5]}px 0`, 710 711 [`.css-${tableWrapperStyle.name}:last-child`]: { 712 marginBottom: spacing[4], 713 }, 714}); 715 716export const STYLE_APIBOX_NO_SPACING = css({ marginBottom: -spacing[5] }); 717 718export const STYLES_NESTED_SECTION_HEADER = css({ 719 display: 'flex', 720 borderTop: `1px solid ${theme.border.default}`, 721 borderBottom: `1px solid ${theme.border.default}`, 722 margin: `${spacing[4]}px -${spacing[5]}px ${spacing[4]}px`, 723 padding: `${spacing[2.5]}px ${spacing[5]}px`, 724 backgroundColor: theme.background.subtle, 725 726 h4: { 727 ...typography.fontSizes[16], 728 fontWeight: 600, 729 marginBottom: 0, 730 marginTop: 0, 731 color: theme.text.secondary, 732 }, 733}); 734 735export const STYLES_NOT_EXPOSED_HEADER = css({ 736 marginBottom: spacing[1], 737 display: 'inline-block', 738 739 code: { 740 marginBottom: 0, 741 }, 742}); 743 744export const STYLES_OPTIONAL = css({ 745 color: theme.text.secondary, 746 fontSize: '90%', 747 paddingTop: 22, 748}); 749 750export const STYLES_SECONDARY = css({ 751 color: theme.text.secondary, 752 fontSize: '90%', 753 fontWeight: 600, 754}); 755 756const defaultValueContainerStyle = css({ 757 marginTop: spacing[2], 758 marginBottom: spacing[2], 759 760 '&:last-child': { 761 marginBottom: 0, 762 }, 763}); 764 765const STYLES_EXAMPLE_IN_TABLE = css({ 766 margin: `${spacing[2]}px 0`, 767}); 768 769export const ELEMENT_SPACING = 'mb-4'; 770