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 = {
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: {
// nav: {
// commanderActive: false,

View File

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

View File

@ -1,5 +1,6 @@
import React from "react"
import { Center } from "@chakra-ui/react"
import emojis from "emojis-list"
export const Icon = (props) => {
const name = props?.name
@ -183,6 +184,28 @@ export const Icon = (props) => {
}
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])
return (

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@ import { Safe } from "../Safety/Safe"
import { SettingsMenu } from "./SettingsMenu"
import { useThingtime } from "./useThingtime"
import { useThings } from "~/hooks/useThings"
import { getThing } from "~/smarts"
export const Thingtime = (props) => {
@ -32,7 +33,10 @@ export const Thingtime = (props) => {
// and add button to expand circular reference
// 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)
@ -42,6 +46,8 @@ export const Thingtime = (props) => {
const thingtimeRef = React.useRef()
const [showFullPathContext, setShowFullPathContext] = React.useState(false)
const editValueRef = React.useRef({})
const depth = React.useMemo(() => {
@ -65,20 +71,20 @@ export const Thingtime = (props) => {
return "100%"
}, [props?.width, props?.w, render])
const chakras = React.useMemo(() => {
if (!props?.edit && props?.chakra) {
const chakraChild = React.useMemo(() => {
if (!props?.edit && props?.chakraChild) {
return true
}
return false
}, [props?.edit, props?.chakra])
}, [props?.edit, props?.chakraChild])
const pl = React.useMemo(() => {
if (!props.edit && chakras) {
if (!props.edit && chakraChild) {
return [0]
}
return props?.pl || [4, 6]
}, [props?.pl, props?.edit, chakras])
}, [props?.pl, props?.edit, chakraChild])
const pr = React.useMemo(() => {
return props?.pr || (depth === 0 ? [4, 6] : 0)
@ -138,7 +144,7 @@ export const Thingtime = (props) => {
const thing = React.useMemo(() => {
return props.thing
}, [props.thing, childrenRef.current])
}, [props.thing, uuid, childrenRef.current])
const chakra = React.useMemo(() => {
return !props?.edit && typeof thing?.chakra === "string" && thing?.chakra
@ -250,7 +256,7 @@ export const Thingtime = (props) => {
}, [props?.valuePl, props?.path])
const renderableValue = React.useMemo(() => {
if (chakras) {
if (chakraChild) {
return null
}
@ -287,11 +293,7 @@ export const Thingtime = (props) => {
} else {
return "Something.."
}
}, [thing, thingDep, type, chakras, keys])
const hasChakraChildren = React.useMemo(() => {
return !props?.edit && chakra && render && thing?.children
}, [chakra, props?.edit, render, thing?.children])
}, [thing, thingDep, type, chakraChild, keys])
const renderChakra = React.useMemo(() => {
if (!props?.edit && chakra && render) {
@ -366,7 +368,7 @@ export const Thingtime = (props) => {
notRoot
fullPath={fullPath + "." + key?.key}
path={key}
chakraChildren={chakra}
chakraChild={chakra}
thing={nextThing}
// thing={{ infinite: { yes: true } }}
valuePl={pl}
@ -382,6 +384,8 @@ export const Thingtime = (props) => {
console.log("Thingtime is chakra", fullPath, chakra)
const rawChildren = thing?.rawChildren
try {
if (ChakraComponent) {
console.log(
@ -393,6 +397,7 @@ export const Thingtime = (props) => {
const ret = (
<ChakraComponent {...(thing?.props || {})}>
{rawChildren}
{inner}
</ChakraComponent>
)
@ -406,7 +411,7 @@ export const Thingtime = (props) => {
// TODO: Is it safe to spread props
// because having props as a dependency will cause a re-render every time
if (props?.chakraChildren) {
if (props?.chakraChild) {
return inner
}
@ -432,7 +437,7 @@ export const Thingtime = (props) => {
circular,
seen,
type,
props?.chakraChildren,
props?.chakraChild,
props?.path,
props?.edit,
chakra,
@ -460,30 +465,34 @@ export const Thingtime = (props) => {
}, [updateValue])
const onChangeType = React.useCallback(
(type) => {
const newType =
type?.key || type?.value || type?.label || type?.icon || type
(args) => {
const { type, wrap } = args
const typeValue =
typeof type?.value === "function" ? type?.value() : type?.value
if (newType === "object") {
updateValue({ value: {} })
} else if (newType === "array") {
updateValue({ value: [] })
} else if (newType === "string") {
updateValue({ value: "" })
} else if (newType === "number") {
updateValue({ value: 0 })
} else if (newType === "boolean") {
updateValue({ value: false })
} else if (newType === "undefined") {
updateValue({ value: undefined })
} else if (newType === "null") {
updateValue({ value: null })
} else if (newType === "any") {
updateValue({ value: null })
} else {
console.error("Unknown type", newType)
if (type) {
const wrapTarget = type?.wrap
if (wrap && wrapTarget) {
const newValue = append({
thing: typeValue[wrapTarget],
value: thing,
})
typeValue[wrapTarget] = newValue
if (typeValue) {
updateValue({ value: typeValue })
}
} else {
updateValue({ value: typeValue })
}
}
},
[updateValue, thing, append, fullPath]
)
const onWrapType = React.useCallback(
(type) => {
// nothing
},
[updateValue]
)
@ -704,7 +713,7 @@ export const Thingtime = (props) => {
const pathRef = React.useRef(null)
const pathDom = React.useMemo(() => {
if (chakras) {
if (chakraChild) {
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(
(e) => {
@ -740,33 +749,73 @@ export const Thingtime = (props) => {
[uuid]
)
const addNewChild = React.useCallback(() => {
const newChild = null
const addNewChild = React.useCallback(
(args) => {
const { type } = args
const newChild =
typeof type?.value === "function" ? type?.value() : type?.value || null
if (thing instanceof Array) {
// add new child to array
const newValue = [...thing, newChild]
setThingtime(fullPath, newValue)
return
}
if (thing instanceof Array) {
// add new child to array
const newValue = [...thing, newChild]
setThingtime(fullPath, newValue)
return
}
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 newChildFullPath = fullPath + "." + newChildPath
// create new child on thing using setThingtime
setThingtime(newChildFullPath, newChild)
}, [fullPath, setThingtime, thing])
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 newChildFullPath = fullPath + "." + newChildPath
// create new child on thing using setThingtime
setThingtime(newChildFullPath, newChild)
},
[fullPath, setThingtime, thing]
)
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
}
@ -790,7 +839,7 @@ export const Thingtime = (props) => {
data-path={props?.path}
>
{/* {uuid?.current} */}
{!chakras && !chakra && (
{!chakraChild && !chakra && (
<Flex position="relative" flexDirection="row">
<Flex
alignItems="center"
@ -824,7 +873,7 @@ export const Thingtime = (props) => {
uuid={uuid}
fullPath={fullPath}
readonly={!props?.edit}
onChangeType={onChangeType}
onType={onChangeType}
onDelete={deleteValue}
></SettingsMenu>
</Flex>
@ -848,14 +897,37 @@ export const Thingtime = (props) => {
{thingtimeChildren}
{!render && type === "object" && (
<Flex
position="relative"
width="100%"
paddingLeft={multiplyPl(2)}
opacity={props?.edit ? 1 : 0}
cursor="pointer"
transition="all 0.2s ease-out"
onClick={addNewChild}
onMouseEnter={() => {
setShowFullPathContext(true)
setShowNewContextIcon(true)
}}
onMouseLeave={() => {
setShowFullPathContext(false)
setShowNewContextIcon(false)
}}
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
_focus={{
outline: "none !important",
@ -870,6 +942,25 @@ export const Thingtime = (props) => {
size={10}
name="seedling"
></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> */}
</Flex>

View File

@ -103,14 +103,14 @@ export const ThingtimeURL = (props) => {
width="600px"
// width="100%"
maxHeight="100vh"
paddingY={2}
// paddingY={2}
>
<Thingtime
path={path}
thing={thing}
render
chakras={{ marginY: "200px" }}
width="600px"
// width="600px"
></Thingtime>
</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: {
db: {},
limit: 9999,
maxDepth: 10,
maxDepth: 99,
count: 0,
},
things: {},

View File

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

View File

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