diff --git a/client/components/DurationView.tsx b/client/components/DurationView.tsx index 0a16170..000fb81 100644 --- a/client/components/DurationView.tsx +++ b/client/components/DurationView.tsx @@ -6,13 +6,90 @@ import { Duration } from "@common/Duration"; import "@client/styles/DurationView"; -export default class DurationView extends React.Component<{ +export interface DurationViewProps { label?: string; inline?: boolean; duration: Duration; onDurationChange?: (newDuration: Duration) => void; className?: string; -}> { +} + +function roundOrString(val: number | string): number | string { + if (typeof val === "number") { + return Math.round(val); + } else { + return val; + } +} + +interface NumberInputProps { + className?: string; + label?: string; + value: number; + max?: number; + onChange: (value: number) => void; +} + +function NumberInput(props: NumberInputProps): React.ReactElement { + const [valueState, setValueState] = React.useState(props.value); + const [elementId, setElementId] = React.useState(() => `NumberInput-${Math.round(Math.random() * 100000000)}`); + const [isWheelChange, setIsWheelChange] = React.useState(false); + + const onChange: InputProps["onChange"] = (_e, data) => { + setValueState(data.value); + const newValue = parseFloat(data.value); + if (!isNaN(newValue) && data.value.length > 0 && isWheelChange) { + props.onChange(Math.round(newValue)); + setIsWheelChange(false); + } + }; + + const onBlur: React.FocusEventHandler = () => { + const newValue = (typeof valueState === "number") ? valueState : parseFloat(valueState); + if (!props.onChange || isNaN(newValue)) { + return; + } + if (props.value !== newValue) { + props.onChange(Math.round(newValue)); + } + }; + + const onWheel = (e: Event) => { + // do nothing + setIsWheelChange(true); + }; + + React.useEffect(() => { + const el = document.getElementById(elementId); + if (el) { + // Not passive events + el.addEventListener("wheel", onWheel); + } + }); + + React.useEffect(() => { + if (props.value !== valueState) { + setValueState(props.value); + } + }, [props.value]); + + return +} + +export default class DurationView extends React.Component { render() { const { duration, label, inline, onDurationChange, className } = this.props; const inputsClassName = classNames("durationInputs", { inline }); @@ -22,24 +99,18 @@ export default class DurationView extends React.Component<{ {label && }
- -
@@ -55,23 +126,25 @@ export default class DurationView extends React.Component<{ } } - private onMinutesChange: InputProps["onChange"] = (e, { value }) => { - if (!this.props.onDurationChange || isNaN(Number(value))) { - return; + componentWillReceiveProps(nextProps: Readonly) { + if (nextProps.duration.minutes !== this.props.duration.minutes || + nextProps.duration.seconds !== this.props.duration.seconds) { + this.setState({ + minutes: nextProps.duration.minutes, + seconds: nextProps.duration.seconds, + }); } - const newMinutes = Number(value); - this.props.onDurationChange(this.props.duration.withMinutes(newMinutes)); - }; + } - private onSecondsChange: InputProps["onChange"] = (e, { value }) => { - if (!this.props.onDurationChange || isNaN(Number(value))) { - return; + private onMinutesChange = (newMinutes: number) => { + if (this.props.onDurationChange) { + this.props.onDurationChange(this.props.duration.withMinutes(newMinutes)); } - const newSeconds = Number(value); - this.props.onDurationChange(this.props.duration.withSeconds(newSeconds)); }; - private onWheel = () => { - // do nothing + private onSecondsChange = (newSeconds: number) => { + if (this.props.onDurationChange) { + this.props.onDurationChange(this.props.duration.withSeconds(newSeconds)); + } }; }