feat: feature/commander-updates
This commit is contained in:
parent
21ad6d7ba2
commit
71d9fade5e
@ -1,4 +1,5 @@
|
||||
import React, { createContext } from "react"
|
||||
import { parse, stringify } from "flatted"
|
||||
|
||||
import { sanitise } from "~/functions/sanitise"
|
||||
import { smarts } from "~/smarts"
|
||||
@ -22,7 +23,7 @@ try {
|
||||
|
||||
const force = {
|
||||
settings: {
|
||||
commanderActive: false,
|
||||
// commanderActive: false,
|
||||
},
|
||||
version: 22,
|
||||
}
|
||||
@ -39,7 +40,7 @@ const newVersionData = {
|
||||
|
||||
const initialValues = {
|
||||
settings: {
|
||||
commanderActive: true,
|
||||
commanderActive: false,
|
||||
clearCommanderOnToggle: true,
|
||||
clearCommanderContextOnToggle: true,
|
||||
},
|
||||
@ -54,47 +55,41 @@ const initialValues = {
|
||||
|
||||
const initialThingtime = smarts.merge(initialValues, force)
|
||||
|
||||
// TODO: Make localStorage be loaded first before initialValues if local version exists
|
||||
// and is valid
|
||||
// Issue seems to be server id is different to client hydration
|
||||
|
||||
// let thingtimeToUse = initialThingtime
|
||||
|
||||
// try {
|
||||
// const thingtimeFromLocalStorage = window.localStorage.getItem("thingtime")
|
||||
|
||||
// if (thingtimeFromLocalStorage) {
|
||||
// const parsed = parse(thingtimeFromLocalStorage)
|
||||
// if (parsed) {
|
||||
// const localIsValid = !parsed.version || parsed.version >= force.version
|
||||
// if (localIsValid) {
|
||||
// const newThingtime = smarts.merge(force, parsed)
|
||||
// console.log("nik comm newThingtime", newThingtime)
|
||||
// thingtimeToUse = newThingtime
|
||||
// } else {
|
||||
// const withVersionUpdates = smarts.merge(newVersionData, parsed)
|
||||
// const newThingtime = smarts.merge(force, withVersionUpdates)
|
||||
// thingtimeToUse = newThingtime
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.error("Caught error restoring thingtime from localstorage", err)
|
||||
// }
|
||||
|
||||
export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
const [thingtime, set] = React.useState(smarts.merge(initialValues, force))
|
||||
const [thingtime, set] = React.useState(initialThingtime)
|
||||
|
||||
const thingtimeRef = React.useRef(thingtime)
|
||||
|
||||
// get thingtime from localstorage
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
const thingtimeFromLocalStorage = window.localStorage.getItem("thingtime")
|
||||
|
||||
if (thingtimeFromLocalStorage) {
|
||||
const parsed = JSON.parse(thingtimeFromLocalStorage)
|
||||
if (parsed) {
|
||||
const localIsValid =
|
||||
!parsed.version || parsed.version >= force.version
|
||||
if (localIsValid) {
|
||||
const newThingtime = smarts.merge(force, parsed)
|
||||
console.log("nik comm newThingtime", newThingtime)
|
||||
set(newThingtime)
|
||||
} else {
|
||||
const withVersionUpdates = smarts.merge(newVersionData, parsed)
|
||||
const newThingtime = smarts.merge(force, withVersionUpdates)
|
||||
set(newThingtime)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("There was an error getting thingtime from localStorage")
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
thingtimeRef.current = thingtime
|
||||
|
||||
try {
|
||||
console.log("Setting thingtime to localStorage")
|
||||
window.localStorage.setItem("thingtime", JSON.stringify(thingtime))
|
||||
} catch (err) {
|
||||
console.error("There was an error saving thingtime to localStorage")
|
||||
}
|
||||
}, [thingtime])
|
||||
const stateRef = React.useRef({
|
||||
c: 1,
|
||||
})
|
||||
|
||||
const setThingtime = React.useCallback(
|
||||
(path, value) => {
|
||||
@ -106,7 +101,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
|
||||
// check if first characters of path starts with thingtime or tt and strip from path
|
||||
|
||||
path = sanitise(path)
|
||||
// path = sanitise(path)
|
||||
|
||||
smarts.setsmart(newThingtime, path, value)
|
||||
|
||||
@ -132,21 +127,69 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
const getThingtime = React.useCallback(
|
||||
(...args) => {
|
||||
const rawPath = args[0]
|
||||
if (
|
||||
rawPath === "thingtime" ||
|
||||
rawPath === "tt" ||
|
||||
rawPath === "." ||
|
||||
!rawPath
|
||||
) {
|
||||
return thingtime
|
||||
}
|
||||
const path = sanitise(rawPath)
|
||||
const path = rawPath
|
||||
// do we need to sanitise?
|
||||
// const path = sanitise(rawPath)
|
||||
console.log("Getting thingtime at path", path)
|
||||
return smarts.getsmart(thingtime, path)
|
||||
},
|
||||
[thingtime]
|
||||
)
|
||||
|
||||
const populatePaths = React.useCallback((obj, path, paths, seen = []) => {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const val = obj[key]
|
||||
const newPath = path ? `${path}${path ? "." : ""}${key}` : key
|
||||
if (typeof val === "object") {
|
||||
paths.push(newPath)
|
||||
if (!seen?.includes(val)) {
|
||||
seen.push(val)
|
||||
populatePaths(val, newPath, paths, seen)
|
||||
}
|
||||
} else {
|
||||
paths.push(newPath)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const paths = React.useMemo(() => {
|
||||
// const paths = ["tt", "thingtime", "."]
|
||||
const paths = []
|
||||
|
||||
// populatePaths(thingtime, commandPath)
|
||||
populatePaths(thingtime, "", paths)
|
||||
|
||||
return paths
|
||||
}, [populatePaths, thingtime])
|
||||
|
||||
// get thingtime from localstorage
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
const thingtimeFromLocalStorage = window.localStorage.getItem("thingtime")
|
||||
|
||||
if (thingtimeFromLocalStorage) {
|
||||
const parsed = parse(thingtimeFromLocalStorage)
|
||||
if (parsed) {
|
||||
const localIsValid =
|
||||
!parsed.version || parsed.version >= force.version
|
||||
let newThingtime = null
|
||||
if (localIsValid) {
|
||||
newThingtime = smarts.merge(force, parsed)
|
||||
} else {
|
||||
const withVersionUpdates = smarts.merge(newVersionData, parsed)
|
||||
newThingtime = smarts.merge(force, withVersionUpdates)
|
||||
}
|
||||
console.log("nik setting new thingtime", newThingtime)
|
||||
console.log("nik localIsValid", localIsValid)
|
||||
set(newThingtime)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("There was an error getting thingtime from localStorage")
|
||||
}
|
||||
}, [])
|
||||
|
||||
// thingtime change listener
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
window.setThingtime = setThingtime
|
||||
@ -156,6 +199,36 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
// nothing
|
||||
}
|
||||
|
||||
console.log("nik detected thingtime change", thingtime)
|
||||
|
||||
if (stateRef.current.initialized) {
|
||||
if (thingtime.thingtime !== thingtime || thingtime.tt !== thingtime) {
|
||||
if (!(stateRef?.current?.c >= 10)) {
|
||||
stateRef.current.c++
|
||||
const newThingtime = {
|
||||
...thingtime,
|
||||
}
|
||||
newThingtime.thingtime = newThingtime
|
||||
newThingtime.tt = newThingtime
|
||||
set(newThingtime)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
console.log("Setting thingtime to localStorage", thingtime)
|
||||
setTimeout(() => {
|
||||
const stringified = stringify(thingtime)
|
||||
window.localStorage.setItem("thingtime", stringified)
|
||||
}, 600)
|
||||
} catch (err) {
|
||||
console.error("There was an error saving thingtime to localStorage")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stateRef.current.initialized = true
|
||||
}
|
||||
|
||||
thingtimeRef.current = thingtime
|
||||
|
||||
const keyListener = (e) => {}
|
||||
|
||||
window.addEventListener("keydown", keyListener)
|
||||
@ -170,6 +243,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
setThingtime,
|
||||
getThingtime,
|
||||
thingtimeRef,
|
||||
paths,
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,6 +1,7 @@
|
||||
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"
|
||||
@ -10,16 +11,25 @@ import { sanitise } from "~/functions/sanitise"
|
||||
import { getParentPath } from "~/smarts"
|
||||
|
||||
export const Commander = (props) => {
|
||||
const { thingtime, setThingtime, getThingtime, thingtimeRef } = useThingtime()
|
||||
const { thingtime, setThingtime, getThingtime, thingtimeRef, paths } =
|
||||
useThingtime()
|
||||
|
||||
const inputRef = React.useRef()
|
||||
|
||||
const [value, setValue] = React.useState("")
|
||||
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 [showContext, setShowContextState] = React.useState(false)
|
||||
|
||||
const mobileVW = React.useMemo(() => {
|
||||
return "calc(100vw - 45px)"
|
||||
}, [])
|
||||
|
||||
const rainbowRepeats = 2
|
||||
|
||||
const setShowContext = React.useCallback(
|
||||
(value, from?: string) => {
|
||||
setShowContextState(value)
|
||||
@ -48,7 +58,8 @@ export const Commander = (props) => {
|
||||
document.activeElement.blur()
|
||||
|
||||
if (thingtimeRef?.current?.settings?.clearCommanderOnToggle) {
|
||||
setValue("")
|
||||
setInputValue("")
|
||||
setHoveredSuggestion(null)
|
||||
}
|
||||
if (thingtimeRef?.current?.settings?.clearCommanderContextOnToggle) {
|
||||
setShowContext(false, "commanderActive useEffect")
|
||||
@ -64,13 +75,14 @@ export const Commander = (props) => {
|
||||
commanderActive,
|
||||
thingtimeRef,
|
||||
setShowContext,
|
||||
value,
|
||||
inputValue,
|
||||
contextPath,
|
||||
showContext,
|
||||
])
|
||||
|
||||
const onChange = React.useCallback((e) => {
|
||||
setValue(e.target.value)
|
||||
const onInputChange = React.useCallback((e) => {
|
||||
setInputValue(e.target.value)
|
||||
setHoveredSuggestion(null)
|
||||
}, [])
|
||||
|
||||
const validSetters = React.useMemo(() => {
|
||||
@ -78,7 +90,9 @@ export const Commander = (props) => {
|
||||
}, [])
|
||||
|
||||
const command = React.useMemo(() => {
|
||||
const sanitizedCommand = sanitise(value)
|
||||
// const sanitizedCommand = sanitise(value)
|
||||
// const sanitizedCommand = inputValue
|
||||
const sanitizedCommand = virtualValue
|
||||
|
||||
if (sanitizedCommand?.includes(validSetters[0])) {
|
||||
const indexOfSplitter = sanitizedCommand?.indexOf(validSetters[0])
|
||||
@ -97,10 +111,15 @@ export const Commander = (props) => {
|
||||
}
|
||||
console.log("nik sanitizedCommand", sanitizedCommand)
|
||||
return [sanitizedCommand]
|
||||
}, [value, validSetters])
|
||||
}, [
|
||||
// inputValue,
|
||||
virtualValue,
|
||||
validSetters,
|
||||
])
|
||||
|
||||
const commandPath = React.useMemo(() => {
|
||||
return sanitise(command?.[0])
|
||||
return command?.[0]
|
||||
// return sanitise(command?.[0])
|
||||
}, [command])
|
||||
|
||||
const commandValue = React.useMemo(() => {
|
||||
@ -132,64 +151,33 @@ export const Commander = (props) => {
|
||||
return commandPath && commandValue
|
||||
}, [commandPath, commandValue])
|
||||
|
||||
const showSuggestions = React.useMemo(() => {
|
||||
return inputValue?.length
|
||||
}, [inputValue])
|
||||
|
||||
const suggestions = React.useMemo(() => {
|
||||
if (value?.length) {
|
||||
const suggestions = ["tt", "thingtime", "."]
|
||||
const populateSuggestions = (obj, path) => {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const val = obj[key]
|
||||
const newPath = path ? `${path}${path ? "." : ""}${key}` : key
|
||||
if (typeof val === "object") {
|
||||
suggestions.push(newPath)
|
||||
populateSuggestions(val, newPath)
|
||||
} else {
|
||||
suggestions.push(newPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
const fuse = new Fuse(paths)
|
||||
|
||||
// populateSuggestions(thingtime, commandPath)
|
||||
populateSuggestions(thingtime, "")
|
||||
const results = fuse.search(inputValue)
|
||||
|
||||
// console.log('nik suggestions', suggestions)
|
||||
const mappedResults = results?.map((result) => {
|
||||
return result?.item
|
||||
})
|
||||
|
||||
const removeSuggestions = [contextPath]
|
||||
return mappedResults
|
||||
}, [inputValue, paths])
|
||||
|
||||
let ret = []
|
||||
const selectSuggestion = React.useCallback(
|
||||
(suggestionIdx) => {
|
||||
const suggestion = suggestions?.[suggestionIdx]
|
||||
|
||||
if (commandPath) {
|
||||
const filteredSuggestions = suggestions.filter((suggestion, i) => {
|
||||
return (
|
||||
suggestion?.toLowerCase()?.includes(commandPath?.toLowerCase()) ||
|
||||
suggestion?.includes(commandPath)
|
||||
)
|
||||
})
|
||||
if (!filteredSuggestions?.includes(commandPath)) {
|
||||
const adjustedSuggestions = [commandPath, ...filteredSuggestions]
|
||||
ret = adjustedSuggestions
|
||||
} else {
|
||||
ret = filteredSuggestions
|
||||
}
|
||||
} else {
|
||||
ret = suggestions
|
||||
}
|
||||
|
||||
const filtered = ret.filter((suggestion) => {
|
||||
return !removeSuggestions?.includes(suggestion)
|
||||
})
|
||||
|
||||
console.log("nik useEffect suggestions commandPath", commandPath)
|
||||
console.log("nik useEffect suggestions ret", ret)
|
||||
console.log("nik useEffect suggestions suggestions", suggestions)
|
||||
console.log("nik useEffect suggestions filtered", filtered)
|
||||
|
||||
return filtered
|
||||
|
||||
// if (value) {
|
||||
// setShowContext(true, 'Thingtime changes update suggestions')
|
||||
// }
|
||||
}
|
||||
}, [value, thingtime, commandPath, contextPath])
|
||||
setInputValue(suggestion)
|
||||
setHoveredSuggestion(null)
|
||||
setContextPath(suggestion)
|
||||
setShowContext(true, "Select suggestion")
|
||||
},
|
||||
[setInputValue, setContextPath, setShowContext, suggestions]
|
||||
)
|
||||
|
||||
const commandContainsPath = React.useMemo(() => {
|
||||
console.log("nik command", commandPath)
|
||||
@ -202,83 +190,17 @@ export const Commander = (props) => {
|
||||
return commandIncludesSuggestion
|
||||
}, [commandPath, suggestions])
|
||||
|
||||
const onEnter = React.useCallback(
|
||||
(props) => {
|
||||
// if first characters of value equal tt. then run command
|
||||
// or if first character is a dot then run command
|
||||
try {
|
||||
console.log("nik Commander onEnter")
|
||||
console.log("nik commandIsAction", commandIsAction)
|
||||
console.log("nik commandContainsPath", commandContainsPath)
|
||||
|
||||
console.log("nik onEnter commandPath", commandPath)
|
||||
|
||||
if (commandIsAction) {
|
||||
// nothing
|
||||
try {
|
||||
const fn = `() => { return ${escapedCommandValue} }`
|
||||
const evalFn = eval(fn)
|
||||
const realVal = evalFn()
|
||||
const prevVal = getThingtime(commandPath)
|
||||
const parentPath = getParentPath(commandPath)
|
||||
setThingtime(commandPath, realVal)
|
||||
if (!prevVal) {
|
||||
setContextPath(parentPath)
|
||||
setShowContext(true, "commandIsAction check")
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("setThingtime errored in Commander", err)
|
||||
}
|
||||
} else if (commandContainsPath) {
|
||||
// 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)
|
||||
}
|
||||
},
|
||||
[
|
||||
setShowContext,
|
||||
escapedCommandValue,
|
||||
setThingtime,
|
||||
getThingtime,
|
||||
commandIsAction,
|
||||
commandPath,
|
||||
commandContainsPath,
|
||||
]
|
||||
)
|
||||
|
||||
// trigger on enter
|
||||
const onKeyDown = React.useCallback(
|
||||
(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
onEnter({ e })
|
||||
// setThingtime(
|
||||
// 'settings.commanderActive',
|
||||
// !thingtime?.settings?.commanderActive
|
||||
// )
|
||||
}
|
||||
},
|
||||
[onEnter]
|
||||
)
|
||||
|
||||
const openCommander = React.useCallback(() => {
|
||||
console.log("nik commander opening commander")
|
||||
setThingtime("settings.commanderActive", true)
|
||||
}, [setThingtime])
|
||||
|
||||
const closeCommander = React.useCallback(
|
||||
(e) => {
|
||||
(e?: any) => {
|
||||
console.log("nik e", e)
|
||||
if (thingtime?.settings?.commanderActive) {
|
||||
console.log("nik commander closing commander")
|
||||
console.log("nik setting commanderActive to false")
|
||||
setThingtime("settings.commanderActive", false)
|
||||
}
|
||||
},
|
||||
@ -293,51 +215,122 @@ export const Commander = (props) => {
|
||||
}
|
||||
}, [thingtime?.settings?.commanderActive, closeCommander, openCommander])
|
||||
|
||||
React.useEffect(() => {
|
||||
const keyListener = (e: any) => {
|
||||
const allCommanderKeyListener = React.useCallback(
|
||||
(e: any) => {
|
||||
console.log("commander key listener e?.code", e?.code)
|
||||
if (e?.metaKey && e?.code === "KeyP") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
toggleCommander()
|
||||
}
|
||||
// if key escape close all modals
|
||||
console.log("commander key listener e?.code", e?.code)
|
||||
if (e?.code === "Escape") {
|
||||
else if (e?.code === "Escape") {
|
||||
closeCommander()
|
||||
}
|
||||
}
|
||||
// if arrow keys then move selection
|
||||
else 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") {
|
||||
// if selection is active then select it
|
||||
const curSuggestionIdx = hoveredSuggestion
|
||||
if (curSuggestionIdx !== null) {
|
||||
selectSuggestion(curSuggestionIdx)
|
||||
}
|
||||
if (commanderActive) {
|
||||
try {
|
||||
console.log("nik Commander onEnter")
|
||||
console.log("nik commandIsAction", commandIsAction)
|
||||
console.log("nik commandContainsPath", commandContainsPath)
|
||||
|
||||
window.addEventListener("keydown", keyListener)
|
||||
console.log("nik onEnter commandPath", commandPath)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", keyListener)
|
||||
}
|
||||
}, [setThingtime, thingtime, toggleCommander, closeCommander])
|
||||
if (commandIsAction) {
|
||||
// nothing
|
||||
try {
|
||||
const fn = `() => { return ${escapedCommandValue} }`
|
||||
const evalFn = eval(fn)
|
||||
const realVal = evalFn()
|
||||
const prevVal = getThingtime(commandPath)
|
||||
const parentPath = getParentPath(commandPath)
|
||||
setThingtime(commandPath, realVal)
|
||||
if (!prevVal) {
|
||||
setContextPath(parentPath)
|
||||
setShowContext(true, "commandIsAction check")
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("setThingtime errored in Commander", err)
|
||||
}
|
||||
} else if (commandContainsPath) {
|
||||
// const prevValue = getThingtime(commandPath)
|
||||
|
||||
const selectSuggestion = React.useCallback(
|
||||
(suggestion) => {
|
||||
setValue(suggestion)
|
||||
setContextPath(suggestion)
|
||||
setShowContext(true, "Select suggestion")
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[setValue, setContextPath, setShowContext]
|
||||
[
|
||||
closeCommander,
|
||||
toggleCommander,
|
||||
hoveredSuggestion,
|
||||
selectSuggestion,
|
||||
suggestions,
|
||||
commanderActive,
|
||||
commandIsAction,
|
||||
commandContainsPath,
|
||||
commandPath,
|
||||
escapedCommandValue,
|
||||
getThingtime,
|
||||
setThingtime,
|
||||
setShowContext,
|
||||
]
|
||||
)
|
||||
|
||||
const excludedSuggestions = React.useMemo(() => {
|
||||
return ["."]
|
||||
}, [])
|
||||
React.useEffect(() => {
|
||||
window.addEventListener("keydown", allCommanderKeyListener)
|
||||
|
||||
const renderedSuggestions = React.useMemo(() => {
|
||||
return suggestions?.filter((suggestion) => {
|
||||
return !excludedSuggestions?.includes(suggestion)
|
||||
})
|
||||
}, [suggestions, excludedSuggestions])
|
||||
return () => {
|
||||
window.removeEventListener("keydown", allCommanderKeyListener)
|
||||
}
|
||||
}, [allCommanderKeyListener])
|
||||
|
||||
const mobileVW = React.useMemo(() => {
|
||||
return "calc(100vw - 45px)"
|
||||
}, [])
|
||||
React.useEffect(() => {
|
||||
if (typeof hoveredSuggestion === "number") {
|
||||
setVirtualValue(suggestions?.[hoveredSuggestion])
|
||||
} else {
|
||||
setVirtualValue(inputValue)
|
||||
}
|
||||
}, [hoveredSuggestion, inputValue, suggestions])
|
||||
|
||||
const rainbowRepeats = 2
|
||||
React.useEffect(() => {
|
||||
setVirtualValue(inputValue)
|
||||
}, [inputValue])
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={closeCommander}>
|
||||
@ -374,7 +367,7 @@ export const Commander = (props) => {
|
||||
>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
display={renderedSuggestions?.length ? "flex" : "none"}
|
||||
display={showSuggestions ? "flex" : "none"}
|
||||
width={["100%", "400px"]}
|
||||
maxWidth="100%"
|
||||
marginBottom={3}
|
||||
@ -382,17 +375,20 @@ export const Commander = (props) => {
|
||||
borderRadius="12px"
|
||||
pointerEvents="all"
|
||||
id="commander-suggestions"
|
||||
onMouseLeave={() => setHoveredSuggestion(null)}
|
||||
paddingY={3}
|
||||
>
|
||||
{renderedSuggestions?.map((suggestion, i) => {
|
||||
{suggestions?.map((suggestion, i) => {
|
||||
return (
|
||||
<Flex
|
||||
key={i}
|
||||
background={hoveredSuggestion === i ? "greys.lightt" : null}
|
||||
_hover={{
|
||||
bg: "greys.lightt",
|
||||
background: "greys.lightt",
|
||||
}}
|
||||
cursor="pointer"
|
||||
onClick={() => selectSuggestion(suggestion)}
|
||||
onClick={() => selectSuggestion(i)}
|
||||
onMouseEnter={() => setHoveredSuggestion(i)}
|
||||
paddingX={4}
|
||||
>
|
||||
{suggestion}
|
||||
@ -458,11 +454,10 @@ export const Commander = (props) => {
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={onChange}
|
||||
onChange={onInputChange}
|
||||
onFocus={openCommander}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Imagine.."
|
||||
value={value}
|
||||
value={inputValue}
|
||||
></Input>
|
||||
</Center>
|
||||
</Rainbow>
|
||||
|
@ -73,7 +73,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
|
||||
const keyframe = `${(i / (repeatedColours.length - 1)) * 100}%`
|
||||
ret[keyframe] = {
|
||||
fill: colour,
|
||||
"animation-timing-function": "ease-out",
|
||||
animationTimingFunction: "ease-out",
|
||||
stroke: colour,
|
||||
}
|
||||
})
|
||||
@ -108,10 +108,8 @@ export const Rainbow = (allProps: any): JSX.Element => {
|
||||
}, [state?.width, state?.height])
|
||||
|
||||
const svg = React.useMemo(() => {
|
||||
const id = Math.random().toString(36).substring(2, 15)
|
||||
|
||||
return (
|
||||
<Box width="100%" height="100%" id={id}>
|
||||
<Box width="100%" height="100%" id={"rainbow-svg-container-" + uuid}>
|
||||
<svg
|
||||
overflow="visible"
|
||||
viewBox={`0 0 ${state?.width || 100} ${state?.height || 100}`}
|
||||
@ -130,7 +128,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
|
||||
</svg>
|
||||
</Box>
|
||||
)
|
||||
}, [state, rect])
|
||||
}, [state, rect, uuid])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (uuid) {
|
||||
|
@ -21,7 +21,7 @@ export const Thingtime = (props) => {
|
||||
|
||||
const pr = React.useMemo(() => {
|
||||
return props?.pr || (depth === 1 ? [4, 6] : 0)
|
||||
}, [props?.pr])
|
||||
}, [props?.pr, depth])
|
||||
|
||||
// will only run on the client
|
||||
React.useEffect(() => {
|
||||
@ -34,6 +34,21 @@ export const Thingtime = (props) => {
|
||||
return props.thing
|
||||
}, [props.thing])
|
||||
|
||||
const seen = React.useMemo(() => {
|
||||
if (props?.seen instanceof Array) {
|
||||
if (props?.seen?.includes(thing)) {
|
||||
return props?.seen
|
||||
} else if (typeof thing === "object") {
|
||||
return [...props.seen, thing]
|
||||
}
|
||||
return props?.seen || []
|
||||
}
|
||||
if (typeof thing === "object") {
|
||||
return [thing]
|
||||
}
|
||||
return []
|
||||
}, [props?.seen, thing])
|
||||
|
||||
const mode = React.useMemo(() => {
|
||||
return "view"
|
||||
}, [])
|
||||
@ -67,7 +82,7 @@ export const Thingtime = (props) => {
|
||||
const trimmed = thing.trim()
|
||||
|
||||
if (!trimmed) {
|
||||
return "Empty string"
|
||||
return ""
|
||||
}
|
||||
return trimmed
|
||||
} else if (type === "number") {
|
||||
@ -85,11 +100,11 @@ export const Thingtime = (props) => {
|
||||
try {
|
||||
return JSON.stringify(thing, null, 2)
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Caught error making renderableValue of thing",
|
||||
err,
|
||||
thing
|
||||
)
|
||||
// console.error(
|
||||
// "Caught error making renderableValue of thing",
|
||||
// err,
|
||||
// thing
|
||||
// )
|
||||
return "Circular reference in object."
|
||||
}
|
||||
} else {
|
||||
@ -145,7 +160,7 @@ export const Thingtime = (props) => {
|
||||
const template1Modes = ["view", "edit"]
|
||||
|
||||
if (template1Modes?.includes(mode)) {
|
||||
if (keys?.length) {
|
||||
if (keys?.length && !props?.circular) {
|
||||
value = (
|
||||
<Safe {...props}>
|
||||
<Flex
|
||||
@ -168,9 +183,17 @@ export const Thingtime = (props) => {
|
||||
|
||||
const nextThing = thing[key?.key]
|
||||
|
||||
const nextSeen = [...seen]
|
||||
|
||||
if (typeof nextThing === "object") {
|
||||
nextSeen.push(nextThing)
|
||||
}
|
||||
|
||||
return (
|
||||
<Thingtime
|
||||
key={idx}
|
||||
seen={nextSeen}
|
||||
circular={seen?.includes?.(nextThing)}
|
||||
depth={depth + 1}
|
||||
parent={thing}
|
||||
path={key}
|
||||
@ -272,7 +295,7 @@ export const Thingtime = (props) => {
|
||||
onMouseLeave={handleMouseEvent}
|
||||
// minW={depth === 1 ? '120px' : null}
|
||||
paddingY={3}
|
||||
{...props}
|
||||
{...(props.chakras || {})}
|
||||
className={`thing-${uuid?.current}`}
|
||||
>
|
||||
{/* {uuid?.current} */}
|
||||
|
@ -1,25 +1,25 @@
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import React from "react"
|
||||
import { Text } from "@chakra-ui/react"
|
||||
|
||||
export const RainbowText = props => {
|
||||
export const RainbowText = (props) => {
|
||||
return (
|
||||
<Text
|
||||
as='h1'
|
||||
userSelect={'none'}
|
||||
position='relative'
|
||||
fontSize='6xl'
|
||||
fontWeight='bold'
|
||||
backgroundClip={'text'}
|
||||
color='transparent'
|
||||
bgGradient='linear-gradient(to right, #f34a4a, #ffbc48, #58ca70, #47b5e6, #a555e8, #f34a4a)'
|
||||
backgroundSize='200%'
|
||||
as="h1"
|
||||
sx={{
|
||||
'@keyframes moving-rainbow': {
|
||||
'0%': { backgroundPosition: '0 0' },
|
||||
'100%': { backgroundPosition: '200% 0' }
|
||||
"@keyframes moving-rainbow": {
|
||||
"0%": { backgroundPosition: "0 0" },
|
||||
"100%": { backgroundPosition: "200% 0" },
|
||||
},
|
||||
animation: 'moving-rainbow 5s infinite linear'
|
||||
animation: "moving-rainbow 5s infinite linear",
|
||||
}}
|
||||
position="relative"
|
||||
color="transparent"
|
||||
fontSize="6xl"
|
||||
fontWeight="bold"
|
||||
backgroundSize="200%"
|
||||
bgGradient="linear-gradient(to right, #f34a4a, #ffbc48, #58ca70, #47b5e6, #a555e8, #f34a4a)"
|
||||
backgroundClip="text"
|
||||
userSelect="none"
|
||||
>
|
||||
{props?.children}
|
||||
</Text>
|
||||
|
@ -1,26 +1,15 @@
|
||||
export const sanitise = (str) => {
|
||||
let ret = ""
|
||||
return str
|
||||
|
||||
// TODO: maybe sanitise
|
||||
|
||||
const ret = ""
|
||||
|
||||
const value = str?.trim()
|
||||
|
||||
const sanitised = ["tt", "thingtime"]
|
||||
// remove any leading "tt" or "thingtime"
|
||||
|
||||
const suffixes = [".", " "]
|
||||
|
||||
// find combination of sanitised + suffixes which str starts with
|
||||
|
||||
const match = sanitised.find((s) => value?.startsWith(s))
|
||||
const withSuffix = suffixes.find(
|
||||
(s) => match?.length && value[match?.length] === s
|
||||
)
|
||||
const noSuffix = match && value?.length === match?.length
|
||||
|
||||
if (match && withSuffix) {
|
||||
ret = value?.slice(match.length + 1)
|
||||
} else if (!noSuffix) {
|
||||
ret = value
|
||||
}
|
||||
console.log("nik thingtime sanitis 1", match, withSuffix, ret)
|
||||
const toSanitise = ["tt", "thingtime"]
|
||||
|
||||
return ret
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { ChakraWrapper } from './Providers/Chakra/ChakraWrapper'
|
||||
// import type { MetaFunction } from "@vercel/remix"
|
||||
import {
|
||||
Links,
|
||||
@ -6,24 +5,26 @@ import {
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration
|
||||
} from '@remix-run/react'
|
||||
import { Analytics } from '@vercel/analytics/react'
|
||||
import { Main } from './components/Layout/Main'
|
||||
import { ThingtimeProvider } from './Providers/ThingtimeProvider'
|
||||
ScrollRestoration,
|
||||
} from "@remix-run/react"
|
||||
import { Analytics } from "@vercel/analytics/react"
|
||||
|
||||
function Document ({
|
||||
import { Main } from "./components/Layout/Main"
|
||||
import { ChakraWrapper } from "./Providers/Chakra/ChakraWrapper"
|
||||
import { ThingtimeProvider } from "./Providers/ThingtimeProvider"
|
||||
|
||||
function Document({
|
||||
children,
|
||||
title = 'Thingtime'
|
||||
title = "Thingtime",
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
title?: string
|
||||
}) {
|
||||
return (
|
||||
<html lang='en'>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet='utf-8' />
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1' />
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<Meta />
|
||||
<title>{title}</title>
|
||||
<Links />
|
||||
@ -39,7 +40,7 @@ function Document ({
|
||||
)
|
||||
}
|
||||
|
||||
export default function App () {
|
||||
export default function App() {
|
||||
return (
|
||||
<Document>
|
||||
<ChakraWrapper>
|
||||
@ -54,7 +55,7 @@ export default function App () {
|
||||
}
|
||||
|
||||
// limiter
|
||||
const setThingtime = glob => {
|
||||
const setThingtime = (glob) => {
|
||||
try {
|
||||
glob.thingtime = {
|
||||
tmp: {},
|
||||
@ -64,8 +65,8 @@ const setThingtime = glob => {
|
||||
db: {},
|
||||
limit: 9999,
|
||||
maxDepth: 10,
|
||||
count: 0
|
||||
}
|
||||
count: 0,
|
||||
},
|
||||
}
|
||||
} catch (err) {
|
||||
// will error on server
|
||||
|
@ -20,13 +20,20 @@ export default function Index() {
|
||||
{/* <Box paddingTop={200}></Box> */}
|
||||
<Splash></Splash>
|
||||
<Thingtime
|
||||
marginBottom={200}
|
||||
chakras={{
|
||||
marginBottom: 200,
|
||||
}}
|
||||
width="600px"
|
||||
path="Content"
|
||||
valuePl={0}
|
||||
thing={thingtime["Content"]}
|
||||
></Thingtime>
|
||||
<ThingtimeDemo></ThingtimeDemo>
|
||||
<Thingtime
|
||||
thing={thingtime}
|
||||
chakras={{ marginY: 200 }}
|
||||
width="600px"
|
||||
></Thingtime>
|
||||
<ProfileDrawer></ProfileDrawer>
|
||||
</Flex>
|
||||
)
|
||||
|
@ -14,6 +14,8 @@
|
||||
"@remix-run/serve": "^1.15.0",
|
||||
"@vercel/analytics": "^0.1.11",
|
||||
"@vercel/remix": "^1.15.0",
|
||||
"flatted": "^3.2.7",
|
||||
"fuse.js": "^6.6.2",
|
||||
"gradient-path": "^2.3.0",
|
||||
"isbot": "latest",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
@ -23,6 +23,12 @@ dependencies:
|
||||
'@vercel/remix':
|
||||
specifier: ^1.15.0
|
||||
version: 1.15.0(react-dom@18.2.0)(react@18.2.0)
|
||||
flatted:
|
||||
specifier: ^3.2.7
|
||||
version: 3.2.7
|
||||
fuse.js:
|
||||
specifier: ^6.6.2
|
||||
version: 6.6.2
|
||||
gradient-path:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
@ -6087,7 +6093,6 @@ packages:
|
||||
|
||||
/flatted@3.2.7:
|
||||
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
|
||||
dev: true
|
||||
|
||||
/focus-lock@0.11.6:
|
||||
resolution: {integrity: sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==}
|
||||
@ -6204,6 +6209,11 @@ packages:
|
||||
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
||||
dev: true
|
||||
|
||||
/fuse.js@6.6.2:
|
||||
resolution: {integrity: sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/generic-names@4.0.0:
|
||||
resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user