tinymce / tinymce-blazor

Blazor integration
MIT License
44 stars 12 forks source link

Set dynamically height of editor acording to content for read only mode. #69

Open magiino opened 1 year ago

magiino commented 1 year ago

I have integrated TinyMCE editor 6 to .NET 7 blazor web assembly app according to official doc. https://www.tiny.cloud/docs/tinymce/6/blazor-ref/ https://www.tiny.cloud/docs/tinymce/6/blazor-cloud/

Nuget packages:

<PackageReference Include="TinyMCE" Version="6.4.2" />
<PackageReference Include="TinyMCE.Blazor" Version="1.0.4" />

I'm using TinyMCE Editor component also for 'read only' mode and I have issue with setting dynamically height of editor acording to content.

I'm using currently autoresize plugin, but it has some bug and it not always resize editor correctly. Sometimes I can see just first 3 lines of text. This bug happens totally randomly.

This is how it looks like if it will not resize correctly. image

This is how it looks like if it will resize correctly. image

<MudCard>
      <MudCardContent>
            <Editor
                Value="@_html"
                Conf="@_editorReadOnlyConfig"
                Disable=true
                ScriptSrc="/lib/tinymce/tinymce.min.js"
            />
     </MudCardContent>
</MudCard>
    private readonly Dictionary<string, object> _editorReadOnlyConfig = new()
    {
        { "plugins", new string[] { "autoresize" } }, // https://www.tiny.cloud/docs/plugins/opensource/autoresize/
        { "toolbar", false },
        { "menubar", false },
        { "statusbar", false },
    };

Any ideas how to deal with this? Can i do the same thing just with css? I'm using Editor component in readonly mode to view html because the blazor app will break all css if i would render the html as MarkupString.

Thank you!

TinyITAdmin commented 1 year ago

Ref: INT-3183

magiino commented 1 year ago

I hacked it by invoking tiny mce command 'autoResizeCommand' 500ms after blazor component with editor is initialized. Which is really nasty fix. See code below.

Can you add to component event which will fire after content is loaded or something like that? I think calling StateHasChanged() after content is loaded is sufficient.

@using TinyMCE.Blazor

<Editor Value="@HtmlString"
        Conf="@_editorReadOnlyConfig"
        Disable=true
        ScriptSrc="/lib/tinymce/tinymce.min.js" />
public partial class ReadonlyEditor
{
    private readonly Dictionary<string, object> _editorReadOnlyConfig = new()
    {
        { "plugins", new string[] { "autoresize" } }, // https://www.tiny.cloud/docs/plugins/opensource/autoresize/
        { "toolbar", false },
        { "menubar", false },
        { "statusbar", false },
    };

    [Inject]
    public IJSRuntime JSRuntime { get; set; } = default!;

    [Parameter, EditorRequired]
    public string HtmlString { get; set; } = default!;

    protected async override Task OnInitializedAsync()
    {
        await Task.Delay(500);
        await JSRuntime.InvokeVoidAsync("autoResizeCommand");
        await base.OnInitializedAsync();
    }
}
window.autoResizeCommand = () => {
    const global = typeof window !== 'undefined' ? window : global;

    try {
        global.tinymce.activeEditor.execCommand('mceAutoResize');
    } catch (e) {
        console.log(e);
    }
}
magiino commented 1 year ago

Okey, Can you add to component event which will fire after content is loaded or something like that? I think calling StateHasChanged() after content is loaded is sufficient. this was too abcisious claim :D this is not true. It works just with executing autoResizeCommand command after 500MS. Can I do this with some tiny mce event?

I have tried this but it did not worked:

window.editorEventsConfiguration = {
     setup: (editor) => {
        editor.on('LoadContent', (e) => {
            console.log('The Editor has load content.');
            try {
                editor.activeEditor.execCommand('mceAutoResize');
            } catch (e) {
                console.log(e)
            }
        });
    },
   init_instance_callback: (editor) => {
       editor.on('ExecCommand', (e) => {
           console.log(`The ${e.command} command was fired.`);
        });
        editor.on('LoadContent', (e) => {
            console.log('AAAAAAAAAThe Editor has initialized.');
            editor.activeEditor.execCommand('mceAutoResize');
       });
    }]
}
@using TinyMCE.Blazor

<Editor Id="@_editorId"
        Value="@HtmlString"
        Conf="@_editorReadOnlyConfig"
        JsConfSrc="editorEventsConfiguration"
        Disable=true
        ScriptSrc="/lib/tinymce/tinymce.min.js" />
magiino commented 1 year ago

This code throws

window.editorEventsConfiguration = {
    setup: (editor, tinyEventHandler) => {
        tinyEventHandler.bindEvent(editor, 'Load', (e) => {
            try {
                editor.execCommand('mceAutoResize');
            } catch (e) {
                console.log(e);
                console.log('Load');
            }
        });
        tinyEventHandler.bindEvent(editor, 'LoadContent', (e) => {
            try {
                console.log(editor)
                editor.execCommand('mceAutoResize');
            } catch (e) {
                console.log(e);
                console.log('LoadContent');
            }
        });
        tinyEventHandler.bindEvent(editor, 'ScriptsLoaded', (e) => {
            try {
                console.log('ScriptsLoaded Enter');
                console.log(editor)
                editor.execCommand('mceAutoResize');
            } catch (e) {
                console.log(e);
                console.log('ScriptsLoaded');
            }
            const global = typeof window !== 'undefined' ? window : global;

            try {
                global.tinymce.activeEditor.execCommand('mceAutoResize');
                console.log("global done");

            } catch (e) {
                console.log(e);
                console.log("global");
            }

        });
    },
}

this error: image

ScriptsLoaded Enter
main.min.js:1 kT {plugins: {…}, contentCSS: Array(0), contentStyles: Array(0), loadedCSS: {…}, isNotDirty: true, …}
main.min.js:1 TypeError: Cannot read properties of undefined (reading 'getRng')
    at tinymce.min.js:4:382988
    at tinymce.min.js:4:383369
    at mceFocus (tinymce.min.js:4:383375)
    at $O.execCommand (tinymce.min.js:4:383961)
    at kT.execCommand (tinymce.min.js:4:411066)
    at kT.focus (tinymce.min.js:4:410376)
    at $O.execCommand (tinymce.min.js:4:383820)
    at kT.execCommand (tinymce.min.js:4:411066)
    at kT.<anonymous> (main.min.js:1:543)
    at JO.dispatch (tinymce.min.js:4:386989)
main.min.js:1 ScriptsLoaded
main.min.js:1 TypeError: Cannot read properties of undefined (reading 'getRng')
    at tinymce.min.js:4:382988
    at tinymce.min.js:4:383369
    at mceFocus (tinymce.min.js:4:383375)
    at $O.execCommand (tinymce.min.js:4:383961)
    at kT.execCommand (tinymce.min.js:4:411066)
    at kT.focus (tinymce.min.js:4:410376)
    at $O.execCommand (tinymce.min.js:4:383820)
    at kT.execCommand (tinymce.min.js:4:411066)
    at kT.<anonymous> (main.min.js:1:696)
    at JO.dispatch (tinymce.min.js:4:386989)
main.min.js:1 global
main.min.js:1 TypeError: Cannot read properties of undefined (reading 'getRng')
    at tinymce.min.js:4:382988
    at tinymce.min.js:4:383369
    at mceFocus (tinymce.min.js:4:383375)
    at $O.execCommand (tinymce.min.js:4:383961)
    at kT.execCommand (tinymce.min.js:4:411066)
    at kT.focus (tinymce.min.js:4:410376)
    at $O.execCommand (tinymce.min.js:4:383820)
    at kT.execCommand (tinymce.min.js:4:411066)
    at kT.<anonymous> (main.min.js:1:237)
    at JO.dispatch (tinymce.min.js:4:386989)
main.min.js:1 Load
main.min.js:1 kT {plugins: {…}, contentCSS: Array(2), contentStyles: Array(0), loadedCSS: {…}, isNotDirty: true, …}

Any ideas how to force editor resize? How is possible that https://github.com/tinymce/tinymce-blazor/issues/69#issuecomment-1591996388 this works, but this events does not works?

magiino commented 1 year ago

Okey I hacked this bug with better solution. I try to execute every 100 ms autoResizeCommand command until it succeeds.

    protected async override Task OnInitializedAsync()
    {
        bool success;
        do
        {
            await Task.Delay(100);
            success = await JSRuntime.InvokeAsync<bool>("");
        }
        while (!success);

        await base.OnInitializedAsync();
    }
window.autoResizeCommand = () => {
    try {
        const global = typeof window !== 'undefined' ? window : global;
        if (global.tinymce && global.tinymce.activeEditor) {
            global.tinymce.activeEditor.execCommand('mceAutoResize');
            return true;
        }
        return false;
    } catch (e) {
        return false;
    }
}