diff --git a/js/webui/src/elements.js b/js/webui/src/elements.js
index 429fb017..5ae0de59 100644
--- a/js/webui/src/elements.js
+++ b/js/webui/src/elements.js
@@ -2,6 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import spriteSvg from 'open-iconic/sprite/sprite.svg'
import { makeClassName } from './dom_utils.js';
+import { bindHandlers } from './utils.js';
function makeClickHandler(callback)
{
@@ -69,6 +70,99 @@ Button.propTypes = {
active: PropTypes.bool
};
+const repeatInterval = 500;
+
+function isLeftButtonOrTouch(e)
+{
+ return e.button === 0 || typeof e.touches !== 'undefined';
+}
+
+export class RepeatingButton extends React.PureComponent
+{
+ constructor(props)
+ {
+ super(props);
+ this.intervalId = null;
+ this.hasInitialClick = false;
+ bindHandlers(this);
+ }
+
+ handleTimer()
+ {
+ this.hasInitialClick = true;
+ this.props.onClick();
+ }
+
+ handleClick(e)
+ {
+ if (e.button !== 0)
+ return;
+
+ e.preventDefault();
+
+ if (!this.hasInitialClick)
+ this.props.onClick();
+ }
+
+ componentDidUpdate(prevProps, prevState, snapshot)
+ {
+ if (prevProps.onClick === this.props.onClick || !this.intervalId)
+ return;
+
+ clearInterval(this.intervalId);
+ this.intervalId = setInterval(this.handleTimer, repeatInterval);
+ }
+
+ handleStart(e)
+ {
+ if (!isLeftButtonOrTouch(e) || this.intervalId)
+ return;
+
+ this.hasInitialClick = false;
+ this.intervalId = setInterval(this.handleTimer, repeatInterval);
+ }
+
+ handleEnd(e)
+ {
+ if (!isLeftButtonOrTouch(e) || !this.intervalId)
+ return;
+
+ clearInterval(this.intervalId);
+ this.intervalId = null;
+ }
+
+ render()
+ {
+ const { name, title, className, active } = this.props;
+
+ const fullClassName = 'button'
+ + (className ? ' ' + className : '')
+ + (active ? ' active' : '');
+
+ return (
+
+
+
+ );
+ }
+}
+
+RepeatingButton.propTypes = {
+ name: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ className: PropTypes.string,
+ onClick: PropTypes.func.isRequired,
+ active: PropTypes.bool
+};
+
export function Menu(props)
{
const { children } = props;
diff --git a/js/webui/src/volume_control.js b/js/webui/src/volume_control.js
index 72cd1173..1096bb18 100644
--- a/js/webui/src/volume_control.js
+++ b/js/webui/src/volume_control.js
@@ -1,6 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
-import { Button } from './elements.js'
+import { Button, RepeatingButton } from './elements.js';
import { bindHandlers, dbToLinear, linearToDb } from './utils.js'
import ModelBinding from './model_binding.js';
import { DropdownButton } from './dropdown.js';
@@ -54,7 +54,7 @@ class VolumeControl_ extends React.PureComponent
this.context.playerModel.volumeUp();
}
- handleVolumeDown(e)
+ handleVolumeDown()
{
this.context.playerModel.volumeDown();
}
@@ -84,8 +84,8 @@ class VolumeControl_ extends React.PureComponent
{
isUpDown
? <>
-