Refactor TimeRangeField
This commit is contained in:
parent
12004b8584
commit
e9945a19d5
|
|
@ -8,7 +8,8 @@
|
||||||
"@next/next/no-img-element": "off",
|
"@next/next/no-img-element": "off",
|
||||||
"react/display-name": "off",
|
"react/display-name": "off",
|
||||||
"react-hooks/exhaustive-deps": "off",
|
"react-hooks/exhaustive-deps": "off",
|
||||||
"space-infix-ops": "warn"
|
"space-infix-ops": "warn",
|
||||||
|
"comma-spacing": "warn"
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { FieldValues,useController, UseControllerProps } from 'react-hook-form'
|
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { makeClass } from '/src/utils'
|
||||||
import styles from './Month.module.scss'
|
import styles from './Month.module.scss'
|
||||||
|
|
||||||
// TODO: use from giraugh tools
|
// TODO: use from giraugh tools
|
||||||
export const rotateArray = <T,>(arr: T[], amount = 1): T[] =>
|
export const rotateArray = <T, >(arr: T[], amount = 1): T[] =>
|
||||||
arr.map((_, i) => arr[((( -amount + i ) % arr.length) + arr.length) % arr.length])
|
arr.map((_, i) => arr[((( -amount + i ) % arr.length) + arr.length) % arr.length])
|
||||||
|
|
||||||
interface MonthProps {
|
interface MonthProps {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { makeClass } from '/src/utils'
|
||||||
import styles from '../Month/Month.module.scss'
|
import styles from '../Month/Month.module.scss'
|
||||||
|
|
||||||
// TODO: use from giraugh tools
|
// TODO: use from giraugh tools
|
||||||
export const rotateArray = <T,>(arr: T[], amount = 1): T[] =>
|
export const rotateArray = <T, >(arr: T[], amount = 1): T[] =>
|
||||||
arr.map((_, i) => arr[((( -amount + i ) % arr.length) + arr.length) % arr.length])
|
arr.map((_, i) => arr[((( -amount + i ) % arr.length) + arr.length) % arr.length])
|
||||||
|
|
||||||
interface WeekdaysProps {
|
interface WeekdaysProps {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { SubmitHandler, useForm } from 'react-hook-form'
|
import { SubmitHandler, useForm } from 'react-hook-form'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ import Button from '/src/components/Button/Button'
|
||||||
import CalendarField from '/src/components/CalendarField/CalendarField'
|
import CalendarField from '/src/components/CalendarField/CalendarField'
|
||||||
import { default as ErrorAlert } from '/src/components/Error/Error'
|
import { default as ErrorAlert } from '/src/components/Error/Error'
|
||||||
import TextField from '/src/components/TextField/TextField'
|
import TextField from '/src/components/TextField/TextField'
|
||||||
import ToggleField from '/src/components/ToggleField/ToggleField'
|
import TimeRangeField from '/src/components/TimeRangeField/TimeRangeField'
|
||||||
import { API_BASE } from '/src/config/api'
|
import { API_BASE } from '/src/config/api'
|
||||||
import dayjs from '/src/config/dayjs'
|
import dayjs from '/src/config/dayjs'
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
|
|
@ -47,7 +47,7 @@ const CreateForm = () => {
|
||||||
const [error, setError] = useState<React.ReactNode>()
|
const [error, setError] = useState<React.ReactNode>()
|
||||||
|
|
||||||
const onSubmit: SubmitHandler<Fields> = async values => {
|
const onSubmit: SubmitHandler<Fields> = async values => {
|
||||||
console.log({values})
|
console.log({values}) // TODO:
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ const CreateForm = () => {
|
||||||
if (dates.length === 0) {
|
if (dates.length === 0) {
|
||||||
return setError(t('form.errors.no_dates'))
|
return setError(t('form.errors.no_dates'))
|
||||||
}
|
}
|
||||||
const isSpecificDates = typeof dates[0] === 'string' && dates[0].length === 8
|
const isSpecificDates = dates[0].length === 8
|
||||||
if (time.start === time.end) {
|
if (time.start === time.end) {
|
||||||
return setError(t('form.errors.same_times'))
|
return setError(t('form.errors.same_times'))
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ const CreateForm = () => {
|
||||||
} else {
|
} else {
|
||||||
day.push(
|
day.push(
|
||||||
dayjs().tz(timezone)
|
dayjs().tz(timezone)
|
||||||
.day(date).hour(i).minute(0).utc().format('HHmm-d')
|
.day(Number(date)).hour(i).minute(0).utc().format('HHmm-d')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,13 +87,13 @@ const CreateForm = () => {
|
||||||
} else {
|
} else {
|
||||||
day.push(
|
day.push(
|
||||||
dayjs().tz(timezone)
|
dayjs().tz(timezone)
|
||||||
.day(date).hour(i).minute(0).utc().format('HHmm-d')
|
.day(Number(date)).hour(i).minute(0).utc().format('HHmm-d')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...times, ...day]
|
return [...times, ...day]
|
||||||
}, [])
|
}, [] as string[])
|
||||||
|
|
||||||
if (times.length === 0) {
|
if (times.length === 0) {
|
||||||
return setError(t('form.errors.no_time'))
|
return setError(t('form.errors.no_time'))
|
||||||
|
|
@ -141,15 +141,14 @@ const CreateForm = () => {
|
||||||
name="dates"
|
name="dates"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <TimeRangeField
|
<TimeRangeField
|
||||||
label={t('form.times.label')}
|
label={t('form.times.label')}
|
||||||
subLabel={t('form.times.sublabel')}
|
description={t('form.times.sublabel')}
|
||||||
required
|
control={control}
|
||||||
setValue={setValue}
|
name="time"
|
||||||
{...register('time')}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectField
|
{/* <SelectField
|
||||||
label={t('form.timezone.label')}
|
label={t('form.timezone.label')}
|
||||||
options={timezones}
|
options={timezones}
|
||||||
required
|
required
|
||||||
|
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
import { useState, useEffect, useRef, forwardRef } from 'react'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
import { useSettingsStore, useLocaleUpdateStore } from '/src/stores'
|
|
||||||
|
|
||||||
import {
|
|
||||||
Wrapper,
|
|
||||||
StyledLabel,
|
|
||||||
StyledSubLabel,
|
|
||||||
Range,
|
|
||||||
Handle,
|
|
||||||
Selected,
|
|
||||||
} from './TimeRangeField.styles'
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,24 +1,4 @@
|
||||||
import { styled } from 'goober'
|
.range {
|
||||||
import { forwardRef } from 'react'
|
|
||||||
|
|
||||||
export const Wrapper = styled('div')`
|
|
||||||
margin: 30px 0;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const StyledLabel = styled('label')`
|
|
||||||
display: block;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
font-size: 18px;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const StyledSubLabel = styled('label')`
|
|
||||||
display: block;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
font-size: 13px;
|
|
||||||
opacity: .6;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const Range = styled('div', forwardRef)`
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: var(--surface);
|
background-color: var(--surface);
|
||||||
border: 1px solid var(--primary);
|
border: 1px solid var(--primary);
|
||||||
|
|
@ -26,9 +6,9 @@ export const Range = styled('div', forwardRef)`
|
||||||
height: 50px;
|
height: 50px;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 38px 6px 18px;
|
margin: 38px 6px 18px;
|
||||||
`
|
}
|
||||||
|
|
||||||
export const Handle = styled('div')`
|
.handle {
|
||||||
height: calc(100% + 20px);
|
height: calc(100% + 20px);
|
||||||
width: 20px;
|
width: 20px;
|
||||||
border: 1px solid var(--primary);
|
border: 1px solid var(--primary);
|
||||||
|
|
@ -36,10 +16,10 @@ export const Handle = styled('div')`
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10px;
|
top: -10px;
|
||||||
left: calc(${props => props.$value * 4.166}% - 11px);
|
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
transition: left .1s;
|
transition: left .1s;
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
@ -59,27 +39,26 @@ export const Handle = styled('div')`
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: '${props => props.label}';
|
content: attr(data-label);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: calc(100% + 8px);
|
bottom: calc(100% + 8px);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
${props => props.$extraPadding}
|
padding-inline: var(--extra-padding);
|
||||||
}
|
}
|
||||||
`
|
}
|
||||||
|
|
||||||
export const Selected = styled('div')`
|
.selected {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: ${props => props.$start * 4.166}%;
|
|
||||||
right: calc(100% - ${props => props.$end * 4.166}%);
|
|
||||||
top: 0;
|
top: 0;
|
||||||
background-color: var(--primary);
|
background-color: var(--primary);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
transition: left .1s, right .1s;
|
transition: left .1s, right .1s;
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
`
|
}
|
||||||
127
frontend/src/components/TimeRangeField/TimeRangeField.tsx
Normal file
127
frontend/src/components/TimeRangeField/TimeRangeField.tsx
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
import { useRef } from 'react'
|
||||||
|
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
||||||
|
import useSettingsStore from '/src/stores/settingsStore'
|
||||||
|
|
||||||
|
import styles from './TimeRangeField.module.scss'
|
||||||
|
|
||||||
|
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'] as const
|
||||||
|
|
||||||
|
interface TimeRangeFieldProps<TValues extends FieldValues> extends UseControllerProps<TValues> {
|
||||||
|
label?: React.ReactNode
|
||||||
|
description?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimeRangeField = <TValues extends FieldValues>({
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
...props
|
||||||
|
}: TimeRangeFieldProps<TValues>) => {
|
||||||
|
const { field: { value, onChange } } = useController(props)
|
||||||
|
|
||||||
|
return <Wrapper>
|
||||||
|
{label && <Label>{label}</Label>}
|
||||||
|
{description && <Description>{description}</Description>}
|
||||||
|
|
||||||
|
<div className={styles.range}>
|
||||||
|
<Selection
|
||||||
|
start={value.start}
|
||||||
|
end={value.start > value.end ? 24 : value.end}
|
||||||
|
/>
|
||||||
|
{value.start > value.end && <Selection
|
||||||
|
start={value.start > value.end ? 0 : value.start}
|
||||||
|
end={value.end}
|
||||||
|
/>}
|
||||||
|
|
||||||
|
<Handle
|
||||||
|
value={value.start}
|
||||||
|
onChange={start => onChange({ ...value, start })}
|
||||||
|
labelPadding={value.end - value.start === 1 ? '0 20px' : (value.start - value.end === 1 ? '20px 0' : '0')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Handle
|
||||||
|
value={value.end}
|
||||||
|
onChange={end => onChange({ ...value, end })}
|
||||||
|
labelPadding={value.end - value.start === 1 ? '20px 0' : (value.start - value.end === 1 ? '0 20px' : '0')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Wrapper>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimeRangeField
|
||||||
|
|
||||||
|
const Selection = ({ start, end }: { start: number, end: number }) => <div
|
||||||
|
className={styles.selected}
|
||||||
|
style={{
|
||||||
|
left: `${start * 4.166}%`,
|
||||||
|
right: `calc(100% - ${end * 4.166}%)`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
interface HandleProps {
|
||||||
|
value: number
|
||||||
|
onChange: (value: number) => void
|
||||||
|
labelPadding: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Handle = ({ value, onChange, labelPadding }: HandleProps) => {
|
||||||
|
const timeFormat = useSettingsStore(state => state.timeFormat)
|
||||||
|
|
||||||
|
const isMoving = useRef(false)
|
||||||
|
const rangeRect = useRef({ left: 0, width: 0 })
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
if (isMoving.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)
|
||||||
|
|
||||||
|
onChange(step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div
|
||||||
|
ref={el => {
|
||||||
|
const bb = el?.parentElement?.getBoundingClientRect()
|
||||||
|
rangeRect.current = { left: bb?.left ?? 0, width: bb?.width ?? 0 }
|
||||||
|
}}
|
||||||
|
className={styles.handle}
|
||||||
|
style={{
|
||||||
|
left: `calc(${value * 4.166}% - 11px)`,
|
||||||
|
'--extra-padding': labelPadding,
|
||||||
|
} as React.CSSProperties}
|
||||||
|
data-label={timeFormat === '24h' ? times[value] : dayjs().hour(Number(times[value])).format('ha')}
|
||||||
|
onMouseDown={() => {
|
||||||
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
|
isMoving.current = true
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', () => {
|
||||||
|
isMoving.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)
|
||||||
|
onChange(step)
|
||||||
|
}}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={e => {
|
||||||
|
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault()
|
||||||
|
onChange(Math.max(value - 1, 0))
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault()
|
||||||
|
onChange(Math.min(value + 1, 24))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue