summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api.d.ts1
-rw-r--r--components/config.ts1
-rw-r--r--components/tweaks.tsx75
-rw-r--r--org-roam-ui.el24
-rw-r--r--pages/index.tsx4
5 files changed, 100 insertions, 5 deletions
diff --git a/api.d.ts b/api.d.ts
index 59b4c82..72f1c47 100644
--- a/api.d.ts
+++ b/api.d.ts
@@ -1,6 +1,7 @@
export type OrgRoamGraphReponse = {
nodes: OrgRoamNode[]
links: OrgRoamLink[]
+ tags: string[]
}
export type OrgRoamNode = {
diff --git a/components/config.ts b/components/config.ts
index 1181f65..e9471f8 100644
--- a/components/config.ts
+++ b/components/config.ts
@@ -35,6 +35,7 @@ export const initialFilter = {
orphans: false,
parents: true,
tags: [],
+ tagColors: [],
nodes: [],
links: [],
date: [],
diff --git a/components/tweaks.tsx b/components/tweaks.tsx
index 3c9ee00..e46baf3 100644
--- a/components/tweaks.tsx
+++ b/components/tweaks.tsx
@@ -6,6 +6,8 @@ import {
InfoOutlineIcon,
RepeatIcon,
ArrowRightIcon,
+ AddIcon,
+ DeleteIcon,
} from '@chakra-ui/icons'
import {
Accordion,
@@ -38,6 +40,7 @@ import {
Grid,
Portal,
SlideFade,
+ Input,
} from '@chakra-ui/react'
import React, { useState, useContext } from 'react'
import Scrollbars from 'react-custom-scrollbars-2'
@@ -64,6 +67,7 @@ export interface TweakProps {
setMouse: any
behavior: typeof initialBehavior
setBehavior: any
+ tags: string[]
}
export const Tweaks = (props: TweakProps) => {
@@ -80,6 +84,7 @@ export const Tweaks = (props: TweakProps) => {
setMouse,
behavior,
setBehavior,
+ tags,
} = props
const [showTweaks, setShowTweaks] = useState(true)
const { highlightColor, setHighlightColor } = useContext(ThemeContext)
@@ -107,6 +112,7 @@ export const Tweaks = (props: TweakProps) => {
'gray.900',
'black',
]
+
return (
<>
<SlideFade in={!showTweaks}>
@@ -223,6 +229,24 @@ export const Tweaks = (props: TweakProps) => {
></Switch>
</Flex>
</VStack>
+ <Accordion allowToggle allowMultiple paddingLeft={3}>
+ <AccordionItem>
+ <AccordionButton>
+ Tag filters
+ <AccordionIcon />
+ </AccordionButton>
+ <AccordionPanel>
+ <TagPanel filter={filter} setFilter={setFilter} tags={tags} />
+ </AccordionPanel>
+ </AccordionItem>
+ <AccordionItem>
+ <AccordionButton>
+ Tag colors
+ <AccordionIcon />
+ </AccordionButton>
+ <AccordionPanel></AccordionPanel>
+ </AccordionItem>
+ </Accordion>
</AccordionPanel>
</AccordionItem>
<AccordionItem>
@@ -1242,3 +1266,54 @@ export const ColorMenu = (props: ColorMenuProps) => {
</Flex>
)
}
+
+export interface TagPanelProps {
+ tags: string[]
+ filter: typeof initialFilter
+ setFilter: any
+}
+
+export const TagPanel = (props: TagPanelProps) => {
+ const { filter, setFilter, tags } = props
+ const [tagFilterText, setTagFilterText] = useState('')
+
+ return (
+ <VStack>
+ <Flex alignItems="center" justifyContent="space-between">
+ <Input
+ placeHolder="New tag..."
+ value={tagFilterText}
+ onChange={(v) => setTagFilterText(v.target.value)}
+ />
+ <IconButton
+ onClick={() => {
+ if (!tags.includes(tagFilterText)) {
+ return
+ }
+ setFilter({ ...filter, tags: [...filter.tags, tagFilterText] })
+ setTagFilterText('')
+ }}
+ aria-label="add tag"
+ icon={<AddIcon />}
+ />
+ </Flex>
+ {filter.tags.map((tag) => {
+ return (
+ <Flex aignItems="center" justifyContent="space-between">
+ {tag}
+ <IconButton
+ aria-label={'delete tag ' + tag}
+ icon={<DeleteIcon />}
+ onClick={() =>
+ setFilter({
+ ...filter,
+ tags: filter.tags.filter((t) => t !== tag),
+ })
+ }
+ />
+ </Flex>
+ )
+ })}
+ </VStack>
+ )
+}
diff --git a/org-roam-ui.el b/org-roam-ui.el
index 098e99e..5ac7eac 100644
--- a/org-roam-ui.el
+++ b/org-roam-ui.el
@@ -159,9 +159,14 @@ This serves the web-build and API over HTTP."
(defun org-roam-ui--send-graphdata ()
"Get roam data, make JSON, send through websocket to org-roam-ui."
- (let* ((nodes-columns [id file title level properties])
+ (let* ((nodes-columns [id file title level properties ,(funcall group-concat tag (emacsql-escape-raw \, ))])
+ (nodes-names [id file title level properties tags])
(links-columns [links:source links:dest links:type refs:node-id])
- (nodes-db-rows (org-roam-db-query `[:select ,nodes-columns :from nodes]))
+ (nodes-db-rows (org-roam-db-query `[:select ,nodes-columns :as tags
+ :from nodes
+ :left-join tags
+ :on (= id node_id)
+ :group :by id]))
;; Left outer join on refs means any id link (or cite link without a
;; corresponding node) will have 'nil for the `refs:node-id' value. Any
;; cite link where a node has that `:ROAM_REFS:' will have a value.
@@ -177,8 +182,9 @@ This serves the web-build and API over HTTP."
(if node-id
(list source node-id "cite")
(list source dest type)))) links-db-rows))
- (response `((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 type)) links-db-rows)))))
+ (response `((nodes . ,(mapcar (apply-partially #'org-roam-ui-sql-to-alist (append nodes-names nil)) nodes-db-rows))
+ (links . ,(mapcar (apply-partially #'org-roam-ui-sql-to-alist '(source target type)) links-db-rows))
+ (tags . ,(seq-mapcat #'seq-reverse (org-roam-db-query [:select :distinct tag :from tags]))))))
(websocket-send-text oru-ws (json-encode `((type . "graphdata") (data . ,response))))))
(defun org-roam-ui--update-current-node ()
@@ -227,7 +233,15 @@ This serves the web-build and API over HTTP."
ROWS is the sql result, while COLUMN-NAMES is the columns to use."
(let (res)
(while rows
- (push (cons (pop column-names) (pop rows)) res))
+ ;; emacsql does not want to give us the tags as a list, so we post process it
+ (if (not (string= (car column-names) "tags"))
+ (push (cons (pop column-names) (pop rows)) res)
+ (push (cons (pop column-names)
+ (seq-remove
+ (lambda (elt) (string= elt ","))
+ rows))
+ res)
+ (setq rows nil)))
res))
diff --git a/pages/index.tsx b/pages/index.tsx
index 4a178be..ee826ce 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -51,6 +51,7 @@ const ForceGraph3D = (
export type NodeById = { [nodeId: string]: OrgRoamNode | undefined }
export type LinksByNodeId = { [nodeId: string]: OrgRoamLink[] | undefined }
export type NodesByFile = { [file: string]: OrgRoamNode[] | undefined }
+export type Tags = string[]
export type Scope = {
nodeIds: string[]
}
@@ -79,8 +80,10 @@ export function GraphPage() {
const nodeByIdRef = useRef<NodeById>({})
const linksByNodeIdRef = useRef<LinksByNodeId>({})
+ const tagsRef = useRef<Tags>([])
const updateGraphData = (orgRoamGraphData: OrgRoamGraphReponse) => {
+ tagsRef.current = orgRoamGraphData.tags
const nodesByFile = orgRoamGraphData.nodes.reduce<NodesByFile>((acc, node) => {
return {
...acc,
@@ -270,6 +273,7 @@ export function GraphPage() {
behavior,
setBehavior,
}}
+ tags={tagsRef.current}
/>
<Box position="absolute" alignItems="top">
<Graph