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