From 9c4a9cb1fb077670db048250c902031a9cda9210 Mon Sep 17 00:00:00 2001 From: "Thomas F. K. Jorna" Date: Sat, 24 Jul 2021 19:49:59 +0200 Subject: added filter and orphan toggle --- api.d.ts | 2 + org-roam-ui.el | 6 +-- pages/index.tsx | 122 ++++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/api.d.ts b/api.d.ts index 8053efc..8539ad8 100644 --- a/api.d.ts +++ b/api.d.ts @@ -7,9 +7,11 @@ export type OrgRoamNode = { id: string file: string title: string + level: number } export type OrgRoamLink = { source: string target: string + type: string } diff --git a/org-roam-ui.el b/org-roam-ui.el index 560315b..d823e77 100644 --- a/org-roam-ui.el +++ b/org-roam-ui.el @@ -78,11 +78,11 @@ This serves the web-build and API over HTTP." (defservlet* graph application/json () (let* ((nodes-columns [id file title level]) - (links-columns [source dest]) + (links-columns [source dest type]) (nodes-db-rows (org-roam-db-query `[:select ,nodes-columns :from nodes])) - (links-db-rows (org-roam-db-query `[:select ,links-columns :from links :where (= type "id")])) + (links-db-rows (org-roam-db-query `[:select ,links-columns :from links :where (or (= type "id") (= type "cite"))])) (response (json-encode `((nodes . ,(mapcar (apply-partially #'org-roam-ui-sql-to-alist (append nodes-columns nil)) nodes-db-rows)) - (links . ,(mapcar (apply-partially #'org-roam-ui-sql-to-alist '(source target)) links-db-rows)))))) + (links . ,(mapcar (apply-partially #'org-roam-ui-sql-to-alist '(source target type)) links-db-rows)))))) (insert response) (httpd-send-header t "application/json" 200 :Access-Control-Allow-Origin "*"))) diff --git a/pages/index.tsx b/pages/index.tsx index 785efbd..f1f9349 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -119,8 +119,16 @@ const initialPhysics = { algorithmOptions: options, algorithmName: 'CubicOut', orphans: false, + follow: 'Local', } +const initialFilter = { + orphans: false, + tags: [], + nodes: [], + links: [], + date: [], +} export default function Home() { // only render on the client const [showPage, setShowPage] = useState(false) @@ -137,6 +145,7 @@ export default function Home() { export function GraphPage() { const [physics, setPhysics] = usePersistantState('physics', initialPhysics) + const [filter, setFilter] = usePersistantState('filter', initialFilter) // const [theme, setTheme] = useState(initialTheme) const [graphData, setGraphData] = useState(null) const [emacsNodeId, setEmacsNodeId] = useState(null) @@ -156,9 +165,10 @@ export function GraphPage() { ...acc, [link.source]: [...(acc[link.source] ?? []), link], [link.target]: [...(acc[link.target] ?? []), link], + [link.type]: [...(acc[link.type] ?? []), link], } }, {}) - + console.log(linksByNodeIdRef.current) // react-force-graph modifies the graph data implicitly, // so we make sure there's no overlap between the objects we pass it and // nodeByIdRef, linksByNodeIdRef @@ -173,6 +183,7 @@ export function GraphPage() { const emacsNodeId = e.data setEmacsNodeId(emacsNodeId) }) + console.log('tttt') updateGraphData() }, []) @@ -198,6 +209,9 @@ export function GraphPage() { physics, setPhysics, threeDim, + setThreeDim, + filter, + setFilter, }} onClose={() => { setShowTweaks(false) @@ -220,6 +234,7 @@ export function GraphPage() { graphData, threeDim, emacsNodeId, + filter, }} /> @@ -339,10 +354,13 @@ export interface TweakProps { physics: typeof initialPhysics setPhysics: any threeDim: boolean + setTreedim: (boolean) => void + filter: typeof initialFilter + setFilter: any onClose: () => void } export const Tweaks = (props: TweakProps) => { - const { physics, setPhysics, threeDim, onClose } = props + const { physics, setPhysics, threeDim, setThreeDim, filter, setFilter, onClose } = props return ( { )} > + + + + Filter + + + + Kill orphans + { + setFilter({ ...filter, orphans: !filter.orphans }) + }} + isChecked={filter.orphans} + > + + + @@ -499,16 +535,6 @@ export const Tweaks = (props: TweakProps) => { divider={} align="stretch" > - {/* - Kill orphans - { - setPhysics({ ...physics, orphans: !physics.orphans }) - }} - isChecked={physics.orphans} - > - */} (null) const graph3dRef = useRef(null) @@ -674,9 +701,13 @@ export const Graph = function (props: GraphProps) { if (!emacsNodeId) { return } - setScope({ - nodeIds: [emacsNodeId], - }) + switch (physics.follow) { + case 'Local': + setScope({ nodeIds: [emacsNodeId] }) + break + case 'Zoom': + default: + } }, [emacsNodeId]) const centralHighlightedNode = hoverNode @@ -698,8 +729,32 @@ export const Graph = function (props: GraphProps) { ) }, [centralHighlightedNode, linksByNodeId]) - const scopedNodes = useMemo(() => { + const filteredNodes = useMemo(() => { return graphData.nodes.filter((node) => { + const links = linksByNodeId[node.id as string] ?? [] + let filterNode = true + if (filter.orphans) { + filterNode = links.length !== 0 + } + return filterNode + }) + }, [filter, graphData.nodes, linksByNodeId]) + + const filteredLinks = useMemo(() => { + return graphData.links.filter((link) => { + // we need to cover both because force-graph modifies the original data + // but if we supply the original data on each render, the graph will re-render sporadically + //const sourceId = typeof link.source === 'object' ? link.source.id! : (link.source as string) + //const targetId = typeof link.target === 'object' ? link.target.id! : (link.target as string) + const linkType = link.type + + return link.type !== 'cite' + }) + }, [filter, JSON.stringify(graphData.links)]) + + const scopedNodes = useMemo(() => { + console.log(filteredNodes) + return filteredNodes.filter((node) => { const links = linksByNodeId[node.id as string] ?? [] /* if (physics.orphans && links.length === 0) { * return false @@ -711,34 +766,32 @@ export const Graph = function (props: GraphProps) { }) ) }) - }, [graphData, linksByNodeId, scope.nodeIds]) + }, [filteredNodes, linksByNodeId, scope.nodeIds]) const scopedNodeIds = scopedNodes.map((node) => node.id as string) - const scopedLinks = useMemo( - () => - graphData.links.filter((link) => { - // we need to cover both because force-graph modifies the original data - // but if we supply the original data on each render, the graph will re-render sporadically - const sourceId = typeof link.source === 'object' ? link.source.id! : (link.source as string) - const targetId = typeof link.target === 'object' ? link.target.id! : (link.target as string) - - return ( - scopedNodeIds.includes(sourceId as string) && scopedNodeIds.includes(targetId as string) - ) - }), - [graphData, scopedNodeIds], - ) + const scopedLinks = useMemo(() => { + return filteredLinks.filter((link) => { + // we need to cover both because force-graph modifies the original data + // but if we supply the original data on each render, the graph will re-render sporadically + const sourceId = typeof link.source === 'object' ? link.source.id! : (link.source as string) + const targetId = typeof link.target === 'object' ? link.target.id! : (link.target as string) + + return ( + scopedNodeIds.includes(sourceId as string) && scopedNodeIds.includes(targetId as string) + ) + }) + }, [filteredLinks, scopedNodes]) const scopedGraphData = useMemo( () => scope.nodeIds.length === 0 - ? graphData + ? { nodes: filteredNodes, links: filteredLinks } : { nodes: scopedNodes, links: scopedLinks, }, - [scope, JSON.stringify(Object.keys(nodeById))], + [filter, scope, JSON.stringify(Object.keys(nodeById))], ) // make sure the camera position and zoom or fine when the list of nodes to render is changed @@ -978,6 +1031,7 @@ export const Graph = function (props: GraphProps) { onNodeClick, onBackgroundClick: () => { + console.log(scope) setScope((currentScope) => ({ ...currentScope, nodeIds: [], -- cgit v1.2.3