import React, { useMemo } from 'react'; import type { TypeCastMap } from 'hadron-type-checker'; import { Binary } from 'bson'; import type { DBRef } from 'bson'; import { variantColors } from '@leafygreen-ui/code'; import { Icon, Link } from './leafygreen'; import { spacing } from '@leafygreen-ui/tokens'; import { css, cx } from '@leafygreen-ui/emotion'; import { Theme, useDarkMode } from '../hooks/use-theme'; type ValueProps = | { [type in keyof TypeCastMap]: { type: type; value: TypeCastMap[type] }; }[keyof TypeCastMap] | { type: 'DBRef'; value: DBRef }; function truncate(str: string, length = 70): string { const truncated = str.slice(0, length); return length < str.length ? `${truncated}…` : str; } type ValueTypes = ValueProps['type']; type PropsByValueType = Omit< Extract, 'type' >; const VALUE_COLOR_BY_THEME_AND_TYPE: Record< Theme, Partial> > = { [Theme.Dark]: { Int32: variantColors.dark[9], Double: variantColors.dark[9], Decimal128: variantColors.dark[9], Date: variantColors.dark[9], Boolean: variantColors.dark[10], String: variantColors.dark[7], ObjectId: variantColors.dark[5], }, [Theme.Light]: { Int32: variantColors.light[9], Double: variantColors.light[9], Decimal128: variantColors.light[9], Date: variantColors.light[9], Boolean: variantColors.light[10], String: variantColors.light[7], ObjectId: variantColors.light[5], }, }; const bsonValue = css({ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', }); const bsonValuePrewrap = css({ whiteSpace: 'pre-wrap', }); export const BSONValueContainer: React.FunctionComponent< React.HTMLProps & { type?: ValueTypes; chidren?: React.ReactChildren; } > = ({ type, children, className, ...props }) => { const darkMode = useDarkMode(); const color = useMemo(() => { if (!type) { return; } return VALUE_COLOR_BY_THEME_AND_TYPE[darkMode ? Theme.Dark : Theme.Light][ type ]; }, [type, darkMode]); return (
{children}
); }; const nonSelectable = css({ userSelect: 'none', }); const encryptedHelpLinkStyle = css({ color: 'inherit', marginLeft: spacing[1], }); const ObjectIdValue: React.FunctionComponent> = ({ value, }) => { const stringifiedValue = useMemo(() => { return String(value); }, [value]); return ( ObjectId(' {stringifiedValue} ') ); }; const BinaryValue: React.FunctionComponent> = ({ value, }) => { const { stringifiedValue, title, additionalHints } = useMemo(() => { if (value.sub_type === Binary.SUBTYPE_ENCRYPTED) { return { stringifiedValue: '*********', title: 'Encrypted', additionalHints: ( ), }; } if (value.sub_type === Binary.SUBTYPE_UUID) { let uuid: string; try { // Try to get the pretty hex version of the UUID uuid = value.toUUID().toString(); } catch { // If uuid is not following the uuid format converting it to UUID will // fail, we don't want the UI to fail rendering it and instead will // just display "unformatted" hex value of the binary whatever it is uuid = value.toString('hex'); } return { stringifiedValue: `UUID('${uuid}')` }; } return { stringifiedValue: `Binary.createFromBase64('${truncate( value.toString('base64'), 100 )}', ${value.sub_type})`, }; }, [value]); return ( {stringifiedValue} {additionalHints} ); }; const CodeValue: React.FunctionComponent> = ({ value, }) => { const stringifiedValue = useMemo(() => { return `Code('${String(value.code)}'${ value.scope ? `, ${JSON.stringify(value.scope)}` : '' })`; }, [value.code, value.scope]); return ( {stringifiedValue} ); }; const DateValue: React.FunctionComponent> = ({ value, }) => { const stringifiedValue = useMemo(() => { try { return new Date(value).toISOString().replace('Z', '+00:00'); } catch { return String(value); } }, [value]); return ( {stringifiedValue} ); }; const NumberValue: React.FunctionComponent< PropsByValueType<'Int32' | 'Double'> & { type: 'Int32' | 'Double' } > = ({ type, value }) => { const stringifiedValue = useMemo(() => { return String(value.valueOf()); }, [value]); return ( {stringifiedValue} ); }; const StringValue: React.FunctionComponent> = ({ value, }) => { const truncatedValue = useMemo(() => { return truncate(value, 70); }, [value]); return ( "{truncatedValue}" ); }; const RegExpValue: React.FunctionComponent> = ({ value, }) => { const stringifiedValue = useMemo(() => { return `/${value.pattern}/${value.options}`; }, [value.pattern, value.options]); return ( {stringifiedValue} ); }; const TimestampValue: React.FunctionComponent< PropsByValueType<'Timestamp'> > = ({ value }) => { const stringifiedValue = useMemo(() => { return `Timestamp({ t: ${value.getHighBits()}, i: ${value.getLowBits()} })`; }, [value]); return ( {stringifiedValue} ); }; const KeyValue: React.FunctionComponent<{ type: 'MinKey' | 'MaxKey'; }> = ({ type }) => { const stringifiedValue = useMemo(() => { return `${type}()`; }, [type]); return ( {stringifiedValue} ); }; const DBRefValue: React.FunctionComponent> = ({ value, }) => { const stringifiedValue = useMemo(() => { return `DBRef('${value.collection}', '${String(value.oid)}'${ value?.db ? `, '${value.db}'` : '' })`; }, [value.collection, value.oid, value.db]); return ( {stringifiedValue} ); }; const SymbolValue: React.FunctionComponent> = ({ value, }) => { const stringifiedValue = useMemo(() => { return `Symbol('${String(value)}')`; }, [value]); return ( {stringifiedValue} ); }; const UnknownValue: React.FunctionComponent<{ type: string; value: unknown; }> = ({ value }) => { const stringifiedValue = useMemo(() => { return String(value); }, [value]); return ( {stringifiedValue} ); }; const ArrayValue: React.FunctionComponent> = ({ value, }) => { const lengthString = useMemo(() => { return `(${value.length > 0 ? value.length : 'empty'})`; }, [value.length]); return ( Array {lengthString} ); }; const BSONValue: React.FunctionComponent = (props) => { switch (props.type) { case 'ObjectId': return ; case 'Date': return ; case 'Binary': return ; case 'Int32': case 'Double': return ; case 'String': return ; case 'BSONRegExp': return ; case 'Code': return ; case 'MinKey': case 'MaxKey': return ; case 'DBRef': return ; case 'Timestamp': return ; case 'BSONSymbol': return ; case 'Array': return ; case 'Object': return ; default: return ( ); } }; export default BSONValue;