crabfit/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx
Ben Grant fdb7f0ef67 Tabs -> spaces
I have become my own worst enemy
2021-06-19 12:04:52 +10:00

147 lines
4.7 KiB
TypeScript

import { useState, useEffect, useRef, forwardRef } from 'react';
import dayjs from 'dayjs';
import { useSettingsStore, useLocaleUpdateStore } from 'stores';
import {
Wrapper,
StyledLabel,
StyledSubLabel,
Range,
Handle,
Selected,
} from './timeRangeFieldStyle';
const times = ['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24'];
const TimeRangeField = forwardRef(({
label,
subLabel,
id,
setValue,
...props
}, ref) => {
const timeFormat = useSettingsStore(state => state.timeFormat);
const locale = useLocaleUpdateStore(state => state.locale);
const [start, setStart] = useState(9);
const [end, setEnd] = useState(17);
const isStartMoving = useRef(false);
const isEndMoving = useRef(false);
const rangeRef = useRef();
const rangeRect = useRef();
useEffect(() => {
if (rangeRef.current) {
rangeRect.current = rangeRef.current.getBoundingClientRect();
}
}, [rangeRef]);
useEffect(() => setValue(props.name, JSON.stringify({start, end})), [start, end, setValue, props.name]);
const handleMouseMove = e => {
if (isStartMoving.current || isEndMoving.current) {
let step = Math.round(((e.pageX - rangeRect.current.left) / rangeRect.current.width) * 24);
if (step < 0) step = 0;
if (step > 24) step = 24;
step = Math.abs(step);
if (isStartMoving.current) {
setStart(step);
} else if (isEndMoving.current) {
setEnd(step);
}
}
};
return (
<Wrapper locale={locale}>
{label && <StyledLabel htmlFor={id}>{label}</StyledLabel>}
{subLabel && <StyledSubLabel htmlFor={id}>{subLabel}</StyledSubLabel>}
<input
id={id}
type="hidden"
value={JSON.stringify({start, end})}
ref={ref}
{...props}
/>
<Range ref={rangeRef}>
<Selected start={start} end={start > end ? 24 : end} />
{start > end && <Selected start={start > end ? 0 : start} end={end} />}
<Handle
value={start}
label={timeFormat === '24h' ? times[start] : dayjs().hour(times[start]).format('ha')}
extraPadding={end - start === 1 ? 'padding-right: 20px;' : (start - end === 1 ? 'padding-left: 20px;' : '')}
onMouseDown={() => {
document.addEventListener('mousemove', handleMouseMove);
isStartMoving.current = true;
document.addEventListener('mouseup', () => {
isStartMoving.current = false;
document.removeEventListener('mousemove', handleMouseMove);
}, { once: true });
}}
onTouchMove={(e) => {
const touch = e.targetTouches[0];
let step = Math.round(((touch.pageX - rangeRect.current.left) / rangeRect.current.width) * 24);
if (step < 0) step = 0;
if (step > 24) step = 24;
step = Math.abs(step);
setStart(step);
}}
tabIndex="0"
onKeyDown={e => {
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
e.preventDefault();
setStart(Math.max(start-1, 0));
}
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
e.preventDefault();
setStart(Math.min(start+1, 24));
}
}}
/>
<Handle
value={end}
label={timeFormat === '24h' ? times[end] : dayjs().hour(times[end]).format('ha')}
extraPadding={end - start === 1 ? 'padding-left: 20px;' : (start - end === 1 ? 'padding-right: 20px;' : '')}
onMouseDown={() => {
document.addEventListener('mousemove', handleMouseMove);
isEndMoving.current = true;
document.addEventListener('mouseup', () => {
isEndMoving.current = false;
document.removeEventListener('mousemove', handleMouseMove);
}, { once: true });
}}
onTouchMove={(e) => {
const touch = e.targetTouches[0];
let step = Math.round(((touch.pageX - rangeRect.current.left) / rangeRect.current.width) * 24);
if (step < 0) step = 0;
if (step > 24) step = 24;
step = Math.abs(step);
setEnd(step);
}}
tabIndex="0"
onKeyDown={e => {
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
e.preventDefault();
setEnd(Math.max(end-1, 0));
}
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
e.preventDefault();
setEnd(Math.min(end+1, 24));
}
}}
/>
</Range>
</Wrapper>
);
});
export default TimeRangeField;