fix: fix multiple issues and update styles

This commit is contained in:
jongomez 2023-09-29 10:10:07 +01:00
parent 7adcfeb5e5
commit e30959a103
8 changed files with 62 additions and 18 deletions

View File

@ -117,7 +117,7 @@ export const CalendarStyles = css`
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 0;
bottom: 2px;
}
.${calendarClasses.disabled} {

View File

@ -22,6 +22,9 @@ export type CalendarProps = Omit<
handleRef: React.RefObject<HTMLElement>
size?: 'large' | 'medium' | 'small'
onClose?: () => void
onCalendarClickaway?: (event: Event) => void
minDate?: Date
maxDate?: Date
}
export const Calendar: React.FC<CalendarProps> & {
@ -34,17 +37,28 @@ export const Calendar: React.FC<CalendarProps> & {
disabled = false,
onChange,
onClose,
onCalendarClickaway,
// minDate and maxDate are necessary because onDateFocus freaks out with small/large date values.
minDate = new Date(1900, 0, 1),
maxDate = new Date(2100, 0, 1),
children,
...props
}) => {
const ref = useRef<HTMLDivElement>(null)
const [style, setStyle] = useState<React.CSSProperties>({})
const [value, setValue] = useState<Date | null>(
valueProp ? safeConvertDateToString(valueProp).date : null,
valueProp
? safeConvertDateToString(valueProp, minDate, maxDate).date
: null,
)
const isOpenControlled = typeof open !== 'undefined'
useClickAway(ref, (event) => {
if (!open || event.composedPath().includes(handleRef.current!)) return
if (!open) return
onCalendarClickaway && onCalendarClickaway(event)
if (isOpenControlled) return
onClose && onClose()
})
@ -84,7 +98,7 @@ export const Calendar: React.FC<CalendarProps> & {
useEffect(() => {
if (typeof valueProp === 'undefined') return
const { date } = safeConvertDateToString(valueProp)
const { date } = safeConvertDateToString(valueProp, minDate, maxDate)
setValue(date)
}, [valueProp])

View File

@ -64,7 +64,7 @@ export const Day = ({ day, date, disabled = false }: DayProps) => {
<Typography variant="label2">{parseInt(day, 10)}</Typography>
{isToday && (
<Typography variant="label2" className={calendarClasses.todayIndicator}>
</Typography>
)}
</button>

View File

@ -4,6 +4,7 @@ export const dateFieldClasses = {
inputContainer: `lsd-date-field__input-container`,
input: `lsd-date-field__input-container__input`,
inputFilled: `lsd-date-field__input-container__input--filled`,
icon: `lsd-date-field__input-container__icon`,
iconButton: `lsd-date-field__input-container__icon-button`,

View File

@ -42,6 +42,8 @@ export const DateFieldStyles = css`
color: rgb(var(--lsd-text-primary));
background: none;
width: 100%;
opacity: 0.4;
transition: opacity 0.2s ease-in-out;
}
.${dateFieldClasses.input}::-webkit-inner-spin-button,
@ -116,11 +118,9 @@ export const DateFieldStyles = css`
}
}
.${dateFieldClasses.input}::-webkit-datetime-edit-month-field:focus,
.${dateFieldClasses.input}::-webkit-datetime-edit-day-field:focus,
.${dateFieldClasses.input}::-webkit-datetime-edit-year-field:focus {
color: rgb(var(--lsd-text-primary));
opacity: 0.4;
.${dateFieldClasses.input}:invalid, .${dateFieldClasses.inputFilled} {
color: rgb(var(--lsd-border-primary));
opacity: 1;
}
.${dateFieldClasses.error}

View File

@ -24,6 +24,7 @@ export type DateFieldProps = Omit<
onIconClick?: () => void
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
variant?: 'outlined' | 'outlined-bottom'
calendarIconRef?: React.RefObject<HTMLSpanElement>
}
export const DateField: React.FC<DateFieldProps> & {
@ -48,7 +49,12 @@ export const DateField: React.FC<DateFieldProps> & {
...props
}) => {
const ref = useRef<HTMLInputElement>(null)
const input = useInput({ defaultValue, value, onChange, ref })
const input = useInput({
defaultValue,
value,
onChange,
ref,
})
const onCancel = () => input.setValue('')
@ -90,15 +96,20 @@ export const DateField: React.FC<DateFieldProps> & {
placeholder={placeholder}
{...inputProps}
ref={ref}
value={input.value}
value={input.value || ''}
onChange={input.onChange}
className={clsx(inputProps.className, dateFieldClasses.input)}
className={clsx(
inputProps.className,
dateFieldClasses.input,
input.filled && dateFieldClasses.inputFilled,
)}
max={inputProps.max || '9999-12-31'}
/>
{icon ? (
<span
className={dateFieldClasses.icon}
onClick={() => !disabled && onIconClick && onIconClick()}
ref={props.calendarIconRef}
>
{icon}
</span>

View File

@ -43,7 +43,9 @@ export const DatePicker: React.FC<DatePickerProps> & {
...props
}) => {
const ref = useRef<HTMLDivElement>(null)
const calendarIconRef = useRef<HTMLSpanElement>(null)
const [openCalendar, setOpenCalendar] = useState(false)
const isControlled = typeof valueProp !== 'undefined'
const input = useInput({
value: valueProp,
@ -77,8 +79,10 @@ export const DatePicker: React.FC<DatePickerProps> & {
variant={variant}
icon={withCalendar && <CalendarIcon color="primary" />}
onIconClick={() => setOpenCalendar((prev) => !prev)}
value={input.value}
// The DateField component is only controlled when the value prop is provided OR the calendar is open.
value={isControlled || openCalendar ? input.value : undefined}
onChange={input.onChange}
calendarIconRef={calendarIconRef}
{...props}
>
<Portal id="calendar">
@ -86,7 +90,18 @@ export const DatePicker: React.FC<DatePickerProps> & {
<Calendar
onChange={(date) => handleDateChange(date)}
open={openCalendar}
onClose={() => setOpenCalendar(false)}
onCalendarClickaway={(event) => {
// If the calendar icon was clicked, return and don't close the calendar here.
// Let the onIconClick above handle the closing.
if (
calendarIconRef.current &&
event?.composedPath().includes(calendarIconRef.current)
) {
return
}
setOpenCalendar(false)
}}
handleRef={ref}
value={input.value}
disabled={props.disabled}

View File

@ -1,13 +1,16 @@
export const safeConvertDateToString = (value: string) => {
export const safeConvertDateToString = (
value: string,
minDate: Date,
maxDate: Date,
) => {
const date = new Date(value ?? undefined)
const isValid = !Number.isNaN(+date)
const isValid = !Number.isNaN(+date) && date >= minDate && date <= maxDate
return {
isValid,
date: isValid ? date : new Date(),
}
}
export const removeDateTimezoneOffset = (date: Date) =>
new Date(+date - date.getTimezoneOffset() * 60 * 1000)