Skip to content
Open
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
18 changes: 18 additions & 0 deletions components/WordCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

interface WordCounterProps {
text: string;
}

const WordCounter: React.FC<WordCounterProps> = ({ text }) => {
const wordCount = text.trim().split(/\s+/).length;
const characterCount = text.length;

return (
<div className="text-sm text-gray-600">
Words: {wordCount} | Characters: {characterCount}
</div>
);
};

export default WordCounter;
158 changes: 100 additions & 58 deletions pages/editor.tsx
Original file line number Diff line number Diff line change
@@ -1,86 +1,128 @@
import dynamic from "next/dynamic";
import { useState } from "react";
import { useState, useEffect } from "react";
import Head from 'next/head';
import Switch from "react-switch";
import { useRouter } from 'next/router';
import Image from 'next/image'
import "@uiw/react-md-editor/markdown-editor.css";
import "@uiw/react-markdown-preview/markdown.css";
import "../styles/Editor.module.css";
import {uncheckedIcon, checkedIcon} from '../src/svg';
import Quotes from '../src/mdQuotes';


// Dynamic import for the markdown editor
const MDEditor = dynamic(
() => import("@uiw/react-md-editor").then((mod) => mod.default),
{ ssr: false }
);
const EditerMarkdown = dynamic(
() =>
import("@uiw/react-md-editor").then((mod) => {
return mod.default.Markdown;
}),
{ ssr: false }
);

// WordCounter Component
const WordCounter = ({ text, darkMode }: { text: string; darkMode: boolean }) => {
const wordCount = text.trim() ? text.trim().split(/\s+/).length : 0; // Count words
const characterCount = text.length; // Count characters

return (
<div className={`fixed bottom-0 left-0 right-0 p-2 shadow-md text-sm ${darkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-600'}`}>
Words: {wordCount} | Characters: {characterCount}
</div>
);
};

function HomePage() {
// generate random number from quotes range
var randomnumber = Math.floor(Math.random() * ((Quotes.quotes.length-1) - 0 + 1)) + 0;
const [value, setValue] = useState(`
## A quote you may need for today 😊
> ${Quotes.quotes[randomnumber].quote}
const [value, setValue] = useState<string>(""); // Initialize with an empty string
const [darkMode, setDarkMode] = useState(false);
const router = useRouter();

${Quotes.quotes[randomnumber].author}
// Load saved content from localStorage when the component mounts
useEffect(() => {
if (typeof window !== "undefined") { // Check if running in the browser
const savedContent = localStorage.getItem('markdownContent');
if (savedContent) {
setValue(savedContent);
} else {
// Set initial content if nothing is saved
const randomnumber = Math.floor(Math.random() * Quotes.quotes.length);
setValue(`
## A quote you may need for today 😊
> ${Quotes.quotes[randomnumber].quote}

----
${Quotes.quotes[randomnumber].author}

---
### Getting started 😎
- Delete this template before starting.
- This editor supports all markdown functionalities.
- Fully open-source.
- We don't store your data, anything you type remains in your local browser (_Once we move to cloud, we will encryt all of your data before saving them on servers_).
- Want any improvemnts or contribute @ [GitHub](https://github.com/hotheadhacker/next-markdown).
` as any);
const [darkMode, setDarkMode] = useState(false);
const router = useRouter();
let navBackground = {
dark: 'flex justify-between bg-gradient-to-r from-slate-700 via-neutral-700 to-gray-800',
light: 'flex justify-between bg-gradient-to-r from-indigo-100 via-purple-200 to-pink-200'
}
- We don't store your data, anything you type remains in your local browser (_Once we move to cloud, we will encrypt all of your data before saving them on servers_).
- Want any improvements or contribute @ [GitHub](https://github.com/hotheadhacker/next-markdown).
`);
}
}
}, []);

// Save content to localStorage whenever it changes
useEffect(() => {
if (typeof window !== "undefined") { // Check if running in the browser
localStorage.setItem('markdownContent', value);
}
}, [value]);

// Wrapper function for onChange
const handleEditorChange = (value?: string) => {
setValue(value || ""); // Update state with the new value
};

// Function to save the markdown content as a .md file
const saveMarkdownFile = () => {
const blob = new Blob([value], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.md'; // Name of the file to be downloaded
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url); // Clean up the URL object
};

return (
<>
<Head>
<title>Online Markdown Editor | isalman.dev</title>
<meta name="description" content="Online Markdown editor with live privew" />
<meta name="author" content="isalman.dev, hotheadhacker, Salman Quresi" />
<link rel="icon" href="/favicon.ico" />
</Head>
<nav className={darkMode ? navBackground.dark : navBackground.light}>
<div >
<button type="button" className="m-4 px-8 py-2 font-medium border-double border-4 border-indigo-600 bg-gradient-to-r from-green-400 to-blue-500 hover:from-pink-500 hover:to-yellow-500" onClick={()=> router.push('/')}> Home</button>
</div>
<div className="m-3">
<label>
{/* <span> <Image src='/theme-icon.svg' alt='icon' height={10} width={10} /> </span> */}
<Switch
uncheckedIcon = {uncheckedIcon}
checkedIcon = {checkedIcon}
onChange={() => {setDarkMode(!darkMode)}} checked={darkMode} />
</label>
</div>

</nav>
<div data-color-mode={darkMode ? 'dark' : 'light'}>
<MDEditor value={value}
onChange={setValue}
fullscreen = {false}
height = {700}
<Head>
<title>Online Markdown Editor | isalman.dev</title>
<meta name="description" content="Online Markdown editor with live preview" />
<meta name="author" content="isalman.dev, hotheadhacker, Salman Quresi" />
<link rel="icon" href="/favicon.ico" />
</Head>
<nav className={`flex justify-between items-center p-4 ${darkMode ? 'bg-gray-800' : 'bg-white'} shadow-md`}>
<button
className="px-6 py-2 font-medium text-white bg-gradient-to-r from-green-400 to-blue-500 hover:from-pink-500 hover:to-yellow-500 rounded transition duration-300"
onClick={() => router.push('/')}
>
Home
</button>
<button
onClick={() => setDarkMode(!darkMode)}
className="flex items-center justify-center w-10 h-10 rounded-full bg-gray-200 hover:bg-gray-300 transition duration-300"
>
{darkMode ? (
<span role="img" aria-label="Sun">☀️</span>
) : (
<span role="img" aria-label="Moon">🌙</span>
)}
</button>
</nav>
<div data-color-mode={darkMode ? 'dark' : 'light'} className="relative" style={{ paddingBottom: '50px' }}>
<MDEditor
value={value}
onChange={handleEditorChange} // Use the wrapper function here
fullscreen={false}
height={700}
/>
{/* <div style={{ paddingTop: 50 }}>
<EditerMarkdown source={value} />
</div> */}
</div>
<WordCounter text={value} darkMode={darkMode} /> {/* Pass darkMode to WordCounter */}
<button
onClick={saveMarkdownFile}
className="fixed bottom-4 right-4 px-6 py-2 font-medium text-white bg-gradient-to-r from-green-400 to-blue-500 hover:from-pink-500 hover:to-yellow-500 rounded transition duration-300 shadow-md"
>
Download
</button>
</div>
</>
);
}
Expand Down