| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- import { format } from 'date-fns'
- import { Clock } from 'lucide-react'
- import { useEffect, useState } from 'react'
- import { cn } from 'ui'
- import type { TimeSplitInputProps, TimeType } from './DatePicker.types'
- import {
- isUnixMicro,
- unixMicroToIsoTimestamp,
- } from '@/components/interfaces/Settings/Logs/Logs.utils'
- const inputStyle = cn(
- 'w-6 p-0 text-center text-xs text-foreground outline-hidden cursor-text',
- 'ring-0 focus:ring-0 ring-none border-none bg-transparent'
- )
- export const TimeSplitInput = ({
- type,
- time,
- setTime,
- setStartTime,
- setEndTime,
- startTime,
- endTime,
- startDate,
- endDate,
- }: TimeSplitInputProps) => {
- const [focus, setFocus] = useState(false)
- function handleOnBlur() {
- const _time = time
- if (_time.HH.length === 1) _time.HH = '0' + _time.HH
- if (_time.mm.length === 1) _time.mm = '0' + _time.mm
- if (_time.ss.length === 1) _time.ss = '0' + _time.ss
- if (!_time.HH) _time.HH = '00'
- if (!_time.mm) _time.mm = '00'
- if (!_time.ss) _time.ss = '00'
- let endTimeChanges = false
- let endTimePayload = endTime
- let startTimeChanges = false
- let startTimePayload = startTime
- // Only run time conflicts if
- // startDate and endDate are the same date
- if (format(new Date(startDate), 'dd/mm/yyyy') == format(new Date(endDate), 'dd/mm/yyyy')) {
- // checks if start time is ahead of end time
- if (type === 'start') {
- if (_time.HH && Number(_time.HH) > Number(endTime.HH)) {
- endTimePayload.HH = _time.HH
- endTimeChanges = true
- }
- if (
- // also check the hour
- _time.HH &&
- Number(_time.HH) >= Number(endTime.HH) &&
- // check the minutes
- _time.mm &&
- Number(_time.mm) > Number(endTime.mm)
- ) {
- endTimePayload.mm = _time.mm
- endTimeChanges = true
- }
- if (
- // also check the hour
- _time.HH &&
- Number(_time.HH) >= Number(endTime.HH) &&
- // check the minutes
- _time.mm &&
- Number(_time.mm) >= Number(endTime.mm) &&
- // check the seconds
- _time.ss &&
- Number(_time.ss) > Number(endTime.ss)
- ) {
- endTimePayload.ss = _time.ss
- endTimeChanges = true
- }
- }
- if (type === 'end') {
- if (_time.HH && Number(_time.HH) < Number(startTime.HH)) {
- startTimePayload.HH = _time.HH
- startTimeChanges = true
- }
- if (
- // also check the hour
- _time.HH &&
- Number(_time.HH) <= Number(startTime.HH) &&
- // check the minutes
- _time.mm &&
- Number(_time.mm) < Number(startTime.mm)
- ) {
- startTimePayload.mm = _time.mm
- startTimeChanges = true
- }
- if (
- // also check the hour
- _time.HH &&
- Number(_time.HH) <= Number(startTime.HH) &&
- // check the minutes
- _time.mm &&
- Number(_time.mm) <= Number(startTime.mm) &&
- // check the seconds
- _time.ss &&
- Number(_time.ss) < Number(startTime.ss)
- ) {
- startTimePayload.ss = _time.ss
- startTimeChanges = true
- }
- }
- }
- setTime({ ..._time })
- if (endTimeChanges) {
- setEndTime({ ...endTimePayload })
- }
- if (startTimeChanges) {
- setStartTime({ ...startTimePayload })
- }
- setFocus(false)
- }
- function handleOnChange(value: string, valueType: TimeType) {
- const payload = {
- HH: time.HH,
- mm: time.mm,
- ss: time.ss,
- }
- if (value.length > 2) return
- switch (valueType) {
- case 'HH':
- if (value && Number(value) > 23) return
- break
- case 'mm':
- if (value && Number(value) > 59) return
- break
- case 'ss':
- if (value && Number(value) > 59) return
- break
- default:
- break
- }
- payload[valueType] = value
- setTime({ ...payload })
- }
- const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
- event.target.select()
- setFocus(true)
- // Prevent parent dialog from stealing focus
- event.stopPropagation()
- }
- const handleClick = (event: React.MouseEvent<HTMLInputElement>) => {
- event.stopPropagation()
- }
- const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
- // Allow only numbers and navigation keys
- const allowedKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Enter']
- const isNumber = /^[0-9]$/.test(event.key)
- if (!isNumber && !allowedKeys.includes(event.key)) {
- event.preventDefault()
- }
- // Prevent parent dialog from stealing focus on keydown
- event.stopPropagation()
- }
- const handleInput = (event: React.FormEvent<HTMLInputElement>) => {
- // Prevent parent dialog from stealing focus on input
- event.stopPropagation()
- }
- function handlePaste(event: ClipboardEvent) {
- event.preventDefault()
- event.stopPropagation()
- navigator.clipboard.readText().then((text) => {
- let date: null | Date = null
- if (isUnixMicro(text)) {
- date = new Date(unixMicroToIsoTimestamp(text))
- } else {
- date = new Date(Number(text))
- }
- if (isNaN(date.getTime())) {
- console.warn('Invalid date or timestamp in clipboard')
- return
- }
- if (date) {
- // Offset the date by 1s to make sure it will find the logs in that second
- if (type === 'start') {
- date.setSeconds(date.getSeconds() - 1)
- }
- if (type === 'end') {
- date.setSeconds(date.getSeconds() + 1)
- }
- setTime({
- HH: date.getHours().toString().padStart(2, '0'),
- mm: date.getMinutes().toString().padStart(2, '0'),
- ss: date.getSeconds().toString().padStart(2, '0'),
- })
- }
- })
- }
- useEffect(() => {
- document.addEventListener('paste', handlePaste)
- return () => document.removeEventListener('paste', handlePaste)
- }, [startDate, endDate])
- return (
- <div
- className={`
- flex h-7 items-center justify-center
- gap-0 rounded-sm border border-strong bg-surface-100 text-xs text-foreground-light
- ${focus && ' border-stronger outline outline-2 outline-border'}
- hover:border-stronger transition-colors
- `}
- >
- <div className="mr-1 text-foreground-lighter">
- <Clock size={14} strokeWidth={1.5} />
- </div>
- <input
- type="text"
- onBlur={() => handleOnBlur()}
- onFocus={handleFocus}
- onClick={handleClick}
- onKeyDown={handleKeyDown}
- onInput={handleInput}
- pattern="[0-23]*"
- placeholder="00"
- onChange={(e) => handleOnChange(e.target.value, 'HH')}
- aria-label="Hours"
- className={inputStyle}
- value={time.HH}
- />
- <span className="text-foreground-lighter">:</span>
- <input
- type="text"
- onBlur={() => handleOnBlur()}
- onFocus={handleFocus}
- onClick={handleClick}
- onKeyDown={handleKeyDown}
- onInput={handleInput}
- pattern="[0-59]*"
- placeholder="00"
- onChange={(e) => handleOnChange(e.target.value, 'mm')}
- aria-label="Minutes"
- className={inputStyle}
- value={time.mm}
- />
- <span className="text-foreground-lighter">:</span>
- <input
- type="text"
- onBlur={() => handleOnBlur()}
- onFocus={handleFocus}
- onClick={handleClick}
- onKeyDown={handleKeyDown}
- onInput={handleInput}
- pattern="[0-59]*"
- placeholder="00"
- onChange={(e) => handleOnChange(e.target.value, 'ss')}
- aria-label="Seconds"
- className={inputStyle}
- value={time.ss}
- />
- </div>
- )
- }
|