import React from "react" import { Box, Flex } from "@chakra-ui/react" import { Safe } from "../Safety/Safe" import { useThingtime } from "./useThingtime" export const Thingtime = (props) => { // TODO: Add a circular reference seen prop check // and add button to expand circular reference // up to 1 level deep const [uuid, setUuid] = React.useState() const depth = React.useMemo(() => { return props?.depth || 1 }, [props?.depth]) const pl = React.useMemo(() => { return props?.pl || [4, 6] }, [props?.pl]) const pr = React.useMemo(() => { return props?.pr || (depth === 1 ? [4, 6] : 0) }, [props?.pr]) // will only run on the client React.useEffect(() => { setUuid(Math.random().toString(36).substring(7)) }, []) const { thingtime } = useThingtime() const thing = React.useMemo(() => { return props.thing }, [props.thing]) const mode = React.useMemo(() => { return "view" }, []) const validKeyTypes = React.useMemo(() => { return ["object", "array"] }, []) const keys = React.useMemo(() => { if (validKeyTypes?.includes(typeof thing)) { const keysRet = Object.keys(thing) return keysRet } else { return [] } }, [thing, validKeyTypes]) const type = React.useMemo(() => { return typeof thing }, [thing]) const valuePl = React.useMemo(() => { if (typeof props?.valuePl === "number") { return props?.valuePl } return props?.path ? [4, 6] : [0, 0] }, [props?.valuePl, props?.path]) const renderableValue = React.useMemo(() => { if (type === "string") { const trimmed = thing.trim() if (!trimmed) { return "Empty string" } return trimmed } else if (type === "number") { return thing } else if (type === "boolean") { return thing ? "true" : "false" } else if (type === "object") { if (thing === null) { return "null" } if (!keys?.length) { return "Something!" } try { return JSON.stringify(thing, null, 2) } catch (err) { console.error( "Caught error making renderableValue of thing", err, thing ) return "Circular reference in object." } } else { return "Something!" } }, [thing, type]) const flattenedKeys = React.useMemo(() => { // create an array of all keys on object so that if object is // { my: { child: {} } } // the array looks like // ['my', 'my.child'] const ret = [] try { const randId = Math.random().toString(36).substring(7) window.thingtime.tmp[randId] = 0 const recurse = (obj, prefix) => { Object.keys(obj).forEach((key) => { if (typeof obj[key] === "object") { if (window?.thingtime?.tmp[randId] < 1000) { window.thingtime.tmp[randId]++ recurse(obj[key], `${prefix}${prefix && "."}${key}`) } else { console.error("Recursion limit reached in Thingtime.tsx") } } else { ret.push({ key: `${prefix}${prefix && "."}${key}`, human: `${prefix}${prefix && " "}${key}`, }) } }) } recurse(thing, "") } catch (err) { // console.error('Error in Thingtime.tsx creating flattenedKeys', err) } return ret }, [thing]) let value = null let editableValue = null const keysToUse = keys // const keysToUse = flattenedKeys const template1Modes = ["view", "edit"] if (template1Modes?.includes(mode)) { if (keys?.length) { value = ( {keysToUse?.length && keysToUse.map((key, idx) => { if (!key?.human) { key = { human: key, key: key, } } const nextThing = thing[key?.key] return ( ) })} ) } else { editableValue = ( {renderableValue} ) } } const contextMenu = ( Settings ) const [showContextMenu, setShowContextMenu] = React.useState(false) const humanPath = React.useMemo(() => { if (typeof props?.path === "string") { return props?.path } return props?.path?.human || "" }, [props?.path]) const renderedPath = React.useMemo(() => { if (humanPath?.includes?.("hidden")) { return null } if (humanPath?.includes?.("unique")) { // take only path from before the string unique return humanPath.split?.("unique")?.[0] } return humanPath }, [humanPath]) const path = React.useMemo(() => { return ( {renderedPath} ) }, [renderedPath, pl, props?.pathPl]) const handleMouseEvent = React.useCallback( (e) => { const target = e?.target // extract uuid from className const className = target?.className if (className?.includes(uuid?.current)) { setShowContextMenu(e?.type === "mouseenter") } }, [uuid] ) return ( {/* {uuid?.current} */} {path} {/* {showContextMenu && contextMenu} */} {editableValue} {value} ) }