From 1750542182ff7fb092d07386604aa3a75bc3e5cb Mon Sep 17 00:00:00 2001 From: Mia Rose Winter Date: Mon, 18 Nov 2024 14:30:06 +0100 Subject: [PATCH] Implemented Image Upload Modal in Article Editor --- Wave/Assets/React/ArticleEditor.tsx | 88 ++++++++++++++++++++-------- Wave/Assets/React/Forms.tsx | 2 +- Wave/Assets/React/ImageEditor.tsx | 35 +++++++++++ Wave/Assets/React/Modal.tsx | 29 +++++++++ Wave/Assets/main.tsx | 6 ++ Wave/Controllers/ImageController.cs | 24 ++++++++ Wave/Services/ImageService.cs | 6 +- Wave/package-lock.json | Bin 220275 -> 244936 bytes Wave/package.json | 3 +- 9 files changed, 164 insertions(+), 29 deletions(-) create mode 100644 Wave/Assets/React/ImageEditor.tsx create mode 100644 Wave/Assets/React/Modal.tsx diff --git a/Wave/Assets/React/ArticleEditor.tsx b/Wave/Assets/React/ArticleEditor.tsx index 9b0a008..c2b1fa4 100644 --- a/Wave/Assets/React/ArticleEditor.tsx +++ b/Wave/Assets/React/ArticleEditor.tsx @@ -1,11 +1,15 @@ import React, { useState, useEffect, useRef } from "react"; import { updateCharactersLeft, insertBeforeSelection, insertBeforeAndAfterSelection } from "../utilities/md_functions"; import { LabelInput, ToolBarButton } from "./Forms"; +import ImageEditor from "./ImageEditor"; import { CategoryColor, Category, ArticleStatus, ArticleView, ArticleDto } from "../model/Models"; import { useTranslation } from 'react-i18next'; +// @ts-ignore import markdownit from "markdown-it"; +// @ts-ignore import markdownitmark from "markdown-it-mark"; import "groupby-polyfill/lib/polyfill.js"; +import TextareaMarkdownEditor from 'react-textarea-markdown-editor'; const nameof = function(name: keyof T) { return name; } @@ -62,6 +66,7 @@ export default function Editor() { const [isPublished, setIsPublished] = useState(false); const [article, setArticle] = useState(null); const [categories, setCategories] = useState([]); + const [imageDialog, setImageDialog] = useState(false); const [model, setModel] = useState({ body: "", categories: [], @@ -155,6 +160,7 @@ export default function Editor() { categories: result.categories.map(c => c.id), })); setArticle(result); + // setMarkdown(result.body) console.log("Article loaded"); }) .catch(error => { @@ -165,16 +171,27 @@ export default function Editor() { } }, ([setArticle, setNotice, console, location]) as any[]); - const markdownArea = useRef(null); + const textAreaMarkdown = useRef(null); + const markdownOnChange = function(v: string) { + onChangeModel({ + target: { + // @ts-ignore + value: v, + name: nameof("body") + } + }); + return {}; + } + // @ts-ignore return ( <> { dirty &&
- + -

{t("editor.unsaved_changes_notice")}

+

{t("editor.unsaved_changes_notice")}

} { @@ -198,6 +215,11 @@ export default function Editor() {
  • {t("Published")}
  • + setImageDialog(false)} callback={(location) => { + textAreaMarkdown.current?.append(`\n![](${location})\n`) + setImageDialog(false) + }} /> +
    @@ -213,6 +235,7 @@ export default function Editor() { onChange={onChangeModel} name={nameof("categories")} defaultValue={article.categories.map(c => c.id)}> { + // @ts-ignore Array.from(Map.groupBy(categories, (c: Category) => c.color) as Map) .map((value, _) =>
    -
    insertBeforeSelection(markdownArea.current, "# ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("# ")}> {t("Tools.H1_Label")} insertBeforeSelection(markdownArea.current, "## ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("## ")}> {t("Tools.H2_Label")} insertBeforeSelection(markdownArea.current, "### ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("### ")}> {t("Tools.H3_Label")} insertBeforeSelection(markdownArea.current, "#### ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("#### ")}> {t("Tools.H4_Label")}
    insertBeforeAndAfterSelection(markdownArea.current, "**")}> + onClick={() => textAreaMarkdown.current?.mark('**', '**',t("Tools.Bold_Tooltip"))}> B insertBeforeAndAfterSelection(markdownArea.current, "*")}> + onClick={() => textAreaMarkdown.current?.mark('*', '*',t("Tools.Italic_Tooltip"))}> I insertBeforeAndAfterSelection(markdownArea.current, "++")}> + onClick={() => textAreaMarkdown.current?.mark('+', '+',t("Tools.Underline_Tooltip"))}> U insertBeforeAndAfterSelection(markdownArea.current, "~~")}> + onClick={() => textAreaMarkdown.current?.mark('~~', '~~',t("Tools.StrikeThrough_Tooltip"))}> {t("Tools.StrikeThrough_Label")} insertBeforeAndAfterSelection(markdownArea.current, "==")}> + onClick={() => textAreaMarkdown.current?.mark('==', '==',t("Tools.Mark_Tooltip"))}> {t("Tools.Mark_Label")} insertBeforeSelection(markdownArea.current, "> ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("> ")}> | {t("Tools.Cite_Label")}
    insertBeforeSelection(markdownArea.current, "1. ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("1. ")}> 1. insertBeforeSelection(markdownArea.current, "a. ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("a. ")}> a. insertBeforeSelection(markdownArea.current, "A. ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("A. ")}> A. insertBeforeSelection(markdownArea.current, "i. ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("i. ")}> i. insertBeforeSelection(markdownArea.current, "I. ", true)}> + onClick={() => textAreaMarkdown.current?.markLine("I. ")}> I.
    insertBeforeAndAfterSelection(markdownArea.current, "`")}> + onClick={() => textAreaMarkdown.current?.mark('`', '`',t("Tools.CodeLine_Tooltip"))}> @@ -322,7 +345,7 @@ export default function Editor() { insertBeforeAndAfterSelection(markdownArea.current, "```")}> + onClick={() => textAreaMarkdown.current?.mark('```\n', '\n```\n',t("Tools.CodeBlock_Tooltip"))}> @@ -332,12 +355,25 @@ export default function Editor() {
    +
    + setImageDialog(true)}> + + + + {t("Tools.ImageAdd_Label")} + +
    -