summaryrefslogtreecommitdiff
path: root/pages/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'pages/index.tsx')
-rw-r--r--pages/index.tsx452
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}