feat: feature/mvp-sprint-1 Made commander re-usable for undefined values
This commit is contained in:
parent
9223138d12
commit
6663e12ebb
@ -9,11 +9,12 @@ export interface ThingtimeContextInterface {
|
||||
setThingtime: any
|
||||
getThingtime: any
|
||||
thingtimeRef: any
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
export const ThingtimeContext = createContext<
|
||||
ThingtimeContextInterface[] | null
|
||||
>(null)
|
||||
export const ThingtimeContext = createContext<ThingtimeContextInterface | null>(
|
||||
null
|
||||
)
|
||||
|
||||
try {
|
||||
window.smarts = smarts
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React from "react"
|
||||
import ClickAwayListener from "react-click-away-listener"
|
||||
import { Center, Flex, Input } from "@chakra-ui/react"
|
||||
import { Box, Center, Flex, Input } from "@chakra-ui/react"
|
||||
import Fuse from "fuse.js"
|
||||
|
||||
import { MagicInput } from "../MagicInput/MagicInput"
|
||||
import { Rainbow } from "../Rainbow/Rainbow"
|
||||
import { Thingtime } from "../Thingtime/Thingtime"
|
||||
import { useThingtime } from "../Thingtime/useThingtime"
|
||||
@ -69,9 +70,13 @@ export const Commander = (props) => {
|
||||
// commanderActive useEffect
|
||||
React.useEffect(() => {
|
||||
if (commanderActive) {
|
||||
inputRef?.current?.focus?.()
|
||||
if (props?.global) {
|
||||
inputRef?.current?.focus?.()
|
||||
}
|
||||
} else {
|
||||
document.activeElement.blur()
|
||||
if (props?.global) {
|
||||
document.activeElement.blur()
|
||||
}
|
||||
|
||||
if (
|
||||
thingtimeRef?.current?.settings?.commander?.[commanderId]
|
||||
@ -98,6 +103,7 @@ export const Commander = (props) => {
|
||||
commanderActive,
|
||||
thingtimeRef,
|
||||
setShowContext,
|
||||
props?.global,
|
||||
commanderId,
|
||||
inputValue,
|
||||
contextPath,
|
||||
@ -116,27 +122,43 @@ export const Commander = (props) => {
|
||||
const command = React.useMemo(() => {
|
||||
// const sanitizedCommand = sanitise(value)
|
||||
// const sanitizedCommand = inputValue
|
||||
const sanitizedCommand = virtualValue
|
||||
const sanitizedInput = virtualValue
|
||||
|
||||
const validSetter = validSetters?.find((setter) => {
|
||||
if (sanitizedCommand?.includes(setter)) {
|
||||
if (sanitizedInput?.includes(setter)) {
|
||||
return setter
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if (typeof validSetter === "string") {
|
||||
const indexOfSplitter = sanitizedCommand?.indexOf(validSetter)
|
||||
const indexOfSplitter = sanitizedInput?.indexOf(validSetter)
|
||||
const [pathRaw, valRaw] = [
|
||||
sanitizedCommand?.slice(0, indexOfSplitter),
|
||||
sanitizedCommand?.slice(indexOfSplitter + validSetter?.length),
|
||||
sanitizedInput?.slice(0, indexOfSplitter),
|
||||
sanitizedInput?.slice(indexOfSplitter + validSetter?.length),
|
||||
]
|
||||
|
||||
return [pathRaw?.trim(), valRaw?.trim()]
|
||||
const pathTrimmed = pathRaw?.trim()
|
||||
|
||||
let path = pathTrimmed
|
||||
|
||||
if (pathTrimmed && props?.pathPrefix) {
|
||||
path = props?.pathPrefix + "." + pathTrimmed
|
||||
} else if (props?.pathPrefix) {
|
||||
path = props?.pathPrefix
|
||||
}
|
||||
|
||||
return [path, valRaw?.trim()]
|
||||
}
|
||||
return [sanitizedCommand]
|
||||
|
||||
if (props?.pathPrefix) {
|
||||
return [props?.pathPrefix, sanitizedInput]
|
||||
}
|
||||
|
||||
return [sanitizedInput]
|
||||
}, [
|
||||
// inputValue,
|
||||
props?.pathPrefix,
|
||||
virtualValue,
|
||||
validSetters,
|
||||
])
|
||||
@ -167,7 +189,9 @@ export const Commander = (props) => {
|
||||
const escaped = restOfCommandValue
|
||||
?.replace(/"/g, '\\"')
|
||||
?.replace(/'/g, "\\'")
|
||||
const ret = `"${escaped}"`
|
||||
?.replace(/`/g, "\\`")
|
||||
|
||||
const ret = `\`${escaped}\``
|
||||
return ret
|
||||
}, [commandValue, validQuotations])
|
||||
|
||||
@ -270,6 +294,8 @@ export const Commander = (props) => {
|
||||
if (curSuggestionIdx !== null) {
|
||||
selectSuggestion(curSuggestionIdx)
|
||||
}
|
||||
console.log("nik commanderActive", commanderActive)
|
||||
console.log("nik commandIsAction", commandIsAction)
|
||||
if (commanderActive) {
|
||||
try {
|
||||
if (commandIsAction) {
|
||||
@ -378,7 +404,10 @@ export const Commander = (props) => {
|
||||
setHoveredSuggestion(0)
|
||||
}
|
||||
} else if (e?.code === "Enter") {
|
||||
executeCommand()
|
||||
// if not shift enter then execute command
|
||||
if (!e?.shiftKey) {
|
||||
executeCommand()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -414,89 +443,140 @@ export const Commander = (props) => {
|
||||
setVirtualValue(inputValue)
|
||||
}, [inputValue])
|
||||
|
||||
const InputJSX = React.useMemo(() => {
|
||||
return (
|
||||
<Input
|
||||
// display='none'
|
||||
// opacity={0}
|
||||
ref={inputRef}
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
},
|
||||
}}
|
||||
width="100%"
|
||||
height="100%"
|
||||
background="grey"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={onInputChange}
|
||||
onFocus={openCommander}
|
||||
placeholder="Imagine.."
|
||||
value={inputValue}
|
||||
></Input>
|
||||
)
|
||||
}, [inputRef, inputValue, onInputChange, openCommander])
|
||||
const onMagicInput = React.useCallback((args) => {
|
||||
// props?.onValueChange?.(args)
|
||||
|
||||
setInputValue(args?.value)
|
||||
setHoveredSuggestion(null)
|
||||
}, [])
|
||||
|
||||
const InputPartWrapper = React.useCallback(
|
||||
(props) => {
|
||||
return <Box paddingX={commanderActive ? 1 : 0}>{props?.children}</Box>
|
||||
},
|
||||
[commanderActive]
|
||||
)
|
||||
|
||||
const InputPart = React.useMemo(() => {
|
||||
if (props?.simple) {
|
||||
return (
|
||||
<Input
|
||||
// display='none'
|
||||
// opacity={0}
|
||||
ref={inputRef}
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
},
|
||||
}}
|
||||
width="100%"
|
||||
height="100%"
|
||||
background="grey"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={onInputChange}
|
||||
onFocus={openCommander}
|
||||
placeholder="Imagine.."
|
||||
value={inputValue}
|
||||
></Input>
|
||||
)
|
||||
}
|
||||
|
||||
const MainInput = React.useMemo(() => {
|
||||
return (
|
||||
<Center
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
padding="1px"
|
||||
borderRadius="6px"
|
||||
pointerEvents="all"
|
||||
outline="none"
|
||||
>
|
||||
{props?.rainbow && (
|
||||
<Rainbow
|
||||
opacity={commanderActive ? 0.6 : 0}
|
||||
position="absolute"
|
||||
repeats={rainbowRepeats}
|
||||
opacityTransition="all 2500ms ease"
|
||||
thickness={10}
|
||||
></Rainbow>
|
||||
)}
|
||||
{InputJSX}
|
||||
</Center>
|
||||
<MagicInput
|
||||
placeholder={props?.placeholder || "Imagine.."}
|
||||
onValueChange={onMagicInput}
|
||||
onFocus={openCommander}
|
||||
chakras={{
|
||||
marginX: commanderActive && props?.rainbow ? 4 : 0,
|
||||
}}
|
||||
transition="all 0.5s ease-in-out"
|
||||
></MagicInput>
|
||||
)
|
||||
}, [InputJSX, commanderActive, rainbowRepeats, props?.rainbow, mobileVW])
|
||||
}, [
|
||||
inputRef,
|
||||
onInputChange,
|
||||
commanderActive,
|
||||
props?.rainbow,
|
||||
props?.placeholder,
|
||||
openCommander,
|
||||
onMagicInput,
|
||||
props?.simple,
|
||||
inputValue,
|
||||
])
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={closeCommander}>
|
||||
<Flex
|
||||
position="absolute"
|
||||
top={0}
|
||||
right={0}
|
||||
// position="absolute"
|
||||
// top={0}
|
||||
// right={0}
|
||||
// left={0}
|
||||
// zIndex={99999}
|
||||
// position='fixed'
|
||||
// top='100px'
|
||||
left={0}
|
||||
justifyContent={["flex-start", "center"]}
|
||||
className={"commander-uuid-" + commanderId}
|
||||
// display={["flex", commanderActive ? "flex" : "none"]}
|
||||
justifyContent="flex-start"
|
||||
maxWidth="100%"
|
||||
height={12}
|
||||
// height="100%"
|
||||
pointerEvents="none"
|
||||
id="commander"
|
||||
paddingX={1}
|
||||
// paddingX={1}
|
||||
>
|
||||
<Center
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
>
|
||||
{props?.rainbow && (
|
||||
<Rainbow
|
||||
filter="blur(15px)"
|
||||
opacity={commanderActive ? 0.25 : 0}
|
||||
repeats={rainbowRepeats}
|
||||
thickness={10}
|
||||
opacityTransition="all 1000ms ease"
|
||||
overflow="visible"
|
||||
>
|
||||
<Center
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
padding="1px"
|
||||
borderRadius="6px"
|
||||
pointerEvents="all"
|
||||
outline="none"
|
||||
>
|
||||
<Rainbow
|
||||
opacity={commanderActive ? 0.6 : 0}
|
||||
position="absolute"
|
||||
repeats={rainbowRepeats}
|
||||
opacityTransition="all 2500ms ease"
|
||||
thickness={1}
|
||||
>
|
||||
{/* <InputPartWrapper>{InputPart}</InputPartWrapper> */}
|
||||
{InputPart}
|
||||
</Rainbow>
|
||||
</Center>
|
||||
</Rainbow>
|
||||
)}
|
||||
{!props?.rainbow && InputPart}
|
||||
</Center>
|
||||
<Flex
|
||||
position="absolute"
|
||||
top="100%"
|
||||
right={0}
|
||||
left={0}
|
||||
alignItems={["flex-start", "center"]}
|
||||
// position="absolute"
|
||||
// top="100%"
|
||||
// right={0}
|
||||
// left={0}
|
||||
alignItems="flex-start"
|
||||
flexDirection="column"
|
||||
maxWidth="100%"
|
||||
height="auto"
|
||||
marginTop={2}
|
||||
// marginTop={2}
|
||||
borderRadius="12px"
|
||||
marginX={1}
|
||||
// marginX={1}
|
||||
>
|
||||
<Flex
|
||||
alignItems={["flex-start", "center"]}
|
||||
@ -541,7 +621,7 @@ export const Commander = (props) => {
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
{showContext && (
|
||||
{showContext && props?.context && (
|
||||
<Flex
|
||||
display={showContext ? "flex" : "none"}
|
||||
maxWidth="100%"
|
||||
@ -559,26 +639,6 @@ export const Commander = (props) => {
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Center
|
||||
position="relative"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
>
|
||||
{props?.rainbow && (
|
||||
<Rainbow
|
||||
filter="blur(15px)"
|
||||
opacity={commanderActive ? 0.25 : 0}
|
||||
repeats={rainbowRepeats}
|
||||
thickness={8}
|
||||
opacityTransition="all 1000ms ease"
|
||||
overflow="visible"
|
||||
>
|
||||
{MainInput}
|
||||
</Rainbow>
|
||||
)}
|
||||
{!props?.rainbow && MainInput}
|
||||
</Center>
|
||||
</Flex>
|
||||
</ClickAwayListener>
|
||||
)
|
||||
|
568
remix/app/components/Commander/CommanderV1.tsx
Normal file
568
remix/app/components/Commander/CommanderV1.tsx
Normal file
@ -0,0 +1,568 @@
|
||||
import React from "react"
|
||||
import ClickAwayListener from "react-click-away-listener"
|
||||
import { Center, Flex, Input } from "@chakra-ui/react"
|
||||
import Fuse from "fuse.js"
|
||||
|
||||
import { Rainbow } from "../Rainbow/Rainbow"
|
||||
import { Thingtime } from "../Thingtime/Thingtime"
|
||||
import { useThingtime } from "../Thingtime/useThingtime"
|
||||
|
||||
import { sanitise } from "~/functions/sanitise"
|
||||
import { getParentPath } from "~/smarts"
|
||||
|
||||
export const CommanderV1 = (props) => {
|
||||
const { thingtime, setThingtime, getThingtime, thingtimeRef, paths } =
|
||||
useThingtime()
|
||||
|
||||
const commanderId = React.useMemo(() => {
|
||||
return props?.id || "global"
|
||||
}, [props?.id])
|
||||
|
||||
const inputRef = React.useRef()
|
||||
|
||||
const global = props?.global
|
||||
|
||||
const commanderSettings = React.useMemo(() => {
|
||||
return thingtime?.settings?.commander?.[commanderId] || {}
|
||||
}, [
|
||||
thingtime?.settings?.commander,
|
||||
thingtime?.settings?.commander?.[commanderId],
|
||||
commanderId,
|
||||
])
|
||||
|
||||
const [inputValue, setInputValue] = React.useState("")
|
||||
const [virtualValue, setVirtualValue] = React.useState("")
|
||||
const [hoveredSuggestion, setHoveredSuggestion] = React.useState()
|
||||
const [active, setActive] = React.useState(false)
|
||||
const [contextPath, setContextPath] = React.useState()
|
||||
|
||||
const mode = React.useMemo(() => {
|
||||
return props?.mode || "value"
|
||||
}, [props?.mode])
|
||||
|
||||
const [showContext, setShowContextState] = React.useState(false)
|
||||
|
||||
const mobileVW = React.useMemo(() => {
|
||||
return "calc(100vw - 55px)"
|
||||
}, [])
|
||||
|
||||
const rainbowRepeats = 2
|
||||
|
||||
const setShowContext = React.useCallback(
|
||||
(value, from?: string) => {
|
||||
setShowContextState(value)
|
||||
},
|
||||
[setShowContextState]
|
||||
)
|
||||
// const [suggestions, setSuggestions] = React.useState([])
|
||||
|
||||
const contextValue = React.useMemo(() => {
|
||||
// TODO: Figure out why this is running on every click
|
||||
const ret = getThingtime(contextPath)
|
||||
return ret
|
||||
}, [contextPath, getThingtime])
|
||||
|
||||
const commanderActive = React.useMemo(() => {
|
||||
return thingtime?.settings?.commander?.[commanderId]?.commanderActive
|
||||
}, [commanderSettings, commanderId])
|
||||
|
||||
// commanderActive useEffect
|
||||
React.useEffect(() => {
|
||||
if (commanderActive) {
|
||||
inputRef?.current?.focus?.()
|
||||
} else {
|
||||
document.activeElement.blur()
|
||||
|
||||
if (
|
||||
thingtimeRef?.current?.settings?.commander?.[commanderId]
|
||||
?.clearCommanderOnToggle
|
||||
) {
|
||||
setInputValue("")
|
||||
setHoveredSuggestion(null)
|
||||
}
|
||||
if (
|
||||
thingtimeRef?.current?.settings?.commander?.[commanderId]?.commander?.[
|
||||
commanderId
|
||||
]?.clearCommanderContextOnToggle
|
||||
) {
|
||||
setShowContext(false, "commanderActive useEffect")
|
||||
}
|
||||
if (contextPath !== undefined && !inputValue) {
|
||||
setContextPath(undefined)
|
||||
}
|
||||
if (showContext !== false) {
|
||||
setShowContext(false)
|
||||
}
|
||||
}
|
||||
}, [
|
||||
commanderActive,
|
||||
thingtimeRef,
|
||||
setShowContext,
|
||||
commanderId,
|
||||
inputValue,
|
||||
contextPath,
|
||||
showContext,
|
||||
])
|
||||
|
||||
const onInputChange = React.useCallback((e) => {
|
||||
setInputValue(e.target.value)
|
||||
setHoveredSuggestion(null)
|
||||
}, [])
|
||||
|
||||
const validSetters = React.useMemo(() => {
|
||||
return ["=", " is ", " IS ", " Is ", " iS "]
|
||||
}, [])
|
||||
|
||||
const command = React.useMemo(() => {
|
||||
// const sanitizedCommand = sanitise(value)
|
||||
// const sanitizedCommand = inputValue
|
||||
const sanitizedCommand = virtualValue
|
||||
|
||||
const validSetter = validSetters?.find((setter) => {
|
||||
if (sanitizedCommand?.includes(setter)) {
|
||||
return setter
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if (typeof validSetter === "string") {
|
||||
const indexOfSplitter = sanitizedCommand?.indexOf(validSetter)
|
||||
const [pathRaw, valRaw] = [
|
||||
sanitizedCommand?.slice(0, indexOfSplitter),
|
||||
sanitizedCommand?.slice(indexOfSplitter + validSetter?.length),
|
||||
]
|
||||
|
||||
return [pathRaw?.trim(), valRaw?.trim()]
|
||||
}
|
||||
return [sanitizedCommand]
|
||||
}, [
|
||||
// inputValue,
|
||||
virtualValue,
|
||||
validSetters,
|
||||
])
|
||||
|
||||
const commandPath = React.useMemo(() => {
|
||||
return command?.[0]
|
||||
// return sanitise(command?.[0])
|
||||
}, [command])
|
||||
|
||||
const commandValue = React.useMemo(() => {
|
||||
return command?.[1]
|
||||
}, [command])
|
||||
|
||||
const validQuotations = React.useMemo(() => {
|
||||
return ['"', "'"]
|
||||
}, [])
|
||||
|
||||
const escapedCommandValue = React.useMemo(() => {
|
||||
// replace quotations with escaped quoations except for first and last quotation
|
||||
const startingQuotation = commandValue?.[0]
|
||||
const endingQuotation = commandValue?.[commandValue?.length - 1]
|
||||
const isQuoted =
|
||||
validQuotations?.includes(startingQuotation) &&
|
||||
validQuotations?.includes(endingQuotation)
|
||||
const restOfCommandValue = isQuoted
|
||||
? commandValue?.slice(1, commandValue?.length - 1)
|
||||
: commandValue
|
||||
const escaped = restOfCommandValue
|
||||
?.replace(/"/g, '\\"')
|
||||
?.replace(/'/g, "\\'")
|
||||
const ret = `"${escaped}"`
|
||||
return ret
|
||||
}, [commandValue, validQuotations])
|
||||
|
||||
const commandIsAction = React.useMemo(() => {
|
||||
return commandPath && commandValue
|
||||
}, [commandPath, commandValue])
|
||||
|
||||
const suggestions = React.useMemo(() => {
|
||||
try {
|
||||
const fuse = new Fuse(paths)
|
||||
|
||||
const results = fuse.search(inputValue)
|
||||
|
||||
const mappedResults = results?.map((result) => {
|
||||
return result?.item
|
||||
})
|
||||
|
||||
return mappedResults
|
||||
} catch (err) {
|
||||
console.error("fuse error", err)
|
||||
}
|
||||
}, [inputValue, paths])
|
||||
|
||||
const showSuggestions = React.useMemo(() => {
|
||||
return (
|
||||
inputValue?.length &&
|
||||
suggestions?.length &&
|
||||
commanderActive &&
|
||||
thingtime?.settings?.commander?.[commanderId]?.hideSuggestionsOnToggle
|
||||
)
|
||||
}, [
|
||||
inputValue,
|
||||
suggestions,
|
||||
commanderActive,
|
||||
commanderId,
|
||||
thingtime?.settings?.commander,
|
||||
commanderSettings,
|
||||
])
|
||||
|
||||
const selectSuggestion = React.useCallback(
|
||||
(suggestionIdx) => {
|
||||
const suggestion = suggestions?.[suggestionIdx]
|
||||
|
||||
setInputValue(suggestion)
|
||||
setHoveredSuggestion(null)
|
||||
setContextPath(suggestion)
|
||||
setShowContext(true, "Select suggestion")
|
||||
},
|
||||
[setInputValue, setContextPath, setShowContext, suggestions]
|
||||
)
|
||||
|
||||
const commandContainsPath = React.useMemo(() => {
|
||||
const commandIncludesSuggestion = suggestions?.find((suggestion) => {
|
||||
return commandPath?.includes(suggestion)
|
||||
})
|
||||
// return false
|
||||
return commandIncludesSuggestion
|
||||
}, [commandPath, suggestions])
|
||||
|
||||
const openCommander = React.useCallback(() => {
|
||||
setThingtime(`settings.commander.${commanderId}.commanderActive`, true)
|
||||
}, [setThingtime, commanderId])
|
||||
|
||||
const closeCommander = React.useCallback(
|
||||
(e?: any) => {
|
||||
if (!e?.defaultPrevented) {
|
||||
if (thingtime?.settings?.commander?.[commanderId]?.commanderActive) {
|
||||
setThingtime(
|
||||
`settings.commander.${commanderId}.commanderActive`,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
setThingtime,
|
||||
commanderId,
|
||||
commanderSettings,
|
||||
thingtime?.settings?.commander,
|
||||
]
|
||||
)
|
||||
|
||||
const toggleCommander = React.useCallback(() => {
|
||||
if (thingtime?.settings?.commander?.[commanderId]?.commanderActive) {
|
||||
closeCommander()
|
||||
} else {
|
||||
openCommander()
|
||||
}
|
||||
}, [
|
||||
thingtime?.settings?.commander,
|
||||
commanderSettings,
|
||||
commanderId,
|
||||
closeCommander,
|
||||
openCommander,
|
||||
])
|
||||
|
||||
const executeCommand = React.useCallback(() => {
|
||||
// if selection is active then select it
|
||||
const curSuggestionIdx = hoveredSuggestion
|
||||
if (curSuggestionIdx !== null) {
|
||||
selectSuggestion(curSuggestionIdx)
|
||||
}
|
||||
if (commanderActive) {
|
||||
try {
|
||||
if (commandIsAction) {
|
||||
// nothing
|
||||
const prevVal = getThingtime(commandPath)
|
||||
const parentPath = getParentPath(commandPath) || "thingtime"
|
||||
try {
|
||||
// first try to execute literal javscript
|
||||
const fn = `() => { return ${commandValue} }`
|
||||
const evalFn = eval(fn)
|
||||
const realVal = evalFn()
|
||||
setThingtime(commandPath, realVal)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
"Caught error after trying to execute literal javascript",
|
||||
err
|
||||
)
|
||||
|
||||
// likely literaly javascript wasn't valid
|
||||
try {
|
||||
const fn = `() => { return ${escapedCommandValue} }`
|
||||
const evalFn = eval(fn)
|
||||
const realVal = evalFn()
|
||||
const prevVal = getThingtime(commandPath)
|
||||
const parentPath = getParentPath(commandPath)
|
||||
setThingtime(commandPath, realVal)
|
||||
} catch {
|
||||
// something very bad went wrong
|
||||
console.log(
|
||||
"Caught error after trying to execute escaped literal javascript",
|
||||
err
|
||||
)
|
||||
}
|
||||
}
|
||||
// if (!prevVal) {
|
||||
setContextPath(commandPath)
|
||||
setShowContext(true, "commandIsAction check")
|
||||
// }
|
||||
}
|
||||
// if (commandContainsPath)
|
||||
else {
|
||||
// const prevValue = getThingtime(commandPath)
|
||||
|
||||
// const newValue = setThingtime(commandPath, prevValue)
|
||||
|
||||
console.log("Setting context path", commandPath)
|
||||
setContextPath(commandPath)
|
||||
setShowContext(true, "commandContainsPath check")
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Caught error on commander onEnter", err)
|
||||
}
|
||||
}
|
||||
}, [
|
||||
hoveredSuggestion,
|
||||
selectSuggestion,
|
||||
commanderActive,
|
||||
commandIsAction,
|
||||
commandPath,
|
||||
commandValue,
|
||||
escapedCommandValue,
|
||||
getThingtime,
|
||||
setThingtime,
|
||||
setContextPath,
|
||||
setShowContext,
|
||||
])
|
||||
|
||||
const allCommanderKeyListener = React.useCallback(
|
||||
(e: any) => {
|
||||
console.log("commander key listener e?.code", e?.code)
|
||||
thingtimeRef.current = thingtime
|
||||
if (e?.metaKey && e?.code === "KeyP") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
toggleCommander()
|
||||
}
|
||||
// if key escape close all modals
|
||||
else if (e?.code === "Escape") {
|
||||
closeCommander()
|
||||
}
|
||||
|
||||
// only run these if commander active
|
||||
|
||||
if (commanderActive) {
|
||||
// if arrow keys then move selection
|
||||
if (e?.code === "ArrowUp") {
|
||||
// move selection up
|
||||
const curSuggestionIdx =
|
||||
typeof hoveredSuggestion === "number"
|
||||
? hoveredSuggestion
|
||||
: suggestions?.length
|
||||
const newSuggestionIdx = curSuggestionIdx - 1
|
||||
if (newSuggestionIdx >= 0) {
|
||||
setHoveredSuggestion(newSuggestionIdx)
|
||||
} else {
|
||||
setHoveredSuggestion(suggestions?.length - 1)
|
||||
}
|
||||
} else if (e?.code === "ArrowDown") {
|
||||
// move selection down
|
||||
const curSuggestionIdx =
|
||||
typeof hoveredSuggestion === "number" ? hoveredSuggestion : -1
|
||||
const newSuggestionIdx = curSuggestionIdx + 1
|
||||
if (newSuggestionIdx < suggestions?.length) {
|
||||
setHoveredSuggestion(newSuggestionIdx)
|
||||
} else {
|
||||
setHoveredSuggestion(0)
|
||||
}
|
||||
} else if (e?.code === "Enter") {
|
||||
executeCommand()
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
closeCommander,
|
||||
toggleCommander,
|
||||
hoveredSuggestion,
|
||||
suggestions,
|
||||
thingtime,
|
||||
thingtimeRef,
|
||||
commanderActive,
|
||||
executeCommand,
|
||||
]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener("keydown", allCommanderKeyListener)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", allCommanderKeyListener)
|
||||
}
|
||||
}, [allCommanderKeyListener])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof hoveredSuggestion === "number") {
|
||||
setVirtualValue(suggestions?.[hoveredSuggestion])
|
||||
} else {
|
||||
setVirtualValue(inputValue)
|
||||
}
|
||||
}, [hoveredSuggestion, inputValue, suggestions])
|
||||
|
||||
React.useEffect(() => {
|
||||
setVirtualValue(inputValue)
|
||||
}, [inputValue])
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={closeCommander}>
|
||||
<Flex
|
||||
position="absolute"
|
||||
top={0}
|
||||
right={0}
|
||||
// zIndex={99999}
|
||||
// position='fixed'
|
||||
// top='100px'
|
||||
left={0}
|
||||
justifyContent={["flex-start", "center"]}
|
||||
// display={["flex", commanderActive ? "flex" : "none"]}
|
||||
maxWidth="100%"
|
||||
height={12}
|
||||
// height="100%"
|
||||
pointerEvents="none"
|
||||
id="commander"
|
||||
paddingX={1}
|
||||
>
|
||||
<Flex
|
||||
position="absolute"
|
||||
top="100%"
|
||||
right={0}
|
||||
left={0}
|
||||
alignItems={["flex-start", "center"]}
|
||||
flexDirection="column"
|
||||
maxWidth="100%"
|
||||
height="auto"
|
||||
marginTop={2}
|
||||
borderRadius="12px"
|
||||
marginX={1}
|
||||
>
|
||||
<Flex
|
||||
alignItems={["flex-start", "center"]}
|
||||
flexDirection="column"
|
||||
overflowY="scroll"
|
||||
width="auto"
|
||||
maxWidth="100%"
|
||||
maxHeight="90vh"
|
||||
borderRadius="12px"
|
||||
>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
flexShrink={0}
|
||||
display={showSuggestions ? "flex" : "none"}
|
||||
overflowY="scroll"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth="100%"
|
||||
maxHeight="300px"
|
||||
marginBottom={3}
|
||||
background="grey"
|
||||
borderRadius="12px"
|
||||
pointerEvents="all"
|
||||
id="commander-suggestions"
|
||||
onMouseLeave={() => setHoveredSuggestion(null)}
|
||||
paddingY={3}
|
||||
>
|
||||
{suggestions?.map((suggestion, i) => {
|
||||
return (
|
||||
<Flex
|
||||
key={i}
|
||||
background={hoveredSuggestion === i ? "greys.lightt" : null}
|
||||
_hover={{
|
||||
background: "greys.lightt",
|
||||
}}
|
||||
cursor="pointer"
|
||||
onClick={() => selectSuggestion(i)}
|
||||
onMouseEnter={() => setHoveredSuggestion(i)}
|
||||
paddingX={4}
|
||||
>
|
||||
{suggestion}
|
||||
</Flex>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
{showContext && (
|
||||
<Flex
|
||||
display={showContext ? "flex" : "none"}
|
||||
maxWidth="100%"
|
||||
background="grey"
|
||||
borderRadius="12px"
|
||||
pointerEvents="all"
|
||||
paddingY={3}
|
||||
>
|
||||
<Thingtime
|
||||
width="600px"
|
||||
path={contextPath}
|
||||
thing={contextValue}
|
||||
></Thingtime>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Center
|
||||
position="relative"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
>
|
||||
<Rainbow
|
||||
filter="blur(15px)"
|
||||
opacity={commanderActive ? 0.25 : 0}
|
||||
repeats={rainbowRepeats}
|
||||
thickness={8}
|
||||
opacityTransition="all 1000ms ease"
|
||||
overflow="visible"
|
||||
>
|
||||
<Center
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
padding="1px"
|
||||
borderRadius="6px"
|
||||
pointerEvents="all"
|
||||
outline="none"
|
||||
>
|
||||
<Rainbow
|
||||
opacity={commanderActive ? 0.6 : 0}
|
||||
position="absolute"
|
||||
repeats={rainbowRepeats}
|
||||
opacityTransition="all 2500ms ease"
|
||||
thickness={10}
|
||||
></Rainbow>
|
||||
<Input
|
||||
// display='none'
|
||||
// opacity={0}
|
||||
ref={inputRef}
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
},
|
||||
}}
|
||||
width="100%"
|
||||
height="100%"
|
||||
background="grey"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={onInputChange}
|
||||
onFocus={openCommander}
|
||||
placeholder="Imagine.."
|
||||
value={inputValue}
|
||||
></Input>
|
||||
</Center>
|
||||
</Rainbow>
|
||||
</Center>
|
||||
</Flex>
|
||||
</ClickAwayListener>
|
||||
)
|
||||
}
|
@ -12,12 +12,21 @@ export const Icon = (props) => {
|
||||
if (["crystal"]?.includes(name)) {
|
||||
return "🔮"
|
||||
}
|
||||
if (["flower", "hibiscus"]?.includes(name)) {
|
||||
return "🌺"
|
||||
}
|
||||
if (["sparke", "magic"]?.includes(name)) {
|
||||
return "✨"
|
||||
}
|
||||
if (["box", "thing", "object"]?.includes(name)) {
|
||||
return "📦"
|
||||
}
|
||||
if (["pencil"]?.includes(name)) {
|
||||
return "✏️"
|
||||
}
|
||||
if (["edit", "paint", "create"]?.includes(name)) {
|
||||
return "🎨"
|
||||
}
|
||||
if (["book", "books"]?.includes(name)) {
|
||||
return "📚"
|
||||
}
|
||||
@ -82,6 +91,9 @@ export const Icon = (props) => {
|
||||
if (["star", "favorite"]?.includes(name)) {
|
||||
return "⭐"
|
||||
}
|
||||
if (["glowing star", "glowing favorite"]?.includes(name)) {
|
||||
return "🌟"
|
||||
}
|
||||
if (["question", "help"]?.includes(name)) {
|
||||
return "❓"
|
||||
}
|
||||
@ -152,7 +164,11 @@ export const Icon = (props) => {
|
||||
}, [name])
|
||||
|
||||
return (
|
||||
<Center {...props?.chakras} fontSize={props?.size}>
|
||||
<Center
|
||||
transition="all 0.2s ease-out"
|
||||
{...props?.chakras}
|
||||
fontSize={props?.size}
|
||||
>
|
||||
{icon}
|
||||
</Center>
|
||||
)
|
||||
|
150
remix/app/components/MagicInput/MagicInput.tsx
Normal file
150
remix/app/components/MagicInput/MagicInput.tsx
Normal file
@ -0,0 +1,150 @@
|
||||
import React from "react"
|
||||
import { Box } from "@chakra-ui/react"
|
||||
|
||||
import { useThingtime } from "../Thingtime/useThingtime"
|
||||
|
||||
export const MagicInput = (props) => {
|
||||
const { thingtime, setThingtime, loading } = useThingtime()
|
||||
|
||||
const [inputValue, setInputValue] = React.useState()
|
||||
|
||||
const contentEditableRef = React.useRef(null)
|
||||
const editValueRef = React.useRef({})
|
||||
|
||||
const fullPath = React.useMemo(() => {
|
||||
const ret = props?.fullPath || props?.path
|
||||
|
||||
// store this thing in the global db
|
||||
try {
|
||||
// window.meta.things[ret] = props?.value
|
||||
} catch {
|
||||
// nothing
|
||||
}
|
||||
|
||||
return ret
|
||||
}, [props?.fullPath, props?.path, props?.value])
|
||||
|
||||
const [contentEditableValue, setContentEditableValue] = React.useState(
|
||||
props?.value || props?.placeholder
|
||||
)
|
||||
|
||||
const updateContentEditableValue = React.useCallback((value) => {
|
||||
// replace all new line occurences in value with <div><br></div>
|
||||
|
||||
console.log("MagicInput updating contentEditableValue with value", value)
|
||||
|
||||
// extract all series of new lines
|
||||
const newlines = value?.split?.(/[^\n]/)?.filter((v) => v !== "")
|
||||
|
||||
let newValue = value
|
||||
|
||||
// replace all new lines groups with <div><br></div>
|
||||
newlines?.forEach?.((newline) => {
|
||||
const baseLength = "\n"?.length
|
||||
|
||||
const newlineClone = newline
|
||||
|
||||
const newlineClonePart1 = newlineClone?.replace(
|
||||
"\n\n\n",
|
||||
"<div><br /></div>"
|
||||
)
|
||||
const newlineClonePart2 = newlineClonePart1?.replace(
|
||||
/\n\n/g,
|
||||
"<div><br /></div>"
|
||||
)
|
||||
const newlineClonePart3 = newlineClonePart2?.replace(/\n/g, "<br />")
|
||||
|
||||
newValue = newValue?.replace(newline, newlineClonePart3)
|
||||
})
|
||||
|
||||
setContentEditableValue(newValue)
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
setInputValue(contentEditableValue)
|
||||
}, [contentEditableValue])
|
||||
|
||||
React.useEffect(() => {
|
||||
const entries = Object.entries(editValueRef.current)
|
||||
const propsValueInEntries = entries?.find?.(
|
||||
(entry) => entry[1] === props?.value
|
||||
)
|
||||
if (!propsValueInEntries) {
|
||||
// const valueToUse = props?.value || props?.placeholder
|
||||
const valueToUse = props?.value
|
||||
updateContentEditableValue(valueToUse)
|
||||
// setContentEditableValue(props?.value)
|
||||
} else {
|
||||
const [time, value] = propsValueInEntries
|
||||
if (time && value) {
|
||||
delete editValueRef.current[time]
|
||||
}
|
||||
}
|
||||
}, [props?.value, props?.placeholder, updateContentEditableValue])
|
||||
|
||||
const onValueChange = React.useCallback(
|
||||
(args) => {
|
||||
const { value } = args
|
||||
props?.onValueChange?.({ value })
|
||||
// updateContentEditableValue(value)
|
||||
},
|
||||
[props?.onValueChange]
|
||||
)
|
||||
|
||||
const updateValue = React.useCallback(
|
||||
(args) => {
|
||||
const { value } = args
|
||||
|
||||
onValueChange({ value })
|
||||
setInputValue(value)
|
||||
// setThingtime(fullPath, value)
|
||||
},
|
||||
[fullPath, setThingtime, onValueChange]
|
||||
)
|
||||
|
||||
const onFocus = React.useCallback(
|
||||
(e) => {
|
||||
props?.onFocus?.(e)
|
||||
},
|
||||
[props?.onFocus]
|
||||
)
|
||||
|
||||
return (
|
||||
<Box
|
||||
position="relative"
|
||||
width="100%"
|
||||
transition={props?.transition}
|
||||
{...(props?.chakras || {})}
|
||||
>
|
||||
<Box
|
||||
ref={contentEditableRef}
|
||||
width="100%"
|
||||
border="none"
|
||||
outline="none"
|
||||
contentEditable={true}
|
||||
dangerouslySetInnerHTML={{ __html: contentEditableValue }}
|
||||
onFocus={onFocus}
|
||||
onInput={(value) => {
|
||||
const innerText = value?.target?.innerText
|
||||
console.log("MagicInput got onInput event value", value)
|
||||
console.log("MagicInput got onInput event innerText", innerText)
|
||||
if (typeof innerText === "string") {
|
||||
const time = Date.now()
|
||||
editValueRef.current[time] = innerText
|
||||
updateValue({ value: innerText })
|
||||
}
|
||||
}}
|
||||
></Box>
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
display={inputValue ? "none" : "block"}
|
||||
color="greys.dark"
|
||||
pointerEvents="none"
|
||||
>
|
||||
{props?.placeholder || "Imagine.."}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import React from "react"
|
||||
import { Box, Center, Flex } from "@chakra-ui/react"
|
||||
import { Link } from "@remix-run/react"
|
||||
import { Link, useLocation, useNavigate } from "@remix-run/react"
|
||||
|
||||
import { Commander } from "../Commander/Commander"
|
||||
import { CommanderV1 } from "../Commander/CommanderV1"
|
||||
import { Icon } from "../Icon/Icon"
|
||||
import { RainbowSkeleton } from "../Skeleton/RainbowSkeleton"
|
||||
import { ProfileDrawer } from "./ProfileDrawer"
|
||||
@ -10,10 +11,47 @@ import { ProfileDrawer } from "./ProfileDrawer"
|
||||
export const Nav = (props) => {
|
||||
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false)
|
||||
|
||||
const { pathname } = useLocation()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const toggleProfileDrawer = React.useCallback(() => {
|
||||
setProfileDrawerOpen(!profileDrawerOpen)
|
||||
}, [profileDrawerOpen])
|
||||
|
||||
const inEditMode = React.useMemo(() => {
|
||||
if (pathname.slice(0, 5) === "/edit") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
|
||||
const editorToggleable = React.useMemo(() => {
|
||||
if (pathname.slice(0, 7) === "/things") {
|
||||
return true
|
||||
} else if (pathname.slice(0, 5) === "/edit") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
|
||||
const toggleEditor = React.useCallback(
|
||||
(e) => {
|
||||
// if first characters of pathname are /things replace with /edit
|
||||
// or if first characters of pathname are /edit replace with /things
|
||||
if (pathname.slice(0, 7) === "/things") {
|
||||
const newPathname = pathname.replace("/things", "/edit")
|
||||
e?.preventDefault?.()
|
||||
navigate(newPathname)
|
||||
} else if (pathname.slice(0, 5) === "/edit") {
|
||||
const newPathname = pathname.replace("/edit", "/things")
|
||||
e?.preventDefault?.()
|
||||
navigate(newPathname)
|
||||
}
|
||||
},
|
||||
[pathname, navigate]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
@ -33,31 +71,47 @@ export const Nav = (props) => {
|
||||
width="100%"
|
||||
maxWidth="100%"
|
||||
marginY={1}
|
||||
paddingX="12px"
|
||||
paddingY="10px"
|
||||
paddingX="18px"
|
||||
paddingY="14px"
|
||||
// bg='white'
|
||||
// boxShadow={'0px 0px 10px rgba(0,0,0,0.1)'}
|
||||
>
|
||||
<Center
|
||||
width="25px"
|
||||
height="25px"
|
||||
marginRight="auto"
|
||||
transform="scaleX(-100%)"
|
||||
cursor="pointer"
|
||||
>
|
||||
<Link to="/">
|
||||
<Icon size="12px" name="unicorn"></Icon>
|
||||
</Link>
|
||||
<Center className="nav-left-section" height="100%" marginRight="auto">
|
||||
<Center transform="scaleX(-100%)" cursor="pointer">
|
||||
<Link to="/">
|
||||
<Icon size="12px" name="unicorn"></Icon>
|
||||
</Link>
|
||||
</Center>
|
||||
</Center>
|
||||
<Commander global id="nav" rainbow></Commander>
|
||||
<CommanderV1 global id="nav" rainbow></CommanderV1>
|
||||
<Center
|
||||
width="25px"
|
||||
height="25px"
|
||||
className="nav-right-section"
|
||||
columnGap={[3, 5]}
|
||||
height="100%"
|
||||
marginLeft="auto"
|
||||
transform="scaleX(-100%)"
|
||||
cursor="pointer"
|
||||
>
|
||||
<Icon size="12px" name="rainbow"></Icon>
|
||||
{editorToggleable && (
|
||||
<Center
|
||||
transform="scaleX(-100%)"
|
||||
cursor="pointer"
|
||||
onClick={toggleEditor}
|
||||
>
|
||||
<Icon
|
||||
chakras={{
|
||||
opacity: inEditMode ? 1 : 0.3,
|
||||
}}
|
||||
size="12px"
|
||||
name="paint"
|
||||
></Icon>
|
||||
{/* <Icon
|
||||
size="12px"
|
||||
name={inEditMode ? "glowing star" : "star"}
|
||||
></Icon> */}
|
||||
</Center>
|
||||
)}
|
||||
<Center transform="scaleX(-100%)" cursor="pointer">
|
||||
<Icon size="12px" name="rainbow"></Icon>
|
||||
</Center>
|
||||
</Center>
|
||||
{/* <RainbowSkeleton
|
||||
marginLeft="auto"
|
||||
|
@ -210,6 +210,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
|
||||
width="100%"
|
||||
height="100%"
|
||||
opacity={hidden ? "0" : opacity}
|
||||
// pointerEvents="none"
|
||||
transition={opacityTransition}
|
||||
>
|
||||
{/* debug svg */}
|
||||
|
@ -17,7 +17,9 @@ import {
|
||||
} from "@chakra-ui/react"
|
||||
|
||||
import { Commander } from "../Commander/Commander"
|
||||
// import { Magic } from "../Commander/Magic"
|
||||
import { Icon } from "../Icon/Icon"
|
||||
import { MagicInput } from "../MagicInput/MagicInput"
|
||||
import { Safe } from "../Safety/Safe"
|
||||
import { useThingtime } from "./useThingtime"
|
||||
|
||||
@ -242,64 +244,91 @@ export const Thingtime = (props) => {
|
||||
return ["view", "edit"]
|
||||
}, [])
|
||||
|
||||
const AtomicWrapper = React.useCallback((args) => {
|
||||
return (
|
||||
<Flex
|
||||
position="relative"
|
||||
flexDirection="row"
|
||||
flexShrink={1}
|
||||
width="100%"
|
||||
paddingLeft={args?.pl || args?.paddingLeft}
|
||||
fontSize="20px"
|
||||
border="none"
|
||||
// whiteSpace="pre-line"
|
||||
whiteSpace="pre-wrap"
|
||||
wordBreak={args?.wordBreak || "break-word"}
|
||||
outline="none"
|
||||
// paddingY={2}
|
||||
// dangerouslySetInnerHTML={{ __html: renderableValue }}
|
||||
>
|
||||
{args?.children}
|
||||
</Flex>
|
||||
)
|
||||
}, [])
|
||||
|
||||
const thingtimeChildren = React.useMemo(() => {
|
||||
if (template1Modes?.includes(mode)) {
|
||||
if (keys?.length && !circular) {
|
||||
const ret = (
|
||||
<Safe {...props}>
|
||||
<Flex
|
||||
className="nested-things"
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
// w={'500px'}
|
||||
// w={['200px', '500px']}
|
||||
maxWidth="100%"
|
||||
paddingLeft={valuePl}
|
||||
paddingY={props?.path ? 3 : 0}
|
||||
>
|
||||
{keysToUse?.length &&
|
||||
keysToUse.map((key, idx) => {
|
||||
if (!key?.human) {
|
||||
key = {
|
||||
human: key,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
let inner = <AtomicWrapper paddingLeft={pl}>Imagine..</AtomicWrapper>
|
||||
if (keys?.length && !circular) {
|
||||
inner = (
|
||||
<>
|
||||
{keysToUse?.length &&
|
||||
keysToUse.map((key, idx) => {
|
||||
if (!key?.human) {
|
||||
key = {
|
||||
human: key,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
const nextThing = thing[key?.key]
|
||||
const nextThing = thing[key?.key]
|
||||
|
||||
const nextSeen = [...seen]
|
||||
const nextSeen = [...seen]
|
||||
|
||||
if (typeof nextThing === "object") {
|
||||
nextSeen.push(nextThing)
|
||||
}
|
||||
if (typeof nextThing === "object") {
|
||||
nextSeen.push(nextThing)
|
||||
}
|
||||
|
||||
return (
|
||||
<Thingtime
|
||||
key={idx}
|
||||
seen={nextSeen}
|
||||
edit={props?.edit}
|
||||
circular={seen?.includes?.(nextThing)}
|
||||
depth={depth + 1}
|
||||
parent={thing}
|
||||
notRoot
|
||||
fullPath={fullPath + "." + key?.key}
|
||||
path={key}
|
||||
thing={nextThing}
|
||||
// thing={{ infinite: { yes: true } }}
|
||||
valuePl={pl}
|
||||
></Thingtime>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Safe>
|
||||
)
|
||||
return ret
|
||||
}
|
||||
return (
|
||||
<Thingtime
|
||||
key={idx}
|
||||
seen={nextSeen}
|
||||
edit={props?.edit}
|
||||
circular={seen?.includes?.(nextThing)}
|
||||
depth={depth + 1}
|
||||
parent={thing}
|
||||
notRoot
|
||||
fullPath={fullPath + "." + key?.key}
|
||||
path={key}
|
||||
thing={nextThing}
|
||||
// thing={{ infinite: { yes: true } }}
|
||||
valuePl={pl}
|
||||
></Thingtime>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
if (type === "object" && !circular) {
|
||||
return (
|
||||
<Safe {...props}>
|
||||
<Flex
|
||||
className="nested-things"
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
rowGap={9}
|
||||
// w={'500px'}
|
||||
// w={['200px', '500px']}
|
||||
maxWidth="100%"
|
||||
paddingLeft={valuePl}
|
||||
paddingY={props?.path ? 4 : 0}
|
||||
>
|
||||
{inner}
|
||||
</Flex>
|
||||
</Safe>
|
||||
)
|
||||
}
|
||||
}, [
|
||||
keysToUse,
|
||||
mode,
|
||||
circular,
|
||||
seen,
|
||||
type,
|
||||
@ -311,31 +340,8 @@ export const Thingtime = (props) => {
|
||||
valuePl,
|
||||
pl,
|
||||
keys,
|
||||
template1Modes,
|
||||
])
|
||||
|
||||
const AtomicWrapper = React.useCallback((props) => {
|
||||
return (
|
||||
<Flex
|
||||
position="relative"
|
||||
flexDirection="row"
|
||||
flexShrink={1}
|
||||
width="100%"
|
||||
paddingLeft={props?.pl || props?.paddingLeft}
|
||||
fontSize="20px"
|
||||
border="none"
|
||||
// whiteSpace="pre-line"
|
||||
whiteSpace="pre-wrap"
|
||||
wordBreak={props?.wordBreak || "break-word"}
|
||||
outline="none"
|
||||
paddingY={2}
|
||||
// dangerouslySetInnerHTML={{ __html: renderableValue }}
|
||||
>
|
||||
{props?.children}
|
||||
</Flex>
|
||||
)
|
||||
}, [])
|
||||
|
||||
const [contentEditableThing, setContentEditableThing] = React.useState(thing)
|
||||
|
||||
const updateContentEditableThing = React.useCallback((value) => {
|
||||
@ -388,6 +394,7 @@ export const Thingtime = (props) => {
|
||||
(args) => {
|
||||
const { value } = args
|
||||
|
||||
console.log("nik running updateValue", value)
|
||||
setThingtime(fullPath, value)
|
||||
},
|
||||
[fullPath, setThingtime]
|
||||
@ -449,10 +456,15 @@ export const Thingtime = (props) => {
|
||||
</AtomicWrapper>
|
||||
)
|
||||
}
|
||||
if (type === "string" && typeof contentEditableThing === "string") {
|
||||
if (type === "string") {
|
||||
return (
|
||||
<AtomicWrapper paddingLeft={pl} className="string-atomic-wrapper">
|
||||
<Box
|
||||
<MagicInput
|
||||
value={thing}
|
||||
placeholder="Imagine.."
|
||||
onValueChange={updateValue}
|
||||
></MagicInput>
|
||||
{/* <Box
|
||||
ref={contentEditableRef}
|
||||
width="100%"
|
||||
border="none"
|
||||
@ -467,15 +479,21 @@ export const Thingtime = (props) => {
|
||||
updateValue({ value: innerText })
|
||||
}
|
||||
}}
|
||||
></Box>
|
||||
></Box> */}
|
||||
</AtomicWrapper>
|
||||
)
|
||||
}
|
||||
if (type === "undefined") {
|
||||
return (
|
||||
<AtomicWrapper>
|
||||
<AtomicWrapper paddingLeft={pl}>
|
||||
{/* TODO: Implement UI-less commander */}
|
||||
<Commander path={fullPath} id={uuid}></Commander>
|
||||
<Commander
|
||||
// rainbow
|
||||
id={uuid}
|
||||
pathPrefix={fullPath}
|
||||
placeholder="Imagine.."
|
||||
// onValueChange={updateValue}
|
||||
></Commander>
|
||||
</AtomicWrapper>
|
||||
)
|
||||
}
|
||||
@ -614,13 +632,14 @@ export const Thingtime = (props) => {
|
||||
position="relative"
|
||||
flexDirection="column"
|
||||
// width="500px"
|
||||
rowGap={2}
|
||||
width={props?.width || props?.w || "100%"}
|
||||
maxWidth="100%"
|
||||
// marginTop={3}
|
||||
paddingRight={pr}
|
||||
// minW={depth === 1 ? '120px' : null}
|
||||
onMouseEnter={handleMouseEvent}
|
||||
onMouseLeave={handleMouseEvent}
|
||||
// minW={depth === 1 ? '120px' : null}
|
||||
paddingY={3}
|
||||
{...(props.chakras || {})}
|
||||
className={`thing uuid-${uuid}`}
|
||||
data-path={props?.path}
|
||||
@ -663,9 +682,11 @@ export const Thingtime = (props) => {
|
||||
<Box className="atomicValue">{atomicValue}</Box>
|
||||
)}
|
||||
{!loading && thingtimeChildren && (
|
||||
<Box className="thingtimeChildren">{thingtimeChildren}</Box>
|
||||
<Box className="thingtimeChildren">
|
||||
{thingtimeChildren}
|
||||
{addChildUi}
|
||||
</Box>
|
||||
)}
|
||||
{addChildUi}
|
||||
</Flex>
|
||||
</Safe>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user