feat: feature/mvp-sprint-1 Added type wrapping, custom types, new child types, commander address bar behaviour

This commit is contained in:
Nikolaj Frey 2023-08-22 21:31:37 +10:00
parent 168035201d
commit a69c4067df
13 changed files with 617 additions and 101 deletions

View File

@ -30,7 +30,207 @@ try {
const force = { const force = {
settings: { settings: {
undoLimit: 999, types: {
javascript: {
any: {
type: "any",
value: () => {
return null
},
},
object: {
type: "object",
value: () => {
return {}
},
},
array: {
type: "array",
value: () => {
return []
},
},
string: {
type: "string",
value: () => {
return ""
},
},
number: {
type: "number",
value: () => {
return 0
},
},
boolean: {
type: "boolean",
value: () => {
return false
},
},
function: {
type: "function",
value: () => {
return () => {}
},
},
},
custom: {
"Thingtime Logo": {
type: "chakra",
value: {
type: "chakra",
chakra: "Box",
props: {
fontSize: 12,
},
rawChildren: ["🌈 Thingtime"],
},
},
"Violet Container Centered": {
name: "Violet Container Centered",
type: "chakra",
icon: "💜",
wrap: "children",
value: {
name: "Violet Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#AB47BC",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Indigo Container Centered": {
name: "Indigo Container Centered",
type: "chakra",
icon: "🩷",
wrap: "children",
value: {
name: "Indigo Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#5C6BC0",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Blue Container Centered": {
name: "Blue Container Centered",
type: "chakra",
icon: "💙",
wrap: "children",
value: {
name: "Blue Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#42A5F5",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Green Container Centered": {
name: "Green Container Centered",
type: "chakra",
icon: "💚",
wrap: "children",
value: {
name: "Green Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#66BB6A",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Yellow Container Centered": {
name: "Yellow Container Centered",
type: "chakra",
icon: "💛",
wrap: "children",
value: {
name: "Yellow Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#FFEE58",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Orange Container Centered": {
name: "Orange Container Centered",
type: "chakra",
icon: "🧡",
wrap: "children",
value: {
name: "Orange Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#FF7043",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Red Container Centered": {
name: "Red Container Centered",
type: "chakra",
icon: "❤️",
wrap: "children",
value: {
name: "Red Container Centered",
type: "chakra",
chakra: "Center",
props: {
bg: "#C62828",
padding: 4,
borderRadius: 12,
},
children: [],
},
},
"Left Aligned": {
type: "chakra",
value: {
type: "chakra",
chakra: "Flex",
props: {
mr: "auto",
},
children: [],
},
},
"Right Aligned": {
type: "chakra",
value: {
type: "chakra",
chakra: "Flex",
props: {
ml: "auto",
},
children: [],
},
},
},
},
// undoLimit: 999,
// commander: { // commander: {
// nav: { // nav: {
// commanderActive: false, // commanderActive: false,

View File

@ -1,6 +1,7 @@
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 { Center, Flex, Input } from "@chakra-ui/react"
import { useLocation } from "@remix-run/react"
import Fuse from "fuse.js" import Fuse from "fuse.js"
import { Rainbow } from "../Rainbow/Rainbow" import { Rainbow } from "../Rainbow/Rainbow"
@ -8,12 +9,15 @@ import { Thingtime } from "../Thingtime/Thingtime"
import { useThingtime } from "../Thingtime/useThingtime" import { useThingtime } from "../Thingtime/useThingtime"
import { sanitise } from "~/functions/sanitise" import { sanitise } from "~/functions/sanitise"
import { usePath } from "~/hooks/usePath"
import { getParentPath } from "~/smarts" import { getParentPath } from "~/smarts"
export const CommanderV1 = (props) => { export const CommanderV1 = (props) => {
const { thingtime, setThingtime, getThingtime, thingtimeRef, paths } = const { thingtime, setThingtime, getThingtime, thingtimeRef, paths } =
useThingtime() useThingtime()
const { mode, changePath } = usePath()
const commanderId = React.useMemo(() => { const commanderId = React.useMemo(() => {
return props?.id || "global" return props?.id || "global"
}, [props?.id]) }, [props?.id])
@ -36,7 +40,7 @@ export const CommanderV1 = (props) => {
const [active, setActive] = React.useState(false) const [active, setActive] = React.useState(false)
const [contextPath, setContextPath] = React.useState() const [contextPath, setContextPath] = React.useState()
const mode = React.useMemo(() => { const commanderMode = React.useMemo(() => {
return props?.mode || "value" return props?.mode || "value"
}, [props?.mode]) }, [props?.mode])
@ -318,8 +322,13 @@ export const CommanderV1 = (props) => {
// const newValue = setThingtime(commandPath, prevValue) // const newValue = setThingtime(commandPath, prevValue)
console.log("Setting context path", commandPath) console.log("Setting context path", commandPath)
setContextPath(commandPath) // setContextPath(commandPath)
setShowContext(true, "commandContainsPath check")
changePath({
path: commandPath,
})
// setShowContext(true, "commandContainsPath check")
} }
} catch (err) { } catch (err) {
console.error("Caught error on commander onEnter", err) console.error("Caught error on commander onEnter", err)
@ -328,6 +337,8 @@ export const CommanderV1 = (props) => {
}, [ }, [
hoveredSuggestion, hoveredSuggestion,
selectSuggestion, selectSuggestion,
mode,
changePath,
commanderActive, commanderActive,
commandIsAction, commandIsAction,
commandPath, commandPath,
@ -421,9 +432,9 @@ export const CommanderV1 = (props) => {
<ClickAwayListener onClickAway={closeCommander}> <ClickAwayListener onClickAway={closeCommander}>
<Flex <Flex
position="absolute" position="absolute"
zIndex={9999}
top={0} top={0}
right={0} right={0}
// zIndex={99999}
// position='fixed' // position='fixed'
// top='100px' // top='100px'
left={0} left={0}
@ -438,6 +449,7 @@ export const CommanderV1 = (props) => {
> >
<Flex <Flex
position="absolute" position="absolute"
zIndex={9999}
top="100%" top="100%"
right={0} right={0}
left={0} left={0}
@ -528,6 +540,7 @@ export const CommanderV1 = (props) => {
> >
<Center <Center
position="relative" position="relative"
zIndex={9999}
overflow="hidden" overflow="hidden"
width={["100%", "400px"]} width={["100%", "400px"]}
maxWidth={[mobileVW, "100%"]} maxWidth={[mobileVW, "100%"]}
@ -553,6 +566,7 @@ export const CommanderV1 = (props) => {
color: "greys.dark", color: "greys.dark",
}, },
}} }}
zIndex={9999}
width="100%" width="100%"
height="100%" height="100%"
background="grey" background="grey"
@ -570,6 +584,7 @@ export const CommanderV1 = (props) => {
{!props?.rainbow && ( {!props?.rainbow && (
<Center <Center
position="relative" position="relative"
zIndex={9999}
overflow="hidden" overflow="hidden"
width={["100%", "400px"]} width={["100%", "400px"]}
maxWidth={[mobileVW, "100%"]} maxWidth={[mobileVW, "100%"]}
@ -588,6 +603,7 @@ export const CommanderV1 = (props) => {
color: "greys.dark", color: "greys.dark",
}, },
}} }}
zIndex={9999}
width="100%" width="100%"
height="100%" height="100%"
background="grey" background="grey"

View File

@ -1,5 +1,6 @@
import React from "react" import React from "react"
import { Center } from "@chakra-ui/react" import { Center } from "@chakra-ui/react"
import emojis from "emojis-list"
export const Icon = (props) => { export const Icon = (props) => {
const name = props?.name const name = props?.name
@ -183,6 +184,28 @@ export const Icon = (props) => {
} }
return "🌀" return "🌀"
} }
if (["function", "lambda"]?.includes(name)) {
return "📐"
}
if (["pin", "pinned", "located"]?.includes(name)) {
return "📌"
}
if (["wrap", "wrapped"]?.includes(name)) {
return "🎁"
// return "🎀"
}
if (emojis?.includes(name)) {
return name
}
if (["random"]?.includes(name)) {
return emojis[Math.floor(Math.random() * emojis.length)]
}
// question mark
return "❓"
}, [name]) }, [name])
return ( return (

View File

@ -7,6 +7,11 @@ import { ProfileDrawer } from "../Nav/ProfileDrawer"
export const Main = (props) => { export const Main = (props) => {
return ( return (
<Flex <Flex
sx={{
"*": {
whiteSpace: "pre-wrap",
},
}}
position="relative" position="relative"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"

View File

@ -79,7 +79,7 @@ export const Nav = (props) => {
<> <>
<Box <Box
position="fixed" position="fixed"
zIndex={999} zIndex={9999}
top={0} top={0}
right={0} right={0}
left={0} left={0}

View File

@ -9,6 +9,15 @@ export const SettingsMenu = (props) => {
const [show, setShow] = useState(false) const [show, setShow] = useState(false)
const hideRef = React.useRef(null) const hideRef = React.useRef(null)
const [opacity, setOpacity] = React.useState(props?.opacity === 0 ? 0 : 1) const [opacity, setOpacity] = React.useState(props?.opacity === 0 ? 0 : 1)
const [pinStatus, setPinStatus] = React.useState(false)
const stateRef = React.useRef({
pinStatus,
})
React.useEffect(() => {
stateRef.current.pinStatus = pinStatus
}, [pinStatus])
const { thingtime, events } = useThingtime() const { thingtime, events } = useThingtime()
@ -25,7 +34,10 @@ export const SettingsMenu = (props) => {
React.useEffect(() => { React.useEffect(() => {
const subscription = events.subscribe((event) => { const subscription = events.subscribe((event) => {
if (event?.type === "settings-menu-hide" && event?.uuid !== uuid) { if (event?.type === "settings-menu-hide" && event?.uuid !== uuid) {
setShow(false) if (!stateRef?.current?.pinStatus || event?.force) {
setShow(false)
setOpacity(0)
}
} }
}) })
@ -40,8 +52,10 @@ export const SettingsMenu = (props) => {
setOpacity(props?.opacity) setOpacity(props?.opacity)
} else { } else {
opacityRef.current = setInterval(() => { opacityRef.current = setInterval(() => {
setOpacity(props?.opacity) if (!stateRef?.current?.pinStatus) {
setShow(false) setOpacity(props?.opacity)
setShow(false)
}
}, waitTime) }, waitTime)
} }
}, [props?.opacity]) }, [props?.opacity])
@ -53,17 +67,23 @@ export const SettingsMenu = (props) => {
type: "settings-menu-hide", type: "settings-menu-hide",
uuid, uuid,
}) })
} else if (!show) {
setPinStatus(false)
} }
}, [show, props?.opacity, events, uuid]) }, [show, props?.opacity, events, uuid])
const maybeHide = React.useCallback(() => { const maybeHide = React.useCallback(() => {
clearInterval(hideRef?.current) clearInterval(hideRef?.current)
hideRef.current = setTimeout(() => { hideRef.current = setTimeout(() => {
setShow(false) if (!stateRef?.current?.pinStatus) {
setShow(false)
setOpacity(0)
}
}, waitTime) }, waitTime)
}, []) }, [])
const showMenu = React.useCallback(() => { const showMenu = React.useCallback(() => {
clearInterval(hideRef?.current)
setShow(true) setShow(true)
}, []) }, [])
@ -76,22 +96,41 @@ export const SettingsMenu = (props) => {
}, []) }, [])
const types = React.useMemo(() => { const types = React.useMemo(() => {
const ret = [ const baseTypes = thingtime?.settings?.types?.javascript || {}
{ label: "any", icon: "any" }, const baseTypeKeys = Object.keys(baseTypes)
"object",
"array",
"string",
"number",
"boolean",
]
return ret
}, [])
const onChangeType = React.useCallback( const customTypes = thingtime?.settings?.types?.custom || {}
(type) => { const customTypeKeysRaw = Object.keys(customTypes)
props?.onChangeType?.(type) const customTypeKeys = customTypeKeysRaw?.filter((key) => {
return !baseTypeKeys?.includes?.(key)
})
const types = [
...(baseTypeKeys?.map?.((key) => {
return {
...baseTypes?.[key],
key,
}
}) || []),
...(customTypeKeys?.map?.((key) => {
return {
...customTypes?.[key],
key,
}
}) || []),
]
return types
}, [
thingtime?.settings?.types?.javascript,
thingtime?.settings?.types?.custom,
])
const onType = React.useCallback(
(args) => {
props?.onType?.(args)
}, },
[props?.onChangeType] [props?.onType]
) )
const onDelete = React.useCallback( const onDelete = React.useCallback(
(type) => { (type) => {
@ -100,7 +139,8 @@ export const SettingsMenu = (props) => {
[props?.onDelete] [props?.onDelete]
) )
const iconSize = 10 const childIconSize = 10
const iconSize = props?.iconSize || 7
return ( return (
<ClickAwayListener onClickAway={hideMenu}> <ClickAwayListener onClickAway={hideMenu}>
@ -120,7 +160,7 @@ export const SettingsMenu = (props) => {
// onClick={deleteValue} // onClick={deleteValue}
transition="all 0.2s ease-in-out" transition="all 0.2s ease-in-out"
> >
<Icon name="wizard" size={7}></Icon> <Icon name="wizard" size={iconSize}></Icon>
</Flex> </Flex>
<Flex <Flex
position="absolute" position="absolute"
@ -131,6 +171,20 @@ export const SettingsMenu = (props) => {
opacity={show ? 1 : 0} opacity={show ? 1 : 0}
pointerEvents={show ? "all" : "none"} pointerEvents={show ? "all" : "none"}
> >
<Flex
position="absolute"
top={0}
right={0}
padding="5px"
cursor="pointer"
onClick={() => setPinStatus((prev) => !prev)}
>
<Icon
opacity={pinStatus ? 1 : 0.5}
name={pinStatus ? "pinned" : "pin"}
size="8px"
></Icon>
</Flex>
<Flex <Flex
flexDirection="column" flexDirection="column"
// rowGap={basePadding / 3} // rowGap={basePadding / 3}
@ -143,8 +197,8 @@ export const SettingsMenu = (props) => {
<Flex <Flex
alignItems="center" alignItems="center"
flexDirection="row" flexDirection="row"
paddingRight={basePadding * 2} // paddingRight={basePadding}
paddingLeft={basePadding * 1} paddingLeft={basePadding}
_hover={{ _hover={{
background: "greys.light", background: "greys.light",
}} }}
@ -152,7 +206,11 @@ export const SettingsMenu = (props) => {
// paddingX={basePadding * 1} // paddingX={basePadding * 1}
paddingY={basePadding / 2} paddingY={basePadding / 2}
> >
<Icon marginBottom="-2px" name="cyclone" size={iconSize}></Icon> <Icon
marginBottom="-2px"
name="cyclone"
size={childIconSize}
></Icon>
<Text marginTop="-2px" paddingLeft={2} fontSize="xs"> <Text marginTop="-2px" paddingLeft={2} fontSize="xs">
Types Types
</Text> </Text>
@ -161,6 +219,8 @@ export const SettingsMenu = (props) => {
<Flex <Flex
flexDirection="column" flexDirection="column"
// rowGap={basePadding} // rowGap={basePadding}
overflowY="scroll"
maxHeight="300px"
background="greys.lightt" background="greys.lightt"
cursor="pointer" cursor="pointer"
> >
@ -176,32 +236,53 @@ export const SettingsMenu = (props) => {
}, },
}} }}
cursor="pointer" cursor="pointer"
onClick={() => onChangeType(type?.key || type)} onClick={() => onType({ type })}
paddingY={1} paddingY={1}
> >
<Flex <Flex
alignItems="center" alignItems="center"
flexDirection="row" flexDirection="row"
width="100%" width="100%"
paddingRight={basePadding * 4} paddingRight={basePadding}
paddingLeft={basePadding * 2} paddingLeft={basePadding * 2}
paddingY={basePadding / 2} paddingY={basePadding / 2}
> >
<Icon <Icon
marginBottom="-2px" marginBottom="-2px"
name={type?.icon || type?.key || type?.label || type} name={type?.icon || type?.key || type?.label || type}
size={iconSize} size={childIconSize}
></Icon> ></Icon>
<Text marginTop="-2px" paddingLeft={2} fontSize="xs"> <Text marginTop="-2px" paddingLeft={2} fontSize="xs">
{type?.label || type?.key || type} {type?.label || type?.key || type}
</Text> </Text>
{type?.wrap && (
<Flex
marginLeft="auto"
_hover={{
transform: "scale(1.3)",
}}
transition="all 0.2s ease-out"
onClick={(e) => {
e?.preventDefault?.()
e?.stopPropagation?.()
// cancel bubble
e?.nativeEvent?.stopImmediatePropagation?.()
onType({
type,
wrap: true,
})
}}
>
<Icon name="wrap" size={childIconSize}></Icon>
</Flex>
)}
</Flex> </Flex>
</Flex> </Flex>
) )
return ret return ret
})} })}
</Flex> </Flex>
{!props?.readonly && ( {!props?.readonly && props?.onDelete && (
<Flex <Flex
alignItems="center" alignItems="center"
flexDirection="row" flexDirection="row"
@ -213,7 +294,11 @@ export const SettingsMenu = (props) => {
paddingX={basePadding * 1} paddingX={basePadding * 1}
paddingY={basePadding / 2} paddingY={basePadding / 2}
> >
<Icon marginBottom="-2px" name="bin" size={iconSize}></Icon> <Icon
marginBottom="-2px"
name="bin"
size={childIconSize}
></Icon>
<Text marginTop="-2px" paddingLeft={2} fontSize="xs"> <Text marginTop="-2px" paddingLeft={2} fontSize="xs">
Recycle Recycle
</Text> </Text>

View File

@ -25,6 +25,7 @@ import { Safe } from "../Safety/Safe"
import { SettingsMenu } from "./SettingsMenu" import { SettingsMenu } from "./SettingsMenu"
import { useThingtime } from "./useThingtime" import { useThingtime } from "./useThingtime"
import { useThings } from "~/hooks/useThings"
import { getThing } from "~/smarts" import { getThing } from "~/smarts"
export const Thingtime = (props) => { export const Thingtime = (props) => {
@ -32,7 +33,10 @@ export const Thingtime = (props) => {
// and add button to expand circular reference // and add button to expand circular reference
// up to 1 level deep // up to 1 level deep
const { thingtime, setThingtime, getThingtime, loading } = useThingtime() const { append } = useThings()
const { thingtime, setThingtime, getThingtime, loading, events } =
useThingtime()
const [uuid, setUuid] = React.useState(undefined) const [uuid, setUuid] = React.useState(undefined)
@ -42,6 +46,8 @@ export const Thingtime = (props) => {
const thingtimeRef = React.useRef() const thingtimeRef = React.useRef()
const [showFullPathContext, setShowFullPathContext] = React.useState(false)
const editValueRef = React.useRef({}) const editValueRef = React.useRef({})
const depth = React.useMemo(() => { const depth = React.useMemo(() => {
@ -65,20 +71,20 @@ export const Thingtime = (props) => {
return "100%" return "100%"
}, [props?.width, props?.w, render]) }, [props?.width, props?.w, render])
const chakras = React.useMemo(() => { const chakraChild = React.useMemo(() => {
if (!props?.edit && props?.chakra) { if (!props?.edit && props?.chakraChild) {
return true return true
} }
return false return false
}, [props?.edit, props?.chakra]) }, [props?.edit, props?.chakraChild])
const pl = React.useMemo(() => { const pl = React.useMemo(() => {
if (!props.edit && chakras) { if (!props.edit && chakraChild) {
return [0] return [0]
} }
return props?.pl || [4, 6] return props?.pl || [4, 6]
}, [props?.pl, props?.edit, chakras]) }, [props?.pl, props?.edit, chakraChild])
const pr = React.useMemo(() => { const pr = React.useMemo(() => {
return props?.pr || (depth === 0 ? [4, 6] : 0) return props?.pr || (depth === 0 ? [4, 6] : 0)
@ -138,7 +144,7 @@ export const Thingtime = (props) => {
const thing = React.useMemo(() => { const thing = React.useMemo(() => {
return props.thing return props.thing
}, [props.thing, childrenRef.current]) }, [props.thing, uuid, childrenRef.current])
const chakra = React.useMemo(() => { const chakra = React.useMemo(() => {
return !props?.edit && typeof thing?.chakra === "string" && thing?.chakra return !props?.edit && typeof thing?.chakra === "string" && thing?.chakra
@ -250,7 +256,7 @@ export const Thingtime = (props) => {
}, [props?.valuePl, props?.path]) }, [props?.valuePl, props?.path])
const renderableValue = React.useMemo(() => { const renderableValue = React.useMemo(() => {
if (chakras) { if (chakraChild) {
return null return null
} }
@ -287,11 +293,7 @@ export const Thingtime = (props) => {
} else { } else {
return "Something.." return "Something.."
} }
}, [thing, thingDep, type, chakras, keys]) }, [thing, thingDep, type, chakraChild, keys])
const hasChakraChildren = React.useMemo(() => {
return !props?.edit && chakra && render && thing?.children
}, [chakra, props?.edit, render, thing?.children])
const renderChakra = React.useMemo(() => { const renderChakra = React.useMemo(() => {
if (!props?.edit && chakra && render) { if (!props?.edit && chakra && render) {
@ -366,7 +368,7 @@ export const Thingtime = (props) => {
notRoot notRoot
fullPath={fullPath + "." + key?.key} fullPath={fullPath + "." + key?.key}
path={key} path={key}
chakraChildren={chakra} chakraChild={chakra}
thing={nextThing} thing={nextThing}
// thing={{ infinite: { yes: true } }} // thing={{ infinite: { yes: true } }}
valuePl={pl} valuePl={pl}
@ -382,6 +384,8 @@ export const Thingtime = (props) => {
console.log("Thingtime is chakra", fullPath, chakra) console.log("Thingtime is chakra", fullPath, chakra)
const rawChildren = thing?.rawChildren
try { try {
if (ChakraComponent) { if (ChakraComponent) {
console.log( console.log(
@ -393,6 +397,7 @@ export const Thingtime = (props) => {
const ret = ( const ret = (
<ChakraComponent {...(thing?.props || {})}> <ChakraComponent {...(thing?.props || {})}>
{rawChildren}
{inner} {inner}
</ChakraComponent> </ChakraComponent>
) )
@ -406,7 +411,7 @@ export const Thingtime = (props) => {
// TODO: Is it safe to spread props // TODO: Is it safe to spread props
// because having props as a dependency will cause a re-render every time // because having props as a dependency will cause a re-render every time
if (props?.chakraChildren) { if (props?.chakraChild) {
return inner return inner
} }
@ -432,7 +437,7 @@ export const Thingtime = (props) => {
circular, circular,
seen, seen,
type, type,
props?.chakraChildren, props?.chakraChild,
props?.path, props?.path,
props?.edit, props?.edit,
chakra, chakra,
@ -460,30 +465,34 @@ export const Thingtime = (props) => {
}, [updateValue]) }, [updateValue])
const onChangeType = React.useCallback( const onChangeType = React.useCallback(
(type) => { (args) => {
const newType = const { type, wrap } = args
type?.key || type?.value || type?.label || type?.icon || type const typeValue =
typeof type?.value === "function" ? type?.value() : type?.value
if (newType === "object") { if (type) {
updateValue({ value: {} }) const wrapTarget = type?.wrap
} else if (newType === "array") { if (wrap && wrapTarget) {
updateValue({ value: [] }) const newValue = append({
} else if (newType === "string") { thing: typeValue[wrapTarget],
updateValue({ value: "" }) value: thing,
} else if (newType === "number") { })
updateValue({ value: 0 }) typeValue[wrapTarget] = newValue
} else if (newType === "boolean") { if (typeValue) {
updateValue({ value: false }) updateValue({ value: typeValue })
} else if (newType === "undefined") { }
updateValue({ value: undefined }) } else {
} else if (newType === "null") { updateValue({ value: typeValue })
updateValue({ value: null }) }
} else if (newType === "any") {
updateValue({ value: null })
} else {
console.error("Unknown type", newType)
} }
}, },
[updateValue, thing, append, fullPath]
)
const onWrapType = React.useCallback(
(type) => {
// nothing
},
[updateValue] [updateValue]
) )
@ -704,7 +713,7 @@ export const Thingtime = (props) => {
const pathRef = React.useRef(null) const pathRef = React.useRef(null)
const pathDom = React.useMemo(() => { const pathDom = React.useMemo(() => {
if (chakras) { if (chakraChild) {
return <></> return <></>
} }
@ -726,7 +735,7 @@ export const Thingtime = (props) => {
</> </>
) )
} }
}, [renderedPath, pl, chakras, props?.edit, props?.pathPl]) }, [renderedPath, pl, chakraChild, props?.edit, props?.pathPl])
const handleMouseEvent = React.useCallback( const handleMouseEvent = React.useCallback(
(e) => { (e) => {
@ -740,33 +749,73 @@ export const Thingtime = (props) => {
[uuid] [uuid]
) )
const addNewChild = React.useCallback(() => { const addNewChild = React.useCallback(
const newChild = null (args) => {
const { type } = args
const newChild =
typeof type?.value === "function" ? type?.value() : type?.value || null
if (thing instanceof Array) { if (thing instanceof Array) {
// add new child to array // add new child to array
const newValue = [...thing, newChild] const newValue = [...thing, newChild]
setThingtime(fullPath, newValue) setThingtime(fullPath, newValue)
return return
} }
const newChildBasePath = "New Value" const newChildBasePath = "New Value"
// find increment that thing doesn't already have New Value N+1 // find increment that thing doesn't already have New Value N+1
let increment = 0 let increment = 0
let newPath = newChildBasePath let newPath = newChildBasePath
while (Object.hasOwnProperty.call(thing, newPath) && increment <= 999) { while (Object.hasOwnProperty.call(thing, newPath) && increment <= 999) {
increment++ increment++
newPath = newChildBasePath + " " + increment newPath = newChildBasePath + " " + increment
} }
const newChildPath = newPath const newChildPath = newPath
const newChildFullPath = fullPath + "." + newChildPath const newChildFullPath = fullPath + "." + newChildPath
// create new child on thing using setThingtime // create new child on thing using setThingtime
setThingtime(newChildFullPath, newChild) setThingtime(newChildFullPath, newChild)
}, [fullPath, setThingtime, thing]) },
[fullPath, setThingtime, thing]
)
const [showContextIcon, setShowContextIcon] = React.useState(false) const [showContextIcon, setShowContextIcon] = React.useState(false)
const [showNewContextIcon, setShowNewContextIcon] = React.useState(false)
if (props?.chakraChildren) { // should be absolute last
React.useEffect(() => {
try {
window.meta.things[uuid] = {
thing: props?.thing,
props,
state: {
chakra,
chakraChild,
circular,
depth,
fullPath,
parent,
parentPath,
path,
},
}
} catch {
// nothing
}
}, [
thing,
props,
uuid,
chakra,
chakraChild,
circular,
depth,
fullPath,
parent,
parentPath,
path,
])
if (chakra || chakraChild) {
return thingtimeChildren return thingtimeChildren
} }
@ -790,7 +839,7 @@ export const Thingtime = (props) => {
data-path={props?.path} data-path={props?.path}
> >
{/* {uuid?.current} */} {/* {uuid?.current} */}
{!chakras && !chakra && ( {!chakraChild && !chakra && (
<Flex position="relative" flexDirection="row"> <Flex position="relative" flexDirection="row">
<Flex <Flex
alignItems="center" alignItems="center"
@ -824,7 +873,7 @@ export const Thingtime = (props) => {
uuid={uuid} uuid={uuid}
fullPath={fullPath} fullPath={fullPath}
readonly={!props?.edit} readonly={!props?.edit}
onChangeType={onChangeType} onType={onChangeType}
onDelete={deleteValue} onDelete={deleteValue}
></SettingsMenu> ></SettingsMenu>
</Flex> </Flex>
@ -848,14 +897,37 @@ export const Thingtime = (props) => {
{thingtimeChildren} {thingtimeChildren}
{!render && type === "object" && ( {!render && type === "object" && (
<Flex <Flex
position="relative"
width="100%" width="100%"
paddingLeft={multiplyPl(2)} paddingLeft={multiplyPl(2)}
opacity={props?.edit ? 1 : 0} opacity={props?.edit ? 1 : 0}
cursor="pointer" cursor="pointer"
transition="all 0.2s ease-out" transition="all 0.2s ease-out"
onClick={addNewChild} onClick={addNewChild}
onMouseEnter={() => {
setShowFullPathContext(true)
setShowNewContextIcon(true)
}}
onMouseLeave={() => {
setShowFullPathContext(false)
setShowNewContextIcon(false)
}}
paddingY={2} paddingY={2}
> >
<Flex
position="absolute"
bottom="100%"
left={0}
display={showFullPathContext ? "flex" : "none"}
fontSize="sm"
background="greys.light"
borderRadius={6}
pointerEvents="none"
paddingX={3}
paddingY={1}
>
{fullPath}
</Flex>
<Icon <Icon
_focus={{ _focus={{
outline: "none !important", outline: "none !important",
@ -870,6 +942,25 @@ export const Thingtime = (props) => {
size={10} size={10}
name="seedling" name="seedling"
></Icon> ></Icon>
<Flex
marginLeft={2}
onClick={(e) => {
e?.preventDefault()
e?.stopPropagation()
e?.nativeEvent?.stopImmediatePropagation()
}}
>
<SettingsMenu
transition="all 0.2s ease-in-out"
opacity={showNewContextIcon ? 1 : 0}
uuid={uuid}
iconSize={10}
fullPath={fullPath}
readonly={!props?.edit}
onType={addNewChild}
></SettingsMenu>
</Flex>
{/* <Icon size={7} name="plus"></Icon> {/* <Icon size={7} name="plus"></Icon>
<Icon size={7} name="plus"></Icon> */} <Icon size={7} name="plus"></Icon> */}
</Flex> </Flex>

View File

@ -103,14 +103,14 @@ export const ThingtimeURL = (props) => {
width="600px" width="600px"
// width="100%" // width="100%"
maxHeight="100vh" maxHeight="100vh"
paddingY={2} // paddingY={2}
> >
<Thingtime <Thingtime
path={path} path={path}
thing={thing} thing={thing}
render render
chakras={{ marginY: "200px" }} chakras={{ marginY: "200px" }}
width="600px" // width="600px"
></Thingtime> ></Thingtime>
</Box> </Box>
)} )}

View File

@ -0,0 +1,55 @@
import React from "react"
import { useLocation } from "@remix-run/react"
import { useNavigate } from "@remix-run/react"
export const usePath = (props?: any) => {
const location = useLocation()
const { pathname } = location
const navigate = useNavigate()
const [mode, setMode] = React.useState("")
const modes = React.useMemo(() => {
// make sure any substrings come first
return [
"edit",
"editor",
"code",
"coder",
"thing",
"things",
"thingtime",
"thingtimes",
]
}, [])
React.useEffect(() => {
let set = false
modes.forEach((mode) => {
const pathPart = pathname.slice(1, mode.length + 1)
if (pathPart === mode) {
setMode(mode)
set = true
}
})
// if (!set) {
// setMode("things")
// }
}, [pathname, modes])
const changePath = React.useCallback(
(props) => {
const { path } = props
navigate(`${mode}/${path}`)
},
[navigate, mode]
)
const ret = {
mode,
changePath,
}
return ret
}

View File

@ -0,0 +1,34 @@
import React from "react"
export const useThings = (props?: any) => {
const append = React.useCallback((args) => {
const { thing, value } = args
if (thing instanceof Array) {
const newValue = [...thing, value]
return newValue
}
if (typeof thing === "object") {
const newChildBasePath = "New Value"
// find increment that thing doesn't already have New Value N+1
let increment = 0
let newPath = newChildBasePath
while (Object.hasOwnProperty.call(thing, newPath) && increment <= 999) {
increment++
newPath = newChildBasePath + " " + increment
}
const newChildPath = newPath
const newValue = { ...thing }
newValue[newChildPath] = value
return newValue
}
}, [])
const ret = {
append,
}
return ret
}

View File

@ -68,7 +68,7 @@ const setThingtime = (glob) => {
stats: { stats: {
db: {}, db: {},
limit: 9999, limit: 9999,
maxDepth: 10, maxDepth: 99,
count: 0, count: 0,
}, },
things: {}, things: {},

View File

@ -20,6 +20,7 @@
"@vercel/analytics": "^0.1.11", "@vercel/analytics": "^0.1.11",
"@vercel/remix": "^1.15.0", "@vercel/remix": "^1.15.0",
"draft-js": "^0.11.7", "draft-js": "^0.11.7",
"emojis-list": "^3.0.0",
"flatted": "^3.2.7", "flatted": "^3.2.7",
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"gradient-path": "^2.3.0", "gradient-path": "^2.3.0",

View File

@ -44,6 +44,9 @@ dependencies:
draft-js: draft-js:
specifier: ^0.11.7 specifier: ^0.11.7
version: 0.11.7(react-dom@18.2.0)(react@18.2.0) version: 0.11.7(react-dom@18.2.0)(react@18.2.0)
emojis-list:
specifier: ^3.0.0
version: 3.0.0
flatted: flatted:
specifier: ^3.2.7 specifier: ^3.2.7
version: 3.2.7 version: 3.2.7
@ -2572,6 +2575,7 @@ packages:
/@emotion/memoize@0.7.4: /@emotion/memoize@0.7.4:
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
requiresBuild: true
dev: false dev: false
optional: true optional: true
@ -4501,6 +4505,7 @@ packages:
/bindings@1.5.0: /bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
requiresBuild: true
dependencies: dependencies:
file-uri-to-path: 1.0.0 file-uri-to-path: 1.0.0
dev: true dev: true
@ -5192,7 +5197,6 @@ packages:
/emojis-list@3.0.0: /emojis-list@3.0.0:
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
dev: true
/encodeurl@1.0.2: /encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
@ -6159,6 +6163,7 @@ packages:
/file-uri-to-path@1.0.0: /file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -7939,6 +7944,7 @@ packages:
/node-addon-api@1.7.2: /node-addon-api@1.7.2:
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
requiresBuild: true
dev: true dev: true
optional: true optional: true