import { CloseIcon, RepeatClockIcon, ChevronDownIcon, SettingsIcon, InfoOutlineIcon, RepeatIcon, ArrowRightIcon, } from '@chakra-ui/icons' import { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Button, Flex, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuOptionGroup, MenuItemOption, Select, Slider, SliderFilledTrack, SliderThumb, SliderTrack, StackDivider, Switch, Text, Tooltip, VStack, Heading, Collapse, Grid, Portal, SlideFade, } from '@chakra-ui/react' import React, { useState, useContext } from 'react' import Scrollbars from 'react-custom-scrollbars-2' import { initialPhysics, initialFilter, initialVisuals, initialMouse, initialBehavior, } from './config' import { ThemeContext } from '../util/themecontext' export interface TweakProps { physics: typeof initialPhysics setPhysics: any threeDim: boolean setThreeDim: (newValue: boolean) => void filter: typeof initialFilter setFilter: any visuals: typeof initialVisuals setVisuals: any mouse: typeof initialMouse setMouse: any behavior: typeof initialBehavior setBehavior: any } export const Tweaks = (props: TweakProps) => { const { physics, setPhysics, threeDim, setThreeDim, filter, setFilter, visuals, setVisuals, mouse, setMouse, behavior, setBehavior, } = props const [showTweaks, setShowTweaks] = useState(true) const { highlightColor, setHighlightColor } = useContext(ThemeContext) const colorList = [ 'red.500', 'orange.500', 'yellow.500', 'green.500', 'cyan.500', 'blue.500', 'pink.500', 'purple.500', 'gray.400', 'gray.500', 'gray.600', 'white', 'black', ] const grays = [ 'black', 'gray.100', 'gray.200', 'gray.300', 'gray.400', 'gray.500', 'gray.600', 'gray.700', 'gray.800', 'gray.900', 'white', ] return ( <> <SlideFade in={!showTweaks}> <Box position="absolute" zIndex="overlay" marginTop={10} marginLeft={10} display={showTweaks ? 'none' : 'block'} > <IconButton aria-label="Settings" icon={<SettingsIcon />} onClick={() => setShowTweaks(true)} /> </Box> </SlideFade> <SlideFade in={showTweaks}> <Box bg="alt.100" w="xs" marginTop={10} marginLeft={10} borderRadius="xl" maxH={650} paddingBottom={5} zIndex={300} position="relative" boxShadow="xl" > <Box display="flex" justifyContent="space-between" alignItems="center" paddingRight={2} paddingTop={1} > <Tooltip label={'Switch to ' + threeDim ? '2D' : '3D' + ' view'}> <Button onClick={() => setThreeDim(!threeDim)} variant="ghost" zIndex="overlay"> {threeDim ? '3D' : '2D'} </Button> </Tooltip> <Box display="flex" alignItems="center"> <Tooltip label="Reset settings to defaults"> <IconButton aria-label="Reset Defaults" icon={<RepeatClockIcon />} onClick={() => { setVisuals(initialVisuals) setFilter(initialFilter) setMouse(initialMouse) setPhysics(initialPhysics) setBehavior(initialBehavior) }} variant="none" size="sm" /> </Tooltip> <IconButton size="sm" icon={<CloseIcon />} aria-label="Close Tweak Panel" variant="ghost" onClick={() => setShowTweaks(false)} /> </Box> </Box> <Scrollbars autoHeight autoHeightMax={600} autoHide renderThumbVertical={({ style, ...props }) => ( <Box {...props} style={{ ...style, borderRadius: 10, }} bg={highlightColor} /> )} > <Accordion allowMultiple allowToggle color="black"> <AccordionItem> <AccordionButton> <AccordionIcon marginRight={2} /> <Heading size="sm">Filter</Heading> </AccordionButton> <AccordionPanel> <VStack spacing={2} justifyContent="flex-start" divider={<StackDivider borderColor="gray.500" />} align="stretch" paddingLeft={7} color="gray.800" > <Flex justifyContent="space-between"> <Text>Orphans</Text> <Switch onChange={() => { setFilter({ ...filter, orphans: !filter.orphans }) }} isChecked={filter.orphans} ></Switch> </Flex> <Flex justifyContent="space-between"> <Text>Link nodes with parent file</Text> <Switch onChange={() => { setFilter({ ...filter, parents: !filter.parents }) }} isChecked={filter.parents} ></Switch> </Flex> </VStack> </AccordionPanel> </AccordionItem> <AccordionItem> <AccordionButton display="flex" justifyContent="space-between"> <Box display="flex"> <AccordionIcon marginRight={2} /> <Heading size="sm">Physics</Heading> </Box> {/* <Switch id="physicsOn" onChange={() => setPhysics({ ...physics, enabled: !physics.enabled })} isChecked={physics.enabled} /> */} </AccordionButton> <AccordionPanel> <VStack spacing={2} justifyContent="flex-start" divider={<StackDivider borderColor="gray.500" />} align="stretch" paddingLeft={7} color="gray.800" > <EnableSection label="Gravity" value={physics.gravityOn} onChange={() => setPhysics({ ...physics, gravityOn: !physics.gravityOn })} > <SliderWithInfo label="Strength" value={physics.gravity * 10} onChange={(v) => setPhysics({ ...physics, gravity: v / 10 })} /> </EnableSection> <SliderWithInfo value={-physics.charge / 100} onChange={(value) => setPhysics({ ...physics, charge: -100 * value })} label="Repulsive Force" /> <EnableSection label="Collision" infoText="Perfomance sap, disable if slow" value={physics.collision} onChange={() => setPhysics({ ...physics, collision: !physics.collision })} > <SliderWithInfo value={physics.collisionStrength / 5} onChange={(value) => setPhysics({ ...physics, collisionStrength: value * 5 }) } label="Collision Radius" infoText="Easy with this one, high values can lead to a real jiggly mess" /> </EnableSection> <SliderWithInfo value={physics.linkStrength * 5} onChange={(value) => setPhysics({ ...physics, linkStrength: value / 5 })} label="Link Force" /> <SliderWithInfo label="Link Iterations" value={physics.linkIts} onChange={(value) => setPhysics({ ...physics, linkIts: value })} min={0} max={6} step={1} infoText="How many links down the line the physics of a single node affects (Slow)" /> <SliderWithInfo label="Viscosity" value={physics.velocityDecay * 10} onChange={(value) => setPhysics({ ...physics, velocityDecay: value / 10 })} /> </VStack> <Box> <Accordion paddingLeft={3} allowToggle> <AccordionItem> <AccordionButton> <Text>Advanced</Text> <AccordionIcon marginRight={2} /> </AccordionButton> <AccordionPanel> <VStack spacing={2} justifyContent="flex-start" divider={<StackDivider borderColor="gray.500" />} align="stretch" paddingLeft={3} color="gray.800" > <SliderWithInfo label="Stabilization rate" value={physics.alphaDecay * 50} onChange={(value) => setPhysics({ ...physics, alphaDecay: value / 50 }) } /> <EnableSection label="Center nodes" value={physics.centering} onChange={() => setPhysics({ ...physics, centering: !physics.centering }) } infoText="Keeps the nodes in the center of the viewport. If disabled you can drag the nodes anywhere you want." > <SliderWithInfo label="Centering Strength" value={physics.centeringStrength} max={2} step={0.01} onChange={(v) => setPhysics({ ...physics, centeringStrength: v })} /> </EnableSection> </VStack> </AccordionPanel> </AccordionItem> </Accordion> </Box> </AccordionPanel> </AccordionItem> <AccordionItem> <AccordionButton> <AccordionIcon marginRight={2} /> <Heading size="sm">Visual</Heading> </AccordionButton> <AccordionPanel> <VStack justifyContent="flex-start" align="stretch"> <Accordion allowToggle defaultIndex={[0]} paddingLeft={3}> <AccordionItem> <AccordionButton> <Flex justifyContent="space-between" w="100%"> <Text>Colors</Text> <AccordionIcon marginRight={2} /> </Flex> </AccordionButton> <AccordionPanel> <VStack spacing={2} justifyContent="flex-start" divider={<StackDivider borderColor="gray.500" />} align="stretch" color="gray.800" > <Box> <Flex alignItems="center" justifyContent="space-between"> <Text>Nodes</Text> <Tooltip label="Shuffle node colors"> <IconButton aria-label="Shuffle node colors" size="sm" icon={<RepeatIcon />} variant="ghost" onClick={() => { const arr = visuals.nodeColorScheme ?? [] setVisuals({ ...visuals, //shuffle that guy //definitely thought of this myself nodeColorScheme: arr .map((x: any) => [Math.random(), x]) .sort(([a], [b]) => a - b) .map(([_, x]) => x), }) }} /> </Tooltip> <Tooltip label="Cycle node colors"> <IconButton aria-label="Shift node colors" icon={<ArrowRightIcon />} size="sm" variant="ghost" onClick={() => { const arr = visuals.nodeColorScheme ?? [] setVisuals({ ...visuals, nodeColorScheme: [...arr.slice(1, arr.length), arr[0]], }) }} /> </Tooltip> <Menu placement="right" closeOnSelect={false} matchWidth> <MenuButton width={20} as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > <Flex height={6} width={6} flexDirection="column" flexWrap="wrap" > {visuals.nodeColorScheme.map((color) => ( <Box key={color} bgColor={color} flex="1 1 8px" borderRadius="2xl" ></Box> ))} </Flex> </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bgColor="gray.200"> <MenuOptionGroup width={500} type="checkbox" defaultValue={visuals.nodeColorScheme} onChange={(colors) => { if (!colors.length) { return } setVisuals({ ...visuals, nodeColorScheme: colors }) }} > {colorList.map((color) => ( <MenuItemOption key={color} isChecked={visuals.nodeColorScheme.some( (c) => c === color, )} value={color} isDisabled={ visuals.nodeColorScheme.length === 1 && visuals.nodeColorScheme[0] === color } > <Box justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </Box> </MenuItemOption> ))} </MenuOptionGroup> </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Links</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > <Box> {visuals.linkColorScheme ? ( <Box bgColor={visuals.linkColorScheme} borderRadius="sm" height={6} width={6} ></Box> ) : ( <Flex height={6} width={6} flexDirection="column" flexWrap="wrap" > {visuals.nodeColorScheme.map((color) => ( <Box key={color} bgColor={color} flex="1 1 8px" borderRadius="2xl" ></Box> ))} </Flex> )} </Box> </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bgColor="gray.200"> <MenuItem onClick={() => setVisuals({ ...visuals, linkColorScheme: '' }) } justifyContent="space-between" alignItems="center" display="flex" > <Flex height={6} width={6} flexDirection="column" flexWrap="wrap" > {visuals.nodeColorScheme.map((color) => ( <Box key={color} bgColor={color} flex="1 1 8px" borderRadius="2xl" ></Box> ))} </Flex> </MenuItem> {grays.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, linkColorScheme: color, }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Accent</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > { <Box bgColor={highlightColor} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bgColor="gray.200"> {colorList.map((color) => ( <MenuItem key={color} onClick={() => setHighlightColor(color)} justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Link Highlight</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > { <Box bgColor={visuals.linkHighlight} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bgColor="gray.200"> <MenuItem key={'none'} onClick={() => setVisuals({ ...visuals, linkHighlight: '' }) } justifyContent="space-between" alignItems="center" display="flex" > <Box borderRadius="sm" height={6} width={6}></Box> </MenuItem> {colorList.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, linkHighlight: color }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Node Highlight</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > { <Box bgColor={visuals.nodeHighlight} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bgColor="gray.200"> <MenuItem key={'none'} onClick={() => setVisuals({ ...visuals, nodeHighlight: '' }) } justifyContent="space-between" alignItems="center" display="flex" > <Box borderRadius="sm" height={6} width={6}></Box> </MenuItem> {colorList.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, nodeHighlight: color }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Background</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > { <Box bgColor={visuals.backgroundColor} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minWidth={10} zIndex="popover" bgColor="gray.200"> {grays.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, backgroundColor: color }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Emacs Node</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > { <Box bgColor={visuals.emacsNodeColor} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minWidth={10} zIndex="popover" bgColor="gray.200"> <MenuItem key={'none'} onClick={() => setVisuals({ ...visuals, emacsNodeColor: '' }) } justifyContent="space-between" alignItems="center" display="flex" > <Box borderRadius="sm" height={6} width={6}></Box> </MenuItem> {colorList.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, emacsNodeColor: color }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> </Box> </VStack> </AccordionPanel> </AccordionItem> </Accordion> <VStack spacing={2} justifyContent="flex-start" divider={<StackDivider borderColor="gray.500" />} align="stretch" paddingLeft={7} color="gray.800" > <SliderWithInfo label="Node size" value={visuals.nodeRel} onChange={(value) => setVisuals({ ...visuals, nodeRel: value })} /> {threeDim && ( <> <SliderWithInfo label="Node opacity" value={visuals.nodeOpacity} min={0} max={1} onChange={(value) => setVisuals({ ...visuals, nodeOpacity: value })} /> <SliderWithInfo label="Node resolution" value={visuals.nodeResolution} min={5} max={32} step={1} onChange={(value) => setVisuals({ ...visuals, nodeResolution: value })} /> </> )} <SliderWithInfo label="Link width" value={visuals.linkWidth} onChange={(value) => setVisuals({ ...visuals, linkWidth: value })} /> {threeDim && ( <SliderWithInfo label="Link opacity" min={0} max={1} value={visuals.linkOpacity} onChange={(value) => setVisuals({ ...visuals, linkOpacity: value })} /> )} <Box> <Flex alignItems="center" justifyContent="space-between"> <Text>Labels</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > {!visuals.labels ? 'Never' : visuals.labels < 2 ? 'On Highlight' : 'Always'} </MenuButton> <Portal> {' '} <MenuList zIndex="popover" bgColor="gray.200"> <MenuItem onClick={() => setVisuals({ ...visuals, labels: 0 })}> Never </MenuItem> <MenuItem onClick={() => setVisuals({ ...visuals, labels: 1 })}> On Highlight </MenuItem> <MenuItem onClick={() => setVisuals({ ...visuals, labels: 2 })}> Always </MenuItem> <MenuItem onClick={() => setVisuals({ ...visuals, labels: 3 })}> Always (even in 3D) </MenuItem> </MenuList> </Portal> </Menu> </Flex> <Collapse in={visuals.labels > 0} animateOpacity> <VStack spacing={1} justifyContent="flex-start" divider={<StackDivider borderColor="gray.400" />} align="stretch" paddingLeft={2} color="gray.800" > <Flex alignItems="center" justifyContent="space-between"> <Text>Text</Text> <Menu matchWidth> <MenuButton as={Button} rightIcon={<ChevronDownIcon />} color="black" colorScheme="" > { <Box bgColor={visuals.labelTextColor} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bg="gray.200"> {grays.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, labelTextColor: color, }) } > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Background</Text> <Menu placement="right"> <MenuButton as={Button} rightIcon={<ChevronDownIcon />} color="black" colorScheme="" > { <Box bgColor={visuals.labelBackgroundColor} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bg="gray.200"> <MenuItem onClick={() => setVisuals({ ...visuals, labelBackgroundColor: '', }) } justifyContent="space-between" alignItems="center" display="flex" > None </MenuItem> {grays.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, labelBackgroundColor: color, }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> <Collapse in={!!visuals.labelBackgroundColor} animateOpacity> <Box paddingTop={2}> <SliderWithInfo label="Background opacity" value={visuals.labelBackgroundOpacity} onChange={(value) => { console.log(visuals.labelBackgroundOpacity) setVisuals({ ...visuals, labelBackgroundOpacity: value }) }} min={0} max={1} step={0.01} /> </Box> </Collapse> <Collapse in={visuals.labels > 1} animateOpacity> <Box paddingTop={2}> <SliderWithInfo label="Label Appearance Scale" value={visuals.labelScale * 5} onChange={(value) => setVisuals({ ...visuals, labelScale: value / 5 }) } /> </Box> </Collapse> </VStack> </Collapse> </Box> <EnableSection label="Link arrows" value={visuals.arrows} onChange={() => setVisuals({ ...visuals, arrows: !visuals.arrows })} > <SliderWithInfo label="Arrow size" value={visuals.arrowsLength / 10} onChange={(value) => setVisuals({ ...visuals, arrowsLength: 10 * value })} /> <SliderWithInfo label="Arrow Position" value={visuals.arrowsPos} min={0} max={1} step={0.01} onChange={(value) => setVisuals({ ...visuals, arrowsPos: value })} /> <Flex alignItems="center" justifyContent="space-between"> <Text>Arrow Color</Text> <Menu placement="right"> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > { <Box bgColor={visuals.arrowsColor} borderRadius="sm" height={6} width={6} ></Box> } </MenuButton> <Portal> {' '} <MenuList minW={10} zIndex="popover" bgColor="gray.200"> <MenuItem onClick={() => setVisuals({ ...visuals, arrowsColor: '' })} justifyContent="space-between" alignItems="center" display="flex" > <Box height={6} width={6}></Box> </MenuItem> {colorList.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, arrowsColor: color, }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} {grays.map((color) => ( <MenuItem key={color} onClick={() => setVisuals({ ...visuals, arrowsColor: color, }) } justifyContent="space-between" alignItems="center" display="flex" > <Box bgColor={color} borderRadius="sm" height={6} width={6} ></Box> </MenuItem> ))} </MenuList> </Portal> </Menu> </Flex> </EnableSection> <EnableSection label="Directional Particles" value={visuals.particles} onChange={() => setVisuals({ ...visuals, particles: !visuals.particles })} > <SliderWithInfo label="Particle Number" value={visuals.particlesNumber} max={5} step={1} onChange={(value) => setVisuals({ ...visuals, particlesNumber: value })} /> <SliderWithInfo label="Particle Size" value={visuals.particlesWidth} onChange={(value) => setVisuals({ ...visuals, particlesWidth: value })} /> </EnableSection> <EnableSection label="Highlight" onChange={() => setVisuals({ ...visuals, highlight: !visuals.highlight })} value={visuals.highlight} > <VStack spacing={1} justifyContent="flex-start" divider={<StackDivider borderColor="gray.400" />} align="stretch" paddingLeft={0} > <SliderWithInfo label="Highlight Link Thickness" value={visuals.highlightLinkSize} onChange={(value) => setVisuals({ ...visuals, highlightLinkSize: value }) } /> <SliderWithInfo label="Highlight Node Size" value={visuals.highlightNodeSize} onChange={(value) => setVisuals({ ...visuals, highlightNodeSize: value }) } /> {/*<Flex justifyContent="space-between"> <Text> Highlight node color </Text> </Flex> <Flex justifyContent="space-between"> <Text> Highlight link color </Text> </Flex>*/} <EnableSection label="Highlight Animation" onChange={() => { setVisuals({ ...visuals, highlightAnim: !visuals.highlightAnim }) }} value={visuals.highlightAnim} > <SliderWithInfo label="Animation speed" onChange={(v) => setVisuals({ ...visuals, animationSpeed: v })} value={visuals.animationSpeed} infoText="Slower speed has a chance of being buggy" min={50} max={1000} step={10} /> <Select placeholder={visuals.algorithmName} onChange={(v) => { setVisuals({ ...visuals, algorithmName: v.target.value }) }} > {visuals.algorithmOptions.map((opt: string) => ( <option key={opt} value={opt}> {opt} </option> ))} </Select> </EnableSection> </VStack> </EnableSection> </VStack> </VStack> </AccordionPanel> </AccordionItem> <AccordionItem> <AccordionButton> <AccordionIcon marginRight={2} /> <Heading size="sm">Behavior</Heading> </AccordionButton> <AccordionPanel> <VStack spacing={2} justifyContent="flex-start" divider={<StackDivider borderColor="gray.500" />} align="stretch" paddingLeft={7} color="gray.800" > <Flex alignItems="center" justifyContent="space-between"> <Flex> <Text>Expand Node</Text> <InfoTooltip infoText="View only the node and its direct neighbors" /> </Flex> <Menu placement="right"> <MenuButton as={Button} rightIcon={<ChevronDownIcon />} colorScheme="" color="black" > <Text>{mouse.local[0]!.toUpperCase() + mouse.local!.slice(1)}</Text> </MenuButton> <Portal> {' '} <MenuList zIndex="popover" bgColor="gray.200"> <MenuItem onClick={() => setMouse({ ...mouse, local: '' })}> Never </MenuItem> <MenuItem onClick={() => setMouse({ ...mouse, local: 'click' })}> Click </MenuItem> <MenuItem onClick={() => setMouse({ ...mouse, local: 'double' })}> Double Click </MenuItem> <MenuItem onClick={() => setMouse({ ...mouse, local: 'right' })}> Right Click </MenuItem> </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Open in Emacs</Text> <Menu placement="right"> <MenuButton as={Button} rightIcon={<ChevronDownIcon />} colorScheme="" color="black" > <Text>{mouse.follow[0]!.toUpperCase() + mouse.follow!.slice(1)}</Text> </MenuButton> <Portal> {' '} <MenuList bgColor="gray.200" zIndex="popover"> <MenuItem onClick={() => setMouse({ ...mouse, follow: '' })}> Never </MenuItem> <MenuItem onClick={() => setMouse({ ...mouse, follow: 'click' })}> Click </MenuItem> <MenuItem onClick={() => setMouse({ ...mouse, follow: 'double' })}> Double Click </MenuItem> <MenuItem onClick={() => setMouse({ ...mouse, follow: 'right' })}> Right Click </MenuItem> </MenuList> </Portal> </Menu> </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Follow Emacs by...</Text> <Menu placement="right"> <MenuButton as={Button} rightIcon={<ChevronDownIcon />} colorScheme="" color="black" > <Text>{behavior.follow[0].toUpperCase() + behavior.follow.slice(1)}</Text> </MenuButton> <Portal> {' '} <MenuList bgColor="gray.200" zIndex="popover"> <MenuItem onClick={() => setBehavior({ ...behavior, follow: 'local' })}> Opening the local graph </MenuItem> <MenuItem onClick={() => setBehavior({ ...behavior, follow: 'zoom' })}> Zooming to the current node </MenuItem> </MenuList> </Portal> </Menu> </Flex> {/* <Flex alignItems="center" justifyContent="space-between"> <Flex> <Text>Follow local graph</Text> <InfoTooltip infoText="When in local mode and opening a node that already exists in Emacs, should I add that local graph or open the new one?" /> </Flex> <Menu placement="right"> <MenuButton as={Button} rightIcon={<ChevronDownIcon />} colorScheme="" color="black" > <Text>{behavior.localSame === 'add' ? 'Add' : 'New'}</Text> </MenuButton> <Portal> {' '} <MenuList bgColor="gray.200" zIndex="popover"> <MenuItem onClick={() => setBehavior({ ...behavior, localSame: 'new' })} > Open that nodes graph </MenuItem> <MenuItem onClick={() => setBehavior({ ...behavior, localSame: 'add' })} > Add node to local graph </MenuItem> </MenuList> </Portal> </Menu> </Flex> */} <SliderWithInfo label="Zoom speed" value={behavior.zoomSpeed} min={0} max={4000} step={100} onChange={(value) => setBehavior({ ...behavior, zoomSpeed: value })} /> <SliderWithInfo label="Zoom padding" value={behavior.zoomPadding} min={0} max={400} step={1} onChange={(value) => setBehavior({ ...behavior, zoomPadding: value })} infoText="How much to zoom out to accomodate all nodes when changing the view." /> </VStack> </AccordionPanel> </AccordionItem> </Accordion> </Scrollbars> </Box> </SlideFade> </> ) } export interface InfoTooltipProps { infoText?: string | boolean } export const InfoTooltip = (props: InfoTooltipProps) => { const { infoText } = props return ( <Box paddingLeft="1"> <Tooltip label={infoText} placement="top" color="gray.100" bg="gray.800" hasArrow> <InfoOutlineIcon /> </Tooltip> </Box> ) } export interface SliderWithInfoProps { min?: number max?: number step?: number value: number onChange: (arg0: number) => void label: string infoText?: string } export const SliderWithInfo = ({ min = 0, max = 10, step = 0.1, value = 1, ...rest }: SliderWithInfoProps) => { const { onChange, label, infoText } = rest const { highlightColor } = useContext(ThemeContext) return ( <Box> <Box display="flex" alignItems="flex-end"> <Text>{label}</Text> {infoText && <InfoTooltip infoText={infoText} />} </Box> <Slider value={value} onChange={onChange} min={min} max={max} step={step}> <SliderTrack> <SliderFilledTrack /> </SliderTrack> <Tooltip bg={highlightColor} label={value.toFixed(1)}> <SliderThumb bg="white" /> </Tooltip> </Slider> </Box> ) } export interface EnableSectionProps { label: string value: boolean | number onChange: () => void infoText?: string children: React.ReactNode } export const EnableSection = (props: EnableSectionProps) => { const { value, onChange, label, infoText, children } = props return ( <Box paddingTop={2}> <Box display="flex" justifyContent="space-between" paddingBottom={2}> <Box display="flex" alignItems="center"> <Text>{label}</Text> {infoText && <InfoTooltip infoText={infoText} />} </Box> <Switch isChecked={!!value} onChange={onChange} /> </Box> <Collapse in={!!value} animateOpacity> <Box paddingLeft={4} paddingTop={2} paddingBottom={2}> {children} </Box> </Collapse> </Box> ) } export interface DropDownMenuProps { textArray: string[] onClickArray: (() => void)[] displayValue: string } export const DropDownMenu = (props: DropDownMenuProps) => { const { textArray, onClickArray, displayValue } = props return ( <Menu placement="right"> <MenuButton as={Button} rightIcon={<ChevronDownIcon />}> {displayValue} </MenuButton> <Portal> {' '} <MenuList zIndex="popover"> {textArray.map((option, i) => { ;<MenuItem onClick={onClickArray[i]}> {option} </MenuItem> })} </MenuList> </Portal> </Menu> ) }