Contextmenu location

Hi folks,

Currently the node-red-ui-contextmenu node can be displayed at a location that is specified:

  • In the input message. For example when you click on an SVG shape, the node-red-contrib-ui-svg node sends the mouse-click coordinates in the output message, which can be used by the contextmenu node to show the menu nicely where the mouse has being clicked.
  • In the config screen as a fixed location (x and y coordinates). For example when a dashboard button is being clicked, no coordinates will be available in the output message (so you need to used a fixed location).

@Paul-Reed has a problem with following flow:

image

[{"id":"b3e815dc.72d658","type":"ui_context_menu","z":"434e5cf6.9faa04","group":"b6d02685.824a58","order":17,"width":0,"height":0,"fontSize":16,"position":"fixed","outputField":"payload","xCoordinate":"125","yCoordinate":"118","menu":"fixed","menuItems":[{"id":"1","icon":"fa-arrow-right","label":"Realtime Power","topic":"dash","payload":"Realtime Power","payloadType":"str","visible":true,"enabled":true},{"id":"2","icon":"fa-heart","label":"Historical Energy","topic":"dash","payload":"Historical Energy","payloadType":"str","visible":true,"enabled":true}],"colors":"native","textColor":"#000000","backgroundColor":"#ffffff","borderColor":"#626262","intervalLength":"0","intervalUnit":"secs","startTimerAtOpen":true,"startTimerAtLeave":true,"stopTimerAtEnter":true,"name":"Menu","x":670,"y":280,"wires":[[]]},{"id":"721ee856.4907d8","type":"ui_button","z":"434e5cf6.9faa04","name":"","group":"b6d02685.824a58","order":2,"width":2,"height":1,"passthru":false,"label":"","tooltip":"","color":"","bgcolor":"#2A2A2A","icon":"fa-navicon fa-2x","payload":"","payloadType":"str","topic":"","x":510,"y":280,"wires":[["b3e815dc.72d658","e69ef5e8.3eda68"]]},{"id":"1440d7ac.5854a8","type":"ui_button","z":"434e5cf6.9faa04","name":"","group":"b6d02685.824a58","order":4,"width":2,"height":1,"passthru":false,"label":"My button","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":520,"y":240,"wires":[[]]},{"id":"e69ef5e8.3eda68","type":"debug","z":"434e5cf6.9faa04","name":"Coordinate test","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":700,"y":340,"wires":[]},{"id":"b6d02685.824a58","type":"ui_group","z":0,"name":"flowtest","tab":"eb2b42a7.665b9","disp":true,"width":22,"collapse":false},{"id":"eb2b42a7.665b9","type":"ui_tab","z":0,"name":"Test","icon":"dashboard","order":6}]

When the button is clicked, a message (without coordinates) is being send to the contextmenu node. The contextmenu node shows the menu at a fixed location. But when the screen is resized, the menu is not drawn on top of the button anymore:

contextmenu_location

I 'think' are two valid solutions for this problem.

  1. The contextmenu node currently displays the menu at clientx and clienty:

    I could add an extra dropdown to the config screen (in case 'fixed coordinates' are selected) with options: "client coordinates" and "offset coordinates".

  2. The dashboard button node could send the clicked coordinates (and perhaps if possible the bounding box as suggested in this discussion):

I will implement the first solution anyway, but the second solution would be much easier to use. But not sure if our friend @dceejay will agree with this proposal, since his impact analyses are always better than mine ...

Thanks for any suggestions!!
Bart

5 Likes

BTW @Christian-Me had also a third solution (see discussion):

But I have never used the ui_control node, so I cannot judge about this one ...

2 Likes

Verry useful node. Currently emits a message when a client connects or looses a connection to the dashboard. And when the user of the dashboard changes tabs.

For our problem I would wish for another option: message when mouse click left, right, hold, released, clicked, taped (...) containing an object with the absolute and relative coordinates, the coordinates of the widget under the mouse and a widget identifier or the position in the dashboard tree (tab/group/widget)

1 Like

As always - Pull Requests are welcome. But all this does sound like an awful lot of extra traffic from front end to back and back again... Most context menu type implementations are front end only.

Admittedly the Node-RED style is that everything goes via the flow on the server - but adding all these events seems like an awful lot of plumbing and traffic for a small use case.

Would it not be possible to add a mouse click listener to the contextmenu node itself so it can handle any right clicks itself - and as a result get accurate co-ordinates.

1 Like

Could perhaps do this, but currently they use a standard dashboard button. As soon as they left-mouse (or via touch?) click, the button sends a message to the Node-RED flow. There they can compose a contextmenu structure in a function node, and then have the contextmenu node to show that structure. In my personal opinion, the contextmenu node shouldn't mess up that nice process by intercepting events. But I may be mistaken...

Another Idea (and question if this could be feasible)
Assuming that is a niche application:
Make ui-control to accept a callback function via msg.ui_control.onMouseClick=“function (mouseInfo) {.... this.send(...)}”
The callback function can filter the relevant data on client side and only emit a message to the backend if useful.
Basically this is how I did it in my remote-device-table with ui-table/tabulator.

This could perhaps keep traffic to a minimum and no confusion options for normal users.

you mean apart from the security implications of allowing the sending of random bits of javascript into the page ?

1 Like

Yes! I know about the risks :heavy_check_mark:- proceed :wink:

But you are right I'm always concerned about risks even if I can`t evaluate the consequences in detail. Talking about risks ... I always question my self how can I know if in a nice contrib-node which works great opens a hidden backdoor of some kind or doing some nasty stuff inside the frontend :shushing_face:. I have the same concerns when I install a custom firmware or even use a library/framework in my ESP8266 projects: There is everything available WiFi SSID + password, network access .... And even when it is open source who reads and understand many thousands lines of c-code? :face_with_monocle:

But if you think there is a real risk and no way to "sandbox" it a way then the answer might be No :x:
Totally fine with that.

@BartButenaers so is this "only" a problem about the button widget ? As they are currently the only widgets that register an ng-click handler - I don't want to start adding to others. (OK the switch does but that doesn't make sense in this context).

If we do this then it would have to be a new msg property (msg.click ?) as we can't overload the existing payload without breaking things...

Well, to be honest I will only use the contextmenu myself in combination with the svg node. But I could understand that @Paul-Reed wanted to support a button, since - like you say - it already supports a click handler (so should be doable to send coordinates in e.g. your msg.click field).
But most probably someone will need a contextmenu in the future on his UI table widget, and so on... Therefore the proposal about the ui-control node made sense for me, since it would offer a general solution for all widgets. But I assume it would be very hard to implement. And like you say, you have to assure that your flow is not going to be overwhelmed with events.
Personally I think it is better to create a separate implementation for each widget, instead of a general solution:

  • We have done it already for the svg node.
  • @hotNipi has done it for his state trail node. Advantage is that he calculates and sends information about the clicked timeslice. That kind of information would never be available in a global click handler solution.
  • Somebody could do it now or the button node.
  • Somebody could do it in the future for the table node. It could e.g. contain the selected row nr and column nr ...
  • ...

Summarized I think a separate click handler in each widget could include very specific useful information, which is not available otherwise. So I vote for a simple msg.click on the button node...

Simple example to explain my statement. The state trail node shows a series of coloured timeslices. But in fact it is a single html element filled with a complex gradient. When you click on it, you want to know which timeslice has been clicked. With a general click handler, you will always get the same single html element (doesn't matter which timeslice is being clicked...). But currently the clicked timeslice is being calculated inside the widget, which is much better info ...

If it will be more widely used, the click metadata property name should be somehow unified.
I did it that way, but this is me and my already well known not so correct English.
image

Yes that would be nice, but then we would already have backwards compatibility issues. Because this is what we currently send for the SVG node:

"coordinates": {
   x: 195.3749237060547,
   y: 201.20571899414062
}
"elementId": "circle"
"event": "click"
"payload": {
   elementId: "cam3spin",
   status: "start"
}
"position": {x: 854, y: 284}
"selector": undefined
"topic": "circle"

P.S. We also send the "event" because we support a whole series of events... And the "elementId" is the id of the clicked element, since an SVG can contain LOT's of elements...

To put into context what I'm doing, it's to hide the dashboard's Nav menu, and then navigate by using the contextmenu node.
The contextmenu node cascades the menu items, and looks IMO much better than the Nav menu.
It's a work in progress, and not all pages have been built!

Cool !!!! :+1: :+1: :+1:
With a button it is simple: there is only one element, and that is being clicked. So a general click handler would also do the job for you. But for most other widgets, it seems to me that you wouldn't have enough information when you know which html element has been clicked. In those cases, you need to get more functional information from the widget about what/where you have clicked.

Yes, but when I view the same tab in different browsers, the menu appears at different screen locations. Example, the same tab on my phone.

So if I add position to the button msg that would help fix that ? But as noted there are now two formats of possible property. I think clickCoordinates is more self-explanatory vs the more generic position.
Thoughts

Yes I agree. But I'm not going to change my position field, to avoid breaking all flows.

That wasn't my suggestion, I'm just explaining my user case for the contextmenu node. The technicalities of a solution is above my pay grade unfortunately :thinking:

Agree.
May I ask to include the coordinates of that button too like x1.y1,x2,y2
Sooner or later somebody (like me) would like to position the menu aligned to the lower left corner regardless of the actual mouse position. Another question: should it respond to right click only or left clicks too? Because a right click on a button (hamburger menu) seems not to be intuitive?