From 138055141d66c2f9d9eaf2483c31066fc6e635e5 Mon Sep 17 00:00:00 2001 From: Hyperblast Date: Wed, 19 Feb 2025 14:46:40 +0500 Subject: [PATCH 1/5] introduce RepeatingButton, use for up-down volume control --- js/webui/src/elements.js | 103 ++++++++++++++++++++++++++++++++- js/webui/src/volume_control.js | 8 +-- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/js/webui/src/elements.js b/js/webui/src/elements.js index 429fb017..9b83d668 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) { @@ -40,7 +41,7 @@ Icon.propTypes = { className: PropTypes.string, }; -export const Button = React.forwardRef(function Button(props, ref) +function Button_(props, ref) { const { name, title, className, href, onClick, active } = props; @@ -58,9 +59,9 @@ export const Button = React.forwardRef(function Button(props, ref) ); -}); +} -Button.propTypes = { +Button_.propTypes = { name: PropTypes.string.isRequired, title: PropTypes.string.isRequired, className: PropTypes.string, @@ -69,6 +70,102 @@ Button.propTypes = { active: PropTypes.bool }; +export const Button = React.forwardRef(Button_); + +const repeatInterval = 500; + +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 || e.button === 1) + { + e.preventDefault(); + + if (e.button === 0 && !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 (e.button !== 0 && typeof e.touches === 'undefined') + return; + + if (!this.intervalId) + { + this.hasInitialClick = false; + this.intervalId = setInterval(this.handleTimer, repeatInterval); + } + } + + handleEnd(e) + { + if (e.button !== 0 && typeof e.touches === 'undefined') + return; + + if (this.intervalId) + { + 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 ? <> -