diff --git a/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.cs b/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.cs index 58aadcc..073c90d 100644 --- a/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.cs +++ b/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.cs @@ -5,6 +5,8 @@ namespace Tizzani.MudBlazor.HtmlEditor; public sealed partial class MudHtmlEditor : IAsyncDisposable { + private readonly MudHtmlEditorOptions _options = new(); + private DotNetObjectReference? _dotNetRef; private IJSObjectReference? _quill; private ElementReference _toolbar; @@ -23,7 +25,7 @@ public sealed partial class MudHtmlEditor : IAsyncDisposable public bool Outlined { get; set; } = true; /// - /// The placeholder text to display when the editor has not content. + /// The placeholder text to display when the editor has no content. /// [Parameter] public string Placeholder { get; set; } = "Tell your story..."; @@ -52,6 +54,18 @@ public sealed partial class MudHtmlEditor : IAsyncDisposable [Parameter] public EventCallback TextChanged { get; set; } + /// + /// When true, ol elements containing li elements with data-list="bullet" will be replaced with ul elements. + /// Default value is true. Set to false to revert to previous behavior. + /// + [Obsolete("This parameter was added to preserve backwards compatibility, but will be removed in a future version.")] + [Parameter] + public bool ReplaceOrderedWithUnorderedListTag + { + get => _options.SanitizeHtml; + set => _options.SanitizeHtml = value; + } + /// /// Whether or not the user can resize the editor. Default value is . /// @@ -114,7 +128,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) _dotNetRef = DotNetObjectReference.Create(this); await using var module = await JS.InvokeAsync("import", "./_content/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.js"); - _quill = await module.InvokeAsync("createQuillInterop", _dotNetRef, _editor, _toolbar, Placeholder); + _quill = await module.InvokeAsync("createQuillInterop", _dotNetRef, _editor, _toolbar, Placeholder, _options); await SetHtml(Html); @@ -152,4 +166,9 @@ async ValueTask IAsyncDisposable.DisposeAsync() _dotNetRef?.Dispose(); _dotNetRef = null; } +} + +public sealed class MudHtmlEditorOptions +{ + public bool SanitizeHtml { get; set; } = true; } \ No newline at end of file diff --git a/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.js b/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.js index 974d5b6..34bb154 100644 --- a/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.js +++ b/src/Tizzani.MudBlazor.HtmlEditor/MudHtmlEditor.razor.js @@ -16,7 +16,7 @@ try { Quill.register('modules/blotFormatter', QuillBlotFormatter.default); } catch { } -export function createQuillInterop(dotNetRef, editorRef, toolbarRef, placeholder) { +export function createQuillInterop(dotNetRef, editorRef, toolbarRef, placeholder, options) { var quill = new Quill(editorRef, { modules: { toolbar: { @@ -27,22 +27,19 @@ export function createQuillInterop(dotNetRef, editorRef, toolbarRef, placeholder placeholder: placeholder, theme: 'snow' }); - return new MudQuillInterop(dotNetRef, quill, editorRef, toolbarRef); + return new MudQuillInterop(dotNetRef, quill, editorRef, toolbarRef, options); } export class MudQuillInterop { - /** - * @param {Quill} quill - * @param {Element} editorRef - * @param {Element} toolbarRef - */ - constructor(dotNetRef, quill, editorRef, toolbarRef) { + + constructor(dotNetRef, quill, editorRef, toolbarRef, options) { quill.getModule('toolbar').addHandler('hr', this.insertDividerHandler); quill.on('text-change', this.textChangedHandler); this.dotNetRef = dotNetRef; this.quill = quill; this.editorRef = editorRef; this.toolbarRef = toolbarRef; + this.options = options; } getText = () => { @@ -50,12 +47,69 @@ export class MudQuillInterop { }; getHtml = () => { - return this.quill.root.innerHTML; + const html = this.quill.root.innerHTML; + return this.options.sanitizeHtml ? this.getSanitizedHtml(html) : html; }; + + getSanitizedHtml = (html) => { - setHtml = (html) => { - this.quill.root.innerHTML = html; - } + // Parse the HTML into a DOM + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + + // Find all
    elements + doc.querySelectorAll('ol').forEach(ol => { + + const hasBullets = Array.from(ol.querySelectorAll('li')).some( + li => li.getAttribute('data-list') === 'bullet' + ); + + if (hasBullets) { + // Create a new
      element + const ul = doc.createElement('ul'); + + // Move children from
        to
          + while (ol.firstChild) { + ul.appendChild(ol.firstChild); + } + + // Replace
            with
              + ol.replaceWith(ul); + } + }); + + return doc.body.innerHTML; + }; + + getQuillHtml = (html) => { + + // Parse the HTML into a DOM + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + + // Find all
                elements + doc.querySelectorAll('ul').forEach(ul => { + + const hasBullets = Array.from(ul.querySelectorAll('li')).some( + li => li.getAttribute('data-list') === 'bullet' + ); + + if (hasBullets) { + // Create a new
                  element + const ol = doc.createElement('ol'); + + // Move children from
                    to
                      + while (ul.firstChild) { + ol.appendChild(ul.firstChild); + } + + // Replace
                        with
                          + ul.replaceWith(ol); + } + }); + + return doc.body.innerHTML; + }; insertDividerHandler = () => { const range = this.quill.getSelection(); @@ -65,14 +119,12 @@ export class MudQuillInterop { } }; - /** - * - * @param {Delta} delta - * @param {Delta} oldDelta - * @param {any} source - */ + setHtml = (html) => { + this.quill.root.innerHTML = this.options.sanitizeHtml ? this.getQuillHtml(html) : html; + } + textChangedHandler = (delta, oldDelta, source) => { this.dotNetRef.invokeMethodAsync('HandleHtmlContentChanged', this.getHtml()); this.dotNetRef.invokeMethodAsync('HandleTextContentChanged', this.getText()); }; -} \ No newline at end of file +} diff --git a/src/Tizzani.MudBlazor.HtmlEditor/Tizzani.MudBlazor.HtmlEditor.csproj b/src/Tizzani.MudBlazor.HtmlEditor/Tizzani.MudBlazor.HtmlEditor.csproj index f462726..ea380c6 100644 --- a/src/Tizzani.MudBlazor.HtmlEditor/Tizzani.MudBlazor.HtmlEditor.csproj +++ b/src/Tizzani.MudBlazor.HtmlEditor/Tizzani.MudBlazor.HtmlEditor.csproj @@ -9,7 +9,7 @@ https://github.com/erinnmclaughlin/MudBlazor.HtmlEditor LICENSE README.md - 2.3.0 + 2.3.1 A customizable HTML editor component for MudBlazor, powered by QuillJS. 2024 Erin McLaughlin https://github.com/erinnmclaughlin/MudBlazor.HtmlEditor