add markdown viewer

This commit is contained in:
Janis
2023-01-15 02:48:17 +01:00
parent 9617dc2ef3
commit a0598484b4
11 changed files with 1263 additions and 122 deletions

View File

@@ -3,32 +3,36 @@ import PropTypes from "prop-types";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import oneDark from "react-syntax-highlighter/dist/esm/styles/prism/one-dark"; import oneDark from "react-syntax-highlighter/dist/esm/styles/prism/one-dark";
import LoadMarkdown from "./articles/[categoryName]/[articleName]/LoadMarkdown";
export default function Markdown({ value }: { value: any }) { export default function Markdown({ value }: { value: any }) {
return ( return (
<ReactMarkdown <div>
// eslint-disable-next-line react/no-children-prop <ReactMarkdown
children={value} // eslint-disable-next-line react/no-children-prop
components={{ children={value}
code({ node, inline, className, children, ...props }) { components={{
const match = /language-(\w+)/.exec(className || ""); code({ node, inline, className, children, ...props }) {
return !inline && match ? ( const match = /language-(\w+)/.exec(className || "");
<SyntaxHighlighter return !inline && match ? (
// eslint-disable-next-line react/no-children-prop <SyntaxHighlighter
children={String(children).replace(/\n$/, "")} // eslint-disable-next-line react/no-children-prop
style={oneDark} children={String(children).replace(/\n$/, "")}
language={match[1]} style={oneDark}
PreTag="div" language={match[1]}
{...props} PreTag="div"
/> {...props}
) : ( />
<code className={className} {...props}> ) : (
{children} <code className={className} {...props}>
</code> {children}
); </code>
}, );
}} },
/> }}
/>
<LoadMarkdown />
</div>
); );
} }

View File

@@ -84,6 +84,7 @@ export default function Nav({ categories }: { categories: Category[] }) {
<div className={styles.dropDownContainer}> <div className={styles.dropDownContainer}>
<div className={styles.content}> <div className={styles.content}>
<Link href={"/articles"}>All</Link> <Link href={"/articles"}>All</Link>
{categories?.map((cat, i) => { {categories?.map((cat, i) => {
{ {
return ( return (

View File

@@ -6,23 +6,33 @@ import { useState, useRef } from "react";
import styles from "../../../../styles/modules/AdminArticlesCreate.module.scss"; import styles from "../../../../styles/modules/AdminArticlesCreate.module.scss";
import { PostArticle } from "../../../../types/postData"; import { PostArticle } from "../../../../types/postData";
import Markdown from "../../../Markdown"; import Markdown from "../../../Markdown";
import { Category } from "@prisma/client";
import "../../../../styles/inputs.scss";
import Select, { GroupBase, OptionsOrGroups } from "react-select";
import { apiUrl } from "../../../global";
import urlJoin from "url-join";
export default function AdminArticlesCreate() { export default function AdminArticlesCreate() {
const [formData, setFormData] = useState<PostArticle>(null); const [formData, setFormData] = useState<PostArticle>(null);
const [markdown, setMarkdown] = useState<string>(""); const [markdown, setMarkdown] = useState<string>("");
const [selectedCategory, setSelectedCategory] = useState<string>();
const titleRef = useRef<HTMLInputElement>(null); const titleRef = useRef<HTMLInputElement>(null);
const markdownTextAreaRef = useRef<HTMLTextAreaElement>(null);
function handleFormChange({ newMarkdownText = markdown }) { const selectCategoriesOptions: any = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
];
function handleFormChange() {
setMarkdown(markdownTextAreaRef.current.value);
setFormData({ setFormData({
name: titleRef.current.value.replaceAll(" ", "%20"), name: titleRef.current.value.replaceAll(" ", "%20"),
title: titleRef.current.value, title: titleRef.current.value,
introduction: "test intro", introduction: "test intro",
markdown: markdown, markdown: markdown,
categoryId: 1, categoryId: 2,
}); });
setMarkdown(newMarkdownText);
} }
async function postData() { async function postData() {
@@ -49,20 +59,29 @@ export default function AdminArticlesCreate() {
send send
</button> </button>
<div className={styles.form}> <div className={styles.form}>
<div className={styles.ContentTable}>contenttable</div> <div className={styles.contentTableEditor}>contenttable</div>
<div className={styles.content}> <div className={styles.articleEditor}>
<Select
className="react-select-container"
classNamePrefix="react-select"
value={selectedCategory}
onChange={handleFormChange}
options={selectCategoriesOptions}
/>
<input <input
onChange={() => { onChange={handleFormChange}
handleFormChange({}); className={""}
}}
type="text" type="text"
name="title" name="title"
placeholder="title" placeholder="title"
ref={titleRef} ref={titleRef}
/> />
<textarea></textarea>
<Markdown value={markdown} /> <div className={styles.markdown}>
<textarea ref={markdownTextAreaRef} onChange={handleFormChange}></textarea>
<Markdown value={markdown} />
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -7,35 +7,35 @@ import "../../../../styles/prism_themes/prism-one-dark.css";
//import "../../../styles/prism_themes/prism-one-light.css"; //import "../../../styles/prism_themes/prism-one-light.css";
export default function LoadMarkdown() { export default function LoadMarkdown() {
useEffect(() => { useEffect(() => {
document.querySelectorAll("pre").forEach((pre) => { document.querySelectorAll("pre").forEach((pre) => {
if (pre.classList.length < 1) { if (pre.classList.length < 1) {
pre.classList.add("language-"); pre.classList.add("language-");
} }
}); });
document.querySelectorAll("code").forEach((c) => { document.querySelectorAll("code").forEach((c) => {
if (c.classList.length < 1) { if (c.classList.length < 1) {
c.classList.add("language-"); c.classList.add("language-");
} }
}); });
document.querySelectorAll("blockquote").forEach((bq) => { document.querySelectorAll("blockquote").forEach((bq) => {
bq.classList.add("blockquote"); bq.classList.add("blockquote");
}); });
document.querySelectorAll("li").forEach((li) => { document.querySelectorAll("li").forEach((li) => {
let paragraphText = ""; let paragraphText = "";
li.querySelectorAll("p").forEach((p) => { li.querySelectorAll("p").forEach((p) => {
paragraphText = p.innerHTML; paragraphText = p.innerHTML;
}); });
if (paragraphText != "") { if (paragraphText != "") {
li.innerHTML = paragraphText; li.innerHTML = paragraphText;
} }
}); });
Prism.highlightAll(); Prism.highlightAll();
}, []); }, []);
return <div></div>; return <div></div>;
} }

View File

@@ -65,7 +65,7 @@ export default async function ArticlePage({ params }: { params: { articleName: s
<p>{article?.introduction}</p> <p>{article?.introduction}</p>
</div> </div>
<Markdown value={markdown} /> <Markdown value={markdown} />
<LoadMarkdown />
{/* <div {/* <div
className="markdown" className="markdown"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{

1119
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,7 @@
"react-code-blocks": "^0.0.9-0", "react-code-blocks": "^0.0.9-0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-markdown": "^8.0.4", "react-markdown": "^8.0.4",
"react-select": "^5.7.0",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"sass": "^1.57.0", "sass": "^1.57.0",

View File

@@ -12,18 +12,12 @@ export default async function handler(req: Request, res: Response) {
if (result !== null) { if (result !== null) {
res.end(JSON.stringify(result)); res.end(JSON.stringify(result));
} else { } else {
const error: ResponseError = { console.log("No categories found");
code: "404", res.end(JSON.stringify([]));
message: "No categories found!",
};
res.status(404).send(JSON.stringify(error));
} }
}) })
.catch((err) => { .catch((err) => {
const error: ResponseError = { console.log(err);
code: "500", res.end(JSON.stringify([]));
message: err,
};
res.status(500).send(JSON.stringify(error));
}); });
} }

73
styles/inputs.scss Normal file
View File

@@ -0,0 +1,73 @@
@import "variables_colors.scss";
@import "variables.scss";
input {
border: 2px solid rgba(95, 95, 95, 0.5);
background-color: transparent;
height: 30px;
padding: 5px 5px 5px 5px;
color: var(--color-font);
border-radius: 3px;
outline: none;
transition: all 50ms linear;
&::placeholder {
font-weight: bold;
}
&:hover {
border-color: rgba(177, 177, 177, 0.608);
}
&:focus {
border-color: var(--color-accent);
}
&.error {
border-color: var(--color-error);
}
&.success {
border-color: var(--color-success);
}
&.warning {
border-color: var(--color-warning);
}
&.info {
border-color: var(--color-info);
}
}
.react-select-container {
.react-select__control {
background-color: var(--color-background-secondary);
border-color: rgba(95, 95, 95, 0.5);
transition: none;
&:hover {
border-color: rgba(95, 95, 95, 0.5);
}
}
.react-select__menu {
background-color: var(--color-background-secondary);
border: 1px solid rgba(95, 95, 95, 0.5);
}
.react-select__option {
background-color: var(--color-background-secondary);
&:hover {
background-color: var(--bg-primary);
}
}
.react-select__indicator-separator {
background-color: rgba(95, 95, 95, 0.5);
}
.react-select__placeholder,
.react-select__single-value {
color: var(--font-color);
}
}

View File

@@ -9,14 +9,26 @@
max-width: 1800px; max-width: 1800px;
padding: 0px 24px; padding: 0px 24px;
.content { .articleEditor {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
textarea { .markdown {
color: var(--font-color); display: grid;
background-color: rgba(16, 16, 16, 0.234); grid-template-columns: 1fr 1fr;
width: 100%; gap: 10px 10px;
min-height: 500px; textarea {
color: var(--font-color);
border: 2px solid rgba(59, 59, 59, 0.434);
background-color: transparent;
min-height: 700px;
resize: none;
display: block;
border-radius: 0px;
outline: 0;
resize: vertical;
font-family: inherit;
font-size: inherit;
}
} }
} }
} }

View File

@@ -6,10 +6,16 @@
/*! By default colors are in DARK mode */ /*! By default colors are in DARK mode */
/* Colors: General */ /* Colors: General */
--color-background-body: #181a1b; --color-background-body: #181a1b;
--color-background-secondary: #2c3032;
--color-font: #ffffff; --color-font: #ffffff;
--color-font-muted: #929292; --color-font-muted: #929292;
--color-shadow-nav: #00000033; --color-shadow-nav: #00000033;
--color-error: #cf000f;
--color-success: #009944;
--color-info: #63c0df;
--color-warning: #f0541e;
--color-overlay-mix: var(--color-font); --color-overlay-mix: var(--color-font);
--color-background-nav: transparent; --color-background-nav: transparent;
--color-svg-nav: #bfbfbf; --color-svg-nav: #bfbfbf;