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