From 7ef0354687bb6abeda97a32ebcb0e39de91691e5 Mon Sep 17 00:00:00 2001 From: rohan09-raj Date: Mon, 19 Feb 2024 20:50:51 +0530 Subject: feat: Implement frontend UI for minibin - Use prism.js for syntax highlighting - USe react-router-dom for routing --- client/.env.production | 1 + client/.eslintrc.cjs | 21 --- client/.gitignore | 136 +++++++++++++++++--- client/bun.lockb | Bin 0 -> 126010 bytes client/index.html | 4 +- client/package.json | 8 +- client/public/assets/icons/save.svg | 6 + client/public/vite.svg | 1 - client/src/App.css | 41 ------ client/src/App.jsx | 36 +----- client/src/assets/react.svg | 1 - client/src/components/Editor/Editor.jsx | 129 +++++++++++++++++++ client/src/components/Editor/Editor.module.css | 107 +++++++++++++++ client/src/components/Header/Header.jsx | 13 ++ client/src/components/Header/Header.module.css | 9 ++ .../components/prism-themes/prism-gruvbox-dark.css | 143 +++++++++++++++++++++ client/src/index.css | 53 +------- client/src/main.jsx | 19 +-- client/src/pages/Home/Home.jsx | 18 +++ client/src/pages/Home/Home.module.css | 6 + client/src/utils/constants.js | 25 ++++ client/vite.config.js | 36 +++++- 22 files changed, 638 insertions(+), 175 deletions(-) create mode 100644 client/.env.production delete mode 100644 client/.eslintrc.cjs create mode 100755 client/bun.lockb create mode 100644 client/public/assets/icons/save.svg delete mode 100644 client/public/vite.svg delete mode 100644 client/src/assets/react.svg create mode 100644 client/src/components/Editor/Editor.jsx create mode 100644 client/src/components/Editor/Editor.module.css create mode 100644 client/src/components/Header/Header.jsx create mode 100644 client/src/components/Header/Header.module.css create mode 100644 client/src/components/prism-themes/prism-gruvbox-dark.css create mode 100644 client/src/pages/Home/Home.jsx create mode 100644 client/src/pages/Home/Home.module.css create mode 100644 client/src/utils/constants.js diff --git a/client/.env.production b/client/.env.production new file mode 100644 index 0000000..09e06f4 --- /dev/null +++ b/client/.env.production @@ -0,0 +1 @@ +VITE_SERVER_BASE_URL=https://api.example.com diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs deleted file mode 100644 index 3e212e1..0000000 --- a/client/.eslintrc.cjs +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react/jsx-no-target-blank': 'off', - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/client/.gitignore b/client/.gitignore index a547bf3..c6bba59 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -4,21 +4,127 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* -pnpm-debug.log* lerna-debug.log* +.pnpm-debug.log* -node_modules +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/client/bun.lockb b/client/bun.lockb new file mode 100755 index 0000000..bcdf0dc Binary files /dev/null and b/client/bun.lockb differ diff --git a/client/index.html b/client/index.html index 0c589ec..dbf0011 100644 --- a/client/index.html +++ b/client/index.html @@ -2,9 +2,9 @@ - + - Vite + React + Minibin
diff --git a/client/package.json b/client/package.json index 8badeff..c4514df 100644 --- a/client/package.json +++ b/client/package.json @@ -10,8 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "prismjs": "^1.29.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.1" }, "devDependencies": { "@types/react": "^18.2.55", @@ -21,6 +23,8 @@ "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", - "vite": "^5.1.0" + "prettier": "^3.2.5", + "vite": "^5.1.0", + "vite-plugin-prismjs": "^0.0.11" } } diff --git a/client/public/assets/icons/save.svg b/client/public/assets/icons/save.svg new file mode 100644 index 0000000..3108366 --- /dev/null +++ b/client/public/assets/icons/save.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/client/public/vite.svg b/client/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/client/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/src/App.css b/client/src/App.css index b9d355d..8b13789 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -1,42 +1 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/client/src/App.jsx b/client/src/App.jsx index b8b8473..23b5903 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,35 +1,9 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import "./App.css"; -function App() { - const [count, setCount] = useState(0) +import Home from "./pages/Home/Home"; - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) +function App() { + return ; } -export default App +export default App; diff --git a/client/src/assets/react.svg b/client/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/client/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file 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 ( +
+ {!params.id && ( + <> + + + + )} + +
+
+ {lines.map((_, index) => ( +
+ {index + 1} +
+ ))} +
+
+