summaryrefslogtreecommitdiff
path: root/components/Sidebar/Link.tsx
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2021-10-11 21:27:17 +0200
committerGitHub <[email protected]>2021-10-11 21:27:17 +0200
commit58b7030d45370072dee25214748670d6413343a9 (patch)
tree9632df7273415f4b197413c45ad11563af32d53a /components/Sidebar/Link.tsx
parent89be3b67b2d10d35d72b5c54e1e166beeeef3095 (diff)
parent6e3dcf585c35620c6804f3c208e6882c29dfc17e (diff)
Merge pull request #101 from org-roam/sidebar
feat: Add file preview functionality
Diffstat (limited to 'components/Sidebar/Link.tsx')
-rw-r--r--components/Sidebar/Link.tsx241
1 files changed, 241 insertions, 0 deletions
diff --git a/components/Sidebar/Link.tsx b/components/Sidebar/Link.tsx
new file mode 100644
index 0000000..49fe9cf
--- /dev/null
+++ b/components/Sidebar/Link.tsx
@@ -0,0 +1,241 @@
+/* eslint-disable react/display-name */
+import {
+ Box,
+ Button,
+ Link,
+ Popover,
+ PopoverArrow,
+ PopoverBody,
+ PopoverCloseButton,
+ PopoverContent,
+ PopoverHeader,
+ PopoverTrigger,
+ Portal,
+ Text,
+ useTheme,
+} from '@chakra-ui/react'
+import React, { ReactElement, useContext, useEffect, useMemo, useState } from 'react'
+
+import unified from 'unified'
+//import createStream from 'unified-stream'
+import uniorgParse from 'uniorg-parse'
+import uniorg2rehype from 'uniorg-rehype'
+//import highlight from 'rehype-highlight'
+import katex from 'rehype-katex'
+import 'katex/dist/katex.css'
+import rehype2react from 'rehype-react'
+import { ThemeContext } from '../../util/themecontext'
+import { NodeByCite, NodeById } from '../../pages'
+
+export interface LinkProps {
+ href: any
+ children: any
+ previewNode?: any
+ setPreviewNode: any
+ setSidebarHighlightedNode: any
+ nodeByCite: NodeByCite
+ nodeById: NodeById
+ openContextMenu: any
+}
+
+export interface NormalLinkProps {
+ setPreviewNode: any
+ nodeById: NodeById
+ nodeByCite: NodeByCite
+ href: any
+ children: any
+ setSidebarHighlightedNode: any
+ openContextMenu: any
+}
+
+import { hexToRGBA, getThemeColor } from '../../pages/index'
+import noteStyle from './noteStyle'
+import { OrgImage } from './OrgImage'
+
+export const NormalLink = (props: NormalLinkProps) => {
+ const { setSidebarHighlightedNode, setPreviewNode, nodeById, openContextMenu, href, children } =
+ props
+ const { highlightColor } = useContext(ThemeContext)
+
+ const theme = useTheme()
+ const coolHighlightColor = getThemeColor(highlightColor, theme)
+ const [whatever, type, uri] = [...href.matchAll(/(.*?)\:(.*)/g)][0]
+ return (
+ <Text
+ onMouseEnter={() => setSidebarHighlightedNode(nodeById[uri])}
+ onMouseLeave={() => setSidebarHighlightedNode({})}
+ tabIndex={0}
+ display="inline"
+ overflow="hidden"
+ 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 }}
+ >
+ {children}
+ </Text>
+ )
+}
+
+export const PreviewLink = (props: LinkProps) => {
+ const {
+ href,
+ children,
+ nodeById,
+ setSidebarHighlightedNode,
+ previewNode,
+ setPreviewNode,
+ nodeByCite,
+ openContextMenu,
+ } = props
+ // TODO figure out how to properly type this
+ // see https://github.com/rehypejs/rehype-react/issues/25
+ const [orgText, setOrgText] = useState<any>(null)
+ const [whatever, type, uri] = [...href.matchAll(/(.*?)\:(.*)/g)][0]
+ const [hover, setHover] = useState(false)
+
+ const getId = (type: string, uri: string) => {
+ if (type === 'id') {
+ return uri
+ }
+
+ if (type.includes('cite')) {
+ const node = nodeByCite[uri] ?? false
+ if (!node) {
+ return ''
+ }
+ if (node?.properties.FILELESS) {
+ return ''
+ }
+ return node?.id
+ }
+ return ''
+ }
+
+ const id = getId(type, uri)
+ const file = encodeURIComponent(encodeURIComponent(nodeById[id]?.file as string))
+
+ const processor = unified()
+ .use(uniorgParse)
+ .use(uniorg2rehype)
+ .use(katex)
+ .use(rehype2react, {
+ createElement: React.createElement,
+ components: {
+ // eslint-disable-next-line react/display-name
+ a: ({ children, href }) => (
+ <PreviewLink
+ nodeByCite={nodeByCite}
+ setSidebarHighlightedNode={setSidebarHighlightedNode}
+ href={href}
+ nodeById={nodeById}
+ setPreviewNode={setPreviewNode}
+ openContextMenu={openContextMenu}
+ >
+ {children}
+ </PreviewLink>
+ ),
+ img: ({ src }) => {
+ return <OrgImage src={src as string} file={nodeById[id]?.file as string} />
+ },
+ },
+ })
+
+ const getText = () => {
+ fetch(`http://localhost:35901/file/${file}`)
+ .then((res) => {
+ return res.text()
+ })
+ .then((res) => {
+ if (res !== 'error') {
+ const text = processor.processSync(res).result
+ setOrgText(text)
+ return
+ }
+ })
+ .catch((e) => {
+ console.log(e)
+ return 'Could not fetch the text for some reason, sorry!\n\n This can happen because you have an id with forward slashes (/) in it.'
+ })
+ }
+
+ useEffect(() => {
+ if (!!orgText) {
+ return
+ }
+ if (!hover) {
+ return
+ }
+ getText()
+ }, [hover, orgText])
+
+ if (id) {
+ return (
+ <>
+ <Popover gutter={12} trigger="hover" placement="top-start">
+ <PopoverTrigger>
+ <Box
+ display="inline"
+ onMouseEnter={() => setHover(true)}
+ onMouseLeave={() => setHover(false)}
+ >
+ <NormalLink
+ key={nodeById[id]?.title ?? id}
+ {...{
+ setSidebarHighlightedNode,
+ setPreviewNode,
+ nodeById,
+ href,
+ children,
+ nodeByCite,
+ openContextMenu,
+ }}
+ />
+ </Box>
+ </PopoverTrigger>
+ <Portal>
+ <PopoverContent
+ key={nodeById[id]?.title ?? id}
+ boxShadow="xl"
+ position="relative"
+ zIndex="tooltip"
+ onMouseEnter={() => {
+ setSidebarHighlightedNode(nodeById[id] ?? {})
+ }}
+ onMouseLeave={() => {
+ setSidebarHighlightedNode({})
+ }}
+ >
+ <PopoverArrow />
+ <PopoverBody
+ pb={5}
+ fontSize="xs"
+ px={5}
+ position="relative"
+ zIndex="tooltip"
+ maxHeight={300}
+ overflow="scroll"
+ >
+ <Box color="black" sx={noteStyle}>
+ {orgText}
+ </Box>
+ </PopoverBody>
+ </PopoverContent>
+ </Portal>
+ </Popover>
+ </>
+ )
+ }
+ return (
+ <Text display="inline" color="base.700" cursor="not-allowed">
+ {children}
+ </Text>
+ )
+}