diff options
author | Thomas F. K. Jorna <[email protected]> | 2021-07-29 06:07:45 +0200 |
---|---|---|
committer | Thomas F. K. Jorna <[email protected]> | 2021-07-29 06:07:45 +0200 |
commit | e0c6d0ec5091536734288ab06ad4587b43387d2f (patch) | |
tree | 6716d07880f76e88999b3c03389e71886c4b9a59 | |
parent | 29fbfba2db6fdca308a9f88c3e757b2a36367955 (diff) |
handled all the link color cases
-rw-r--r-- | components/config.ts | 28 | ||||
-rw-r--r-- | components/tweaks.tsx | 464 | ||||
-rw-r--r-- | pages/_app.tsx | 3 | ||||
-rw-r--r-- | pages/index.tsx | 122 |
4 files changed, 409 insertions, 208 deletions
diff --git a/components/config.ts b/components/config.ts index c6f581d..2c7a1b6 100644 --- a/components/config.ts +++ b/components/config.ts @@ -84,33 +84,9 @@ export const initialVisuals = { algorithms: algorithms, algorithmOptions: options, algorithmName: 'CubicOut', - linkColorScheme: 'grey', + linkColorScheme: '500', nodeColorScheme: ['gray', 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'pink', 'purple'], nodeHighlight: '', linkHighlight: '', -} - -export type Visuals = { - particles: boolean - particlesNumber: number - particlesWidth: number - linkOpacity: number - linkWidth: number - nodeRel: number - nodeOpacity: number - nodeResolution: number - labels: number - labelScale: number - highlight: boolean - highlightNodeSize: number - highlightLinkSize: number - highlightAnim: boolean - animationSpeed: number - algorithms: typeof algorithms - algorithmOptions: typeof options - algorithmName: string - linkColorScheme: string - nodeColorScheme: string[] - nodeHighlight: string - linkHighlight: string + backgroundColor: 'white', } diff --git a/components/tweaks.tsx b/components/tweaks.tsx index 9da387b..709cbf5 100644 --- a/components/tweaks.tsx +++ b/components/tweaks.tsx @@ -4,6 +4,8 @@ import { ChevronDownIcon, SettingsIcon, InfoOutlineIcon, + RepeatIcon, + ArrowRightIcon, } from '@chakra-ui/icons' import { Accordion, @@ -48,7 +50,7 @@ export interface TweakProps { setThreeDim: (newValue: boolean) => void filter: typeof initialFilter setFilter: any - visuals: Visuals + visuals: typeof initialVisuals setVisuals: any } @@ -58,6 +60,7 @@ export const Tweaks = (props: TweakProps) => { const [showTweaks, setShowTweaks] = useState(true) const { highlightColor, setHighlightColor } = useContext(ThemeContext) const colorList = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'pink', 'purple', 'gray'] + const grays = ['100', '200', '300', '400', '500', '600', '700', '800', '900'] return ( <> <Box @@ -287,15 +290,8 @@ export const Tweaks = (props: TweakProps) => { <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" - > - <Accordion allowToggle defaultIndex={[0]}> + <VStack> + <Accordion allowToggle defaultIndex={[0]} width={250} marginLeft={10}> <AccordionItem> <AccordionButton> <Flex justifyContent="space-between" w="100%"> @@ -314,6 +310,39 @@ export const Tweaks = (props: TweakProps) => { <Box> <Flex alignItems="center" justifyContent="space-between"> <Text>Nodes</Text> + <Tooltip label="Shuffle node colors"> + <IconButton + 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) => [Math.random(), x]) + .sort(([a], [b]) => a - b) + .map(([_, x]) => x), + }) + }} + /> + </Tooltip> + <Tooltip label="Cycle node colors"> + <IconButton + icon={<ArrowRightIcon />} + size="sm" + variant="ghost" + onClick={() => { + const arr = visuals.nodeColorScheme ?? [] + setVisuals({ + ...visuals, + nodeColorScheme: [...arr.slice(1, arr.length), arr[0]], + }) + }} + /> + </Tooltip> <Menu closeOnSelect={false}> <MenuButton width={20} @@ -376,25 +405,88 @@ export const Tweaks = (props: TweakProps) => { </Flex> <Flex alignItems="center" justifyContent="space-between"> <Text>Links</Text> - <Menu> + <Menu isLazy> <MenuButton as={Button} colorScheme="" color="black" rightIcon={<ChevronDownIcon />} > - <Text> - {visuals.linkColorScheme[0]!.toUpperCase() + - visuals.linkColorScheme!.slice(1)} - </Text> + <Box> + {visuals.linkColorScheme ? ( + <Box + bgColor={'gray.' + 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 + '.500'} + flex="1 1 8px" + borderRadius="2xl" + ></Box> + ))} + </Flex> + )} + </Box> </MenuButton> - <MenuList bgColor="gray.200"> - <MenuItem onClick={() => setPhysics({ ...physics, labels: 1 })}> - Match Nodes - </MenuItem> - <MenuItem onClick={() => setPhysics({ ...physics, labels: 2 })}> - Gray + <MenuList bgColor="gray.200" width={50}> + <MenuItem + onClick={() => + setVisuals({ ...visuals, linkColorScheme: '' }) + } + justifyContent="space-between" + alignItems="center" + display="flex" + > + <Text>Match nodes</Text> + <Flex + height={6} + width={6} + flexDirection="column" + flexWrap="wrap" + > + {visuals.nodeColorScheme.map((color) => ( + <Box + key={color} + bgColor={color + '.500'} + 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" + > + <Text>{color[0]!.toUpperCase() + color!.slice(1)}</Text> + <Box + bgColor={'gray.' + color} + borderRadius="sm" + height={6} + width={6} + ></Box> + </MenuItem> + ))} </MenuList> </Menu> </Flex> @@ -537,172 +629,228 @@ export const Tweaks = (props: TweakProps) => { </MenuList> </Menu> </Flex> + <Flex alignItems="center" justifyContent="space-between"> + <Text>Background</Text> + <Menu isLazy> + <MenuButton + as={Button} + colorScheme="" + color="black" + rightIcon={<ChevronDownIcon />} + > + { + <Box + bgColor={'gray.' + visuals.backgroundColor} + borderRadius="sm" + height={6} + width={6} + ></Box> + } + </MenuButton> + <MenuList bgColor="gray.200" width={50}> + {grays.map((color) => ( + <MenuItem + key={color} + onClick={() => + setVisuals({ ...visuals, backgroundColor: color }) + } + justifyContent="space-between" + alignItems="center" + display="flex" + > + <Text>{color[0]!.toUpperCase() + color!.slice(1)}</Text> + <Box + bgColor={'gray.' + color} + borderRadius="sm" + height={6} + width={6} + ></Box> + </MenuItem> + ))} + </MenuList> + </Menu> + </Flex> </Box> </VStack> </AccordionPanel> </AccordionItem> </Accordion> - <SliderWithInfo - label="Node size" - value={physics.nodeRel} - onChange={(value) => setPhysics({ ...physics, nodeRel: value })} - /> - {threeDim && ( - <> - <SliderWithInfo - label="Node opacity" - value={physics.nodeOpacity} - min={0} - max={1} - onChange={(value) => setPhysics({ ...physics, nodeOpacity: value })} - /> - <SliderWithInfo - label="Node resolution" - value={physics.nodeResolution} - min={5} - max={32} - step={1} - onChange={(value) => setPhysics({ ...physics, nodeResolution: value })} - /> - </> - )} - <SliderWithInfo - label="Link width" - value={physics.linkWidth} - onChange={(value) => setPhysics({ ...physics, linkWidth: value })} - /> - {threeDim && ( + <VStack + spacing={2} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.500" />} + align="stretch" + paddingLeft={7} + color="gray.800" + > <SliderWithInfo - label="Link opacity" - min={0} - max={1} - value={physics.linkOpacity} - onChange={(value) => setPhysics({ ...physics, linkOpacity: value })} + label="Node size" + value={physics.nodeRel} + onChange={(value) => setPhysics({ ...physics, nodeRel: value })} /> - )} - <Box> - <Flex alignItems="center" justifyContent="space-between"> - <Text>Labels</Text> - <Menu> - <MenuButton - as={Button} - colorScheme="" - color="black" - rightIcon={<ChevronDownIcon />} - > - {!physics.labels - ? 'Never' - : physics.labels < 2 - ? 'On Highlight' - : 'Always'} - </MenuButton> - <MenuList bgColor="gray.200"> - <MenuItem onClick={() => setPhysics({ ...physics, labels: 0 })}> - Never - </MenuItem> - <MenuItem onClick={() => setPhysics({ ...physics, labels: 1 })}> - On Highlight - </MenuItem> - <MenuItem onClick={() => setPhysics({ ...physics, labels: 2 })}> - Always - </MenuItem> - </MenuList> - </Menu> - </Flex> - <Collapse in={physics.labels > 1} animateOpacity> - <Box paddingLeft={4} paddingTop={2}> + {threeDim && ( + <> <SliderWithInfo - label="Label Appearance Scale" - value={physics.labelScale * 5} - onChange={(value) => setPhysics({ ...physics, labelScale: value / 5 })} + label="Node opacity" + value={physics.nodeOpacity} + min={0} + max={1} + onChange={(value) => setPhysics({ ...physics, nodeOpacity: value })} /> - </Box> - </Collapse> - </Box> - <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="Node resolution" + value={physics.nodeResolution} + min={5} + max={32} + step={1} + onChange={(value) => setPhysics({ ...physics, nodeResolution: value })} + /> + </> + )} <SliderWithInfo - label="Particle Size" - value={physics.particlesWidth} - onChange={(value) => setPhysics({ ...physics, particlesWidth: value })} + label="Link width" + value={physics.linkWidth} + onChange={(value) => setPhysics({ ...physics, linkWidth: value })} /> - </EnableSection> - <EnableSection - label="Highlight" - onChange={() => setPhysics({ ...physics, highlight: !physics.highlight })} - value={physics.highlight} - > - <VStack - spacing={1} - justifyContent="flex-start" - divider={<StackDivider borderColor="gray.400" />} - align="stretch" - paddingLeft={0} + {threeDim && ( + <SliderWithInfo + label="Link opacity" + min={0} + max={1} + value={physics.linkOpacity} + onChange={(value) => setPhysics({ ...physics, linkOpacity: value })} + /> + )} + <Box> + <Flex alignItems="center" justifyContent="space-between"> + <Text>Labels</Text> + <Menu> + <MenuButton + as={Button} + colorScheme="" + color="black" + rightIcon={<ChevronDownIcon />} + > + {!physics.labels + ? 'Never' + : physics.labels < 2 + ? 'On Highlight' + : 'Always'} + </MenuButton> + <MenuList bgColor="gray.200"> + <MenuItem onClick={() => setPhysics({ ...physics, labels: 0 })}> + Never + </MenuItem> + <MenuItem onClick={() => setPhysics({ ...physics, labels: 1 })}> + On Highlight + </MenuItem> + <MenuItem onClick={() => setPhysics({ ...physics, labels: 2 })}> + Always + </MenuItem> + </MenuList> + </Menu> + </Flex> + <Collapse in={physics.labels > 1} animateOpacity> + <Box paddingLeft={4} paddingTop={2}> + <SliderWithInfo + label="Label Appearance Scale" + value={physics.labelScale * 5} + onChange={(value) => + setPhysics({ ...physics, labelScale: value / 5 }) + } + /> + </Box> + </Collapse> + </Box> + <EnableSection + label="Directional Particles" + value={physics.particles} + onChange={() => setPhysics({ ...physics, particles: !physics.particles })} > <SliderWithInfo - label="Highlight Link Thickness" - value={physics.highlightLinkSize} - onChange={(value) => setPhysics({ ...physics, highlightLinkSize: value })} + label="Particle Number" + value={physics.particlesNumber} + max={5} + step={1} + onChange={(value) => setPhysics({ ...physics, particlesNumber: value })} /> <SliderWithInfo - label="Highlight Node Size" - value={physics.highlightNodeSize} - onChange={(value) => setPhysics({ ...physics, highlightNodeSize: value })} + label="Particle Size" + value={physics.particlesWidth} + onChange={(value) => setPhysics({ ...physics, particlesWidth: value })} /> - {/*<Flex justifyContent="space-between"> + </EnableSection> + <EnableSection + label="Highlight" + onChange={() => setPhysics({ ...physics, highlight: !physics.highlight })} + value={physics.highlight} + > + <VStack + spacing={1} + justifyContent="flex-start" + divider={<StackDivider borderColor="gray.400" />} + align="stretch" + paddingLeft={0} + > + <SliderWithInfo + label="Highlight Link Thickness" + value={physics.highlightLinkSize} + onChange={(value) => + setPhysics({ ...physics, highlightLinkSize: value }) + } + /> + <SliderWithInfo + label="Highlight Node Size" + 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 - 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 }) + <EnableSection + label="Highlight Animation" + onChange={() => { + setPhysics({ ...physics, highlightAnim: !physics.highlightAnim }) }} + value={physics.highlightAnim} > - {physics.algorithmOptions.map((opt: string) => ( - <option key={opt} value={opt}> - {opt} - </option> - ))} - </Select> - {/* <DropDownMenu + <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: string) => ( + <option key={opt} value={opt}> + {opt} + </option> + ))} + </Select> + {/* <DropDownMenu displayValue={physics.algorithmName} textArray={physics.algorithmOptions} onClickArray={physics.algorithmOptions.map((option) => setPhysics({ ...physics, algorithmName: { option } }), )} /> */} - </EnableSection> - </VStack> - </EnableSection> + </EnableSection> + </VStack> + </EnableSection> + </VStack> </VStack> </AccordionPanel> </AccordionItem> diff --git a/pages/_app.tsx b/pages/_app.tsx index c2edb69..f8ff942 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -110,7 +110,6 @@ function SubApp(props: any) { const missingColor = d3int.interpolate(emacsTheme.base1, emacsTheme.base2)(0.2) const borderColor = getBorderColor() const theme = useMemo(() => { - console.log('ii') return { colors: { white: emacsTheme.bg, @@ -171,6 +170,8 @@ function SubApp(props: any) { }, ghost: { color: highlightColor + '.500', + _hover: { bg: `inherit`, border: '1px solid', borderColor: highlightColor + '.500' }, + _active: { color: `inherit`, bg: highlightColor + '.500' }, }, }, }, diff --git a/pages/index.tsx b/pages/index.tsx index caf216c..bb801a4 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -133,13 +133,10 @@ export function GraphPage() { console.log(typeof message.type) switch (message.type) { case 'graphdata': - console.log('hey') parseGraphData(message.data) break case 'theme': console.log('Received theme data') - console.log(message.data) - console.log(setEmacsTheme) setEmacsTheme(message.data) break case 'command': @@ -157,7 +154,6 @@ export function GraphPage() { ].map((nodeId) => [nodeId, {}]), ) /* zoomToFit(500, 200, (node: OrgRoamNode)=>nodes[node.id!]) */ - console.log(nodes) } default: console.log('oopsie whoopsie') @@ -437,6 +433,35 @@ export const Graph = function (props: GraphProps) { [theme], ) + const highlightColors = useMemo(() => { + const allColors = [].concat( + visuals.nodeColorScheme /* && visuals.nodeColorScheme.map((c) => theme.colors[c][500])) */ || + [], + visuals.linkColorScheme /* && theme.colors.gray[visuals.linkColorScheme])]] */ || [], + visuals.linkHighlight /* && theme.colors[visuals.linkHighlight][500]) */ || [], + visuals.nodeHighlight /* && theme.colors[visuals.linkHighlight][500]) */ || [], + ) + + console.log(visuals.linkColorScheme) + const getColor = (c) => (isNaN(c) ? theme.colors[c][500] : theme.colors.gray[c]) + const highlightColors = Object.fromEntries( + allColors.map((color) => { + const color1 = getColor(color) + const crisscross = allColors.map((color2) => [ + color2, + d3int.interpolate(color1, getColor(color2)), + ]) + return [color, Object.fromEntries(crisscross)] + }), + ) + console.log(highlightColors) + return highlightColors + }, [ + visuals.nodeColorScheme, + visuals.linkHighlight, + visuals.nodeHighlight, + visuals.linkColorScheme, + ]) const highlightedLinks = useMemo(() => linksByNodeId[hoverNode?.id!] ?? [], [hoverNode]) const previouslyHighlightedLinks = useMemo( @@ -455,11 +480,67 @@ export const Graph = function (props: GraphProps) { [hoverNode, previouslyHighlightedLinks, lastHoverNode], ) + const getNodeColorById = (id: string) => { + const linklen = linksByNodeId[id!]?.length ?? 0 + const parentCiteNeighbors = linklen + ? linksByNodeId[id!]?.filter((link) => link.type === 'parent' || link.type === 'cite').length + : 0 + const neighbors = filter.parents ? linklen : linklen - parentCiteNeighbors! + + return visuals.nodeColorScheme[ + numbereWithinRange(neighbors, 0, visuals.nodeColorScheme.length - 1) + ] + } + const getLinkNodeColor = (link: OrgRoamLinkObject) => + linksByNodeId[link.target.id!]?.length < linksByNodeId[link.source.id!]?.length + ? getNodeColorById(link.target.id!) + : getNodeColorById(link.source.id!) + + const getLinkColor = (link: OrgRoamLinkObject) => { + // I'm so sorry + const linkIsHighlighted = isLinkRelatedToNode(link, centralHighlightedNode) + const linkWasHighlighted = isLinkRelatedToNode(link, lastHoverNode.current) + const needsHighlighting = linkIsHighlighted || linkWasHighlighted + // if we are matching the node color and don't have a highlight color + // or we don't have our own scheme and we're not being highlighted + if (!visuals.linkHighlight && !visuals.linkColorScheme && !needsHighlighting) { + const nodeColor = getLinkNodeColor(link) + return theme.colors[nodeColor][500] + } + if (!needsHighlighting && !visuals.linkColorScheme) { + const nodeColor = getLinkNodeColor(link) + return theme.colors[nodeColor][500] + } + if (!needsHighlighting && !visuals.linkColorScheme) { + const nodeColor = getLinkNodeColor(link) + return theme.colors[nodeColor][500] + } + if (!needsHighlighting) { + return theme.colors.gray[visuals.linkColorScheme] + } + + if (!visuals.linkHighlight && !visuals.linkColorScheme) { + const nodeColor = getLinkNodeColor(link) + return theme.colors[nodeColor][500] + } + if (!visuals.linkHighlight) { + return theme.colors.gray[visuals.linkColorScheme] + } + if (!visuals.linkColorScheme) { + return highlightColors[getLinkNodeColor(link)][visuals.linkHighlight](opacity) + } + return highlightColors[visuals.linkColorScheme][visuals.linkHighlight](opacity) + } + + const getLinkBaseColor = (link: OrgRoamLinkObject) => { + if (!visuals.linkColorScheme) { + } + } const graphCommonProps: ComponentPropsWithoutRef<typeof TForceGraph2D> = { graphData: scopedGraphData, width: windowWidth, height: windowHeight, - backgroundColor: theme.white, + backgroundColor: theme.colors.gray[visuals.backgroundColor], nodeLabel: (node) => (node as OrgRoamNode).title, nodeColor: (node) => { if (!physics.colorful) { @@ -468,20 +549,9 @@ export const Graph = function (props: GraphProps) { : interGray(opacity) } if (node.id === emacsNodeId) { - return theme.colors['red'][500] + return theme.colors[visuals.emacsNodeColor][500] } - const palette = [ - 'pink', - 'purple', - 'blue', - 'cyan', - 'teal', - 'green', - 'yellow', - 'orange', - 'red', - ].filter((color) => !['red'].includes(color)) // otherwise links with parents get shown as having one note const linklen = linksByNodeId[node.id!]?.length ?? 0 const parentCiteNeighbors = linklen @@ -490,7 +560,11 @@ export const Graph = function (props: GraphProps) { : 0 const neighbors = filter.parents ? linklen : linklen - parentCiteNeighbors! - return theme.colors[palette[numbereWithinRange(neighbors, 0, palette.length - 1)]][500] + return theme.colors[ + visuals.nodeColorScheme[ + numbereWithinRange(neighbors, 0, visuals.nodeColorScheme.length - 1) + ] + ][500] }, nodeRelSize: physics.nodeRel, nodeVal: (node) => { @@ -570,11 +644,13 @@ export const Graph = function (props: GraphProps) { linkDirectionalParticles: physics.particles ? physics.particlesNumber : undefined, linkColor: (link) => { - const linkIsHighlighted = isLinkRelatedToNode(link, centralHighlightedNode) - const linkWasHighlighted = isLinkRelatedToNode(link, lastHoverNode.current) - return linkIsHighlighted || linkWasHighlighted - ? interHighlight(opacity) - : theme.colors.gray[500] + /* if (!physics.highlight || !visuals.linkHighlight) { + * return visuals.linkColorScheme + * ? theme.colors.gray[visuals.linkColorScheme] + * : theme.colors[getLinkNodeColor(link)][500] + * } */ + + return getLinkColor(link) }, linkWidth: (link) => { const linkIsHighlighted = isLinkRelatedToNode(link, centralHighlightedNode) |