From 31d7477b376501bd80fe635b91412bc7f6ae7ea7 Mon Sep 17 00:00:00 2001 From: "Thomas F. K. Jorna" Date: Sat, 9 Oct 2021 22:20:34 +0200 Subject: feat(preview): node history --- components/Sidebar/OrgImage.tsx | 9 ++++- components/Sidebar/Toolbar.tsx | 36 ++++++++++++++++++-- components/Sidebar/index.tsx | 27 +++++++++++++-- components/Sidebar/noteStyle.ts | 15 ++++----- package-lock.json | 73 +++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ pages/index.tsx | 42 ++++++++++++++++++------ util/processOrg.tsx | 1 + 8 files changed, 181 insertions(+), 24 deletions(-) diff --git a/components/Sidebar/OrgImage.tsx b/components/Sidebar/OrgImage.tsx index a87fc4c..f9f508a 100644 --- a/components/Sidebar/OrgImage.tsx +++ b/components/Sidebar/OrgImage.tsx @@ -30,6 +30,13 @@ export const OrgImage = (props: OrgImageProps) => { } return ( - + ) } diff --git a/components/Sidebar/Toolbar.tsx b/components/Sidebar/Toolbar.tsx index 458361f..f6f1e39 100644 --- a/components/Sidebar/Toolbar.tsx +++ b/components/Sidebar/Toolbar.tsx @@ -9,21 +9,51 @@ import { BiRightIndent, } from 'react-icons/bi' import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons' +import { NodeObject } from 'force-graph' export interface ToolbarProps { setJustification: any justification: number setIndent: any setFont: any + setPreviewNode: any + canUndo: any + canRedo: any + resetPreviewNode: any + previousPreviewNode: any + nextPreviewNode: any } export const Toolbar = (props: ToolbarProps) => { - const { setJustification, setIndent, setFont, justification } = props + const { + setJustification, + setIndent, + setFont, + justification, + setPreviewNode, + canUndo, + canRedo, + resetPreviewNode, + previousPreviewNode, + nextPreviewNode, + } = props return ( - } aria-label="Previous node" /> - } aria-label="Previous node" /> + } + aria-label="Previous node" + disabled={!canUndo} + onClick={() => previousPreviewNode()} + /> + } + aria-label="Next node" + disabled={!canRedo} + onClick={() => nextPreviewNode()} + /> { @@ -36,6 +41,11 @@ const Sidebar = (props: SidebarProps) => { linksByNodeId, nodeByCite, setSidebarHighlightedNode, + canUndo, + canRedo, + resetPreviewNode, + previousPreviewNode, + nextPreviewNode, } = props const { highlightColor } = useContext(ThemeContext) @@ -97,7 +107,20 @@ const Sidebar = (props: SidebarProps) => { onClick={onClose} /> - + =6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -7579,6 +7589,19 @@ "react": "17.0.2" } }, + "node_modules/react-draggable": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz", + "integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-fast-compare": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", @@ -7707,6 +7730,18 @@ } } }, + "node_modules/react-resizable": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz", + "integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==", + "dependencies": { + "prop-types": "15.x", + "react-draggable": "^4.0.3" + }, + "peerDependencies": { + "react": ">= 16.3" + } + }, "node_modules/react-spring": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-9.2.4.tgz", @@ -9411,6 +9446,15 @@ "react": "^16.8.0 || ^17.0.0" } }, + "node_modules/use-undo": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/use-undo/-/use-undo-1.0.5.tgz", + "integrity": "sha512-rLlwvcrioEqZ0t/M4hpgCsrCMgCpbodUXMRskK98g3ftdGFi7wr9DzxPY93vKHK17mMgxBY2ar7bgZ5cNFSiNQ==", + "peerDependencies": { + "react": "^16.8.6", + "react-dom": "^16.8.6" + } + }, "node_modules/util": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", @@ -12087,6 +12131,11 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -15560,6 +15609,15 @@ "scheduler": "^0.20.2" } }, + "react-draggable": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz", + "integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==", + "requires": { + "clsx": "^1.1.1", + "prop-types": "^15.6.0" + } + }, "react-fast-compare": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", @@ -15647,6 +15705,15 @@ "tslib": "^1.0.0" } }, + "react-resizable": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz", + "integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==", + "requires": { + "prop-types": "15.x", + "react-draggable": "^4.0.3" + } + }, "react-spring": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-9.2.4.tgz", @@ -16941,6 +17008,12 @@ "object-assign": "^4.1.1" } }, + "use-undo": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/use-undo/-/use-undo-1.0.5.tgz", + "integrity": "sha512-rLlwvcrioEqZ0t/M4hpgCsrCMgCpbodUXMRskK98g3ftdGFi7wr9DzxPY93vKHK17mMgxBY2ar7bgZ5cNFSiNQ==", + "requires": {} + }, "util": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", diff --git a/package.json b/package.json index 66aab66..a0b4777 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-dom": "17.0.2", "react-force-graph": "^1.41.7", "react-icons": "^4.3.1", + "react-resizable": "^3.0.4", "react-spring": "^9.2.4", "reconnecting-websocket": "^4.4.0", "rehype": "^11.0.0", @@ -50,6 +51,7 @@ "uniorg-rehype": "^0.4.0", "uniorg-slug": "^0.4.0", "use-constant": "^1.1.0", + "use-undo": "^1.0.5", "vfile": "^4.2.1", "word-wrap": "^1.2.3" }, diff --git a/pages/index.tsx b/pages/index.tsx index 59fae1a..a1df7e7 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -46,6 +46,7 @@ import ReconnectingWebSocket from 'reconnecting-websocket' 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 = ( @@ -97,7 +98,22 @@ export function GraphPage() { const [emacsNodeId, setEmacsNodeId] = useState(null) const [behavior, setBehavior] = usePersistantState('behavior', initialBehavior) const [mouse, setMouse] = usePersistantState('mouse', initialMouse) - const [previewNode, setPreviewNode] = useState({}) + const [ + previewNodeState, + { + set: setPreviewNode, + reset: resetPreviewNode, + undo: previousPreviewNode, + redo: nextPreviewNode, + canUndo, + canRedo, + }, + ] = useUndo({}) + const { + past: pastPreviewNodes, + present: previewNode, + future: futurePreviewNodes, + } = previewNodeState const [sidebarHighlightedNode, setSidebarHighlightedNode] = useState(null) const { isOpen, onOpen, onClose } = useDisclosure() @@ -509,6 +525,10 @@ export function GraphPage() { onClose, previewNode, setPreviewNode, + canUndo, + canRedo, + previousPreviewNode, + nextPreviewNode, setSidebarHighlightedNode, }} nodeById={nodeByIdRef.current!} @@ -843,14 +863,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) { algorithm: algos[visuals.algorithmName], }, ) - useEffect(() => { - console.log('aaa') - if (sidebarHighlightedNode?.id) { - setHoverNode(sidebarHighlightedNode) - } else { - setHoverNode(null) - } - }, [sidebarHighlightedNode]) + const highlightedNodes = useMemo(() => { if (!centralHighlightedNode.current) { return {} @@ -862,7 +875,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) { } return Object.fromEntries( [ - centralHighlightedNode.current.id! as string, + centralHighlightedNode.current?.id! as string, ...links.flatMap((link) => [link.source, link.target]), ].map((nodeId) => [nodeId, {}]), ) @@ -870,6 +883,15 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) { JSON.stringify(centralHighlightedNode.current), JSON.stringify(filteredLinksByNodeIdRef.current), ]) + + useEffect(() => { + if (sidebarHighlightedNode?.id) { + setHoverNode(sidebarHighlightedNode) + } else { + setHoverNode(null) + } + }, [sidebarHighlightedNode]) + const lastHoverNode = useRef(null) useEffect(() => { centralHighlightedNode.current = hoverNode diff --git a/util/processOrg.tsx b/util/processOrg.tsx index 3f32d30..1bab2cd 100644 --- a/util/processOrg.tsx +++ b/util/processOrg.tsx @@ -42,6 +42,7 @@ export const ProcessedOrg = (props: ProcessedOrgProps) => { .use(attachments) .use(uniorgSlug) .use(uniorg2rehype) + .use(highlight) .use(katex) .use(rehype2react, { createElement: React.createElement, -- cgit v1.2.3