2023-07-07 07:42:14 +00:00
|
|
|
import React from "react"
|
|
|
|
import { Box, Flex } from "@chakra-ui/react"
|
2023-06-28 08:25:17 +00:00
|
|
|
|
2023-07-07 07:42:14 +00:00
|
|
|
import { Safe } from "../Safety/Safe"
|
|
|
|
import { useThingtime } from "./useThingtime"
|
|
|
|
|
|
|
|
export const Thingtime = (props) => {
|
2023-07-01 14:22:57 +00:00
|
|
|
// TODO: Add a circular reference seen prop check
|
|
|
|
// and add button to expand circular reference
|
|
|
|
// up to 1 level deep
|
2023-06-29 23:41:06 +00:00
|
|
|
|
2023-07-01 09:46:36 +00:00
|
|
|
const [uuid, setUuid] = React.useState()
|
2023-06-30 01:46:42 +00:00
|
|
|
|
2023-07-04 08:05:08 +00:00
|
|
|
const depth = React.useMemo(() => {
|
|
|
|
return props?.depth || 1
|
|
|
|
}, [props?.depth])
|
|
|
|
|
2023-07-01 14:13:27 +00:00
|
|
|
const pl = React.useMemo(() => {
|
|
|
|
return props?.pl || [4, 6]
|
|
|
|
}, [props?.pl])
|
|
|
|
|
2023-07-04 08:05:08 +00:00
|
|
|
const pr = React.useMemo(() => {
|
|
|
|
return props?.pr || (depth === 1 ? [4, 6] : 0)
|
|
|
|
}, [props?.pr])
|
|
|
|
|
2023-07-01 09:46:36 +00:00
|
|
|
// will only run on the client
|
2023-06-30 01:46:42 +00:00
|
|
|
React.useEffect(() => {
|
2023-07-01 09:46:36 +00:00
|
|
|
setUuid(Math.random().toString(36).substring(7))
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
const { thingtime } = useThingtime()
|
|
|
|
|
2023-06-28 08:25:17 +00:00
|
|
|
const thing = React.useMemo(() => {
|
|
|
|
return props.thing
|
|
|
|
}, [props.thing])
|
|
|
|
|
2023-06-29 11:06:58 +00:00
|
|
|
const mode = React.useMemo(() => {
|
2023-07-07 07:42:14 +00:00
|
|
|
return "view"
|
2023-06-29 11:06:58 +00:00
|
|
|
}, [])
|
|
|
|
|
|
|
|
const validKeyTypes = React.useMemo(() => {
|
2023-07-07 07:42:14 +00:00
|
|
|
return ["object", "array"]
|
2023-06-29 11:06:58 +00:00
|
|
|
}, [])
|
|
|
|
|
|
|
|
const keys = React.useMemo(() => {
|
|
|
|
if (validKeyTypes?.includes(typeof thing)) {
|
2023-07-01 09:46:36 +00:00
|
|
|
const keysRet = Object.keys(thing)
|
|
|
|
return keysRet
|
2023-06-29 11:06:58 +00:00
|
|
|
} else {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
}, [thing, validKeyTypes])
|
|
|
|
|
|
|
|
const type = React.useMemo(() => {
|
|
|
|
return typeof thing
|
|
|
|
}, [thing])
|
|
|
|
|
2023-07-01 15:05:55 +00:00
|
|
|
const valuePl = React.useMemo(() => {
|
2023-07-07 07:42:14 +00:00
|
|
|
if (typeof props?.valuePl === "number") {
|
2023-07-01 15:05:55 +00:00
|
|
|
return props?.valuePl
|
|
|
|
}
|
|
|
|
return props?.path ? [4, 6] : [0, 0]
|
|
|
|
}, [props?.valuePl, props?.path])
|
|
|
|
|
2023-06-29 11:06:58 +00:00
|
|
|
const renderableValue = React.useMemo(() => {
|
2023-07-07 07:42:14 +00:00
|
|
|
if (type === "string") {
|
2023-07-01 14:13:27 +00:00
|
|
|
if (!thing) {
|
2023-07-07 07:42:14 +00:00
|
|
|
return "Empty string"
|
2023-07-01 14:13:27 +00:00
|
|
|
}
|
2023-06-29 11:06:58 +00:00
|
|
|
return thing
|
2023-07-07 07:42:14 +00:00
|
|
|
} else if (type === "number") {
|
2023-06-29 11:06:58 +00:00
|
|
|
return thing
|
2023-07-07 07:42:14 +00:00
|
|
|
} else if (type === "boolean") {
|
|
|
|
return thing ? "true" : "false"
|
|
|
|
} else if (type === "object") {
|
2023-07-01 14:13:27 +00:00
|
|
|
if (thing === null) {
|
2023-07-07 07:42:14 +00:00
|
|
|
return "null"
|
2023-07-01 14:13:27 +00:00
|
|
|
}
|
2023-07-01 09:46:36 +00:00
|
|
|
if (!keys?.length) {
|
2023-07-07 07:42:14 +00:00
|
|
|
return "Something!"
|
2023-07-01 09:46:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
return JSON.stringify(thing, null, 2)
|
|
|
|
} catch (err) {
|
|
|
|
console.error(
|
2023-07-07 07:42:14 +00:00
|
|
|
"Caught error making renderableValue of thing",
|
2023-07-01 09:46:36 +00:00
|
|
|
err,
|
|
|
|
thing
|
|
|
|
)
|
2023-07-07 07:42:14 +00:00
|
|
|
return "Circular reference in object."
|
2023-07-01 09:46:36 +00:00
|
|
|
}
|
2023-06-29 11:06:58 +00:00
|
|
|
} else {
|
2023-07-07 07:42:14 +00:00
|
|
|
return "Something!"
|
2023-06-29 11:06:58 +00:00
|
|
|
}
|
|
|
|
}, [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) => {
|
2023-07-07 07:42:14 +00:00
|
|
|
Object.keys(obj).forEach((key) => {
|
|
|
|
if (typeof obj[key] === "object") {
|
2023-06-29 11:06:58 +00:00
|
|
|
if (window?.thingtime?.tmp[randId] < 1000) {
|
|
|
|
window.thingtime.tmp[randId]++
|
2023-07-07 07:42:14 +00:00
|
|
|
recurse(obj[key], `${prefix}${prefix && "."}${key}`)
|
2023-06-29 11:06:58 +00:00
|
|
|
} else {
|
2023-07-07 07:42:14 +00:00
|
|
|
console.error("Recursion limit reached in Thingtime.tsx")
|
2023-06-29 11:06:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret.push({
|
2023-07-07 07:42:14 +00:00
|
|
|
key: `${prefix}${prefix && "."}${key}`,
|
|
|
|
human: `${prefix}${prefix && " "}${key}`,
|
2023-06-29 11:06:58 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-07-07 07:42:14 +00:00
|
|
|
recurse(thing, "")
|
2023-06-29 11:06:58 +00:00
|
|
|
} catch (err) {
|
|
|
|
// console.error('Error in Thingtime.tsx creating flattenedKeys', err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}, [thing])
|
|
|
|
|
2023-06-29 11:39:40 +00:00
|
|
|
let value = null
|
|
|
|
let editableValue = null
|
2023-06-29 11:06:58 +00:00
|
|
|
|
|
|
|
const keysToUse = keys
|
|
|
|
// const keysToUse = flattenedKeys
|
|
|
|
|
2023-07-07 07:42:14 +00:00
|
|
|
const template1Modes = ["view", "edit"]
|
2023-06-29 11:06:58 +00:00
|
|
|
|
|
|
|
if (template1Modes?.includes(mode)) {
|
|
|
|
if (keys?.length) {
|
2023-06-29 11:39:40 +00:00
|
|
|
value = (
|
2023-06-29 23:41:06 +00:00
|
|
|
<Safe {...props}>
|
|
|
|
<Flex
|
2023-07-07 07:42:14 +00:00
|
|
|
position="relative"
|
|
|
|
flexDirection="column"
|
2023-06-29 23:41:06 +00:00
|
|
|
// w={'500px'}
|
|
|
|
// w={['200px', '500px']}
|
2023-07-07 07:42:14 +00:00
|
|
|
maxWidth="100%"
|
|
|
|
paddingLeft={valuePl}
|
|
|
|
paddingY={props?.path ? 3 : 0}
|
2023-06-29 23:41:06 +00:00
|
|
|
>
|
|
|
|
{keysToUse?.length &&
|
|
|
|
keysToUse.map((key, idx) => {
|
|
|
|
if (!key?.human) {
|
|
|
|
key = {
|
|
|
|
human: key,
|
2023-07-07 07:42:14 +00:00
|
|
|
key: key,
|
2023-06-29 23:41:06 +00:00
|
|
|
}
|
2023-06-29 11:06:58 +00:00
|
|
|
}
|
2023-06-29 23:41:06 +00:00
|
|
|
|
|
|
|
const nextThing = thing[key?.key]
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Thingtime
|
|
|
|
key={idx}
|
|
|
|
depth={depth + 1}
|
|
|
|
parent={thing}
|
|
|
|
path={key}
|
|
|
|
thing={nextThing}
|
|
|
|
// thing={{ infinite: { yes: true } }}
|
2023-07-01 14:13:27 +00:00
|
|
|
valuePl={pl}
|
2023-06-29 23:41:06 +00:00
|
|
|
></Thingtime>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</Flex>
|
|
|
|
</Safe>
|
2023-06-29 11:06:58 +00:00
|
|
|
)
|
|
|
|
} else {
|
2023-06-29 11:39:40 +00:00
|
|
|
editableValue = (
|
|
|
|
<Box
|
2023-07-07 07:42:14 +00:00
|
|
|
paddingLeft={pl}
|
|
|
|
fontSize="20px"
|
|
|
|
border="none"
|
|
|
|
whiteSpace="pre-line"
|
|
|
|
outline="none"
|
|
|
|
contentEditable={mode === "edit"}
|
|
|
|
paddingY={2}
|
2023-07-01 15:05:55 +00:00
|
|
|
// dangerouslySetInnerHTML={{ __html: renderableValue }}
|
2023-06-29 11:39:40 +00:00
|
|
|
>
|
|
|
|
{renderableValue}
|
|
|
|
</Box>
|
2023-06-29 11:06:58 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const contextMenu = (
|
2023-07-07 07:42:14 +00:00
|
|
|
<Flex
|
|
|
|
position="absolute"
|
|
|
|
top={0}
|
|
|
|
right={0}
|
|
|
|
paddingRight={4}
|
|
|
|
userSelect="none"
|
|
|
|
>
|
2023-06-29 11:06:58 +00:00
|
|
|
Settings
|
|
|
|
</Flex>
|
|
|
|
)
|
|
|
|
|
|
|
|
const [showContextMenu, setShowContextMenu] = React.useState(false)
|
|
|
|
|
2023-07-01 15:05:55 +00:00
|
|
|
const humanPath = React.useMemo(() => {
|
2023-07-07 07:42:14 +00:00
|
|
|
if (typeof props?.path === "string") {
|
2023-07-01 15:05:55 +00:00
|
|
|
return props?.path
|
|
|
|
}
|
2023-07-07 07:42:14 +00:00
|
|
|
return props?.path?.human || ""
|
2023-07-01 15:05:55 +00:00
|
|
|
}, [props?.path])
|
|
|
|
|
2023-06-29 11:39:40 +00:00
|
|
|
const path = React.useMemo(() => {
|
2023-07-07 07:42:14 +00:00
|
|
|
if (humanPath?.includes?.("hidden")) {
|
2023-07-01 14:22:04 +00:00
|
|
|
return null
|
|
|
|
}
|
2023-07-07 07:42:14 +00:00
|
|
|
if (humanPath?.includes?.("unique")) {
|
2023-07-01 14:39:02 +00:00
|
|
|
// take only path from before the string unique
|
2023-07-07 07:42:14 +00:00
|
|
|
return humanPath.split?.("unique")?.[0]
|
2023-07-01 14:39:02 +00:00
|
|
|
}
|
2023-06-29 23:41:06 +00:00
|
|
|
return (
|
2023-07-01 15:05:55 +00:00
|
|
|
<Flex
|
2023-07-07 07:42:14 +00:00
|
|
|
maxWidth="100%"
|
|
|
|
paddingLeft={props?.pathPl || pl}
|
|
|
|
fontSize="12px"
|
|
|
|
wordBreak="break-all"
|
2023-07-01 15:05:55 +00:00
|
|
|
>
|
|
|
|
{humanPath}
|
2023-06-29 23:41:06 +00:00
|
|
|
</Flex>
|
|
|
|
)
|
2023-07-01 15:05:55 +00:00
|
|
|
}, [humanPath, pl, props?.pathPl])
|
2023-06-29 11:39:40 +00:00
|
|
|
|
|
|
|
const handleMouseEvent = React.useCallback(
|
2023-07-07 07:42:14 +00:00
|
|
|
(e) => {
|
2023-06-29 11:39:40 +00:00
|
|
|
const target = e?.target
|
2023-06-29 23:41:06 +00:00
|
|
|
// extract uuid from className
|
2023-06-29 11:39:40 +00:00
|
|
|
const className = target?.className
|
2023-06-29 23:41:06 +00:00
|
|
|
if (className?.includes(uuid?.current)) {
|
2023-07-07 07:42:14 +00:00
|
|
|
setShowContextMenu(e?.type === "mouseenter")
|
2023-06-29 11:39:40 +00:00
|
|
|
}
|
|
|
|
},
|
2023-06-29 23:41:06 +00:00
|
|
|
[uuid]
|
2023-06-29 11:39:40 +00:00
|
|
|
)
|
|
|
|
|
2023-06-29 23:41:06 +00:00
|
|
|
return (
|
|
|
|
<Safe {...props} depth={depth} uuid={uuid?.current}>
|
|
|
|
<Flex
|
2023-07-07 07:42:14 +00:00
|
|
|
position="relative"
|
|
|
|
flexDirection="column"
|
|
|
|
width="500px"
|
|
|
|
maxWidth="100%"
|
|
|
|
paddingRight={pr}
|
2023-06-29 23:41:06 +00:00
|
|
|
onMouseEnter={handleMouseEvent}
|
|
|
|
onMouseLeave={handleMouseEvent}
|
|
|
|
// minW={depth === 1 ? '120px' : null}
|
2023-07-07 07:42:14 +00:00
|
|
|
paddingY={3}
|
2023-06-29 23:41:06 +00:00
|
|
|
{...props}
|
|
|
|
className={`thing-${uuid?.current}`}
|
|
|
|
>
|
|
|
|
{/* {uuid?.current} */}
|
|
|
|
{path}
|
2023-07-01 14:13:27 +00:00
|
|
|
{/* {showContextMenu && contextMenu} */}
|
2023-06-29 23:41:06 +00:00
|
|
|
{editableValue}
|
|
|
|
{value}
|
|
|
|
</Flex>
|
|
|
|
</Safe>
|
2023-06-29 11:06:58 +00:00
|
|
|
)
|
|
|
|
}
|