diff options
Diffstat (limited to 'pages/index.tsx')
-rw-r--r-- | pages/index.tsx | 452 |
1 files changed, 448 insertions, 4 deletions
diff --git a/pages/index.tsx b/pages/index.tsx index fb15ddc..dcdb225 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -9,6 +9,42 @@ import { GraphData, NodeObject } from 'force-graph' import { useWindowSize } from '@react-hook/window-size' +import { + Accordion, + AccordionButton, + AccordionItem, + AccordionIcon, + AccordionPanel, + Text, + Heading, + VStack, + StackDivider, + Button, + CloseButton, + Slider, + SliderThumb, + SliderTrack, + SliderFilledTrack, + Switch, + FormControl, + FormLabel, + Box, + Container, + Icon, + IconButton, + Tooltip, + Menu, + MenuList, + MenuButton, + MenuItem, + MenuGroup, + MenuDivider, + MenuOptionGroup, + MenuItemOption, +} from '@chakra-ui/react' + +import { InfoOutlineIcon, RepeatClockIcon, ChevronDownIcon } from '@chakra-ui/icons' + // react-force-graph fails on import when server-rendered // https://github.com/vasturiano/react-force-graph/issues/155 const ForceGraph2D = ( @@ -19,6 +55,7 @@ export type NodeById = { [nodeId: string]: OrgRoamNode | undefined } export type LinksByNodeId = { [nodeId: string]: OrgRoamLink[] | undefined } const initialPhysics = { + enabled: true, charge: -350, collision: true, linkStrength: 0.1, @@ -30,15 +67,18 @@ const initialPhysics = { nodeRel: 4, labels: true, labelScale: 1.5, - alphaDecay: 0.16, + alphaDecay: 0.02, alphaTarget: 0, + alphaMin: 0, velocityDecay: 0.25, gravity: 0.5, gravityOn: true, - hover: true, colorful: true, galaxy: true, - rootId: 0, + ticks: 1, + hover: 'highlight', + click: 'select', + doubleClick: 'local', } const initialTheme = { @@ -127,6 +167,14 @@ export function GraphPage() { return ( <div> + <Tweaks + {...{ + physics, + setPhysics, + threeDim, + local, + }} + /> <Graph nodeById={nodeByIdRef.current!} linksByNodeId={linksByNodeIdRef.current!} @@ -141,6 +189,402 @@ export function GraphPage() { ) } +export interface TweakProps { + physics: typeof initialPhysics + setPhysics: any + threeDim: boolean + local: boolean +} + +export const Tweaks = function (props: TweakProps) { + const { physics, setPhysics, threeDim, local } = props + + interface InfoTooltipProps { + infoText: string + } + 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> + ) + } + + interface SliderWithInfo { + min: number + max: number + step: number + value: number + onChange: (arg0: number) => void + label: string + infoText: string + } + + const SliderWithInfo = (props: SliderWithLabel) => { + const { min, max, step, value, onChange, label, infoText } = props + return ( + <Box> + <Box display="flex" alignItems="center"> + <Text>{label}</Text> + <InfoTooltip infoText={infoText} /> + </Box> + <Slider value={value} onChange={onChange} min={min} max={max} step={step}> + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={value} color="gray.100" bg="gray.800"> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + ) + } + + interface SliderWithoutInfo { + min: number + max: number + step: number + val: number + change: (arg0: number) => void + label: string + physics: typeof initialPhysics + setPhysics: any + } + + const SliderWithoutInfo = (props: SliderWithoutInfo) => { + const { min, max, step, physics, setPhysics, label } = props + return ( + <Box> + <Box display="flex"> + <Text>{label}</Text> + </Box> + <Slider + value={physics.gravity} + onChange={(value) => setPhysics({ ...physics, gravity: value })} + min={min} + max={max} + step={step} + focusThumbOnChange={false} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <SliderThumb /> + </Slider> + </Box> + ) + } + + return ( + <Container zIndex="overlay" position="absolute" bg="white" w="xs"> + <Box display="flex" justifyContent="flex-end"> + <Tooltip label="Reset settings to defaults"> + <IconButton + aria-label="Reset Defaults" + icon={<RepeatClockIcon />} + onClick={() => setPhysics(initialPhysics)} + /> + </Tooltip> + <CloseButton /> + </Box> + <Accordion allowMultiple defaultIndex={[0]} allowToggle> + <AccordionItem> + <AccordionButton display="flex" justifyContent="space-between"> + <Box display="flex"> + <AccordionIcon /> + <Text>Physics</Text> + </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.200" />} + align="stretch" + > + <Box> + <Box display="flex" justifyContent="space-between"> + <Text>Gravity</Text> + <Switch + id="physicsOn" + onChange={() => setPhysics({ ...physics, gravityOn: !physics.gravityOn })} + isChecked={physics.gravityOn} + /> + </Box> + {physics.gravityOn && ( + <Box> + <Text>Strength</Text> + <Slider + value={physics.gravity} + onChange={(value) => setPhysics({ ...physics, gravity: value })} + min={0} + max={1} + step={0.01} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.gravity}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + )} + </Box> + <SliderWithoutInfo + label="Test" + min={0} + max={1} + step={0.01} + physics={physics} + setPhysics={setPhysics} + /> + <Box> + <Text>Repulsive Force</Text> + <Slider + value={-physics.charge} + onChange={(value) => setPhysics({ ...physics, charge: -value })} + min={0} + max={1000} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.charge}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + <Box> + <Box display="flex" justifyContent="space-between"> + <Box display="flex" alignItems="center"> + <Text>Collision</Text> + <InfoTooltip infoText="This is rather slow, disable if experiencing performance issues" /> + </Box> + <Switch + id="physicsCollision" + onChange={() => setPhysics({ ...physics, collision: !physics.collision })} + isChecked={physics.collision} + /> + </Box> + {physics.collision && ( + <Box> + <Text>Strength</Text> + <Slider + value={physics.collisionStrength} + onChange={(value) => setPhysics({ ...physics, collisionStrength: value })} + min={0} + max={2} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.collisionStrength}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + )} + </Box> + <Box> + <Text>Link Strength</Text> + <Slider + value={physics.linkStrength} + onChange={(value) => setPhysics({ ...physics, linkStrength: value })} + min={0} + max={2} + step={0.01} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.linkStrength}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + <StackDivider bg="grey.200" /> + <Box> + <Box display="flex" alignItems="center"> + <Text>Link Iterations</Text> + <InfoTooltip infoText="How many links down the line the physics of a single node effects (Slow)" /> + </Box> + <Slider + value={physics.linkIts} + onChange={(value) => setPhysics({ ...physics, linkIts: value })} + min={0} + max={6} + step={1} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.linkIts}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + <Box> + <Text>Viscosity</Text> + <Slider + value={physics.velocityDecay} + onChange={(value) => setPhysics({ ...physics, velocityDecay: value })} + min={0} + max={1} + step={0.01} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.velocityDecay}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + </VStack> + <Box> + <Accordion allowToggle> + <AccordionItem> + <AccordionButton> + <Text>Advanced</Text> + <AccordionIcon /> + </AccordionButton> + <AccordionPanel> + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.200" />} + align="stretch" + > + <Box> + <Text>Simulation Ticks</Text> + <Slider value={0} min={0} max={1} step={0.01}> + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.ticks}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + <Box> + <Text>Alpha Decay</Text> + <Slider + value={physics.alphaDecay} + onChange={(value) => setPhysics({ ...physics, alphaDecay: value })} + min={0} + max={1} + step={0.01} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.alphaDecay}> + <SliderThumb /> + </Tooltip> + </Slider> + </Box> + <Text>Alpha Minimum</Text> + <Slider + value={physics.alphaMin} + onChange={(value) => setPhysics({ ...physics, alphaMin: value })} + min={0.01} + max={1} + step={0.01} + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={physics.alphaMin}> + <SliderThumb /> + </Tooltip> + </Slider> + </VStack> + </AccordionPanel> + </AccordionItem> + </Accordion> + </Box> + {/* </VStack> */} + </AccordionPanel> + </AccordionItem> + <AccordionItem> + <AccordionButton> + <AccordionIcon /> + Visual + </AccordionButton> + <AccordionPanel> + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.200" />} + align="stretch" + > + <Text>Ayyyy</Text> + </VStack> + </AccordionPanel> + </AccordionItem> + <AccordionItem> + <AccordionButton> + <AccordionIcon /> + Behavior + </AccordionButton> + <AccordionPanel> + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.200" />} + align="stretch" + > + <Box display="flex" justifyContent="space-between" alignItems="center"> + <Text>Hover Higlight</Text> + <Menu> + <MenuButton as={Button} rightIcon={<ChevronDownIcon />}> + {physics.hover} + </MenuButton> + <MenuList> + <MenuItem>Off</MenuItem> + <MenuItem>On</MenuItem> + </MenuList> + </Menu> + </Box> + <Box display="flex" justifyContent="space-between"> + <Text>Click</Text> + </Box> + <Box display="flex" justifyContent="space-between"> + <Text>Double-click</Text> + </Box> + </VStack> + </AccordionPanel> + </AccordionItem> + <AccordionItem> + <AccordionButton> + <AccordionIcon /> + Visual + </AccordionButton> + <AccordionPanel> + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.200" />} + align="stretch" + > + <Text>Ayyyy</Text> + </VStack> + </AccordionPanel> + </AccordionItem> + </Accordion> + </Container> + ) +} + export interface GraphProps { nodeById: NodeById linksByNodeId: LinksByNodeId @@ -360,7 +804,7 @@ export const Graph = function (props: GraphProps) { }} nodeCanvasObjectMode={() => 'after'} d3AlphaDecay={physics.alphaDecay} - d3AlphaMin={physics.alphaTarget} + d3AlphaMin={physics.alphaMin} d3VelocityDecay={physics.velocityDecay} backgroundColor={'#242730'} onNodeClick={selectClick} |