diff --git a/docs/styleguide.config.js b/docs/styleguide.config.js index ee4c491b6..600f9c0a3 100644 --- a/docs/styleguide.config.js +++ b/docs/styleguide.config.js @@ -81,6 +81,7 @@ module.exports = { '../react/ContactPicker', '../react/CozyDialogs', '../react/CozyDialogs/SpecificDialogs', + '../react/DatePicker', '../react/FileImageLoader', '../react/FilePicker', '../react/HistoryRow', diff --git a/package.json b/package.json index d97ad4ee3..d3a188f8c 100644 --- a/package.json +++ b/package.json @@ -156,8 +156,10 @@ }, "dependencies": { "@babel/runtime": "^7.3.4", + "@date-io/date-fns": "1", "@material-ui/core": "4.12.3", "@material-ui/lab": "^4.0.0-alpha.61", + "@material-ui/pickers": "3.3.11", "@popperjs/core": "^2.4.4", "chart.js": "3.7.1", "classnames": "^2.2.5", diff --git a/react/DatePicker/Readme.md b/react/DatePicker/Readme.md new file mode 100644 index 000000000..8c71fecb4 --- /dev/null +++ b/react/DatePicker/Readme.md @@ -0,0 +1,208 @@ +`Breakpoints` & `I18n` Providers are needed for this component + +### Usage + +### Date Picker + +```jsx +import React, { useState } from 'react'; +import DemoProvider from 'cozy-ui/docs/components/DemoProvider'; +import Variants from 'cozy-ui/docs/components/Variants'; +import DatePicker from 'cozy-ui/transpiled/react/DatePicker'; + +const placeholder = isTesting() ? "01/01/2025" : undefined; + +const initialVariants = [ + { enableKeyboard: false, clearable: false, showTodayButton: false, disableFuture: false, disablePast: false, disableToolbar: false, autoOk: false, animateYearScrolling: false, disabled: false, readOnly: false } +]; + +// Example +const [selectedDate, setSelectedDate] = useState(null); + + + {variant => ( + + ) + } + + +``` + +### DateTime Picker + +```jsx +import React, { useState } from 'react'; +import DemoProvider from 'cozy-ui/docs/components/DemoProvider'; +import Variants from 'cozy-ui/docs/components/Variants'; +import DatePicker from 'cozy-ui/transpiled/react/DatePicker'; + +const placeholder = isTesting() ? "01/01/2025" : undefined; + +const initialVariants = [ + { enableKeyboard: false, clearable: false, showTodayButton: false, disableFuture: false, disablePast: false, disableToolbar: false, autoOk: false, animateYearScrolling: false, disabled: false, readOnly: false, ampm: false } +]; + +// Example +const [selectedDate, setSelectedDate] = useState(null); + + + {variant => ( + + ) + } + + +``` + +### Time Picker + +```jsx +import React, { useState } from 'react'; +import DemoProvider from 'cozy-ui/docs/components/DemoProvider'; +import Variants from 'cozy-ui/docs/components/Variants'; +import DatePicker from 'cozy-ui/transpiled/react/DatePicker'; + +const placeholder = isTesting() ? "01/01/2025" : undefined; + +const initialVariants = [ + { enableKeyboard: false, clearable: false, showTodayButton: false, disableFuture: false, disablePast: false, disableToolbar: false, autoOk: false, animateYearScrolling: false, disabled: false, readOnly: false, ampm: false } +]; + +// Example +const [selectedDate, setSelectedDate] = useState(null); + + + {variant => ( + + ) + } + + +``` + +### API + +### Inheritance + +Any prop not recognized by the pickers and their sub-components are passed down to material-ui [TextField](https://v4.mui.com/api/text-field/#textfield-api) component. + +`DateIOType` — date object type of your linked date-io adapter (Date-fns, DayJS, etc.) + +### Props + +#### **Date Picker** + +|name|type|default|description| +|----|----|-------|-----------| +|onChange|(date: DateIOType) => void||onChange callback| +|value|Date||Picker value| +|allowKeyboardControl|Boolean|true|Enables keyboard listener for moving between days in calendar| +|autoOk|Boolean|false|Auto accept date on selection| +|disabled|Boolean||Disable picker and text field| +|disableFuture|Boolean|false|Disable future dates| +|disablePast|boolean|false|Disable past dates| +|disableToolbar|boolean|false|Hide toolbar and show only date/time views| +|emptyLabel|string|""|Message displaying in text field, if null passed (doesn't work in keyboard mode)| +|format|string||Format string| +|initialFocusedDate|Date||Date that will be initially highlighted if null was passed| +|inputVariant|"standard" \| "outlined" \| "filled"||Pass material-ui text field variant down, bypass internal variant prop| +|invalidDateMessage|ReactNode|"Invalid Date Format"|Message, appearing when date cannot be parsed| +|invalidLabel|string|"unknown"|Message displaying in text field if date is invalid (doesn't work in keyboard mode)| +|labelFunc|(date: DateIOType, invalidLabel: string) => string||Dynamic formatter of text field value| +|leftArrowButtonProps|Partial||Props to pass to left arrow button| +|leftArrowIcon|ReactNode||Left arrow icon| +|loadingIndicator|Element||Custom loading indicator| +|maxDate|Date|Date(2100-01-01)|Max selectable date| +|maxDateMessage|ReactNode|"Date should not be after maximal date"|Error message, shown if date is more then maximal date| +|minDate|Date|Date(1900-01-01)|Min selectable date| +|minDateMessage|ReactNode|"Date should not be before minimal date"|Error message, shown if date is less then minimal date| +|onAccept|(date: DateIOType) => void||Callback fired when date is accepted| +|onClose|() => void||On close callback| +|onError|(error: ReactNode, value: DateIOType) => void||Callback fired when new error should be displayed (!! This is a side effect. Be careful if you want to rerender the component)| +|onMonthChange|(date: DateIOType) => void \| Promise||Callback firing on month change. Return promise to render spinner till it will not be resolved| +|onOpen|Date||On open callback| +|onYearChange|Date||Callback firing on year change| +|open|boolean||Controlled picker open state| +|openTo|"date" \| "year" \| "month"||First view to show in DatePicker| +|orientation|"portrait" \| "landscape"|"portrait"|Force rendering in particular orientation| +|PopoverProps|Partial||Popover props passed to material-ui Popover (with variant="inline")| +|readOnly|boolean||Make picker read only| +|renderDay|(day: DateIOType, selectedDate: DateIOType, dayInCurrentMonth: boolean, dayComponent: Element) => Element||Custom renderer for day| +|rightArrowButtonProps|Partial||Props to pass to right arrow button| +|rightArrowIcon|ReactNode||Right arrow icon| +|shouldDisableDate|(day: DateIOType) => boolean||Disable specific date| +|strictCompareDates|boolean|false|Compare dates by the exact timestamp, instead of start/end of date| +|TextFieldComponent|FunctionComponent||Override input component| +|ToolbarComponent|FunctionComponent||Component that will replace default toolbar renderer| +|variant|"dialog" \| "inline" \| "static"|'dialog'|Picker container option| +|views|Array<"year" \| "date" \| "month">||Array of views to show| +___ +
+ +#### **Keyboard Date Picker** + +Additional props for keyboard date picker + +|name|type|default|description| +|----|----|-------|-----------| +|onChange|(date: DateIOType, isValid: boolean) => void||Keyboard onChange callback, that returns the value of the input when it changes and if it is a valid Date| +|InputAdornmentProps|Partial||Props to pass to keyboard input adornment| +|inputValue|string||String value for controlling value with pure input string. Overrides value prop| +|KeyboardButtonProps|Partial||Props to pass to keyboard adornment button| +|keyboardIcon|ReactNode||Icon displaying for open picker button| +|mask|string||Custom mask. Can be used to override generate from format. (e.g. __/__/____ __:__)| +|maskChar|string|"_"|Char string that will be replaced with number (for "_" mask will be "__/__/____")| +|refuse|RegExp|/\[^\\d\]+/gi|Refuse values regexp| +|rifmFormatter|(str: string) => string||Custom formatter to be passed into Rifm component| +___ +
+ +#### **DateTime & Time Picker** + +Additional props for time date picker + +|name|type|default|description| +|----|----|-------|-----------| +|ampm|boolean||12h/24h view for hour selection clock| +___ +
+ +### Modal Wrapper + +|name|type|default|description| +|----|----|-------|-----------| +|cancelLabel|ReactNode|"Cancel"|"CANCEL" label message| +|clearable|boolean||Show clear action in picker dialog| +|clearLabel|ReactNode|"Clear"|"Clear" label message| +|DialogProps|Partial||Props to be passed directly to material-ui Dialog| +|okLabel|ReactNode|"OK"|"OK" label message| +|showTodayButton|boolean||If true today button will be displayed. **Note:** that clear button has higher priority| +|todayLabel|ReactNode|"TODAY"|"TODAY" label message| +___ +
+ +### Additional props + +|name|type|default|description| +|----|----|-------|-----------| +|isValid|() => boolean||Function that returns if the value of the input is a valid Date| +|enableKeyboard|boolean||Enable the keyboard date picker| +|mode|"date" \| "time" \| "dateTime"|"date"|Picker mode| +___ diff --git a/react/DatePicker/helpers.js b/react/DatePicker/helpers.js new file mode 100644 index 000000000..06b6da4cd --- /dev/null +++ b/react/DatePicker/helpers.js @@ -0,0 +1,16 @@ +export const makeFormat = ({ ampm, mode, lang }) => { + switch (mode) { + case 'date': + return lang === 'fr' ? 'dd/LL/yyyy' : 'LL/dd/yyyy' + case 'time': + return lang === 'fr' ? 'HH:mm' : ampm ? 'HH:mm a' : 'HH:mm' + case 'dateTime': + return lang === 'fr' + ? 'dd/LL/yyyy HH:mm' + : ampm + ? 'LL/dd/yyyy HH:mm a' + : 'LL/dd/yyyy HH:mm' + default: + return lang === 'fr' ? 'dd/LL/yyyy' : ampm ? 'LL/dd/yyyy a' : 'LL/dd/yyyy' + } +} diff --git a/react/DatePicker/helpers.spec.js b/react/DatePicker/helpers.spec.js new file mode 100644 index 000000000..16c322ecc --- /dev/null +++ b/react/DatePicker/helpers.spec.js @@ -0,0 +1,71 @@ +import { makeFormat } from './helpers' + +describe('makeFormat', () => { + describe('When lang is "fr"', () => { + it('should return the right format for default mode', () => { + const format = makeFormat({ lang: 'fr' }) + expect(format).toBe('dd/LL/yyyy') + }) + + it('should return the right format for time mode', () => { + const format = makeFormat({ mode: 'time', lang: 'fr' }) + expect(format).toBe('HH:mm') + }) + + it('should return the right format for dateTime mode', () => { + const format = makeFormat({ mode: 'dateTime', lang: 'fr' }) + expect(format).toBe('dd/LL/yyyy HH:mm') + }) + + describe('When ampm is true', () => { + it('should return the right format for default mode', () => { + const format = makeFormat({ ampm: true, lang: 'fr' }) + expect(format).toBe('dd/LL/yyyy') + }) + + it('should return the right format for time mode', () => { + const format = makeFormat({ mode: 'time', ampm: true, lang: 'fr' }) + expect(format).toBe('HH:mm') + }) + + it('should return the right format for dateTime mode', () => { + const format = makeFormat({ mode: 'dateTime', ampm: true, lang: 'fr' }) + expect(format).toBe('dd/LL/yyyy HH:mm') + }) + }) + }) + + describe('When lang is not "fr"', () => { + it('should return the right format for default mode', () => { + const format = makeFormat({ lang: 'en', ampm: false }) + expect(format).toBe('LL/dd/yyyy') + }) + + it('should return the right format for time mode', () => { + const format = makeFormat({ mode: 'time', lang: 'en' }) + expect(format).toBe('HH:mm') + }) + + it('should return the right format for dateTime mode', () => { + const format = makeFormat({ mode: 'dateTime', lang: 'en' }) + expect(format).toBe('LL/dd/yyyy HH:mm') + }) + + describe('When ampm is true', () => { + it('should return the right format for default mode', () => { + const format = makeFormat({ ampm: true, lang: 'en' }) + expect(format).toBe('LL/dd/yyyy a') + }) + + it('should return the right format for time mode', () => { + const format = makeFormat({ mode: 'time', ampm: true, lang: 'en' }) + expect(format).toBe('HH:mm a') + }) + + it('should return the right format for dateTime mode', () => { + const format = makeFormat({ mode: 'dateTime', ampm: true, lang: 'en' }) + expect(format).toBe('LL/dd/yyyy HH:mm a') + }) + }) + }) +}) diff --git a/react/DatePicker/index.jsx b/react/DatePicker/index.jsx new file mode 100644 index 000000000..0609f0872 --- /dev/null +++ b/react/DatePicker/index.jsx @@ -0,0 +1,289 @@ +import DateFnsUtils from '@date-io/date-fns' +import { + MuiPickersUtilsProvider, + KeyboardDatePicker as MuiKeyboardDatePicker, + KeyboardTimePicker as MuiKeyboardTimePicker, + KeyboardDateTimePicker as MuiKeyboardDateTimePicker, + DatePicker as MuiDatePicker, + TimePicker as MuiTimePicker, + DateTimePicker as MuiDateTimePicker +} from '@material-ui/pickers' +import cx from 'classnames' +import formatFNS from 'date-fns/format' +import isBefore from 'date-fns/isBefore' +import LocaleEN from 'date-fns/locale/en-US' +import LocaleFR from 'date-fns/locale/fr' +import subDays from 'date-fns/subDays' +import PropTypes from 'prop-types' +import React, { forwardRef, useState } from 'react' + +import { makeFormat } from './helpers' +import withOwnLocales from './locales/withOwnLocales' +import useBreakpoints from '../providers/Breakpoints' +import { useI18n } from '../providers/I18n' +import { makeStyles } from '../styles' + +const localesFNS = { + fr: LocaleFR, + en: LocaleEN +} + +const useStyles = makeStyles(() => ({ + overrides: { + width: '100%', + height: isDesktop => (isDesktop ? '5rem' : 'inherit'), + MuiOutlinedInput: { + '&:focused': { + notchedOutline: { + borderColor: 'var(--primaryColor)' + } + } + } + } +})) + +const DatePicker = forwardRef( + ( + { + className, + label, + clearable = false, + value = null, + placeholder, + onFocus, + onBlur, + onChange, + minDate, + minDateLabelError, + format, + cancelLabel, + clearLabel, + okLabel, + todayLabel, + showTodayButton = false, + helperText, + errorLabel, + inputVariant = 'outlined', + inputProps, + KeyboardButtonProps, + enableKeyboard, + mode = 'date', + ampm, + ...props + }, + ref + ) => { + const [error, setError] = useState(null) + const [isFocused, setIsFocused] = useState(false) + + const { isDesktop } = useBreakpoints() + const classes = useStyles(isDesktop) + const { t, lang } = useI18n() + + const isError = !isFocused && Boolean(error) + const _helperText = isError ? error : helperText ?? null + const _format = format || makeFormat({ ampm, mode, lang }) + const _ampm = ampm ?? !(mode === 'time' || mode === 'dateTime') + const _placeholder = placeholder ?? formatFNS(new Date(), _format) + const _clearLabel = clearLabel || t('clear') + const _todayLabel = todayLabel || t('today') + const _cancelLabel = cancelLabel || t('cancel') + const _okLabel = okLabel || t('ok') + const _minDateLabelError = minDateLabelError + ? minDateLabelError + : minDate + ? t('minDateLabelError', { + date: formatFNS(minDate, _format) + }) + : null + + const _KeyboardButtonProps = { + 'aria-label': label, + ...KeyboardButtonProps + } + const _inputProps = { inputMode: 'numeric', ...inputProps } + + const handleChange = val => { + if (val?.toString() !== 'Invalid Date') { + if (minDate && isBefore(val, subDays(minDate, 1))) { + setError(_minDateLabelError) + onChange(val, false) + return + } + setError(null) + onChange(val, true) + } else if (val === '') { + setError(null) + onChange(null, true) + } else { + setError(errorLabel ?? t('invalidDate')) + onChange(val, false) + } + } + + const handleBlur = () => { + setIsFocused(false) + onFocus?.(true) + onBlur?.(false) + } + const handleFocus = () => { + setIsFocused(true) + onFocus?.(false) + onBlur?.(true) + } + + let DatePickerComponent + switch (mode) { + case 'date': + DatePickerComponent = enableKeyboard + ? MuiKeyboardDatePicker + : MuiDatePicker + break + case 'time': + DatePickerComponent = enableKeyboard + ? MuiKeyboardTimePicker + : MuiTimePicker + break + case 'dateTime': + DatePickerComponent = enableKeyboard + ? MuiKeyboardDateTimePicker + : MuiDateTimePicker + break + } + + return ( + + + + ) + } +) + +DatePicker.displayName = 'DatePicker' + +DatePicker.prototype = { + /* + Classname to override the input style + */ + className: PropTypes.string, + /* + Label of the input + */ + label: PropTypes.string, + /* + Value of th input. If set by default with a Date, isValidDate will be false if the value is empty (KeyboardDatePicker behavior) + */ + value: PropTypes.string.isRequired, + /* + Placeholder of the input + */ + placeholder: PropTypes.string, + /* + Function that returns the value of the input when it changes and if it is a valid Date + */ + onChange: PropTypes.func.isRequired, + /* + Function that returns if the input is blured + */ + onBlur: PropTypes.func, + /* + Function that returns if the input is focused + */ + onFocus: PropTypes.func, + /* + Helper text to display when the input is in error + */ + helperText: PropTypes.string, + /* + Min date selectable with the date picker (exclusive) + */ + minDate: PropTypes.instanceOf(Date), + /* + Error message when the min date is not respected + */ + minDateLabelError: PropTypes.string, + /* + Format of the date + */ + format: PropTypes.string, + /* + Date picker cancellation label + */ + cancelLabel: PropTypes.string, + /* + Show today button + */ + showTodayButton: PropTypes.bool, + /* + Date picker today label + */ + todayLabel: PropTypes.string, + /* + Date picker ok label + */ + okLabel: PropTypes.string, + /* + Show the clear button + */ + clearable: PropTypes.bool, + /* + Date picker clear label + */ + clearLabel: PropTypes.string, + /* + Error message when the date is invalid + */ + errorLabel: PropTypes.string, + /* + Variant of the input + */ + inputVariant: PropTypes.string, + /* + Props to override the input + */ + inputProps: PropTypes.object, + /* + Props to override the keyboard button + */ + KeyboardButtonProps: PropTypes.object, + /* + Enable the keyboard date picker + */ + enableKeyboard: PropTypes.bool, + /* + Mode of the date picker. Default is "date" + */ + mode: PropTypes.oneOf(['date', 'time', 'dateTime']), + /* + Enable the AM/PM selector + */ + ampm: PropTypes.bool +} + +export default withOwnLocales(React.memo(DatePicker)) diff --git a/react/DatePicker/locales/en.json b/react/DatePicker/locales/en.json new file mode 100644 index 000000000..458bacb44 --- /dev/null +++ b/react/DatePicker/locales/en.json @@ -0,0 +1,8 @@ +{ + "cancel": "Cancel", + "clear": "Clear", + "invalidDate": "Invalid date", + "ok": "Ok", + "today": "Today", + "minDateLabelError": "Date should not be before minimal date (%{date})" +} diff --git a/react/DatePicker/locales/fr.json b/react/DatePicker/locales/fr.json new file mode 100644 index 000000000..43dd14f9d --- /dev/null +++ b/react/DatePicker/locales/fr.json @@ -0,0 +1,8 @@ +{ + "cancel": "Annuler", + "clear": "Supprimer", + "invalidDate": "Date invalide", + "ok": "Ok", + "today": "Aujourd'hui", + "minDateLabelError": "La date ne doit pas être antérieure à la date minimale (%{date})" +} diff --git a/react/DatePicker/locales/withOwnLocales.jsx b/react/DatePicker/locales/withOwnLocales.jsx new file mode 100644 index 000000000..67dbe68a3 --- /dev/null +++ b/react/DatePicker/locales/withOwnLocales.jsx @@ -0,0 +1,10 @@ +import en from './en.json' +import fr from './fr.json' +import withOnlyLocales from '../../providers/I18n/withOnlyLocales' + +export const locales = { + en, + fr +} + +export default withOnlyLocales(locales) diff --git a/react/index.js b/react/index.js index c0582cc1d..fc8e92e12 100644 --- a/react/index.js +++ b/react/index.js @@ -139,3 +139,4 @@ export { default as Modal } from './Modal' export { ListSkeleton, ListItemSkeleton } from './Skeletons' export { default as ActionsBar } from './ActionsBar' export { default as Markdown } from './Markdown' +export { default as DatePicker } from './DatePicker' diff --git a/yarn.lock b/yarn.lock index a6b91d5e4..b35decb9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1403,7 +1403,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.21.0": +"@babel/runtime@^7.21.0", "@babel/runtime@^7.6.0": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -1539,6 +1539,18 @@ dependencies: microee "0.0.6" +"@date-io/core@1.x", "@date-io/core@^1.3.13": + version "1.3.13" + resolved "https://registry.yarnpkg.com/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa" + integrity sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA== + +"@date-io/date-fns@1": + version "1.3.13" + resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-1.3.13.tgz#7798844041640ab393f7e21a7769a65d672f4735" + integrity sha512-yXxGzcRUPcogiMj58wVgFjc9qUYrCnnU9eLcyNbsQCmae4jPuZCDoIBR21j8ZURsM7GRtU62VOw5yNd4dDHunA== + dependencies: + "@date-io/core" "^1.3.13" + "@emotion/cache@^11.0.0", "@emotion/cache@^11.1.3": version "11.1.3" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.1.3.tgz#c7683a9484bcd38d5562f2b9947873cf66829afd" @@ -1910,6 +1922,18 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" +"@material-ui/pickers@3.3.11": + version "3.3.11" + resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.3.11.tgz#dfaaf49955f7bbe3b1c3720293f69dcddeab3ca4" + integrity sha512-pDYjbjUeabapijS2FpSwK/ruJdk7IGeAshpLbKDa3PRRKRy7Nv6sXxAvUg2F+lID/NwUKgBmCYS5bzrl7Xxqzw== + dependencies: + "@babel/runtime" "^7.6.0" + "@date-io/core" "1.x" + "@types/styled-jsx" "^2.2.8" + clsx "^1.0.2" + react-transition-group "^4.0.0" + rifm "^0.7.0" + "@material-ui/styles@^4.11.4": version "4.11.4" resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d" @@ -2960,6 +2984,13 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== +"@types/styled-jsx@^2.2.8": + version "2.2.9" + resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.9.tgz#e50b3f868c055bcbf9bc353eca6c10fdad32a53f" + integrity sha512-W/iTlIkGEyTBGTEvZCey8EgQlQ5l0DwMqi3iOXlLs2kyBwYTXHKEiU6IZ5EwoRwngL8/dGYuzezSup89ttVHLw== + dependencies: + "@types/react" "*" + "@types/tapable@^1": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" @@ -5397,6 +5428,11 @@ clone@^2.1.1: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= +clsx@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + clsx@^1.0.4: version "1.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" @@ -16315,6 +16351,16 @@ react-test-renderer@16.12.0: react-is "^16.8.6" scheduler "^0.18.0" +react-transition-group@^4.0.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-transition-group@^4.3.0, react-transition-group@^4.4.0: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" @@ -17355,6 +17401,13 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= +rifm@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.7.0.tgz#debe951a9c83549ca6b33e5919f716044c2230be" + integrity sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ== + dependencies: + "@babel/runtime" "^7.3.1" + rimraf@2.6.3, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"