diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/config.ts | 65 | ||||
-rw-r--r-- | components/tweaks.tsx | 471 |
2 files changed, 536 insertions, 0 deletions
diff --git a/components/config.ts b/components/config.ts new file mode 100644 index 0000000..41d5345 --- /dev/null +++ b/components/config.ts @@ -0,0 +1,65 @@ +import { Easing } from '@tweenjs/tween.js' + +export const initialPhysics = { + enabled: true, + charge: -350, + collision: true, + collisionStrength: 0, + linkStrength: 0.1, + linkIts: 1, + particles: false, + particlesNumber: 0, + particlesWidth: 4, + linkOpacity: 0.4, + linkWidth: 1, + nodeRel: 4, + labels: true, + labelScale: 1.5, + alphaDecay: 0.02, + alphaTarget: 0, + alphaMin: 0, + velocityDecay: 0.25, + gravity: 0.5, + gravityOn: true, + colorful: true, + galaxy: true, + ticks: 1, + hover: 'highlight', + click: 'select', + doubleClick: 'local', + iterations: 0, + highlight: true, + highlightNodeSize: 2, + highlightLinkSize: 2, + highlightAnim: false, + animationSpeed: 250, + algorithms: getAlgos(false), + algorithmOptions: getAlgos(true), + algorithmName: 'CubicOut', + orphans: false, + follow: 'Local', +} + +export const initialFilter = { + orphans: false, + parents: true, + tags: [], + nodes: [], + links: [], + date: [], +} + +function getAlgos(option?: boolean) { + const options: string[] = [] + const algorithms: { [name: string]: (percent: number) => number } = {} + for (let type in Easing) { + for (let mode in (Easing as any)[type]) { + let name = type + mode + if (name === 'LinearNone') { + name = 'Linear' + } + option ? options.push(name) : (algorithms[name] = (Easing as any)[type][mode]) + } + } + return option ? options : algorithms +} diff --git a/components/tweaks.tsx b/components/tweaks.tsx new file mode 100644 index 0000000..a082306 --- /dev/null +++ b/components/tweaks.tsx @@ -0,0 +1,471 @@ +import { RepeatClockIcon, ChevronDownIcon, SettingsIcon, InfoOutlineIcon } from '@chakra-ui/icons' +import { + Accordion, + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + Box, + Button, + CloseButton, + Flex, + IconButton, + Menu, + MenuButton, + MenuItem, + MenuList, + Select, + Slider, + SliderFilledTrack, + SliderThumb, + SliderTrack, + StackDivider, + Switch, + Text, + Tooltip, + VStack, +} from '@chakra-ui/react' +import Scrollbars from 'react-custom-scrollbars-2' +import { initialPhysics, initialFilter } from './config' + +export interface TweakProps { + physics: typeof initialPhysics + setPhysics: any + threeDim: boolean + setThreeDim: (newValue: boolean) => void + filter: typeof initialFilter + setFilter: any + onClose: () => void +} + +export const Tweaks = (props: TweakProps) => { + const { physics, setPhysics, threeDim, filter, setFilter, onClose } = props + return ( + <Box + zIndex="overlay" + position="absolute" + bg="alt.100" + w="xs" + marginTop="2%" + marginLeft="2%" + borderRadius="md" + maxH={650} + paddingBottom={5} + //overflowY="scroll" + > + <Box display="flex" justifyContent="flex-end"> + <Tooltip label="Reset settings to defaults"> + <IconButton + aria-label="Reset Defaults" + icon={<RepeatClockIcon />} + onClick={() => setPhysics(initialPhysics)} + colorScheme="purple" + /> + </Tooltip> + <CloseButton onClick={onClose} /> + </Box> + <Scrollbars + autoHeight + autoHeightMax={600} + autoHide + renderThumbVertical={({ style, ...props }) => ( + <Box + {...props} + style={{ + ...style, + borderRadius: 10, + }} + bg="purple.500" + /> + )} + > + <Accordion allowMultiple allowToggle> + <AccordionItem> + <AccordionButton> + <AccordionIcon /> + <Text>Filter</Text> + </AccordionButton> + <AccordionPanel> + <Flex justifyContent="space-between"> + <Text>Kill orphans</Text> + <Switch + colorScheme="purple" + onChange={() => { + setFilter({ ...filter, orphans: !filter.orphans }) + }} + isChecked={filter.orphans} + ></Switch> + </Flex> + <Flex justifyContent="space-between"> + <Text>Link nodes with parent file</Text> + <Switch + colorScheme="purple" + onChange={() => { + setFilter({ ...filter, parents: !filter.parents }) + }} + isChecked={filter.parents} + ></Switch> + </Flex> + </AccordionPanel> + </AccordionItem> + <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} + colorScheme="purple" + /> + </AccordionButton> + <AccordionPanel> + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.200" />} + align="stretch" + > + <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 * 10} + onChange={(value) => setPhysics({ ...physics, collisionStrength: value / 10 })} + label="Strength" + /> + </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 allowToggle> + <AccordionItem> + <AccordionButton> + <Text>Advanced</Text> + <AccordionIcon /> + </AccordionButton> + <AccordionPanel> + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.200" />} + align="stretch" + > + <SliderWithInfo + label="Iterations per tick" + min={1} + max={10} + step={1} + value={physics.iterations} + onChange={(v) => setPhysics({ ...physics, iterations: v })} + infoText="Number of times the physics simulation iterates per simulation step" + /> + <SliderWithInfo + label="Stabilization rate" + value={physics.alphaDecay * 50} + onChange={(value) => setPhysics({ ...physics, alphaDecay: value / 50 })} + /> + </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" + > + <EnableSection + label="Colors" + onChange={() => setPhysics({ ...physics, colorful: !physics.colorful })} + value={physics.colorful} + > + <Text>Child</Text> + </EnableSection> + <SliderWithInfo + label="Node size" + value={physics.nodeRel} + onChange={(value) => setPhysics({ ...physics, nodeRel: value })} + /> + <SliderWithInfo + label="Link width" + value={physics.linkWidth} + onChange={(value) => setPhysics({ ...physics, linkWidth: value })} + /> + <EnableSection + label="Labels" + value={physics.labels} + onChange={() => setPhysics({ ...physics, labels: !physics.labels })} + > + <SliderWithInfo + label="Label Appearance Scale" + value={physics.labelScale * 5} + onChange={(value) => setPhysics({ ...physics, labelScale: value / 5 })} + /> + </EnableSection> + <EnableSection + label="Directional Particles" + value={physics.particles} + onChange={() => setPhysics({ ...physics, particles: !physics.particles })} + > + <SliderWithInfo + label="Particle Number" + value={physics.particlesNumber} + max={5} + step={1} + onChange={(value) => setPhysics({ ...physics, particlesNumber: value })} + /> + <SliderWithInfo + label="Particle Size" + value={physics.particlesWidth} + onChange={(value) => setPhysics({ ...physics, particlesWidth: value })} + /> + </EnableSection> + <EnableSection + label="Highlight Animation" + onChange={() => { + setPhysics({ ...physics, highlightAnim: !physics.highlightAnim }) + }} + value={physics.highlightAnim} + > + <SliderWithInfo + label="Animation speed" + onChange={(v) => setPhysics({ ...physics, animationSpeed: v })} + value={physics.animationSpeed} + infoText="Slower speed has a chance of being buggy" + min={50} + max={1000} + step={10} + /> + <Select + placeholder={physics.algorithmName} + onChange={(v) => { + setPhysics({ ...physics, algorithmName: v.target.value }) + }} + > + {physics.algorithmOptions.map((opt, i) => ( + <option key={i} value={opt}> + {opt} + </option> + ))} + </Select> + {/* <DropDownMenu + displayValue={physics.algorithmName} + textArray={physics.algorithmOptions} + onClickArray={physics.algorithmOptions.map((option) => + setPhysics({ ...physics, algorithmName: { option } }), + )} + /> */} + </EnableSection> + <EnableSection + label="Highlight" + onChange={() => setPhysics({ ...physics, highlight: !physics.highlight })} + value={physics.highlight} + > + <SliderWithInfo + label="Highlight Link Thickness Multiplier" + value={physics.highlightLinkSize} + onChange={(value) => setPhysics({ ...physics, highlightLinkSize: value })} + /> + <SliderWithInfo + label="Highlight Node Size Multiplier" + value={physics.highlightNodeSize} + onChange={(value) => setPhysics({ ...physics, highlightNodeSize: value })} + /> + <Flex justifyContent="space-between"> + <Text> Highlight node color </Text> + </Flex> + <Flex justifyContent="space-between"> + <Text> Highlight link color </Text> + </Flex> + </EnableSection> + </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> + </Accordion> + </Scrollbars> + </Box> + ) +} +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 + 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} + colorScheme="purple" + > + <SliderTrack> + <SliderFilledTrack /> + </SliderTrack> + <Tooltip label={value.toFixed(1)}> + <SliderThumb /> + </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> + <Box display="flex" justifyContent="space-between"> + <Box display="flex" alignItems="center"> + <Text>{label}</Text> + {infoText && <InfoTooltip infoText={infoText} />} + </Box> + <Switch isChecked={!!value} onChange={onChange} colorScheme="purple" /> + </Box> + {value && children} + </Box> + ) +} + +export interface DropDownMenuProps { + textArray: string[] + onClickArray: any + displayValue: string +} + +export const DropDownMenu = (props: DropDownMenuProps) => { + const { textArray, onClickArray, displayValue } = props + return ( + <Menu> + <MenuButton as={Button} rightIcon={<ChevronDownIcon />}> + {displayValue} + </MenuButton> + <MenuList> + {textArray.map((option, i) => { + ;<MenuItem onClick={onClickArray[i]}> {option} </MenuItem> + })} + </MenuList> + </Menu> + ) +} |