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
|
setThingtime: any
|
||||||
getThingtime: any
|
getThingtime: any
|
||||||
thingtimeRef: any
|
thingtimeRef: any
|
||||||
|
loading: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ThingtimeContext = createContext<
|
export const ThingtimeContext = createContext<ThingtimeContextInterface | null>(
|
||||||
ThingtimeContextInterface[] | null
|
null
|
||||||
>(null)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.smarts = smarts
|
window.smarts = smarts
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import ClickAwayListener from "react-click-away-listener"
|
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 Fuse from "fuse.js"
|
||||||
|
|
||||||
|
import { MagicInput } from "../MagicInput/MagicInput"
|
||||||
import { Rainbow } from "../Rainbow/Rainbow"
|
import { Rainbow } from "../Rainbow/Rainbow"
|
||||||
import { Thingtime } from "../Thingtime/Thingtime"
|
import { Thingtime } from "../Thingtime/Thingtime"
|
||||||
import { useThingtime } from "../Thingtime/useThingtime"
|
import { useThingtime } from "../Thingtime/useThingtime"
|
||||||
@ -69,9 +70,13 @@ export const Commander = (props) => {
|
|||||||
// commanderActive useEffect
|
// commanderActive useEffect
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (commanderActive) {
|
if (commanderActive) {
|
||||||
|
if (props?.global) {
|
||||||
inputRef?.current?.focus?.()
|
inputRef?.current?.focus?.()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (props?.global) {
|
||||||
document.activeElement.blur()
|
document.activeElement.blur()
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
thingtimeRef?.current?.settings?.commander?.[commanderId]
|
thingtimeRef?.current?.settings?.commander?.[commanderId]
|
||||||
@ -98,6 +103,7 @@ export const Commander = (props) => {
|
|||||||
commanderActive,
|
commanderActive,
|
||||||
thingtimeRef,
|
thingtimeRef,
|
||||||
setShowContext,
|
setShowContext,
|
||||||
|
props?.global,
|
||||||
commanderId,
|
commanderId,
|
||||||
inputValue,
|
inputValue,
|
||||||
contextPath,
|
contextPath,
|
||||||
@ -116,27 +122,43 @@ export const Commander = (props) => {
|
|||||||
const command = React.useMemo(() => {
|
const command = React.useMemo(() => {
|
||||||
// const sanitizedCommand = sanitise(value)
|
// const sanitizedCommand = sanitise(value)
|
||||||
// const sanitizedCommand = inputValue
|
// const sanitizedCommand = inputValue
|
||||||
const sanitizedCommand = virtualValue
|
const sanitizedInput = virtualValue
|
||||||
|
|
||||||
const validSetter = validSetters?.find((setter) => {
|
const validSetter = validSetters?.find((setter) => {
|
||||||
if (sanitizedCommand?.includes(setter)) {
|
if (sanitizedInput?.includes(setter)) {
|
||||||
return setter
|
return setter
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if (typeof validSetter === "string") {
|
if (typeof validSetter === "string") {
|
||||||
const indexOfSplitter = sanitizedCommand?.indexOf(validSetter)
|
const indexOfSplitter = sanitizedInput?.indexOf(validSetter)
|
||||||
const [pathRaw, valRaw] = [
|
const [pathRaw, valRaw] = [
|
||||||
sanitizedCommand?.slice(0, indexOfSplitter),
|
sanitizedInput?.slice(0, indexOfSplitter),
|
||||||
sanitizedCommand?.slice(indexOfSplitter + validSetter?.length),
|
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 [sanitizedCommand]
|
|
||||||
|
return [path, valRaw?.trim()]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props?.pathPrefix) {
|
||||||
|
return [props?.pathPrefix, sanitizedInput]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [sanitizedInput]
|
||||||
}, [
|
}, [
|
||||||
// inputValue,
|
// inputValue,
|
||||||
|
props?.pathPrefix,
|
||||||
virtualValue,
|
virtualValue,
|
||||||
validSetters,
|
validSetters,
|
||||||
])
|
])
|
||||||
@ -167,7 +189,9 @@ export const Commander = (props) => {
|
|||||||
const escaped = restOfCommandValue
|
const escaped = restOfCommandValue
|
||||||
?.replace(/"/g, '\\"')
|
?.replace(/"/g, '\\"')
|
||||||
?.replace(/'/g, "\\'")
|
?.replace(/'/g, "\\'")
|
||||||
const ret = `"${escaped}"`
|
?.replace(/`/g, "\\`")
|
||||||
|
|
||||||
|
const ret = `\`${escaped}\``
|
||||||
return ret
|
return ret
|
||||||
}, [commandValue, validQuotations])
|
}, [commandValue, validQuotations])
|
||||||
|
|
||||||
@ -270,6 +294,8 @@ export const Commander = (props) => {
|
|||||||
if (curSuggestionIdx !== null) {
|
if (curSuggestionIdx !== null) {
|
||||||
selectSuggestion(curSuggestionIdx)
|
selectSuggestion(curSuggestionIdx)
|
||||||
}
|
}
|
||||||
|
console.log("nik commanderActive", commanderActive)
|
||||||
|
console.log("nik commandIsAction", commandIsAction)
|
||||||
if (commanderActive) {
|
if (commanderActive) {
|
||||||
try {
|
try {
|
||||||
if (commandIsAction) {
|
if (commandIsAction) {
|
||||||
@ -378,9 +404,12 @@ export const Commander = (props) => {
|
|||||||
setHoveredSuggestion(0)
|
setHoveredSuggestion(0)
|
||||||
}
|
}
|
||||||
} else if (e?.code === "Enter") {
|
} else if (e?.code === "Enter") {
|
||||||
|
// if not shift enter then execute command
|
||||||
|
if (!e?.shiftKey) {
|
||||||
executeCommand()
|
executeCommand()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
closeCommander,
|
closeCommander,
|
||||||
@ -414,7 +443,22 @@ export const Commander = (props) => {
|
|||||||
setVirtualValue(inputValue)
|
setVirtualValue(inputValue)
|
||||||
}, [inputValue])
|
}, [inputValue])
|
||||||
|
|
||||||
const InputJSX = React.useMemo(() => {
|
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 (
|
return (
|
||||||
<Input
|
<Input
|
||||||
// display='none'
|
// display='none'
|
||||||
@ -437,10 +481,64 @@ export const Commander = (props) => {
|
|||||||
value={inputValue}
|
value={inputValue}
|
||||||
></Input>
|
></Input>
|
||||||
)
|
)
|
||||||
}, [inputRef, inputValue, onInputChange, openCommander])
|
}
|
||||||
|
|
||||||
const MainInput = React.useMemo(() => {
|
|
||||||
return (
|
return (
|
||||||
|
<MagicInput
|
||||||
|
placeholder={props?.placeholder || "Imagine.."}
|
||||||
|
onValueChange={onMagicInput}
|
||||||
|
onFocus={openCommander}
|
||||||
|
chakras={{
|
||||||
|
marginX: commanderActive && props?.rainbow ? 4 : 0,
|
||||||
|
}}
|
||||||
|
transition="all 0.5s ease-in-out"
|
||||||
|
></MagicInput>
|
||||||
|
)
|
||||||
|
}, [
|
||||||
|
inputRef,
|
||||||
|
onInputChange,
|
||||||
|
commanderActive,
|
||||||
|
props?.rainbow,
|
||||||
|
props?.placeholder,
|
||||||
|
openCommander,
|
||||||
|
onMagicInput,
|
||||||
|
props?.simple,
|
||||||
|
inputValue,
|
||||||
|
])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClickAwayListener onClickAway={closeCommander}>
|
||||||
|
<Flex
|
||||||
|
// position="absolute"
|
||||||
|
// top={0}
|
||||||
|
// right={0}
|
||||||
|
// left={0}
|
||||||
|
// zIndex={99999}
|
||||||
|
// position='fixed'
|
||||||
|
// top='100px'
|
||||||
|
className={"commander-uuid-" + commanderId}
|
||||||
|
// display={["flex", commanderActive ? "flex" : "none"]}
|
||||||
|
justifyContent="flex-start"
|
||||||
|
maxWidth="100%"
|
||||||
|
// height="100%"
|
||||||
|
// 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
|
<Center
|
||||||
position="relative"
|
position="relative"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
@ -452,51 +550,33 @@ export const Commander = (props) => {
|
|||||||
pointerEvents="all"
|
pointerEvents="all"
|
||||||
outline="none"
|
outline="none"
|
||||||
>
|
>
|
||||||
{props?.rainbow && (
|
|
||||||
<Rainbow
|
<Rainbow
|
||||||
opacity={commanderActive ? 0.6 : 0}
|
opacity={commanderActive ? 0.6 : 0}
|
||||||
position="absolute"
|
position="absolute"
|
||||||
repeats={rainbowRepeats}
|
repeats={rainbowRepeats}
|
||||||
opacityTransition="all 2500ms ease"
|
opacityTransition="all 2500ms ease"
|
||||||
thickness={10}
|
thickness={1}
|
||||||
></Rainbow>
|
|
||||||
)}
|
|
||||||
{InputJSX}
|
|
||||||
</Center>
|
|
||||||
)
|
|
||||||
}, [InputJSX, commanderActive, rainbowRepeats, props?.rainbow, mobileVW])
|
|
||||||
|
|
||||||
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}
|
|
||||||
>
|
>
|
||||||
|
{/* <InputPartWrapper>{InputPart}</InputPartWrapper> */}
|
||||||
|
{InputPart}
|
||||||
|
</Rainbow>
|
||||||
|
</Center>
|
||||||
|
</Rainbow>
|
||||||
|
)}
|
||||||
|
{!props?.rainbow && InputPart}
|
||||||
|
</Center>
|
||||||
<Flex
|
<Flex
|
||||||
position="absolute"
|
// position="absolute"
|
||||||
top="100%"
|
// top="100%"
|
||||||
right={0}
|
// right={0}
|
||||||
left={0}
|
// left={0}
|
||||||
alignItems={["flex-start", "center"]}
|
alignItems="flex-start"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
maxWidth="100%"
|
maxWidth="100%"
|
||||||
height="auto"
|
height="auto"
|
||||||
marginTop={2}
|
// marginTop={2}
|
||||||
borderRadius="12px"
|
borderRadius="12px"
|
||||||
marginX={1}
|
// marginX={1}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
alignItems={["flex-start", "center"]}
|
alignItems={["flex-start", "center"]}
|
||||||
@ -541,7 +621,7 @@ export const Commander = (props) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
{showContext && (
|
{showContext && props?.context && (
|
||||||
<Flex
|
<Flex
|
||||||
display={showContext ? "flex" : "none"}
|
display={showContext ? "flex" : "none"}
|
||||||
maxWidth="100%"
|
maxWidth="100%"
|
||||||
@ -559,26 +639,6 @@ export const Commander = (props) => {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</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>
|
</Flex>
|
||||||
</ClickAwayListener>
|
</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)) {
|
if (["crystal"]?.includes(name)) {
|
||||||
return "🔮"
|
return "🔮"
|
||||||
}
|
}
|
||||||
|
if (["flower", "hibiscus"]?.includes(name)) {
|
||||||
|
return "🌺"
|
||||||
|
}
|
||||||
if (["sparke", "magic"]?.includes(name)) {
|
if (["sparke", "magic"]?.includes(name)) {
|
||||||
return "✨"
|
return "✨"
|
||||||
}
|
}
|
||||||
if (["box", "thing", "object"]?.includes(name)) {
|
if (["box", "thing", "object"]?.includes(name)) {
|
||||||
return "📦"
|
return "📦"
|
||||||
}
|
}
|
||||||
|
if (["pencil"]?.includes(name)) {
|
||||||
|
return "✏️"
|
||||||
|
}
|
||||||
|
if (["edit", "paint", "create"]?.includes(name)) {
|
||||||
|
return "🎨"
|
||||||
|
}
|
||||||
if (["book", "books"]?.includes(name)) {
|
if (["book", "books"]?.includes(name)) {
|
||||||
return "📚"
|
return "📚"
|
||||||
}
|
}
|
||||||
@ -82,6 +91,9 @@ export const Icon = (props) => {
|
|||||||
if (["star", "favorite"]?.includes(name)) {
|
if (["star", "favorite"]?.includes(name)) {
|
||||||
return "⭐"
|
return "⭐"
|
||||||
}
|
}
|
||||||
|
if (["glowing star", "glowing favorite"]?.includes(name)) {
|
||||||
|
return "🌟"
|
||||||
|
}
|
||||||
if (["question", "help"]?.includes(name)) {
|
if (["question", "help"]?.includes(name)) {
|
||||||
return "❓"
|
return "❓"
|
||||||
}
|
}
|
||||||
@ -152,7 +164,11 @@ export const Icon = (props) => {
|
|||||||
}, [name])
|
}, [name])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Center {...props?.chakras} fontSize={props?.size}>
|
<Center
|
||||||
|
transition="all 0.2s ease-out"
|
||||||
|
{...props?.chakras}
|
||||||
|
fontSize={props?.size}
|
||||||
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</Center>
|
</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 React from "react"
|
||||||
import { Box, Center, Flex } from "@chakra-ui/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 { Commander } from "../Commander/Commander"
|
||||||
|
import { CommanderV1 } from "../Commander/CommanderV1"
|
||||||
import { Icon } from "../Icon/Icon"
|
import { Icon } from "../Icon/Icon"
|
||||||
import { RainbowSkeleton } from "../Skeleton/RainbowSkeleton"
|
import { RainbowSkeleton } from "../Skeleton/RainbowSkeleton"
|
||||||
import { ProfileDrawer } from "./ProfileDrawer"
|
import { ProfileDrawer } from "./ProfileDrawer"
|
||||||
@ -10,10 +11,47 @@ import { ProfileDrawer } from "./ProfileDrawer"
|
|||||||
export const Nav = (props) => {
|
export const Nav = (props) => {
|
||||||
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false)
|
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false)
|
||||||
|
|
||||||
|
const { pathname } = useLocation()
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const toggleProfileDrawer = React.useCallback(() => {
|
const toggleProfileDrawer = React.useCallback(() => {
|
||||||
setProfileDrawerOpen(!profileDrawerOpen)
|
setProfileDrawerOpen(!profileDrawerOpen)
|
||||||
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@ -33,32 +71,48 @@ export const Nav = (props) => {
|
|||||||
width="100%"
|
width="100%"
|
||||||
maxWidth="100%"
|
maxWidth="100%"
|
||||||
marginY={1}
|
marginY={1}
|
||||||
paddingX="12px"
|
paddingX="18px"
|
||||||
paddingY="10px"
|
paddingY="14px"
|
||||||
// bg='white'
|
// bg='white'
|
||||||
// boxShadow={'0px 0px 10px rgba(0,0,0,0.1)'}
|
// boxShadow={'0px 0px 10px rgba(0,0,0,0.1)'}
|
||||||
>
|
>
|
||||||
<Center
|
<Center className="nav-left-section" height="100%" marginRight="auto">
|
||||||
width="25px"
|
<Center transform="scaleX(-100%)" cursor="pointer">
|
||||||
height="25px"
|
|
||||||
marginRight="auto"
|
|
||||||
transform="scaleX(-100%)"
|
|
||||||
cursor="pointer"
|
|
||||||
>
|
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<Icon size="12px" name="unicorn"></Icon>
|
<Icon size="12px" name="unicorn"></Icon>
|
||||||
</Link>
|
</Link>
|
||||||
</Center>
|
</Center>
|
||||||
<Commander global id="nav" rainbow></Commander>
|
</Center>
|
||||||
|
<CommanderV1 global id="nav" rainbow></CommanderV1>
|
||||||
<Center
|
<Center
|
||||||
width="25px"
|
className="nav-right-section"
|
||||||
height="25px"
|
columnGap={[3, 5]}
|
||||||
|
height="100%"
|
||||||
marginLeft="auto"
|
marginLeft="auto"
|
||||||
|
>
|
||||||
|
{editorToggleable && (
|
||||||
|
<Center
|
||||||
transform="scaleX(-100%)"
|
transform="scaleX(-100%)"
|
||||||
cursor="pointer"
|
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>
|
<Icon size="12px" name="rainbow"></Icon>
|
||||||
</Center>
|
</Center>
|
||||||
|
</Center>
|
||||||
{/* <RainbowSkeleton
|
{/* <RainbowSkeleton
|
||||||
marginLeft="auto"
|
marginLeft="auto"
|
||||||
width="25px"
|
width="25px"
|
||||||
|
@ -210,6 +210,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
|
|||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
opacity={hidden ? "0" : opacity}
|
opacity={hidden ? "0" : opacity}
|
||||||
|
// pointerEvents="none"
|
||||||
transition={opacityTransition}
|
transition={opacityTransition}
|
||||||
>
|
>
|
||||||
{/* debug svg */}
|
{/* debug svg */}
|
||||||
|
@ -17,7 +17,9 @@ import {
|
|||||||
} from "@chakra-ui/react"
|
} from "@chakra-ui/react"
|
||||||
|
|
||||||
import { Commander } from "../Commander/Commander"
|
import { Commander } from "../Commander/Commander"
|
||||||
|
// import { Magic } from "../Commander/Magic"
|
||||||
import { Icon } from "../Icon/Icon"
|
import { Icon } from "../Icon/Icon"
|
||||||
|
import { MagicInput } from "../MagicInput/MagicInput"
|
||||||
import { Safe } from "../Safety/Safe"
|
import { Safe } from "../Safety/Safe"
|
||||||
import { useThingtime } from "./useThingtime"
|
import { useThingtime } from "./useThingtime"
|
||||||
|
|
||||||
@ -242,21 +244,33 @@ export const Thingtime = (props) => {
|
|||||||
return ["view", "edit"]
|
return ["view", "edit"]
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const thingtimeChildren = React.useMemo(() => {
|
const AtomicWrapper = React.useCallback((args) => {
|
||||||
if (template1Modes?.includes(mode)) {
|
return (
|
||||||
if (keys?.length && !circular) {
|
|
||||||
const ret = (
|
|
||||||
<Safe {...props}>
|
|
||||||
<Flex
|
<Flex
|
||||||
className="nested-things"
|
|
||||||
position="relative"
|
position="relative"
|
||||||
flexDirection="column"
|
flexDirection="row"
|
||||||
// w={'500px'}
|
flexShrink={1}
|
||||||
// w={['200px', '500px']}
|
width="100%"
|
||||||
maxWidth="100%"
|
paddingLeft={args?.pl || args?.paddingLeft}
|
||||||
paddingLeft={valuePl}
|
fontSize="20px"
|
||||||
paddingY={props?.path ? 3 : 0}
|
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(() => {
|
||||||
|
let inner = <AtomicWrapper paddingLeft={pl}>Imagine..</AtomicWrapper>
|
||||||
|
if (keys?.length && !circular) {
|
||||||
|
inner = (
|
||||||
|
<>
|
||||||
{keysToUse?.length &&
|
{keysToUse?.length &&
|
||||||
keysToUse.map((key, idx) => {
|
keysToUse.map((key, idx) => {
|
||||||
if (!key?.human) {
|
if (!key?.human) {
|
||||||
@ -291,15 +305,30 @@ export const Thingtime = (props) => {
|
|||||||
></Thingtime>
|
></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>
|
</Flex>
|
||||||
</Safe>
|
</Safe>
|
||||||
)
|
)
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
keysToUse,
|
keysToUse,
|
||||||
mode,
|
|
||||||
circular,
|
circular,
|
||||||
seen,
|
seen,
|
||||||
type,
|
type,
|
||||||
@ -311,31 +340,8 @@ export const Thingtime = (props) => {
|
|||||||
valuePl,
|
valuePl,
|
||||||
pl,
|
pl,
|
||||||
keys,
|
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 [contentEditableThing, setContentEditableThing] = React.useState(thing)
|
||||||
|
|
||||||
const updateContentEditableThing = React.useCallback((value) => {
|
const updateContentEditableThing = React.useCallback((value) => {
|
||||||
@ -388,6 +394,7 @@ export const Thingtime = (props) => {
|
|||||||
(args) => {
|
(args) => {
|
||||||
const { value } = args
|
const { value } = args
|
||||||
|
|
||||||
|
console.log("nik running updateValue", value)
|
||||||
setThingtime(fullPath, value)
|
setThingtime(fullPath, value)
|
||||||
},
|
},
|
||||||
[fullPath, setThingtime]
|
[fullPath, setThingtime]
|
||||||
@ -449,10 +456,15 @@ export const Thingtime = (props) => {
|
|||||||
</AtomicWrapper>
|
</AtomicWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (type === "string" && typeof contentEditableThing === "string") {
|
if (type === "string") {
|
||||||
return (
|
return (
|
||||||
<AtomicWrapper paddingLeft={pl} className="string-atomic-wrapper">
|
<AtomicWrapper paddingLeft={pl} className="string-atomic-wrapper">
|
||||||
<Box
|
<MagicInput
|
||||||
|
value={thing}
|
||||||
|
placeholder="Imagine.."
|
||||||
|
onValueChange={updateValue}
|
||||||
|
></MagicInput>
|
||||||
|
{/* <Box
|
||||||
ref={contentEditableRef}
|
ref={contentEditableRef}
|
||||||
width="100%"
|
width="100%"
|
||||||
border="none"
|
border="none"
|
||||||
@ -467,15 +479,21 @@ export const Thingtime = (props) => {
|
|||||||
updateValue({ value: innerText })
|
updateValue({ value: innerText })
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></Box>
|
></Box> */}
|
||||||
</AtomicWrapper>
|
</AtomicWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (type === "undefined") {
|
if (type === "undefined") {
|
||||||
return (
|
return (
|
||||||
<AtomicWrapper>
|
<AtomicWrapper paddingLeft={pl}>
|
||||||
{/* TODO: Implement UI-less commander */}
|
{/* TODO: Implement UI-less commander */}
|
||||||
<Commander path={fullPath} id={uuid}></Commander>
|
<Commander
|
||||||
|
// rainbow
|
||||||
|
id={uuid}
|
||||||
|
pathPrefix={fullPath}
|
||||||
|
placeholder="Imagine.."
|
||||||
|
// onValueChange={updateValue}
|
||||||
|
></Commander>
|
||||||
</AtomicWrapper>
|
</AtomicWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -614,13 +632,14 @@ export const Thingtime = (props) => {
|
|||||||
position="relative"
|
position="relative"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
// width="500px"
|
// width="500px"
|
||||||
|
rowGap={2}
|
||||||
width={props?.width || props?.w || "100%"}
|
width={props?.width || props?.w || "100%"}
|
||||||
maxWidth="100%"
|
maxWidth="100%"
|
||||||
|
// marginTop={3}
|
||||||
paddingRight={pr}
|
paddingRight={pr}
|
||||||
|
// minW={depth === 1 ? '120px' : null}
|
||||||
onMouseEnter={handleMouseEvent}
|
onMouseEnter={handleMouseEvent}
|
||||||
onMouseLeave={handleMouseEvent}
|
onMouseLeave={handleMouseEvent}
|
||||||
// minW={depth === 1 ? '120px' : null}
|
|
||||||
paddingY={3}
|
|
||||||
{...(props.chakras || {})}
|
{...(props.chakras || {})}
|
||||||
className={`thing uuid-${uuid}`}
|
className={`thing uuid-${uuid}`}
|
||||||
data-path={props?.path}
|
data-path={props?.path}
|
||||||
@ -663,9 +682,11 @@ export const Thingtime = (props) => {
|
|||||||
<Box className="atomicValue">{atomicValue}</Box>
|
<Box className="atomicValue">{atomicValue}</Box>
|
||||||
)}
|
)}
|
||||||
{!loading && thingtimeChildren && (
|
{!loading && thingtimeChildren && (
|
||||||
<Box className="thingtimeChildren">{thingtimeChildren}</Box>
|
<Box className="thingtimeChildren">
|
||||||
)}
|
{thingtimeChildren}
|
||||||
{addChildUi}
|
{addChildUi}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Safe>
|
</Safe>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user