Show div (absolute positon) next to cursor

I am already using the inline mode to show the toolbar for selected text. We would like to show another toolbar when Enter is typed. Froala is another editor we've used and you see how they do it here:
https://www.loom.com/share/8db0f627cb634ca0a21e432e1bdf259e?sid=afce55f1-fc62-43bc-8569-cc0dffe14db9

Currently, I created my own HTML and show it when OnActionBegin fires and RequestType == "EnterAction". This would work, but we would much rather have it positioned next to or under the cursor.

Questions:
1 - Is there a better solution? We thought about trying "Mention" integration, but it's only triggered when it's the first letter typed. Hitting Enter would be done at the end of a line.
2 - If there isn't a better solution? If not, is there any way to get the cursor location?

Video (what we've done so far)
https://www.loom.com/share/c668c60c6dea4a87bf6ad46f46670b8e?sid=aa55fa18-f70d-4703-b4ec-c2c8b30f2caa

Sample Project
Please see attached. Be sure to set SF License in in Program.cs


Attachment: SFIssues_53a34a91.zip


11 Replies

VJ Vinitha Jeyakumar Syncfusion Team February 19, 2025 07:17 AM UTC

Hi Brian Pautsch,


Your requirement to show the inline toolbar when clicking the enter key in the Editor can be achieved by using the ShowInlineToolbarAsync method, which brings the inline toolbar into view, offering quick access to frequently used editing options. Please refer to the modified code below,

Code snippet:
 
public async Task OnActionBegin(ActionBeginEventArgs args)
{
   if (args.RequestType == "EnterAction")
   {
       await _rteMessage.ShowInlineToolbarAsync();
    }
}

Attached the modified sample,


Regards,
Vinitha


Attachment: SFIssues_Solved_fe33c049.zip


BP Brian Pautsch February 19, 2025 01:38 PM UTC

Thanks for the quick reply. The only issue is the customer wants 2 different toolbars.

Toolbar #1: When text is highlighted. Shows Bold, Underline, etc.

Toolbar #2: When Enter is pressed. Shows only Insert Image, Video and Audio.

Are 2 toolbars possible?

Or could I swap out the tools dynamically? Something like: when OnActionBegin occurs, change toolbar to 3 actions. And then when maybe on key press (not enter), change toolbar back to BoldUnderline/etc.




BP Brian Pautsch February 19, 2025 04:02 PM UTC

Attached are some ideas I implemented. It's close, but not quite right.

Code Updates
- Razor: Added RichTextEditorEvents "ValueChange"
- CS: Created 
UpdateToolbar() to swap our toolbar config. Called in OnInitialized.
- CS: Both OnActionBegin() and ValueChangeHandler() call UpdateToolbar() to swap out the buttons.

Video Demo
https://www.loom.com/share/af5384db4a654751b228bb80b955486b?sid=771800a3-967a-49ee-89ed-cb61ee81bb09

Sample project attached.

Issues
- The inline toolbar displays at the end of the last line. If I type a long line and hit Enter, the inline toolbar shows at the far right but I would like it by the cursor (on the left). Could we fire a javascript method to show the inline toolbar? If needed, the javascript could execute after 100 ms to let the cursor get to the left first.
- When selecting text, the toolbar buttons don't swap back to the bigger set reliably. I think this is because ValueChange fires intermittently (not every key stroke) so there is a timing issue.

Any better solutions or suggestions?

Thanks!


Attachment: SFIssues_v2_79935adb.zip


VJ Vinitha Jeyakumar Syncfusion Team February 20, 2025 10:54 AM UTC

Hi Brian Pautsch,


Your reported issue can be resolved by switching to the bigger toolbar items using the QuickToolbarClosed event of the RichTextEditor. Please check the modified code and sample below,

Code snippet:
 
<SfRichTextEditor ID="rteMessageID" @ref="_rteMessage" SaveInterval="1"  @bind-Value="_postModel.Content" CssClass="w-100 h-100">
 
   <RichTextEditorEvents QuickToolbarClosed="QuickToolbarClosed"/>
 
</SfRichTextEditor>

@code {

public bool check = false;
public async Task OnActionBegin(ActionBeginEventArgs args)
{
   if (args.RequestType == "EnterAction")
   {
       //Update toolset
       
UpdateToolbar(false);

       //Show toolbar
       
       
await _rteMessage.ShowInlineToolbarAsync();

   
}
}

public void QuickToolbarClosed(QuickToolbarEventArgs args)
{
   if (check)
   {
       UpdateToolbar(true);
   }
}
public void ValueChangeHandler(Syncfusion.Blazor.RichTextEditor.ChangeEventArgs args)
{
   
   check = true;
 
}
}


Regards,
Vinitha

Attachment: SFIssues_v2_solved_d1680f5a.zip


BP Brian Pautsch February 20, 2025 02:11 PM UTC

QuickToolbarClosed is a much better solution. Thanks for the quick reply!


Is there any way to solve the other issue?

ISSUE: The inline toolbar displays at the end of the last line. If I type a long line and hit Enter, the inline toolbar shows at the far right but I would like it by the cursor (on the left).
Idea: Could we fire a javascript method to show the inline toolbar? If needed, the javascript could execute after 100 ms to let the cursor get to the left first. Any other ideas?

Froala Editor: this editor has a Quick Insert feature built-in and our customers like it a lot:
https://www.loom.com/share/e05dc3983ac8424eb4994bfaf03870c1?sid=5194609f-a036-4528-b0f9-da29597c74e1

Issue Demo: Here's a video of the issue happening in SF:
https://www.loom.com/share/05e1fd875b6d4b3288367717be5e9fa7?sid=4203ce33-583a-4aaa-9080-7dbb0c0a5dda

Thanks! Your team should have a link to take donations or at least let us buy you a coffee or beer. :)



BP Brian Pautsch February 20, 2025 02:28 PM UTC

Oh wait...I just realized I should move the ShowInlineToolbarAsync() call from OnActionBegin to  OnActionComplete. I did that...the code executes, but the Inline toolbar doesn't display.

BEFORE:
public async Task OnActionBegin(ActionBeginEventArgs args)
{
    if (args.RequestType == "EnterAction")
    {
        UpdateToolbar(false);
        
await _rteMessage.ShowInlineToolbarAsync();
    
}
}

AFTER:
public async Task OnActionComplete(ActionCompleteEventArgs args)

{
    if (args.RequestType == "EnterAction")
    {
        UpdateToolbar(false);
        
await _rteMessage.ShowInlineToolbarAsync();
    
}
}


Attachment: SFIssues_v3_e2e1f35e.zip


VJ Vinitha Jeyakumar Syncfusion Team February 21, 2025 10:13 AM UTC

Hi Brian Pautsch,


Issue : 1 "The inline toolbar displays at the end of the last line. If I type a long line and hit Enter, the inline toolbar shows at the far right but I would like it by the cursor (on the left)"

Your reported issue can be resolved by customizing the position of the quick toolbar popup using the OnQuickToolbarOpen event arguments. Please check the code and sample attached below,

Code snippet:

ModelEditor.razor
 
<div @oninput="UpdateCursorPosition" @onkeydown="UpdateCursorPosition" id="editableDiv">
 <SfRichTextEditor ID="editableDiv"  @ref="_rteMessage" SaveInterval="1" @bind-Value="_postModel.Content" CssClass="w-100 h-100">
     <RichTextEditorToolbarSettings Items="@_toolbar" />
     <RichTextEditorInlineMode Enable="true" ShowOnSelection="true" />
     <RichTextEditorEvents OnActionBegin="OnActionBegin" OnImageUploadSuccess="@OnImageUploadSuccess" FileUploadSuccess="@FileUploadSuccess"
                           OnQuickToolbarOpen="OnQuickToolbarOpen" QuickToolbarClosed="QuickToolbarClosed" />
     <RichTextEditorAudioSettings SaveUrl="@(_rteAudioSaveUrl)" Path="" />
     <RichTextEditorImageSettings SaveUrl="@(_rteImageSaveUrl)" Path="" />
     <RichTextEditorVideoSettings SaveUrl="@(_rteVideoSaveUrl)" Path="" MaxFileSize="1073741824" />
 </SfRichTextEditor>
 </div>


ModelEditor.razor.cs
 
public bool isEnter = false;
//Init

private int  CursorX;
private int  CursorY;

private async Task UpdateCursorPosition()
{
   if (isEnter)
   {
       var position = await JSRuntime.InvokeAsync<CursorPosition>("getCaretCoordinates",
           new object[]
               { await JSRuntime.InvokeAsync<IJSObjectReference>("document.getElementById", "editableDiv") });

       CursorX = position.X;
       CursorY = position.Y;
   }
}

public class CursorPosition
{
   public int X { get; set; }
   public int Y { get; set; }
}
public async Task OnQuickToolbarOpen(BeforeQuickToolbarOpenArgs args)
{
   
   if (isEnter)
   {
       await Task.Delay(100);
       args.PositionX = CursorX;
       args.PositionY = CursorY;
   }
 
}
public async Task OnActionBegin(ActionBeginEventArgs args)
{
   if (args.RequestType == "EnterAction")
   {
       isEnter = true;
       //Update toolset
       
UpdateToolbar(false);

       //Show toolbar
       
await _rteMessage.ShowInlineToolbarAsync();
   }
}


App.razor
 
window.getCaretCoordinates = (element) => {

   let sel = window.getSelection();
   if (sel.rangeCount > 0) {
       let range = sel.getRangeAt(0);
       let rect = range.getBoundingClientRect();
       let valX = parseInt(rect.left);
       let valY = parseInt(rect.top);
       if (rect.width === 0 && rect.height === 0) {
           let container = range.startContainer;
           if (container.nodeType === Node.ELEMENT_NODE && container.innerHTML === "<br>") {
               let parentRect = container.parentElement.getBoundingClientRect();
               let valX = parseInt(parentRect.left);
               let valY = parseInt((parentRect.height + parentRect.top) - 30);
               return { X: valX, Y: valY };
           }
       }
       return { x: valX, y: valY };
   }
};


Issue 2: "I should move the ShowInlineToolbarAsync() call from OnActionBegin to  OnActionComplete. I did that...the code executes, but the Inline toolbar doesn't display."

Could you please share us with your necessity to use OnActionComplete instead of OnActionBegin event to open the inline toolbar?

Regards,
Vinitha

Attachment: SFIssues_v3_solved_5ef9d173.zip


BP Brian Pautsch February 21, 2025 07:49 PM UTC

Thanks for the quick feedback and code work, but this doesn't work as requested.

You added a div and that made the code in "OnOpen" (args.MaxHeight = "75vh") not work anymore. When you added that, the Rich text Editor was much smaller. This seemed to work for you since the code is getting coordinates of the text area. See the video below. The code always puts the inline toolbar at the bottom of the Rich text Editor. We need it under the cursor location.

https://www.loom.com/share/ad7ecd12a3494ab7a0b60e9fbd99b122?sid=79627079-5ad3-4535-9bb7-bc011c3b6d53

Also, our solution is a Server App. Calling "UpdateCursorPosition" on every @oninput and @onkeydown is not good. This is much too chatty and causes the editor to lag.


SOLUTION?

Please see the attached code. We thought a good solution would be to call "ShowInlineToolbarAsync()" in "OnActionComplete". This means the cursor would already on the next line and the inline toolbar will show in the correct location. The code executes, but the inline toolbar never displays. Is this a bug?

openmodal.png


Thanks,
Brian


Attachment: SFIssues_v4_9598ef29.zip


VJ Vinitha Jeyakumar Syncfusion Team February 25, 2025 12:25 PM UTC

Hi Brian Pautsch,


We have considered the reported issue "Calling ShowInlineToolbarAsync from OnActionComplete event does not open the Quick toolbar in RichTextEditor " as a bug from our end and the fix for the issue will be included with our upcoming patch release on the mid of March 2025.


Disclaimer: “Inclusion of this solution in the weekly release may change due to other factors including but not limited to QA checks and works reprioritization.”

Regards,
Vinitha



BP Brian Pautsch February 25, 2025 02:24 PM UTC

Great! Thanks for the update.



KP Kokila Poovendran Syncfusion Team March 26, 2025 09:48 AM UTC

Hi Brian Pautsch,

Query: Calling ShowInlineToolbarAsync from OnActionComplete event does not open the Quick toolbar in RichTextEditor

 

Feedback: Calling ShowInlineToolbarAsync from OnActionComplete event does not open the Quick toolbar in RichTextEditor in Blazor | Feedback Portal

 

We have included the fix for the issue "Calling ShowInlineToolbarAsync from OnActionComplete event does not open the Quick toolbar in RichTextEditor" with our package version 28.2.11. So, can you please upgrade your package to the latest to resolve the issue from your end?


Root Cause

When the Enter key is pressed, a new line is created, moving the cursor to an empty paragraph or line without any text. Since getClientRects() requires a visible element or text node to determine coordinates, calling it on an empty range results in an invalid position, causing the issue.


Release Notes: 


Loader.
Up arrow icon