datePickerField.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import {lazy, Suspense} from 'react';
  2. import type {OnChangeProps} from 'react-date-range';
  3. import styled from '@emotion/styled';
  4. import moment from 'moment';
  5. import DropdownMenu from 'app/components/dropdownMenu';
  6. import LoadingIndicator from 'app/components/loadingIndicator';
  7. import Placeholder from 'app/components/placeholder';
  8. import {IconCalendar} from 'app/icons';
  9. import {inputStyles} from 'app/styles/input';
  10. import space from 'app/styles/space';
  11. import InputField, {onEvent} from './inputField';
  12. type Props = Omit<InputField['props'], 'field'>;
  13. function handleChangeDate(
  14. onChange: onEvent,
  15. onBlur: onEvent,
  16. date: OnChangeProps,
  17. close: Function
  18. ) {
  19. onChange(date);
  20. onBlur(date);
  21. // close dropdown menu
  22. close();
  23. }
  24. const Calendar = lazy(() => import('./calendarField'));
  25. export default function DatePickerField(props: Props) {
  26. return (
  27. <InputField
  28. {...props}
  29. field={({onChange, onBlur, value, id}) => {
  30. const dateObj = new Date(value);
  31. const inputValue = !isNaN(dateObj.getTime()) ? dateObj : new Date();
  32. const dateString = moment(inputValue).format('LL');
  33. return (
  34. <DropdownMenu keepMenuOpen>
  35. {({isOpen, getRootProps, getActorProps, getMenuProps, actions}) => (
  36. <div {...getRootProps()}>
  37. <InputWrapper id={id} {...getActorProps()} isOpen={isOpen}>
  38. <StyledInput readOnly value={dateString} />
  39. <CalendarIcon>
  40. <IconCalendar />
  41. </CalendarIcon>
  42. </InputWrapper>
  43. {isOpen && (
  44. <CalendarMenu {...getMenuProps()}>
  45. <Suspense
  46. fallback={
  47. <Placeholder width="332px" height="282px">
  48. <LoadingIndicator />
  49. </Placeholder>
  50. }
  51. >
  52. <Calendar
  53. date={inputValue}
  54. onChange={date =>
  55. handleChangeDate(onChange, onBlur, date, actions.close)
  56. }
  57. />
  58. </Suspense>
  59. </CalendarMenu>
  60. )}
  61. </div>
  62. )}
  63. </DropdownMenu>
  64. );
  65. }}
  66. />
  67. );
  68. }
  69. type InputWrapperProps = {
  70. isOpen: boolean;
  71. };
  72. const InputWrapper = styled('div')<InputWrapperProps>`
  73. ${inputStyles}
  74. cursor: text;
  75. display: flex;
  76. z-index: ${p => p.theme.zIndex.dropdownAutocomplete.actor};
  77. ${p => p.isOpen && 'border-bottom-left-radius: 0'}
  78. `;
  79. const StyledInput = styled('input')`
  80. border: none;
  81. outline: none;
  82. flex: 1;
  83. `;
  84. const CalendarMenu = styled('div')`
  85. display: flex;
  86. background: ${p => p.theme.background};
  87. position: absolute;
  88. left: 0;
  89. border: 1px solid ${p => p.theme.border};
  90. border-top: none;
  91. z-index: ${p => p.theme.zIndex.dropdownAutocomplete.menu};
  92. margin-top: -1px;
  93. .rdrMonthAndYearWrapper {
  94. height: 50px;
  95. padding-top: 0;
  96. }
  97. `;
  98. const CalendarIcon = styled('div')`
  99. display: flex;
  100. align-items: center;
  101. justify-content: center;
  102. padding: ${space(1)};
  103. `;