diff --git a/.babelrc.js b/.babelrc.js index a12cc6d4..6a3e2887 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -1,11 +1,17 @@ module.exports = { - presets: [['babel-preset-jason', { runtime: false }]], + presets: [ + ['babel-preset-jason', { runtime: false }], + '@babel/preset-typescript', + ], plugins: [ ['babel-plugin-transform-react-remove-prop-types', { mode: 'wrap' }], ], env: { esm: { - presets: [['babel-preset-jason', { modules: false }]], + presets: [ + ['babel-preset-jason', { modules: false }], + '@babel/preset-typescript', + ], }, }, }; diff --git a/.eslintrc.yml b/.eslintrc.yml index 0428d894..8dda0cb9 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,4 +1,4 @@ -parser: babel-eslint +parser: '@typescript-eslint/parser' extends: - jason/react - plugin:jsx-a11y/recommended @@ -11,6 +11,11 @@ env: browser: true plugins: - jsx-a11y + - '@typescript-eslint' +rules: + '@typescript-eslint/no-unused-vars': + - error + - ignoreRestSiblings: true overrides: - files: www/**/* diff --git a/.size-snapshot.json b/.size-snapshot.json index 28cd868b..7825d6c3 100644 --- a/.size-snapshot.json +++ b/.size-snapshot.json @@ -1,12 +1,12 @@ { - "./lib/dist/react-transition-group.js": { - "bundled": 82684, - "minified": 22426, - "gzipped": 6876 + "lib/dist/react-transition-group.js": { + "bundled": 98066, + "minified": 26272, + "gzipped": 8027 }, - "./lib/dist/react-transition-group.min.js": { - "bundled": 47269, - "minified": 14623, - "gzipped": 4616 + "lib/dist/react-transition-group.min.js": { + "bundled": 55134, + "minified": 17999, + "gzipped": 5654 } } diff --git a/.storybook/main.js b/.storybook/main.js index 6d952e40..03c63cc6 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -4,9 +4,18 @@ module.exports = { stories: ['../stories/index.js'], webpackFinal: (config) => { config.module = { - rules: [rules.js(), rules.astroturf(), rules.css({ extract: false })], + rules: [ + { + test: /\.[t|j]sx?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + rules.astroturf(), + rules.css({ extract: false }), + ], }; - config.plugins.push(plugins.extractCss({ disable: true })); return config; diff --git a/package.json b/package.json index 8a7b252e..7c92c13b 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "tdd": "jest --watch", "build": "rimraf lib && yarn build:cjs && yarn build:esm && yarn build:pick && yarn build:dist && cp README.md LICENSE ./lib", "build:docs": "yarn --cwd www run build", - "build:cjs": "babel src --out-dir lib/cjs", - "build:esm": "cross-env BABEL_ENV=esm babel src --out-dir lib/esm", + "build:cjs": "babel src --out-dir lib/cjs --extensions '.js,.ts,.tsx'", + "build:esm": "cross-env BABEL_ENV=esm babel src --out-dir lib/esm --extensions '.js,.ts,.tsx'", "build:pick": "cherry-pick --cwd=lib --input-dir=../src --cjs-dir=cjs --esm-dir=esm", "build:dist": "cross-env BABEL_ENV=esm rollup -c", "bootstrap": "yarn && yarn --cwd www", @@ -19,8 +19,9 @@ "fix:eslint": "yarn lint:eslint --fix", "fix:prettier": "yarn lint:prettier --write", "lint": "run-p lint:*", - "lint:eslint": "eslint .", + "lint:eslint": "eslint . --ext '.js,.ts,.tsx'", "lint:prettier": "prettier . --check", + "lint:tsc": "tsc", "release": "release", "release:next": "release --preid beta --tag next", "deploy-docs": "yarn --cwd www run deploy", @@ -73,6 +74,7 @@ "devDependencies": { "@babel/cli": "^7.8.4", "@babel/core": "^7.9.0", + "@babel/preset-typescript": "^7.16.7", "@restart/hooks": "^0.3.22", "@semantic-release/changelog": "^5.0.1", "@semantic-release/git": "^9.0.0", @@ -81,10 +83,13 @@ "@storybook/addon-actions": "^6.3.4", "@storybook/react": "^6.3.4", "@testing-library/react": "alpha", + "@types/prop-types": "^15.7.4", + "@types/react": "^17.0.41", + "@types/react-dom": "^17.0.14", "@typescript-eslint/eslint-plugin": "^4.26.1", + "@typescript-eslint/parser": "4", "astroturf": "^0.10.4", - "babel-eslint": "^10.1.0", - "babel-loader": "^8.1.0", + "babel-loader": "^8.2.3", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "babel-preset-jason": "^6.2.0", "cherry-pick": "^0.5.0", @@ -112,7 +117,7 @@ "rollup-plugin-terser": "^5.3.0", "semantic-release": "^17.0.6", "semantic-release-alt-publish-dir": "^3.0.0", - "typescript": "^4.3.2", + "typescript": "^4.6.2", "webpack-atoms": "14.0.0" }, "release": { diff --git a/rollup.config.js b/rollup.config.js index 5000c878..bbabeb46 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,7 +5,7 @@ import replace from 'rollup-plugin-replace'; import { sizeSnapshot } from 'rollup-plugin-size-snapshot'; import { terser } from 'rollup-plugin-terser'; -const input = './src/index.js'; +const input = './src/index.ts'; const name = 'ReactTransitionGroup'; const globals = { react: 'React', @@ -14,11 +14,13 @@ const globals = { const babelOptions = { exclude: /node_modules/, + extensions: ['.js', '.ts', '.tsx'], runtimeHelpers: true, }; const commonjsOptions = { include: /node_modules/, + extensions: ['.js', '.ts', '.tsx'], namedExports: { 'prop-types': ['object', 'oneOfType', 'element', 'bool', 'func'], }, diff --git a/src/CSSTransition.js b/src/CSSTransition.tsx similarity index 78% rename from src/CSSTransition.js rename to src/CSSTransition.tsx index ef97b825..7abfa62e 100644 --- a/src/CSSTransition.js +++ b/src/CSSTransition.tsx @@ -4,14 +4,32 @@ import addOneClass from 'dom-helpers/addClass'; import removeOneClass from 'dom-helpers/removeClass'; import React from 'react'; -import Transition from './Transition'; +import Transition, { Props as TransitionProps } from './Transition'; import { classNamesShape } from './utils/PropTypes'; -const addClass = (node, classes) => +const addClass = (node: HTMLElement, classes: string) => node && classes && classes.split(' ').forEach((c) => addOneClass(node, c)); -const removeClass = (node, classes) => +const removeClass = (node: HTMLElement, classes: string) => node && classes && classes.split(' ').forEach((c) => removeOneClass(node, c)); +type TransitionClassNames = { + appear: string; + appearActive: string; + appearDone: string; + enter: string; + enterActive: string; + enterDone: string; + exit: string; + exitActive: string; + exitDone: string; +}; + +type Props = TransitionProps & { + classNames: string | Partial; +}; + +type TransitionClassNameKeys = 'appear' | 'enter' | 'exit'; + /** * A transition component inspired by the excellent * [ng-animate](https://docs.angularjs.org/api/ngAnimate) library, you should @@ -81,7 +99,7 @@ const removeClass = (node, classes) => * [`appear`](http://reactcommunity.org/react-transition-group/transition#Transition-prop-appear) * prop, make sure to define styles for `.appear-*` classes as well. */ -class CSSTransition extends React.Component { +class CSSTransition extends React.Component { static defaultProps = { classNames: '', }; @@ -92,7 +110,7 @@ class CSSTransition extends React.Component { exit: {}, }; - onEnter = (maybeNode, maybeAppearing) => { + onEnter = (maybeNode: HTMLElement | boolean, maybeAppearing?: boolean) => { const [node, appearing] = this.resolveArguments(maybeNode, maybeAppearing); this.removeClasses(node, 'exit'); this.addClass(node, appearing ? 'appear' : 'enter', 'base'); @@ -102,7 +120,7 @@ class CSSTransition extends React.Component { } }; - onEntering = (maybeNode, maybeAppearing) => { + onEntering = (maybeNode: HTMLElement | boolean, maybeAppearing?: boolean) => { const [node, appearing] = this.resolveArguments(maybeNode, maybeAppearing); const type = appearing ? 'appear' : 'enter'; this.addClass(node, type, 'active'); @@ -112,7 +130,7 @@ class CSSTransition extends React.Component { } }; - onEntered = (maybeNode, maybeAppearing) => { + onEntered = (maybeNode: HTMLElement | boolean, maybeAppearing?: boolean) => { const [node, appearing] = this.resolveArguments(maybeNode, maybeAppearing); const type = appearing ? 'appear' : 'enter'; this.removeClasses(node, type); @@ -123,7 +141,7 @@ class CSSTransition extends React.Component { } }; - onExit = (maybeNode) => { + onExit = (maybeNode?: HTMLElement) => { const [node] = this.resolveArguments(maybeNode); this.removeClasses(node, 'appear'); this.removeClasses(node, 'enter'); @@ -134,7 +152,7 @@ class CSSTransition extends React.Component { } }; - onExiting = (maybeNode) => { + onExiting = (maybeNode?: HTMLElement) => { const [node] = this.resolveArguments(maybeNode); this.addClass(node, 'exit', 'active'); @@ -143,7 +161,7 @@ class CSSTransition extends React.Component { } }; - onExited = (maybeNode) => { + onExited = (maybeNode?: HTMLElement) => { const [node] = this.resolveArguments(maybeNode); this.removeClasses(node, 'exit'); this.addClass(node, 'exit', 'done'); @@ -154,12 +172,16 @@ class CSSTransition extends React.Component { }; // when prop `nodeRef` is provided `node` is excluded - resolveArguments = (maybeNode, maybeAppearing) => + resolveArguments = ( + maybeNode: HTMLElement | boolean | undefined, + maybeAppearing?: boolean + ): [HTMLElement, boolean] => + // @ts-expect-error FIXME: Type at position 1 in source is not compatible with type at position 1 in target. Type 'boolean | HTMLElement' is not assignable to type 'boolean'. Type 'HTMLElement' is not assignable to type 'boolean'.ts(2322) this.props.nodeRef ? [this.props.nodeRef.current, maybeNode] // here `maybeNode` is actually `appearing` : [maybeNode, maybeAppearing]; // `findDOMNode` was used - getClassNames = (type) => { + getClassNames = (type: TransitionClassNameKeys) => { const { classNames } = this.props; const isStringClassNames = typeof classNames === 'string'; const prefix = isStringClassNames && classNames ? `${classNames}-` : ''; @@ -183,7 +205,11 @@ class CSSTransition extends React.Component { }; }; - addClass(node, type, phase) { + addClass( + node: HTMLElement | null, + type: TransitionClassNameKeys, + phase: 'base' | 'active' | 'done' + ) { let className = this.getClassNames(type)[`${phase}ClassName`]; const { doneClassName } = this.getClassNames('enter'); @@ -194,38 +220,46 @@ class CSSTransition extends React.Component { // This is to force a repaint, // which is necessary in order to transition styles when adding a class name. if (phase === 'active') { - /* eslint-disable no-unused-expressions */ + /* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ node && node.scrollTop; } if (className) { + // @ts-expect-error FIXME: Property 'active' does not exist on type '{} | {} | {}'.ts(7053) this.appliedClasses[type][phase] = className; + // @ts-expect-error FIXME: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.ts(2345) addClass(node, className); } } - removeClasses(node, type) { + removeClasses(node: HTMLElement | null, type: TransitionClassNameKeys) { const { + // @ts-expect-error FIXME: Property 'base' does not exist on type '{} | {} | {}'.ts(2339) base: baseClassName, + // @ts-expect-error FIXME: Property 'active' does not exist on type '{} | {} | {}'.ts(2339) active: activeClassName, + // @ts-expect-error FIMXE: Property 'done' does not exist on type '{} | {} | {}'.ts(2339) done: doneClassName, } = this.appliedClasses[type]; this.appliedClasses[type] = {}; if (baseClassName) { + // @ts-expect-error FIXME: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.ts(2345) removeClass(node, baseClassName); } if (activeClassName) { + // @ts-expect-error FIXME: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.ts(2345) removeClass(node, activeClassName); } if (doneClassName) { + // @ts-expect-error FIXME: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.ts(2345) removeClass(node, doneClassName); } } render() { - const { classNames: _, ...props } = this.props; + const { classNames, ...props } = this.props; return ( ` component is a specialized `Transition` component - * that animates between two children. - * - * ```jsx - * - *
I appear first
- *
I replace the above
- *
- * ``` - */ -class ReplaceTransition extends React.Component { - handleEnter = (...args) => this.handleLifecycle('onEnter', 0, args); - handleEntering = (...args) => this.handleLifecycle('onEntering', 0, args); - handleEntered = (...args) => this.handleLifecycle('onEntered', 0, args); - - handleExit = (...args) => this.handleLifecycle('onExit', 1, args); - handleExiting = (...args) => this.handleLifecycle('onExiting', 1, args); - handleExited = (...args) => this.handleLifecycle('onExited', 1, args); - - handleLifecycle(handler, idx, originalArgs) { - const { children } = this.props; - const child = React.Children.toArray(children)[idx]; - - if (child.props[handler]) child.props[handler](...originalArgs); - if (this.props[handler]) { - const maybeNode = child.props.nodeRef - ? undefined - : ReactDOM.findDOMNode(this); - - this.props[handler](maybeNode); - } - } - - render() { - const { children, in: inProp, ...props } = this.props; - const [first, second] = React.Children.toArray(children); - - delete props.onEnter; - delete props.onEntering; - delete props.onEntered; - delete props.onExit; - delete props.onExiting; - delete props.onExited; - - return ( - - {inProp - ? React.cloneElement(first, { - key: 'first', - onEnter: this.handleEnter, - onEntering: this.handleEntering, - onEntered: this.handleEntered, - }) - : React.cloneElement(second, { - key: 'second', - onEnter: this.handleExit, - onEntering: this.handleExiting, - onEntered: this.handleExited, - })} - - ); - } -} - -ReplaceTransition.propTypes = { - in: PropTypes.bool.isRequired, - children(props, propName) { - if (React.Children.count(props[propName]) !== 2) - return new Error( - `"${propName}" must be exactly two transition components.` - ); - - return null; - }, -}; - -export default ReplaceTransition; diff --git a/src/ReplaceTransition.tsx b/src/ReplaceTransition.tsx new file mode 100644 index 00000000..29859780 --- /dev/null +++ b/src/ReplaceTransition.tsx @@ -0,0 +1,118 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import type { ReactElement } from 'react'; +import ReactDOM from 'react-dom'; +import TransitionGroup from './TransitionGroup'; +import type { Props as TransitionProps } from './Transition'; + +type Props = Omit & { + children: [ReactElement, ReactElement]; +}; + +type LifecycleMethodNames = + | 'onEnter' + | 'onEntering' + | 'onEntered' + | 'onExit' + | 'onExiting' + | 'onExited'; + +type HandlerArgs = [HTMLElement | boolean, boolean | undefined]; +type ChildElement = ReactElement; +type ReplaceElements = [ChildElement, ChildElement]; + +/** + * The `` component is a specialized `Transition` component + * that animates between two children. + * + * ```jsx + * + *
I appear first
+ *
I replace the above
+ *
+ * ``` + */ +class ReplaceTransition extends React.Component { + handleEnter = (...args: HandlerArgs) => + this.handleLifecycle('onEnter', 0, args); + handleEntering = (...args: HandlerArgs) => + this.handleLifecycle('onEntering', 0, args); + handleEntered = (...args: HandlerArgs) => + this.handleLifecycle('onEntered', 0, args); + + handleExit = (...args: HandlerArgs) => + this.handleLifecycle('onExit', 1, args); + handleExiting = (...args: HandlerArgs) => + this.handleLifecycle('onExiting', 1, args); + handleExited = (...args: HandlerArgs) => + this.handleLifecycle('onExited', 1, args); + + handleLifecycle( + handler: LifecycleMethodNames, + idx: number, + originalArgs: HandlerArgs + ) { + const { children } = this.props; + // @ts-expect-error FIXME: Type 'string' is not assignable to type 'ReactElement>'.ts(2322) + const child: ChildElement = React.Children.toArray(children)[idx]; + + // @ts-expect-error FIXME: Type 'false' is not assignable to type '(((boolean | HTMLElement) & (HTMLElement | undefined)) & (HTMLElement | undefined)) & (HTMLElement | undefined)'.ts(2345) + if (child.props[handler]) child.props[handler](...originalArgs); + if (this.props[handler]) { + const maybeNode = child.props.nodeRef + ? undefined + : ReactDOM.findDOMNode(this); + // @ts-expect-error FIXME: Argument of type 'Element | Text | null | undefined' is not assignable to parameter of type 'HTMLElement'.ts(2769) + this.props[handler](maybeNode); + } + } + + render() { + const { + children, + in: inProp, + onEnter, + onEntering, + onEntered, + onExit, + onExiting, + onExited, + ...props + } = this.props; + // @ts-expect-error FIXME: Target requires 2 element(s) but source may have fewer.ts(2322) + const [first, second]: ReplaceElements = React.Children.toArray(children); + + return ( + + {inProp + ? React.cloneElement(first, { + key: 'first', + onEnter: this.handleEnter, + onEntering: this.handleEntering, + onEntered: this.handleEntered, + }) + : React.cloneElement(second, { + key: 'second', + onEnter: this.handleExit, + onEntering: this.handleExiting, + onEntered: this.handleExited, + })} + + ); + } +} + +// @ts-expect-error To make TS migration diffs minimum, I've left propTypes here instead of defining a static property +ReplaceTransition.propTypes = { + in: PropTypes.bool.isRequired, + children(props: any, propName: LifecycleMethodNames) { + if (React.Children.count(props[propName]) !== 2) + return new Error( + `"${propName}" must be exactly two transition components.` + ); + + return null; + }, +}; + +export default ReplaceTransition; diff --git a/src/SwitchTransition.js b/src/SwitchTransition.tsx similarity index 66% rename from src/SwitchTransition.js rename to src/SwitchTransition.tsx index cb3a45b3..ee74f773 100644 --- a/src/SwitchTransition.js +++ b/src/SwitchTransition.tsx @@ -1,9 +1,13 @@ import React from 'react'; +import { ReactElement } from 'react'; import PropTypes from 'prop-types'; -import { ENTERED, ENTERING, EXITING } from './Transition'; +import { ENTERED, ENTERING, EXITING, TransitionState } from './Transition'; import TransitionGroupContext from './TransitionGroupContext'; -function areChildrenDifferent(oldChildren, newChildren) { +function areChildrenDifferent( + oldChildren: ReactElement, + newChildren: ReactElement +) { if (oldChildren === newChildren) return false; if ( React.isValidElement(oldChildren) && @@ -26,21 +30,29 @@ export const modes = { }; const callHook = - (element, name, cb) => - (...args) => { + (element: ReactElement, name: string, cb: () => void) => + (...args: unknown[]) => { element.props[name] && element.props[name](...args); cb(); }; +type RenderCallbackParameter = { + current: ReactElement | null; + changeState: (status: TransitionState, current?: ReactElement | null) => void; + children: ReactElement; +}; + const leaveRenders = { - [modes.out]: ({ current, changeState }) => + [modes.out]: ({ current, changeState }: RenderCallbackParameter) => + // @ts-expect-error FIXME: Type 'null' is not assignable to type 'ReactElement>'.ts(2769) React.cloneElement(current, { in: false, + // @ts-expect-error FIXME: Type 'null' is not assignable to type 'ReactElement>'.ts(2345) onExited: callHook(current, 'onExited', () => { changeState(ENTERING, null); }), }), - [modes.in]: ({ current, changeState, children }) => [ + [modes.in]: ({ current, changeState, children }: RenderCallbackParameter) => [ current, React.cloneElement(children, { in: true, @@ -52,16 +64,18 @@ const leaveRenders = { }; const enterRenders = { - [modes.out]: ({ children, changeState }) => + [modes.out]: ({ children, changeState }: RenderCallbackParameter) => React.cloneElement(children, { in: true, onEntered: callHook(children, 'onEntered', () => { changeState(ENTERED, React.cloneElement(children, { in: true })); }), }), - [modes.in]: ({ current, children, changeState }) => [ + [modes.in]: ({ current, children, changeState }: RenderCallbackParameter) => [ + // @ts-expect-error FIXME: Type 'null' is not assignable to type 'ReactElement>'.ts(2769) React.cloneElement(current, { in: false, + // @ts-expect-error FIXME: Type 'null' is not assignable to type 'ReactElement>'.ts(2345) onExited: callHook(current, 'onExited', () => { changeState(ENTERED, React.cloneElement(children, { in: true })); }), @@ -72,6 +86,16 @@ const enterRenders = { ], }; +type Props = { + mode: 'in-out' | 'out-in'; + children: ReactElement; +}; + +type State = { + status: TransitionState; + current: ReactElement | null; +}; + /** * A transition component inspired by the [vue transition modes](https://vuejs.org/v2/guide/transitions.html#Transition-Modes). * You can use it when you want to control the render between state transitions. @@ -124,19 +148,38 @@ const enterRenders = { * } * ``` */ -class SwitchTransition extends React.Component { - state = { +class SwitchTransition extends React.Component { + state: State = { status: ENTERED, current: null, }; + static propTypes = { + /** + * Transition modes. + * `out-in`: Current element transitions out first, then when complete, the new element transitions in. + * `in-out`: New element transitions in first, then when complete, the current element transitions out. + * + * @type {'out-in'|'in-out'} + */ + mode: PropTypes.oneOf([modes.in, modes.out]), + /** + * Any `Transition` or `CSSTransition` component. + */ + children: PropTypes.oneOfType([PropTypes.element.isRequired]), + }; + + static defaultProps = { + mode: modes.out, + }; + appeared = false; componentDidMount() { this.appeared = true; } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: Props, state: State) { if (props.children == null) { return { current: null, @@ -162,7 +205,7 @@ class SwitchTransition extends React.Component { }; } - changeState = (status, current = this.state.current) => { + changeState = (status: TransitionState, current = this.state.current) => { this.setState({ status, current, @@ -196,23 +239,4 @@ class SwitchTransition extends React.Component { } } -SwitchTransition.propTypes = { - /** - * Transition modes. - * `out-in`: Current element transitions out first, then when complete, the new element transitions in. - * `in-out`: New element transitions in first, then when complete, the current element transitions out. - * - * @type {'out-in'|'in-out'} - */ - mode: PropTypes.oneOf([modes.in, modes.out]), - /** - * Any `Transition` or `CSSTransition` component. - */ - children: PropTypes.oneOfType([PropTypes.element.isRequired]), -}; - -SwitchTransition.defaultProps = { - mode: modes.out, -}; - export default SwitchTransition; diff --git a/src/Transition.js b/src/Transition.js deleted file mode 100644 index 04e845db..00000000 --- a/src/Transition.js +++ /dev/null @@ -1,601 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ReactDOM from 'react-dom'; - -import config from './config'; -import { timeoutsShape } from './utils/PropTypes'; -import TransitionGroupContext from './TransitionGroupContext'; - -export const UNMOUNTED = 'unmounted'; -export const EXITED = 'exited'; -export const ENTERING = 'entering'; -export const ENTERED = 'entered'; -export const EXITING = 'exiting'; - -/** - * The Transition component lets you describe a transition from one component - * state to another _over time_ with a simple declarative API. Most commonly - * it's used to animate the mounting and unmounting of a component, but can also - * be used to describe in-place transition states as well. - * - * --- - * - * **Note**: `Transition` is a platform-agnostic base component. If you're using - * transitions in CSS, you'll probably want to use - * [`CSSTransition`](https://reactcommunity.org/react-transition-group/css-transition) - * instead. It inherits all the features of `Transition`, but contains - * additional features necessary to play nice with CSS transitions (hence the - * name of the component). - * - * --- - * - * By default the `Transition` component does not alter the behavior of the - * component it renders, it only tracks "enter" and "exit" states for the - * components. It's up to you to give meaning and effect to those states. For - * example we can add styles to a component when it enters or exits: - * - * ```jsx - * import { Transition } from 'react-transition-group'; - * - * const duration = 300; - * - * const defaultStyle = { - * transition: `opacity ${duration}ms ease-in-out`, - * opacity: 0, - * } - * - * const transitionStyles = { - * entering: { opacity: 1 }, - * entered: { opacity: 1 }, - * exiting: { opacity: 0 }, - * exited: { opacity: 0 }, - * }; - * - * const Fade = ({ in: inProp }) => ( - * - * {state => ( - *
- * I'm a fade Transition! - *
- * )} - *
- * ); - * ``` - * - * There are 4 main states a Transition can be in: - * - `'entering'` - * - `'entered'` - * - `'exiting'` - * - `'exited'` - * - * Transition state is toggled via the `in` prop. When `true` the component - * begins the "Enter" stage. During this stage, the component will shift from - * its current transition state, to `'entering'` for the duration of the - * transition and then to the `'entered'` stage once it's complete. Let's take - * the following example (we'll use the - * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook): - * - * ```jsx - * function App() { - * const [inProp, setInProp] = useState(false); - * return ( - *
- * - * {state => ( - * // ... - * )} - * - * - *
- * ); - * } - * ``` - * - * When the button is clicked the component will shift to the `'entering'` state - * and stay there for 500ms (the value of `timeout`) before it finally switches - * to `'entered'`. - * - * When `in` is `false` the same thing happens except the state moves from - * `'exiting'` to `'exited'`. - */ -class Transition extends React.Component { - static contextType = TransitionGroupContext; - - constructor(props, context) { - super(props, context); - - let parentGroup = context; - // In the context of a TransitionGroup all enters are really appears - let appear = - parentGroup && !parentGroup.isMounting ? props.enter : props.appear; - - let initialStatus; - - this.appearStatus = null; - - if (props.in) { - if (appear) { - initialStatus = EXITED; - this.appearStatus = ENTERING; - } else { - initialStatus = ENTERED; - } - } else { - if (props.unmountOnExit || props.mountOnEnter) { - initialStatus = UNMOUNTED; - } else { - initialStatus = EXITED; - } - } - - this.state = { status: initialStatus }; - - this.nextCallback = null; - } - - static getDerivedStateFromProps({ in: nextIn }, prevState) { - if (nextIn && prevState.status === UNMOUNTED) { - return { status: EXITED }; - } - return null; - } - - // getSnapshotBeforeUpdate(prevProps) { - // let nextStatus = null - - // if (prevProps !== this.props) { - // const { status } = this.state - - // if (this.props.in) { - // if (status !== ENTERING && status !== ENTERED) { - // nextStatus = ENTERING - // } - // } else { - // if (status === ENTERING || status === ENTERED) { - // nextStatus = EXITING - // } - // } - // } - - // return { nextStatus } - // } - - componentDidMount() { - this.updateStatus(true, this.appearStatus); - } - - componentDidUpdate(prevProps) { - let nextStatus = null; - if (prevProps !== this.props) { - const { status } = this.state; - - if (this.props.in) { - if (status !== ENTERING && status !== ENTERED) { - nextStatus = ENTERING; - } - } else { - if (status === ENTERING || status === ENTERED) { - nextStatus = EXITING; - } - } - } - this.updateStatus(false, nextStatus); - } - - componentWillUnmount() { - this.cancelNextCallback(); - } - - getTimeouts() { - const { timeout } = this.props; - let exit, enter, appear; - - exit = enter = appear = timeout; - - if (timeout != null && typeof timeout !== 'number') { - exit = timeout.exit; - enter = timeout.enter; - // TODO: remove fallback for next major - appear = timeout.appear !== undefined ? timeout.appear : enter; - } - return { exit, enter, appear }; - } - - updateStatus(mounting = false, nextStatus) { - if (nextStatus !== null) { - // nextStatus will always be ENTERING or EXITING. - this.cancelNextCallback(); - - if (nextStatus === ENTERING) { - this.performEnter(mounting); - } else { - this.performExit(); - } - } else if (this.props.unmountOnExit && this.state.status === EXITED) { - this.setState({ status: UNMOUNTED }); - } - } - - performEnter(mounting) { - const { enter } = this.props; - const appearing = this.context ? this.context.isMounting : mounting; - const [maybeNode, maybeAppearing] = this.props.nodeRef - ? [appearing] - : [ReactDOM.findDOMNode(this), appearing]; - - const timeouts = this.getTimeouts(); - const enterTimeout = appearing ? timeouts.appear : timeouts.enter; - // no enter animation skip right to ENTERED - // if we are mounting and running this it means appear _must_ be set - if ((!mounting && !enter) || config.disabled) { - this.safeSetState({ status: ENTERED }, () => { - this.props.onEntered(maybeNode); - }); - return; - } - - this.props.onEnter(maybeNode, maybeAppearing); - - this.safeSetState({ status: ENTERING }, () => { - this.props.onEntering(maybeNode, maybeAppearing); - - this.onTransitionEnd(enterTimeout, () => { - this.safeSetState({ status: ENTERED }, () => { - this.props.onEntered(maybeNode, maybeAppearing); - }); - }); - }); - } - - performExit() { - const { exit } = this.props; - const timeouts = this.getTimeouts(); - const maybeNode = this.props.nodeRef - ? undefined - : ReactDOM.findDOMNode(this); - - // no exit animation skip right to EXITED - if (!exit || config.disabled) { - this.safeSetState({ status: EXITED }, () => { - this.props.onExited(maybeNode); - }); - return; - } - - this.props.onExit(maybeNode); - - this.safeSetState({ status: EXITING }, () => { - this.props.onExiting(maybeNode); - - this.onTransitionEnd(timeouts.exit, () => { - this.safeSetState({ status: EXITED }, () => { - this.props.onExited(maybeNode); - }); - }); - }); - } - - cancelNextCallback() { - if (this.nextCallback !== null) { - this.nextCallback.cancel(); - this.nextCallback = null; - } - } - - safeSetState(nextState, callback) { - // This shouldn't be necessary, but there are weird race conditions with - // setState callbacks and unmounting in testing, so always make sure that - // we can cancel any pending setState callbacks after we unmount. - callback = this.setNextCallback(callback); - this.setState(nextState, callback); - } - - setNextCallback(callback) { - let active = true; - - this.nextCallback = (event) => { - if (active) { - active = false; - this.nextCallback = null; - - callback(event); - } - }; - - this.nextCallback.cancel = () => { - active = false; - }; - - return this.nextCallback; - } - - onTransitionEnd(timeout, handler) { - this.setNextCallback(handler); - const node = this.props.nodeRef - ? this.props.nodeRef.current - : ReactDOM.findDOMNode(this); - - const doesNotHaveTimeoutOrListener = - timeout == null && !this.props.addEndListener; - if (!node || doesNotHaveTimeoutOrListener) { - setTimeout(this.nextCallback, 0); - return; - } - - if (this.props.addEndListener) { - const [maybeNode, maybeNextCallback] = this.props.nodeRef - ? [this.nextCallback] - : [node, this.nextCallback]; - this.props.addEndListener(maybeNode, maybeNextCallback); - } - - if (timeout != null) { - setTimeout(this.nextCallback, timeout); - } - } - - render() { - const status = this.state.status; - - if (status === UNMOUNTED) { - return null; - } - - const { - children, - // filter props for `Transition` - in: _in, - mountOnEnter: _mountOnEnter, - unmountOnExit: _unmountOnExit, - appear: _appear, - enter: _enter, - exit: _exit, - timeout: _timeout, - addEndListener: _addEndListener, - onEnter: _onEnter, - onEntering: _onEntering, - onEntered: _onEntered, - onExit: _onExit, - onExiting: _onExiting, - onExited: _onExited, - nodeRef: _nodeRef, - ...childProps - } = this.props; - - return ( - // allows for nested Transitions - - {typeof children === 'function' - ? children(status, childProps) - : React.cloneElement(React.Children.only(children), childProps)} - - ); - } -} - -Transition.propTypes = { - /** - * A React reference to DOM element that need to transition: - * https://stackoverflow.com/a/51127130/4671932 - * - * - When `nodeRef` prop is used, `node` is not passed to callback functions - * (e.g. `onEnter`) because user already has direct access to the node. - * - When changing `key` prop of `Transition` in a `TransitionGroup` a new - * `nodeRef` need to be provided to `Transition` with changed `key` prop - * (see - * [test/CSSTransition-test.js](https://github.com/reactjs/react-transition-group/blob/13435f897b3ab71f6e19d724f145596f5910581c/test/CSSTransition-test.js#L362-L437)). - */ - nodeRef: PropTypes.shape({ - current: - typeof Element === 'undefined' - ? PropTypes.any - : (propValue, key, componentName, location, propFullName, secret) => { - const value = propValue[key]; - - return PropTypes.instanceOf( - value && 'ownerDocument' in value - ? value.ownerDocument.defaultView.Element - : Element - )(propValue, key, componentName, location, propFullName, secret); - }, - }), - - /** - * A `function` child can be used instead of a React element. This function is - * called with the current transition status (`'entering'`, `'entered'`, - * `'exiting'`, `'exited'`), which can be used to apply context - * specific props to a component. - * - * ```jsx - * - * {state => ( - * - * )} - * - * ``` - */ - children: PropTypes.oneOfType([ - PropTypes.func.isRequired, - PropTypes.element.isRequired, - ]).isRequired, - - /** - * Show the component; triggers the enter or exit states - */ - in: PropTypes.bool, - - /** - * By default the child component is mounted immediately along with - * the parent `Transition` component. If you want to "lazy mount" the component on the - * first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay - * mounted, even on "exited", unless you also specify `unmountOnExit`. - */ - mountOnEnter: PropTypes.bool, - - /** - * By default the child component stays mounted after it reaches the `'exited'` state. - * Set `unmountOnExit` if you'd prefer to unmount the component after it finishes exiting. - */ - unmountOnExit: PropTypes.bool, - - /** - * By default the child component does not perform the enter transition when - * it first mounts, regardless of the value of `in`. If you want this - * behavior, set both `appear` and `in` to `true`. - * - * > **Note**: there are no special appear states like `appearing`/`appeared`, this prop - * > only adds an additional enter transition. However, in the - * > `` component that first enter transition does result in - * > additional `.appear-*` classes, that way you can choose to style it - * > differently. - */ - appear: PropTypes.bool, - - /** - * Enable or disable enter transitions. - */ - enter: PropTypes.bool, - - /** - * Enable or disable exit transitions. - */ - exit: PropTypes.bool, - - /** - * The duration of the transition, in milliseconds. - * Required unless `addEndListener` is provided. - * - * You may specify a single timeout for all transitions: - * - * ```jsx - * timeout={500} - * ``` - * - * or individually: - * - * ```jsx - * timeout={{ - * appear: 500, - * enter: 300, - * exit: 500, - * }} - * ``` - * - * - `appear` defaults to the value of `enter` - * - `enter` defaults to `0` - * - `exit` defaults to `0` - * - * @type {number | { enter?: number, exit?: number, appear?: number }} - */ - timeout: (props, ...args) => { - let pt = timeoutsShape; - if (!props.addEndListener) pt = pt.isRequired; - return pt(props, ...args); - }, - - /** - * Add a custom transition end trigger. Called with the transitioning - * DOM node and a `done` callback. Allows for more fine grained transition end - * logic. Timeouts are still used as a fallback if provided. - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed. - * - * ```jsx - * addEndListener={(node, done) => { - * // use the css transitionend event to mark the finish of a transition - * node.addEventListener('transitionend', done, false); - * }} - * ``` - */ - addEndListener: PropTypes.func, - - /** - * Callback fired before the "entering" status is applied. An extra parameter - * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed. - * - * @type Function(node: HtmlElement, isAppearing: bool) -> void - */ - onEnter: PropTypes.func, - - /** - * Callback fired after the "entering" status is applied. An extra parameter - * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed. - * - * @type Function(node: HtmlElement, isAppearing: bool) - */ - onEntering: PropTypes.func, - - /** - * Callback fired after the "entered" status is applied. An extra parameter - * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed. - * - * @type Function(node: HtmlElement, isAppearing: bool) -> void - */ - onEntered: PropTypes.func, - - /** - * Callback fired before the "exiting" status is applied. - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed. - * - * @type Function(node: HtmlElement) -> void - */ - onExit: PropTypes.func, - - /** - * Callback fired after the "exiting" status is applied. - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed. - * - * @type Function(node: HtmlElement) -> void - */ - onExiting: PropTypes.func, - - /** - * Callback fired after the "exited" status is applied. - * - * **Note**: when `nodeRef` prop is passed, `node` is not passed - * - * @type Function(node: HtmlElement) -> void - */ - onExited: PropTypes.func, -}; - -// Name the function so it is clearer in the documentation -function noop() {} - -Transition.defaultProps = { - in: false, - mountOnEnter: false, - unmountOnExit: false, - appear: false, - enter: true, - exit: true, - - onEnter: noop, - onEntering: noop, - onEntered: noop, - - onExit: noop, - onExiting: noop, - onExited: noop, -}; - -Transition.UNMOUNTED = UNMOUNTED; -Transition.EXITED = EXITED; -Transition.ENTERING = ENTERING; -Transition.ENTERED = ENTERED; -Transition.EXITING = EXITING; - -export default Transition; diff --git a/src/Transition.tsx b/src/Transition.tsx new file mode 100644 index 00000000..1d68605a --- /dev/null +++ b/src/Transition.tsx @@ -0,0 +1,647 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import type { RefObject, ReactNode } from 'react'; +import ReactDOM from 'react-dom'; + +import config from './config'; +import { timeoutsShape } from './utils/PropTypes'; +import TransitionGroupContext from './TransitionGroupContext'; + +export const UNMOUNTED = 'unmounted'; +export const EXITED = 'exited'; +export const ENTERING = 'entering'; +export const ENTERED = 'entered'; +export const EXITING = 'exiting'; + +export type TransitionState = + | 'unmounted' + | 'exited' + | 'entering' + | 'entered' + | 'exiting'; + +export type Props = { + nodeRef?: RefObject; + // The childProps argument is not documented + children: (state: TransitionState, childProps: any) => ReactNode; + in: boolean; + mountOnEnter: boolean; + unmountOnExit: boolean; + appear: boolean; + enter: boolean; + exit: boolean; + timeout: number | { appear?: number; enter?: number; exit?: number }; + addEndListener: (node: HTMLElement | undefined, done: boolean) => void; + onEnter: (maybeNode: HTMLElement | boolean, isAppearing?: boolean) => void; + onEntering: (maybeNode: HTMLElement | boolean, isAppearing?: boolean) => void; + onEntered: (maybeNode: HTMLElement | boolean, isAppearing?: boolean) => void; + onExit: (node?: HTMLElement) => void; + onExiting: (node?: HTMLElement) => void; + onExited: (node?: HTMLElement) => void; +}; + +type State = { + status: TransitionState; +}; + +/** + * The Transition component lets you describe a transition from one component + * state to another _over time_ with a simple declarative API. Most commonly + * it's used to animate the mounting and unmounting of a component, but can also + * be used to describe in-place transition states as well. + * + * --- + * + * **Note**: `Transition` is a platform-agnostic base component. If you're using + * transitions in CSS, you'll probably want to use + * [`CSSTransition`](https://reactcommunity.org/react-transition-group/css-transition) + * instead. It inherits all the features of `Transition`, but contains + * additional features necessary to play nice with CSS transitions (hence the + * name of the component). + * + * --- + * + * By default the `Transition` component does not alter the behavior of the + * component it renders, it only tracks "enter" and "exit" states for the + * components. It's up to you to give meaning and effect to those states. For + * example we can add styles to a component when it enters or exits: + * + * ```jsx + * import { Transition } from 'react-transition-group'; + * + * const duration = 300; + * + * const defaultStyle = { + * transition: `opacity ${duration}ms ease-in-out`, + * opacity: 0, + * } + * + * const transitionStyles = { + * entering: { opacity: 1 }, + * entered: { opacity: 1 }, + * exiting: { opacity: 0 }, + * exited: { opacity: 0 }, + * }; + * + * const Fade = ({ in: inProp }) => ( + * + * {state => ( + *
+ * I'm a fade Transition! + *
+ * )} + *
+ * ); + * ``` + * + * There are 4 main states a Transition can be in: + * - `'entering'` + * - `'entered'` + * - `'exiting'` + * - `'exited'` + * + * Transition state is toggled via the `in` prop. When `true` the component + * begins the "Enter" stage. During this stage, the component will shift from + * its current transition state, to `'entering'` for the duration of the + * transition and then to the `'entered'` stage once it's complete. Let's take + * the following example (we'll use the + * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook): + * + * ```jsx + * function App() { + * const [inProp, setInProp] = useState(false); + * return ( + *
+ * + * {state => ( + * // ... + * )} + * + * + *
+ * ); + * } + * ``` + * + * When the button is clicked the component will shift to the `'entering'` state + * and stay there for 500ms (the value of `timeout`) before it finally switches + * to `'entered'`. + * + * When `in` is `false` the same thing happens except the state moves from + * `'exiting'` to `'exited'`. + */ +class Transition extends React.Component { + appearStatus: TransitionState | null; + nextCallback: any; + + static defaultProps = { + in: false, + mountOnEnter: false, + unmountOnExit: false, + appear: false, + enter: true, + exit: true, + + onEnter: noop, + onEntering: noop, + onEntered: noop, + + onExit: noop, + onExiting: noop, + onExited: noop, + }; + static UNMOUNTED = UNMOUNTED; + static EXITED = EXITED; + static ENTERING = ENTERING; + static ENTERED = ENTERED; + static EXITING = EXITING; + + static propTypes = { + /** + * A React reference to DOM element that need to transition: + * https://stackoverflow.com/a/51127130/4671932 + * + * - When `nodeRef` prop is used, `node` is not passed to callback functions + * (e.g. `onEnter`) because user already has direct access to the node. + * - When changing `key` prop of `Transition` in a `TransitionGroup` a new + * `nodeRef` need to be provided to `Transition` with changed `key` prop + * (see + * [test/CSSTransition-test.js](https://github.com/reactjs/react-transition-group/blob/13435f897b3ab71f6e19d724f145596f5910581c/test/CSSTransition-test.js#L362-L437)). + */ + nodeRef: PropTypes.shape({ + // @ts-expect-error We'll remove the PropTypes definition + current: + typeof Element === 'undefined' + ? PropTypes.any + : // @ts-expect-error We'll remove the PropTypes definition + (propValue, key, componentName, location, propFullName, secret) => { + const value = propValue[key]; + + return PropTypes.instanceOf( + value && 'ownerDocument' in value + ? value.ownerDocument.defaultView.Element + : Element + // @ts-expect-error We'll remove the PropTypes definition + )(propValue, key, componentName, location, propFullName, secret); + }, + }), + + /** + * A `function` child can be used instead of a React element. This function is + * called with the current transition status (`'entering'`, `'entered'`, + * `'exiting'`, `'exited'`), which can be used to apply context + * specific props to a component. + * + * ```jsx + * + * {state => ( + * + * )} + * + * ``` + */ + children: PropTypes.oneOfType([ + PropTypes.func.isRequired, + PropTypes.element.isRequired, + ]).isRequired, + + /** + * Show the component; triggers the enter or exit states + */ + in: PropTypes.bool, + + /** + * By default the child component is mounted immediately along with + * the parent `Transition` component. If you want to "lazy mount" the component on the + * first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay + * mounted, even on "exited", unless you also specify `unmountOnExit`. + */ + mountOnEnter: PropTypes.bool, + + /** + * By default the child component stays mounted after it reaches the `'exited'` state. + * Set `unmountOnExit` if you'd prefer to unmount the component after it finishes exiting. + */ + unmountOnExit: PropTypes.bool, + + /** + * By default the child component does not perform the enter transition when + * it first mounts, regardless of the value of `in`. If you want this + * behavior, set both `appear` and `in` to `true`. + * + * > **Note**: there are no special appear states like `appearing`/`appeared`, this prop + * > only adds an additional enter transition. However, in the + * > `` component that first enter transition does result in + * > additional `.appear-*` classes, that way you can choose to style it + * > differently. + */ + appear: PropTypes.bool, + + /** + * Enable or disable enter transitions. + */ + enter: PropTypes.bool, + + /** + * Enable or disable exit transitions. + */ + exit: PropTypes.bool, + + /** + * The duration of the transition, in milliseconds. + * Required unless `addEndListener` is provided. + * + * You may specify a single timeout for all transitions: + * + * ```jsx + * timeout={500} + * ``` + * + * or individually: + * + * ```jsx + * timeout={{ + * appear: 500, + * enter: 300, + * exit: 500, + * }} + * ``` + * + * - `appear` defaults to the value of `enter` + * - `enter` defaults to `0` + * - `exit` defaults to `0` + * + * @type {number | { enter?: number, exit?: number, appear?: number }} + */ + timeout: (props: any, ...args: any[]) => { + let pt = timeoutsShape; + // @ts-expect-error We'll remove the PropTypes definition + if (!props.addEndListener) pt = pt.isRequired; + // @ts-expect-error We'll remove the PropTypes definition + return pt(props, ...args); + }, + + /** + * Add a custom transition end trigger. Called with the transitioning + * DOM node and a `done` callback. Allows for more fine grained transition end + * logic. Timeouts are still used as a fallback if provided. + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed. + * + * ```jsx + * addEndListener={(node, done) => { + * // use the css transitionend event to mark the finish of a transition + * node.addEventListener('transitionend', done, false); + * }} + * ``` + */ + addEndListener: PropTypes.func, + + /** + * Callback fired before the "entering" status is applied. An extra parameter + * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed. + * + * @type Function(node: HtmlElement, isAppearing: bool) -> void + */ + onEnter: PropTypes.func, + + /** + * Callback fired after the "entering" status is applied. An extra parameter + * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed. + * + * @type Function(node: HtmlElement, isAppearing: bool) + */ + onEntering: PropTypes.func, + + /** + * Callback fired after the "entered" status is applied. An extra parameter + * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed. + * + * @type Function(node: HtmlElement, isAppearing: bool) -> void + */ + onEntered: PropTypes.func, + + /** + * Callback fired before the "exiting" status is applied. + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed. + * + * @type Function(node: HtmlElement) -> void + */ + onExit: PropTypes.func, + + /** + * Callback fired after the "exiting" status is applied. + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed. + * + * @type Function(node: HtmlElement) -> void + */ + onExiting: PropTypes.func, + + /** + * Callback fired after the "exited" status is applied. + * + * **Note**: when `nodeRef` prop is passed, `node` is not passed + * + * @type Function(node: HtmlElement) -> void + */ + onExited: PropTypes.func, + }; + + static contextType = TransitionGroupContext; + + constructor(props: Props, context: any) { + super(props, context); + + let parentGroup = context; + // In the context of a TransitionGroup all enters are really appears + let appear = + parentGroup && !parentGroup.isMounting ? props.enter : props.appear; + + let initialStatus: TransitionState; + + this.appearStatus = null; + + if (props.in) { + if (appear) { + initialStatus = EXITED; + this.appearStatus = ENTERING; + } else { + initialStatus = ENTERED; + } + } else { + if (props.unmountOnExit || props.mountOnEnter) { + initialStatus = UNMOUNTED; + } else { + initialStatus = EXITED; + } + } + + this.state = { status: initialStatus }; + + this.nextCallback = null; + } + + static getDerivedStateFromProps( + { in: nextIn }: { in: boolean }, + prevState: State + ) { + if (nextIn && prevState.status === UNMOUNTED) { + return { status: EXITED }; + } + return null; + } + + // getSnapshotBeforeUpdate(prevProps) { + // let nextStatus = null + + // if (prevProps !== this.props) { + // const { status } = this.state + + // if (this.props.in) { + // if (status !== ENTERING && status !== ENTERED) { + // nextStatus = ENTERING + // } + // } else { + // if (status === ENTERING || status === ENTERED) { + // nextStatus = EXITING + // } + // } + // } + + // return { nextStatus } + // } + + componentDidMount() { + this.updateStatus(true, this.appearStatus); + } + + componentDidUpdate(prevProps: Props) { + let nextStatus: TransitionState | null = null; + if (prevProps !== this.props) { + const { status } = this.state; + + if (this.props.in) { + if (status !== ENTERING && status !== ENTERED) { + nextStatus = ENTERING; + } + } else { + if (status === ENTERING || status === ENTERED) { + nextStatus = EXITING; + } + } + } + this.updateStatus(false, nextStatus); + } + + componentWillUnmount() { + this.cancelNextCallback(); + } + + getTimeouts() { + const { timeout } = this.props; + let exit, enter, appear; + + if (timeout != null && typeof timeout !== 'number') { + exit = timeout.exit; + enter = timeout.enter; + // TODO: remove fallback for next major + appear = timeout.appear !== undefined ? timeout.appear : enter; + } else { + exit = enter = appear = timeout; + } + return { exit, enter, appear }; + } + + updateStatus(mounting = false, nextStatus: TransitionState | null) { + if (nextStatus !== null) { + // nextStatus will always be ENTERING or EXITING. + this.cancelNextCallback(); + + if (nextStatus === ENTERING) { + this.performEnter(mounting); + } else { + this.performExit(); + } + } else if (this.props.unmountOnExit && this.state.status === EXITED) { + this.setState({ status: UNMOUNTED }); + } + } + + performEnter(mounting: boolean) { + const { enter } = this.props; + const appearing = this.context ? this.context.isMounting : mounting; + const [maybeNode, maybeAppearing] = this.props.nodeRef + ? [appearing] + : [ReactDOM.findDOMNode(this), appearing]; + + const timeouts = this.getTimeouts(); + const enterTimeout = appearing ? timeouts.appear : timeouts.enter; + // no enter animation skip right to ENTERED + // if we are mounting and running this it means appear _must_ be set + if ((!mounting && !enter) || config.disabled) { + this.safeSetState({ status: ENTERED }, () => { + this.props.onEntered(maybeNode); + }); + return; + } + + this.props.onEnter(maybeNode, maybeAppearing); + + this.safeSetState({ status: ENTERING }, () => { + this.props.onEntering(maybeNode, maybeAppearing); + + this.onTransitionEnd(enterTimeout, () => { + this.safeSetState({ status: ENTERED }, () => { + this.props.onEntered(maybeNode, maybeAppearing); + }); + }); + }); + } + + performExit() { + const { exit } = this.props; + const timeouts = this.getTimeouts(); + // @ts-expect-error FIXME: Type 'Element | Text | null | undefined' is not assignable to type 'HTMLElement | undefined' Type 'null' is not assignable to type 'HTMLElement | undefined'.ts(2322) + const maybeNode: HTMLElement | undefined = this.props.nodeRef + ? undefined + : ReactDOM.findDOMNode(this); + + // no exit animation skip right to EXITED + if (!exit || config.disabled) { + this.safeSetState({ status: EXITED }, () => { + this.props.onExited(maybeNode); + }); + return; + } + + this.props.onExit(maybeNode); + + this.safeSetState({ status: EXITING }, () => { + this.props.onExiting(maybeNode); + + this.onTransitionEnd(timeouts.exit, () => { + this.safeSetState({ status: EXITED }, () => { + this.props.onExited(maybeNode); + }); + }); + }); + } + + cancelNextCallback() { + if (this.nextCallback !== null) { + this.nextCallback.cancel(); + this.nextCallback = null; + } + } + + safeSetState(nextState: State, callback: () => void) { + // This shouldn't be necessary, but there are weird race conditions with + // setState callbacks and unmounting in testing, so always make sure that + // we can cancel any pending setState callbacks after we unmount. + callback = this.setNextCallback(callback); + this.setState(nextState, callback); + } + + setNextCallback(callback: () => void) { + let active = true; + + this.nextCallback = () => { + if (active) { + active = false; + this.nextCallback = null; + + callback(); + } + }; + + this.nextCallback.cancel = () => { + active = false; + }; + + return this.nextCallback; + } + + onTransitionEnd(timeout: number | undefined, handler: () => void) { + this.setNextCallback(handler); + const node = this.props.nodeRef + ? this.props.nodeRef.current + : ReactDOM.findDOMNode(this); + + const doesNotHaveTimeoutOrListener = + timeout == null && !this.props.addEndListener; + if (!node || doesNotHaveTimeoutOrListener) { + setTimeout(this.nextCallback, 0); + return; + } + + if (this.props.addEndListener) { + const [maybeNode, maybeNextCallback] = this.props.nodeRef + ? [this.nextCallback] + : [node, this.nextCallback]; + this.props.addEndListener(maybeNode, maybeNextCallback); + } + + if (timeout != null) { + setTimeout(this.nextCallback, timeout); + } + } + + render() { + const status = this.state.status; + + if (status === UNMOUNTED) { + return null; + } + + const { + children, + // filter props for `Transition` + in: _in, + mountOnEnter, + unmountOnExit, + appear, + enter, + exit, + timeout, + addEndListener, + onEnter, + onEntering, + onEntered, + onExit, + onExiting, + onExited, + nodeRef, + ...childProps + } = this.props; + + return ( + // allows for nested Transitions + + {typeof children === 'function' + ? children(status, childProps) + : // @ts-expect-error FIXME: Type 'ReactChildren' is missing the following properties from type 'ReactElement>': type, props, keyts(2769) + React.cloneElement(React.Children.only(children), childProps)} + + ); + } +} + +// Name the function so it is clearer in the documentation +function noop() { + /* noop */ +} + +export default Transition; diff --git a/src/TransitionGroup.js b/src/TransitionGroup.tsx similarity index 72% rename from src/TransitionGroup.js rename to src/TransitionGroup.tsx index 14783c0f..f39013f7 100644 --- a/src/TransitionGroup.js +++ b/src/TransitionGroup.tsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; +import type { ReactElement, ReactChild } from 'react'; import TransitionGroupContext from './TransitionGroupContext'; import { @@ -8,11 +9,32 @@ import { getNextChildMapping, } from './utils/ChildMapping'; -const values = Object.values || ((obj) => Object.keys(obj).map((k) => obj[k])); +const values = + Object.values || + ((obj: Record) => Object.keys(obj).map((k) => obj[k])); const defaultProps = { component: 'div', - childFactory: (child) => child, + childFactory: (child: ReactElement) => child, +}; + +type Props = { + component: any; + children: any; + appear: boolean; + enter: boolean; + exit: boolean; + childFactory: (child: ReactElement) => ReactElement; +}; + +type State = { + children: Record; + contextValue: { isMounting: boolean }; + handleExited: ( + child: ReactElement<{ onExited: (node: HTMLElement) => void }>, + node: HTMLElement + ) => void; + firstRender: boolean; }; /** @@ -29,13 +51,17 @@ const defaultProps = { * component. This means you can mix and match animations across different list * items. */ -class TransitionGroup extends React.Component { - constructor(props, context) { +class TransitionGroup extends React.Component { + static defaultProps = defaultProps; + + mounted = false; + constructor(props: Props, context: any) { super(props, context); const handleExited = this.handleExited.bind(this); // Initial children should all be entering, dependent on appear + // @ts-expect-error FIXME: Property 'children' is missing in type '{ contextValue: { isMounting: true; }; handleExited: (child: React.ReactElement<{ onExited: (node: HTMLElement) => void; }, string | React.JSXElementConstructor>, node: HTMLElement) => void; firstRender: true; }' but required in type 'Readonly'.ts(2741) this.state = { contextValue: { isMounting: true }, handleExited, @@ -55,8 +81,8 @@ class TransitionGroup extends React.Component { } static getDerivedStateFromProps( - nextProps, - { children: prevChildMapping, handleExited, firstRender } + nextProps: Props, + { children: prevChildMapping, handleExited, firstRender }: State ) { return { children: firstRender @@ -67,10 +93,12 @@ class TransitionGroup extends React.Component { } // node is `undefined` when user provided `nodeRef` prop - handleExited(child, node) { + handleExited( + child: ReactElement<{ onExited: (node: HTMLElement) => void }>, + node: HTMLElement + ) { let currentChildMapping = getChildMapping(this.props.children); - - if (child.key in currentChildMapping) return; + if (child.key && child.key in currentChildMapping) return; if (child.props.onExited) { child.props.onExited(node); @@ -79,8 +107,9 @@ class TransitionGroup extends React.Component { if (this.mounted) { this.setState((state) => { let children = { ...state.children }; - - delete children[child.key]; + if (child.key) { + delete children[child.key]; + } return { children }; }); } @@ -89,11 +118,9 @@ class TransitionGroup extends React.Component { render() { const { component: Component, childFactory, ...props } = this.props; const { contextValue } = this.state; + // @ts-expect-error FIXME: Type 'undefined' is not assignable to type 'ReactElement>'.ts(2345) const children = values(this.state.children).map(childFactory); - - delete props.appear; - delete props.enter; - delete props.exit; + const { appear, enter, exit, ...delegatingProps } = props; if (Component === null) { return ( @@ -104,12 +131,13 @@ class TransitionGroup extends React.Component { } return ( - {children} + {children} ); } } +// @ts-expect-error To make TS migration diffs minimum, I've left propTypes here instead of defining a static property TransitionGroup.propTypes = { /** * `` renders a `
` by default. You can change this @@ -166,6 +194,4 @@ TransitionGroup.propTypes = { childFactory: PropTypes.func, }; -TransitionGroup.defaultProps = defaultProps; - export default TransitionGroup; diff --git a/src/TransitionGroupContext.js b/src/TransitionGroupContext.js deleted file mode 100644 index 51b82c7d..00000000 --- a/src/TransitionGroupContext.js +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react'; - -export default React.createContext(null); diff --git a/src/TransitionGroupContext.ts b/src/TransitionGroupContext.ts new file mode 100644 index 00000000..afe8ddbb --- /dev/null +++ b/src/TransitionGroupContext.ts @@ -0,0 +1,3 @@ +import React from 'react'; + +export default React.createContext<{ isMounting: boolean } | null>(null); diff --git a/src/config.js b/src/config.ts similarity index 100% rename from src/config.js rename to src/config.ts diff --git a/src/index.js b/src/index.ts similarity index 100% rename from src/index.js rename to src/index.ts diff --git a/src/utils/ChildMapping.js b/src/utils/ChildMapping.ts similarity index 77% rename from src/utils/ChildMapping.js rename to src/utils/ChildMapping.ts index cdd33a3e..98740760 100644 --- a/src/utils/ChildMapping.js +++ b/src/utils/ChildMapping.ts @@ -1,4 +1,5 @@ import { Children, cloneElement, isValidElement } from 'react'; +import type { ReactChild, ReactElement, ReactNode } from 'react'; /** * Given `this.props.children`, return an object mapping key to child. @@ -6,14 +7,19 @@ import { Children, cloneElement, isValidElement } from 'react'; * @param {*} children `this.props.children` * @return {object} Mapping of key to child */ -export function getChildMapping(children, mapFn) { - let mapper = (child) => +export function getChildMapping( + children: ReactNode[] | ReactNode, + mapFn?: (child: ReactElement) => ReactElement +) { + let mapper = (child: ReactNode) => mapFn && isValidElement(child) ? mapFn(child) : child; let result = Object.create(null); if (children) + // @ts-expect-error FIXME: Object is possibly 'null' or 'undefined'.ts(2533) Children.map(children, (c) => c).forEach((child) => { // run the map function here instead so that the key is the computed one + // @ts-expect-error FIXME: Property 'key' does not exist on type 'string'.ts(2339) result[child.key] = mapper(child); }); return result; @@ -36,11 +42,14 @@ export function getChildMapping(children, mapFn) { * @return {object} a key set that contains all keys in `prev` and all keys * in `next` in a reasonable order. */ -export function mergeChildMappings(prev, next) { +export function mergeChildMappings( + prev: Record, + next: Record +) { prev = prev || {}; next = next || {}; - function getValueForKey(key) { + function getValueForKey(key: string) { return key in next ? next[key] : prev[key]; } @@ -61,7 +70,7 @@ export function mergeChildMappings(prev, next) { } let i; - let childMapping = {}; + let childMapping: Record = {}; for (let nextKey in next) { if (nextKeysPending[nextKey]) { for (i = 0; i < nextKeysPending[nextKey].length; i++) { @@ -81,12 +90,22 @@ export function mergeChildMappings(prev, next) { return childMapping; } -function getProp(child, prop, props) { +function getProp( + child: ReactElement, + prop: string, + props: Record +) { return props[prop] != null ? props[prop] : child.props[prop]; } -export function getInitialChildMapping(props, onExited) { - return getChildMapping(props.children, (child) => { +export function getInitialChildMapping( + props: { children: ReactNode[] }, + onExited: ( + child: ReactElement<{ onExited: (node: HTMLElement) => void }>, + node: HTMLElement + ) => void +) { + return getChildMapping(props.children, (child: ReactElement) => { return cloneElement(child, { onExited: onExited.bind(null, child), in: true, @@ -97,7 +116,14 @@ export function getInitialChildMapping(props, onExited) { }); } -export function getNextChildMapping(nextProps, prevChildMapping, onExited) { +export function getNextChildMapping( + nextProps: { children: ReactNode[] }, + prevChildMapping: Record, + onExited: ( + child: ReactElement<{ onExited: (node: HTMLElement) => void }>, + node: HTMLElement + ) => void +) { let nextChildMapping = getChildMapping(nextProps.children); let children = mergeChildMappings(prevChildMapping, nextChildMapping); diff --git a/src/utils/PropTypes.js b/src/utils/PropTypes.ts similarity index 100% rename from src/utils/PropTypes.js rename to src/utils/PropTypes.ts diff --git a/src/utils/SimpleSet.js b/src/utils/SimpleSet.js deleted file mode 100644 index 1617ecba..00000000 --- a/src/utils/SimpleSet.js +++ /dev/null @@ -1,21 +0,0 @@ -export default class SimpleSet { - constructor() { - this.v = []; - } - clear() { - this.v.length = 0; - } - has(k) { - return this.v.indexOf(k) !== -1; - } - add(k) { - if (this.has(k)) return; - this.v.push(k); - } - delete(k) { - const idx = this.v.indexOf(k); - if (idx === -1) return false; - this.v.splice(idx, 1); - return true; - } -} diff --git a/test/SSR-test.js b/test/SSR-test.js index 629acacd..20c282de 100644 --- a/test/SSR-test.js +++ b/test/SSR-test.js @@ -3,6 +3,7 @@ */ // test that import does not crash +// eslint-disable-next-line @typescript-eslint/no-unused-vars import * as ReactTransitionGroup from '../src'; // eslint-disable-line no-unused-vars describe('SSR', () => { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..20c79ba7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,104 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "lib": [ + "es2015", + "DOM" + ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, + "jsx": "react" /* Specify what JSX code is generated. */, + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */, + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + "noEmit": true /* Disable emitting files from a compilation. */, + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/yarn.lock b/yarn.lock index 062bb735..e9105f1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -72,6 +72,13 @@ dependencies: "@babel/highlight" "^7.14.5" +"@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.5.tgz#8ef4c18e58e801c5c95d3c1c0f2874a2680fadea" @@ -160,6 +167,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.17.3": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" + integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.3.4", "@babel/generator@^7.9.0", "@babel/generator@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" @@ -177,6 +193,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" @@ -250,6 +273,19 @@ "@babel/helper-replace-supers" "^7.14.5" "@babel/helper-split-export-declaration" "^7.14.5" +"@babel/helper-create-class-features-plugin@^7.16.7": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" + integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-create-class-features-plugin@^7.8.3": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.5.tgz#79753d44017806b481017f24b02fd4113c7106ea" @@ -316,6 +352,13 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-explode-assignable-expression@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" @@ -340,6 +383,15 @@ "@babel/template" "^7.14.5" "@babel/types" "^7.14.5" +"@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== + dependencies: + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" @@ -356,6 +408,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" @@ -370,6 +429,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-hoist-variables@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" @@ -384,6 +450,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" + integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== + dependencies: + "@babel/types" "^7.17.0" + "@babel/helper-member-expression-to-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" @@ -439,6 +512,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-optimise-call-expression@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" @@ -461,6 +541,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== +"@babel/helper-plugin-utils@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + "@babel/helper-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" @@ -498,6 +583,17 @@ "@babel/traverse" "^7.14.5" "@babel/types" "^7.14.5" +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" @@ -537,6 +633,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -549,6 +652,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + "@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" @@ -559,6 +667,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + "@babel/helper-wrap-function@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz#5919d115bf0fe328b8a5d63bcb610f51601f2bff" @@ -606,6 +719,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.8.3": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" @@ -630,6 +752,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.6.tgz#d85cc68ca3cac84eae384c06f032921f5227f4b2" integrity sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ== +"@babel/parser@^7.16.7", "@babel/parser@^7.17.3": + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" + integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e" @@ -1080,6 +1207,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-typescript@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" @@ -1658,6 +1792,15 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript" "^7.14.5" +"@babel/plugin-transform-typescript@^7.16.7": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" + "@babel/plugin-transform-unicode-escapes@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" @@ -1890,6 +2033,15 @@ "@babel/helper-validator-option" "^7.14.5" "@babel/plugin-transform-typescript" "^7.14.5" +"@babel/preset-typescript@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" + "@babel/register@^7.12.1": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233" @@ -1939,6 +2091,15 @@ "@babel/parser" "^7.14.5" "@babel/types" "^7.14.5" +"@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/template@^7.2.2", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -1993,6 +2154,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.16.7": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.3.4", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" @@ -2010,6 +2187,14 @@ "@babel/helper-validator-identifier" "^7.14.5" to-fast-properties "^2.0.0" +"@babel/types@^7.16.7", "@babel/types@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3750,6 +3935,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/prop-types@^15.7.4": + version "15.7.4" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" + integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== + "@types/q@^1.5.1": version "1.5.2" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" @@ -3767,7 +3957,7 @@ dependencies: "@types/react" "*" -"@types/react-dom@*": +"@types/react-dom@*", "@types/react-dom@^17.0.14": version "17.0.14" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f" integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ== @@ -3789,6 +3979,15 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/react@^17.0.41": + version "17.0.41" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.41.tgz#6e179590d276394de1e357b3f89d05d7d3da8b85" + integrity sha512-chYZ9ogWUodyC7VUTRBfblysKLjnohhFY9bGLwvnUFFy48+vB9DikmB3lW0qTFmBcKSzmdglcvkHK71IioOlDA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -3801,6 +4000,11 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -3914,6 +4118,16 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/parser@4": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== + dependencies: + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" + debug "^4.3.1" + "@typescript-eslint/parser@^4.9.1": version "4.26.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.26.1.tgz#cecfdd5eb7a5c13aabce1c1cfd7fbafb5a0f1e8e" @@ -3932,11 +4146,24 @@ "@typescript-eslint/types" "4.26.1" "@typescript-eslint/visitor-keys" "4.26.1" +"@typescript-eslint/scope-manager@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + "@typescript-eslint/types@4.26.1": version "4.26.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.1.tgz#9e7c523f73c34b04a765e4167ca5650436ef1d38" integrity sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg== +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== + "@typescript-eslint/typescript-estree@4.26.1": version "4.26.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz#b2ce2e789233d62283fae2c16baabd4f1dbc9633" @@ -3950,6 +4177,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + "@typescript-eslint/visitor-keys@4.26.1": version "4.26.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz#0d55ea735cb0d8903b198017d6d4f518fdaac546" @@ -3958,6 +4198,14 @@ "@typescript-eslint/types" "4.26.1" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -4755,7 +5003,7 @@ axobject-query@^2.2.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== -babel-eslint@^10.0.3, babel-eslint@^10.1.0: +babel-eslint@^10.0.3: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== @@ -4801,6 +5049,16 @@ babel-loader@^8.2.2: make-dir "^3.1.0" schema-utils "^2.6.5" +babel-loader@^8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" + integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^1.4.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + babel-plugin-add-module-exports@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-1.0.2.tgz#96cd610d089af664f016467fc4567c099cce2d9c" @@ -6771,6 +7029,11 @@ csstype@^2.2.0, csstype@^2.5.7, csstype@^2.6.7: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b" integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w== +csstype@^3.0.2: + version "3.0.11" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" + integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -16896,10 +17159,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" - integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== +typescript@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== uglify-js@^3.1.4: version "3.13.5"