From fbd30633cc95ddaa243093b3dd1d86b28a282d66 Mon Sep 17 00:00:00 2001
From: Saleh Yusefnejad <msynk@outlook.com>
Date: Mon, 17 Mar 2025 17:00:08 +0330
Subject: [PATCH 1/2] add BitMarkdownEditor component #10230

---
 .../MarkdownEditor/BitMarkdownEditor.razor    |  11 +
 .../MarkdownEditor/BitMarkdownEditor.razor.cs |  34 ++
 .../MarkdownEditor/BitMarkdownEditor.scss     |  12 +
 .../MarkdownEditor/BitMarkdownEditor.ts       | 351 ++++++++++++++++++
 .../BitMarkdownEditorJsRuntimeExtensions.cs   |  14 +
 .../Styles/extra-components.scss              |   1 +
 .../Components/BitComponentBase.cs            |   2 +-
 .../BitMarkdownEditorDemo.razor               |  21 ++
 .../BitMarkdownEditorDemo.razor.cs            |  22 ++
 .../BitMarkdownEditorDemo.razor.scss          |   7 +
 .../Pages/Home/ComponentsSection.razor        |   3 +
 .../Shared/MainLayout.razor.NavItems.cs       |   1 +
 .../compilerconfig.json                       |   6 +
 13 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor
 create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs
 create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.scss
 create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.ts
 create mode 100644 src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditorJsRuntimeExtensions.cs
 create mode 100644 src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor
 create mode 100644 src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs
 create mode 100644 src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss

diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor
new file mode 100644
index 0000000000..9d9802a461
--- /dev/null
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor
@@ -0,0 +1,11 @@
+@namespace Bit.BlazorUI
+@inherits BitComponentBase
+
+<div @ref="RootElement"
+     @attributes="HtmlAttributes"
+     id="@_Id"
+     style="@StyleBuilder.Value"
+     class="@ClassBuilder.Value"
+     dir="@Dir?.ToString().ToLower()">
+    <textarea @ref="_textAreaRef" class="bit-mde-txa" />
+</div>
\ No newline at end of file
diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs
new file mode 100644
index 0000000000..bd89745522
--- /dev/null
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs
@@ -0,0 +1,34 @@
+namespace Bit.BlazorUI;
+
+/// <summary>
+/// BitMarkdownEditor is a simple editor like GitHub md editor.
+/// </summary>
+public partial class BitMarkdownEditor : BitComponentBase
+{
+    private ElementReference _textAreaRef = default!;
+
+
+
+    [Inject] private IJSRuntime _js { get; set; } = default!;
+
+
+
+    public async ValueTask<string> GetValue()
+    {
+        return await _js.BitMarkdownEditorGetValue(_Id);
+    }
+
+
+
+    protected override string RootElementClass => "bit-mde";
+
+    protected override Task OnAfterRenderAsync(bool firstRender)
+    {
+        if (firstRender)
+        {
+            _js.BitMarkdownEditorInit(_Id, _textAreaRef);
+        }
+
+        return base.OnAfterRenderAsync(firstRender);
+    }
+}
diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.scss
new file mode 100644
index 0000000000..3220996f8b
--- /dev/null
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.scss
@@ -0,0 +1,12 @@
+@import '../../../Bit.BlazorUI/Styles/functions.scss';
+
+.bit-mde {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.bit-mde-txa {
+    width: 100%;
+    height: 100%;
+    padding: spacing(1);
+}
diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.ts b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.ts
new file mode 100644
index 0000000000..0e766c5a7d
--- /dev/null
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.ts
@@ -0,0 +1,351 @@
+namespace BitBlazorUI {
+    type Content = {
+        value: string;
+        type: "inline" | "block" | "wrap";
+    };
+
+    export class MarkdownEditor {
+        private static _editors: { [key: string]: MarkdownEditor } = {};
+
+        public static init(id: string, textArea: HTMLTextAreaElement) {
+            const editor = new MarkdownEditor(textArea);
+
+            MarkdownEditor._editors[id] = editor;
+        }
+
+        public static getValue(id: string) {
+            const editor = MarkdownEditor._editors[id];
+            if (!editor) return;
+
+            return editor.value;
+        }
+
+        #opens: string[] = [];
+
+        #pairs: { [key: string]: string } = {
+            "(": ")",
+            "{": "}",
+            "[": "]",
+            "<": ">",
+            '"': '"',
+            "`": "`",
+        };
+
+        #textArea: HTMLTextAreaElement;
+
+        constructor(textArea: HTMLTextAreaElement) {
+            this.#textArea = textArea;
+
+            this.#init();
+        }
+
+        get value() {
+            return this.#textArea.value;
+        }
+
+        set value(value) {
+            this.#textArea.value = value;
+        }
+
+        get #block() {
+            const codeBlocks = this.value.split("```");
+            let total = 0;
+            for (const [i, b] of codeBlocks.entries()) {
+                total += b.length + 3;
+                if (this.#start < total) {
+                    return i;
+                }
+            }
+            return 0;
+        }
+
+        get #end() {
+            return this.#textArea.selectionEnd;
+        }
+
+        get #start() {
+            return this.#textArea.selectionStart;
+        }
+
+        #set(start: number, end: number) {
+            this.#textArea.setSelectionRange(start, end);
+        }
+
+        #getLine() {
+            const total = this.value.split("\n");
+            let count = 0;
+            for (let i = 0; i < total.length; i++) {
+                const length = total.at(i)?.length ?? 0;
+                count++;
+                count += length;
+                if (count > this.#end) {
+                    return {
+                        total,
+                        num: i,
+                        col: this.#end - (count - length - 1),
+                    };
+                }
+            }
+            return { total, num: 0, col: 0 };
+        }
+
+        #insert(
+            element: Content,
+            start: number,
+            end: number,
+        ) {
+            if (element.type === "inline") {
+                this.value = `${this.value.slice(0, end)}${element.value}${this.value.slice(end)}`;
+            } else if (element.type === "wrap") {
+                this.value = insert(this.value, element.value, start);
+                this.value = insert(
+                    this.value,
+                    this.#pairs[element.value] as string,
+                    end + element.value.length,
+                );
+                if (element.value.length < 2) this.#opens.push(element.value);
+            } else if (element.type === "block") {
+                const { total, num } = this.#getLine();
+                const first = element.value.at(0);
+                if (first && total[num]?.startsWith(first)) {
+                    total[num] = element.value.trim() + total[num];
+                } else {
+                    total[num] = element.value + total[num];
+                }
+                this.value = total.join("\n");
+            }
+        }
+
+        #setCaret(
+            text: string,
+            start: number,
+            end: number,
+        ) {
+            let startPos = 0;
+            let endPos = 0;
+            if (/[a-z]/i.test(text)) {
+                for (let i = end; i < this.value.length; i++) {
+                    if (this.value[i]?.match(/[a-z]/i)) {
+                        if (!startPos) {
+                            startPos = i;
+                        } else {
+                            endPos = i + 1;
+                        }
+                    } else if (startPos) {
+                        break;
+                    }
+                }
+            } else {
+                startPos = start + text.length;
+                endPos = end + text.length;
+            }
+
+            this.#set(startPos, endPos);
+            this.#textArea.focus();
+        }
+
+        #add(element: Content) {
+            const end = this.#end;
+            const start = this.#start;
+
+            this.#insert(element, start, end);
+            this.#setCaret(element.value, start, end);
+        }
+
+        #getLists(str: string | undefined) {
+            if (!str) return;
+
+            if (startsWithDash(str)) {
+                return '- ';
+            }
+
+            const listNum = startsWithNumber(str);
+            if (listNum) return `${listNum}. `;
+        }
+
+        #correct(cur: number, isDec = false) {
+            const { total } = this.#getLine();
+            for (let i = cur + 1; i < total.length; i++) {
+                const l = total[i];
+                if (!l) continue;
+
+                if (startsWithDash(l)) {
+                    if (l.length > 2) {
+                        total[i] = l;
+                    } else {
+                        continue;
+                    }
+                } else {
+                    const number = startsWithNumber(l);
+                    if (!number) {
+                        break;
+                    } else {
+                        let newNumber: number;
+                        if (isDec) {
+                            if (number > 1) {
+                                newNumber = number - 1;
+                            } else {
+                                break;
+                            }
+                        } else {
+                            newNumber = number + 1;
+                        }
+                        total[i] = l.slice(String(number).length);
+                        total[i] = String(newNumber) + total[i];
+                    }
+                }
+            }
+            this.value = total.join("\n");
+        }
+
+        #init() {
+            this.#textArea.addEventListener("keydown", async (e) => {
+                const reseters = ["Delete", "ArrowUp", "ArrowDown"];
+                const next = this.value[this.#end] ?? "";
+                if (reseters.includes(e.key)) {
+                    this.#opens = [];
+                } else if (e.key === "Backspace") {
+                    const prev = this.value[this.#start - 1];
+                    if (
+                        prev &&
+                        prev in this.#pairs &&
+                        next === this.#pairs[prev]
+                    ) {
+                        e.preventDefault();
+                        const start = this.#start - 1;
+                        const end = this.#end - 1;
+                        this.value = remove(this.value, start);
+                        this.value = remove(this.value, end);
+                        setTimeout(() => {
+                            this.#set(start, end);
+                        }, 0);
+                        this.#opens.pop();
+                    }
+                    if (prev === "\n" && this.#start === this.#end) {
+                        e.preventDefault();
+                        const pos = this.#start - 1;
+                        const { num } = this.#getLine();
+                        this.#correct(num, true);
+                        this.value = remove(this.value, pos);
+                        setTimeout(async () => {
+                            this.#set(pos, pos);
+                        }, 0);
+                    }
+                } else if (e.key === "Tab") {
+                    if (this.#block % 2 !== 0) {
+                        e.preventDefault();
+                        this.#add({
+                            type: "inline",
+                            value: "\t",
+                        });
+                    }
+                } else if (e.key === "Enter") {
+                    const { total, num, col } = this.#getLine();
+                    const line = total.at(num);
+                    let rep = this.#getLists(line);
+                    const orig = rep;
+
+                    const n = startsWithNumber(rep);
+                    if (n) rep = `${n + 1}. `;
+
+                    if (rep && (orig && orig.length < col)) {
+                        e.preventDefault();
+                        if (n) this.#correct(num);
+                        this.#add({
+                            type: "inline",
+                            value: `\n${rep}`,
+                        });
+                    } else if (rep && (orig && orig.length === col)) {
+                        e.preventDefault();
+
+                        const origEnd = this.#end;
+                        const pos = origEnd - orig.length;
+
+                        for (let i = 0; i < orig.length; i++) {
+                            this.value = remove(this.value, origEnd - (i + 1));
+                        }
+
+                        setTimeout(async () => {
+                            this.#set(pos, pos);
+                            this.#textArea.focus();
+                            this.#add({
+                                type: "inline",
+                                value: `\n`,
+                            });
+                        }, 0);
+                    }
+                } else {
+                    const nextIsPaired = Object.values(this.#pairs).includes(next);
+                    const isSelected = this.#start !== this.#end;
+                    if (e.ctrlKey || e.metaKey) {
+                        if (this.#start === this.#end) {
+                            if (e.key === "c" || e.key === "x") {
+                                e.preventDefault();
+                                const { total, num, col } = this.#getLine();
+
+                                await navigator.clipboard.writeText(`${num === 0 && e.key === "x" ? "" : "\n"}${total[num]}`);
+
+                                if (e.key === "x") {
+                                    const pos = this.#start - col;
+                                    total.splice(num, 1);
+                                    this.value = total.join("\n");
+                                    setTimeout(() => this.#set(pos, pos), 0);
+                                }
+                            }
+                        }
+                    }
+
+                    if ((e.ctrlKey || e.metaKey) && e.key) {
+                        // TODO: handle shortkeys for example
+                    } else if (
+                        nextIsPaired &&
+                        (next === e.key || e.key === "ArrowRight") &&
+                        this.#opens.length &&
+                        !isSelected
+                    ) {
+                        e.preventDefault();
+                        this.#set(this.#start + 1, this.#end + 1);
+                        this.#opens.pop();
+                    } else if (e.key in this.#pairs) {
+                        e.preventDefault();
+                        this.#add({
+                            type: "wrap",
+                            value: e.key,
+                        });
+                        this.#opens.push(e.key);
+                    }
+                }
+            });
+
+            this.#textArea.addEventListener("dblclick", () => {
+                if (this.#start !== this.#end) {
+                    if (this.value[this.#start] === " ") {
+                        this.#set(this.#start + 1, this.#end);
+                    }
+                    if (this.value[this.#end - 1] === " ") {
+                        this.#set(this.#start, this.#end - 1);
+                    }
+                }
+            });
+
+            this.#textArea.addEventListener("click", () => (this.#opens = []));
+        }
+    }
+
+    const startsWithDash = (str: string | undefined) => {
+        return !!(str?.startsWith('- '));
+    };
+
+    const startsWithNumber = (str: string | undefined) => {
+        const result = str?.match(/^(\d+)\./);
+        return result ? Number(result[1]) : null;
+    };
+
+    const insert = (str: string, char: string, index: number) => {
+        return str.slice(0, index) + char + str.slice(index);
+    };
+
+    const remove = (str: string, index: number) => {
+        return str.slice(0, index) + str.slice(index + 1);
+    };
+}
\ No newline at end of file
diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditorJsRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditorJsRuntimeExtensions.cs
new file mode 100644
index 0000000000..795bce58c9
--- /dev/null
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditorJsRuntimeExtensions.cs
@@ -0,0 +1,14 @@
+namespace Bit.BlazorUI;
+
+internal static class BitMarkdownEditorJsRuntimeExtensions
+{
+    public static ValueTask BitMarkdownEditorInit(this IJSRuntime jsRuntime, string id, ElementReference element)
+    {
+        return jsRuntime.InvokeVoid("BitBlazorUI.MarkdownEditor.init", id, element);
+    }
+
+    public static ValueTask<string> BitMarkdownEditorGetValue(this IJSRuntime jsRuntime, string id)
+    {
+        return jsRuntime.Invoke<string>("BitBlazorUI.MarkdownEditor.getValue", id);
+    }
+}
diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss
index a58e1aa4f7..a5be6a9d53 100644
--- a/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Styles/extra-components.scss
@@ -3,6 +3,7 @@
 @import "../Components/DataGrid/Pagination/BitDataGridPaginator.scss";
 @import "../Components/ErrorBoundary/BitErrorBoundary.scss";
 @import "../Components/InfiniteScrolling/BitInfiniteScrolling.scss";
+@import "../Components/MarkdownEditor/BitMarkdownEditor.scss";
 @import "../Components/MarkdownViewer/BitMarkdownViewer.scss";
 @import "../Components/MessageBox/BitMessageBox.scss";
 @import "../Components/NavPanel/BitNavPanel.scss";
diff --git a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs
index 3f496563bf..eea0bb5368 100644
--- a/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs
+++ b/src/BlazorUI/Bit.BlazorUI/Components/BitComponentBase.cs
@@ -78,7 +78,7 @@ public BitDir? Dir
     public override Task SetParametersAsync(ParameterView parameters)
     {
         HtmlAttributes.Clear();
-        var parametersDictionary = ParametersCache ?? throw new InvalidOperationException();
+        var parametersDictionary = ParametersCache ?? new Dictionary<string, object?>(parameters.ToDictionary());
         foreach (var parameter in parametersDictionary!)
         {
             switch (parameter.Key)
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor
new file mode 100644
index 0000000000..010ddd47a0
--- /dev/null
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor
@@ -0,0 +1,21 @@
+@page "/components/markdowneditor"
+
+<PageOutlet Url="components/markdowneditor"
+            Title="MarkdownEditor"
+            Description="markdowneditor component of the bit BlazorUI components" />
+
+<DemoPage Name="MarkdownEditor"
+          Notes="To use this component, you need to install the `Bit.BlazorUI.Extras` nuget package, as described in the Optional steps of the Getting started page."
+          Description="BitMarkdownEditor is a SEO friendly Blazor wrapper around the famous markedjs library."
+          SecondaryNames="@(["MdViewer", "MD"])"
+          Parameters="componentParameters">
+    <DemoSection Title="Basic" RazorCode="@example1RazorCode" Id="example1">
+        <BitButton OnClick="GetValue">Get Value</BitButton>
+        <div style="margin-top:1rem;display:flex;flex-direction:row;gap:1rem;height:300px">
+            <BitMarkdownEditor @ref="editorRef" />
+            <pre style="width:100%">
+                @value
+            </pre>
+        </div>
+    </DemoSection>
+</DemoPage>
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs
new file mode 100644
index 0000000000..50c68a6101
--- /dev/null
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs
@@ -0,0 +1,22 @@
+namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Extras.MarkdownEditor;
+
+public partial class BitMarkdownEditorDemo
+{
+    private readonly List<ComponentParameter> componentParameters =
+    [
+    ];
+
+
+
+    private BitMarkdownEditor editorRef = default!;
+    private string? value;
+    private async Task GetValue()
+    {
+        value = await editorRef.GetValue();
+    }
+
+
+
+    private readonly string example1RazorCode = @"
+<BitMarkdownViewer Markdown=""@(""# Marked in the browser\n\nRendered by **marked**."")"" />";
+}
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss
new file mode 100644
index 0000000000..954a678855
--- /dev/null
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss
@@ -0,0 +1,7 @@
+::deep {
+    .advanced {
+        img {
+            max-width: 100%;
+        }
+    }
+}
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor
index d496fabc78..f78a206150 100644
--- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Home/ComponentsSection.razor
@@ -259,6 +259,9 @@
             <BitLink Href="/components/infinitescrolling" title="InfiniteScrolling" Style="display: flex">
                 <BitText Typography="BitTypography.Subtitle1">InfiniteScrolling</BitText>
             </BitLink>
+            <BitLink Href="/components/markdowneditor" title="MarkdownEditor" Style="display: flex">
+                <BitText Typography="BitTypography.Subtitle1">MarkdownEditor</BitText>
+            </BitLink>
             <BitLink Href="/components/markdownviewer" title="MarkdownViewer" Style="display: flex">
                 <BitText Typography="BitTypography.Subtitle1">MarkdownViewer</BitText>
             </BitLink>
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs
index 0df3a24cac..4e03ceb45a 100644
--- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Shared/MainLayout.razor.NavItems.cs
@@ -155,6 +155,7 @@ public partial class MainLayout
                 new() { Text = "DataGrid", Url = "/components/datagrid", AdditionalUrls = ["/components/data-grid"] },
                 new() { Text = "ErrorBoundary", Url = "/components/errorboundary" },
                 new() { Text = "InfiniteScrolling", Url = "/components/infinitescrolling" },
+                new() { Text = "MarkdownEditor", Url = "/components/markdowneditor", Description = "MdEditor" },
                 new() { Text = "MarkdownViewer", Url = "/components/markdownviewer", Description = "MdViewer, MD" },
                 new() { Text = "MessageBox", Url = "/components/messagebox" },
                 new() { Text = "NavPanel", Url = "/components/navpanel" },
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json
index 67619b86d9..b6e9834124 100644
--- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/compilerconfig.json
@@ -77,6 +77,12 @@
         "minify": { "enabled": false },
         "options": { "sourceMap": false }
     },
+    {
+        "outputFile": "Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.css",
+        "inputFile": "Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss",
+        "minify": { "enabled": false },
+        "options": { "sourceMap": false }
+    },
     {
         "outputFile": "Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.css",
         "inputFile": "Pages/Components/Extras/MarkdownViewer/BitMarkdownViewerDemo.razor.scss",

From b3a11034f4d96fa13429b849f856c432e214bde9 Mon Sep 17 00:00:00 2001
From: Saleh Yusefnejad <msynk@outlook.com>
Date: Mon, 17 Mar 2025 17:04:02 +0330
Subject: [PATCH 2/2] fix review

---
 .../MarkdownEditor/BitMarkdownEditor.razor.cs     |  3 +++
 .../MarkdownEditor/BitMarkdownEditorDemo.razor    |  2 +-
 .../MarkdownEditor/BitMarkdownEditorDemo.razor.cs | 15 ++++++++++++++-
 .../BitMarkdownEditorDemo.razor.scss              |  5 -----
 4 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs
index bd89745522..bff1b45cbb 100644
--- a/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs
+++ b/src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownEditor/BitMarkdownEditor.razor.cs
@@ -13,6 +13,9 @@ public partial class BitMarkdownEditor : BitComponentBase
 
 
 
+    /// <summary>
+    /// Returns the current value of the editor.
+    /// </summary>
     public async ValueTask<string> GetValue()
     {
         return await _js.BitMarkdownEditorGetValue(_Id);
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor
index 010ddd47a0..98ce765a56 100644
--- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor
@@ -9,7 +9,7 @@
           Description="BitMarkdownEditor is a SEO friendly Blazor wrapper around the famous markedjs library."
           SecondaryNames="@(["MdViewer", "MD"])"
           Parameters="componentParameters">
-    <DemoSection Title="Basic" RazorCode="@example1RazorCode" Id="example1">
+    <DemoSection Title="Basic" RazorCode="@example1RazorCode" CsharpCode="@example1CsharpCode" Id="example1">
         <BitButton OnClick="GetValue">Get Value</BitButton>
         <div style="margin-top:1rem;display:flex;flex-direction:row;gap:1rem;height:300px">
             <BitMarkdownEditor @ref="editorRef" />
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs
index 50c68a6101..743aa373b8 100644
--- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.cs
@@ -18,5 +18,18 @@ private async Task GetValue()
 
 
     private readonly string example1RazorCode = @"
-<BitMarkdownViewer Markdown=""@(""# Marked in the browser\n\nRendered by **marked**."")"" />";
+<BitButton OnClick=""GetValue"">Get Value</BitButton>
+<div style=""margin-top:1rem;display:flex;flex-direction:row;gap:1rem;height:300px"">
+    <BitMarkdownEditor @ref=""editorRef"" />
+    <pre style=""width:100%"">
+        @value
+    </pre>
+</div>";
+    private readonly string example1CsharpCode = @"
+private BitMarkdownEditor editorRef = default!;
+private string? value;
+private async Task GetValue()
+{
+    value = await editorRef.GetValue();
+}";
 }
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss
index 954a678855..e4c5a61f90 100644
--- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss
+++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Extras/MarkdownEditor/BitMarkdownEditorDemo.razor.scss
@@ -1,7 +1,2 @@
 ::deep {
-    .advanced {
-        img {
-            max-width: 100%;
-        }
-    }
 }