Blazor-Diagrams / Blazor.Diagrams

A fully customizable and extensible all-purpose diagrams library for Blazor
https://blazor-diagrams.zhaytam.com
MIT License
919 stars 176 forks source link

How can the key down event in the Diagram be suppressed from propagating into the child nodes? #328

Closed mrakgr closed 1 year ago

mrakgr commented 1 year ago

Repo: https://github.com/mrakgr/helix/blob/bae32b6b1c6ed609e7889fbcf46d1cc56124f046/Helix.Fun/Helix.Nodes.fs#L140

type HelixDiagramBase(Js : IJSRuntime, opts) as this =
    inherit Diagram(opts)

    let redo = Stack()
    let undo = Stack()

    let mutable is_loaded = false
    let mutable is_handler_active = true

    let handler_template f x =
        if is_handler_active then
            undo.Push(f x)
            redo.Clear()
    do 
        this.Nodes.add_Added (handler_template U_NodeAdded); this.Nodes.add_Removed (handler_template U_NodeRemoved)
        this.Links.add_Added (handler_template U_LinkAdded); this.Links.add_Removed (handler_template U_LinkAdded)

    do this.add_KeyDown (fun ev ->
        match ev.Key.ToLower() with
        | "z" when ev.CtrlKey && ev.ShiftKey -> this.Redo()
        | "z" when ev.CtrlKey -> this.Undo()
        | _ -> ()
        )

I need to suppress this keydown event from propagating after it has been handled, but I don't know how. The undo is causing an component to be selected inadvertently. Check out the video to see what I mean.

https://github.com/Blazor-Diagrams/Blazor.Diagrams/assets/6266635/5d38de2a-5db7-4434-a210-596a091529fc

zHaytam commented 1 year ago

In order to do what you want (a.k.a not have the browser undo too), you need to preventDefault on the shortcuts of the browser.

Check out https://stackoverflow.com/questions/39802159/disable-chromes-text-input-undo-redo-ctrlz-ctrly for an example

This isn't really possible in Blazor right now so your only solution is to do this in JS and prevent any shortcuts you don't want the browser to do, but you can still act on the events yourself in F# and do your Undo logic there.

mrakgr commented 1 year ago
        document.onkeydown = function (e) {
            if (e.ctrlKey && e.key === 'z') {
                e.preventDefault();
            }
        } 

I gave the above a try, but the problem with the solution you linked to is that it also disables the undo for the text component. I'd like to retain that functionality.

What are my options here. Should I be implementing my own onkeydown for the text field to get around this, or is there something better?

zHaytam commented 1 year ago
  1. Have your undo (CTRL+Z) only work after the diagram is selected (SelectionChanged event with false), then you modify your JS to only prevent default when the object is the diagram canvas
  2. Go the easy way and do this functionality using buttons? I thinks thats what most libraries do
mrakgr commented 1 year ago
        document.onkeydown = function (e) {
            if (e.ctrlKey && e.key.toLowerCase() === 'z' && e.target.tagName !== "TEXTAREA") {
                e.preventDefault()
            }
        }

Here is one solution that I am happy with. I just so noob at DOM manipulation, that it took me a lot of time to figure it out.

I do not really understand your solutions, but I'll think about them.

With buttons, I'd probably have to implement my own undo functionality for the text field. Alternatively, is it possible to trigger the undo on a text field from a button? I mean the inbuilt one. I am not sure how that could be possible with Blazor.

And for your first suggestion, do you mean the selection changed event on the diagram itself? Wouldn't that just give me a list of the selected components? How could I use that to prevent the default on the diagram?

Or did you mean @onselectionchanged on some different component? The docs say that that is for when a text selection has changed. The isn't much in the object args.

mrakgr commented 1 year ago

And for your first suggestion, do you mean the selection changed event on the diagram itself? Wouldn't that just give me a list of the selected components? How could I use that to prevent the default on the diagram?

Oh, nevermind, I see what you mean. It has a bool property, that is for when the canvas is selected, right? So I could use this to set a global variable on the JS side, and have it act as a switch in the event handler, I think that is what you mean.