Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added code editor feature successfully issue 171 #189

Merged
merged 3 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,039 changes: 3,708 additions & 331 deletions frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@monaco-editor/react": "^4.6.0",
"axios": "^1.7.2",
"clsx": "^2.1.1",
"codemirror": "^6.0.1",
"dompurify": "^3.1.4",
"framer-motion": "^11.2.10",
"i18next": "^23.11.5",
Expand Down Expand Up @@ -42,7 +44,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.3",
"tailwindcss": "^3.4.4",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import CustomizeWithAi from "./pages/CustomizeWithAi";
import ScrollToTopWhenRouteChanges from "./components/ScrollToTopWhenRouteChanges";
import './i18n';
import LanguageDropdown from "./components/LanguageDropdown";
import CodeEditor from "./components/CodeEditor";
// import axios from "axios";
// axios.defaults.baseURL = "http://localhost:3001/";

Expand Down Expand Up @@ -98,6 +99,14 @@ function App() {
<LeaderBoard />
}
/>
<Route
path="/app/code"
element={
<AuthenticatedRoute>
<CodeEditor />
</AuthenticatedRoute>
}
/>
<Route
path="/app/contact-us"
element={
Expand Down
131 changes: 131 additions & 0 deletions frontend/src/components/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { useEffect, useRef, useState } from 'react';
import MonacoEditor from '@monaco-editor/react';
import { CiLight, CiDark } from 'react-icons/ci';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from "react-i18next";

const CodeEditor = () => {
const initialCode = `<div class="relative flex min-h-screen flex-col justify-center overflow-hidden bg-blue-400 py-6 sm:py-12">
<div class="relative px-6 pt-10 pb-8 shadow-2xl ring-1 ring-gray-900/5 sm:mx-auto sm:max-w-lg sm:rounded-lg sm:px-10">
<div class="mx-auto max-w-md text-white">
<h1 class="text-3xl font-semibold">💅 Style Share</h1>
<div class="divide-y divide-gray-300/50">
<div class="space-y-6 py-8 text-base leading-7">
<p>An advanced online playground for Tailwind CSS, including support for things like:</p>
<ul class="space-y-4">
<li class="flex items-center">
<p class="ml-4">
🤜 Customizing your
<code class="text-sm font-bold">tailwind.config.js</code> file
</p>
</li>
<li class="flex items-center">
<p class="ml-4">
🤜 Extracting classes with
<code class="text-sm font-bold">@apply</code>
</p>
</li>
<li class="flex items-center">
<p class="ml-4">🤜 Code completion with instant preview</p>
</li>
</ul>
<p>Perfect for learning how the framework works, prototyping a new idea, or creating a demo to share online.</p>
</div>
<div class="pt-8 text-base font-semibold leading-7">
<p class="mb-3">Want to dig deeper into Tailwind?</p>
<p>
<a href="https://tailwindcss.com/docs" class="text-blue-500 p-2 bg-white rounded-lg hover:text-blue-600">Read the docs &rarr;</a>
</p>
</div>
</div>
</div>
</div>
</div>`;
const [code, setCode] = useState(initialCode);
const [theme, setTheme] = useState<'light' | 'dark'>(
() => (localStorage.getItem('theme') as 'light' | 'dark') || 'dark'
);
const outputRef = useRef<HTMLIFrameElement>(null);
const navigate = useNavigate();
const { t } = useTranslation();

const updateOutput = (code: string) => {
const doc = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>${code}</body>
</html>
`;
if (outputRef.current) {
outputRef.current.srcdoc = doc;
}
};

useEffect(() => {
updateOutput(code);
}, [code]);

useEffect(() => {
localStorage.setItem('theme', theme);
}, [theme]);

const handlePublish = () => {
navigate('/app/new-post', { state: { codeSnippet: code } });
};

return (
<div className='flex flex-col h-screen -mt-28 sm:-mt-8'>
<nav className={`bg-${theme === 'dark' ? 'gray-900' : 'white'} text-white p-16 sm:p-5 mt-20 sm:mt-1`}>
<div className="flex justify-between items-center">
<div className="flex-1"></div>
<button
onClick={handlePublish}
className='p-2 rounded cursor-pointer focus:outline-none bg-sky-500 hover:bg-sky-600 mr-1'>
{t("share")}
</button>
<div className="flex-1 flex justify-end">
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
className={`p-2 rounded cursor-pointer focus:outline-none bg-${theme === 'dark' ? 'gray-600 text-white hover:bg-gray-700' : 'gray-200 text-black hover:bg-gray-300'}`}
>
{theme === 'light' ? <CiDark className="text-xl" /> : <CiLight className="text-xl" />}
</button>
</div>
</div>
</nav>
<div className="flex flex-col lg:flex-row flex-1">
<div className="w-full lg:w-1/2 h-1/2 lg:h-full">
<MonacoEditor
height="100%"
language="html"
theme={theme === 'light' ? 'vs-light' : 'vs-dark'}
value={code}
onChange={(newValue) => {
if (newValue !== undefined) {
setCode(newValue);
}
}}
/>
</div>
<div className="w-full lg:w-1/2 h-1/2 lg:h-full">
<iframe
ref={outputRef}
style={{
width: '100%',
height: '100%',
border: 'none',
backgroundColor: "white"
}}
></iframe>
</div>
</div>
</div>
);
};

export default CodeEditor;
5 changes: 5 additions & 0 deletions frontend/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ const Navbar = () => {
{t("navbar.links.newpost")}
</Link>
</li>
<li className="mt-2">
<Link to="/app/code" className={getNavLinkClass("/app/code")} onClick={closeMenu}>
{t("code")}
</Link>
</li>
<li className="mt-2">
<Link to="/app/profile" className={getNavLinkClass("/app/profile")} onClick={closeMenu}>
{t("navbar.links.profile")}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom":{
"og":"Original Code Snippet",
"des":"📝 Describe your customization here..."
}
},
"code":"Code Editor",
"share":"Share Your Masterpiece"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/guj/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom":{
"og":"અસલ કોડ સ્નિપેટ",
"des":"📝 અહીં તમારા કસ્ટમાઇઝેશનનું વર્ણન કરો..."
}
},
"code":"કોડ એડિટર",
"share":"તમારી શ્રેષ્ઠ કૃતિ શેર કરો"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/hi/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom": {
"og": "मौलिक कोड स्निपेट",
"des": "📝 यहाँ अपनी अनुकूलन का विवरण दें..."
}
},
"code":"कोड संपादक",
"share":"अपनी उत्कृष्ट कृति साझा करें"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/mh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,7 @@
"custom": {
"og": "मूळ कोड स्निपेट",
"des": "📝 येथे आपले सानुकूलन वर्णन करा..."
}
},
"code":"कोड एडिटर",
"share":"तुमचा उत्कृष्ट नमुना शेअर करा"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/tam/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom":{
"og":"அசல் குறியீட்டு துணுக்கு",
"des":"📝 உங்கள் தனிப்பயனாக்கத்தை இங்கு விவரிக்கவும்..."
}
},
"code":"குறியீடு திருத்தி",
"share":"உங்கள் தலைசிறந்த படைப்பைப் பகிரவும்"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/tel/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom":{
"og":"మూల కోడ్ స్నిపెట్",
"des":"📝 ఇక్కడ మీ అనుకూలీకరణను వివరించండి..."
}
},
"code":"కోడ్ ఎడిటర్",
"share":"మీ మాస్టర్‌పీస్‌ను భాగస్వామ్యం చేయండి"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/ur/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom":{
"og":"اصلی کوڈ اسنیپٹ",
"des":"📝 یہاں اپنی حسب ضرورت بیان کریں..."
}
},
"code":"کوڈ ایڈیٹر",
"share":"اپنا شاہکار شیئر کریں۔"
}
4 changes: 3 additions & 1 deletion frontend/src/locales/wb/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,7 @@
"custom":{
"og":"মূল কোড স্নিপেট",
"des":"📝 এখানে আপনার কাস্টমাইজেশন বর্ণনা করুন..."
}
},
"code":"কোড এডিটর",
"share":"আপনার মাস্টারপিস শেয়ার করুন"
}
18 changes: 9 additions & 9 deletions frontend/src/pages/NewPost.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import axios, { AxiosError } from "axios";
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { useRecoilValue } from "recoil";
import { tokenState } from "../store/atoms/auth";
import { useNavigate } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";
import toast from 'react-hot-toast';
import { useTranslation } from "react-i18next";

Expand All @@ -14,16 +14,16 @@ const NewPost = () => {
const [tagInput, setTagInput] = useState("");
const token = useRecoilValue(tokenState);
const navigate = useNavigate();
// const [error, setError] = useState({
// title: "",
// description: "",
// codeSnippet: "",
// tags: "",
// message: "",
// });
const location = useLocation();
const [errorMessage, setErrorMessage] = useState("");
const { t } = useTranslation();

useEffect(() => {
if (location.state && location.state.codeSnippet) {
setCodeSnippet(location.state.codeSnippet);
}
}, [location.state]);

const handleAddTag = () => {
if (tagInput.length > 0 && !tags.includes(tagInput)) {
setTags([...tags, tagInput]);
Expand Down
Loading