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; position: absolute;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
bottom: 0; bottom: 2px;
} }
.${calendarClasses.disabled} { .${calendarClasses.disabled} {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,7 +43,9 @@ export const DatePicker: React.FC<DatePickerProps> & {
...props ...props
}) => { }) => {
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const calendarIconRef = useRef<HTMLSpanElement>(null)
const [openCalendar, setOpenCalendar] = useState(false) const [openCalendar, setOpenCalendar] = useState(false)
const isControlled = typeof valueProp !== 'undefined'
const input = useInput({ const input = useInput({
value: valueProp, value: valueProp,
@ -77,8 +79,10 @@ export const DatePicker: React.FC<DatePickerProps> & {
variant={variant} variant={variant}
icon={withCalendar && <CalendarIcon color="primary" />} icon={withCalendar && <CalendarIcon color="primary" />}
onIconClick={() => setOpenCalendar((prev) => !prev)} 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} onChange={input.onChange}
calendarIconRef={calendarIconRef}
{...props} {...props}
> >
<Portal id="calendar"> <Portal id="calendar">
@ -86,7 +90,18 @@ export const DatePicker: React.FC<DatePickerProps> & {
<Calendar <Calendar
onChange={(date) => handleDateChange(date)} onChange={(date) => handleDateChange(date)}
open={openCalendar} 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} handleRef={ref}
value={input.value} value={input.value}
disabled={props.disabled} 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 date = new Date(value ?? undefined)
const isValid = !Number.isNaN(+date) const isValid = !Number.isNaN(+date) && date >= minDate && date <= maxDate
return { return {
isValid, isValid,
date: isValid ? date : new Date(), date: isValid ? date : new Date(),
} }
} }
export const removeDateTimezoneOffset = (date: Date) => export const removeDateTimezoneOffset = (date: Date) =>
new Date(+date - date.getTimezoneOffset() * 60 * 1000) new Date(+date - date.getTimezoneOffset() * 60 * 1000)