diff options
author | rohan09-raj <[email protected]> | 2024-02-19 20:50:51 +0530 |
---|---|---|
committer | Blaster4385 <[email protected]> | 2024-02-21 23:52:45 +0530 |
commit | 7ef0354687bb6abeda97a32ebcb0e39de91691e5 (patch) | |
tree | 76e0d842e9d5688d1526ddc9c627d8465d1e4a7c /client/src/components/Editor | |
parent | 63f1cbe9c0a52b9b3a8724b7c6f47957414bda8a (diff) |
feat: Implement frontend UI for minibin
- Use prism.js for syntax highlighting
- USe react-router-dom for routing
Diffstat (limited to 'client/src/components/Editor')
-rw-r--r-- | client/src/components/Editor/Editor.jsx | 129 | ||||
-rw-r--r-- | client/src/components/Editor/Editor.module.css | 107 |
2 files changed, 236 insertions, 0 deletions
diff --git a/client/src/components/Editor/Editor.jsx b/client/src/components/Editor/Editor.jsx new file mode 100644 index 0000000..c291872 --- /dev/null +++ b/client/src/components/Editor/Editor.jsx @@ -0,0 +1,129 @@ +import { useState, useEffect, useRef } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import Prism from "prismjs"; +import styles from "./Editor.module.css"; +import "../prism-themes/prism-gruvbox-dark.css"; +import { SERVER_BASE_URL, SUPPORTED_LANGUAGES } from "../../utils/constants"; + +const Editor = () => { + const navigate = useNavigate(); + const params = useParams(); + const [text, setText] = useState(""); + const [language, setLanguage] = useState("js"); + const textareaRef = useRef(null); + const lineNumberRef = useRef(null); + + const handleTextChange = (event) => { + setText(event.target.value); + }; + + const handleScroll = () => { + if (textareaRef.current && lineNumberRef.current) { + lineNumberRef.current.scrollTop = textareaRef.current.scrollTop; + } + }; + + const handleClick = async () => { + const response = await fetch(`${SERVER_BASE_URL}/bin`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + language, + content: text, + }), + }); + const data = await response.json(); + if (response.ok) { + navigate(`/${data.id}`); + } else { + console.error(data); + } + }; + + const handleLanguageChange = (event) => { + setLanguage(event.target.value); + }; + + useEffect(() => { + Prism.highlightAll(); + }, [text, language]); + + useEffect(() => { + if (params.id) fetchData(); + else { + textareaRef.current.value = ""; + setText(""); + } + }, [params.id]); + + const fetchData = async () => { + const response = await fetch(`${SERVER_BASE_URL}/bin/${params.id}`); + const data = await response.json(); + if (response.ok) { + setLanguage(data.language); + setText(data.content); + } + }; + + const lines = text.split("\n"); + + return ( + <div className={styles.container}> + {!params.id && ( + <> + <select + className={styles.languages} + onChange={(event) => handleLanguageChange(event)} + > + {Object.keys(SUPPORTED_LANGUAGES).map((language) => ( + <option + className={styles.languages__option} + key={language} + value={language} + > + {SUPPORTED_LANGUAGES[language]} + </option> + ))} + </select> + <button className={styles.btn__save} onClick={() => handleClick()}> + <img src="assets/icons/save.svg" className={styles.btn__icon} /> + </button> + </> + )} + + <div className={styles.editor}> + <div + className={styles.line__numbers} + ref={lineNumberRef} + onScroll={handleScroll} + > + {lines.map((_, index) => ( + <div key={index + 1} className={styles.line__number}> + {index + 1} + </div> + ))} + </div> + <div className={styles.codespace}> + <textarea + className={styles.codespace__textarea} + onChange={handleTextChange} + onScroll={handleScroll} + hidden={params.id ? true : false} + spellCheck="false" + ref={textareaRef} + placeholder="Type your text here..." + /> + <pre className={styles.codespace__pre}> + <code className={`${styles.codespace__code} language-${language}`}> + {text} + </code> + </pre> + </div> + </div> + </div> + ); +}; + +export default Editor; diff --git a/client/src/components/Editor/Editor.module.css b/client/src/components/Editor/Editor.module.css new file mode 100644 index 0000000..5eabc10 --- /dev/null +++ b/client/src/components/Editor/Editor.module.css @@ -0,0 +1,107 @@ +.container { + width: 100vw; + display: flex; + flex-direction: column; + flex: 1; +} + +.languages { + background-color: transparent; + border: none; + padding: 10px 0; + margin-left: 36px; + font-family: inherit; + font-size: inherit; + cursor: inherit; + line-height: inherit; + width: 7rem; + outline: none; + color: white; +} + +.languages__option { + width: 10rem; + border: none; + cursor: pointer; + transition: 0.3s; + background: #3c3836; +} + +.editor { + display: flex; + flex-direction: row; + flex: 1; + padding: 20px 70px 0 40px; + border-top: 1px solid #3c3836; +} + +.line__numbers { + display: flex; + flex-direction: column; + padding: 0px 20px 0px 0px; + overflow-y: auto; +} + +.line__numbers::-webkit-scrollbar { + display: none; +} + +.line__number { + margin: 0px; + font-size: 16px; + line-height: 1.5rem; + text-align: right; +} + +.codespace { + position: relative; + display: flex; + flex: 1; + margin-left: 10px; +} + +.codespace__textarea { + caret-color: white; + background: transparent; + position: absolute; + color: transparent; + left: 0; + top: 0; + width: 100%; + height: 100%; + font-size: 1rem; + line-height: 1.5rem; + resize: none; + border: none; + outline: none; + padding: 0px; + overflow-y: auto; +} + +.codespace__pre { + background: transparent !important; + height: 100%; + width: 100%; + margin: 0 !important; + padding: 0 !important; +} + +.btn__save { + position: fixed; + bottom: 3rem; + right: 3rem; + height: 8rem; + width: 8rem; + background-color: #ebdbb2; + color: white; + border: none; + border-radius: 50%; + cursor: pointer; + transition: 0.3s; + z-index: 1; +} + +.btn__icon { + height: 4rem; + width: 4rem; +} |