summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2021-07-28 21:45:53 +0200
committerThomas F. K. Jorna <[email protected]>2021-07-28 21:45:53 +0200
commit938a8e121667ab8bb0e495af6b35d9cb1affdaa7 (patch)
tree3fd0f4516c02c9924d6f9f2391dafabee28e7b6a
parentf9aac2c130dbec61c9466ae4b932aa9b4158d309 (diff)
added the ability to change the main highlight color
-rw-r--r--components/config.ts24
-rw-r--r--components/tweaks.tsx142
-rw-r--r--pages/_app.tsx120
-rw-r--r--pages/index.tsx41
4 files changed, 247 insertions, 80 deletions
diff --git a/components/config.ts b/components/config.ts
index d6c2296..753f2db 100644
--- a/components/config.ts
+++ b/components/config.ts
@@ -64,3 +64,27 @@ export const initialFilter = {
links: [],
date: [],
}
+
+export const initialVisuals = {
+ particles: false,
+ particlesNumber: 0,
+ particlesWidth: 4,
+ linkOpacity: 0.7,
+ linkWidth: 1,
+ nodeRel: 4,
+ nodeOpacity: 0.9,
+ nodeResolution: 8,
+ labels: 2,
+ labelScale: 1.5,
+ highlight: true,
+ highlightNodeSize: 2,
+ highlightLinkSize: 2,
+ highlightAnim: false,
+ animationSpeed: 250,
+ algorithms: algorithms,
+ algorithmOptions: options,
+ algorithmName: 'CubicOut',
+ linkColorScheme: 'plain',
+ nodeColorScheme: 'colorful',
+ highlightColor: 'purple',
+}
diff --git a/components/tweaks.tsx b/components/tweaks.tsx
index f9c58a1..15fadcf 100644
--- a/components/tweaks.tsx
+++ b/components/tweaks.tsx
@@ -32,9 +32,11 @@ import {
Heading,
Collapse,
} from '@chakra-ui/react'
-import React, { useState } from 'react'
+import React, { useState, useContext } from 'react'
import Scrollbars from 'react-custom-scrollbars-2'
-import { initialPhysics, initialFilter } from './config'
+import { initialPhysics, initialFilter, initialVisuals } from './config'
+
+import { ThemeContext } from '../pages/themecontext'
export interface TweakProps {
physics: typeof initialPhysics
@@ -43,11 +45,15 @@ export interface TweakProps {
setThreeDim: (newValue: boolean) => void
filter: typeof initialFilter
setFilter: any
+ visuals: typeof initialVisuals
+ setVisuals: any
}
export const Tweaks = (props: TweakProps) => {
- const { physics, setPhysics, threeDim, setThreeDim, filter, setFilter } = props
+ const { physics, setPhysics, threeDim, setThreeDim, filter, setFilter, visuals, setVisuals } =
+ props
const [showTweaks, setShowTweaks] = useState(true)
+ const { highlightColor, setHighlightColor } = useContext(ThemeContext)
return (
<>
@@ -67,24 +73,19 @@ export const Tweaks = (props: TweakProps) => {
<Collapse in={showTweaks} animateOpacity>
<Box
bg="alt.100"
- w="xs"
+ w={300}
marginTop={10}
marginLeft={10}
borderRadius="xl"
maxH={650}
paddingBottom={5}
- zIndex="overlay"
+ zIndex={300}
position="relative"
boxShadow="xl"
>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Tooltip label={'Switch to ' + threeDim ? '2D' : '3D' + ' view'}>
- <Button
- onClick={() => setThreeDim(!threeDim)}
- colorScheme="purple"
- variant="ghost"
- zIndex="overlay"
- >
+ <Button onClick={() => setThreeDim(!threeDim)} variant="ghost" zIndex="overlay">
{threeDim ? '3D' : '2D'}
</Button>
</Tooltip>
@@ -94,14 +95,12 @@ export const Tweaks = (props: TweakProps) => {
aria-label="Reset Defaults"
icon={<RepeatClockIcon />}
onClick={() => setPhysics(initialPhysics)}
- colorScheme="purple"
variant="none"
size="sm"
/>
</Tooltip>
<IconButton
size="sm"
- colorScheme="purple"
icon={<CloseIcon />}
aria-label="Close Tweak Panel"
variant="ghost"
@@ -120,7 +119,7 @@ export const Tweaks = (props: TweakProps) => {
...style,
borderRadius: 10,
}}
- bg="purple.500"
+ bg={highlightColor + '.500'}
/>
)}
>
@@ -142,7 +141,6 @@ export const Tweaks = (props: TweakProps) => {
<Flex justifyContent="space-between">
<Text>Orphans</Text>
<Switch
- colorScheme="purple"
onChange={() => {
setFilter({ ...filter, orphans: !filter.orphans })
}}
@@ -152,7 +150,6 @@ export const Tweaks = (props: TweakProps) => {
<Flex justifyContent="space-between">
<Text>Link nodes with parent file</Text>
<Switch
- colorScheme="purple"
onChange={() => {
setFilter({ ...filter, parents: !filter.parents })
}}
@@ -172,7 +169,6 @@ export const Tweaks = (props: TweakProps) => {
id="physicsOn"
onChange={() => setPhysics({ ...physics, enabled: !physics.enabled })}
isChecked={physics.enabled}
- colorScheme="purple"
/>
</AccordionButton>
<AccordionPanel>
@@ -270,7 +266,7 @@ export const Tweaks = (props: TweakProps) => {
label="Centering Strength"
value={physics.centeringStrength}
max={2}
- step={0.1}
+ step={0.01}
onChange={(v) => setPhysics({ ...physics, centeringStrength: v })}
/>
</EnableSection>
@@ -296,13 +292,95 @@ export const Tweaks = (props: TweakProps) => {
paddingLeft={7}
color="gray.800"
>
- <EnableSection
- label="Colors"
- onChange={() => setPhysics({ ...physics, colorful: !physics.colorful })}
- value={physics.colorful}
- >
- <Text>Child</Text>
- </EnableSection>
+ <Accordion>
+ <AccordionItem>
+ <AccordionButton>
+ <AccordionIcon marginRight={2} />
+ <Heading size="sm">Visual</Heading>
+ </AccordionButton>
+ <AccordionPanel>
+ <VStack
+ spacing={2}
+ justifyContent="flex-start"
+ divider={<StackDivider borderColor="gray.500" />}
+ align="stretch"
+ paddingLeft={7}
+ color="gray.800"
+ >
+ <Box>
+ <Flex alignItems="center" justifyContent="space-between">
+ <Text>Node Color Scheme</Text>
+ <Menu>
+ <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
+ {visuals.nodeColorScheme === 'colorful' ? 'Colorful' : 'Plain'}
+ </MenuButton>
+ <MenuList bgColor="gray.200">
+ <MenuItem onClick={() => setPhysics({ ...physics, labels: 1 })}>
+ Colorful
+ </MenuItem>
+ <MenuItem onClick={() => setPhysics({ ...physics, labels: 2 })}>
+ Plain
+ </MenuItem>
+ </MenuList>
+ </Menu>
+ </Flex>
+ <Flex alignItems="center" justifyContent="space-between">
+ <Text>Link Color Scheme</Text>
+ <Menu>
+ <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
+ {visuals.linkColorScheme === 'colorful' ? 'Colorful' : 'Plain'}
+ </MenuButton>
+ <MenuList bgColor="gray.200">
+ <MenuItem onClick={() => setPhysics({ ...physics, labels: 1 })}>
+ Colorful
+ </MenuItem>
+ <MenuItem onClick={() => setPhysics({ ...physics, labels: 2 })}>
+ Plain
+ </MenuItem>
+ </MenuList>
+ </Menu>
+ </Flex>
+ <Flex alignItems="center" justifyContent="space-between">
+ <Text>Highlight color</Text>
+ <Menu>
+ <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
+ {highlightColor[0]!.toUpperCase() + highlightColor!.slice(1)}
+ </MenuButton>
+ <MenuList bgColor="gray.200" width={50}>
+ {[
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'cyan',
+ 'blue',
+ 'pink',
+ 'purple',
+ ].map((color) => (
+ <MenuItem
+ key={color}
+ onClick={() => setHighlightColor(color)}
+ justifyContent="space-between"
+ alignItems="center"
+ display="flex"
+ >
+ <Text>{color[0]!.toUpperCase() + color!.slice(1)}</Text>
+ <Box
+ bgColor={color + '.500'}
+ borderRadius="sm"
+ height={6}
+ width={6}
+ ></Box>
+ </MenuItem>
+ ))}
+ </MenuList>
+ </Menu>
+ </Flex>
+ </Box>
+ </VStack>
+ </AccordionPanel>
+ </AccordionItem>
+ </Accordion>
<SliderWithInfo
label="Node size"
value={physics.nodeRel}
@@ -516,24 +594,18 @@ export const SliderWithInfo = ({
...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}
- colorScheme="purple"
- >
+ <Slider value={value} onChange={onChange} min={min} max={max} step={step}>
<SliderTrack>
<SliderFilledTrack />
</SliderTrack>
- <Tooltip bg="purple.500" label={value.toFixed(1)}>
+ <Tooltip bg={highlightColor + '.500'} label={value.toFixed(1)}>
<SliderThumb bg="white" />
</Tooltip>
</Slider>
@@ -558,7 +630,7 @@ export const EnableSection = (props: EnableSectionProps) => {
<Text>{label}</Text>
{infoText && <InfoTooltip infoText={infoText} />}
</Box>
- <Switch isChecked={!!value} onChange={onChange} colorScheme="purple" />
+ <Switch isChecked={!!value} onChange={onChange} />
</Box>
<Collapse in={!!value} animateOpacity>
<Box paddingLeft={4} paddingTop={2}>
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 410b333..c2edb69 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -1,9 +1,12 @@
import '../styles/globals.css'
import type { AppProps } from 'next/app'
-import { ChakraProvider, extendTheme } from '@chakra-ui/react'
-import { useEffect, useState, useMemo } from 'react'
+import { ChakraProvider, extendTheme, withDefaultColorScheme } from '@chakra-ui/react'
+import { useEffect, useState, useMemo, useContext, useReducer } from 'react'
import * as d3int from 'd3-interpolate'
+import { ThemeContext } from './themecontext'
+import { usePersistantState } from '../util/persistant-state'
+
function MyApp({ Component, pageProps }: AppProps) {
const initialTheme = {
base1: '#1c1f24',
@@ -31,30 +34,81 @@ function MyApp({ Component, pageProps }: AppProps) {
violet: '#a991f1',
yellow: '#FCCE7B',
}
- const [emacsTheme, setEmacsTheme] = useState<typeof initialTheme>(initialTheme)
- /* useEffect(() => {
- * const trackTheme = new EventSource('http://127.0.0.1:35901/theme')
- * trackTheme.addEventListener('message', (e) => {
- * const themeData = JSON.parse(e.data)
- * console.log("aa")
- * if (!themeData.base4) {
- * const bgfgInterpolate = d3int.interpolate(emacsTheme.bg, emacsTheme.fg)
- * themeData.base1 = bgfgInterpolate(0.1)
- * themeData.base2 = bgfgInterpolate(0.2)
- * themeData.base3 = bgfgInterpolate(0.3)
- * themeData.base4 = bgfgInterpolate(0.4)
- * themeData.base5 = bgfgInterpolate(0.5)
- * themeData.base6 = bgfgInterpolate(0.6)
- * themeData.base7 = bgfgInterpolate(0.7)
- * themeData.base8 = bgfgInterpolate(0.8)
- * console.log('o o')
- * }
- * emacsTheme.bg !== themeData.bg && setEmacsTheme(themeData)
- * })
- * }, [])
- */
- const borderColor = emacsTheme.violet + 'aa'
+ const [isInitialized, setIsInitialized] = useState(false)
+
+ const [emacsTheme, setEmacsTheme] = useState(initialTheme)
+ const [highlightColor, setHighlightColor] = useState('purple')
+
+ useEffect(() => {
+ if (isInitialized) {
+ localStorage.setItem('theme', JSON.stringify(emacsTheme))
+ }
+ }, [emacsTheme])
+
+ useEffect(() => {
+ if (isInitialized) {
+ localStorage.setItem('highlightColor', JSON.stringify(highlightColor))
+ }
+ }, [highlightColor])
+
+ useEffect(() => {
+ setEmacsTheme(
+ JSON.parse(localStorage.getItem('theme') ?? JSON.stringify(initialTheme)) ?? initialTheme,
+ )
+ setHighlightColor(
+ JSON.parse(localStorage.getItem('highlightColor') ?? JSON.stringify(highlightColor)) ??
+ highlightColor,
+ )
+ setIsInitialized(true)
+ }, [])
+
+ const themeObject = {
+ emacsTheme: emacsTheme,
+ setEmacsTheme: setEmacsTheme,
+ highlightColor: highlightColor,
+ setHighlightColor: setHighlightColor,
+ }
+ return (
+ <ThemeContext.Provider value={themeObject as typeof themeObject}>
+ <SubApp>
+ <Component {...pageProps} />
+ </SubApp>
+ </ThemeContext.Provider>
+ )
+}
+
+function SubApp(props: any) {
+ const { children } = props
+ const { highlightColor, emacsTheme } = useContext(ThemeContext)
+ // yeah it's annoying, should put this someplace more sensible
+ const getBorderColor = () => {
+ if (highlightColor === 'purple') {
+ return emacsTheme.violet + 'aa'
+ }
+ if (highlightColor === 'pink') {
+ return emacsTheme.magenta + 'aa'
+ }
+ if (highlightColor === 'blue') {
+ return emacsTheme.blue + 'aa'
+ }
+ if (highlightColor === 'cyan') {
+ return emacsTheme.cyan + 'aa'
+ }
+ if (highlightColor === 'green') {
+ return emacsTheme.green + 'aa'
+ }
+ if (highlightColor === 'yellow') {
+ return emacsTheme.yellow + 'aa'
+ }
+ if (highlightColor === 'orange') {
+ return emacsTheme.orange + 'aa'
+ }
+ if (highlightColor === 'red') {
+ return emacsTheme.red + 'aa'
+ }
+ }
const missingColor = d3int.interpolate(emacsTheme.base1, emacsTheme.base2)(0.2)
+ const borderColor = getBorderColor()
const theme = useMemo(() => {
console.log('ii')
return {
@@ -112,23 +166,19 @@ function MyApp({ Component, pageProps }: AppProps) {
variants: {
outline: {
border: '2px solid',
- borderColor: 'purple.500',
- color: 'purple.500',
+ borderColor: highlightColor + '.500',
+ color: highlightColor + '.500',
},
ghost: {
- color: 'purple.500',
+ color: highlightColor + '.500',
},
},
},
},
}
- }, [JSON.stringify(emacsTheme)])
+ }, [highlightColor, JSON.stringify(emacsTheme)])
- const extendedTheme = extendTheme(theme)
- return (
- <ChakraProvider theme={extendedTheme}>
- <Component {...pageProps} setEmacsTheme={setEmacsTheme} />
- </ChakraProvider>
- )
+ const extendedTheme = extendTheme(theme, withDefaultColorScheme({ colorScheme: highlightColor }))
+ return <ChakraProvider theme={extendedTheme}>{children}</ChakraProvider>
}
export default MyApp
diff --git a/pages/index.tsx b/pages/index.tsx
index e330b91..6add88e 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -1,4 +1,11 @@
-import React, { ComponentPropsWithoutRef, useEffect, useRef, useState, useMemo } from 'react'
+import React, {
+ ComponentPropsWithoutRef,
+ useEffect,
+ useRef,
+ useState,
+ useMemo,
+ useContext,
+} from 'react'
import { usePersistantState } from '../util/persistant-state'
const d3promise = import('d3-force-3d')
import * as d3int from 'd3-interpolate'
@@ -15,9 +22,10 @@ import { useAnimation } from '@lilib/hooks'
import { Box, useTheme } from '@chakra-ui/react'
-import { initialPhysics, initialFilter } from '../components/config'
+import { initialPhysics, initialFilter, initialVisuals } from '../components/config'
import { Tweaks } from '../components/tweaks'
+import { ThemeContext, ThemeContextProps } from './themecontext'
// react-force-graph fails on import when server-rendered
// https://github.com/vasturiano/react-force-graph/issues/155
const ForceGraph2D = (
@@ -35,7 +43,7 @@ export type Scope = {
nodeIds: string[]
}
-export default function Home(setEmacsTheme: any) {
+export default function Home() {
// only render on the client
const [showPage, setShowPage] = useState(false)
useEffect(() => {
@@ -46,12 +54,13 @@ export default function Home(setEmacsTheme: any) {
return null
}
- return <GraphPage setEmacsTheme={setEmacsTheme} />
+ return <GraphPage />
}
-export function GraphPage(setEmacsTheme: any) {
+export function GraphPage() {
const [physics, setPhysics] = usePersistantState('physics', initialPhysics)
const [filter, setFilter] = usePersistantState('filter', initialFilter)
+ const [visuals, setVisuals] = usePersistantState('visuals', initialVisuals)
const [graphData, setGraphData] = useState<GraphData | null>(null)
const [emacsNodeId, setEmacsNodeId] = useState<string | null>(null)
@@ -114,7 +123,7 @@ export function GraphPage(setEmacsTheme: any) {
const orgRoamGraphDataClone = JSON.parse(JSON.stringify(orgRoamGraphDataWithFileLinks))
setGraphData(orgRoamGraphDataClone)
}
-
+ const { setEmacsTheme } = useContext(ThemeContext)
useEffect(() => {
const socket = new WebSocket('ws://localhost:35903')
socket.addEventListener('open', (e) => {
@@ -132,7 +141,7 @@ export function GraphPage(setEmacsTheme: any) {
console.log('Received theme data')
console.log(message.data)
console.log(setEmacsTheme)
- setEmacsTheme.setEmacsTheme.setEmacsTheme(message.data)
+ setEmacsTheme(message.data)
break
case 'command':
console.log('command')
@@ -182,6 +191,8 @@ export function GraphPage(setEmacsTheme: any) {
setThreeDim,
filter,
setFilter,
+ visuals,
+ setVisuals,
}}
/>
<Box position="absolute" alignItems="top">
@@ -194,6 +205,7 @@ export function GraphPage(setEmacsTheme: any) {
threeDim,
emacsNodeId,
filter,
+ visuals,
}}
/>
</Box>
@@ -209,10 +221,12 @@ export interface GraphProps {
threeDim: boolean
filter: typeof initialFilter
emacsNodeId: string | null
+ visuals: typeof initialVisuals
}
export const Graph = function (props: GraphProps) {
- const { physics, graphData, threeDim, linksByNodeId, filter, emacsNodeId, nodeById } = props
+ const { physics, graphData, threeDim, linksByNodeId, filter, emacsNodeId, nodeById, visuals } =
+ props
const graph2dRef = useRef<any>(null)
const graph3dRef = useRef<any>(null)
@@ -410,10 +424,15 @@ export const Graph = function (props: GraphProps) {
}
}, [hoverNode])
const theme = useTheme()
+ const themeContext = useContext<ThemeContextProps>(ThemeContext)
const interPurple = useMemo(
() => d3int.interpolate(theme.colors.gray[500], theme.colors.purple[500]),
[theme],
)
+ const interHighlight = useMemo(
+ () => d3int.interpolate(theme.colors.gray[500], theme.colors[themeContext.highlightColor][500]),
+ [theme, visuals.highlightColor],
+ )
const interGray = useMemo(
() => d3int.interpolate(theme.colors.gray[500], theme.colors.gray[400]),
[theme],
@@ -446,7 +465,7 @@ export const Graph = function (props: GraphProps) {
nodeColor: (node) => {
if (!physics.colorful) {
return previouslyHighlightedNodes[node.id!] || highlightedNodes[node.id!]
- ? interPurple(opacity)
+ ? interHighlight(opacity)
: interGray(opacity)
}
if (node.id === emacsNodeId) {
@@ -554,7 +573,9 @@ export const Graph = function (props: GraphProps) {
linkColor: (link) => {
const linkIsHighlighted = isLinkRelatedToNode(link, centralHighlightedNode)
const linkWasHighlighted = isLinkRelatedToNode(link, lastHoverNode.current)
- return linkIsHighlighted || linkWasHighlighted ? interPurple(opacity) : theme.colors.gray[500]
+ return linkIsHighlighted || linkWasHighlighted
+ ? interHighlight(opacity)
+ : theme.colors.gray[500]
},
linkWidth: (link) => {
const linkIsHighlighted = isLinkRelatedToNode(link, centralHighlightedNode)