diff --git a/apps/react/src/app/components/calendar-input/calendar-input.scss b/apps/react/src/app/components/calendar-input/calendar-input.scss index 8f627cf9f..605a44a24 100644 --- a/apps/react/src/app/components/calendar-input/calendar-input.scss +++ b/apps/react/src/app/components/calendar-input/calendar-input.scss @@ -1,30 +1,91 @@ .calendar-input { width: 100%; + &__wrapper { + position: relative; + display: flex; + align-items: center; + width: 100%; + gap: 0; + } + + &__native-input { + flex: 1; + padding: 8px 12px; + border: 1px solid #e0e0e0; + border-right: none; + border-radius: 4px 0 0 4px; + font-size: 14px; + font-family: inherit; + transition: border-color 0.2s ease; + + &:focus { + border-color: #1976d2; + outline: none; + box-shadow: inset 0 0 0 2px rgba(25, 118, 210, 0.1); + } + + &:disabled { + background-color: #f5f5f5; + color: #999; + cursor: not-allowed; + } + + &::placeholder { + color: #999; + } + } + + &__calendar-wrapper { + display: flex; + align-items: center; + border: 1px solid #e0e0e0; + border-left: none; + border-radius: 0 4px 4px 0; + background: white; + padding-right: 4px; + + &:has(:focus) { + border-color: #1976d2; + } + } + :global { - .calendar-input__field { - width: 100%; + .calendar-input__calendar { + width: auto; + + .p-calendar-trigger { + background: none; + border: none; + cursor: pointer; + padding: 4px 8px; + color: #1976d2; + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s ease; + + &:hover { + color: #1565c0; + } + + &:focus { + outline: none; + } + + .pi { + font-size: 16px; + } + } + + .p-inputtext { + display: none; + } } .p-calendar { width: 100%; - - .p-inputtext { - width: 100%; - padding: 8px 12px; - border: 1px solid #e0e0e0; - border-radius: 4px; - font-size: 14px; - - &:focus { - border-color: #1976d2; - outline: none; - } - } - - .p-calendar-trigger { - padding: 8px 12px; - } } } } diff --git a/apps/react/src/app/components/calendar-input/calendar-input.tsx b/apps/react/src/app/components/calendar-input/calendar-input.tsx index 897fdcb58..b9c7e575e 100644 --- a/apps/react/src/app/components/calendar-input/calendar-input.tsx +++ b/apps/react/src/app/components/calendar-input/calendar-input.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useRef, useEffect, useState } from 'react' import { Calendar } from 'primereact/calendar' import './calendar-input.scss' @@ -21,20 +21,103 @@ export const CalendarInput: React.FC = ({ disabled = false, 'data-testid': dataTestId, }) => { + const inputRef = useRef(null) + const calendarRef = useRef(null) + const [internalValue, setInternalValue] = useState('') + + // Format Date to ISO string (YYYY-MM-DD) + const formatDateToISO = (date: Date | null): string => { + if (!date) return '' + const d = new Date(date) + const month = String(d.getMonth() + 1).padStart(2, '0') + const day = String(d.getDate()).padStart(2, '0') + const year = d.getFullYear() + return `${year}-${month}-${day}` + } + + // Parse ISO string to Date + const parseISOToDate = (dateStr: string): Date | null => { + if (!dateStr || dateStr.trim() === '') return null + + const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/) + if (!match) return null + + const [, year, month, day] = match + const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)) + + // Validate date is valid + if (isNaN(date.getTime())) return null + + return date + } + + // Update input display when value prop changes + useEffect(() => { + const formattedValue = formatDateToISO(value) + setInternalValue(formattedValue) + if (inputRef.current) { + inputRef.current.value = formattedValue + } + }, [value]) + + // Handle manual input change from user typing or clearing + const handleInputChange = (e: React.ChangeEvent) => { + const inputValue = e.target.value + setInternalValue(inputValue) + + // If input is cleared + if (inputValue === '' || inputValue.trim() === '') { + onChange(null) + return + } + + // Try to parse the input + const parsedDate = parseISOToDate(inputValue) + if (parsedDate) { + onChange(parsedDate) + } + // If invalid format, we don't call onChange - let user fix it + } + + // Handle calendar selection + const handleCalendarChange = (e: any) => { + const selectedDate = e.value as Date | null + onChange(selectedDate) + // Focus back on input after selection + setTimeout(() => { + inputRef.current?.focus() + }, 0) + } + return (
- onChange(e.value as Date | null)} - dateFormat="dd.mm.yy" - minDate={minDate} - maxDate={maxDate} - placeholder={placeholder} - disabled={disabled} - showIcon - inline={false} - className="calendar-input__field" - /> +
+ +
+ +
+
) }