drop down for code editor

This commit is contained in:
Виталий Лавшонок
2025-10-27 07:35:36 +03:00
parent e2a9e68666
commit 8fa48ef67e
7 changed files with 121 additions and 84 deletions

View File

@@ -1,125 +0,0 @@
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;

View File

@@ -1,78 +1,87 @@
import React from "react";
import { cn } from "../../lib/cn";
import { eyeClosed, eyeOpen } from "../../assets/icons/input";
import { checkMark, chevroneDropDownList } 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 interface DropDownListItem{
text: string;
value: string;
}
export const DwopDownList: React.FC<DwopDownListProps> = ({
type = "text",
error = "",
// disabled = false,
// required = false,
label = "",
placeholder = "",
className = "",
onChange,
defaultState = "",
name = "",
autocomplete="",
interface DropDownListProps {
disabled?: boolean;
className?: string;
onChange: (state: string) => void;
defaultState?: DropDownListItem;
items: DropDownListItem[];
}
export const DropDownList: React.FC<DropDownListProps> = ({
// disabled = false,
className = "",
onChange,
defaultState,
items = [{text: "", value: ""}],
}) => {
const [value, setValue] = React.useState<string>(defaultState);
const [visible, setVIsible] = React.useState<boolean>(type != "password");
if (items.length == 0)
items.push({text: "", value: ""});
React.useEffect(() => onChange(value), [value]);
const [value, setValue] = React.useState<DropDownListItem>(defaultState != undefined ? defaultState : items[0]);
const [active, setActive] = React.useState<boolean>(false);
React.useEffect(() => onChange(value.value), [value]);
return (
<div className={cn(
"relative",
className
)}>
<div className={cn(" flex items-center h-[40px] rounded-[10px] bg-liquid-lighter px-[16px] w-[180px]",
"text-[18px] font-bold cursor-pointer select-none",
"transitin-all active:scale-95 duration-300"
)}
onClick={() => setActive(!active)}>
{value.text}
</div>
<img src={chevroneDropDownList}
className={cn(" absolute right-[16px] h-[24px] w-[24px] top-[8.5px] rotate-0 transition-all duration-300 pointer-events-none",
active && " rotate-180"
)}/>
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(" absolute rounded-[10px] bg-liquid-lighter w-[180px] left-0 top-[48px] z-50 transition-all duration-300",
"grid overflow-hidden",
active ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0",
)}>
<div className=" overflow-hidden p-[8px]">
<div className={cn(
" overflow-y-scroll max-h-[200px] thin-scrollbar pr-[8px]",
)}>
<div className={cn("text-liquid-red text-[14px] h-[18px] text-right mt-[5px]",
error == "" && "h-0 mt-0"
)}>
{error}
</div>
{items.map((v, i) =>
<div
key={i}
className={cn(
"cursor-pointer h-[36px] relative",
i + 1 != items.length && "border-b-liquid-light border-b-[1px]",
"text-[16px] font-medium cursor-pointer select-none flex items-center pl-[8px]",
)}
onClick={() => {
setValue(v);
setActive(false);
}}>
{v.text}
</div>
);
{v.text == value.text &&
<img src={checkMark} className=" absolute right-[8px]"/>
}
</div>
)}
</div>
</div>
</div>
</div>
);
};