Skip to content

Commit

Permalink
Merge pull request #567 from FalkorDB/add-table-view
Browse files Browse the repository at this point in the history
Fix #566 add table view
  • Loading branch information
Anchel123 authored Dec 10, 2024
2 parents e6b08b7 + 99e0dcf commit 5985919
Show file tree
Hide file tree
Showing 14 changed files with 866 additions and 795 deletions.
35 changes: 7 additions & 28 deletions app/api/graph/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,6 @@ export interface Category {
show: boolean,
}


const NODE_RESERVED_KEYS = ["parent", "id", "position"]
const NODE_ALTERNATIVE_RESERVED_KEYS = ["_parent_", "_id_", "_position_"]
// Used to avoid using reserved words in cytoscape `NodeDataDefinition`
function nodeSafeKey(key: string): string {
const index = NODE_RESERVED_KEYS.indexOf(key);
if (index === -1) {
return key;
}
return NODE_ALTERNATIVE_RESERVED_KEYS[index];
}

const EDGE_RESERVED_KEYS = ["source", "target", "id", "position"]
const EDGE_ALTERNATIVE_RESERVED_KEYS = ["_source_", "_target_", "_parent_", "_id_", "_position_"]
// Used to avoid using reserved words in cytoscape `EdgeDataDefinition`
function edgeSafeKey(key: string): string {
const index = EDGE_RESERVED_KEYS.indexOf(key);
if (index === -1) {
return key;
}
return EDGE_ALTERNATIVE_RESERVED_KEYS[index];
}

export interface ExtractedData {
data: any[][],
columns: string[],
Expand Down Expand Up @@ -189,14 +166,16 @@ export class Graph {
if (!currentNode) {
const node: NodeDataDefinition = {
id: cell.id.toString(),
name: cell.id.toString(),
category: categories.map(c => c.name),
color: this.getCategoryColorValue(categories[0].index),
expand: false,
collapsed,
data: {
name: cell.id.toString(),
}
}
Object.entries(cell.properties).forEach(([key, value]) => {
node[nodeSafeKey(key)] = value as string;
node.data[key] = value as string;
});
this.nodesMap.set(cell.id, node)
this.elements.push({ data: node })
Expand All @@ -206,13 +185,13 @@ export class Graph {
if (currentNode.category === "") {
// set values in a fake node
currentNode.id = cell.id.toString();
currentNode.name = cell.id.toString();
currentNode.category = categories.map(c => c.name);
currentNode.color = this.getCategoryColorValue(categories[0].index)
currentNode.expand = false
currentNode.collapsed = collapsed
currentNode.data.name = cell.id.toString();
Object.entries(cell.properties).forEach(([key, value]) => {
currentNode[nodeSafeKey(key)] = value as string;
currentNode.data[key] = value as string;
});

// remove empty category if there are no more empty nodes category
Expand Down Expand Up @@ -244,7 +223,7 @@ export class Graph {
}

Object.entries(cell.properties).forEach(([key, value]) => {
edge[edgeSafeKey(key)] = value as string;
edge.data[key] = value as string;
});

this.edgesMap.set(cell.id, edge)
Expand Down
2 changes: 2 additions & 0 deletions app/components/EditorComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ export default function EditorComponent({ currentQuery, historyQueries, setCurre
{ token: 'keyword', foreground: '#99E4E5' },
{ token: 'function', foreground: '#DCDCAA' },
{ token: 'type', foreground: '#89D86D' },
{ token: 'string', foreground: '#CE9178' },
{ token: 'number', foreground: '#b5cea8' },
],
colors: {
'editor.background': '#1F1F3D',
Expand Down
2 changes: 1 addition & 1 deletion app/components/ui/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function Combobox({ isSelectGraph, disabled = false, inTable, typ
useEffect(() => {
if (options.length !== 1) return
setSelectedValue(options[0])
}, [options, setSelectedValue])
}, [options])

const onExport = async (graphName: string) => {
const result = await securedFetch(`api/graph/${prepareArg(graphName)}/export`, {
Expand Down
12 changes: 11 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@

@layer components {

.tabs-trigger {
@apply !p-3 rounded-lg hover:bg-[#444466] bg-[#57577B] data-[state=active]:!bg-[#444466] data-[state=active]:font-bold;
}

.hide-scrollbar {
scrollbar-width: none;
-ms-overflow-style: none;
}

.hide-scrollbar {
scrollbar-width: none;
-ms-overflow-style: none;
Expand All @@ -54,6 +58,12 @@
font-family: "Obviously Narrow" !important;
}

.Text {
background: linear-gradient(90deg, #EC806C 0%, #B66EBD 43.41%, #7568F2 100%);
background-clip: text;
color: transparent;
}

.Top {
background: linear-gradient(90deg, #EC806C 0%, #B66EBD 43.41%, #7568F2 100%);
}
Expand Down
145 changes: 79 additions & 66 deletions app/graph/GraphDataPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { ElementDataDefinition, prepareArg, securedFetch, Toast } from "@/lib/utils";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { ChevronRight, PlusCircle, Trash2 } from "lucide-react";
import { Check, ChevronRight, MinusCircle, Pencil, PlusCircle, Trash2, X } from "lucide-react";
import { Session } from "next-auth";
import Button from "../components/ui/Button";
import Input from "../components/ui/Input";
Expand All @@ -20,31 +20,19 @@ interface Props {
data: Session | null;
}

const excludedProperties = new Set([
"category",
"color",
"_id",
"id",
"label",
"target",
"source",
"collapsed",
"expand",
]);

export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement, graph, data }: Props) {

const [attributes, setAttributes] = useState<string[]>([]);
const [editable, setEditable] = useState<string>("");
const [mouseEnter, setMouseEnter] = useState<string>("");
const [hover, setHover] = useState<string>("");
const [isAddValue, setIsAddValue] = useState<boolean>(false);
const [newKey, setNewKey] = useState<string>("");
const [newVal, setNewVal] = useState<string>("");
const [label, setLabel] = useState("");
const type = !("source" in obj)

useEffect(() => {
setAttributes(Object.keys(obj).filter((key) => !excludedProperties.has(key) && (key !== "name" || obj.name !== obj.id)));
setAttributes(Object.keys(obj.data).filter((key) => (key !== "name" || obj.data.name !== obj.id)));
setLabel(type ? obj.category : obj.label);
}, [obj, type]);

Expand All @@ -56,11 +44,11 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
})).ok

if (success) {
graph.Elements.forEach(({ data: d }) => {
if (d.id !== id) return
d[key] = val
graph.Elements.forEach(({ data: e }) => {
if (e.id !== id) return
e.data[key] = val
})
setObj((prev) => ({ ...prev, [key]: val }))
setObj((prev) => ({ ...prev, data: { ...prev.data, [key]: val } }))
setNewVal("")
}

Expand All @@ -75,20 +63,38 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
})).ok

if (success) {
graph.Elements.forEach(element => {
if (element.data.id !== id) return
const e = element
graph.Elements.forEach(({ data: e }) => {
if (e.id !== id) return
delete e.data[key]
})

const newObj = { ...obj }
delete newObj[key]
delete newObj.data[key]
setObj(newObj)
}

return success
}

const handelAddValue = async (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Escape") {
setIsAddValue(false)
setNewKey("")
setNewVal("")
}

if (e.key !== "Enter") return

if (!newKey || !newVal) {
Toast("Please fill in both fields")
}

const success = await setProperty(newKey, newVal)
if (!success) return
setIsAddValue(false)
setNewKey("")
}


return (
<div className="DataPanel">
Expand All @@ -112,16 +118,49 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
<div
key={key}
className="flex gap-2 items-center p-4"
onMouseEnter={() => setMouseEnter(key)}
onMouseLeave={() => setMouseEnter("")}
onMouseEnter={() => setHover(key)}
onMouseLeave={() => setHover("")}
>
<div className="w-6">
<div className="w-6 h-12">
{
mouseEnter === key &&
<Button
icon={<Trash2 />}
onClick={() => removeProperty(key)}
/>
editable === key ?
<div className="flex flex-col gap-2">
<Button
variant="button"
icon={<Check size={20} />}
onClick={(e) => {
e.stopPropagation()
setProperty(key, newVal)
setEditable("")
}}
/>
<Button
variant="button"
icon={<X size={20} />}
onClick={(e) => {
e.stopPropagation()
setEditable("")
}}
/>
</div>
: hover === key &&
<div className="flex flex-col gap-2">
<Button
icon={<Trash2 size={20} />}
variant="button"
onClick={() => {
removeProperty(key)
}}
/>
<Button
variant="button"
icon={<Pencil size={20} />}
onClick={() => {
setEditable(key)
setNewVal(obj.data[key])
}}
/>
</div>
}
</div>
<div>
Expand All @@ -135,7 +174,6 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
variant="Small"
value={newVal}
onChange={(e) => setNewVal(e.target.value)}
onBlur={() => setEditable("")}
onKeyDown={(e) => {
if (e.key === "Escape") {
setEditable("")
Expand All @@ -149,10 +187,11 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
/>
: <Button
className="max-w-full"
label={obj[key]?.toString()}
onClick={() => {
label={obj.data[key]?.toString()}
onClick={(e) => {
e.stopPropagation()
setEditable(key)
setNewVal(obj[key])
setNewVal(obj.data[key])
}}
/>
}
Expand All @@ -167,50 +206,24 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
variant="Small"
value={newKey}
onChange={(e) => setNewKey(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Escape") {
setIsAddValue(false)
}

if (e.key !== "Enter") return

if (!newKey || !newVal) {
Toast("Please fill in both fields")
}

setProperty(newKey, newVal)
setIsAddValue(false)
}}
onKeyDown={handelAddValue}
/>
<Input
className="w-1/2"
variant="Small"
value={newVal}
onChange={(e) => setNewVal(e.target.value)}
onBlur={() => setIsAddValue(false)}
onKeyDown={(e) => {
if (e.key === "Escape") {
setIsAddValue(false)
}

if (e.key !== "Enter") return

if (!newKey || !newVal) {
Toast("Please fill in both fields")
}

setProperty(newKey, newVal)
setIsAddValue(false)
}}
onKeyDown={handelAddValue}
/>
</div>
)}
<div key="Is Add" className="p-3">
<div key="Add Value Toggle" className="p-3">
<Button
variant="Secondary"
label="Add Value"
icon={<PlusCircle />}
onClick={() => setIsAddValue(true)}
icon={isAddValue ? <MinusCircle /> : <PlusCircle />}
onClick={() => setIsAddValue(prev => !prev)}
disabled={data?.user.role === "Read-Only"}
/>
</div>
Expand Down
Loading

0 comments on commit 5985919

Please sign in to comment.