code editor
This commit is contained in:
59
package-lock.json
generated
59
package-lock.json
generated
@@ -8,11 +8,13 @@
|
|||||||
"name": "react-kit",
|
"name": "react-kit",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@reduxjs/toolkit": "^2.9.2",
|
"@reduxjs/toolkit": "^2.9.2",
|
||||||
"@types/react-redux": "^7.1.33",
|
"@types/react-redux": "^7.1.33",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"framer-motion": "^11.9.0",
|
"framer-motion": "^11.9.0",
|
||||||
|
"monaco-editor": "^0.54.0",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -1022,6 +1024,29 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@monaco-editor/loader": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.6.1.tgz",
|
||||||
|
"integrity": "sha512-w3tEnj9HYEC73wtjdpR089AqkUPskFRcdkxsiSFt3SoUc3OHpmu+leP94CXBm4mHfefmhsdfI0ZQu6qJ0wgtPg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"state-local": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@monaco-editor/react": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@monaco-editor/loader": "^1.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"monaco-editor": ">= 0.25.0 < 1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -2181,6 +2206,12 @@
|
|||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "3.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
|
||||||
|
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)"
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -3315,6 +3346,18 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -3389,6 +3432,16 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.54.0.tgz",
|
||||||
|
"integrity": "sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dompurify": "3.1.7",
|
||||||
|
"marked": "14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -4142,6 +4195,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/state-local": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
|
|||||||
@@ -10,11 +10,13 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@reduxjs/toolkit": "^2.9.2",
|
"@reduxjs/toolkit": "^2.9.2",
|
||||||
"@types/react-redux": "^7.1.33",
|
"@types/react-redux": "^7.1.33",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"framer-motion": "^11.9.0",
|
"framer-motion": "^11.9.0",
|
||||||
|
"monaco-editor": "^0.54.0",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import { Route, Routes } from "react-router-dom";
|
|||||||
// import { Input } from "./components/input/Input";
|
// import { Input } from "./components/input/Input";
|
||||||
// import { Switch } from "./components/switch/Switch";
|
// import { Switch } from "./components/switch/Switch";
|
||||||
import Home from "./pages/Home";
|
import Home from "./pages/Home";
|
||||||
|
import CodeEditor from "./components/codeeditor/CodeEditor";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full h-full ">
|
<div className="flex w-full h-full ">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/home/*" element={<Home/>}/>
|
<Route path="/home/*" element={<Home/>}/>
|
||||||
|
<Route path="/editor" element={<div className="box-border p-[50px] w-full h-[800px] relative bg-red-800"><CodeEditor/></div>}/>
|
||||||
<Route path="*" element={<Home/>}/>
|
<Route path="*" element={<Home/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import eyeClosed from "./eye-closed.svg"
|
import eyeClosed from "./eye-closed.svg"
|
||||||
import eyeOpen from "./eye-open.png"
|
import eyeOpen from "./eye-open.png"
|
||||||
import googleLogo from "./google-logo.svg"
|
import googleLogo from "./google-logo.svg"
|
||||||
|
import upload from "./upload.svg"
|
||||||
|
|
||||||
export {eyeClosed, eyeOpen, googleLogo}
|
|
||||||
|
export {eyeClosed, eyeOpen, googleLogo, upload}
|
||||||
3
src/assets/icons/input/upload.svg
Normal file
3
src/assets/icons/input/upload.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4 15.2044V18.8925C4 19.4514 4.21071 19.9875 4.58579 20.3827C4.96086 20.778 5.46957 21 6 21H18C18.5304 21 19.0391 20.778 19.4142 20.3827C19.7893 19.9875 20 19.4514 20 18.8925V15.2044M12.0007 14.9425L12.0007 3M12.0007 3L7.42931 7.56318M12.0007 3L16.5722 7.56318" stroke="#EDF6F7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 457 B |
125
src/components/codeeditor/CodeEditor.tsx
Normal file
125
src/components/codeeditor/CodeEditor.tsx
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import Editor from "@monaco-editor/react";
|
||||||
|
import { upload } from "../../assets/icons/input";
|
||||||
|
import { cn } from "../../lib/cn";
|
||||||
|
|
||||||
|
const languageMap: Record<string, string> = {
|
||||||
|
c: "cpp",
|
||||||
|
cpp: "cpp",
|
||||||
|
java: "java",
|
||||||
|
python: "python",
|
||||||
|
pascal: "pascal",
|
||||||
|
};
|
||||||
|
|
||||||
|
const CodeEditor: React.FC = () => {
|
||||||
|
const [language, setLanguage] = useState<string>("cpp");
|
||||||
|
const [code, setCode] = useState<string>("");
|
||||||
|
const [isDragging, setIsDragging] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
const text = event.target?.result;
|
||||||
|
if (typeof text === "string") setCode(text);
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
e.target.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (e: React.DragEvent<HTMLLabelElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragging(false);
|
||||||
|
const droppedFile = e.dataTransfer.files[0];
|
||||||
|
if (!droppedFile) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
const text = event.target?.result;
|
||||||
|
if (typeof text === "string") setCode(text);
|
||||||
|
};
|
||||||
|
reader.readAsText(droppedFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent<HTMLLabelElement>) => {
|
||||||
|
e.preventDefault(); // обязательно
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragEnter = (e: React.DragEvent<HTMLLabelElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragging(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragLeave = (e: React.DragEvent<HTMLLabelElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragging(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-full h-full">
|
||||||
|
{/* Панель выбора языка и загрузки файла */}
|
||||||
|
<div className="flex items-center justify-between p-3 ">
|
||||||
|
<div className="flex items-center gap-[20px]">
|
||||||
|
<select
|
||||||
|
value={language}
|
||||||
|
onChange={(e) => setLanguage(e.target.value)}
|
||||||
|
className=" rounded-md px-3 py-1"
|
||||||
|
>
|
||||||
|
<option value="c">C</option>
|
||||||
|
<option value="cpp">C++</option>
|
||||||
|
<option value="java">Java</option>
|
||||||
|
<option value="python">Python</option>
|
||||||
|
<option value="pascal">Pascal</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label
|
||||||
|
className={cn("h-[40px] w-[250px] rounded-[10px] px-[16px] relative flex items-center cursor-pointer transition-all bg-liquid-lighter outline-dashed outline-[2px] outline-transparent active:scale-[95%]",
|
||||||
|
isDragging && "outline-blue-500 "
|
||||||
|
)}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
>
|
||||||
|
<span className="text-[18px] text-liquid-white font-bold pointer-events-none">
|
||||||
|
{"Загрузить решение"}
|
||||||
|
</span>
|
||||||
|
<img src={upload} className="absolute right-[16px] pointer-events-none" />
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
onChange={(e) => handleFileUpload(e)}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Monaco Editor */}
|
||||||
|
<div className="bg-[#1E1E1E] py-[10px] h-full rounded-[10px]">
|
||||||
|
<Editor
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
language={languageMap[language]}
|
||||||
|
value={code}
|
||||||
|
onChange={(value) => setCode(value ?? "")}
|
||||||
|
theme="vs-dark"
|
||||||
|
options={{
|
||||||
|
fontSize: 14,
|
||||||
|
minimap: { enabled: false },
|
||||||
|
automaticLayout: true,
|
||||||
|
quickSuggestions: true,
|
||||||
|
suggestOnTriggerCharacters: true,
|
||||||
|
tabSize: 4,
|
||||||
|
insertSpaces: true,
|
||||||
|
detectIndentation: false,
|
||||||
|
autoIndent: "full",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CodeEditor;
|
||||||
78
src/components/drop-down-list/DropDownList.tsx
Normal file
78
src/components/drop-down-list/DropDownList.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { cn } from "../../lib/cn";
|
||||||
|
import { eyeClosed, eyeOpen } from "../../assets/icons/input";
|
||||||
|
|
||||||
|
interface DwopDownListProps {
|
||||||
|
name?: string;
|
||||||
|
type: "text" | "email" | "password" | "first_name";
|
||||||
|
error?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
className?: string;
|
||||||
|
onChange: (state: string) => void;
|
||||||
|
defaultState?: string;
|
||||||
|
autocomplete?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DwopDownList: React.FC<DwopDownListProps> = ({
|
||||||
|
type = "text",
|
||||||
|
error = "",
|
||||||
|
// disabled = false,
|
||||||
|
// required = false,
|
||||||
|
label = "",
|
||||||
|
placeholder = "",
|
||||||
|
className = "",
|
||||||
|
onChange,
|
||||||
|
defaultState = "",
|
||||||
|
name = "",
|
||||||
|
autocomplete="",
|
||||||
|
}) => {
|
||||||
|
const [value, setValue] = React.useState<string>(defaultState);
|
||||||
|
const [visible, setVIsible] = React.useState<boolean>(type != "password");
|
||||||
|
|
||||||
|
React.useEffect(() => onChange(value), [value]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn(
|
||||||
|
"relative",
|
||||||
|
className
|
||||||
|
)}>
|
||||||
|
<div className={cn("text-[18px] text-liquid-white font-medium h-[23px] mb-[10px] transition-all",
|
||||||
|
label == "" && "h-0 mb-0"
|
||||||
|
)}>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
className={cn(
|
||||||
|
"bg-liquid-lighter w-full rounded-[10px] outline-none pl-[16px] py-[8px] placeholder:text-liquid-light",
|
||||||
|
type == "password" ? "h-[40px]" : "h-[36px]"
|
||||||
|
)}
|
||||||
|
name={name}
|
||||||
|
autoComplete={autocomplete}
|
||||||
|
type={type == "password" ? (visible ? "text" : "password") : type}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValue(e.target.value);
|
||||||
|
}} />
|
||||||
|
{
|
||||||
|
type == "password" &&
|
||||||
|
<img src={visible ? eyeOpen : eyeClosed} className="w-[24px] h-[24px] cursor-pointer right-[16px] top-[8px] absolute" onClick={() => {
|
||||||
|
setVIsible(!visible);
|
||||||
|
}}/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cn("text-liquid-red text-[14px] h-[18px] text-right mt-[5px]",
|
||||||
|
error == "" && "h-0 mt-0"
|
||||||
|
)}>
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
@@ -19,8 +19,8 @@ interface inputProps {
|
|||||||
export const Input: React.FC<inputProps> = ({
|
export const Input: React.FC<inputProps> = ({
|
||||||
type = "text",
|
type = "text",
|
||||||
error = "",
|
error = "",
|
||||||
disabled = false,
|
// disabled = false,
|
||||||
required = false,
|
// required = false,
|
||||||
label = "",
|
label = "",
|
||||||
placeholder = "",
|
placeholder = "",
|
||||||
className = "",
|
className = "",
|
||||||
|
|||||||
Reference in New Issue
Block a user