feat: feature/rainbow-component Added rainbow animation to commander
This commit is contained in:
parent
302302fa8f
commit
e30dc3f56c
@ -17,7 +17,14 @@ module.exports = {
|
||||
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: [
|
||||
"@remix-run/eslint-config",
|
||||
"eslint:recommended",
|
||||
@ -26,7 +33,10 @@ module.exports = {
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
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",
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
@ -35,8 +45,8 @@ module.exports = {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": "error",
|
||||
// "unused-imports/no-unused-imports": "error",
|
||||
// "unused-imports/no-unused-vars": "error",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"chakra-ui/props-order": "error",
|
||||
"chakra-ui/props-shorthand": [
|
||||
|
@ -20,7 +20,14 @@ try {
|
||||
// nothing
|
||||
}
|
||||
|
||||
const forceable = {
|
||||
const force = {
|
||||
settings: {
|
||||
commanderActive: true,
|
||||
},
|
||||
version: 22,
|
||||
}
|
||||
|
||||
const newVersionData = {
|
||||
Content: {
|
||||
hidden1: "Edit this to your heart's desire.",
|
||||
"How?": "Just search for Content and edit the value to whatever you want.",
|
||||
@ -30,26 +37,25 @@ const forceable = {
|
||||
},
|
||||
}
|
||||
|
||||
const initialThingtime = {
|
||||
nav: {},
|
||||
version: 22,
|
||||
...forceable,
|
||||
}
|
||||
|
||||
const userData = {
|
||||
const initialValues = {
|
||||
settings: {
|
||||
showCommander: true,
|
||||
commanderActive: true,
|
||||
clearCommanderOnToggle: 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 => {
|
||||
const [thingtime, set] = React.useState({
|
||||
...initialThingtime,
|
||||
...userData,
|
||||
})
|
||||
const [thingtime, set] = React.useState(smarts.merge(initialValues, force))
|
||||
|
||||
const thingtimeRef = React.useRef(thingtime)
|
||||
|
||||
@ -61,15 +67,15 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
if (thingtimeFromLocalStorage) {
|
||||
const parsed = JSON.parse(thingtimeFromLocalStorage)
|
||||
if (parsed) {
|
||||
const invalidLocalStorage =
|
||||
!parsed.version || parsed.version < initialThingtime.version
|
||||
if (!invalidLocalStorage) {
|
||||
set(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 newThingtime = {
|
||||
...parsed,
|
||||
...initialThingtime,
|
||||
}
|
||||
const withVersionUpdates = smarts.merge(newVersionData, parsed)
|
||||
const newThingtime = smarts.merge(force, withVersionUpdates)
|
||||
set(newThingtime)
|
||||
}
|
||||
}
|
||||
@ -144,6 +150,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
try {
|
||||
window.setThingtime = setThingtime
|
||||
window.thingtime = thingtime
|
||||
window.tt = thingtime
|
||||
} catch {
|
||||
// nothing
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import React from "react"
|
||||
import ClickAwayListener from "react-click-away-listener"
|
||||
import { Center, Flex, Input } from "@chakra-ui/react"
|
||||
|
||||
import { Rainbow } from "../Rainbow/Rainbow"
|
||||
import { Thingtime } from "../Thingtime/Thingtime"
|
||||
import { useThingtime } from "../Thingtime/useThingtime"
|
||||
|
||||
@ -14,7 +15,7 @@ export const Commander = (props) => {
|
||||
const inputRef = React.useRef()
|
||||
|
||||
const [value, setValue] = React.useState("")
|
||||
|
||||
const [active, setActive] = React.useState(false)
|
||||
const [contextPath, setContextPath] = React.useState()
|
||||
|
||||
const [showContext, setShowContextState] = React.useState(false)
|
||||
@ -33,23 +34,25 @@ export const Commander = (props) => {
|
||||
return ret
|
||||
}, [contextPath, getThingtime])
|
||||
|
||||
const showCommander = React.useMemo(() => {
|
||||
return thingtime?.settings?.showCommander
|
||||
}, [thingtime?.settings?.showCommander])
|
||||
const commanderActive = React.useMemo(() => {
|
||||
return thingtime?.settings?.commanderActive
|
||||
}, [thingtime?.settings?.commanderActive])
|
||||
|
||||
// showCommander useEffect
|
||||
console.log("nik commanderActive", commanderActive)
|
||||
|
||||
// commanderActive useEffect
|
||||
React.useEffect(() => {
|
||||
if (showCommander) {
|
||||
if (commanderActive) {
|
||||
inputRef?.current?.focus?.()
|
||||
} else {
|
||||
if (thingtimeRef?.current?.settings?.clearCommanderOnToggle) {
|
||||
setValue("")
|
||||
}
|
||||
if (thingtimeRef?.current?.settings?.clearCommanderContextOnToggle) {
|
||||
setShowContext(false, "showCommander useEffect")
|
||||
setShowContext(false, "commanderActive useEffect")
|
||||
}
|
||||
}
|
||||
}, [showCommander, thingtimeRef, setShowContext])
|
||||
}, [commanderActive, thingtimeRef, setShowContext])
|
||||
|
||||
const onChange = React.useCallback((e) => {
|
||||
setValue(e.target.value)
|
||||
@ -165,7 +168,7 @@ export const Commander = (props) => {
|
||||
// setShowContext(true, 'Thingtime changes update suggestions')
|
||||
// }
|
||||
}
|
||||
}, [value, thingtime, commandPath, setShowContext])
|
||||
}, [value, thingtime, commandPath])
|
||||
|
||||
const onEnter = React.useCallback(
|
||||
(props) => {
|
||||
@ -202,6 +205,7 @@ export const Commander = (props) => {
|
||||
setShowContext,
|
||||
escapedCommandValue,
|
||||
setThingtime,
|
||||
getThingtime,
|
||||
commandIsAction,
|
||||
commandPath,
|
||||
commandContainsPath,
|
||||
@ -216,8 +220,8 @@ export const Commander = (props) => {
|
||||
e.stopPropagation()
|
||||
onEnter({ e })
|
||||
// setThingtime(
|
||||
// 'settings.showCommander',
|
||||
// !thingtime?.settings?.showCommander
|
||||
// 'settings.commanderActive',
|
||||
// !thingtime?.settings?.commanderActive
|
||||
// )
|
||||
}
|
||||
},
|
||||
@ -225,13 +229,18 @@ export const Commander = (props) => {
|
||||
)
|
||||
|
||||
const openCommander = React.useCallback(() => {
|
||||
setThingtime("settings.showCommander", true)
|
||||
console.log("nik commander opening commander")
|
||||
setThingtime("settings.commanderActive", true)
|
||||
}, [setThingtime])
|
||||
|
||||
const closeCommander = React.useCallback(() => {
|
||||
if (thingtime?.settings?.showCommander) {
|
||||
setThingtime("settings.showCommander", false)
|
||||
if (thingtime?.settings?.commanderActive) {
|
||||
console.log("nik commander closing commander")
|
||||
setThingtime("settings.commanderActive", false)
|
||||
}
|
||||
|
||||
document.activeElement.blur()
|
||||
|
||||
if (value !== "") {
|
||||
setValue("")
|
||||
}
|
||||
@ -241,15 +250,22 @@ export const Commander = (props) => {
|
||||
if (showContext !== false) {
|
||||
setShowContext(false)
|
||||
}
|
||||
}, [setThingtime, setShowContext, value, contextPath, showContext])
|
||||
}, [
|
||||
setThingtime,
|
||||
setShowContext,
|
||||
value,
|
||||
contextPath,
|
||||
showContext,
|
||||
thingtime?.settings?.commanderActive,
|
||||
])
|
||||
|
||||
const toggleCommander = React.useCallback(() => {
|
||||
if (thingtime?.settings?.showCommander) {
|
||||
if (thingtime?.settings?.commanderActive) {
|
||||
closeCommander()
|
||||
} else {
|
||||
openCommander()
|
||||
}
|
||||
}, [thingtime?.settings?.showCommander, closeCommander, openCommander])
|
||||
}, [thingtime?.settings?.commanderActive, closeCommander, openCommander])
|
||||
|
||||
React.useEffect(() => {
|
||||
const keyListener = (e: any) => {
|
||||
@ -295,11 +311,13 @@ export const Commander = (props) => {
|
||||
return "calc(100vw - 45px)"
|
||||
}, [])
|
||||
|
||||
const rainbowRepeats = 1
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={closeCommander}>
|
||||
<Flex
|
||||
position="absolute"
|
||||
// display={['flex', showCommander ? 'flex' : 'none']}
|
||||
// display={['flex', commanderActive ? 'flex' : 'none']}
|
||||
top={0}
|
||||
// zIndex={99999}
|
||||
// position='fixed'
|
||||
@ -368,38 +386,55 @@ export const Commander = (props) => {
|
||||
<Thingtime thing={contextValue}></Thingtime>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Center
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
padding="1px"
|
||||
background="grey"
|
||||
borderRadius="6px"
|
||||
pointerEvents="all"
|
||||
outline="none"
|
||||
<Rainbow
|
||||
filter="blur(15px)"
|
||||
opacity={commanderActive ? 0.25 : 0}
|
||||
repeats={rainbowRepeats}
|
||||
thickness={8}
|
||||
overflow="visible"
|
||||
>
|
||||
<Input
|
||||
// display='none'
|
||||
// opacity={0}
|
||||
ref={inputRef}
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
},
|
||||
}}
|
||||
width="100%"
|
||||
<Center
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
width={["100%", "400px"]}
|
||||
maxWidth={[mobileVW, "100%"]}
|
||||
height="100%"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
padding="1px"
|
||||
borderRadius="6px"
|
||||
pointerEvents="all"
|
||||
outline="none"
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder={"What's on your mind?"}
|
||||
value={value}
|
||||
></Input>
|
||||
</Center>
|
||||
>
|
||||
<Rainbow
|
||||
opacity={commanderActive ? 0.5 : 0}
|
||||
position="absolute"
|
||||
expand
|
||||
repeats={rainbowRepeats}
|
||||
opacityTransition="all 3000ms ease"
|
||||
thickness={10}
|
||||
></Rainbow>
|
||||
<Input
|
||||
// display='none'
|
||||
// opacity={0}
|
||||
ref={inputRef}
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
},
|
||||
}}
|
||||
width="100%"
|
||||
height="100%"
|
||||
background="grey"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={onChange}
|
||||
onFocus={openCommander}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder={"What's on your mind?"}
|
||||
value={value}
|
||||
></Input>
|
||||
</Center>
|
||||
</Rainbow>
|
||||
</Flex>
|
||||
</ClickAwayListener>
|
||||
)
|
||||
|
@ -2,25 +2,56 @@ import React from "react"
|
||||
import { Box, Center } from "@chakra-ui/react"
|
||||
|
||||
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 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 [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(() => {
|
||||
return [...colors, colors[0]]
|
||||
}, [colors])
|
||||
const ret = []
|
||||
|
||||
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
|
||||
// which adjusts to the parent containers size
|
||||
|
||||
const [state, setState] = React.useState({
|
||||
width: 100,
|
||||
height: 100,
|
||||
})
|
||||
|
||||
const [strokeWidth, setStrokeWidth] = React.useState(1)
|
||||
|
||||
const [extraStroke, setExtraStroke] = React.useState(0)
|
||||
|
||||
const [width, setWidth] = React.useState(props?.width || "105%")
|
||||
const [height, setHeight] = React.useState(props?.height || "105%")
|
||||
const [width, setWidth] = React.useState(props?.width || "100%")
|
||||
const [height, setHeight] = React.useState(props?.height || "100%")
|
||||
|
||||
const pathString = React.useMemo(() => {
|
||||
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}`
|
||||
}, [strokeWidth])
|
||||
|
||||
const viewBox = React.useMemo(() => {
|
||||
return `0 0 100 100`
|
||||
}, [])
|
||||
|
||||
const svgRef = React.useRef(null)
|
||||
|
||||
const colourKeyframes = React.useMemo(() => {
|
||||
@ -49,56 +76,45 @@ export const Rainbow = (props: any): JSX.Element => {
|
||||
return ret
|
||||
}, [repeatedColours])
|
||||
|
||||
const colourClasses = React.useMemo(() => {
|
||||
const ret = {}
|
||||
React.useEffect(() => {
|
||||
const updateChildSize = () => {
|
||||
const { width, height } = parentRef?.current?.getBoundingClientRect()
|
||||
console.log("nik width height", width, height)
|
||||
setState({ width, height })
|
||||
}
|
||||
|
||||
const steps = 500
|
||||
updateChildSize()
|
||||
|
||||
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`,
|
||||
}
|
||||
}
|
||||
})
|
||||
new ResizeObserver(updateChildSize).observe(parentRef?.current)
|
||||
}, [])
|
||||
|
||||
return ret
|
||||
}, [repeatedColours])
|
||||
const rect = React.useMemo(() => {
|
||||
return (
|
||||
<rect
|
||||
// stroke="url(#linear-gradient)"
|
||||
x={0}
|
||||
y={0}
|
||||
width={state?.width || 100}
|
||||
height={state?.height || 100}
|
||||
rx={10}
|
||||
ry={10}
|
||||
></rect>
|
||||
)
|
||||
}, [state?.width, state?.height])
|
||||
|
||||
const svg = React.useMemo(() => {
|
||||
const id = Math.random().toString(36).substring(2, 15)
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
rainbowPsych: {
|
||||
rainbowPsych: "rainbowPsych",
|
||||
},
|
||||
"@keyframes rainbow-psych": {
|
||||
...colourKeyframes,
|
||||
},
|
||||
...colourClasses,
|
||||
}}
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<Box width="100%" height="100%" id={id}>
|
||||
<svg
|
||||
ref={svgRef}
|
||||
overflow="visible"
|
||||
viewBox={viewBox}
|
||||
viewBox={`0 0 ${state?.width || 100} ${state?.height || 100}`}
|
||||
width="100%"
|
||||
height="100%"
|
||||
// preserveAspectRatio="none"
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
<rect
|
||||
// stroke="url(#linear-gradient)"
|
||||
x={0}
|
||||
y={0}
|
||||
width={100}
|
||||
height={100}
|
||||
rx={10}
|
||||
ry={10}
|
||||
></rect>
|
||||
{rect}
|
||||
{/* <path
|
||||
fill="none"
|
||||
stroke="blue"
|
||||
@ -109,71 +125,115 @@ export const Rainbow = (props: any): JSX.Element => {
|
||||
</svg>
|
||||
</Box>
|
||||
)
|
||||
}, [
|
||||
pathString,
|
||||
strokeWidth,
|
||||
extraStroke,
|
||||
viewBox,
|
||||
repeatedColours,
|
||||
colors,
|
||||
colourKeyframes,
|
||||
colourClasses,
|
||||
])
|
||||
}, [state, rect])
|
||||
|
||||
React.useEffect(() => {
|
||||
const path = svgRef.current.querySelector("rect")
|
||||
if (uuid) {
|
||||
const svg = svgRef?.current?.querySelector("svg")
|
||||
|
||||
if (path) {
|
||||
const gp = new GradientPath({
|
||||
path,
|
||||
segments: props?.segments || 250,
|
||||
samples: props?.samples || 5,
|
||||
precision: props?.precision || 5,
|
||||
})
|
||||
// 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?.())
|
||||
|
||||
const colors = repeatedColours?.map((color, idx) => {
|
||||
return {
|
||||
color,
|
||||
pos: idx / (repeatedColours.length - 1),
|
||||
if (path) {
|
||||
console.log("nik re-rendering rainbow")
|
||||
const gp = new GradientPath({
|
||||
path,
|
||||
segments: props?.segments || 1000,
|
||||
samples: props?.samples || 1,
|
||||
precision: props?.precision || 5,
|
||||
})
|
||||
|
||||
gp.render({
|
||||
type: "path",
|
||||
width: pathWidth || 1,
|
||||
animation: {
|
||||
name: `rainbow-${uuid}`,
|
||||
duration: 5,
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
setHidden(false)
|
||||
}, 500)
|
||||
|
||||
return () => {
|
||||
// setHidden(true)
|
||||
gp.remove()
|
||||
}
|
||||
})
|
||||
|
||||
gp.render({
|
||||
type: "path",
|
||||
width: 10,
|
||||
fill: ["orange", "blue", "orange"],
|
||||
// fill: colors,
|
||||
strokeWidth: 0.5,
|
||||
stroke: ["orange", "blue", "orange"],
|
||||
// stroke: colors,
|
||||
})
|
||||
|
||||
return () => {
|
||||
// clearInterval(interval)
|
||||
}
|
||||
}
|
||||
}, [props, repeatedColours])
|
||||
}, [
|
||||
uuid,
|
||||
props?.segments,
|
||||
props?.samples,
|
||||
props?.precision,
|
||||
pathWidth,
|
||||
repeatedColours,
|
||||
parentRef,
|
||||
svgRef,
|
||||
rect,
|
||||
])
|
||||
|
||||
const render = true
|
||||
|
||||
useTrace("Rainbow", {
|
||||
props,
|
||||
})
|
||||
|
||||
return (
|
||||
<Center
|
||||
position={props?.position || "relative"}
|
||||
overflow="visible"
|
||||
padding="25px"
|
||||
background="lightblue"
|
||||
>
|
||||
{props?.children}
|
||||
<>
|
||||
<Center
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
overflow="visible"
|
||||
width="100%"
|
||||
height="100%"
|
||||
className="Rainbow_n__n"
|
||||
ref={parentRef}
|
||||
sx={{
|
||||
[`@keyframes rainbow-${uuid}`]: {
|
||||
...colourKeyframes,
|
||||
},
|
||||
}}
|
||||
position={props?.position || "relative"}
|
||||
overflow={overflow}
|
||||
width={props?.expand ? "100%" : ""}
|
||||
height={props?.expand ? "100%" : ""}
|
||||
>
|
||||
<Box flexShrink={0} overflow="visible" width={width} height={height}>
|
||||
{svg}
|
||||
</Box>
|
||||
<Center
|
||||
className="main-svg-container"
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
overflow="visible"
|
||||
width="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}
|
||||
>
|
||||
{svg}
|
||||
</Box>
|
||||
</Center>
|
||||
{allProps?.children}
|
||||
</Center>
|
||||
</Center>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -36,7 +36,18 @@ export const GradientPath = class {
|
||||
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
|
||||
const renderCycle = {}
|
||||
|
||||
@ -62,7 +73,7 @@ export const GradientPath = class {
|
||||
svgElem("path", {
|
||||
class: "path-segment",
|
||||
d: segmentToD(samples),
|
||||
...styleAttrs(fill, stroke, strokeWidth, progress),
|
||||
...styleAttrs(fill, stroke, strokeWidth, progress, animation),
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -79,7 +90,7 @@ export const GradientPath = class {
|
||||
cx: x,
|
||||
cy: y,
|
||||
r: width / 2,
|
||||
...styleAttrs(fill, stroke, strokeWidth, progress),
|
||||
...styleAttrs(fill, stroke, strokeWidth, progress, animation),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
export const svgElem = (type, attrs) => {
|
||||
const elem = document.createElementNS('http://www.w3.org/2000/svg', type),
|
||||
attributes = Object.keys(attrs);
|
||||
const elem = document.createElementNS("http://www.w3.org/2000/svg", type),
|
||||
attributes = Object.keys(attrs)
|
||||
|
||||
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
|
||||
export const styleAttrs = (fill, stroke, strokeWidth, progress) => {
|
||||
export const styleAttrs = (
|
||||
fill,
|
||||
stroke,
|
||||
strokeWidth,
|
||||
progress,
|
||||
animation
|
||||
) => {
|
||||
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) {
|
||||
attrs['stroke'] = determineColor(stroke, progress);
|
||||
attrs['stroke-width'] = strokeWidth;
|
||||
attrs["stroke"] = determineColor(stroke, progress)
|
||||
attrs["stroke-width"] = strokeWidth
|
||||
}
|
||||
|
||||
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
|
||||
export const segmentToD = samples => {
|
||||
let d = '';
|
||||
export const segmentToD = (samples) => {
|
||||
let d = ""
|
||||
|
||||
for (let i = 0; i < samples.length; 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) {
|
||||
d += `M${x},${y}`;
|
||||
d += `M${x},${y}`
|
||||
} else if (x !== prevSample.x && y !== prevSample.y) {
|
||||
d += `L${x},${y}`;
|
||||
d += `L${x},${y}`
|
||||
} else if (x !== prevSample.x) {
|
||||
d += `H${x}`;
|
||||
d += `H${x}`
|
||||
} else if (y !== prevSample.y) {
|
||||
d += `V${y}`;
|
||||
d += `V${y}`
|
||||
}
|
||||
|
||||
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)
|
||||
export const getMiddleSample = samples => {
|
||||
const sortedSamples = [...samples].sort((a, b) => a.progress - b.progress);
|
||||
export const getMiddleSample = (samples) => {
|
||||
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
|
||||
export const convertPathToNode = path =>
|
||||
path instanceof Element || path instanceof HTMLDocument ? path : path.node();
|
||||
export const convertPathToNode = (path) =>
|
||||
path instanceof Element || path instanceof HTMLDocument ? path : path.node()
|
||||
|
10
remix/app/hooks/useProps.tsx
Normal file
10
remix/app/hooks/useProps.tsx
Normal 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)
|
||||
}
|
25
remix/app/hooks/useTrace.tsx
Normal file
25
remix/app/hooks/useTrace.tsx
Normal 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
|
||||
})
|
||||
}
|
11
remix/app/hooks/useUuid.tsx
Normal file
11
remix/app/hooks/useUuid.tsx
Normal 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
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
import { Box, Flex } from "@chakra-ui/react"
|
||||
|
||||
import { ProfileDrawer } from "~/components/Nav/ProfileDrawer"
|
||||
import { Rainbow } from "~/components/Rainbow/Rainbow"
|
||||
import { Splash } from "~/components/Splash/Splash"
|
||||
import { Thingtime } from "~/components/Thingtime/Thingtime"
|
||||
import { ThingtimeDemo } from "~/components/Thingtime/ThingtimeDemo"
|
||||
import { useThingtime } from "~/components/Thingtime/useThingtime"
|
||||
import { GradientPath } from "~/gp/GradientPath"
|
||||
|
||||
export default function Index() {
|
||||
const { thingtime } = useThingtime()
|
||||
@ -17,10 +17,7 @@ export default function Index() {
|
||||
flexDirection="column"
|
||||
maxWidth="100%"
|
||||
>
|
||||
<Box paddingTop={200}></Box>
|
||||
<Rainbow>
|
||||
<Box width="260px" height="40px" background="grey"></Box>
|
||||
</Rainbow>
|
||||
{/* <Box paddingTop={200}></Box> */}
|
||||
<Splash></Splash>
|
||||
<Thingtime
|
||||
marginBottom={200}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user