summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2021-10-11 01:13:10 +0200
committerThomas F. K. Jorna <[email protected]>2021-10-11 01:13:10 +0200
commit546a88ec37073840e98ed689f7139d04985e861c (patch)
tree73467c1720328fd11a77e864ebe6e5fd7c9b3251
parent31d7477b376501bd80fe635b91412bc7f6ae7ea7 (diff)
feat(preview): ui redo
-rw-r--r--components/Sidebar/Backlinks.tsx3
-rw-r--r--components/Sidebar/Link.tsx20
-rw-r--r--components/Sidebar/Note.tsx4
-rw-r--r--components/Sidebar/Toolbar.tsx102
-rw-r--r--components/Sidebar/index.tsx109
-rw-r--r--components/Tweaks/BehaviorPanel.tsx1
-rw-r--r--components/Tweaks/SliderWithInfo.tsx2
-rw-r--r--components/Tweaks/index.tsx27
-rw-r--r--components/config.ts5
-rw-r--r--components/contextmenu.tsx146
-rw-r--r--package-lock.json31
-rw-r--r--package.json1
-rw-r--r--pages/_app.tsx5
-rw-r--r--pages/index.tsx357
-rw-r--r--util/processOrg.tsx7
-rw-r--r--util/uniorg.tsx11
16 files changed, 532 insertions, 299 deletions
diff --git a/components/Sidebar/Backlinks.tsx b/components/Sidebar/Backlinks.tsx
index 8c1e9bc..68ab551 100644
--- a/components/Sidebar/Backlinks.tsx
+++ b/components/Sidebar/Backlinks.tsx
@@ -14,6 +14,7 @@ export interface BacklinksProps {
linksByNodeId: LinksByNodeId
nodeByCite: NodeByCite
setSidebarHighlightedNode: OrgRoamNode
+ openContextMenu: any
}
import { PreviewLink } from './Link'
@@ -27,6 +28,7 @@ export const Backlinks = (props: BacklinksProps) => {
nodeById,
linksByNodeId,
nodeByCite,
+ openContextMenu,
} = props
const links = linksByNodeId[previewNode?.id] ?? []
@@ -59,6 +61,7 @@ export const Backlinks = (props: BacklinksProps) => {
href={`id:${link as string}`}
nodeById={nodeById}
setPreviewNode={setPreviewNode}
+ openContextMenu={openContextMenu}
>
{nodeById[link as string]?.title}
</PreviewLink>
diff --git a/components/Sidebar/Link.tsx b/components/Sidebar/Link.tsx
index 16fc2ac..49fe9cf 100644
--- a/components/Sidebar/Link.tsx
+++ b/components/Sidebar/Link.tsx
@@ -35,6 +35,7 @@ export interface LinkProps {
setSidebarHighlightedNode: any
nodeByCite: NodeByCite
nodeById: NodeById
+ openContextMenu: any
}
export interface NormalLinkProps {
@@ -44,6 +45,7 @@ export interface NormalLinkProps {
href: any
children: any
setSidebarHighlightedNode: any
+ openContextMenu: any
}
import { hexToRGBA, getThemeColor } from '../../pages/index'
@@ -51,7 +53,8 @@ import noteStyle from './noteStyle'
import { OrgImage } from './OrgImage'
export const NormalLink = (props: NormalLinkProps) => {
- const { setSidebarHighlightedNode, setPreviewNode, nodeById, href, children } = props
+ const { setSidebarHighlightedNode, setPreviewNode, nodeById, openContextMenu, href, children } =
+ props
const { highlightColor } = useContext(ThemeContext)
const theme = useTheme()
@@ -67,12 +70,16 @@ export const NormalLink = (props: NormalLinkProps) => {
fontWeight={500}
color={highlightColor}
textDecoration="underline"
+ onContextMenu={(e) => {
+ e.preventDefault()
+ openContextMenu(nodeById[uri], e)
+ }}
onClick={() => setPreviewNode(nodeById[uri])}
// TODO don't hardcode the opacitycolor
_hover={{ textDecoration: 'none', cursor: 'pointer', bgColor: coolHighlightColor + '22' }}
_focus={{ outlineColor: highlightColor }}
>
- {nodeById[uri]?.title}
+ {children}
</Text>
)
}
@@ -86,6 +93,7 @@ export const PreviewLink = (props: LinkProps) => {
previewNode,
setPreviewNode,
nodeByCite,
+ openContextMenu,
} = props
// TODO figure out how to properly type this
// see https://github.com/rehypejs/rehype-react/issues/25
@@ -129,8 +137,9 @@ export const PreviewLink = (props: LinkProps) => {
href={href}
nodeById={nodeById}
setPreviewNode={setPreviewNode}
+ openContextMenu={openContextMenu}
>
- {nodeById[id as string]?.title}
+ {children}
</PreviewLink>
),
img: ({ src }) => {
@@ -186,6 +195,7 @@ export const PreviewLink = (props: LinkProps) => {
href,
children,
nodeByCite,
+ openContextMenu,
}}
/>
</Box>
@@ -213,7 +223,9 @@ export const PreviewLink = (props: LinkProps) => {
maxHeight={300}
overflow="scroll"
>
- <Box sx={noteStyle}>{orgText}</Box>
+ <Box color="black" sx={noteStyle}>
+ {orgText}
+ </Box>
</PopoverBody>
</PopoverContent>
</Portal>
diff --git a/components/Sidebar/Note.tsx b/components/Sidebar/Note.tsx
index ef2e2b2..e425559 100644
--- a/components/Sidebar/Note.tsx
+++ b/components/Sidebar/Note.tsx
@@ -16,6 +16,7 @@ export interface NoteProps {
justification: number
justificationList: string[]
linksByNodeId: LinksByNodeId
+ openContextMenu: any
}
export const Note = (props: NoteProps) => {
@@ -28,6 +29,7 @@ export const Note = (props: NoteProps) => {
nodeByCite,
setSidebarHighlightedNode,
linksByNodeId,
+ openContextMenu,
} = props
return (
<Box
@@ -50,6 +52,7 @@ export const Note = (props: NoteProps) => {
nodeById,
nodeByCite,
setSidebarHighlightedNode,
+ openContextMenu,
}}
/>
<Backlinks
@@ -60,6 +63,7 @@ export const Note = (props: NoteProps) => {
linksByNodeId,
nodeByCite,
setSidebarHighlightedNode,
+ openContextMenu,
}}
/>
</Flex>
diff --git a/components/Sidebar/Toolbar.tsx b/components/Sidebar/Toolbar.tsx
index f6f1e39..8741da5 100644
--- a/components/Sidebar/Toolbar.tsx
+++ b/components/Sidebar/Toolbar.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import { Text, Flex, IconButton } from '@chakra-ui/react'
+import { Text, Flex, IconButton, ButtonGroup, Tooltip } from '@chakra-ui/react'
import {
BiAlignJustify,
BiAlignLeft,
@@ -38,53 +38,65 @@ export const Toolbar = (props: ToolbarProps) => {
nextPreviewNode,
} = props
return (
- <Flex py={3} alignItems="center" justifyContent="space-between" pr={4}>
+ <Flex pb={3} alignItems="center" justifyContent="space-between" pl={1} pr={1}>
<Flex>
- <IconButton
- variant="ghost"
- icon={<ChevronLeftIcon />}
- aria-label="Previous node"
- disabled={!canUndo}
- onClick={() => previousPreviewNode()}
- />
- <IconButton
- variant="ghost"
- icon={<ChevronRightIcon />}
- aria-label="Next node"
- disabled={!canRedo}
- onClick={() => nextPreviewNode()}
- />
+ <ButtonGroup isAttached>
+ <Tooltip label="Go backward">
+ <IconButton
+ variant="subtle"
+ icon={<ChevronLeftIcon />}
+ aria-label="Previous node"
+ disabled={!canUndo}
+ onClick={() => previousPreviewNode()}
+ />
+ </Tooltip>
+ <Tooltip label="Go forward">
+ <IconButton
+ variant="subtle"
+ icon={<ChevronRightIcon />}
+ aria-label="Next node"
+ disabled={!canRedo}
+ onClick={() => nextPreviewNode()}
+ />
+ </Tooltip>
+ </ButtonGroup>
</Flex>
<Flex>
- <IconButton
- variant="ghost"
- aria-label="Justify content"
- icon={
- [
- <BiAlignJustify key="justify" />,
- <BiAlignLeft key="left" />,
- <BiAlignRight key="right" />,
- <BiAlignMiddle key="center" />,
- ][justification]
- }
- onClick={() => setJustification((curr: number) => (curr + 1) % 4)}
- />
- <IconButton
- variant="ghost"
- aria-label="Indent Text"
- icon={<BiRightIndent />}
- onClick={() => {
- setIndent((curr: number) => (curr ? 0 : 1))
- }}
- />
- <IconButton
- variant="ghost"
- aria-label="Change font"
- icon={<BiFont />}
- onClick={() => {
- setFont((curr: string) => (curr === 'sans serif' ? 'serif' : 'sans serif'))
- }}
- />
+ <Tooltip label="Justify content">
+ <IconButton
+ variant="subtle"
+ aria-label="Justify content"
+ icon={
+ [
+ <BiAlignJustify key="justify" />,
+ <BiAlignLeft key="left" />,
+ <BiAlignRight key="right" />,
+ <BiAlignMiddle key="center" />,
+ ][justification]
+ }
+ onClick={() => setJustification((curr: number) => (curr + 1) % 4)}
+ />
+ </Tooltip>
+ {/* <Tooltip label="Indent trees">
+ <IconButton
+ variant="subtle"
+ aria-label="Indent Text"
+ icon={<BiRightIndent />}
+ onClick={() => {
+ setIndent((curr: number) => (curr ? 0 : 1))
+ }}
+ />
+ </Tooltip>
+ <Tooltip label="Switch betwwen sans and serif">
+ <IconButton
+ variant="subtle"
+ aria-label="Change font"
+ icon={<BiFont />}
+ onClick={() => {
+ setFont((curr: string) => (curr === 'sans serif' ? 'serif' : 'sans serif'))
+ }}
+ />
+ </Tooltip> */}
</Flex>
</Flex>
)
diff --git a/components/Sidebar/index.tsx b/components/Sidebar/index.tsx
index 2e32f4f..cbc0cc9 100644
--- a/components/Sidebar/index.tsx
+++ b/components/Sidebar/index.tsx
@@ -3,15 +3,19 @@ import React, { useContext, useEffect, useRef, useState } from 'react'
import { Toolbar } from './Toolbar'
import { Note } from './Note'
-import { Button, Slide, VStack, Flex, Heading, Box, IconButton } from '@chakra-ui/react'
+import { Button, Slide, VStack, Flex, Heading, Box, IconButton, Tooltip } from '@chakra-ui/react'
+import { Collapse } from './Collapse'
import { Scrollbars } from 'react-custom-scrollbars-2'
-import { ChevronLeftIcon, ChevronRightIcon, HamburgerIcon } from '@chakra-ui/icons'
-import { BiFile } from 'react-icons/bi'
+import { ChevronLeftIcon, ChevronRightIcon, CloseIcon, HamburgerIcon } from '@chakra-ui/icons'
+import { BiDotsVerticalRounded, BiFile, BiNetworkChart } from 'react-icons/bi'
+import { BsReverseLayoutSidebarInsetReverse } from 'react-icons/bs'
import { GraphData, NodeObject, LinkObject } from 'force-graph'
import { OrgRoamNode } from '../../api'
import { ThemeContext } from '../../util/themecontext'
-import { LinksByNodeId, NodeByCite, NodeById } from '../../pages/index'
+import { LinksByNodeId, NodeByCite, NodeById, Scope } from '../../pages/index'
+import { Resizable } from 're-resizable'
+import { usePersistantState } from '../../util/persistant-state'
export interface SidebarProps {
isOpen: boolean
@@ -28,6 +32,10 @@ export interface SidebarProps {
resetPreviewNode: any
previousPreviewNode: any
nextPreviewNode: any
+ openContextMenu: any
+ scope: Scope
+ setScope: any
+ windowWidth: number
}
const Sidebar = (props: SidebarProps) => {
@@ -46,10 +54,15 @@ const Sidebar = (props: SidebarProps) => {
resetPreviewNode,
previousPreviewNode,
nextPreviewNode,
+ openContextMenu,
+ scope,
+ setScope,
+ windowWidth,
} = props
const { highlightColor } = useContext(ThemeContext)
const [previewRoamNode, setPreviewRoamNode] = useState<OrgRoamNode>()
+ const [sidebarWidth, setSidebarWidth] = usePersistantState<number>('sidebarWidth', 400)
useEffect(() => {
if (!previewNode?.id) {
@@ -67,25 +80,58 @@ const Sidebar = (props: SidebarProps) => {
//maybe want to close it when clicking outside, but not sure
//const outsideClickRef = useRef();
return (
- <Slide
- direction="right"
+ <Collapse
+ animateOpacity={false}
+ dimension="width"
in={isOpen}
- style={{ width: 'clamp(400px, 30%, 500px)' }}
+ //style={{ position: 'relative' }}
unmountOnExit
+ startingSize={0}
+ style={{ height: '100vh' }}
>
- <Flex flexDirection="row" height="100%">
- <Box pl={2} color="black" bg="alt.100" w="100%" paddingBottom={15}>
+ <Resizable
+ size={{ height: '100%', width: sidebarWidth }}
+ onResizeStop={(e, direction, ref, d) => {
+ setSidebarWidth((curr: number) => curr + d.width)
+ }}
+ enable={{
+ top: false,
+ right: false,
+ bottom: false,
+ left: true,
+ topRight: false,
+ bottomRight: false,
+ bottomLeft: false,
+ topLeft: false,
+ }}
+ minWidth="220px"
+ maxWidth={windowWidth - 200}
+ >
+ <Box pl={2} color="black" h="100%" bg="alt.100" width="100%">
<Flex
- justifyContent="space-between"
- paddingTop={4}
+ whiteSpace="nowrap"
+ overflow="hidden"
+ textOverflow="ellipsis"
pl={4}
- pb={5}
- pr={3}
- alignItems="top"
+ alignItems="center"
color="black"
+ width="100%"
>
- <Flex alignItems="center" whiteSpace="nowrap" textOverflow="ellipsis" overflow="hidden">
- <BiFile />
+ <BiFile
+ onContextMenu={(e) => {
+ e.preventDefault()
+ openContextMenu(previewNode, e)
+ }}
+ />
+ <Flex
+ whiteSpace="nowrap"
+ textOverflow="ellipsis"
+ overflow="hidden"
+ onContextMenu={(e) => {
+ e.preventDefault()
+ openContextMenu(previewNode, e)
+ }}
+ >
<Heading
pl={2}
whiteSpace="nowrap"
@@ -99,13 +145,23 @@ const Sidebar = (props: SidebarProps) => {
{previewRoamNode?.title}
</Heading>
</Flex>
- <IconButton
- // eslint-disable-next-line react/jsx-no-undef
- icon={<HamburgerIcon />}
- aria-label="Close file-viewer"
- variant="link"
- onClick={onClose}
- />
+ <Flex flexDir="row" ml="auto">
+ <IconButton
+ // eslint-disable-next-line react/jsx-no-undef
+ m={1}
+ icon={<BiDotsVerticalRounded />}
+ aria-label="Options"
+ variant="subtle"
+ onClick={(e) => {
+ openContextMenu(previewNode, e, {
+ left: undefined,
+ top: 12,
+ right: -windowWidth + 20,
+ bottom: undefined,
+ })
+ }}
+ />
+ </Flex>
</Flex>
<Toolbar
{...{
@@ -132,7 +188,7 @@ const Sidebar = (props: SidebarProps) => {
borderRadius: 10,
backgroundColor: highlightColor,
}}
- color="black"
+ color="alt.100"
{...props}
/>
)}
@@ -148,13 +204,14 @@ const Sidebar = (props: SidebarProps) => {
justification,
justificationList,
linksByNodeId,
+ openContextMenu,
}}
/>
</VStack>
</Scrollbars>
</Box>
- </Flex>
- </Slide>
+ </Resizable>
+ </Collapse>
)
}
diff --git a/components/Tweaks/BehaviorPanel.tsx b/components/Tweaks/BehaviorPanel.tsx
index 651396d..5d61730 100644
--- a/components/Tweaks/BehaviorPanel.tsx
+++ b/components/Tweaks/BehaviorPanel.tsx
@@ -11,6 +11,7 @@ import {
VStack,
Text,
Box,
+ Switch,
} from '@chakra-ui/react'
import React from 'react'
import { initialBehavior, initialMouse } from '../config'
diff --git a/components/Tweaks/SliderWithInfo.tsx b/components/Tweaks/SliderWithInfo.tsx
index f70faae..9d6903a 100644
--- a/components/Tweaks/SliderWithInfo.tsx
+++ b/components/Tweaks/SliderWithInfo.tsx
@@ -30,7 +30,7 @@ export const SliderWithInfo = ({
const { onChange, label, infoText } = rest
const { highlightColor } = useContext(ThemeContext)
return (
- <Box key={label}>
+ <Box key={label} pt={1} pb={2}>
<Box display="flex" alignItems="flex-end">
<Text>{label}</Text>
{infoText && <InfoTooltip infoText={infoText} />}
diff --git a/components/Tweaks/index.tsx b/components/Tweaks/index.tsx
index c60e670..33f11ee 100644
--- a/components/Tweaks/index.tsx
+++ b/components/Tweaks/index.tsx
@@ -68,6 +68,7 @@ export const Tweaks = (props: TweakProps) => {
tagColors,
setTagColors,
} = props
+
const [showTweaks, setShowTweaks] = usePersistantState('showTweaks', false)
const { highlightColor, setHighlightColor } = useContext(ThemeContext)
@@ -75,12 +76,12 @@ export const Tweaks = (props: TweakProps) => {
<Box
position="absolute"
zIndex="overlay"
- marginTop={10}
- marginLeft={10}
+ marginTop={0}
+ marginLeft={0}
display={showTweaks ? 'none' : 'block'}
>
<IconButton
- variant="ghost"
+ variant="subtle"
aria-label="Settings"
icon={<SettingsIcon />}
onClick={() => setShowTweaks(true)}
@@ -88,17 +89,17 @@ export const Tweaks = (props: TweakProps) => {
</Box>
) : (
<Box
+ position="absolute"
bg="alt.100"
w="xs"
- marginTop={10}
- marginLeft={10}
- borderRadius="xl"
+ marginTop={2}
+ marginLeft={2}
+ borderRadius="lg"
paddingBottom={5}
- zIndex={300}
- position="relative"
+ zIndex="overlay"
boxShadow="xl"
- maxH={0.92 * globalThis.innerHeight}
- marginBottom={10}
+ maxH={'95vh'}
+ fontSize="sm"
>
<Box
display="flex"
@@ -108,7 +109,7 @@ export const Tweaks = (props: TweakProps) => {
paddingTop={1}
>
<Tooltip label={'Switch to ' + threeDim ? '2D' : '3D' + ' view'}>
- <Button onClick={() => setThreeDim(!threeDim)} variant="ghost" zIndex="overlay">
+ <Button onClick={() => setThreeDim(!threeDim)} variant="subtle" zIndex="overlay">
{threeDim ? '3D' : '2D'}
</Button>
</Tooltip>
@@ -124,7 +125,7 @@ export const Tweaks = (props: TweakProps) => {
setPhysics(initialPhysics)
setBehavior(initialBehavior)
}}
- variant="none"
+ variant="subtle"
size="sm"
/>
</Tooltip>
@@ -132,7 +133,7 @@ export const Tweaks = (props: TweakProps) => {
size="sm"
icon={<CloseIcon />}
aria-label="Close Tweak Panel"
- variant="ghost"
+ variant="subtle"
onClick={() => setShowTweaks(false)}
/>
</Box>
diff --git a/components/config.ts b/components/config.ts
index e1923f2..fd77ba3 100644
--- a/components/config.ts
+++ b/components/config.ts
@@ -58,13 +58,13 @@ export const initialVisuals = {
nodeOpacity: 1,
nodeResolution: 12,
labels: 2,
- labelScale: 1.5,
+ labelScale: 1,
labelFontSize: 13,
labelLength: 40,
labelWordWrap: 25,
labelLineSpace: 1,
highlight: true,
- highlightNodeSize: 2,
+ highlightNodeSize: 1.2,
highlightLinkSize: 2,
highlightFade: 0.8,
highlightAnim: true,
@@ -123,6 +123,7 @@ export const initialMouse = {
follow: 'never',
context: 'right',
preview: 'click',
+ backgroundExitsLocal: false,
}
export const colorList = [
diff --git a/components/contextmenu.tsx b/components/contextmenu.tsx
index 73758a5..bff4861 100644
--- a/components/contextmenu.tsx
+++ b/components/contextmenu.tsx
@@ -48,20 +48,21 @@ import { BiNetworkChart } from 'react-icons/bi'
export default interface ContextMenuProps {
background: Boolean
- node?: OrgRoamNode
+ target: OrgRoamNode | null
nodeType?: string
- coordinates: number[]
+ coordinates: { [direction: string]: number }
handleLocal: (node: OrgRoamNode, add: string) => void
menuClose: () => void
scope: { nodeIds: string[] }
webSocket: any
setPreviewNode: any
+ contextMenuRef: any
}
export const ContextMenu = (props: ContextMenuProps) => {
const {
background,
- node,
+ target,
nodeType,
coordinates,
handleLocal,
@@ -69,59 +70,65 @@ export const ContextMenu = (props: ContextMenuProps) => {
scope,
webSocket,
setPreviewNode,
+ contextMenuRef,
} = props
const { isOpen, onOpen, onClose } = useDisclosure()
const copyRef = useRef<any>()
return (
<>
- <Box
- position="absolute"
- zIndex="overlay"
- left={coordinates[0] + 10}
- top={coordinates[1] - 10}
- padding={5}
- >
- <Menu closeOnBlur={false} defaultIsOpen onClose={() => menuClose()}>
- <MenuList zIndex="overlay" bgColor="alt.100" borderColor="gray.500" maxWidth="xs">
- {node && (
- <>
- <Heading size="sm" isTruncated px={3} py={1}>
- {node.title}
- </Heading>
- <MenuDivider borderColor="gray.500" />
- </>
- )}
- {scope.nodeIds.length !== 0 && (
- <>
- <MenuItem onClick={() => handleLocal(node!, 'add')} icon={<PlusSquareIcon />}>
- Expand local graph at node
- </MenuItem>
- <MenuItem onClick={() => handleLocal(node!, 'replace')} icon={<ViewIcon />}>
- Open local graph for this node
- </MenuItem>
- </>
- )}
- {!node?.properties.FILELESS ? (
- <MenuItem
- icon={<EditIcon />}
- onClick={() => openNodeInEmacs(node as OrgRoamNode, webSocket)}
- >
- Open in Emacs
+ <Menu defaultIsOpen closeOnBlur={false} onClose={() => menuClose()}>
+ <MenuList
+ zIndex="overlay"
+ bgColor="white"
+ color="black"
+ borderColor="gray.500"
+ maxWidth="xs"
+ position="absolute"
+ left={coordinates.left}
+ top={coordinates.top}
+ right={coordinates.right}
+ bottom={coordinates.bottom}
+ fontSize="xs"
+ >
+ {target && (
+ <>
+ <Heading size="xs" isTruncated px={3} py={1}>
+ {target.title}
+ </Heading>
+ <MenuDivider borderColor="gray.500" />
+ </>
+ )}
+ {scope.nodeIds.length !== 0 && (
+ <>
+ <MenuItem onClick={() => handleLocal(target!, 'add')} icon={<PlusSquareIcon />}>
+ Expand local graph at node
</MenuItem>
- ) : (
- <MenuItem icon={<AddIcon />} onClick={() => createNodeInEmacs(node, webSocket)}>
- Create node
+ <MenuItem onClick={() => handleLocal(target!, 'replace')} icon={<BiNetworkChart />}>
+ Open local graph for this node
</MenuItem>
- )}
- {node?.properties.ROAM_REFS && (
- <MenuItem icon={<ExternalLinkIcon />}>Open in Zotero</MenuItem>
- )}
- {scope.nodeIds.length === 0 && (
- <MenuItem icon={<BiNetworkChart />} onClick={() => handleLocal(node!, 'replace')}>
- Open local graph
- </MenuItem>
- )}
- {/* Doesn't work at the moment
+ </>
+ )}
+ {!target?.properties?.FILELESS ? (
+ <MenuItem
+ icon={<EditIcon />}
+ onClick={() => openNodeInEmacs(target as OrgRoamNode, webSocket)}
+ >
+ Open in Emacs
+ </MenuItem>
+ ) : (
+ <MenuItem icon={<AddIcon />} onClick={() => createNodeInEmacs(target, webSocket)}>
+ Create node
+ </MenuItem>
+ )}
+ {target?.properties?.ROAM_REFS && (
+ <MenuItem icon={<ExternalLinkIcon />}>Open in Zotero</MenuItem>
+ )}
+ {scope.nodeIds.length === 0 && (
+ <MenuItem icon={<BiNetworkChart />} onClick={() => handleLocal(target!, 'replace')}>
+ Open local graph
+ </MenuItem>
+ )}
+ {/* Doesn't work at the moment
<MenuItem closeOnSelect={false} closeOnBlur={false}>
<Box _hover={{ bg: 'gray.200' }} width="100%">
<Popover
@@ -151,27 +158,26 @@ export const ContextMenu = (props: ContextMenuProps) => {
</Box>
</MenuItem> */}
+ <MenuItem
+ icon={<ViewIcon />}
+ onClick={() => {
+ setPreviewNode(target)
+ }}
+ >
+ Preview
+ </MenuItem>
+ {target?.level === 0 && (
<MenuItem
- icon={<ViewIcon />}
- onClick={() => {
- setPreviewNode(node)
- }}
+ closeOnSelect={false}
+ icon={<DeleteIcon color="red.500" />}
+ color="red.500"
+ onClick={onOpen}
>
- Preview
+ Permenantly delete note
</MenuItem>
- {node?.level === 0 && (
- <MenuItem
- closeOnSelect={false}
- icon={<DeleteIcon color="red.500" />}
- color="red.500"
- onClick={onOpen}
- >
- Permenantly delete note
- </MenuItem>
- )}
- </MenuList>
- </Menu>
- </Box>
+ )}
+ </MenuList>
+ </Menu>
<Modal isCentered isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent zIndex="popover">
@@ -180,8 +186,8 @@ export const ContextMenu = (props: ContextMenuProps) => {
<ModalBody>
<VStack spacing={4} display="flex" alignItems="flex-start">
<Text>This will permanently delete your note:</Text>
- <Text fontWeight="bold">{node?.title}</Text>
- {node?.level !== 0 && (
+ <Text fontWeight="bold">{target?.title}</Text>
+ {target?.level !== 0 && (
<Text>
This will only delete the from this heading until but not including the next node.
Your parent file and all other nodes will not be deleted.
@@ -207,7 +213,7 @@ export const ContextMenu = (props: ContextMenuProps) => {
ml={3}
onClick={() => {
console.log('aaaaa')
- deleteNodeInEmacs(node!, webSocket)
+ deleteNodeInEmacs(target!, webSocket)
onClose()
menuClose()
}}
diff --git a/package-lock.json b/package-lock.json
index 1a6f57f..310c69b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"orgast-util-to-string": "^0.3.1",
"orgast-util-visit-ids": "^0.3.1",
"path": "^0.12.7",
+ "re-resizable": "^6.9.1",
"react": "17.0.2",
"react-custom-scrollbars-2": "^4.4.0",
"react-dom": "17.0.2",
@@ -4813,6 +4814,11 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
+ "node_modules/fast-memoize": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
+ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
+ },
"node_modules/fast-shallow-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
@@ -7539,6 +7545,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/re-resizable": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.1.tgz",
+ "integrity": "sha512-KRYAgr9/j1PJ3K+t+MBhlQ+qkkoLDJ1rs0z1heIWvYbCW/9Vq4djDU+QumJ3hQbwwtzXF6OInla6rOx6hhgRhQ==",
+ "dependencies": {
+ "fast-memoize": "^2.5.1"
+ },
+ "peerDependencies": {
+ "react": "^16.13.1 || ^17.0.0",
+ "react-dom": "^16.13.1 || ^17.0.0"
+ }
+ },
"node_modules/react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
@@ -13414,6 +13432,11 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
+ "fast-memoize": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
+ "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
+ },
"fast-shallow-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
@@ -15572,6 +15595,14 @@
}
}
},
+ "re-resizable": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.1.tgz",
+ "integrity": "sha512-KRYAgr9/j1PJ3K+t+MBhlQ+qkkoLDJ1rs0z1heIWvYbCW/9Vq4djDU+QumJ3hQbwwtzXF6OInla6rOx6hhgRhQ==",
+ "requires": {
+ "fast-memoize": "^2.5.1"
+ }
+ },
"react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
diff --git a/package.json b/package.json
index a0b4777..80f47d6 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"orgast-util-to-string": "^0.3.1",
"orgast-util-visit-ids": "^0.3.1",
"path": "^0.12.7",
+ "re-resizable": "^6.9.1",
"react": "17.0.2",
"react-custom-scrollbars-2": "^4.4.0",
"react-dom": "17.0.2",
diff --git a/pages/_app.tsx b/pages/_app.tsx
index f623786..17554b1 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -173,6 +173,11 @@ function SubApp(props: any) {
_hover: { bg: `inherit`, border: '1px solid', borderColor: highlightColor },
_active: { color: `inherit`, bg: highlightColor },
},
+ subtle: {
+ color: 'gray.800',
+ _hover: { bg: `inherit`, color: highlightColor },
+ _active: { color: `inherit`, bg: borderColor },
+ },
},
},
Accordion: {
diff --git a/pages/index.tsx b/pages/index.tsx
index a1df7e7..c80fc50 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -1,52 +1,59 @@
+import { HamburgerIcon } from '@chakra-ui/icons'
+import {
+ Box,
+ Flex,
+ Heading,
+ IconButton,
+ Slide,
+ Tooltip,
+ useDisclosure,
+ useOutsideClick,
+ useTheme,
+} from '@chakra-ui/react'
+import { useAnimation } from '@lilib/hooks'
+import { useWindowSize, useWindowWidth } from '@react-hook/window-size'
+import * as d3int from 'd3-interpolate'
+import { GraphData, LinkObject, NodeObject } from 'force-graph'
+import Head from 'next/head'
import React, {
ComponentPropsWithoutRef,
+ forwardRef,
+ useContext,
useEffect,
+ useMemo,
useRef,
useState,
- useMemo,
- useContext,
- forwardRef,
} from 'react'
-
-import Head from 'next/head'
-import { usePersistantState } from '../util/persistant-state'
-const d3promise = import('d3-force-3d')
-import * as d3int from 'd3-interpolate'
-
import type {
ForceGraph2D as TForceGraph2D,
ForceGraph3D as TForceGraph3D,
} from 'react-force-graph'
+import { BiChart, BiNetworkChart } from 'react-icons/bi'
+import { BsReverseLayoutSidebarInsetReverse } from 'react-icons/bs'
+import ReconnectingWebSocket from 'reconnecting-websocket'
+import SpriteText from 'three-spritetext'
+import useUndo from 'use-undo'
+import wrap from 'word-wrap'
import { OrgRoamGraphReponse, OrgRoamLink, OrgRoamNode } from '../api'
-import { GraphData, NodeObject, LinkObject } from 'force-graph'
-import Sidebar from '../components/Sidebar'
-
-import { useWindowSize } from '@react-hook/window-size'
-import { useAnimation } from '@lilib/hooks'
-
-import { Box, Flex, IconButton, useDisclosure, useTheme, WithCSSVar } from '@chakra-ui/react'
-
import {
- initialPhysics,
- initialFilter,
- initialVisuals,
+ algos,
+ colorList,
initialBehavior,
+ initialFilter,
initialMouse,
- algos,
+ initialPhysics,
+ initialVisuals,
TagColors,
- colorList,
} from '../components/config'
-import { Tweaks } from '../components/Tweaks'
import { ContextMenu } from '../components/contextmenu'
-
+import Sidebar from '../components/Sidebar'
+import { Tweaks } from '../components/Tweaks'
+import { usePersistantState } from '../util/persistant-state'
import { ThemeContext, ThemeContextProps } from '../util/themecontext'
-import SpriteText from 'three-spritetext'
-import wrap from 'word-wrap'
-import ReconnectingWebSocket from 'reconnecting-websocket'
+import { openNodeInEmacs } from '../util/webSocketFunctions'
+
+const d3promise = import('d3-force-3d')
-import { deleteNodeInEmacs, openNodeInEmacs, createNodeInEmacs } from '../util/webSocketFunctions'
-import { ChevronLeftIcon, HamburgerIcon } from '@chakra-ui/icons'
-import useUndo from 'use-undo'
// react-force-graph fails on import when server-rendered
// https://github.com/vasturiano/react-force-graph/issues/155
const ForceGraph2D = (
@@ -450,53 +457,91 @@ export function GraphPage() {
}, 50)
}, [scope.nodeIds])
+ const [windowWidth, windowHeight] = useWindowSize()
+
+ const contextMenuRef = useRef<any>()
+ const [contextMenuTarget, setContextMenuTarget] = useState<OrgRoamNode | null>(null)
+ const [contextPos, setContextPos] = useState({
+ left: 0,
+ top: 0,
+ right: undefined,
+ bottom: undefined,
+ })
+
+ const contextMenu = useDisclosure()
+ useOutsideClick({
+ ref: contextMenuRef,
+ handler: () => {
+ console.log('click')
+ contextMenu.onClose()
+ },
+ })
+
+ const openContextMenu = (node: OrgRoamNode, event: any, coords?: number[]) => {
+ coords
+ ? setContextPos(coords)
+ : setContextPos({ left: event.pageX, top: event.pageY, right: undefined, bottom: undefined })
+ setContextMenuTarget(node)
+ contextMenu.onOpen()
+ }
+
+ const handleLocal = (node: OrgRoamNode, add: string) => {
+ if (add === 'replace') {
+ setScope({ nodeIds: [node.id] })
+ return
+ }
+ if (scope.nodeIds.includes(node.id as string)) {
+ return
+ }
+ setScope((currentScope: Scope) => ({
+ ...currentScope,
+ nodeIds: [...currentScope.nodeIds, node.id as string],
+ }))
+ return
+ }
+
+ const [mainItem, setMainItem] = useState({
+ type: 'Graph',
+ title: 'Graph',
+ icon: <BiNetworkChart />,
+ })
+
+ const [mainWindowWidth, setMainWindowWidth] = usePersistantState<number>(
+ 'mainWindowWidth',
+ windowWidth,
+ )
if (!graphData) {
return null
}
return (
- <Box display="flex" alignItems="flex-start" flexDirection="row" height="100%" overflow="hidden">
- <Box
- display="flex"
- justifyContent="space-between"
- flexDirection="row"
- height="100%"
- width="100%"
- >
- <Tweaks
- {...{
- physics,
- setPhysics,
- threeDim,
- setThreeDim,
- filter,
- setFilter,
- visuals,
- setVisuals,
- mouse,
- setMouse,
- behavior,
- setBehavior,
- tagColors,
- setTagColors,
- }}
- tags={tagsRef.current}
- />
- <Flex height="100%" flexDirection="column" marginLeft="auto">
- {!isOpen && (
- <IconButton
- icon={<HamburgerIcon />}
- aria-label="Open org-viewer"
- zIndex={2}
- onClick={onOpen}
- variant="ghost"
- marginTop={10}
- mr={8}
- />
- )}
- </Flex>
- </Box>
- <Flex position="absolute" alignItems="top" overflow="hidden">
+ <Box
+ display="flex"
+ alignItems="flex-start"
+ flexDirection="row"
+ height="100vh"
+ overflow="hidden"
+ >
+ <Tweaks
+ {...{
+ physics,
+ setPhysics,
+ threeDim,
+ setThreeDim,
+ filter,
+ setFilter,
+ visuals,
+ setVisuals,
+ mouse,
+ setMouse,
+ behavior,
+ setBehavior,
+ tagColors,
+ setTagColors,
+ }}
+ tags={tagsRef.current}
+ />
+ <Box position="absolute">
<Graph
ref={graphRef}
nodeById={nodeByIdRef.current!}
@@ -516,8 +561,58 @@ export function GraphPage() {
tagColors,
setPreviewNode,
sidebarHighlightedNode,
+ windowWidth,
+ windowHeight,
+ openContextMenu,
+ contextMenu,
+ handleLocal,
+ mainWindowWidth,
+ setMainWindowWidth,
}}
/>
+ </Box>
+ <Box position="relative" zIndex={4} width="100%">
+ <Flex className="headerBar" h={10} flexDir="column">
+ <Flex alignItems="center" h={10} justifyContent="flex-end">
+ {/* <Flex flexDir="row" alignItems="center">
+ * <Box color="blue.500" bgColor="alt.100" h="100%" p={3} mr={4}>
+ * {mainItem.icon}
+ * </Box>
+ * <Heading size="sm">{mainItem.title}</Heading>
+ * </Flex> */}
+ <Flex height="100%" flexDirection="row">
+ {scope.nodeIds.length > 0 && (
+ <Tooltip label="Return to main graph">
+ <IconButton
+ m={1}
+ icon={<BiNetworkChart />}
+ aria-label="Exit local mode"
+ onClick={() =>
+ setScope((currentScope: Scope) => ({
+ ...currentScope,
+ nodeIds: [],
+ }))
+ }
+ variant="subtle"
+ />
+ </Tooltip>
+ )}
+ <Tooltip label={isOpen ? 'Close sidebar' : 'Open sidebar'}>
+ <IconButton
+ m={1}
+ // eslint-disable-next-line react/jsx-no-undef
+ icon={<BsReverseLayoutSidebarInsetReverse />}
+ aria-label="Close file-viewer"
+ variant="subtle"
+ onClick={isOpen ? onClose : onOpen}
+ />
+ </Tooltip>
+ </Flex>
+ </Flex>
+ </Flex>
+ </Box>
+
+ <Box position="relative" zIndex={4}>
<Sidebar
{...{
isOpen,
@@ -529,13 +624,33 @@ export function GraphPage() {
canRedo,
previousPreviewNode,
nextPreviewNode,
+ resetPreviewNode,
setSidebarHighlightedNode,
+ openContextMenu,
+ scope,
+ setScope,
+ windowWidth,
}}
nodeById={nodeByIdRef.current!}
linksByNodeId={linksByNodeIdRef.current!}
nodeByCite={nodeByCiteRef.current!}
/>
- </Flex>
+ </Box>
+ {contextMenu.isOpen && (
+ <div ref={contextMenuRef}>
+ <ContextMenu
+ //contextMenuRef={contextMenuRef}
+ scope={scope}
+ target={contextMenuTarget}
+ background={false}
+ coordinates={contextPos}
+ handleLocal={handleLocal}
+ menuClose={contextMenu.onClose.bind(contextMenu)}
+ webSocket={WebSocketRef.current}
+ setPreviewNode={setPreviewNode}
+ />
+ </div>
+ )}
</Box>
)
}
@@ -557,6 +672,14 @@ export interface GraphProps {
tagColors: { [tag: string]: string }
setPreviewNode: any
sidebarHighlightedNode: OrgRoamNode | null
+ windowWidth: number
+ windowHeight: number
+ setContextMenuTarget: any
+ openContextMenu: any
+ contextMenu: any
+ handleLocal: any
+ mainWindowWidth: number
+ setMainWindowWidth: any
}
export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
@@ -577,47 +700,25 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
tagColors,
setPreviewNode,
sidebarHighlightedNode,
+ windowWidth,
+ windowHeight,
+ setContextMenuTarget,
+ openContextMenu,
+ contextMenu,
+ handleLocal,
} = props
- // react-force-graph does not track window size
- // https://github.com/vasturiano/react-force-graph/issues/233
- // does not work below a certain width
- const [windowWidth, windowHeight] = useWindowSize()
-
const [hoverNode, setHoverNode] = useState<NodeObject | null>(null)
- const [rightClickedNode, setRightClickedNode] = useState<OrgRoamNode | null>(null)
- const [contextPos, setContextPos] = useState([0, 0])
-
const theme = useTheme()
const { emacsTheme } = useContext<ThemeContextProps>(ThemeContext)
- const handleLocal = (node: OrgRoamNode, add: string) => {
- if (add === 'replace') {
- setScope({ nodeIds: [node.id] })
- return
- }
- if (scope.nodeIds.includes(node.id as string)) {
- return
- }
- setScope((currentScope: Scope) => ({
- ...currentScope,
- nodeIds: [...currentScope.nodeIds, node.id as string],
- }))
- return
- }
-
- const contextMenu = useDisclosure()
-
- const openContextMenu = (node: OrgRoamNode, event: any) => {
- setContextPos([event.pageX, event.pageY])
- setRightClickedNode(node)
- contextMenu.onOpen()
- }
-
const handleClick = (click: string, node: OrgRoamNode, event: any) => {
switch (click) {
+ case mouse.preview: {
+ setPreviewNode(node)
+ }
case mouse.local: {
handleLocal(node, behavior.localSame)
break
@@ -629,9 +730,6 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
case mouse.context: {
openContextMenu(node, event)
}
- case mouse.preview: {
- setPreviewNode(node)
- }
default:
break
}
@@ -1062,7 +1160,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
backgroundColor: theme.colors.gray[visuals.backgroundColor],
warmupTicks: scope.nodeIds.length === 1 ? 100 : scope.nodeIds.length > 1 ? 20 : 0,
onZoom: ({ k, x, y }) => setZoom(k),
- nodeLabel: (node) => (node as OrgRoamNode).title,
+ //nodeLabel: (node) => (node as OrgRoamNode).title,
nodeColor: (node) => {
return getNodeColor(node as OrgRoamNode, theme)
},
@@ -1199,7 +1297,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
onNodeClick: (nodeArg: NodeObject, event: any) => {
const node = nodeArg as OrgRoamNode
- contextMenu.onClose()
+ //contextMenu.onClose()
const doubleClickTimeBuffer = 200
const isDoubleClick = event.timeStamp - lastNodeClickRef.current < doubleClickTimeBuffer
lastNodeClickRef.current = event.timeStamp
@@ -1215,17 +1313,19 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
return handleClick('click', node, event)
}, doubleClickTimeBuffer)
},
- onBackgroundClick: () => {
- contextMenu.onClose()
- setHoverNode(null)
- if (scope.nodeIds.length === 0) {
- return
- }
- setScope((currentScope: Scope) => ({
- ...currentScope,
- nodeIds: [],
- }))
- },
+ /* onBackgroundClick: () => {
+ * contextMenu.onClose()
+ * setHoverNode(null)
+ * if (scope.nodeIds.length === 0) {
+ * return
+ * }
+ * if (mouse.backgroundExitsLocal) {
+ * setScope((currentScope: Scope) => ({
+ * ...currentScope,
+ * nodeIds: [],
+ * }))
+ * }
+ * }, */
onNodeHover: (node) => {
if (!visuals.highlight) {
return
@@ -1243,7 +1343,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
handleClick('right', node, event)
},
onNodeDrag: (node) => {
- contextMenu.onClose()
+ //contextMenu.onClose()
setHoverNode(node)
setDragging(true)
},
@@ -1254,20 +1354,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
}
return (
- <Box overflow="hidden">
- {contextMenu.isOpen && (
- <ContextMenu
- scope={scope}
- node={rightClickedNode!}
- nodeType={rightClickedNode?.id}
- background={false}
- coordinates={contextPos}
- handleLocal={handleLocal}
- menuClose={contextMenu.onClose.bind(contextMenu)}
- webSocket={webSocket}
- setPreviewNode={setPreviewNode}
- />
- )}
+ <Box overflow="hidden" onClick={contextMenu.onClose}>
{threeDim ? (
<ForceGraph3D
ref={graphRef}
diff --git a/util/processOrg.tsx b/util/processOrg.tsx
index 1bab2cd..05142dd 100644
--- a/util/processOrg.tsx
+++ b/util/processOrg.tsx
@@ -7,7 +7,7 @@ import extractKeywords from 'uniorg-extract-keywords'
import attachments from 'uniorg-attach'
// rehypeHighlight does not have any types
// @ts-expect-error
-import highlight from 'rehype-highlight'
+// import highlight from 'rehype-highlight'
import katex from 'rehype-katex'
import 'katex/dist/katex.css'
import rehype2react from 'rehype-react'
@@ -24,6 +24,7 @@ export interface ProcessedOrgProps {
previewText: any
nodeByCite: NodeByCite
setSidebarHighlightedNode: any
+ openContextMenu: any
}
export const ProcessedOrg = (props: ProcessedOrgProps) => {
@@ -34,6 +35,7 @@ export const ProcessedOrg = (props: ProcessedOrgProps) => {
previewText,
nodeByCite,
previewNode,
+ openContextMenu,
} = props
const processor = unified()
@@ -42,7 +44,7 @@ export const ProcessedOrg = (props: ProcessedOrgProps) => {
.use(attachments)
.use(uniorgSlug)
.use(uniorg2rehype)
- .use(highlight)
+ // .use(highlight)
.use(katex)
.use(rehype2react, {
createElement: React.createElement,
@@ -56,6 +58,7 @@ export const ProcessedOrg = (props: ProcessedOrgProps) => {
href={`${href as string}`}
nodeById={nodeById}
setPreviewNode={setPreviewNode}
+ openContextMenu={openContextMenu}
>
{children}
</PreviewLink>
diff --git a/util/uniorg.tsx b/util/uniorg.tsx
index 217bad1..8802dd1 100644
--- a/util/uniorg.tsx
+++ b/util/uniorg.tsx
@@ -9,10 +9,18 @@ export interface UniOrgProps {
setPreviewNode: any
nodeByCite: NodeByCite
setSidebarHighlightedNode: any
+ openContextMenu: any
}
export const UniOrg = (props: UniOrgProps) => {
- const { setSidebarHighlightedNode, nodeById, nodeByCite, previewNode, setPreviewNode } = props
+ const {
+ openContextMenu,
+ setSidebarHighlightedNode,
+ nodeById,
+ nodeByCite,
+ previewNode,
+ setPreviewNode,
+ } = props
const [previewText, setPreviewText] = useState('')
@@ -44,6 +52,7 @@ export const UniOrg = (props: UniOrgProps) => {
previewText,
nodeByCite,
setSidebarHighlightedNode,
+ openContextMenu,
}}
/>
)}