208 lines
5.0 KiB
TypeScript
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>
|
|
)
|
|
} |