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