Browse Source

Better DurationView

master
Alex Mikhalev 6 years ago
parent
commit
e5fd8ce364
  1. 125
      client/components/DurationView.tsx

125
client/components/DurationView.tsx

@ -6,13 +6,90 @@ import { Duration } from "@common/Duration";
import "@client/styles/DurationView"; import "@client/styles/DurationView";
export default class DurationView extends React.Component<{ export interface DurationViewProps {
label?: string; label?: string;
inline?: boolean; inline?: boolean;
duration: Duration; duration: Duration;
onDurationChange?: (newDuration: Duration) => void; onDurationChange?: (newDuration: Duration) => void;
className?: string; 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<number | string>(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 <Input
id={elementId}
type="number"
pattern="[0-9\.]*" // for safari
inputMode="numeric"
value={roundOrString(valueState)}
onChange={onChange}
// onMouseOut={onBlur}
onBlur={onBlur}
className={props.className}
label={props.label}
max={props.max}
labelPosition="right"
/>
}
export default class DurationView extends React.Component<DurationViewProps> {
render() { render() {
const { duration, label, inline, onDurationChange, className } = this.props; const { duration, label, inline, onDurationChange, className } = this.props;
const inputsClassName = classNames("durationInputs", { inline }); const inputsClassName = classNames("durationInputs", { inline });
@ -22,24 +99,18 @@ export default class DurationView extends React.Component<{
<Form.Field inline={inline} className={className}> <Form.Field inline={inline} className={className}>
{label && <label>{label}</label>} {label && <label>{label}</label>}
<div className={inputsClassName}> <div className={inputsClassName}>
<Input <NumberInput
type="number"
className="durationInput minutes" className="durationInput minutes"
value={duration.minutes} value={this.props.duration.minutes}
onChange={this.onMinutesChange} onChange={this.onMinutesChange}
label="M" label="M"
labelPosition="right"
onWheel={this.onWheel}
/> />
<Input <NumberInput
type="number"
className="durationInput seconds" className="durationInput seconds"
value={duration.seconds} value={this.props.duration.seconds}
onChange={this.onSecondsChange} onChange={this.onSecondsChange}
max="60" max={60}
label="S" label="S"
labelPosition="right"
onWheel={this.onWheel}
/> />
</div> </div>
</Form.Field> </Form.Field>
@ -55,23 +126,25 @@ export default class DurationView extends React.Component<{
} }
} }
private onMinutesChange: InputProps["onChange"] = (e, { value }) => { componentWillReceiveProps(nextProps: Readonly<DurationViewProps>) {
if (!this.props.onDurationChange || isNaN(Number(value))) { if (nextProps.duration.minutes !== this.props.duration.minutes ||
return; 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 }) => { private onMinutesChange = (newMinutes: number) => {
if (!this.props.onDurationChange || isNaN(Number(value))) { if (this.props.onDurationChange) {
return; this.props.onDurationChange(this.props.duration.withMinutes(newMinutes));
} }
const newSeconds = Number(value);
this.props.onDurationChange(this.props.duration.withSeconds(newSeconds));
}; };
private onWheel = () => { private onSecondsChange = (newSeconds: number) => {
// do nothing if (this.props.onDurationChange) {
this.props.onDurationChange(this.props.duration.withSeconds(newSeconds));
}
}; };
} }

Loading…
Cancel
Save