2023-08-14 02:58:50 +00:00
|
|
|
import React, { useState } from "react"
|
2023-08-15 01:01:05 +00:00
|
|
|
import ClickAwayListener from "react-click-away-listener"
|
2023-08-14 02:58:50 +00:00
|
|
|
import { Center, Flex, Text } from "@chakra-ui/react"
|
|
|
|
|
|
|
|
import { Icon } from "../Icon/Icon"
|
2023-08-22 00:36:01 +00:00
|
|
|
import { useThingtime } from "./useThingtime"
|
2023-08-14 02:58:50 +00:00
|
|
|
|
|
|
|
export const SettingsMenu = (props) => {
|
|
|
|
const [show, setShow] = useState(false)
|
2023-08-15 01:01:05 +00:00
|
|
|
const hideRef = React.useRef(null)
|
|
|
|
const [opacity, setOpacity] = React.useState(props?.opacity === 0 ? 0 : 1)
|
2023-08-22 11:31:37 +00:00
|
|
|
const [pinStatus, setPinStatus] = React.useState(false)
|
|
|
|
|
|
|
|
const stateRef = React.useRef({
|
|
|
|
pinStatus,
|
|
|
|
})
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
stateRef.current.pinStatus = pinStatus
|
|
|
|
}, [pinStatus])
|
2023-08-15 01:01:05 +00:00
|
|
|
|
2023-08-22 00:36:01 +00:00
|
|
|
const { thingtime, events } = useThingtime()
|
|
|
|
|
2023-08-15 01:01:05 +00:00
|
|
|
const opacityRef = React.useRef(null)
|
|
|
|
|
|
|
|
const waitTime = 1555
|
|
|
|
|
2023-08-22 00:36:01 +00:00
|
|
|
const [uuid, setUuid] = React.useState(null)
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
setUuid(Math.random().toString(36).substring(7))
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const subscription = events.subscribe((event) => {
|
|
|
|
if (event?.type === "settings-menu-hide" && event?.uuid !== uuid) {
|
2023-08-22 11:31:37 +00:00
|
|
|
if (!stateRef?.current?.pinStatus || event?.force) {
|
|
|
|
setShow(false)
|
|
|
|
setOpacity(0)
|
|
|
|
}
|
2023-08-22 00:36:01 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
subscription?.unsubscribe?.()
|
|
|
|
}
|
|
|
|
}, [events, uuid])
|
|
|
|
|
2023-08-15 01:01:05 +00:00
|
|
|
React.useEffect(() => {
|
|
|
|
clearInterval(opacityRef?.current)
|
|
|
|
if (props?.opacity) {
|
|
|
|
setOpacity(props?.opacity)
|
|
|
|
} else {
|
|
|
|
opacityRef.current = setInterval(() => {
|
2023-08-22 11:31:37 +00:00
|
|
|
if (!stateRef?.current?.pinStatus) {
|
|
|
|
setOpacity(props?.opacity)
|
|
|
|
setShow(false)
|
|
|
|
}
|
2023-08-15 01:01:05 +00:00
|
|
|
}, waitTime)
|
|
|
|
}
|
|
|
|
}, [props?.opacity])
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (show || props?.opacity) {
|
|
|
|
clearInterval(hideRef?.current)
|
2023-08-22 00:36:01 +00:00
|
|
|
events.next({
|
|
|
|
type: "settings-menu-hide",
|
|
|
|
uuid,
|
|
|
|
})
|
2023-08-22 11:31:37 +00:00
|
|
|
} else if (!show) {
|
|
|
|
setPinStatus(false)
|
2023-08-15 01:01:05 +00:00
|
|
|
}
|
2023-08-22 00:36:01 +00:00
|
|
|
}, [show, props?.opacity, events, uuid])
|
2023-08-15 01:01:05 +00:00
|
|
|
|
|
|
|
const maybeHide = React.useCallback(() => {
|
|
|
|
clearInterval(hideRef?.current)
|
|
|
|
hideRef.current = setTimeout(() => {
|
2023-08-22 11:31:37 +00:00
|
|
|
if (!stateRef?.current?.pinStatus) {
|
|
|
|
setShow(false)
|
|
|
|
setOpacity(0)
|
|
|
|
}
|
2023-08-15 01:01:05 +00:00
|
|
|
}, waitTime)
|
|
|
|
}, [])
|
2023-08-14 02:58:50 +00:00
|
|
|
|
|
|
|
const showMenu = React.useCallback(() => {
|
2023-08-22 11:31:37 +00:00
|
|
|
clearInterval(hideRef?.current)
|
2023-08-14 02:58:50 +00:00
|
|
|
setShow(true)
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
const hideMenu = React.useCallback(() => {
|
|
|
|
setShow(false)
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
const basePadding = React.useMemo(() => {
|
|
|
|
return 4
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
const types = React.useMemo(() => {
|
2023-08-22 11:31:37 +00:00
|
|
|
const baseTypes = thingtime?.settings?.types?.javascript || {}
|
|
|
|
const baseTypeKeys = Object.keys(baseTypes)
|
|
|
|
|
|
|
|
const customTypes = thingtime?.settings?.types?.custom || {}
|
|
|
|
const customTypeKeysRaw = Object.keys(customTypes)
|
|
|
|
const customTypeKeys = customTypeKeysRaw?.filter((key) => {
|
|
|
|
return !baseTypeKeys?.includes?.(key)
|
|
|
|
})
|
|
|
|
|
|
|
|
const types = [
|
|
|
|
...(baseTypeKeys?.map?.((key) => {
|
|
|
|
return {
|
|
|
|
...baseTypes?.[key],
|
|
|
|
key,
|
|
|
|
}
|
|
|
|
}) || []),
|
|
|
|
...(customTypeKeys?.map?.((key) => {
|
|
|
|
return {
|
|
|
|
...customTypes?.[key],
|
|
|
|
key,
|
|
|
|
}
|
|
|
|
}) || []),
|
2023-08-14 05:10:46 +00:00
|
|
|
]
|
2023-08-14 02:58:50 +00:00
|
|
|
|
2023-08-22 11:31:37 +00:00
|
|
|
return types
|
|
|
|
}, [
|
|
|
|
thingtime?.settings?.types?.javascript,
|
|
|
|
thingtime?.settings?.types?.custom,
|
|
|
|
])
|
|
|
|
|
|
|
|
const onType = React.useCallback(
|
|
|
|
(args) => {
|
|
|
|
props?.onType?.(args)
|
2023-08-14 02:58:50 +00:00
|
|
|
},
|
2023-08-22 11:31:37 +00:00
|
|
|
[props?.onType]
|
2023-08-14 02:58:50 +00:00
|
|
|
)
|
2023-08-14 05:10:46 +00:00
|
|
|
const onDelete = React.useCallback(
|
|
|
|
(type) => {
|
|
|
|
props?.onDelete?.()
|
|
|
|
},
|
|
|
|
[props?.onDelete]
|
|
|
|
)
|
2023-08-14 02:58:50 +00:00
|
|
|
|
2023-08-22 11:31:37 +00:00
|
|
|
const childIconSize = 10
|
|
|
|
const iconSize = props?.iconSize || 7
|
2023-08-14 02:58:50 +00:00
|
|
|
|
|
|
|
return (
|
2023-08-15 01:01:05 +00:00
|
|
|
<ClickAwayListener onClickAway={hideMenu}>
|
|
|
|
<Center
|
|
|
|
position="relative"
|
|
|
|
// width="100%"
|
|
|
|
paddingRight={36}
|
|
|
|
opacity={opacity}
|
|
|
|
transition={props?.transition || "all 0.2s ease-in-out"}
|
|
|
|
onMouseEnter={showMenu}
|
|
|
|
onMouseLeave={maybeHide}
|
2023-08-14 02:58:50 +00:00
|
|
|
>
|
|
|
|
<Flex
|
2023-08-15 01:01:05 +00:00
|
|
|
paddingLeft={1}
|
|
|
|
// opacity={showContextIcon ? 1 : 0}
|
|
|
|
cursor="pointer"
|
|
|
|
// onClick={deleteValue}
|
|
|
|
transition="all 0.2s ease-in-out"
|
|
|
|
>
|
2023-08-22 11:31:37 +00:00
|
|
|
<Icon name="wizard" size={iconSize}></Icon>
|
2023-08-15 01:01:05 +00:00
|
|
|
</Flex>
|
|
|
|
<Flex
|
|
|
|
position="absolute"
|
|
|
|
zIndex={999}
|
|
|
|
top="100%"
|
|
|
|
left={0}
|
2023-08-14 02:58:50 +00:00
|
|
|
flexDirection="column"
|
2023-08-15 01:01:05 +00:00
|
|
|
opacity={show ? 1 : 0}
|
|
|
|
pointerEvents={show ? "all" : "none"}
|
2023-08-14 02:58:50 +00:00
|
|
|
>
|
2023-08-22 11:31:37 +00:00
|
|
|
<Flex
|
|
|
|
position="absolute"
|
|
|
|
top={0}
|
|
|
|
right={0}
|
|
|
|
padding="5px"
|
|
|
|
cursor="pointer"
|
|
|
|
onClick={() => setPinStatus((prev) => !prev)}
|
|
|
|
>
|
|
|
|
<Icon
|
|
|
|
opacity={pinStatus ? 1 : 0.5}
|
|
|
|
name={pinStatus ? "pinned" : "pin"}
|
|
|
|
size="8px"
|
|
|
|
></Icon>
|
|
|
|
</Flex>
|
2023-08-14 02:58:50 +00:00
|
|
|
<Flex
|
|
|
|
flexDirection="column"
|
2023-08-15 01:01:05 +00:00
|
|
|
// rowGap={basePadding / 3}
|
2023-08-14 02:58:50 +00:00
|
|
|
background="greys.lightt"
|
2023-08-15 01:01:05 +00:00
|
|
|
borderRadius={4}
|
|
|
|
boxShadow={props?.boxShadow || "0px 2px 7px 0px rgba(0,0,0,0.2)"}
|
|
|
|
paddingY={basePadding}
|
2023-08-14 02:58:50 +00:00
|
|
|
>
|
2023-08-15 01:01:05 +00:00
|
|
|
{!props?.readonly && (
|
|
|
|
<Flex
|
|
|
|
alignItems="center"
|
|
|
|
flexDirection="row"
|
2023-08-22 11:31:37 +00:00
|
|
|
// paddingRight={basePadding}
|
|
|
|
paddingLeft={basePadding}
|
2023-08-15 01:02:24 +00:00
|
|
|
_hover={{
|
|
|
|
background: "greys.light",
|
|
|
|
}}
|
|
|
|
cursor="pointer"
|
2023-08-15 01:01:05 +00:00
|
|
|
// paddingX={basePadding * 1}
|
|
|
|
paddingY={basePadding / 2}
|
|
|
|
>
|
2023-08-22 11:31:37 +00:00
|
|
|
<Icon
|
|
|
|
marginBottom="-2px"
|
|
|
|
name="cyclone"
|
|
|
|
size={childIconSize}
|
|
|
|
></Icon>
|
2023-08-15 01:01:05 +00:00
|
|
|
<Text marginTop="-2px" paddingLeft={2} fontSize="xs">
|
|
|
|
Types
|
|
|
|
</Text>
|
|
|
|
</Flex>
|
|
|
|
)}
|
|
|
|
<Flex
|
|
|
|
flexDirection="column"
|
|
|
|
// rowGap={basePadding}
|
2023-08-22 11:31:37 +00:00
|
|
|
overflowY="scroll"
|
|
|
|
maxHeight="300px"
|
2023-08-15 01:01:05 +00:00
|
|
|
background="greys.lightt"
|
|
|
|
cursor="pointer"
|
|
|
|
>
|
|
|
|
{!props?.readonly &&
|
|
|
|
types.map((type, idx) => {
|
|
|
|
const ret = (
|
2023-08-14 03:13:23 +00:00
|
|
|
<Flex
|
2023-08-15 01:01:05 +00:00
|
|
|
key={props?.uuid + props?.fullPath + "-type-menu-" + idx}
|
2023-08-14 03:13:23 +00:00
|
|
|
width="100%"
|
2023-08-15 01:01:05 +00:00
|
|
|
_hover={{
|
|
|
|
"&>div": {
|
|
|
|
background: "greys.light",
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
cursor="pointer"
|
2023-08-22 11:31:37 +00:00
|
|
|
onClick={() => onType({ type })}
|
2023-08-15 01:01:05 +00:00
|
|
|
paddingY={1}
|
2023-08-14 03:13:23 +00:00
|
|
|
>
|
2023-08-15 01:01:05 +00:00
|
|
|
<Flex
|
|
|
|
alignItems="center"
|
|
|
|
flexDirection="row"
|
|
|
|
width="100%"
|
2023-08-22 11:31:37 +00:00
|
|
|
paddingRight={basePadding}
|
2023-08-15 01:01:05 +00:00
|
|
|
paddingLeft={basePadding * 2}
|
|
|
|
paddingY={basePadding / 2}
|
|
|
|
>
|
|
|
|
<Icon
|
|
|
|
marginBottom="-2px"
|
|
|
|
name={type?.icon || type?.key || type?.label || type}
|
2023-08-22 11:31:37 +00:00
|
|
|
size={childIconSize}
|
2023-08-15 01:01:05 +00:00
|
|
|
></Icon>
|
|
|
|
<Text marginTop="-2px" paddingLeft={2} fontSize="xs">
|
|
|
|
{type?.label || type?.key || type}
|
|
|
|
</Text>
|
2023-08-22 11:31:37 +00:00
|
|
|
{type?.wrap && (
|
|
|
|
<Flex
|
|
|
|
marginLeft="auto"
|
|
|
|
_hover={{
|
|
|
|
transform: "scale(1.3)",
|
|
|
|
}}
|
|
|
|
transition="all 0.2s ease-out"
|
|
|
|
onClick={(e) => {
|
|
|
|
e?.preventDefault?.()
|
|
|
|
e?.stopPropagation?.()
|
|
|
|
// cancel bubble
|
|
|
|
e?.nativeEvent?.stopImmediatePropagation?.()
|
|
|
|
onType({
|
|
|
|
type,
|
|
|
|
wrap: true,
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Icon name="wrap" size={childIconSize}></Icon>
|
|
|
|
</Flex>
|
|
|
|
)}
|
2023-08-15 01:01:05 +00:00
|
|
|
</Flex>
|
2023-08-14 03:13:23 +00:00
|
|
|
</Flex>
|
2023-08-15 01:01:05 +00:00
|
|
|
)
|
|
|
|
return ret
|
|
|
|
})}
|
2023-08-14 05:10:46 +00:00
|
|
|
</Flex>
|
2023-08-22 11:31:37 +00:00
|
|
|
{!props?.readonly && props?.onDelete && (
|
2023-08-15 01:01:05 +00:00
|
|
|
<Flex
|
|
|
|
alignItems="center"
|
|
|
|
flexDirection="row"
|
|
|
|
_hover={{
|
|
|
|
background: "greys.light",
|
|
|
|
}}
|
|
|
|
cursor="pointer"
|
|
|
|
onClick={onDelete}
|
|
|
|
paddingX={basePadding * 1}
|
|
|
|
paddingY={basePadding / 2}
|
|
|
|
>
|
2023-08-22 11:31:37 +00:00
|
|
|
<Icon
|
|
|
|
marginBottom="-2px"
|
|
|
|
name="bin"
|
|
|
|
size={childIconSize}
|
|
|
|
></Icon>
|
2023-08-15 01:01:05 +00:00
|
|
|
<Text marginTop="-2px" paddingLeft={2} fontSize="xs">
|
|
|
|
Recycle
|
|
|
|
</Text>
|
|
|
|
</Flex>
|
|
|
|
)}
|
|
|
|
</Flex>
|
2023-08-14 02:58:50 +00:00
|
|
|
</Flex>
|
2023-08-15 01:01:05 +00:00
|
|
|
</Center>
|
|
|
|
</ClickAwayListener>
|
2023-08-14 02:58:50 +00:00
|
|
|
)
|
|
|
|
}
|