feat: feature/rainbow-component Added rainbow animation to commander

This commit is contained in:
Nikolaj Frey 2023-07-05 20:05:35 +10:00
parent 302302fa8f
commit e30dc3f56c
11 changed files with 944 additions and 751 deletions

View File

@ -17,7 +17,14 @@ module.exports = {
version: "detect", version: "detect",
}, },
}, },
plugins: ["@typescript-eslint", "react", "prettier", "unused-imports", "simple-import-sort", "chakra-ui"], plugins: [
"@typescript-eslint",
"react",
// "unused-imports",
"prettier",
"simple-import-sort",
"chakra-ui",
],
extends: [ extends: [
"@remix-run/eslint-config", "@remix-run/eslint-config",
"eslint:recommended", "eslint:recommended",
@ -26,7 +33,10 @@ module.exports = {
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
], ],
rules: { rules: {
"react/jsx-curly-brace-presence": ["error", { props: "never", children: "never" }], "react/jsx-curly-brace-presence": [
"error",
{ props: "never", children: "never" },
],
"no-async-promise-executor": "off", "no-async-promise-executor": "off",
"react/prop-types": "off", "react/prop-types": "off",
"react/display-name": "off", "react/display-name": "off",
@ -35,8 +45,8 @@ module.exports = {
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "error", // "unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": "error", // "unused-imports/no-unused-vars": "error",
"react/react-in-jsx-scope": "off", "react/react-in-jsx-scope": "off",
"chakra-ui/props-order": "error", "chakra-ui/props-order": "error",
"chakra-ui/props-shorthand": [ "chakra-ui/props-shorthand": [

View File

@ -20,7 +20,14 @@ try {
// nothing // nothing
} }
const forceable = { const force = {
settings: {
commanderActive: true,
},
version: 22,
}
const newVersionData = {
Content: { Content: {
hidden1: "Edit this to your heart's desire.", hidden1: "Edit this to your heart's desire.",
"How?": "Just search for Content and edit the value to whatever you want.", "How?": "Just search for Content and edit the value to whatever you want.",
@ -30,26 +37,25 @@ const forceable = {
}, },
} }
const initialThingtime = { const initialValues = {
nav: {},
version: 22,
...forceable,
}
const userData = {
settings: { settings: {
showCommander: true, commanderActive: true,
clearCommanderOnToggle: true, clearCommanderOnToggle: true,
clearCommanderContextOnToggle: true, clearCommanderContextOnToggle: true,
}, },
...forceable, Content: {
hidden1: "Edit this to your heart's desire.",
"How?": "Just search for Content and edit the value to whatever you want.",
"Example:": `Content = New Content!
Content.Nested Content = New Nested Content!
`,
},
} }
const initialThingtime = smarts.merge(initialValues, force)
export const ThingtimeProvider = (props: any): JSX.Element => { export const ThingtimeProvider = (props: any): JSX.Element => {
const [thingtime, set] = React.useState({ const [thingtime, set] = React.useState(smarts.merge(initialValues, force))
...initialThingtime,
...userData,
})
const thingtimeRef = React.useRef(thingtime) const thingtimeRef = React.useRef(thingtime)
@ -61,15 +67,15 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
if (thingtimeFromLocalStorage) { if (thingtimeFromLocalStorage) {
const parsed = JSON.parse(thingtimeFromLocalStorage) const parsed = JSON.parse(thingtimeFromLocalStorage)
if (parsed) { if (parsed) {
const invalidLocalStorage = const localIsValid =
!parsed.version || parsed.version < initialThingtime.version !parsed.version || parsed.version >= force.version
if (!invalidLocalStorage) { if (localIsValid) {
set(parsed) const newThingtime = smarts.merge(force, parsed)
console.log("nik comm newThingtime", newThingtime)
set(newThingtime)
} else { } else {
const newThingtime = { const withVersionUpdates = smarts.merge(newVersionData, parsed)
...parsed, const newThingtime = smarts.merge(force, withVersionUpdates)
...initialThingtime,
}
set(newThingtime) set(newThingtime)
} }
} }
@ -144,6 +150,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
try { try {
window.setThingtime = setThingtime window.setThingtime = setThingtime
window.thingtime = thingtime window.thingtime = thingtime
window.tt = thingtime
} catch { } catch {
// nothing // nothing
} }

View File

@ -2,6 +2,7 @@ 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 { Rainbow } from "../Rainbow/Rainbow"
import { Thingtime } from "../Thingtime/Thingtime" import { Thingtime } from "../Thingtime/Thingtime"
import { useThingtime } from "../Thingtime/useThingtime" import { useThingtime } from "../Thingtime/useThingtime"
@ -14,7 +15,7 @@ export const Commander = (props) => {
const inputRef = React.useRef() const inputRef = React.useRef()
const [value, setValue] = React.useState("") const [value, setValue] = React.useState("")
const [active, setActive] = React.useState(false)
const [contextPath, setContextPath] = React.useState() const [contextPath, setContextPath] = React.useState()
const [showContext, setShowContextState] = React.useState(false) const [showContext, setShowContextState] = React.useState(false)
@ -33,23 +34,25 @@ export const Commander = (props) => {
return ret return ret
}, [contextPath, getThingtime]) }, [contextPath, getThingtime])
const showCommander = React.useMemo(() => { const commanderActive = React.useMemo(() => {
return thingtime?.settings?.showCommander return thingtime?.settings?.commanderActive
}, [thingtime?.settings?.showCommander]) }, [thingtime?.settings?.commanderActive])
// showCommander useEffect console.log("nik commanderActive", commanderActive)
// commanderActive useEffect
React.useEffect(() => { React.useEffect(() => {
if (showCommander) { if (commanderActive) {
inputRef?.current?.focus?.() inputRef?.current?.focus?.()
} else { } else {
if (thingtimeRef?.current?.settings?.clearCommanderOnToggle) { if (thingtimeRef?.current?.settings?.clearCommanderOnToggle) {
setValue("") setValue("")
} }
if (thingtimeRef?.current?.settings?.clearCommanderContextOnToggle) { if (thingtimeRef?.current?.settings?.clearCommanderContextOnToggle) {
setShowContext(false, "showCommander useEffect") setShowContext(false, "commanderActive useEffect")
} }
} }
}, [showCommander, thingtimeRef, setShowContext]) }, [commanderActive, thingtimeRef, setShowContext])
const onChange = React.useCallback((e) => { const onChange = React.useCallback((e) => {
setValue(e.target.value) setValue(e.target.value)
@ -165,7 +168,7 @@ export const Commander = (props) => {
// setShowContext(true, 'Thingtime changes update suggestions') // setShowContext(true, 'Thingtime changes update suggestions')
// } // }
} }
}, [value, thingtime, commandPath, setShowContext]) }, [value, thingtime, commandPath])
const onEnter = React.useCallback( const onEnter = React.useCallback(
(props) => { (props) => {
@ -202,6 +205,7 @@ export const Commander = (props) => {
setShowContext, setShowContext,
escapedCommandValue, escapedCommandValue,
setThingtime, setThingtime,
getThingtime,
commandIsAction, commandIsAction,
commandPath, commandPath,
commandContainsPath, commandContainsPath,
@ -216,8 +220,8 @@ export const Commander = (props) => {
e.stopPropagation() e.stopPropagation()
onEnter({ e }) onEnter({ e })
// setThingtime( // setThingtime(
// 'settings.showCommander', // 'settings.commanderActive',
// !thingtime?.settings?.showCommander // !thingtime?.settings?.commanderActive
// ) // )
} }
}, },
@ -225,13 +229,18 @@ export const Commander = (props) => {
) )
const openCommander = React.useCallback(() => { const openCommander = React.useCallback(() => {
setThingtime("settings.showCommander", true) console.log("nik commander opening commander")
setThingtime("settings.commanderActive", true)
}, [setThingtime]) }, [setThingtime])
const closeCommander = React.useCallback(() => { const closeCommander = React.useCallback(() => {
if (thingtime?.settings?.showCommander) { if (thingtime?.settings?.commanderActive) {
setThingtime("settings.showCommander", false) console.log("nik commander closing commander")
setThingtime("settings.commanderActive", false)
} }
document.activeElement.blur()
if (value !== "") { if (value !== "") {
setValue("") setValue("")
} }
@ -241,15 +250,22 @@ export const Commander = (props) => {
if (showContext !== false) { if (showContext !== false) {
setShowContext(false) setShowContext(false)
} }
}, [setThingtime, setShowContext, value, contextPath, showContext]) }, [
setThingtime,
setShowContext,
value,
contextPath,
showContext,
thingtime?.settings?.commanderActive,
])
const toggleCommander = React.useCallback(() => { const toggleCommander = React.useCallback(() => {
if (thingtime?.settings?.showCommander) { if (thingtime?.settings?.commanderActive) {
closeCommander() closeCommander()
} else { } else {
openCommander() openCommander()
} }
}, [thingtime?.settings?.showCommander, closeCommander, openCommander]) }, [thingtime?.settings?.commanderActive, closeCommander, openCommander])
React.useEffect(() => { React.useEffect(() => {
const keyListener = (e: any) => { const keyListener = (e: any) => {
@ -295,11 +311,13 @@ export const Commander = (props) => {
return "calc(100vw - 45px)" return "calc(100vw - 45px)"
}, []) }, [])
const rainbowRepeats = 1
return ( return (
<ClickAwayListener onClickAway={closeCommander}> <ClickAwayListener onClickAway={closeCommander}>
<Flex <Flex
position="absolute" position="absolute"
// display={['flex', showCommander ? 'flex' : 'none']} // display={['flex', commanderActive ? 'flex' : 'none']}
top={0} top={0}
// zIndex={99999} // zIndex={99999}
// position='fixed' // position='fixed'
@ -368,6 +386,13 @@ export const Commander = (props) => {
<Thingtime thing={contextValue}></Thingtime> <Thingtime thing={contextValue}></Thingtime>
</Flex> </Flex>
</Flex> </Flex>
<Rainbow
filter="blur(15px)"
opacity={commanderActive ? 0.25 : 0}
repeats={rainbowRepeats}
thickness={8}
overflow="visible"
>
<Center <Center
position="relative" position="relative"
overflow="hidden" overflow="hidden"
@ -375,11 +400,18 @@ export const Commander = (props) => {
maxWidth={[mobileVW, "100%"]} maxWidth={[mobileVW, "100%"]}
height="100%" height="100%"
padding="1px" padding="1px"
background="grey"
borderRadius="6px" borderRadius="6px"
pointerEvents="all" pointerEvents="all"
outline="none" outline="none"
> >
<Rainbow
opacity={commanderActive ? 0.5 : 0}
position="absolute"
expand
repeats={rainbowRepeats}
opacityTransition="all 3000ms ease"
thickness={10}
></Rainbow>
<Input <Input
// display='none' // display='none'
// opacity={0} // opacity={0}
@ -391,15 +423,18 @@ export const Commander = (props) => {
}} }}
width="100%" width="100%"
height="100%" height="100%"
background="grey"
border="none" border="none"
borderRadius="5px" borderRadius="5px"
outline="none" outline="none"
onChange={onChange} onChange={onChange}
onFocus={openCommander}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
placeholder={"What's on your mind?"} placeholder={"What's on your mind?"}
value={value} value={value}
></Input> ></Input>
</Center> </Center>
</Rainbow>
</Flex> </Flex>
</ClickAwayListener> </ClickAwayListener>
) )

View File

@ -2,25 +2,56 @@ import React from "react"
import { Box, Center } from "@chakra-ui/react" import { Box, Center } from "@chakra-ui/react"
import { GradientPath } from "~/gp/GradientPath" import { GradientPath } from "~/gp/GradientPath"
import { useProps } from "~/hooks/useProps"
import { useTrace } from "~/hooks/useTrace"
import { useUuid } from "~/hooks/useUuid"
export const Rainbow = (props: any): JSX.Element => { export const Rainbow = (allProps: any): JSX.Element => {
const rainbow = ["#f34a4a", "#ffbc48", "#58ca70", "#47b5e6", "#a555e8"] const rainbow = ["#f34a4a", "#ffbc48", "#58ca70", "#47b5e6", "#a555e8"]
const props = useProps(allProps)
const uuid = useUuid()
const [hidden, setHidden] = React.useState(true)
const repeats = props?.repeats || 1
const [filter, setFilter] = React.useState(props?.filter)
const opacity = props?.opacity !== undefined ? props?.opacity : 1
const [colors, setColors] = React.useState(props?.colors || rainbow) const [colors, setColors] = React.useState(props?.colors || rainbow)
const [pathWidth, setPathWidth] = React.useState(props?.thickness || 1)
const [overflow, setOverflow] = React.useState(props?.overflow || "hidden")
const [opacityTransition, setOpacityTransition] = React.useState(
props?.opacityTransition || "all 10000ms ease"
)
const parentRef = React.useRef(null)
const repeatedColours = React.useMemo(() => { const repeatedColours = React.useMemo(() => {
return [...colors, colors[0]] const ret = []
}, [colors])
for (let i = 0; i < repeats; i++) {
ret.push(...colors)
}
ret.push(colors[0])
return ret
}, [colors, repeats])
// make SVG that takes makes path in the shape of a box // make SVG that takes makes path in the shape of a box
// which adjusts to the parent containers size // which adjusts to the parent containers size
const [state, setState] = React.useState({
width: 100,
height: 100,
})
const [strokeWidth, setStrokeWidth] = React.useState(1) const [strokeWidth, setStrokeWidth] = React.useState(1)
const [extraStroke, setExtraStroke] = React.useState(0) const [extraStroke, setExtraStroke] = React.useState(0)
const [width, setWidth] = React.useState(props?.width || "105%") const [width, setWidth] = React.useState(props?.width || "100%")
const [height, setHeight] = React.useState(props?.height || "105%") const [height, setHeight] = React.useState(props?.height || "100%")
const pathString = React.useMemo(() => { const pathString = React.useMemo(() => {
const startPoint = 0 + strokeWidth / 2 const startPoint = 0 + strokeWidth / 2
@ -29,10 +60,6 @@ export const Rainbow = (props: any): JSX.Element => {
return `M -${4} ${startPoint} H ${endPoint} V ${endPoint} H ${startPoint} V ${startPoint}` return `M -${4} ${startPoint} H ${endPoint} V ${endPoint} H ${startPoint} V ${startPoint}`
}, [strokeWidth]) }, [strokeWidth])
const viewBox = React.useMemo(() => {
return `0 0 100 100`
}, [])
const svgRef = React.useRef(null) const svgRef = React.useRef(null)
const colourKeyframes = React.useMemo(() => { const colourKeyframes = React.useMemo(() => {
@ -49,56 +76,45 @@ export const Rainbow = (props: any): JSX.Element => {
return ret return ret
}, [repeatedColours]) }, [repeatedColours])
const colourClasses = React.useMemo(() => { React.useEffect(() => {
const ret = {} const updateChildSize = () => {
const { width, height } = parentRef?.current?.getBoundingClientRect()
const steps = 500 console.log("nik width height", width, height)
setState({ width, height })
repeatedColours.forEach((color, i) => {
for (let j = 0; j < steps; j++) {
const nth = `:nth-child(${repeatedColours?.length * j}n+${i + j})`
ret[".path-segment" + nth] = {
// animation: `rainbow-psych 2s linear infinite`,
// "animation-delay": `-${(i + j) * 0.05}s`,
} }
}
})
return ret updateChildSize()
}, [repeatedColours])
const svg = React.useMemo(() => { new ResizeObserver(updateChildSize).observe(parentRef?.current)
}, [])
const rect = React.useMemo(() => {
return ( return (
<Box
sx={{
rainbowPsych: {
rainbowPsych: "rainbowPsych",
},
"@keyframes rainbow-psych": {
...colourKeyframes,
},
...colourClasses,
}}
width="100%"
height="100%"
>
<svg
ref={svgRef}
overflow="visible"
viewBox={viewBox}
width="100%"
height="100%"
// preserveAspectRatio="none"
>
<rect <rect
// stroke="url(#linear-gradient)" // stroke="url(#linear-gradient)"
x={0} x={0}
y={0} y={0}
width={100} width={state?.width || 100}
height={100} height={state?.height || 100}
rx={10} rx={10}
ry={10} ry={10}
></rect> ></rect>
)
}, [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}>
<svg
overflow="visible"
viewBox={`0 0 ${state?.width || 100} ${state?.height || 100}`}
width="100%"
height="100%"
preserveAspectRatio="none"
>
{rect}
{/* <path {/* <path
fill="none" fill="none"
stroke="blue" stroke="blue"
@ -109,71 +125,115 @@ export const Rainbow = (props: any): JSX.Element => {
</svg> </svg>
</Box> </Box>
) )
}, [ }, [state, rect])
pathString,
strokeWidth,
extraStroke,
viewBox,
repeatedColours,
colors,
colourKeyframes,
colourClasses,
])
React.useEffect(() => { React.useEffect(() => {
const path = svgRef.current.querySelector("rect") if (uuid) {
const svg = svgRef?.current?.querySelector("svg")
// path is rect or insert new rect if empty svg
const rectSource = parentRef?.current?.querySelector(".svg-source rect")
const path =
svg?.querySelector?.("rect") ||
svg?.appendChild?.(rectSource?.cloneNode?.())
if (path) { if (path) {
console.log("nik re-rendering rainbow")
const gp = new GradientPath({ const gp = new GradientPath({
path, path,
segments: props?.segments || 250, segments: props?.segments || 1000,
samples: props?.samples || 5, samples: props?.samples || 1,
precision: props?.precision || 5, precision: props?.precision || 5,
}) })
const colors = repeatedColours?.map((color, idx) => {
return {
color,
pos: idx / (repeatedColours.length - 1),
}
})
gp.render({ gp.render({
type: "path", type: "path",
width: 10, width: pathWidth || 1,
fill: ["orange", "blue", "orange"], animation: {
// fill: colors, name: `rainbow-${uuid}`,
strokeWidth: 0.5, duration: 5,
stroke: ["orange", "blue", "orange"], },
// stroke: colors,
}) })
setTimeout(() => {
setHidden(false)
}, 500)
return () => { return () => {
// clearInterval(interval) // setHidden(true)
gp.remove()
} }
} }
}, [props, repeatedColours]) }
}, [
uuid,
props?.segments,
props?.samples,
props?.precision,
pathWidth,
repeatedColours,
parentRef,
svgRef,
rect,
])
const render = true
useTrace("Rainbow", {
props,
})
return ( return (
<>
<Center <Center
className="Rainbow_n__n"
ref={parentRef}
sx={{
[`@keyframes rainbow-${uuid}`]: {
...colourKeyframes,
},
}}
position={props?.position || "relative"} position={props?.position || "relative"}
overflow="visible" overflow={overflow}
padding="25px" width={props?.expand ? "100%" : ""}
background="lightblue" height={props?.expand ? "100%" : ""}
> >
{props?.children}
<Center <Center
className="main-svg-container"
position="absolute" position="absolute"
top={0} top={0}
left={0} left={0}
overflow="visible" overflow="visible"
width="100%" width="100%"
height="100%" height="100%"
opacity={hidden ? "0" : opacity}
transition={opacityTransition}
>
{/* debug svg */}
<Box
className="svg-source"
flexShrink={0}
display="none"
overflow="visible"
width={width}
height={height}
>
{svg}
</Box>
<Box
ref={svgRef}
flexShrink={0}
overflow="visible"
width={width}
height={height}
opacity={hidden ? 0 : !render ? "0" : 1}
filter={filter}
> >
<Box flexShrink={0} overflow="visible" width={width} height={height}>
{svg} {svg}
</Box> </Box>
</Center> </Center>
{allProps?.children}
</Center> </Center>
</>
) )
} }

View File

@ -36,7 +36,18 @@ export const GradientPath = class {
this.path.parentNode.removeChild(this.path) this.path.parentNode.removeChild(this.path)
} }
render({ type, stroke, strokeWidth, fill, width }) { remove() {
this.group.parentNode.removeChild(this.group)
}
render({
type,
stroke = ["white", "black", "white"],
strokeWidth = 1,
fill = ["white", "black", "white"],
width,
animation = {},
}) {
// Store information from this render cycle // Store information from this render cycle
const renderCycle = {} const renderCycle = {}
@ -62,7 +73,7 @@ export const GradientPath = class {
svgElem("path", { svgElem("path", {
class: "path-segment", class: "path-segment",
d: segmentToD(samples), d: segmentToD(samples),
...styleAttrs(fill, stroke, strokeWidth, progress), ...styleAttrs(fill, stroke, strokeWidth, progress, animation),
}) })
) )
} }
@ -79,7 +90,7 @@ export const GradientPath = class {
cx: x, cx: x,
cy: y, cy: y,
r: width / 2, r: width / 2,
...styleAttrs(fill, stroke, strokeWidth, progress), ...styleAttrs(fill, stroke, strokeWidth, progress, animation),
}) })
) )
} }

View File

@ -1,71 +1,90 @@
import tinygradient from 'tinygradient'; import tinygradient from "tinygradient"
// An internal function to help with easily creating SVG elements with an object of attributes // An internal function to help with easily creating SVG elements with an object of attributes
export const svgElem = (type, attrs) => { export const svgElem = (type, attrs) => {
const elem = document.createElementNS('http://www.w3.org/2000/svg', type), const elem = document.createElementNS("http://www.w3.org/2000/svg", type),
attributes = Object.keys(attrs); attributes = Object.keys(attrs)
for (let i = 0; i < attributes.length; i++) { for (let i = 0; i < attributes.length; i++) {
const attr = attributes[i]; const attr = attributes[i]
elem.setAttribute(attr, attrs[attr]); elem.setAttribute(attr, attrs[attr])
} }
return elem; return elem
}; }
// An internal function to help with the repetition of adding fill, stroke, and stroke-width attributes // An internal function to help with the repetition of adding fill, stroke, and stroke-width attributes
export const styleAttrs = (fill, stroke, strokeWidth, progress) => { export const styleAttrs = (
fill,
stroke,
strokeWidth,
progress,
animation
) => {
const determineColor = (type, progress) => const determineColor = (type, progress) =>
typeof type === 'string' ? type : tinygradient(type).rgbAt(progress); typeof type === "string" ? type : tinygradient(type).rgbAt(progress)
const attrs = {}; const attrs = {}
if (stroke) { if (stroke) {
attrs['stroke'] = determineColor(stroke, progress); attrs["stroke"] = determineColor(stroke, progress)
attrs['stroke-width'] = strokeWidth; attrs["stroke-width"] = strokeWidth
} }
if (fill) { if (fill) {
attrs['fill'] = determineColor(fill, progress); attrs["fill"] = determineColor(fill, progress)
} }
return attrs; if (animation?.name) {
}; // TODO: add animation-direction support
const duration = animation.duration || 5
attrs["style"] = `
animation-name: ${animation?.name};
animation-delay: ${progress * duration - duration}s;
animation-duration: ${duration}s;
animation-iteration-count: infinite;
animation-timing-function: linear;`
}
return attrs
}
// An internal function to convert any array of samples into a "d" attribute to be passed to an SVG path // An internal function to convert any array of samples into a "d" attribute to be passed to an SVG path
export const segmentToD = samples => { export const segmentToD = (samples) => {
let d = ''; let d = ""
for (let i = 0; i < samples.length; i++) { for (let i = 0; i < samples.length; i++) {
const { x, y } = samples[i], const { x, y } = samples[i],
prevSample = i === 0 ? null : samples[i - 1]; prevSample = i === 0 ? null : samples[i - 1]
if (i === 0 && i !== samples.length - 1) { if (i === 0 && i !== samples.length - 1) {
d += `M${x},${y}`; d += `M${x},${y}`
} else if (x !== prevSample.x && y !== prevSample.y) { } else if (x !== prevSample.x && y !== prevSample.y) {
d += `L${x},${y}`; d += `L${x},${y}`
} else if (x !== prevSample.x) { } else if (x !== prevSample.x) {
d += `H${x}`; d += `H${x}`
} else if (y !== prevSample.y) { } else if (y !== prevSample.y) {
d += `V${y}`; d += `V${y}`
} }
if (i === samples.length - 1) { if (i === samples.length - 1) {
d += 'Z'; d += "Z"
} }
} }
return d; return d
}; }
// An internal function for getting the colors of a segment, we need to get middle most sample (sorted by progress along the path) // An internal function for getting the colors of a segment, we need to get middle most sample (sorted by progress along the path)
export const getMiddleSample = samples => { export const getMiddleSample = (samples) => {
const sortedSamples = [...samples].sort((a, b) => a.progress - b.progress); const sortedSamples = [...samples].sort((a, b) => a.progress - b.progress)
return sortedSamples[(sortedSamples.length / 2) | 0]; return sortedSamples[(sortedSamples.length / 2) | 0]
}; }
// An internal function for converting any D3 selection or DOM-like element into a DOM node // An internal function for converting any D3 selection or DOM-like element into a DOM node
export const convertPathToNode = path => export const convertPathToNode = (path) =>
path instanceof Element || path instanceof HTMLDocument ? path : path.node(); path instanceof Element || path instanceof HTMLDocument ? path : path.node()

View File

@ -0,0 +1,10 @@
import React from "react"
export const useProps = (allProps) => {
const deps = Object.values(allProps).filter((v) => v !== allProps.children)
return React.useMemo(() => {
const { children, ...other } = allProps
return other
}, deps)
}

View File

@ -0,0 +1,25 @@
import React from "react"
export const useTrace = (name, props) => {
const prev = React.useRef(props)
React.useEffect(() => {
const changedProps = Object.entries(props).reduce(
(changedValues, [key, newValue]) => {
window.trace = window.trace || {}
window.trace[key] = newValue
if (prev.current[key] !== newValue) {
changedValues[key] = {
old: prev.current[key],
new: newValue,
}
}
return changedValues
},
{}
)
if (Object.keys(changedProps)?.length) {
console.table({ TT: { name }, ...changedProps })
}
prev.current = props
})
}

View File

@ -0,0 +1,11 @@
import React from "react"
export const useUuid = () => {
const [uuid, setUuid] = React.useState()
React.useEffect(() => {
setUuid(Math.random().toString(36).substring(7))
}, [])
return uuid
}

View File

@ -1,11 +1,11 @@
import { Box, Flex } from "@chakra-ui/react" import { Box, Flex } from "@chakra-ui/react"
import { ProfileDrawer } from "~/components/Nav/ProfileDrawer" import { ProfileDrawer } from "~/components/Nav/ProfileDrawer"
import { Rainbow } from "~/components/Rainbow/Rainbow"
import { Splash } from "~/components/Splash/Splash" import { Splash } from "~/components/Splash/Splash"
import { Thingtime } from "~/components/Thingtime/Thingtime" import { Thingtime } from "~/components/Thingtime/Thingtime"
import { ThingtimeDemo } from "~/components/Thingtime/ThingtimeDemo" import { ThingtimeDemo } from "~/components/Thingtime/ThingtimeDemo"
import { useThingtime } from "~/components/Thingtime/useThingtime" import { useThingtime } from "~/components/Thingtime/useThingtime"
import { GradientPath } from "~/gp/GradientPath"
export default function Index() { export default function Index() {
const { thingtime } = useThingtime() const { thingtime } = useThingtime()
@ -17,10 +17,7 @@ export default function Index() {
flexDirection="column" flexDirection="column"
maxWidth="100%" maxWidth="100%"
> >
<Box paddingTop={200}></Box> {/* <Box paddingTop={200}></Box> */}
<Rainbow>
<Box width="260px" height="40px" background="grey"></Box>
</Rainbow>
<Splash></Splash> <Splash></Splash>
<Thingtime <Thingtime
marginBottom={200} marginBottom={200}

File diff suppressed because it is too large Load Diff