File tree Expand file tree Collapse file tree 9 files changed +315
-26
lines changed Expand file tree Collapse file tree 9 files changed +315
-26
lines changed Original file line number Diff line number Diff line change 1313 "preview" : " vite preview"
1414 },
1515 "dependencies" : {
16+ "@formkit/tempo" : " ^0.1.2" ,
1617 "@icons-pack/react-simple-icons" : " ^10.2.0" ,
1718 "@monaco-editor/react" : " ^4.6.0" ,
1819 "@radix-ui/react-alert-dialog" : " ^1.1.6" ,
2324 "@radix-ui/react-select" : " ^2.1.4" ,
2425 "@radix-ui/react-slot" : " ^1.1.2" ,
2526 "@radix-ui/react-switch" : " ^1.1.3" ,
27+ "@radix-ui/react-tooltip" : " ^1.1.8" ,
2628 "class-variance-authority" : " ^0.7.1" ,
2729 "clsx" : " ^2.1.1" ,
2830 "cmdk" : " ^1.0.0" ,
4143 },
4244 "devDependencies" : {
4345 "@eslint/js" : " ^9.17.0" ,
46+ "@types/file-saver" : " ^2.0.7" ,
4447 "@types/node" : " ^22.13.14" ,
4548 "@types/react" : " ^18.3.18" ,
4649 "@types/react-dom" : " ^18.3.5" ,
4952 "eslint" : " ^9.23.0" ,
5053 "eslint-plugin-react-hooks" : " ^5.2.0" ,
5154 "eslint-plugin-react-refresh" : " ^0.4.19" ,
55+ "file-saver" : " ^2.0.5" ,
5256 "globals" : " ^15.14.0" ,
5357 "peggy" : " ^4.2.0" ,
5458 "postcss" : " ^8.4.49" ,
5862 "vite" : " ^6.2.5" ,
5963 "vitest" : " ^3.0.9"
6064 }
61- }
65+ }
Original file line number Diff line number Diff line change 1+ import { Button , ButtonProps } from "@/components/ui/button" ;
2+ import { saveAs } from "file-saver" ;
3+ import { Download } from "lucide-react" ;
4+ import {
5+ Tooltip ,
6+ TooltipContent ,
7+ TooltipProvider ,
8+ TooltipTrigger ,
9+ } from "@/components/ui/tooltip" ;
10+ import { cn } from "@/lib/utils" ;
11+ import { useJSONPath } from "@/hooks/use-jsonpath" ;
12+ import { format } from "@formkit/tempo" ;
13+
14+ export const DownloadButton = ( { className } : ButtonProps ) => {
15+ const { result } = useJSONPath ( ) ;
16+
17+ const handleDownload = ( ) => {
18+ const text = result . isValid ? JSON . stringify ( result . values , null , 2 ) : "[]" ;
19+ const blob = new Blob ( [ text ] , {
20+ type : "application/json" ,
21+ } ) ;
22+ saveAs (
23+ blob ,
24+ `evaluation_results_${ format ( new Date ( ) , "YYYYMMDD_HHmmss" ) } .json`
25+ ) ;
26+ } ;
27+
28+ return (
29+ < TooltipProvider >
30+ < Tooltip >
31+ < TooltipTrigger asChild >
32+ < Button
33+ variant = "outline"
34+ size = "icon"
35+ className = { cn ( "rounded-full" , className ) }
36+ onClick = { handleDownload }
37+ >
38+ < Download />
39+ </ Button >
40+ </ TooltipTrigger >
41+ < TooltipContent >
42+ < p > Download file</ p >
43+ </ TooltipContent >
44+ </ Tooltip >
45+ </ TooltipProvider >
46+ ) ;
47+ } ;
Original file line number Diff line number Diff line change 1+ import { Upload } from "lucide-react" ;
2+ import { ReactNode , useState } from "react" ;
3+
4+ interface DropZoneProps {
5+ onDrop ?: ( file : File ) => void ;
6+ children : ReactNode ;
7+ }
8+
9+ export const DropZone = ( { onDrop, children } : DropZoneProps ) => {
10+ const [ isDragging , setIsDragging ] = useState ( false ) ;
11+
12+ const handleOnDrop = ( e : React . DragEvent < HTMLDivElement > ) => {
13+ e . preventDefault ( ) ;
14+ setIsDragging ( false ) ;
15+ const file = e . dataTransfer . files [ 0 ] ;
16+ if ( file ) {
17+ onDrop ?.( file ) ;
18+ }
19+ } ;
20+
21+ return (
22+ < div
23+ onDrop = { handleOnDrop }
24+ onDragOver = { ( e ) => {
25+ setIsDragging ( true ) ;
26+ e . preventDefault ( ) ;
27+ } }
28+ onDragEnter = { ( ) => setIsDragging ( true ) }
29+ onDragLeave = { ( ) => setIsDragging ( false ) }
30+ data-drag = { isDragging ? "true" : "false" }
31+ className = "relative"
32+ >
33+ { children }
34+ < div
35+ data-drag = { isDragging ? "true" : "false" }
36+ className = "
37+ invisible data-[drag=true]:visible opacity-0 data-[drag=true]:opacity-80
38+ absolute flex w-full h-full top-0 left-0 p-2 bg-white
39+ transition-opacity duration-200
40+ "
41+ >
42+ < div
43+ className = "
44+ flex flex-col items-center justify-center gap-4
45+ w-full h-full
46+ border-2 border-dashed border-joe-green-600 rounded-lg text-lg
47+ "
48+ >
49+ < div > Drop JSON file here</ div >
50+ < div >
51+ < Upload />
52+ </ div >
53+ </ div >
54+ </ div >
55+ </ div >
56+ ) ;
57+ } ;
Original file line number Diff line number Diff line change 99 findSmallestNode ,
1010 generateNormalizedPathNode ,
1111} from "@/lib/normalized-path" ;
12+ import { DropZone } from "../drop-zone" ;
1213
1314export const JSONEditor = ( ) => {
1415 const { document, setDocument, jsonDocument } = useJSONPath ( ) ;
@@ -63,25 +64,36 @@ export const JSONEditor = () => {
6364 setDocument ( value || "" ) ;
6465 } ;
6566
67+ const handleOnDrop = ( file : File ) => {
68+ const reader = new FileReader ( ) ;
69+ reader . onload = ( event ) => {
70+ const content = event . target ?. result as string ;
71+ setDocument ( content ) ;
72+ } ;
73+ reader . readAsText ( file ) ;
74+ } ;
75+
6676 return (
67- < Editor
68- className = { cn ( "border-2" , jsonDocument . error && "border-red-400" ) }
69- height = "600px"
70- path = "json"
71- defaultLanguage = "json"
72- value = { document }
73- loading = "Loading..."
74- onMount = { handleEditorDidMount }
75- onChange = { handleOnChange }
76- options = { {
77- wordWrap : "on" ,
78- minimap : {
79- enabled : false ,
80- } ,
81- scrollBeyondLastLine : false ,
82- formatOnPaste : true ,
83- formatOnType : true ,
84- } }
85- />
77+ < DropZone onDrop = { handleOnDrop } >
78+ < Editor
79+ className = { cn ( "border-2" , jsonDocument . error && "border-red-400" ) }
80+ height = "600px"
81+ path = "json"
82+ defaultLanguage = "json"
83+ value = { document }
84+ loading = "Loading..."
85+ onMount = { handleEditorDidMount }
86+ onChange = { handleOnChange }
87+ options = { {
88+ wordWrap : "on" ,
89+ minimap : {
90+ enabled : false ,
91+ } ,
92+ scrollBeyondLastLine : false ,
93+ formatOnPaste : true ,
94+ formatOnType : true ,
95+ } }
96+ />
97+ </ DropZone >
8698 ) ;
8799} ;
Original file line number Diff line number Diff line change 1+ import { useRef } from "react" ;
2+ import { Button } from "./ui/button" ;
3+ import { useJSONPath } from "@/hooks/use-jsonpath" ;
4+
5+ export const ImportFile = ( ) => {
6+ const { setDocument } = useJSONPath ( ) ;
7+ const inputRef = useRef < HTMLInputElement > ( null ) ;
8+
9+ const handleOnClick = ( ) => {
10+ inputRef . current ?. click ( ) ;
11+ } ;
12+
13+ const handleOnInput = ( event : React . ChangeEvent < HTMLInputElement > ) => {
14+ const file = event . target . files ?. [ 0 ] ;
15+ if ( file ) {
16+ const reader = new FileReader ( ) ;
17+ reader . onload = ( event ) => {
18+ const content = event . target ?. result as string ;
19+ setDocument ( content ) ;
20+ } ;
21+ reader . readAsText ( file ) ;
22+ }
23+ } ;
24+
25+ return (
26+ < div >
27+ < Button variant = "outline" onClick = { handleOnClick } >
28+ Import File
29+ </ Button >
30+ < input
31+ ref = { inputRef }
32+ className = "hidden"
33+ type = "file"
34+ onInput = { handleOnInput }
35+ accept = "
36+ .json,
37+ .jsonl,
38+ .ndjson,
39+ .geojson,
40+ .topojson,
41+ .jwt,
42+ .webmanifest,
43+ .har,
44+ .mcstructure,
45+ .eslintrc,
46+ .prettierrc,
47+ .babelrc,
48+ .code-snippets,
49+ .ipynb,
50+ .vg,
51+ .vl,
52+ .template,
53+ application/json,
54+ application/geo+json,
55+ application/x-ndjson,
56+ application/jsonlines,
57+ application/schema+json,
58+ application/jwt,
59+ application/feed+json,
60+ application/vnd.oai.openapi+json,
61+ application/vnd.swagger+json,
62+ application/manifest+json,
63+ application/x-ipynb+json,
64+ "
65+ > </ input >
66+ </ div >
67+ ) ;
68+ } ;
You can’t perform that action at this time.
0 commit comments