import { isUrl, getCurrentPath } from '~/utils/url' import { isEmail } from '~/utils/validate' import { isValidElement } from 'react' import { IVTableCellValue, IVTableCellValueObject } from '../IVTable/useTable' import { imageSizeToPx } from '~/utils/number' import { JSONPrimitive } from '@interval/sdk/dist/ioSchema' import { dateTimeFormatter } from '~/utils/formatters' import RenderMarkdown, { ALLOWED_INLINE_ELEMENTS } from '../RenderMarkdown' import Skeleton from 'react-loading-skeleton' import Truncate from '../Truncate' import { useIsomorphicLink } from '~/utils/useIsomorphicLocation' export type RenderableValue = JSONPrimitive | Date | bigint | undefined export function valueToString(value: RenderableValue): string { if (typeof value === 'number') { return value.toLocaleString() } if (typeof value === 'bigint' || value instanceof BigInt) { return `${value.toString()}n` } if (value instanceof Date) { return dateTimeFormatter.format(value) } if (typeof value === 'string') { const date = new Date(value) if (date.toJSON() === value) { return dateTimeFormatter.format(date) } // JSON.stringify wraps strings in quotes return value } return JSON.stringify(value) } function RenderImage({ image, children, }: { image: NonNullable children?: React.ReactNode }) { const width = image.size ?? image.width return (
{image.alt} {children &&
{children}
}
) } function MarkdownWrapper({ value, allowedElements, }: { value: string allowedElements?: string[] }) { return (
) } const INLINE_ELEMENTS_EXCEPT_LINKS = ALLOWED_INLINE_ELEMENTS.filter( el => el !== 'a' ) function MaybeTruncate({ children, truncate, }: { children: React.ReactNode truncate?: boolean }) { if (truncate) { return {children} } return <>{children} } export default function RenderValue({ value, renderMarkdown = false, showSkeleton = false, shouldTruncate = false, }: { value: IVTableCellValue | RenderableValue renderMarkdown?: boolean showSkeleton?: boolean shouldTruncate?: boolean }) { const Link = useIsomorphicLink() if (isValidElement(value)) { return {value} } let data = value as IVTableCellValueObject | RenderableValue // flatten label-only objects if ( typeof data === 'object' && data && 'label' in data && Object.keys(data).length === 1 ) { data = data.label } // handle plain links if (typeof data === 'string' && (isUrl(data) || isEmail(data))) { const href = isEmail(data) ? `mailto:${data}` : data return ( e.stopPropagation()} > ) } if ( data instanceof Date || typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || typeof data === 'bigint' ) { const stringLabel = valueToString(data) // tables render a dash for empty cells; don't render markdown for those if (renderMarkdown && stringLabel !== '-') { return ( ) } return <>{stringLabel} } // handle image and URL objects if (data && typeof data === 'object') { const stringLabel = valueToString(data.label) let image: IVTableCellValueObject['image'] if (data.image && typeof data.image === 'object') { image = data['image'] } if (data.url) { return data.isInternalActionUrl ? ( e.stopPropagation()} > {image ? ( {stringLabel} ) : ( )} ) : ( e.stopPropagation()} > {image ? ( {stringLabel} ) : ( )} ) } if ('label' in data) { const label = data.label === undefined && showSkeleton ? ( ) : renderMarkdown ? ( ) : ( <>{stringLabel} ) return image ? {label} : label } else if (image) { return } } if ( showSkeleton && data === undefined && value && typeof value === 'object' && 'label' in value ) { return } if (!data || (typeof data === 'object' && Object.keys(data).length === 0)) { return <>  } return ( {JSON.stringify(data, null, 2)} ) }