diff --git a/src/app/jsx-json/page.tsx b/src/app/jsx-json/page.tsx index 491727d..b455ea2 100644 --- a/src/app/jsx-json/page.tsx +++ b/src/app/jsx-json/page.tsx @@ -5,8 +5,9 @@ "use client" import { useMemo, useRef, useState } from "react"; -import ConversionLayout from "@/components/conversion-layout"; import EditorToolbar from '@/components/EditorToolbar'; +import ConversionInput from "@/components/conversion-input"; +import ConversionLayout from "@/components/conversion-layout"; import JSXson from "@/components/jsxson/JSXToJSON"; import { Separator } from "@/components/ui/separator"; import ShowHide from "@/components/ui/show-hide"; @@ -63,16 +64,21 @@ export default function JSXsonPage() { } } + const inputSection = ( + + ) + return ( JSON`} description="A converter between JSX Props and a JSON object whose keys are the same as the JSX Props." - onFromContentsChange={handleFromContentsChange} collapsed={showSecondEditor} - secondEditor={ - - } + secondEditor={} + inputSection={inputSection} >
diff --git a/src/app/jupyter-python/page.tsx b/src/app/jupyter-python/page.tsx index 36c5dcc..a6569c9 100644 --- a/src/app/jupyter-python/page.tsx +++ b/src/app/jupyter-python/page.tsx @@ -7,6 +7,7 @@ import { useMemo, useRef, useState } from "react"; import EditorToolbar from '@/components/EditorToolbar'; +import ConversionInput from "@/components/conversion-input"; import ConversionLayout from "@/components/conversion-layout"; import JupyterToPython from '@/components/jupyswapp/JupyterToPython'; import PythonToJupyter from '@/components/jupyswapp/PythonToJupyter'; @@ -16,8 +17,6 @@ import { sampleJupyterNotebook } from '@/data/sampleJupyterNotebook'; import { Notebook } from "@/types/jupyter"; import pythonToJupyter, { jupyterToPython } from '@/utils/jupyter'; - - export type JupySwapEditMode = 'jupyter-to-py' | 'py-to-jupyter' | 'none' export default function JupyterPythonPage() { const inputRef = useRef(null) @@ -85,12 +84,18 @@ export default function JupyterPythonPage() { } } + const inputSection = ( + + ) + return ( ) } + inputSection={inputSection} >
diff --git a/src/components/conversion-input.tsx b/src/components/conversion-input.tsx new file mode 100644 index 0000000..7e560a5 --- /dev/null +++ b/src/components/conversion-input.tsx @@ -0,0 +1,111 @@ +import { RefObject, forwardRef, useState } from 'react'; + +import { cn } from '@/utils/cn'; +import { ClipboardEdit, Paperclip, Upload } from 'lucide-react'; +import { buttonVariants } from './ui/button'; +import { Textarea } from './ui/text-area'; + +type ConversionInputProps = { + onFromContentsChange: (e: React.ChangeEvent) => void; + acceptableFileExtensions?: string +} + +const ConversionInput = forwardRef(({ onFromContentsChange, acceptableFileExtensions = '', ...props }, ref) => { + // Idealy we would just want this is as a formatted variable. But due to next.js hydration, we need to use state + const [isDragActive, setIsDragActive] = useState(false) + + const textAreaRef = (ref as RefObject).current + const handleFile = (file: File) => { + console.log() + const reader = new FileReader(); + reader.readAsText(file, "UTF-8"); + reader.onload = function (evt) { + if (evt.target) { + const result = evt.target.result as string + onFromContentsChange({ + target: { + value: result + } + } as React.ChangeEvent) + + if (textAreaRef) { + textAreaRef.value = result + } + } + } + reader.onerror = function (evt) { + console.error(evt.target?.error) + } + setIsDragActive(false) + } + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault() + e.stopPropagation() + if (e && e.dataTransfer && e.dataTransfer.files) { + const file = e.dataTransfer.files?.[0] + if (file) { + handleFile(file) + } + } + } + + + return ( +
+
+ { + textAreaRef?.value === '' && ( + isDragActive ? ( + <> + +
+ Drop it to convert! +
+ + ) : ( + <> + +
+ Drag and drop or Paste in the contents of a{acceptableFileExtensions} file here +
+ { + const file = e.target.files?.[0] + if (file) { + handleFile(file) + } + }} + /> + + + ) + ) + } +
+