import React, { useEffect } from 'react'
import ReactDatePicker from 'react-datepicker'
import type { ReactDatePickerProps } from 'react-datepicker'
import styled from 'styled-components'
import styles from '../../utils/datepicker.styles'
import { Input } from '../../atoms/input'
import { Button } from '../../atoms/button'
import { Icon } from '../../atoms/icon'
import { InputGroup } from '../form-group'
import { cssClass } from '../../utils/css-class'
import { PropertyType } from '../../utils'
import useDatePicker from './useDatePicker'
const DatePickerWrapper = styled.div`
position: absolute;
right: 0;
top: ${({ theme }): string => theme.space.xxl};
`
const StyledDatePicker = styled(InputGroup)`
${styles};
position: relative;
&.active ${Input}, &.active ${Button} {
z-index: 101;
}
& .react-datepicker {
border-radius: 0;
border: 1px solid ${({ theme }): string => theme.colors.primary100};
padding: ${({ theme }): string => theme.space.default};
font-family: ${({ theme }): string => theme.font};
z-index: 101;
}
& .react-datepicker__navigation--next {
border-left-color: ${({ theme }): string => theme.colors.primary60};
top: 16px;
}
& .react-datepicker__navigation--next:hover {
border-left-color: ${({ theme }): string => theme.colors.primary100};
}
& .react-datepicker__navigation--previous {
border-right-color: ${({ theme }): string => theme.colors.primary60};
top: 16px;
}
& .react-datepicker__navigation--previous:hover {
border-right-color: ${({ theme }): string => theme.colors.primary100};
}
& .react-datepicker__navigation {
outline: none;
}
& .react-datepicker__year-read-view--down-arrow {
top: 5px;
}
& .react-datepicker__header {
background: ${({ theme }): string => theme.colors.white};
font-size: ${({ theme }): string => theme.fontSizes.default};
border: none;
}
& .react-datepicker__current-month {
font-weight: normal;
padding-bottom: ${({ theme }): string => theme.space.lg};
}
& .react-datepicker__month {
margin-top: 0;
}
& .react-datepicker__day-name {
color: ${({ theme }): string => theme.colors.primary60};
}
& .react-datepicker__day--outside-month {
color: ${({ theme }): string => theme.colors.grey40};
}
& .react-datepicker__day--today.react-datepicker__day--keyboard-selected {
color: ${({ theme }): string => theme.colors.white};
}
& .react-datepicker__day--selected {
color: ${({ theme }): string => theme.colors.white};
}
& .react-datepicker__day--keyboard-selected:not(.react-datepicker__day--today) {
background: none;
color: ${({ theme }): string => theme.colors.grey100};
}
& .react-datepicker__day:hover,
& .react-datepicker__day {
border-radius: 15px;
}
& .react-datepicker__day--selected {
background: ${({ theme }): string => theme.colors.primary100};
border-radius: 15px;
color: ${({ theme }): string => theme.colors.white};
}
`
const Overlay = styled.div`
opacity: 0;
background: #ccc;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 100;
&.hidden {
display: none;
}
`
type CustomProps = Partial<Omit<ReactDatePickerProps, 'value' | 'disabled' | 'onChange'>>
/**
* Props for DatePicker
*
* @memberof DatePicker
* @alias DatePickerProps
* @property {any} {...} Any custom props to pass down to the ReactDatePicker
* @see https://reactdatepicker.com/
*/
export type DatePickerProps = CustomProps & {
/**
* If datepicker should be disabled
*/
disabled?: boolean;
/**
* selected date
*/
value?: string | Date;
/**
* on change callback taking Date object as a date
*/
onChange: (date: Date) => void;
/**
* property type, could be either 'date' or 'datetime'
*/
propertyType?: PropertyType;
}
/**
* @classdesc
*
* <img src="components/date-picker.png" />
*
*
* Component responsible for showing dates. It is a wrapper to
* [react datepicker]{@link https://reactdatepicker.com/}.
*
* ### Usage
*
* ```javascript
* import { DatePicker, DatePickerProps } from '@adminjs/design-system'
* ```
*
* @component
* @subcategory Molecules
* @see https://reactdatepicker.com/
* @see {@link https://storybook.adminjs.co/?path=/story/designsystem-molecules-datepicker--default Storybook}
* @see DatePickerProps
* @hideconstructor
*
* @example
* return (
* <Box width={1/2} height="300px">
* <DatePicker onChange={(date) => console.log(date)}/>
* </Box>
* )
* @section design-system
*/
const DatePicker: React.FC<DatePickerProps> = (props) => {
const { value, onChange, disabled, propertyType, ...other } = props
const [inputValue, setInputValue] = React.useState('')
const {
date,
dateString,
setCalendarVisible,
isCalendarVisible,
onDateChange,
} = useDatePicker({ value, disabled, propertyType, onChange })
useEffect(() => {
// Only update input value if date is selected via the date picker
if (dateString && new Date(dateString).valueOf() !== new Date(inputValue).valueOf()) {
setInputValue(dateString)
}
}, [dateString])
return (
<>
<Overlay
onClick={(): void => setCalendarVisible(false)}
className={isCalendarVisible ? 'visible' : 'hidden'}
/>
<StyledDatePicker className={cssClass('DatePicker', isCalendarVisible ? 'active' : 'normal')}>
<Input
value={inputValue}
onChange={(event): void => {
const newValue = new Date(event.target.value)
setInputValue(event.target.value)
// Check if input value is a valid date
// eslint-disable-next-line no-restricted-globals
if (!isNaN(newValue.valueOf())) {
onChange(new Date(event.target.value))
}
}}
onFocus={(): void => setCalendarVisible(true)}
disabled={disabled}
/>
<Button
variant="primary"
type="button"
size="icon"
disabled={disabled}
onClick={() => setCalendarVisible(!isCalendarVisible)}
>
<Icon icon="Calendar" />
</Button>
{isCalendarVisible && (
<DatePickerWrapper>
<ReactDatePicker
selected={date}
onChange={onDateChange}
inline
showTimeInput={propertyType === 'datetime'}
{...other}
/>
</DatePickerWrapper>
)}
</StyledDatePicker>
</>
)
}
export { DatePicker }
export default DatePicker
Source