feat: main Fixed responsiveness and added Safe and safe functions and added globalThis usage for recursion safety on SSR

This commit is contained in:
Nikolaj Frey 2023-06-30 09:41:06 +10:00
parent 56da3991a5
commit 9793237357
10 changed files with 234 additions and 111 deletions

View File

@ -1,9 +1,18 @@
import { Flex } from '@chakra-ui/react'
import { Nav } from '../Nav/Nav'
import { ProfileDrawer } from '../Nav/ProfileDrawer'
export const Main = props => {
return (
<Flex alignItems='center' justifyContent='center' flexDir={'column'}>
<Flex
position={'relative'}
alignItems='center'
justifyContent='center'
flexDir={'column'}
overflow='hidden'
maxW='100vw'
>
{/* <ProfileDrawer></ProfileDrawer> */}
<Nav />
{props.children}
</Flex>

View File

@ -1,8 +1,15 @@
import React from 'react'
import { Flex } from '@chakra-ui/react'
import { RainbowSkeleton } from '../Skeleton/RainbowSkeleton'
import { ProfileDrawer } from './ProfileDrawer'
export const Nav = props => {
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false)
const toggleProfileDrawer = React.useCallback(() => {
setProfileDrawerOpen(!profileDrawerOpen)
}, [profileDrawerOpen])
return (
<>
<Flex
@ -15,12 +22,24 @@ export const Nav = props => {
top={0}
left={0}
right={0}
py={4}
px={4}
py={6}
px={6}
bg='white'
zIndex={999}
boxShadow={'0px 0px 10px rgba(0,0,0,0.1)'}
>
<RainbowSkeleton
ml={'auto'}
w='25px'
h='25px'
onClick={toggleProfileDrawer}
sx={{}}
borderRadius='999px'
></RainbowSkeleton>
{/* <RainbowSkeleton w='40px' ml='auto' mr={"4px"}></RainbowSkeleton>
<RainbowSkeleton></RainbowSkeleton> */}
</Flex>
{/* <ProfileDrawer isOpen={profileDrawerOpen}></ProfileDrawer> */}
</>
)
}

View File

@ -0,0 +1,27 @@
import React from 'react'
import { Drawer, DrawerBody, DrawerContent, Flex } from '@chakra-ui/react'
export const ProfileDrawer = props => {
const navItems = ['settings']
const onClose = React.useCallback(() => {
// nothing
}, [])
return (
<Drawer
placement='right'
size='md'
isOpen={props?.isOpen}
onClose={onClose}
>
<DrawerContent maxW={'90%'} mt={'35px'} className='ProfileDrawer'>
<DrawerBody>
{navItems.map((item, idx) => {
return <Flex key={idx}>{item}</Flex>
})}
</DrawerBody>
</DrawerContent>
</Drawer>
)
}

View File

@ -0,0 +1,7 @@
import { safe } from '~/functions/safe'
export const Safe = props => {
// do not render more than the limit of things to prevent infinite loops
return safe(props)
}

View File

@ -2,46 +2,46 @@ import React from 'react'
import { Flex } from '@chakra-ui/react'
export const RainbowSkeleton = props => {
const [rainbowColours] = React.useState(["#f34a4a", "#ffbc48", "#58ca70", "#47b5e6", "#a555e8"])
const [rainbowColours] = React.useState([
'#f34a4a',
'#ffbc48',
'#58ca70',
'#47b5e6',
'#a555e8'
])
const keyframes = React.useMemo(() => {
let keyframes = {}
rainbowColours.forEach((colour, idx) => {
keyframes[Math.round(idx * 100 / rainbowColours.length) + "%"] = { backgroundColor: colour }
keyframes[Math.round((idx * 100) / rainbowColours.length) + '%'] = {
backgroundColor: colour
}
})
keyframes["100%"] = { backgroundColor: rainbowColours[0] }
keyframes['100%'] = { backgroundColor: rainbowColours[0] }
return keyframes
}, [rainbowColours])
if (props?.loaded) {
return props?.children
}
return (
<Flex
cursor='pointer'
w='10px'
h='8px'
borderRadius={'2px'}
bg={'rgba(0,0,0,0.1)'}
sx={{
'@keyframes placeholder-rainbow': keyframes,
'@keyframes placeholder-opacity': {
'0%': { opacity: 0.2 },
'100%': { opacity: 1 }
},
// add delay
animation: `placeholder-rainbow 3s infinite linear, placeholder-opacity 1.3s linear 0s infinite alternate none running}`
}}
{...props}
flexDirection={'column'}
justifyContent={'center'}
alignItems={'center'}
cursor={'pointer'}
>
<Flex
w='10px'
h='8px'
borderRadius={'2px'}
mb='10px'
sx={{
'@keyframes placeholder-rainbow': keyframes,
'@keyframes placeholder-opacity': {
'0%': { opacity: 0.2 },
'100%': { opacity: 1 }
},
// add delay
animation: `placeholder-rainbow 3s infinite linear, placeholder-opacity 1.3s linear 0s infinite alternate none running}`
}}
{...props}
></Flex>
</Flex>
></Flex>
)
}

View File

@ -1,12 +1,16 @@
import React from 'react'
import { Box, Flex } from '@chakra-ui/react'
import { safe } from '~/functions/safe'
import { Safe } from '../Safety/Safe'
export const Thingtime = props => {
// const cuid = React.useMemo(() => {
// const uuid = React.useMemo(() => {
// return Math.random().toString(36).substring(7)
// }, [])
const cuid = React.useRef(Math.random().toString(36).substring(7))
const uuid = React.useRef(Math.random().toString(36).substring(7))
const depth = React.useMemo(() => {
return props?.depth || 1
}, [props?.depth])
const thing = React.useMemo(() => {
return props.thing
@ -28,13 +32,6 @@ export const Thingtime = props => {
}
}, [thing, validKeyTypes])
React.useEffect(() => {
if (window?.thingtime?.things) {
window.thingtime.things.count =
(window?.thingtime?.things?.count || 1) + 1
}
}, [])
const type = React.useMemo(() => {
return typeof thing
}, [thing])
@ -84,15 +81,11 @@ export const Thingtime = props => {
})
}
console.log('nik ret 1', thing)
recurse(thing, '')
} catch (err) {
// console.error('Error in Thingtime.tsx creating flattenedKeys', err)
}
console.log('nik ret 2', ret)
return ret
}, [thing])
@ -105,37 +98,42 @@ export const Thingtime = props => {
const template1Modes = ['view', 'edit']
if (template1Modes?.includes(mode)) {
console.log('nik keys', keys)
if (keys?.length) {
value = (
<Flex
position='relative'
flexDir='column'
minW='500px'
maxW='100%'
pl={6}
>
{keysToUse?.length &&
keysToUse.map((key, idx) => {
if (!key?.human) {
key = {
human: key,
key: key
<Safe {...props}>
<Flex
position='relative'
flexDir='column'
// w={'500px'}
// w={['200px', '500px']}
maxW='100%'
pl={[4, 6]}
pr={[4, 6]}
>
{keysToUse?.length &&
keysToUse.map((key, idx) => {
if (!key?.human) {
key = {
human: key,
key: key
}
}
}
const nextThing = thing[key?.key]
const nextThing = thing[key?.key]
return (
<Thingtime
key={idx}
parent={thing}
path={key}
thing={nextThing}
></Thingtime>
)
})}
</Flex>
return (
<Thingtime
key={idx}
depth={depth + 1}
parent={thing}
path={key}
thing={nextThing}
// thing={{ infinite: { yes: true } }}
></Thingtime>
)
})}
</Flex>
</Safe>
)
} else {
editableValue = (
@ -161,39 +159,45 @@ export const Thingtime = props => {
const [showContextMenu, setShowContextMenu] = React.useState(false)
const path = React.useMemo(() => {
return <Flex fontSize='12px'>{props?.path?.human}</Flex>
return (
<Flex maxW='100%' wordBreak={'break-all'} fontSize='12px'>
{props?.path?.human}
</Flex>
)
}, [props?.path])
const handleMouseEvent = React.useCallback(
e => {
const target = e?.target
// extract cuid from className
console.log('nik eh', target?.className, cuid)
// extract uuid from className
const className = target?.className
if (className?.includes(cuid?.current)) {
if (className?.includes(uuid?.current)) {
setShowContextMenu(e?.type === 'mouseenter')
}
},
[cuid]
[uuid]
)
console.log('nik cuid', cuid)
return safe(
<Flex
onMouseEnter={handleMouseEvent}
onMouseLeave={handleMouseEvent}
position='relative'
flexDir='column'
py={3}
{...props}
className={`thing-${cuid?.current}`}
>
{/* {cuid?.current} */}
{path}
{showContextMenu && contextMenu}
{editableValue}
{value}
</Flex>
return (
<Safe {...props} depth={depth} uuid={uuid?.current}>
<Flex
onMouseEnter={handleMouseEvent}
onMouseLeave={handleMouseEvent}
position='relative'
flexDir='column'
py={3}
w='500px'
// minW={depth === 1 ? '120px' : null}
maxW='100%'
{...props}
className={`thing-${uuid?.current}`}
>
{/* {uuid?.current} */}
{path}
{showContextMenu && contextMenu}
{editableValue}
{value}
</Flex>
</Safe>
)
}

View File

@ -28,7 +28,7 @@ export const ThingtimeDemo = props => {
// return null
return (
<Flex pb={40}>
<Flex maxW='100%' pb={40}>
<Thingtime thing={demoThing}></Thingtime>
</Flex>
)

View File

@ -1,16 +1,56 @@
export const safe = response => {
export const safe = props => {
// do not render more than the limit of things to prevent infinite loops
const thingtime = getThingtime()
const uuid = props?.uuid
try {
if (
typeof window?.thingtime?.things?.count === 'number' &&
window?.thingtime?.things?.count > window?.thingtime?.things?.limit
typeof thingtime?.things?.count === 'number' &&
thingtime?.things?.count >= thingtime?.things?.limit
) {
console.error('Maximum things reached')
console.error(
'[codex] Maximum things reached',
thingtime?.things?.count,
thingtime?.things?.limit
)
return null
}
} catch (err) {
// console.error('Error in Thingtime.tsx checking maximum things', err)
console.error('[codex] Error in Thingtime.tsx checking maximum things', err)
}
return response
try {
if (!thingtime?.things?.db?.[uuid]) {
thingtime.things.db[uuid] = {
count: 1
}
thingtime.things.count++
}
} catch {
// empty
}
try {
if (props?.depth >= thingtime?.things?.maxDepth) {
console.error(
'[codex] Reached max depth',
props?.depth,
thingtime?.things?.maxDepth
)
return null
}
} catch {
// nothing
}
return props?.children
}
export const getThingtime = () => {
try {
return window?.thingtime || globalThis?.thingtime
} catch {
return globalThis?.thingtime
}
}

View File

@ -48,16 +48,26 @@ export default function App () {
)
}
// limiter
try {
window.thingtime = {
tmp: {},
things: {
limit: 999,
count: 0,
const setThingtime = glob => {
try {
glob.thingtime = {
tmp: {},
things: {
db: {},
limit: 9999,
maxDepth: 10,
count: 0
}
}
} catch (err) {
// will error on server
}
} catch (err) {
// will error on server
}
}
try {
setThingtime(window)
} catch {
setThingtime(globalThis)
}

View File

@ -1,12 +1,19 @@
import { Flex } from '@chakra-ui/react'
import { ProfileDrawer } from '~/components/Nav/ProfileDrawer'
import { Splash } from '~/components/Splash/Splash'
import { ThingtimeDemo } from '~/components/Thingtime/ThingtimeDemo'
export default function Index () {
return (
<Flex flexDir='column' alignItems='center' justifyContent='center'>
<Flex
maxW='100%'
flexDir='column'
alignItems='center'
justifyContent='center'
>
<Splash></Splash>
<ThingtimeDemo></ThingtimeDemo>
<ProfileDrawer></ProfileDrawer>
</Flex>
)
}