mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-04-12 07:25:50 +00:00
优化一下ui
This commit is contained in:
@@ -192,7 +192,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.export-date-range-time-input {
|
.export-date-range-time-select {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.open .export-date-range-time-trigger {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px rgba(var(--primary-rgb), 0.18);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-trigger {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -203,20 +214,125 @@
|
|||||||
padding: 0 9px;
|
padding: 0 9px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
box-shadow: 0 0 0 1px rgba(var(--primary-rgb), 0.18);
|
box-shadow: 0 0 0 1px rgba(var(--primary-rgb), 0.18);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-calendar-picker-indicator {
|
.export-date-range-time-trigger-value {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 6px);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 24;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: color-mix(in srgb, var(--bg-primary) 88%, var(--bg-secondary));
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
-webkit-backdrop-filter: blur(14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-dropdown-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-quick-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-quick-item,
|
||||||
|
.export-date-range-time-option {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-primary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.6;
|
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
background: var(--bg-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: rgba(var(--primary-rgb), 0.28);
|
||||||
|
background: rgba(var(--primary-rgb), 0.12);
|
||||||
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-quick-item {
|
||||||
|
min-width: 52px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-columns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-column-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-column-list {
|
||||||
|
max-height: 168px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 2px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-date-range-time-option {
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.export-date-range-calendar-nav {
|
.export-date-range-calendar-nav {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { Check, ChevronLeft, ChevronRight, X } from 'lucide-react'
|
import { Check, ChevronDown, ChevronLeft, ChevronRight, X } from 'lucide-react'
|
||||||
import {
|
import {
|
||||||
EXPORT_DATE_RANGE_PRESETS,
|
EXPORT_DATE_RANGE_PRESETS,
|
||||||
WEEKDAY_SHORT_LABELS,
|
WEEKDAY_SHORT_LABELS,
|
||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
createDateRangeByPreset,
|
createDateRangeByPreset,
|
||||||
createDefaultDateRange,
|
createDefaultDateRange,
|
||||||
formatCalendarMonthTitle,
|
formatCalendarMonthTitle,
|
||||||
formatDateInputValue,
|
|
||||||
isSameDay,
|
isSameDay,
|
||||||
parseDateInputValue,
|
parseDateInputValue,
|
||||||
startOfDay,
|
startOfDay,
|
||||||
@@ -37,6 +36,10 @@ interface ExportDateRangeDialogDraft extends ExportDateRangeSelection {
|
|||||||
panelMonth: Date
|
panelMonth: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HOUR_OPTIONS = Array.from({ length: 24 }, (_, index) => `${index}`.padStart(2, '0'))
|
||||||
|
const MINUTE_OPTIONS = Array.from({ length: 60 }, (_, index) => `${index}`.padStart(2, '0'))
|
||||||
|
const QUICK_TIME_OPTIONS = ['00:00', '08:00', '12:00', '18:00', '23:59']
|
||||||
|
|
||||||
const resolveBounds = (minDate?: Date | null, maxDate?: Date | null): { minDate: Date; maxDate: Date } | null => {
|
const resolveBounds = (minDate?: Date | null, maxDate?: Date | null): { minDate: Date; maxDate: Date } | null => {
|
||||||
if (!(minDate instanceof Date) || Number.isNaN(minDate.getTime())) return null
|
if (!(minDate instanceof Date) || Number.isNaN(minDate.getTime())) return null
|
||||||
if (!(maxDate instanceof Date) || Number.isNaN(maxDate.getTime())) return null
|
if (!(maxDate instanceof Date) || Number.isNaN(maxDate.getTime())) return null
|
||||||
@@ -149,6 +152,9 @@ export function ExportDateRangeDialog({
|
|||||||
start: '00:00',
|
start: '00:00',
|
||||||
end: '23:59'
|
end: '23:59'
|
||||||
})
|
})
|
||||||
|
const [openTimeDropdown, setOpenTimeDropdown] = useState<ActiveBoundary | null>(null)
|
||||||
|
const startTimeSelectRef = useRef<HTMLDivElement>(null)
|
||||||
|
const endTimeSelectRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return
|
if (!open) return
|
||||||
@@ -172,6 +178,7 @@ export function ExportDateRangeDialog({
|
|||||||
end: formatTimeOnly(nextDraft.dateRange.end)
|
end: formatTimeOnly(nextDraft.dateRange.end)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
setDateInputError({ start: false, end: false })
|
setDateInputError({ start: false, end: false })
|
||||||
}, [maxDate, minDate, open, value])
|
}, [maxDate, minDate, open, value])
|
||||||
|
|
||||||
@@ -185,6 +192,33 @@ export function ExportDateRangeDialog({
|
|||||||
setDateInputError({ start: false, end: false })
|
setDateInputError({ start: false, end: false })
|
||||||
}, [draft.dateRange.end.getTime(), draft.dateRange.start.getTime(), open])
|
}, [draft.dateRange.end.getTime(), draft.dateRange.start.getTime(), open])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!openTimeDropdown) return
|
||||||
|
|
||||||
|
const handlePointerDown = (event: MouseEvent) => {
|
||||||
|
const target = event.target as Node
|
||||||
|
const activeContainer = openTimeDropdown === 'start'
|
||||||
|
? startTimeSelectRef.current
|
||||||
|
: endTimeSelectRef.current
|
||||||
|
if (!activeContainer?.contains(target)) {
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEscape = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handlePointerDown)
|
||||||
|
document.addEventListener('keydown', handleEscape)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handlePointerDown)
|
||||||
|
document.removeEventListener('keydown', handleEscape)
|
||||||
|
}
|
||||||
|
}, [openTimeDropdown])
|
||||||
|
|
||||||
const bounds = useMemo(() => resolveBounds(minDate, maxDate), [maxDate, minDate])
|
const bounds = useMemo(() => resolveBounds(minDate, maxDate), [maxDate, minDate])
|
||||||
const clampStartDate = useCallback((targetDate: Date) => {
|
const clampStartDate = useCallback((targetDate: Date) => {
|
||||||
if (!bounds) return targetDate
|
if (!bounds) return targetDate
|
||||||
@@ -241,6 +275,11 @@ export function ExportDateRangeDialog({
|
|||||||
const previewRange = bounds
|
const previewRange = bounds
|
||||||
? { start: bounds.minDate, end: bounds.maxDate }
|
? { start: bounds.minDate, end: bounds.maxDate }
|
||||||
: createDefaultDateRange()
|
: createDefaultDateRange()
|
||||||
|
setTimeInput({
|
||||||
|
start: '00:00',
|
||||||
|
end: '23:59'
|
||||||
|
})
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
setDraft(prev => ({
|
setDraft(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
preset,
|
preset,
|
||||||
@@ -257,6 +296,11 @@ export function ExportDateRangeDialog({
|
|||||||
useAllTime: false,
|
useAllTime: false,
|
||||||
dateRange: createDateRangeByPreset(preset)
|
dateRange: createDateRangeByPreset(preset)
|
||||||
}, minDate, maxDate).dateRange
|
}, minDate, maxDate).dateRange
|
||||||
|
setTimeInput({
|
||||||
|
start: '00:00',
|
||||||
|
end: '23:59'
|
||||||
|
})
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
setDraft(prev => ({
|
setDraft(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
preset,
|
preset,
|
||||||
@@ -276,8 +320,7 @@ export function ExportDateRangeDialog({
|
|||||||
return { hours, minutes }
|
return { hours, minutes }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle time picker changes - update draft.dateRange immediately
|
const updateBoundaryTime = useCallback((boundary: ActiveBoundary, timeStr: string) => {
|
||||||
const handleTimePickerChange = useCallback((boundary: 'start' | 'end', timeStr: string) => {
|
|
||||||
setTimeInput(prev => ({ ...prev, [boundary]: timeStr }))
|
setTimeInput(prev => ({ ...prev, [boundary]: timeStr }))
|
||||||
|
|
||||||
const parsedTime = parseTimeValue(timeStr)
|
const parsedTime = parseTimeValue(timeStr)
|
||||||
@@ -299,6 +342,82 @@ export function ExportDateRangeDialog({
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const toggleTimeDropdown = useCallback((boundary: ActiveBoundary) => {
|
||||||
|
setActiveBoundary(boundary)
|
||||||
|
setOpenTimeDropdown(prev => (prev === boundary ? null : boundary))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleTimeColumnSelect = useCallback((boundary: ActiveBoundary, field: 'hour' | 'minute', value: string) => {
|
||||||
|
const parsedCurrent = parseTimeValue(timeInput[boundary]) ?? {
|
||||||
|
hours: boundary === 'start' ? 0 : 23,
|
||||||
|
minutes: boundary === 'start' ? 0 : 59
|
||||||
|
}
|
||||||
|
const nextHours = field === 'hour' ? Number(value) : parsedCurrent.hours
|
||||||
|
const nextMinutes = field === 'minute' ? Number(value) : parsedCurrent.minutes
|
||||||
|
updateBoundaryTime(boundary, `${`${nextHours}`.padStart(2, '0')}:${`${nextMinutes}`.padStart(2, '0')}`)
|
||||||
|
}, [timeInput, updateBoundaryTime])
|
||||||
|
|
||||||
|
const renderTimeDropdown = (boundary: ActiveBoundary) => {
|
||||||
|
const currentTime = timeInput[boundary]
|
||||||
|
const parsedCurrent = parseTimeValue(currentTime) ?? {
|
||||||
|
hours: boundary === 'start' ? 0 : 23,
|
||||||
|
minutes: boundary === 'start' ? 0 : 59
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="export-date-range-time-dropdown" onClick={(event) => event.stopPropagation()}>
|
||||||
|
<div className="export-date-range-time-dropdown-header">
|
||||||
|
<span>{boundary === 'start' ? '开始时间' : '结束时间'}</span>
|
||||||
|
<strong>{currentTime}</strong>
|
||||||
|
</div>
|
||||||
|
<div className="export-date-range-time-quick-list">
|
||||||
|
{QUICK_TIME_OPTIONS.map(option => (
|
||||||
|
<button
|
||||||
|
key={`${boundary}-${option}`}
|
||||||
|
type="button"
|
||||||
|
className={`export-date-range-time-quick-item ${currentTime === option ? 'active' : ''}`}
|
||||||
|
onClick={() => updateBoundaryTime(boundary, option)}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="export-date-range-time-columns">
|
||||||
|
<div className="export-date-range-time-column">
|
||||||
|
<span className="export-date-range-time-column-label">小时</span>
|
||||||
|
<div className="export-date-range-time-column-list">
|
||||||
|
{HOUR_OPTIONS.map(option => (
|
||||||
|
<button
|
||||||
|
key={`${boundary}-hour-${option}`}
|
||||||
|
type="button"
|
||||||
|
className={`export-date-range-time-option ${parsedCurrent.hours === Number(option) ? 'active' : ''}`}
|
||||||
|
onClick={() => handleTimeColumnSelect(boundary, 'hour', option)}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="export-date-range-time-column">
|
||||||
|
<span className="export-date-range-time-column-label">分钟</span>
|
||||||
|
<div className="export-date-range-time-column-list">
|
||||||
|
{MINUTE_OPTIONS.map(option => (
|
||||||
|
<button
|
||||||
|
key={`${boundary}-minute-${option}`}
|
||||||
|
type="button"
|
||||||
|
className={`export-date-range-time-option ${parsedCurrent.minutes === Number(option) ? 'active' : ''}`}
|
||||||
|
onClick={() => handleTimeColumnSelect(boundary, 'minute', option)}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if date input string contains time (YYYY-MM-DD HH:mm format)
|
// Check if date input string contains time (YYYY-MM-DD HH:mm format)
|
||||||
const dateInputHasTime = (dateStr: string): boolean => /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}$/.test(dateStr.trim())
|
const dateInputHasTime = (dateStr: string): boolean => /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}$/.test(dateStr.trim())
|
||||||
|
|
||||||
@@ -357,6 +476,7 @@ export function ExportDateRangeDialog({
|
|||||||
newStart.setHours(time.hours, time.minutes, 0, 0)
|
newStart.setHours(time.hours, time.minutes, 0, 0)
|
||||||
setRangeStart(newStart)
|
setRangeStart(newStart)
|
||||||
setActiveBoundary('end')
|
setActiveBoundary('end')
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +489,7 @@ export function ExportDateRangeDialog({
|
|||||||
// If selecting same day or going backwards, use 23:59:59, otherwise use the time from timeInput
|
// If selecting same day or going backwards, use 23:59:59, otherwise use the time from timeInput
|
||||||
if (pickedStart <= start) {
|
if (pickedStart <= start) {
|
||||||
newEnd.setHours(23, 59, 59, 999)
|
newEnd.setHours(23, 59, 59, 999)
|
||||||
|
setTimeInput(prev => ({ ...prev, end: '23:59' }))
|
||||||
} else {
|
} else {
|
||||||
newEnd.setHours(time.hours, time.minutes, 59, 999)
|
newEnd.setHours(time.hours, time.minutes, 59, 999)
|
||||||
}
|
}
|
||||||
@@ -384,6 +505,7 @@ export function ExportDateRangeDialog({
|
|||||||
panelMonth: toMonthStart(targetDate)
|
panelMonth: toMonthStart(targetDate)
|
||||||
}))
|
}))
|
||||||
setActiveBoundary('start')
|
setActiveBoundary('start')
|
||||||
|
setOpenTimeDropdown(null)
|
||||||
}, [activeBoundary, draft.dateRange.start, draft.useAllTime, timeInput.end, timeInput.start, setRangeStart])
|
}, [activeBoundary, draft.dateRange.start, draft.useAllTime, timeInput.end, timeInput.start, setRangeStart])
|
||||||
|
|
||||||
const isRangeModeActive = !draft.useAllTime
|
const isRangeModeActive = !draft.useAllTime
|
||||||
@@ -491,16 +613,23 @@ export function ExportDateRangeDialog({
|
|||||||
}}
|
}}
|
||||||
onBlur={commitStartFromInput}
|
onBlur={commitStartFromInput}
|
||||||
/>
|
/>
|
||||||
<input
|
<div
|
||||||
type="time"
|
className={`export-date-range-time-select ${openTimeDropdown === 'start' ? 'open' : ''}`}
|
||||||
className="export-date-range-time-input"
|
ref={startTimeSelectRef}
|
||||||
value={timeInput.start}
|
|
||||||
onChange={(event) => {
|
|
||||||
handleTimePickerChange('start', event.target.value)
|
|
||||||
}}
|
|
||||||
onFocus={() => setActiveBoundary('start')}
|
|
||||||
onClick={(event) => event.stopPropagation()}
|
onClick={(event) => event.stopPropagation()}
|
||||||
/>
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="export-date-range-time-trigger"
|
||||||
|
onClick={() => toggleTimeDropdown('start')}
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
aria-expanded={openTimeDropdown === 'start'}
|
||||||
|
>
|
||||||
|
<span className="export-date-range-time-trigger-value">{timeInput.start}</span>
|
||||||
|
<ChevronDown size={14} />
|
||||||
|
</button>
|
||||||
|
{openTimeDropdown === 'start' && renderTimeDropdown('start')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`export-date-range-boundary-card ${activeBoundary === 'end' ? 'active' : ''}`}
|
className={`export-date-range-boundary-card ${activeBoundary === 'end' ? 'active' : ''}`}
|
||||||
@@ -528,16 +657,23 @@ export function ExportDateRangeDialog({
|
|||||||
}}
|
}}
|
||||||
onBlur={commitEndFromInput}
|
onBlur={commitEndFromInput}
|
||||||
/>
|
/>
|
||||||
<input
|
<div
|
||||||
type="time"
|
className={`export-date-range-time-select ${openTimeDropdown === 'end' ? 'open' : ''}`}
|
||||||
className="export-date-range-time-input"
|
ref={endTimeSelectRef}
|
||||||
value={timeInput.end}
|
|
||||||
onChange={(event) => {
|
|
||||||
handleTimePickerChange('end', event.target.value)
|
|
||||||
}}
|
|
||||||
onFocus={() => setActiveBoundary('end')}
|
|
||||||
onClick={(event) => event.stopPropagation()}
|
onClick={(event) => event.stopPropagation()}
|
||||||
/>
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="export-date-range-time-trigger"
|
||||||
|
onClick={() => toggleTimeDropdown('end')}
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
aria-expanded={openTimeDropdown === 'end'}
|
||||||
|
>
|
||||||
|
<span className="export-date-range-time-trigger-value">{timeInput.end}</span>
|
||||||
|
<ChevronDown size={14} />
|
||||||
|
</button>
|
||||||
|
{openTimeDropdown === 'end' && renderTimeDropdown('end')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user