├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.js ├── components │ ├── Code.js │ ├── Header.js │ ├── Post.js │ └── assets │ │ └── icons.js ├── index.css ├── index.js ├── markdown-ss.png └── markdown │ └── article.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ![Project Preview](./src/markdown-ss.png) 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn install` 10 | 11 | Instal All dependencies in this project 12 | 13 | ### `yarn dev` 14 | 15 | Runs the app in the development mode.
16 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 17 | 18 | ### Link 19 | 20 | - React Syntax Highlighter: https://www.npmjs.com/package/react-syntax-highlighter 21 | - React Copy To Clipboard: https://github.com/nkbt/react-copy-to-clipboard 22 | - Markdown To Jsx: https://www.npmjs.com/package/markdown-to-jsx 23 | - Heroicons: https://heroicons.com/ 24 | - article markdown: https://gist.github.com/candraKriswinarto/38e52a600edf73f3b5626acc5c07403e 25 | 26 | ### Video Tutorial 27 | 28 | You can see my youtube video for this project in [here](https://youtu.be/Od-Uj5RSsuM) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-blog", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^13.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "markdown-to-jsx": "^7.1.7", 10 | "react": "^18.2.0", 11 | "react-copy-to-clipboard": "^5.1.0", 12 | "react-dom": "^18.2.0", 13 | "react-scripts": "5.0.1", 14 | "react-syntax-highlighter": "^15.5.0", 15 | "web-vitals": "^2.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candraKriswinarto/react-markdown-blog/b257ceaade87f826df9d1bc71a91f06e8fafb579/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | JS Snipets 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candraKriswinarto/react-markdown-blog/b257ceaade87f826df9d1bc71a91f06e8fafb579/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candraKriswinarto/react-markdown-blog/b257ceaade87f826df9d1bc71a91f06e8fafb579/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from "react"; 2 | import Header from "./components/Header"; 3 | import Post from "./components/Post"; 4 | 5 | // Create Context 6 | export const ThemeContext = createContext(); 7 | 8 | function App() { 9 | const [isDark, setIsDark] = useState(true); 10 | 11 | const value = { 12 | isDark, setIsDark 13 | } 14 | 15 | return ( 16 | 17 |
18 | 19 | 20 | ); 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /src/components/Code.js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useState } from 'react'; 2 | import CopyToClipboard from 'react-copy-to-clipboard'; 3 | import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'; 4 | import { materialDark, materialLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; 5 | import { ThemeContext } from '../App'; 6 | import { CopyIcon, PasteIcon } from './assets/icons'; 7 | 8 | const Code = ({ children, language }) => { 9 | const [copied, setCopied] = useState(false); 10 | const { isDark } = useContext(ThemeContext); 11 | 12 | useEffect(() => { 13 | const timer = setTimeout(() => { 14 | setCopied(false) 15 | }, 1000) 16 | return () => clearTimeout(timer) 17 | }, [copied]) 18 | 19 | return ( 20 |
21 | setCopied(true)}> 22 | 25 | 26 | 30 | {children} 31 | 32 |
33 | ) 34 | } 35 | 36 | export default Code -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { ThemeContext } from "../App"; 3 | import { LogoIcon, MoonIcon, SunIcon } from "./assets/icons"; 4 | 5 | const Header = () => { 6 | const { isDark, setIsDark } = useContext(ThemeContext); 7 | 8 | return ( 9 |
10 |
11 |
12 |
13 | 14 | JS Snipets 15 |
16 | 17 |
18 |
19 |
20 | ); 21 | }; 22 | 23 | export default Header; 24 | -------------------------------------------------------------------------------- /src/components/Post.js: -------------------------------------------------------------------------------- 1 | import Markdown from "markdown-to-jsx" 2 | import { useEffect, useState } from "react" 3 | import Code from "./Code"; 4 | 5 | const Post = () => { 6 | const [postContent, setPostContent] = useState(""); 7 | 8 | useEffect(() => { 9 | import("../markdown/article.md") 10 | .then(res => { 11 | fetch(res.default) 12 | .then(response => response.text()) 13 | .then(response => setPostContent(response)) 14 | .catch(err => console.log(err)) 15 | }) 16 | }, []); 17 | 18 | return ( 19 |
20 |
21 |
22 | 29 | {postContent} 30 | 31 |
32 |
33 |
34 | ) 35 | } 36 | 37 | export default Post -------------------------------------------------------------------------------- /src/components/assets/icons.js: -------------------------------------------------------------------------------- 1 | export const LogoIcon = () => ( 2 | 3 | 4 | 5 | ) 6 | 7 | export const MoonIcon = () => ( 8 | 9 | 10 | 11 | ) 12 | 13 | export const CopyIcon = () => ( 14 | 15 | 16 | 17 | 18 | ) 19 | 20 | export const PasteIcon = () => ( 21 | 22 | 23 | 24 | 25 | ) 26 | 27 | export const SunIcon = () => ( 28 | 29 | 30 | 31 | ) -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); 2 | :root { 3 | --background-body: #292929; 4 | --background-content: #1d1d1d; 5 | --text-color: #e5e5e5; 6 | } 7 | 8 | body { 9 | margin: 0; 10 | font-size: 1rem; 11 | font-family: 'Roboto', sans-serif; 12 | background: var(--background-body); 13 | color: var(--text-color); 14 | } 15 | 16 | /* Global */ 17 | svg { 18 | width: 1.5rem; 19 | } 20 | button.icon { 21 | background: none; 22 | outline: none; 23 | border: none; 24 | cursor: pointer; 25 | } 26 | .container { 27 | max-width: 50rem; 28 | margin: 0 auto; 29 | padding: 0 0.5rem 0 0.5rem; 30 | display: flex; 31 | align-items: center; 32 | height: 100%; 33 | } 34 | 35 | /* header */ 36 | header.header { 37 | position: fixed; 38 | top: 0; 39 | left: 0; 40 | width: 100%; 41 | height: 4.5rem; 42 | background: var(--background-content); 43 | box-shadow: 0 1px 1px rgba(0,0,0, 0.3); 44 | z-index: 10; 45 | } 46 | header .logo-wrapper { 47 | display: flex; 48 | align-items: center; 49 | justify-content: space-between; 50 | width: 100%; 51 | } 52 | header .logo { 53 | display: flex; 54 | align-items: center; 55 | cursor: default; 56 | } 57 | header .logo span { 58 | margin-left: 0.5rem; 59 | } 60 | 61 | /* Post */ 62 | .article { 63 | margin: 4rem 0; 64 | } 65 | .post-wrapper { 66 | width: 100%; 67 | } 68 | 69 | /* Code */ 70 | .code { 71 | position: relative; 72 | } 73 | .code .copy-icon { 74 | position: absolute; 75 | top: 1rem; 76 | right: 1rem; 77 | z-index: 5; 78 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | 13 | -------------------------------------------------------------------------------- /src/markdown-ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candraKriswinarto/react-markdown-blog/b257ceaade87f826df9d1bc71a91f06e8fafb579/src/markdown-ss.png -------------------------------------------------------------------------------- /src/markdown/article.md: -------------------------------------------------------------------------------- 1 | # Useful JavaScript Code Snippets 2 | 3 | 1. Sort an Array 4 | 5 | 6 | //strings 7 | const names = ["Seema", "Rekha", "Jaya"]; 8 | names.sort(); 9 | //['Jaya', 'Rekha', 'Seema' ] 10 | 11 | //Numbers 12 | const numbers = [101, 8, 87]; 13 | numbers.sort((a, b) => { 14 | return a - b; 15 | }); 16 | //[ 8, 87, 101 ] 17 | 18 | 19 | 2. Select a random element 20 | 21 | 22 | const items = ["Ball", "Bat", "Cup"] 23 | const randomIndex = Math.floor(Math.random()*items.length) 24 | items[randomIndex] 25 | 26 | 27 | 3. Reverse a string 28 | 29 | 30 | function reverseString(string) { 31 | return string.split(" ").reverse().join(" ") 32 | } 33 | 34 | revereseString("Random String") 35 | 36 | 37 | 4. Check if element has a class 38 | 39 | 40 | const element = document.querySelector("#element") 41 | element.classList.contains("active") 42 | 43 | 44 | 5. String interpolation 45 | 46 | 47 | const name = "Jaya" 48 | console.log(`Hi, ${name}. You have ${2 ** 3} new notifications.`} 49 | //Hi, Jaya. You have 8 new notifications. 50 | 51 | 52 | 6. Loop through an array 53 | 54 | 55 | const cars = ["Ford", "BMW", "Audi" ] 56 | for (let car of cars) { 57 | console.log(car) 58 | } 59 | 60 | /* 61 | Ford 62 | BMW 63 | Audi 64 | */ 65 | 66 | 67 | 7. Get current time 68 | 69 | 70 | const date = new Date() 71 | const currentTime = 72 | `${date.getHours()}:${date.getMintues()}:${date.getSeconds()}` 73 | 74 | console.log(currentTimes) 75 | //example output: "22:16:41" 76 | 77 | 78 | 8. Replace part of a string 79 | 80 | 81 | const string = "You are awesome." 82 | const replacedString = string.replace("You", "We") 83 | 84 | console.log(replacedString) //Output: "We are awesome" 85 | 86 | 87 | 9. Destructing variable assignment 88 | 89 | 90 | let profile = ['bob', 34, 'carpenter']; 91 | let [name, age, job] = profile; 92 | console.log(name); 93 | // bob 94 | 95 | 96 | 10. Using the spread operator 97 | 98 | 99 | let data = [1,2,3,4,5]; 100 | console.log(...data); 101 | // 1 2 3 4 5 102 | let data2 = [6,7,8,9,10]; 103 | let combined = [...data, ...data2]; 104 | console.log(...combined); 105 | // 1 2 3 4 5 6 7 8 9 10 106 | console.log(Math.max(...combined)); 107 | // 10 108 | --------------------------------------------------------------------------------