UNPKG

@yamatomo/chakra-react-datepicker

Version:
404 lines (403 loc) 15.5 kB
import { Box, useBreakpointValue, useColorMode, Input, } from '@chakra-ui/react'; import React, { useEffect, useState, forwardRef } from 'react'; import ReactDatePicker from 'react-datepicker'; const datePickerThemes = { light: { gray100: 'gray.100', gray200: 'gray.200', gray300: 'gray.300', gray400: 'gray.400', gray500: 'gray.500', color300: 'blue.300', color500: 'blue.500', color600: 'blue.600', header: 'white', text: 'gray.800', negativeText: 'whiteAlpha.900', monthBackground: 'white', outsideDay: '#9f9696', }, dark: { gray100: 'gray.700', gray200: 'gray.600', gray300: 'gray.500', gray400: 'gray.400', gray500: 'gray.300', color300: 'blue.200', color500: 'blue.300', color600: 'blue.500', header: 'gray.700', text: 'whiteAlpha.900', negativeText: 'whiteAlpha.900', monthBackground: 'gray.700', outsideDay: '#9f9696', }, }; const ChakraInputWithRef = (props, ref) => (React.createElement(Input, { ...props, ref: ref })); const CustomInput = forwardRef(ChakraInputWithRef); const DatePicker = ({ rootProps, inputProps, datePickerSize: datePickerSizeProps = 'md', datePickerColorSchema, extendDatePickerTheme, ...datePickerProps }) => { const colorMode = useColorMode().colorMode; const isLight = colorMode === 'light'; const datepickerSize = useDetermineSize(datePickerSizeProps); if (!datepickerSize) { return null; } const datePickerTheme = customizeDatePickerTheme(datePickerThemes[isLight ? 'light' : 'dark'], datePickerColorSchema, extendDatePickerTheme ? (theme) => extendDatePickerTheme(colorMode, theme) : undefined); const normalizedInputProps = { ...inputProps, isDisabled: datePickerProps.disabled }; const timeWidth = getTimeWidth(datepickerSize); const container = { borderColor: datePickerTheme.gray200, }; const dayContainer = { padding: 1.5, bg: datePickerTheme.monthBackground, margin: 0, }; const day = { ...getDayCellSize(datepickerSize), color: datePickerTheme.text, hoverBg: datePickerTheme.gray200, selecting: { bg: datePickerTheme.color300, }, selected: { bg: datePickerTheme.color500, fontWeight: 'normal', color: datePickerTheme.negativeText, hover: { bg: datePickerTheme.color600, }, }, disabled: { bg: 'unset', opacity: 0.2, cursor: 'not-allowed', }, }; const header = { topSpace: 3, borderColor: datePickerTheme.gray300, fontWeight: 600, fontSize: '1rem', color: datePickerTheme.text, bg: datePickerTheme.header, }; const navigationButton = { size: datepickerSize === 'xs' ? 5 : 7, leftRightSpace: '0.8rem', color: datePickerTheme.gray400, hover: { color: datePickerTheme.gray500, }, }; const sx = { '.react-datepicker': { minWidth: 'max-content', fontFamily: 'unset', fontSize: datepickerSize === 'xs' ? 'xs' : 'md', borderColor: container.borderColor, boxShadow: 'sm', bg: dayContainer.bg, margin: dayContainer.margin, color: datePickerTheme.text, }, '.react-datepicker__input-container': { display: 'block', '.react-datepicker__close-icon': { // clear button '&::after': { backgroundColor: 'unset', borderRadius: 'unset', fontSize: inputProps?.size === 'lg' ? '2xl' : inputProps?.size === 'xs' ? 'md' : 'xl', color: datePickerTheme.gray300, h: '20px', w: '20px', }, '&:hover::after': { color: datePickerTheme.gray400, }, }, }, '.react-datepicker__header': { paddingBlockStart: header.topSpace, borderColor: header.borderColor, bg: header.bg, '.react-datepicker__header__dropdown': { // year/month picker marginBlockStart: 2, display: 'flex', justifyContent: 'center', _empty: { display: 'none', }, [multipleSelector('.react-datepicker__month-dropdown-container', '.react-datepicker__year-dropdown-container')]: { cursor: 'pointer', borderRadius: 'md', paddingInlineStart: 1, paddingInlineEnd: 1, }, [multipleSelector('.react-datepicker__month-read-view', '.react-datepicker__year-read-view')]: { display: 'flex', flexDirection: 'row-reverse', paddingInlineEnd: 4, paddingInlineStart: 1, _hover: { bg: datePickerTheme.gray200, [multipleSelector('.react-datepicker__month-read-view--down-arrow', '.react-datepicker__year-read-view--down-arrow')]: { borderColor: navigationButton.hover.color, }, }, }, [multipleSelector('.react-datepicker__month-read-view--down-arrow', '.react-datepicker__year-read-view--down-arrow')]: { position: 'relative', top: 2, right: '-0.5rem', borderColor: navigationButton.color, borderWidth: '2px 2px 0 0', h: '7px', w: '7px', }, [multipleSelector('.react-datepicker__month-dropdown', '.react-datepicker__year-dropdown')]: { bg: datePickerTheme.monthBackground, borderColor: datePickerTheme.gray200, boxShadow: 'md', '& > div': { paddingBlockStart: 1, paddingBlockEnd: 1, color: datePickerTheme.text, '&:hover': { bg: datePickerTheme.gray200, }, }, }, }, [multipleSelector('.react-datepicker__current-month', '.react-datepicker-time__header', '.react-datepicker-year-header')]: { fontWeight: header.fontWeight, color: header.color, fontSize: header.fontSize, }, '.react-datepicker__day-names': { padding: dayContainer.padding, paddingBlockEnd: 0, '.react-datepicker__day-name': { w: day.width, lineHeight: day.lineHeight, color: day.color, }, }, }, /* / .react-datepicker__header */ '.react-datepicker__navigation': { w: navigationButton.size, h: navigationButton.size, top: header.topSpace, marginTop: datepickerSize !== 'xs' ? -1 : undefined, color: 'transparent', _hover: { '.react-datepicker__navigation-icon::before': { borderColor: navigationButton.hover.color, }, }, '.react-datepicker__navigation-icon': { fontSize: 'unset', w: '100%', h: '100%', top: 'unset', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', _before: { left: 'unset', top: 'unset', right: 'unset', bottom: 'unset', borderColor: navigationButton.color, }, }, '&.react-datepicker__navigation--previous': { left: navigationButton.leftRightSpace, }, '&.react-datepicker__navigation--next': { right: navigationButton.leftRightSpace, }, '&.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button)': { right: `calc(${timeWidth}px - -0.8rem)`, }, }, /* / .react-datepicker__navigation' */ [multipleSelector('.react-datepicker__month-read-view--selected-month', '.react-datepicker__year-read-view--selected-year')]: { fontWeight: header.fontWeight, color: header.color, }, '.react-datepicker__month': { ...dayContainer, borderBottomRightRadius: 'md', borderBottomLeftRadius: 'md', '.react-datepicker__day': { w: day.width, lineHeight: day.lineHeight, color: day.color, _hover: { bg: day.hoverBg, }, '&.react-datepicker__day--disabled': { ...day.disabled, _hover: { ...day.disabled, }, }, [multipleSelector('&.react-datepicker__day--in-selecting-range', '&.react-datepicker__month-text--in-selecting-range', '&.react-datepicker__day--keyboard-selected', '&.react-datepicker__month-text--keyboard-selected', '&.react-datepicker__quarter-text--keyboard-selected', '&.react-datepicker__year-text--keyboard-selected')]: { bg: day.selecting.bg, }, [multipleSelector('&.react-datepicker__day--selected:not(.react-datepicker__day--disabled)', '&.react-datepicker__day--in-range:not(.react-datepicker__day--disabled)', '&.react-datepicker__month-text--selected:not(.react-datepicker__day--disabled)', '&.react-datepicker__month-text--in-range:not(.react-datepicker__day--disabled)')]: { bg: day.selected.bg, fontWeight: day.selected.fontWeight, color: day.selected.color, _hover: { bg: day.selected.hover.bg, }, }, }, '.react-datepicker__day--outside-month': { // outside month day color: datePickerTheme.outsideDay, }, }, /* / .react-datepicker__month */ '.react-datepicker__time-container': { borderColor: header.borderColor, w: timeWidth, '.react-datepicker__time': { bg: dayContainer.bg, margin: dayContainer.margin, '.react-datepicker__time-box ': { w: '100%', 'ul.react-datepicker__time-list': { 'li.react-datepicker__time-list-item': { h: 'auto', padding: 2, color: day.color, _hover: { bg: day.hoverBg, }, }, 'li.react-datepicker__time-list-item--selected:not(.react-datepicker__day--disabled)': { bg: day.selected.bg, fontWeight: day.selected.fontWeight, color: day.selected.color, _hover: { bg: day.selected.hover.bg, }, }, }, }, }, }, /* / .react-datepicker__time-container */ '.react-datepicker__triangle': { // https://github.com/Hacker0x01/react-datepicker/issues/3176 '@media screen and (min-width:420px)': { left: `${['top-end', 'bottom-end'].includes(datePickerProps.popperPlacement || '') ? '3rem' : '-3rem'} !important`, }, }, '.react-datepicker-popper[data-placement^=bottom]': { '.react-datepicker__triangle': { '&::before': { // top triangle (border) borderBottomColor: container.borderColor, }, '&::after': { // top triangle (fill color) borderBottomColor: header.bg, top: '1px', }, }, }, '.react-datepicker-popper[data-placement^=top]': { '.react-datepicker__triangle': { '&::before': { // bottom triangle (border) borderTopColor: container.borderColor, }, '&::after': { // bottom triangle (fill color) borderTopColor: dayContainer.bg, }, }, }, '.react-datepicker__input-time-container': { m: 0, p: 3, '.react-datepicker-time__caption': { color: datePickerTheme.text, }, }, '.react-datepicker__portal': { bg: 'blackAlpha.600', }, }; return (React.createElement(Box, { sx: sx, w: '100%', lineHeight: 'normal', ...rootProps }, React.createElement(ReactDatePicker, { ...datePickerProps, customInput: datePickerProps.customInput || React.createElement(CustomInput, { ...normalizedInputProps }) }))); }; const getTimeWidth = (size) => { if (size === 'xl') { return 120; } if (size === 'sm' || size === 'xs') { return 75; } return 105; }; const getDayCellSize = (size) => { const baseSize = getBaseSize(size); if (size === 'xl') { return { width: baseSize, lineHeight: '3rem' }; } return { width: baseSize, lineHeight: baseSize }; }; const getBaseSize = (size) => { if (size === 'xl') { return 12; } if (size === 'sm') { return 8; } if (size === 'xs') { return 6; } return 10; }; const useDetermineSize = (size) => { const [tick, setTick] = useState(0); const breakPoints = size == null || typeof size === 'string' ? [size || 'md'] : size; const result = useBreakpointValue(breakPoints) ?? undefined; useEffect(() => { setTick((v) => v + 1); }, []); if (tick === 0) { // for SSR return undefined; } return result; }; const multipleSelector = (...selectors) => selectors.join(','); const customizeDatePickerTheme = (theme, colorScheme, extendDatePickerTheme) => { let customizedTheme = theme; if (colorScheme) { customizedTheme = { ...customizedTheme, color300: `${colorScheme}.300`, color500: `${colorScheme}.500`, color600: `${colorScheme}.600`, }; } if (extendDatePickerTheme) { customizedTheme = extendDatePickerTheme(customizedTheme); } return customizedTheme; }; export { DatePicker };