import { css, CSSObject } from '@emotion/react'; import { typography } from '@expo/styleguide'; import type { CSSProperties, ComponentType, PropsWithChildren } from 'react'; import { Code as PrismCodeBlock } from '~/components/base/code'; import { Callout } from '~/ui/components/Callout'; import { Cell, HeaderCell, Row, Table, TableHead } from '~/ui/components/Table'; import { H1, H2, H3, H4, H5, A, CODE, P, BOLD, UL, OL, LI, KBD } from '~/ui/components/Text'; type Config = ConfigStyles & { Component: ComponentType> | string; }; type ConfigStyles = { css?: CSSObject; style?: CSSProperties; }; type ComponentProps = PropsWithChildren<{ className?: string; style?: CSSProperties; }>; const markdownStyles: Record = { h1: { Component: H1, }, h2: { Component: H2, }, h3: { Component: H3, }, h4: { Component: H4, }, h5: { Component: H5, }, p: { Component: P, style: { marginBottom: '1.5ch' }, }, strong: { Component: BOLD, }, ul: { Component: UL, style: { marginBottom: '1.5ch', paddingLeft: `1ch` }, }, ol: { Component: OL, style: { marginBottom: '1.5ch', paddingLeft: `1ch` }, }, li: { Component: LI, }, hr: { Component: 'hr', css: typography.utility.hr, style: { margin: `2ch 0` }, }, blockquote: { Component: Callout, }, img: { Component: 'img', style: { width: '100%' }, }, code: { Component: CODE, }, pre: { Component: PrismCodeBlock, }, a: { Component: A, }, table: { Component: Table, }, thead: { Component: TableHead, }, tr: { Component: Row, }, th: { Component: HeaderCell, }, td: { Component: Cell, }, kbd: { Component: KBD, }, }; type MarkdownComponent = Record; export const markdownComponents: MarkdownComponent = Object.keys(markdownStyles).reduce( (all, key) => ({ ...all, [key]: markdownStyles[key] ? createMarkdownComponent(markdownStyles[key]!) : null, }), {} ); function componentName({ Component }: Config) { if (typeof Component === 'string') return Component; return Component.displayName || Component.name || 'Anonymous'; } function createMarkdownComponent(config: Config): ComponentType { const { Component, css: cssClassname, style } = config; const MDXComponent = (props: ComponentProps) => ( {props.children} ); MDXComponent.displayName = `Markdown(${componentName(config)})`; return MDXComponent; }