[Feature-2050] Perspective onMouseUp and onMouseLeave don't catch all situations

I ran into a problem using a slider to select a time of day for a chart query binding. Wanging the slider caused frequent chart binding refreshes which seemed to cause a “too many connections” problem with MySQL and the XY chart was taking quite a while to catch up.

I’ve been trying to come up with a way of delaying the refresh until the onMouseUp event but that doesn’t work if the mouse is released outside the component. I added the onMouseOut event which improves the situation but there are still a few escapes. I created this little demo where the blue bar follows the slider but the green bar should only update when the adjustment is complete.

Slider mouseUp

Note that moving the mouse out diagonally leaves the green out of sync with the blue.

Drop this into a Perspective coordinate view root container to reproduce.
[
  {
    "type": "ia.input.slider",
    "version": 0,
    "props": {
      "value": 7,
      "max": 10
    },
    "meta": {
      "name": "Slider"
    },
    "position": {
      "x": 37,
      "y": 106,
      "height": 32,
      "width": 192
    },
    "custom": {
      "mouseEventTime": {
        "$": [
          "ts",
          192,
          1653630967417
        ],
        "$ts": 1653630967417
      }
    },
    "propConfig": {
      "custom.labelClearTimeDelay": {
        "binding": {
          "type": "expr",
          "config": {
            "expression": "(now(100) - {this.custom.mouseEventTime}) > 1000"
          }
        }
      }
    },
    "events": {
      "dom": {
        "onMouseLeave": {
          "type": "script",
          "scope": "G",
          "config": {
            "script": "\tself.custom.mouseEventTime = system.date.now()\n\tsystem.perspective.sendMessage(\"refreshStartTime\", payload = {\"startTime\": self.props.value}, scope = 'view')\n\tself.getSibling(\"lblMouseEvents\").props.text = \"onMouseLeave\"\n"
          }
        },
        "onMouseUp": {
          "type": "script",
          "scope": "G",
          "config": {
            "script": "\tself.custom.mouseEventTime = system.date.now()\n\tsystem.perspective.sendMessage(\"refreshStartTime\", payload = {\"startTime\": self.props.value}, scope = 'view')\n\tself.getSibling(\"lblMouseEvents\").props.text = \"onMouseUp\"\n\t"
          }
        }
      }
    }
  },
  {
    "type": "ia.display.label",
    "version": 0,
    "props": {
      "textStyle": {
        "borderColor": "--neutral-20",
        "borderStyle": "solid",
        "borderWidth": 1,
        "textAlign": "center"
      }
    },
    "meta": {
      "name": "lblMouseEventsDisplay"
    },
    "position": {
      "x": 40,
      "y": 154,
      "height": 32,
      "width": 191
    },
    "custom": {},
    "propConfig": {
      "props.text": {
        "binding": {
          "type": "expr",
          "config": {
            "expression": "if({../Slider.custom.labelClearTimeDelay}, \" - \", {../lblMouseEvents.props.text})"
          }
        }
      }
    }
  },
  {
    "type": "ia.display.progress",
    "version": 0,
    "props": {
      "max": 10,
      "valueDisplay": {
        "enabled": true
      }
    },
    "meta": {
      "name": "progLive"
    },
    "position": {
      "x": 37,
      "y": 70,
      "height": 20,
      "width": 200
    },
    "custom": {
      "lastAdjustTime": "value"
    },
    "propConfig": {
      "props.value": {
        "binding": {
          "type": "property",
          "config": {
            "path": "../Slider.props.value"
          }
        }
      }
    }
  },
  {
    "type": "ia.display.progress",
    "version": 0,
    "props": {
      "max": 10,
      "value": 7,
      "bar": {
        "color": "#12A30D"
      },
      "valueDisplay": {
        "enabled": true
      }
    },
    "meta": {
      "name": "progDelayed"
    },
    "position": {
      "x": 37,
      "y": 35,
      "height": 20,
      "width": 200
    },
    "custom": {},
    "scripts": {
      "customMethods": [],
      "messageHandlers": [
        {
          "messageType": "refreshStartTime",
          "script": "\tself.props.value = payload[\"startTime\"]",
          "sessionScope": false,
          "pageScope": false,
          "viewScope": true
        }
      ],
      "extensionFunctions": null
    }
  },
  {
    "type": "ia.display.label",
    "version": 0,
    "props": {
      "text": "onMouseLeave",
      "textStyle": {
        "textAlign": "center"
      }
    },
    "meta": {
      "name": "lblMouseEvents"
    },
    "position": {
      "x": 41,
      "y": 192,
      "height": 32,
      "width": 191
    },
    "custom": {}
  }
]

Has anyone got any ideas how to make this more robust?

The snippet you provided is non-functional when pasting into a View.

Oops! Fixed, thanks.

The “best” way to handle this would be to stop user interaction when they leave the component, but Perspective doesn’t currently have the mechanism to “evict” users from an input. We do have an open feature, but it’s been a low-priority feature for so long that I forgot I even requested it in the first place. I’ve marked on the feature that at least one user would benefit from having access to a .blur() component event.

1 Like

Could you add another vote to that? Sometimes I don’t want equipment to react to anything but the end point that the user finally selects on the slider. Or is there a place I can go to give it an upvote?

It’s an internal ticket. I’ve noted your additional interest in the feature.