From 43c78236f343f231f757b5442c760fa69995dc10 Mon Sep 17 00:00:00 2001 From: Anchel123 <110421452+Anchel123@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:21:40 +0300 Subject: [PATCH 1/5] add autocompletion and highlight for the query input --- app/graph/page.tsx | 8 +-- app/graph/query.tsx | 117 ++++++++++++++++++++++++++++++++++++++++++-- package-lock.json | 36 ++++++++++++++ package.json | 2 + 4 files changed, 154 insertions(+), 9 deletions(-) diff --git a/app/graph/page.tsx b/app/graph/page.tsx index 2b179ed..70b6087 100644 --- a/app/graph/page.tsx +++ b/app/graph/page.tsx @@ -47,11 +47,11 @@ export default function Page() { event.preventDefault(); const state = queryState.current; if (!state) { - return + return false } // Proposed abstraction for improved modularity - if (!validateGraphSelection(state.graphName)) return; + if (!validateGraphSelection(state.graphName)) return false; const q = state.query.trim() || "MATCH (n)-[e]-() RETURN n,e limit 100"; @@ -69,7 +69,7 @@ export default function Page() { if (result.status >= 400 && result.status < 500) { signOut({ callbackUrl: '/login' }) } - return + return false } const json = await result.json() @@ -77,8 +77,8 @@ export default function Page() { setGraph(newGraph) setMetaData(json.result.metadata) setShowGraph((!!json.result.data && json.result.data.length > 0)) - graphView.current?.expand(newGraph.Elements) + return true } return ( diff --git a/app/graph/query.tsx b/app/graph/query.tsx index 875cb39..54e64f6 100644 --- a/app/graph/query.tsx +++ b/app/graph/query.tsx @@ -2,13 +2,16 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { cn } from "@/lib/utils"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; import { Menu, Search, Trash2 } from "lucide-react"; import { useToast } from "@/components/ui/use-toast"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import Editor, { Monaco } from "@monaco-editor/react"; +import { languages, editor } from "monaco-editor"; import GraphsList from "./GraphList"; +import { before } from "lodash"; export class QueryState { @@ -18,16 +21,105 @@ export class QueryState { ) { } } +const cypherKeywords = [ + "CALL", + "CREATE", + "DELETE", + "DETACH", + "FOREACH", + "LOAD", + "MATCH", + "MERGE", + "OPTIONAL", + "REMOVE", + "RETURN", + "SET", + "START", + "UNION", + "UNWIND", + "WITH", + "LIMIT", + "ORDER", + "SKIP", + "WHERE", + "YIELD", + "ASC", + "ASCENDING", + "ASSERT", + "BY", + "CSV", + "DESC", + "DESCENDING", + "ON", + "ALL", + "CASE", + "COUNT", + "ELSE", + "END", + "EXISTS", + "THEN", + "WHEN", + "AND", + "AS", + "CONTAIN", + "DISTINCT", + "ENDS", + "IN", + "IS", + "NOT", + "OR", + "STARTS", + "XOR", + "CONSTRAINT", + "DROP", + "INDEX", + "NODE", + "UNIQUE", + "JOIN", + "SCAN", + "ADD", + "DO", + "FOR", + "MANDATORY", + "OF", + "REQUIRE", + "SCALAR", + "false", + "null", + "true" +] + export function Query({ onSubmit, onQueryUpdate, className = "" }: { - onSubmit: (event: React.FormEvent) => void, + onSubmit: (event: React.FormEvent) => Promise, onQueryUpdate: (state: QueryState) => void, className: string }) { const [query, setQuery] = useState(''); const [graphName, setGraphName] = useState(''); const [onDelete, setOnDelete] = useState(false); + const Monaco = useRef(); const { toast } = useToast(); + useEffect(() => { + const monaco = Monaco.current + if (monaco) { + monaco.languages.register({ id: "cypher" }) + monaco.languages.registerCompletionItemProvider("cypher", { + provideCompletionItems: () => { + return { + suggestions: cypherKeywords.map(keyword => { + return { + label: keyword, + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: keyword, + } + }) + } as languages.ProviderResult + } + }) + } + }, [Monaco.current]) + onQueryUpdate(new QueryState(query, graphName)) const handleDelete = () => { @@ -51,14 +143,29 @@ export function Query({ onSubmit, onQueryUpdate, className = "" }: { return (
+ onSubmit={ async (e) => { + await onSubmit(e) && setQuery('') + }}>
- setQuery(event.target.value)} /> + val && setQuery(val)} + theme="vs-dark" + language="cypher" + options={{ + suggest: { + showKeywords: true, + }, + minimap: { enabled: false }, + wordWrap: "on", + lineNumbers: "off", + }} + beforeMount={(monaco) => Monaco.current = monaco} + /> diff --git a/package-lock.json b/package-lock.json index 7b388f2..1341d40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "-": "^0.0.1", "@hookform/resolvers": "^3.3.4", + "@monaco-editor/react": "^4.6.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5", @@ -36,6 +37,7 @@ "falkordb": "beta", "lodash": "^4.17.21", "lucide-react": "^0.301.0", + "monaco-editor": "^0.47.0", "next": "14.1.0", "next-auth": "^4.24.5", "next-themes": "^0.2.1", @@ -2232,6 +2234,30 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz", + "integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==", + "dependencies": { + "@monaco-editor/loader": "^1.4.0" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@next/env": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", @@ -7564,6 +7590,11 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/monaco-editor": { + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.47.0.tgz", + "integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8887,6 +8918,11 @@ "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", "dev": true }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", diff --git a/package.json b/package.json index 59f306e..92ea6f9 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "-": "^0.0.1", "@hookform/resolvers": "^3.3.4", + "@monaco-editor/react": "^4.6.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5", @@ -40,6 +41,7 @@ "falkordb": "beta", "lodash": "^4.17.21", "lucide-react": "^0.301.0", + "monaco-editor": "^0.47.0", "next": "14.1.0", "next-auth": "^4.24.5", "next-themes": "^0.2.1", From 85e0ede7de3534fafbef94d1fd4bb8269bbba62f Mon Sep 17 00:00:00 2001 From: Anchel123 <110421452+Anchel123@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:25:42 +0300 Subject: [PATCH 2/5] add highlighting --- app/graph/query.tsx | 99 +++------------------------------------------ 1 file changed, 5 insertions(+), 94 deletions(-) diff --git a/app/graph/query.tsx b/app/graph/query.tsx index 54e64f6..e51fb76 100644 --- a/app/graph/query.tsx +++ b/app/graph/query.tsx @@ -8,10 +8,9 @@ import { Menu, Search, Trash2 } from "lucide-react"; import { useToast } from "@/components/ui/use-toast"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import Editor, { Monaco } from "@monaco-editor/react"; -import { languages, editor } from "monaco-editor"; +import Editor from "@monaco-editor/react"; import GraphsList from "./GraphList"; -import { before } from "lodash"; +import { find } from "lodash"; export class QueryState { @@ -21,74 +20,6 @@ export class QueryState { ) { } } -const cypherKeywords = [ - "CALL", - "CREATE", - "DELETE", - "DETACH", - "FOREACH", - "LOAD", - "MATCH", - "MERGE", - "OPTIONAL", - "REMOVE", - "RETURN", - "SET", - "START", - "UNION", - "UNWIND", - "WITH", - "LIMIT", - "ORDER", - "SKIP", - "WHERE", - "YIELD", - "ASC", - "ASCENDING", - "ASSERT", - "BY", - "CSV", - "DESC", - "DESCENDING", - "ON", - "ALL", - "CASE", - "COUNT", - "ELSE", - "END", - "EXISTS", - "THEN", - "WHEN", - "AND", - "AS", - "CONTAIN", - "DISTINCT", - "ENDS", - "IN", - "IS", - "NOT", - "OR", - "STARTS", - "XOR", - "CONSTRAINT", - "DROP", - "INDEX", - "NODE", - "UNIQUE", - "JOIN", - "SCAN", - "ADD", - "DO", - "FOR", - "MANDATORY", - "OF", - "REQUIRE", - "SCALAR", - "false", - "null", - "true" -] - export function Query({ onSubmit, onQueryUpdate, className = "" }: { onSubmit: (event: React.FormEvent) => Promise, onQueryUpdate: (state: QueryState) => void, @@ -97,29 +28,8 @@ export function Query({ onSubmit, onQueryUpdate, className = "" }: { const [query, setQuery] = useState(''); const [graphName, setGraphName] = useState(''); const [onDelete, setOnDelete] = useState(false); - const Monaco = useRef(); const { toast } = useToast(); - useEffect(() => { - const monaco = Monaco.current - if (monaco) { - monaco.languages.register({ id: "cypher" }) - monaco.languages.registerCompletionItemProvider("cypher", { - provideCompletionItems: () => { - return { - suggestions: cypherKeywords.map(keyword => { - return { - label: keyword, - kind: monaco.languages.CompletionItemKind.Keyword, - insertText: keyword, - } - }) - } as languages.ProviderResult - } - }) - } - }, [Monaco.current]) - onQueryUpdate(new QueryState(query, graphName)) const handleDelete = () => { @@ -143,7 +53,7 @@ export function Query({ onSubmit, onQueryUpdate, className = "" }: { return ( { + onSubmit={async (e) => { await onSubmit(e) && setQuery('') }}>
@@ -163,8 +73,9 @@ export function Query({ onSubmit, onQueryUpdate, className = "" }: { minimap: { enabled: false }, wordWrap: "on", lineNumbers: "off", + lineHeight: 40, + fontSize: 30, }} - beforeMount={(monaco) => Monaco.current = monaco} /> From dd5f943913ac45ba8d9aa2680021d63972be94be Mon Sep 17 00:00:00 2001 From: Anchel123 <110421452+Anchel123@users.noreply.github.com> Date: Sun, 7 Apr 2024 13:24:33 +0300 Subject: [PATCH 3/5] remove unnecessary imports --- app/graph/query.tsx | 5 ++--- package-lock.json | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/graph/query.tsx b/app/graph/query.tsx index 1abf277..0719ad5 100644 --- a/app/graph/query.tsx +++ b/app/graph/query.tsx @@ -1,8 +1,7 @@ import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { cn } from "@/lib/utils"; -import { useEffect, useRef, useState } from "react"; +import { useState } from "react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; import { Menu, Search, Trash2 } from "lucide-react"; import { useToast } from "@/components/ui/use-toast"; @@ -63,7 +62,7 @@ export function Query({ onSubmit, onQueryUpdate, onDeleteGraph, className = "" }
val && setQuery(val)} + onChange={(val) => val && setQuery(val)} theme="vs-dark" language="cypher" options={{ diff --git a/package-lock.json b/package-lock.json index 9a72eb9..ab935dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "falkordb-browser", - "version": "0.2.1", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "falkordb-browser", - "version": "0.2.1", + "version": "0.3.0", "dependencies": { "-": "^0.0.1", "@hookform/resolvers": "^3.3.4", From 214353ed5d2bc9a5eb747561d2a1a3813428a808 Mon Sep 17 00:00:00 2001 From: Anchel123 <110421452+Anchel123@users.noreply.github.com> Date: Sun, 7 Apr 2024 13:32:27 +0300 Subject: [PATCH 4/5] fix for lint --- app/graph/query.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/graph/query.tsx b/app/graph/query.tsx index 0719ad5..51c716a 100644 --- a/app/graph/query.tsx +++ b/app/graph/query.tsx @@ -52,9 +52,7 @@ export function Query({ onSubmit, onQueryUpdate, onDeleteGraph, className = "" } return ( { - await onSubmit(e) && setQuery('') - }}> + onSubmit={async (e) => await onSubmit(e) && setQuery('')}>
From 8720ef98d293259a9dd664c2eb9afa8ad9b729df Mon Sep 17 00:00:00 2001 From: Anchel123 <110421452+Anchel123@users.noreply.github.com> Date: Sun, 7 Apr 2024 13:35:30 +0300 Subject: [PATCH 5/5] removing the reset of the query after execute --- app/graph/query.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/graph/query.tsx b/app/graph/query.tsx index 51c716a..1b4302a 100644 --- a/app/graph/query.tsx +++ b/app/graph/query.tsx @@ -52,7 +52,7 @@ export function Query({ onSubmit, onQueryUpdate, onDeleteGraph, className = "" } return ( await onSubmit(e) && setQuery('')}> + onSubmit={(e) => onSubmit(e)}>