MDM/mdm-front/src/pages/MapPage/components/MapTrackPeriodControl/MapTrackPeriodControl.tsx

208 lines
5.0 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react'
import { DayPicker } from 'react-day-picker'
import type { DateRange } from 'react-day-picker'
import { ru } from 'date-fns/locale/ru'
import { CalendarDays, ChevronDown } from 'lucide-react'
import './MapTrackPeriodControl.scss'
type PeriodPreset = 'today' | 'yesterday' | 'week' | 'month' | 'custom'
export type MapTrackPeriodValue = {
preset: PeriodPreset
periodStart: Date
periodEnd: Date
}
type MapTrackPeriodControlProps = {
onChange?: (value: MapTrackPeriodValue) => void
}
const presetTabs: Array<{
value: PeriodPreset
label: string
}> = [
{ value: 'today', label: 'Сегодня' },
{ value: 'yesterday', label: 'Вчера' },
{ value: 'week', label: 'Неделя' },
{ value: 'month', label: 'Месяц' },
]
function startOfDay(date: Date) {
const result = new Date(date)
result.setHours(0, 0, 0, 0)
return result
}
function endOfDay(date: Date) {
const result = new Date(date)
result.setHours(23, 59, 59, 999)
return result
}
function addDays(date: Date, days: number) {
const result = new Date(date)
result.setDate(result.getDate() + days)
return result
}
function addMonths(date: Date, months: number) {
const result = new Date(date)
result.setMonth(result.getMonth() + months)
return result
}
function getPresetRange(preset: PeriodPreset) {
const now = new Date()
if (preset === 'today') {
return {
start: startOfDay(now),
end: now,
}
}
if (preset === 'yesterday') {
const yesterday = addDays(now, -1)
return {
start: startOfDay(yesterday),
end: endOfDay(yesterday),
}
}
if (preset === 'week') {
return {
start: startOfDay(addDays(now, -6)),
end: now,
}
}
if (preset === 'month') {
return {
start: startOfDay(addMonths(now, -1)),
end: now,
}
}
return {
start: startOfDay(now),
end: now,
}
}
function formatDate(date: Date) {
return new Intl.DateTimeFormat('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
}).format(date)
}
export function MapTrackPeriodControl({ onChange }: MapTrackPeriodControlProps) {
const initialRange = useMemo(() => getPresetRange('today'), [])
const [preset, setPreset] = useState<PeriodPreset>('today')
const [isCalendarOpen, setIsCalendarOpen] = useState(false)
const [periodStart, setPeriodStart] = useState(initialRange.start)
const [periodEnd, setPeriodEnd] = useState(initialRange.end)
const [calendarRange, setCalendarRange] = useState<DateRange | undefined>({
from: initialRange.start,
to: initialRange.end,
})
function applyPeriod(nextPreset: PeriodPreset, start: Date, end: Date) {
setPreset(nextPreset)
setPeriodStart(start)
setPeriodEnd(end)
setCalendarRange({
from: start,
to: end,
})
}
function handlePresetClick(nextPreset: PeriodPreset) {
const nextRange = getPresetRange(nextPreset)
applyPeriod(nextPreset, nextRange.start, nextRange.end)
}
function handleCalendarSelect(nextRange: DateRange | undefined) {
setCalendarRange(nextRange)
if (!nextRange?.from) return
const from = startOfDay(nextRange.from)
const to = nextRange.to ? endOfDay(nextRange.to) : endOfDay(nextRange.from)
applyPeriod('custom', from, to)
}
useEffect(() => {
onChange?.({
preset,
periodStart,
periodEnd,
})
}, [preset, periodStart, periodEnd, onChange])
return (
<div className="history-period-control">
<div className="history-period-control__top">
<div className="history-period-tabs">
{presetTabs.map((tab) => (
<button
key={tab.value}
className={
preset === tab.value
? 'history-period-tabs__item is-active'
: 'history-period-tabs__item'
}
type="button"
onClick={() => handlePresetClick(tab.value)}
>
{tab.label}
</button>
))}
</div>
<div className="history-date-picker">
<button
className={
preset === 'custom'
? 'history-date-picker__trigger is-active'
: 'history-date-picker__trigger'
}
type="button"
onClick={() => setIsCalendarOpen((prev) => !prev)}
>
<CalendarDays size={16} />
<span>
{formatDate(periodStart)} {formatDate(periodEnd)}
</span>
<ChevronDown size={16} />
</button>
{isCalendarOpen && (
<div className="history-date-picker__dropdown">
<DayPicker
mode="range"
locale={ru}
selected={calendarRange}
onSelect={handleCalendarSelect}
numberOfMonths={1}
defaultMonth={periodStart}
weekStartsOn={1}
showOutsideDays
/>
</div>
)}
</div>
</div>
</div>
)
}