pax_global_header00006660000000000000000000000064142717102450014515gustar00rootroot0000000000000052 comment=4cb51a9be0ebf508cb8f6506452097f7ebb874fe react-transition-group-4.4.5/000077500000000000000000000000001427171024500161475ustar00rootroot00000000000000react-transition-group-4.4.5/.babelrc.js000066400000000000000000000004101427171024500201500ustar00rootroot00000000000000module.exports = { presets: [['babel-preset-jason', { runtime: false }]], plugins: [ ['babel-plugin-transform-react-remove-prop-types', { mode: 'wrap' }], ], env: { esm: { presets: [['babel-preset-jason', { modules: false }]], }, }, }; react-transition-group-4.4.5/.eslintignore000066400000000000000000000000541427171024500206510ustar00rootroot00000000000000**/node_modules www/.cache/ www/public/ lib react-transition-group-4.4.5/.eslintrc.yml000066400000000000000000000004541427171024500205760ustar00rootroot00000000000000parser: babel-eslint extends: - jason/react - plugin:jsx-a11y/recommended - prettier settings: react: version: detect env: node: true browser: true plugins: - jsx-a11y overrides: - files: www/**/* env: es6: true - files: stories/**/* rules: no-console: off react-transition-group-4.4.5/.github/000077500000000000000000000000001427171024500175075ustar00rootroot00000000000000react-transition-group-4.4.5/.github/ISSUE_TEMPLATE/000077500000000000000000000000001427171024500216725ustar00rootroot00000000000000react-transition-group-4.4.5/.github/ISSUE_TEMPLATE/bug-report.md000066400000000000000000000005331427171024500243030ustar00rootroot00000000000000--- name: Bug report about: Something isn't working as expected. --- > What is the current behavior? > What is the expected behavior? > Could you provide a [CodeSandbox](https://codesandbox.io/) demo reproducing the bug? react-transition-group-4.4.5/.github/ISSUE_TEMPLATE/feature-request.md000066400000000000000000000002071427171024500253340ustar00rootroot00000000000000--- name: Feature request about: I have a suggestion on how to improve the library. --- > What would you like improved? react-transition-group-4.4.5/.github/workflows/000077500000000000000000000000001427171024500215445ustar00rootroot00000000000000react-transition-group-4.4.5/.github/workflows/ci.yml000066400000000000000000000043131427171024500226630ustar00rootroot00000000000000name: CI on: push: branches: [master, alpha] pull_request: branches: [master, alpha] jobs: test: runs-on: ubuntu-latest strategy: # Otherwise how would we know if a specific React version caused the failure? fail-fast: false matrix: REACT_DIST: [16, 17, 18] steps: - uses: actions/checkout@v2 - name: Use Node.js 14 uses: actions/setup-node@v2 with: node-version: 14 cache: 'npm' - run: yarn - run: yarn add react@${{ matrix.REACT_DIST }} react-dom@${{ matrix.REACT_DIST }} - run: yarn add @testing-library/react@12 if: matrix.REACT_DIST == '17' || matrix.REACT_DIST == '16' - run: yarn --cwd www # Test whether the web page can be built successfully or not - run: yarn --cwd www build - run: yarn test - name: Dry release uses: cycjimmy/semantic-release-action@v2 with: dry_run: true semantic_version: 17 branches: | [ 'master', {name: 'alpha', prerelease: true} ] env: # These are not available on forks but we need them on actual runs to verify everything is set up. # Otherwise we might fail in the middle of a release GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} release: needs: test runs-on: ubuntu-latest if: ${{ github.repository == 'reactjs/react-transition-group' && contains('refs/heads/master,refs/heads/alpha', github.ref) && github.event_name == 'push' }} steps: - uses: styfle/cancel-workflow-action@0.9.0 - uses: actions/checkout@v2 - name: Use Node.js 14 uses: actions/setup-node@v2 with: node-version: 14 cache: 'npm' - run: yarn - run: yarn build - name: Release uses: cycjimmy/semantic-release-action@v2 with: semantic_version: 17 branches: | [ 'master', {name: 'alpha', prerelease: true} ] env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} react-transition-group-4.4.5/.gitignore000066400000000000000000000011641427171024500201410ustar00rootroot00000000000000/lib # Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages # Optional npm cache directory .npm # Optional REPL history .node_repl_history # Visual Studio Code configuration .vscode react-transition-group-4.4.5/.prettierignore000066400000000000000000000000601427171024500212060ustar00rootroot00000000000000**/node_modules www/.cache/ www/public/ lib *.mdreact-transition-group-4.4.5/.size-snapshot.json000066400000000000000000000003531427171024500217300ustar00rootroot00000000000000{ "./lib/dist/react-transition-group.js": { "bundled": 82684, "minified": 22426, "gzipped": 6876 }, "./lib/dist/react-transition-group.min.js": { "bundled": 47269, "minified": 14623, "gzipped": 4616 } } react-transition-group-4.4.5/.storybook/000077500000000000000000000000001427171024500202605ustar00rootroot00000000000000react-transition-group-4.4.5/.storybook/main.js000066400000000000000000000005221427171024500215410ustar00rootroot00000000000000const { plugins, rules } = require('webpack-atoms'); module.exports = { stories: ['../stories/index.js'], webpackFinal: (config) => { config.module = { rules: [rules.js(), rules.astroturf(), rules.css({ extract: false })], }; config.plugins.push(plugins.extractCss({ disable: true })); return config; }, }; react-transition-group-4.4.5/.storybook/preview.js000066400000000000000000000002161427171024500222760ustar00rootroot00000000000000import React from 'react'; export const decorators = [ (Story) => ( ), ]; react-transition-group-4.4.5/CHANGELOG.md000066400000000000000000000336421427171024500177700ustar00rootroot00000000000000## [4.4.5](https://github.com/reactjs/react-transition-group/compare/v4.4.4...v4.4.5) (2022-08-01) ### Bug Fixes * apply entering animation synchronously when unmountOnExit or mountOnEnter is enabled ([#847](https://github.com/reactjs/react-transition-group/issues/847)) ([1043549](https://github.com/reactjs/react-transition-group/commit/10435492f5a5675b0e80ca6a435834ce4a0f270e)) ## [4.4.4](https://github.com/reactjs/react-transition-group/compare/v4.4.3...v4.4.4) (2022-07-30) ### Bug Fixes * missing build files ([#845](https://github.com/reactjs/react-transition-group/issues/845)) ([97af789](https://github.com/reactjs/react-transition-group/commit/97af7893b0a5bbf69211bc3287aee814123ddeea)) ## [4.4.3](https://github.com/reactjs/react-transition-group/compare/v4.4.2...v4.4.3) (2022-07-30) ### Bug Fixes * enter animations with mountOnEnter or unmountOnExit ([#749](https://github.com/reactjs/react-transition-group/issues/749)) ([51bdceb](https://github.com/reactjs/react-transition-group/commit/51bdceb96c8b6a79f417c32326ef1b31160edb97)) ## [4.4.2](https://github.com/reactjs/react-transition-group/compare/v4.4.1...v4.4.2) (2021-05-29) ### Bug Fixes * `nodeRef` prop type for cross-realm elements ([#732](https://github.com/reactjs/react-transition-group/issues/732)) ([8710c01](https://github.com/reactjs/react-transition-group/commit/8710c01549e09f55eeefec2aadb3af0a23a00f82)) ## [4.4.1](https://github.com/reactjs/react-transition-group/compare/v4.4.0...v4.4.1) (2020-05-06) ### Bug Fixes * transition SSR ([#619](https://github.com/reactjs/react-transition-group/issues/619)) ([2722bb6](https://github.com/reactjs/react-transition-group/commit/2722bb6b755943b8292f0f2bc2fdca55df5c28f0)) # [4.4.0](https://github.com/reactjs/react-transition-group/compare/v4.3.0...v4.4.0) (2020-05-05) ### Features * add `nodeRef` alternative instead of internal `findDOMNode` ([#559](https://github.com/reactjs/react-transition-group/issues/559)) ([85016bf](https://github.com/reactjs/react-transition-group/commit/85016bfddd3831e6d7bb27926f9f178d25502913)) - react-transition-group internally uses `findDOMNode`, which is deprecated and produces warnings in [Strict Mode](https://reactjs.org/docs/strict-mode.html), so now you can optionally pass `nodeRef` to `Transition` and `CSSTransition`, it's a ref object that should point to the transitioning child: ```jsx import React from "react" import { CSSTransition } from "react-transition-group" const MyComponent = () => { const nodeRef = React.useRef(null) return (
Fade
) } ``` ### Bug Fixes * set the values of constants attached to `Transition` to match the exported ones ([#554](https://github.com/reactjs/react-transition-group/pull/554)) # [4.3.0](https://github.com/reactjs/react-transition-group/compare/v4.2.2...v4.3.0) (2019-09-05) ### Features * upgrade dom-helpers ([#549](https://github.com/reactjs/react-transition-group/issues/549)) ([b017e18](https://github.com/reactjs/react-transition-group/commit/b017e18)) ## [4.2.2](https://github.com/reactjs/react-transition-group/compare/v4.2.1...v4.2.2) (2019-08-02) ### Bug Fixes * Fix imports to play nicely with rollup ([#530](https://github.com/reactjs/react-transition-group/issues/530)) ([3d9003e](https://github.com/reactjs/react-transition-group/commit/3d9003e)) ## [4.2.1](https://github.com/reactjs/react-transition-group/compare/v4.2.0...v4.2.1) (2019-07-02) ### Bug Fixes * updated SwitchTransition component to be default export and exported from index.js ([#516](https://github.com/reactjs/react-transition-group/issues/516)) ([cfd0070](https://github.com/reactjs/react-transition-group/commit/cfd0070)) # [4.2.0](https://github.com/reactjs/react-transition-group/compare/v4.1.1...v4.2.0) (2019-06-28) ### Features * add SwitchTransition component ([#470](https://github.com/reactjs/react-transition-group/issues/470)) ([c5e379d](https://github.com/reactjs/react-transition-group/commit/c5e379d)) ## [4.1.1](https://github.com/reactjs/react-transition-group/compare/v4.1.0...v4.1.1) (2019-06-10) ### Bug Fixes * adds missing dependency [@babel](https://github.com/babel)/runtime ([#507](https://github.com/reactjs/react-transition-group/issues/507)) ([228bf5f](https://github.com/reactjs/react-transition-group/commit/228bf5f)) # [4.1.0](https://github.com/reactjs/react-transition-group/compare/v4.0.1...v4.1.0) (2019-05-30) ### Features * add global transition disable switch ([#506](https://github.com/reactjs/react-transition-group/issues/506)) ([4c5ba98](https://github.com/reactjs/react-transition-group/commit/4c5ba98)) ## [4.0.1](https://github.com/reactjs/react-transition-group/compare/v4.0.0...v4.0.1) (2019-05-09) ### Bug Fixes * issue with dynamically applied classes not being properly removed for reentering items ([#499](https://github.com/reactjs/react-transition-group/issues/499)) ([129cb11](https://github.com/reactjs/react-transition-group/commit/129cb11)) # [4.0.0](https://github.com/reactjs/react-transition-group/compare/v3.0.0...v4.0.0) (2019-04-16) ### Features * support esm via package.json routes ([#488](https://github.com/reactjs/react-transition-group/issues/488)) ([6337bf5](https://github.com/reactjs/react-transition-group/commit/6337bf5)), closes [#77](https://github.com/reactjs/react-transition-group/issues/77) ### BREAKING CHANGES * in environments where esm is supported importing from commonjs requires explicitly adding the `.default` after `require()` when resolving to the esm build # [3.0.0](https://github.com/reactjs/react-transition-group/compare/v2.9.0...v3.0.0) (2019-04-15) ### Features * use stable context API ([#471](https://github.com/reactjs/react-transition-group/issues/471)) ([aee4901](https://github.com/reactjs/react-transition-group/commit/aee4901)), closes [#429](https://github.com/reactjs/react-transition-group/issues/429) ### BREAKING CHANGES * use new style react context ```diff // package.json -"react": "^15.0.0", +"react": "^16.6.0", -"react-dom": "^15.0.0", +"react-dom": "^16.6.0", ``` # [2.9.0](https://github.com/reactjs/react-transition-group/compare/v2.8.0...v2.9.0) (2019-04-06) ### Features * **CSSTransition:** add "done" class for appear ([fe3c156](https://github.com/reactjs/react-transition-group/commit/fe3c156)), closes [#383](https://github.com/reactjs/react-transition-group/issues/383) [#327](https://github.com/reactjs/react-transition-group/issues/327) [#327](https://github.com/reactjs/react-transition-group/issues/327) ### Reverts * bump semantic release dependencies ([1bdcaec](https://github.com/reactjs/react-transition-group/commit/1bdcaec)) # [2.8.0](https://github.com/reactjs/react-transition-group/compare/v2.7.1...v2.8.0) (2019-04-02) ### Features * add support for empty classNames ([#481](https://github.com/reactjs/react-transition-group/issues/481)) ([d755dc6](https://github.com/reactjs/react-transition-group/commit/d755dc6)) ## [2.7.1](https://github.com/reactjs/react-transition-group/compare/v2.7.0...v2.7.1) (2019-03-25) ### Bug Fixes * revert tree-shaking support because it was a breaking change ([271364c](https://github.com/reactjs/react-transition-group/commit/271364c)) # [2.7.0](https://github.com/reactjs/react-transition-group/compare/v2.6.1...v2.7.0) (2019-03-22) ### Features * support ESM (tree-shaking) ([#455](https://github.com/reactjs/react-transition-group/issues/455)) ([ef3e357](https://github.com/reactjs/react-transition-group/commit/ef3e357)) ## [2.6.1](https://github.com/reactjs/react-transition-group/compare/v2.6.0...v2.6.1) (2019-03-14) ### Bug Fixes * **Transition:** make `exit` key optional when passing an object to the `timeout` prop ([#464](https://github.com/reactjs/react-transition-group/pull/464)) ([3a4cf9c](https://github.com/reactjs/react-transition-group/commit/3a4cf9c91ab5f25caaa9501b129bce66ec9bb56b)) * **package.json:** mark react-transition-group as side-effect free for webpack tree shaking ([#472](https://github.com/reactjs/react-transition-group/issues/472)) ([b81dc89](https://github.com/reactjs/react-transition-group/commit/b81dc89)) # [2.6.0](https://github.com/reactjs/react-transition-group/compare/v2.5.3...v2.6.0) (2019-02-26) ### Features * add appear timeout ([#462](https://github.com/reactjs/react-transition-group/issues/462)) ([52cdc34](https://github.com/reactjs/react-transition-group/commit/52cdc34)) ## [2.5.3](https://github.com/reactjs/react-transition-group/compare/v2.5.2...v2.5.3) (2019-01-14) ### Bug Fixes * strip custom prop-types in production ([#448](https://github.com/reactjs/react-transition-group/issues/448)) ([46fa20f](https://github.com/reactjs/react-transition-group/commit/46fa20f)) ## [2.5.2](https://github.com/reactjs/react-transition-group/compare/v2.5.1...v2.5.2) (2018-12-20) ### Bug Fixes * pass appear to CSSTransition callbacks ([#441](https://github.com/reactjs/react-transition-group/issues/441)) ([df7adb4](https://github.com/reactjs/react-transition-group/commit/df7adb4)), closes [#143](https://github.com/reactjs/react-transition-group/issues/143) ## [2.5.1](https://github.com/reactjs/react-transition-group/compare/v2.5.0...v2.5.1) (2018-12-10) ### Bug Fixes * prevent calling setState in TransitionGroup if it has been unmounted ([#435](https://github.com/reactjs/react-transition-group/issues/435)) ([6d46b69](https://github.com/reactjs/react-transition-group/commit/6d46b69)) # [2.5.0](https://github.com/reactjs/react-transition-group/compare/v2.4.0...v2.5.0) (2018-09-26) ### Features * update build and package dependencies ([#413](https://github.com/reactjs/react-transition-group/issues/413)) ([af3d45a](https://github.com/reactjs/react-transition-group/commit/af3d45a)) # [2.4.0](https://github.com/reactjs/react-transition-group/compare/v2.3.1...v2.4.0) (2018-06-27) ### Features * remove deprecated lifecycle hooks and polyfill for older react versions ([c1ab1cf](https://github.com/reactjs/react-transition-group/commit/c1ab1cf)) ### Performance Improvements * don't reflow when there's no class to add ([d7b898d](https://github.com/reactjs/react-transition-group/commit/d7b898d)) ## [2.3.1](https://github.com/reactjs/react-transition-group/compare/v2.3.0...v2.3.1) (2018-04-14) ### Bug Fixes * **deps:** Move loose-envify and semantic-release to devDependencies ([#319](https://github.com/reactjs/react-transition-group/issues/319)) ([b4ec774](https://github.com/reactjs/react-transition-group/commit/b4ec774)) ## [v2.3.0] > 2018-03-28 * Added `*-done` classes to CSS Transition ([#269]) * Reorganize docs with more interesting examples! ([#304]) * A bunch of bug fixes [#269]: https://github.com/reactjs/react-transition-group/pull/269 [#304]: https://github.com/reactjs/react-transition-group/pull/304 [v2.3.0]: https://github.com/reactjs/react-transition-group/compare/v2.2.1...2.3.0 ## [v2.2.1] > 2017-09-29 * **Patch:** Allow React v16 ([#198]) [#198]: https://github.com/reactjs/react-transition-group/pull/198 [v2.2.1]: https://github.com/reactjs/react-transition-group/compare/v2.2.0...2.2.1 ## [v2.2.0] > 2017-07-21 * **Feature:** Support multiple classes in `classNames` ([#124]) * **Docs:** fix broken link ([#127]) * **Bugfix:** Fix Transition props pass-through ([#123]) [#124]: https://github.com/reactjs/react-transition-group/pull/124 [#123]: https://github.com/reactjs/react-transition-group/pull/123 [#127]: https://github.com/reactjs/react-transition-group/pull/127 [v2.2.0]: https://github.com/reactjs/react-transition-group/compare/v2.1.0...2.2.0 ## [v2.1.0] > 2017-07-06 * **Feature:** Add back `childFactory` on `` ([#113]) * **Bugfix:** Ensure child specified `onExited` fires in a `` ([#113]) [#113]: https://github.com/reactjs/react-transition-group/pull/113 [v2.1.0]: https://github.com/reactjs/react-transition-group/compare/v2.0.1...2.1.0 ## v2.0.2 > 2017-07-06 * **Fix documentation npm:** No code changes ## v2.0.1 > 2017-07-06 * **Fix documentation on npm:** No code changes ## [v2.0.0] > 2017-07-06 * **Feature:** New API! ([#24]), migration guide at [https://github.com/reactjs/react-transition-group/blob/master/Migration.md](https://github.com/reactjs/react-transition-group/blob/master/Migration.md) [#24]: https://github.com/reactjs/react-transition-group/pull/24 [v2.0.0]: https://github.com/reactjs/react-transition-group/compare/v1.2.0...v2.0.0 ## [v1.2.0] > 2017-06-12 * **Feature:** Dist build now includes both production and development builds ([#64]) * **Feature:** PropTypes are now wrapped allowing for lighter weight production builds ([#69]) [#64]: https://github.com/reactjs/react-transition-group/issues/64 [#69]: https://github.com/reactjs/react-transition-group/issues/69 [v1.1.x]: https://github.com/reactjs/react-transition-group/compare/v1.1.3...master ## [v1.1.3] > 2017-05-02 * bonus release, no additions [v1.1.3]: https://github.com/reactjs/react-transition-group/compare/v1.1.2...v1.1.3 ## [v1.1.2] > 2017-05-02 * **Bugfix:** Fix refs on children ([#39]) [v1.1.2]: https://github.com/reactjs/react-transition-group/compare/v1.1.1...v1.1.2 [#39]: https://github.com/reactjs/react-transition-group/pull/39 ## [v1.1.1] > 2017-03-16 * **Chore:** Add a prebuilt version of the library for jsbin and the like. [v1.1.1]: https://github.com/reactjs/react-transition-group/compare/v1.1.0...v1.1.1 ## [v1.1.0] > 2017-03-16 * **Feature:** Support refs on children ([#9]) * **Feature:** TransitionChild to passes props through ([#4]) * **Bugfix:** Fix TransitionGroup error on quick toggle of components ([#15]) * **Bugfix:** Fix to work enter animation with CSSTransitionGroup ([#13]) [v1.1.0]: https://github.com/reactjs/react-transition-group/compare/v1.0.0...v1.1.0 [#15]: https://github.com/reactjs/react-transition-group/pull/15 [#13]: https://github.com/reactjs/react-transition-group/pull/13 [#9]: https://github.com/reactjs/react-transition-group/pull/9 [#4]: https://github.com/reactjs/react-transition-group/pull/4 react-transition-group-4.4.5/LICENSE000066400000000000000000000031101427171024500171470ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2018, React Community Forked from React (https://github.com/facebook/react) Copyright 2013-present, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. react-transition-group-4.4.5/Migration.md000066400000000000000000000160361427171024500204300ustar00rootroot00000000000000# Migration Guide from v1 to v2 _A few notes to help with migrating from v1 to v2._ The `` component has been removed. A `` component has been added for use with the new `` component to accomplish the same tasks. ### tl;dr: - `transitionName` -> `classNames` - `transitionEnterTimeout` and `transitionLeaveTimeout` -> `timeout={{ exit, enter }}` - `transitionAppear` -> `appear` - `transitionEnter` -> `enter` - `transitionLeave` -> `exit` ## Walkthrough Let's take the [original docs example](https://github.com/reactjs/react-transition-group/tree/v1-stable/#high-level-api-csstransitiongroup) and migrate it. Starting with this CSS: ```css .example-enter { opacity: 0.01; } .example-enter.example-enter-active { opacity: 1; transition: opacity 500ms ease-in; } .example-leave { opacity: 1; } .example-leave.example-leave-active { opacity: 0.01; transition: opacity 300ms ease-in; } ``` And this component: ```js class TodoList extends React.Component { constructor(props) { super(props); this.state = {items: ['hello', 'world', 'click', 'me']}; this.handleAdd = this.handleAdd.bind(this); } handleAdd() { const newItems = this.state.items.concat([ prompt('Enter some text') ]); this.setState({items: newItems}); } handleRemove(i) { let newItems = this.state.items.slice(); newItems.splice(i, 1); this.setState({items: newItems}); } render() { const items = this.state.items.map((item, i) => (
this.handleRemove(i)}> {item}
)); return (
{items}
); } } ``` The most straightforward way to migrate is to use `` instead of ``: ```diff render() { const items = this.state.items.map((item, i) => (
this.handleRemove(i)}> {item}
)); return (
- + {items} - +
) } ``` That doesn't get us much, since we haven't included anything to do the animation. For that, we'll need to wrap each item in a ``. First, though, let's adjust our CSS: ```diff .example-enter { opacity: 0.01; } .example-enter.example-enter-active { opacity: 1; transition: opacity 500ms ease-in; } -.example-leave { +.example-exit { opacity: 1; } -.example-leave.example-leave-active { +.example-exit.example-exit-active { opacity: 0.01; transition: opacity 300ms ease-in; } ``` All we did was replace `leave` with `exit`. v2 uses "exit" instead of "leave" to be more symmetric, avoiding awkwardness with English tenses (like with "entered" and "leaved"). Now we add the `` component: ```diff render() { const items = this.state.items.map((item, i) => ( +
this.handleRemove(i)}> {item}
+
)); return (
{items}
) } ``` Note that we replaced `transitionName` with `classNames`. `` otherwise has essentially the same signature as ``. We also replaced `transitionEnterTimeout` and `transitionLeaveTimeout` with a single `timeout` prop with an object. > **Hint:** If your enter and exit timeouts are the same you can use the shorthand `timeout={500}`. If we want to make this a bit more encapsulated, we can wrap our `` into a separate component for reuse later: ```js const FadeTransition = (props) => ( ); ``` We can then use it like: ```diff render() { const items = this.state.items.map((item, i) => ( - +
this.handleRemove(i)}> {item}
-
+ )); return (
{items}
) } ``` > **Hey!** You may not need `` at all! The lower level `` component is very flexible and may be easier to work with for simpler or more custom cases. Check out how we migrated [React-Bootstrap](https://react-bootstrap.github.io/)'s simple transitions to v2 for the [``](https://github.com/react-bootstrap/react-bootstrap/pull/2676/files#diff-4f938f648d04d4859be417d6590ca7c4) and [``](https://github.com/react-bootstrap/react-bootstrap/pull/2676/files#diff-8f766132cbd9f8de55ee05d63d75abd8) components. ## Wrapping `` Components The old `` component managed transitions through custom static lifecycle methods on its children. In v2 we removed that API in favor of requiring that `` be used with a `` component, and using traditional prop passing to communicate between the two. This means that ``s inject their children with ``-specific props that _must_ be passed through to the `` component for the transition to work. ```js const MyTransition = ({ children: child, ...props }) => ( // NOTICE THE SPREAD! THIS IS REQUIRED! {transitionState => React.cloneElement(child, { style: getStyleForTransitionState(transitionState) })} ); const MyList = () => ( {items.map(item => ( {item} )} ); ``` Note how `` passes all props other than its own to ``. ## Lifecycle Callbacks As noted, child lifecycle methods have been removed. If you do need to do some work when the `` changes from one state to another, use the lifecycle callback props. ```js ``` Each callback is called with the DOM node of the transition component. Note also that there are now _three_ states per enter/exit transition instead of the original two. See the [full documentation](https://reactcommunity.org/react-transition-group/#Transition) for more details. react-transition-group-4.4.5/README.md000066400000000000000000000030401427171024500174230ustar00rootroot00000000000000# react-transition-group [![npm][npm-badge]][npm] > **ATTENTION!** To address many issues that have come up over the years, the API in v2 and above is not backwards compatible with the original [`React addon (v1-stable)`](https://github.com/reactjs/react-transition-group/tree/v1-stable). > > **For a drop-in replacement for `react-addons-transition-group` and `react-addons-css-transition-group`, use the v1 release. Documentation and code for that release are available on the [`v1-stable`](https://github.com/reactjs/react-transition-group/tree/v1-stable) branch.** > > We are no longer updating the v1 codebase, please upgrade to the latest version when possible A set of components for managing component states (including mounting and unmounting) over time, specifically designed with animation in mind. ## Documentation - [**Main documentation**](https://reactcommunity.org/react-transition-group/) - [Migration guide from v1](/Migration.md) ## TypeScript TypeScript definitions are published via [**DefinitelyTyped**](https://github.com/DefinitelyTyped/DefinitelyTyped) and can be installed via the following command: ``` npm install @types/react-transition-group ``` ## Examples Clone the repo first: ``` git@github.com:reactjs/react-transition-group.git ``` Then run `npm install` (or `yarn`), and finally `npm run storybook` to start a storybook instance that you can navigate to in your browser to see the examples. [npm-badge]: https://img.shields.io/npm/v/react-transition-group.svg [npm]: https://www.npmjs.org/package/react-transition-group react-transition-group-4.4.5/package.json000066400000000000000000000102501427171024500204330ustar00rootroot00000000000000{ "name": "react-transition-group", "version": "4.4.5", "description": "A react component toolset for managing animations", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", "scripts": { "test": "npm run lint && npm run testonly", "testonly": "jest --verbose", "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: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", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "yarn lint:eslint --fix", "fix:prettier": "yarn lint:prettier --write", "lint": "run-p lint:*", "lint:eslint": "eslint .", "lint:prettier": "prettier . --check", "release": "release", "release:next": "release --preid beta --tag next", "deploy-docs": "yarn --cwd www run deploy", "start": "yarn --cwd www run develop", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", "semantic-release": "semantic-release" }, "repository": { "type": "git", "url": "https://github.com/reactjs/react-transition-group.git" }, "keywords": [ "react", "transition", "addons", "transition-group", "animation", "css", "transitions" ], "author": "", "license": "BSD-3-Clause", "bugs": { "url": "https://github.com/reactjs/react-transition-group/issues" }, "homepage": "https://github.com/reactjs/react-transition-group#readme", "jest": { "testRegex": "-test\\.js", "setupFiles": [ "./test/setup.js" ], "setupFilesAfterEnv": [ "./test/setupAfterEnv.js" ], "roots": [ "/test" ] }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" }, "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "devDependencies": { "@babel/cli": "^7.8.4", "@babel/core": "^7.9.0", "@restart/hooks": "^0.3.22", "@semantic-release/changelog": "^5.0.1", "@semantic-release/git": "^9.0.0", "@semantic-release/github": "^7.0.5", "@semantic-release/npm": "^7.0.5", "@storybook/addon-actions": "^6.3.4", "@storybook/react": "^6.3.4", "@testing-library/react": "alpha", "@typescript-eslint/eslint-plugin": "^4.26.1", "astroturf": "^0.10.4", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "babel-preset-jason": "^6.2.0", "cherry-pick": "^0.5.0", "cross-env": "^7.0.2", "eslint": "^7.28.0", "eslint-config-jason": "^8.1.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.23.4", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", "jest": "^25.3.0", "npm-run-all": "^4.1.5", "prettier": "^2.3.1", "react": "^18.0.0", "react-dom": "^18.0.0", "release-script": "^1.0.2", "rimraf": "^3.0.2", "rollup": "^2.6.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-replace": "^2.2.0", "rollup-plugin-size-snapshot": "^0.11.0", "rollup-plugin-terser": "^5.3.0", "semantic-release": "^17.0.6", "semantic-release-alt-publish-dir": "^3.0.0", "typescript": "^4.3.2", "webpack-atoms": "14.0.0" }, "release": { "pkgRoot": "lib", "verifyConditions": [ "@semantic-release/changelog", "semantic-release-alt-publish-dir", "@semantic-release/git", "@semantic-release/github" ], "prepare": [ "@semantic-release/changelog", "semantic-release-alt-publish-dir", "@semantic-release/npm", "@semantic-release/git" ] }, "browserify": { "transform": [ "loose-envify" ] }, "sideEffects": false } react-transition-group-4.4.5/prettier.config.js000066400000000000000000000000531427171024500216050ustar00rootroot00000000000000module.exports = { singleQuote: true, }; react-transition-group-4.4.5/rollup.config.js000066400000000000000000000026531427171024500212740ustar00rootroot00000000000000import nodeResolve from 'rollup-plugin-node-resolve'; import babel from 'rollup-plugin-babel'; import commonjs from 'rollup-plugin-commonjs'; 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 name = 'ReactTransitionGroup'; const globals = { react: 'React', 'react-dom': 'ReactDOM', }; const babelOptions = { exclude: /node_modules/, runtimeHelpers: true, }; const commonjsOptions = { include: /node_modules/, namedExports: { 'prop-types': ['object', 'oneOfType', 'element', 'bool', 'func'], }, }; export default [ { input, output: { file: './lib/dist/react-transition-group.js', format: 'umd', name, globals, }, external: Object.keys(globals), plugins: [ nodeResolve(), babel(babelOptions), commonjs(commonjsOptions), replace({ 'process.env.NODE_ENV': JSON.stringify('development') }), sizeSnapshot(), ], }, { input, output: { file: './lib/dist/react-transition-group.min.js', format: 'umd', name, globals, }, external: Object.keys(globals), plugins: [ nodeResolve(), babel(babelOptions), commonjs(commonjsOptions), replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), sizeSnapshot(), terser(), ], }, ]; react-transition-group-4.4.5/src/000077500000000000000000000000001427171024500167365ustar00rootroot00000000000000react-transition-group-4.4.5/src/CSSTransition.js000066400000000000000000000265761427171024500220170ustar00rootroot00000000000000import PropTypes from 'prop-types'; import addOneClass from 'dom-helpers/addClass'; import removeOneClass from 'dom-helpers/removeClass'; import React from 'react'; import Transition from './Transition'; import { classNamesShape } from './utils/PropTypes'; import { forceReflow } from './utils/reflow'; const addClass = (node, classes) => node && classes && classes.split(' ').forEach((c) => addOneClass(node, c)); const removeClass = (node, classes) => node && classes && classes.split(' ').forEach((c) => removeOneClass(node, c)); /** * A transition component inspired by the excellent * [ng-animate](https://docs.angularjs.org/api/ngAnimate) library, you should * use it if you're using CSS transitions or animations. It's built upon the * [`Transition`](https://reactcommunity.org/react-transition-group/transition) * component, so it inherits all of its props. * * `CSSTransition` applies a pair of class names during the `appear`, `enter`, * and `exit` states of the transition. The first class is applied and then a * second `*-active` class in order to activate the CSS transition. After the * transition, matching `*-done` class names are applied to persist the * transition state. * * ```jsx * function App() { * const [inProp, setInProp] = useState(false); * return ( *
* *
* {"I'll receive my-node-* classes"} *
*
* *
* ); * } * ``` * * When the `in` prop is set to `true`, the child component will first receive * the class `example-enter`, then the `example-enter-active` will be added in * the next tick. `CSSTransition` [forces a * reflow](https://github.com/reactjs/react-transition-group/blob/5007303e729a74be66a21c3e2205e4916821524b/src/CSSTransition.js#L208-L215) * between before adding the `example-enter-active`. This is an important trick * because it allows us to transition between `example-enter` and * `example-enter-active` even though they were added immediately one after * another. Most notably, this is what makes it possible for us to animate * _appearance_. * * ```css * .my-node-enter { * opacity: 0; * } * .my-node-enter-active { * opacity: 1; * transition: opacity 200ms; * } * .my-node-exit { * opacity: 1; * } * .my-node-exit-active { * opacity: 0; * transition: opacity 200ms; * } * ``` * * `*-active` classes represent which styles you want to animate **to**, so it's * important to add `transition` declaration only to them, otherwise transitions * might not behave as intended! This might not be obvious when the transitions * are symmetrical, i.e. when `*-enter-active` is the same as `*-exit`, like in * the example above (minus `transition`), but it becomes apparent in more * complex transitions. * * **Note**: If you're using the * [`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 { static defaultProps = { classNames: '', }; appliedClasses = { appear: {}, enter: {}, exit: {}, }; onEnter = (maybeNode, maybeAppearing) => { const [node, appearing] = this.resolveArguments(maybeNode, maybeAppearing); this.removeClasses(node, 'exit'); this.addClass(node, appearing ? 'appear' : 'enter', 'base'); if (this.props.onEnter) { this.props.onEnter(maybeNode, maybeAppearing); } }; onEntering = (maybeNode, maybeAppearing) => { const [node, appearing] = this.resolveArguments(maybeNode, maybeAppearing); const type = appearing ? 'appear' : 'enter'; this.addClass(node, type, 'active'); if (this.props.onEntering) { this.props.onEntering(maybeNode, maybeAppearing); } }; onEntered = (maybeNode, maybeAppearing) => { const [node, appearing] = this.resolveArguments(maybeNode, maybeAppearing); const type = appearing ? 'appear' : 'enter'; this.removeClasses(node, type); this.addClass(node, type, 'done'); if (this.props.onEntered) { this.props.onEntered(maybeNode, maybeAppearing); } }; onExit = (maybeNode) => { const [node] = this.resolveArguments(maybeNode); this.removeClasses(node, 'appear'); this.removeClasses(node, 'enter'); this.addClass(node, 'exit', 'base'); if (this.props.onExit) { this.props.onExit(maybeNode); } }; onExiting = (maybeNode) => { const [node] = this.resolveArguments(maybeNode); this.addClass(node, 'exit', 'active'); if (this.props.onExiting) { this.props.onExiting(maybeNode); } }; onExited = (maybeNode) => { const [node] = this.resolveArguments(maybeNode); this.removeClasses(node, 'exit'); this.addClass(node, 'exit', 'done'); if (this.props.onExited) { this.props.onExited(maybeNode); } }; // when prop `nodeRef` is provided `node` is excluded resolveArguments = (maybeNode, maybeAppearing) => this.props.nodeRef ? [this.props.nodeRef.current, maybeNode] // here `maybeNode` is actually `appearing` : [maybeNode, maybeAppearing]; // `findDOMNode` was used getClassNames = (type) => { const { classNames } = this.props; const isStringClassNames = typeof classNames === 'string'; const prefix = isStringClassNames && classNames ? `${classNames}-` : ''; let baseClassName = isStringClassNames ? `${prefix}${type}` : classNames[type]; let activeClassName = isStringClassNames ? `${baseClassName}-active` : classNames[`${type}Active`]; let doneClassName = isStringClassNames ? `${baseClassName}-done` : classNames[`${type}Done`]; return { baseClassName, activeClassName, doneClassName, }; }; addClass(node, type, phase) { let className = this.getClassNames(type)[`${phase}ClassName`]; const { doneClassName } = this.getClassNames('enter'); if (type === 'appear' && phase === 'done' && doneClassName) { className += ` ${doneClassName}`; } // This is to force a repaint, // which is necessary in order to transition styles when adding a class name. if (phase === 'active') { if (node) forceReflow(node); } if (className) { this.appliedClasses[type][phase] = className; addClass(node, className); } } removeClasses(node, type) { const { base: baseClassName, active: activeClassName, done: doneClassName, } = this.appliedClasses[type]; this.appliedClasses[type] = {}; if (baseClassName) { removeClass(node, baseClassName); } if (activeClassName) { removeClass(node, activeClassName); } if (doneClassName) { removeClass(node, doneClassName); } } render() { const { classNames: _, ...props } = this.props; return ( ); } } CSSTransition.propTypes = { ...Transition.propTypes, /** * The animation classNames applied to the component as it appears, enters, * exits or has finished the transition. A single name can be provided, which * will be suffixed for each stage, e.g. `classNames="fade"` applies: * * - `fade-appear`, `fade-appear-active`, `fade-appear-done` * - `fade-enter`, `fade-enter-active`, `fade-enter-done` * - `fade-exit`, `fade-exit-active`, `fade-exit-done` * * A few details to note about how these classes are applied: * * 1. They are _joined_ with the ones that are already defined on the child * component, so if you want to add some base styles, you can use * `className` without worrying that it will be overridden. * * 2. If the transition component mounts with `in={false}`, no classes are * applied yet. You might be expecting `*-exit-done`, but if you think * about it, a component cannot finish exiting if it hasn't entered yet. * * 2. `fade-appear-done` and `fade-enter-done` will _both_ be applied. This * allows you to define different behavior for when appearing is done and * when regular entering is done, using selectors like * `.fade-enter-done:not(.fade-appear-done)`. For example, you could apply * an epic entrance animation when element first appears in the DOM using * [Animate.css](https://daneden.github.io/animate.css/). Otherwise you can * simply use `fade-enter-done` for defining both cases. * * Each individual classNames can also be specified independently like: * * ```js * classNames={{ * appear: 'my-appear', * appearActive: 'my-active-appear', * appearDone: 'my-done-appear', * enter: 'my-enter', * enterActive: 'my-active-enter', * enterDone: 'my-done-enter', * exit: 'my-exit', * exitActive: 'my-active-exit', * exitDone: 'my-done-exit', * }} * ``` * * If you want to set these classes using CSS Modules: * * ```js * import styles from './styles.css'; * ``` * * you might want to use camelCase in your CSS file, that way could simply * spread them instead of listing them one by one: * * ```js * classNames={{ ...styles }} * ``` * * @type {string | { * appear?: string, * appearActive?: string, * appearDone?: string, * enter?: string, * enterActive?: string, * enterDone?: string, * exit?: string, * exitActive?: string, * exitDone?: string, * }} */ classNames: classNamesShape, /** * A `` callback fired immediately after the 'enter' or 'appear' class is * applied. * * **Note**: when `nodeRef` prop is passed, `node` is not passed. * * @type Function(node: HtmlElement, isAppearing: bool) */ onEnter: PropTypes.func, /** * A `` callback fired immediately after the 'enter-active' or * 'appear-active' class is applied. * * **Note**: when `nodeRef` prop is passed, `node` is not passed. * * @type Function(node: HtmlElement, isAppearing: bool) */ onEntering: PropTypes.func, /** * A `` callback fired immediately after the 'enter' or * 'appear' classes are **removed** and the `done` class is added to the DOM node. * * **Note**: when `nodeRef` prop is passed, `node` is not passed. * * @type Function(node: HtmlElement, isAppearing: bool) */ onEntered: PropTypes.func, /** * A `` callback fired immediately after the 'exit' class is * applied. * * **Note**: when `nodeRef` prop is passed, `node` is not passed * * @type Function(node: HtmlElement) */ onExit: PropTypes.func, /** * A `` callback fired immediately after the 'exit-active' is applied. * * **Note**: when `nodeRef` prop is passed, `node` is not passed * * @type Function(node: HtmlElement) */ onExiting: PropTypes.func, /** * A `` callback fired immediately after the 'exit' classes * are **removed** and the `exit-done` class is added to the DOM node. * * **Note**: when `nodeRef` prop is passed, `node` is not passed * * @type Function(node: HtmlElement) */ onExited: PropTypes.func, }; export default CSSTransition; react-transition-group-4.4.5/src/ReplaceTransition.js000066400000000000000000000046421427171024500227300ustar00rootroot00000000000000import PropTypes from 'prop-types'; import React from 'react'; import ReactDOM from 'react-dom'; import TransitionGroup from './TransitionGroup'; /** * 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) => 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; react-transition-group-4.4.5/src/SwitchTransition.js000066400000000000000000000127211427171024500226130ustar00rootroot00000000000000import React from 'react'; import PropTypes from 'prop-types'; import { ENTERED, ENTERING, EXITING } from './Transition'; import TransitionGroupContext from './TransitionGroupContext'; function areChildrenDifferent(oldChildren, newChildren) { if (oldChildren === newChildren) return false; if ( React.isValidElement(oldChildren) && React.isValidElement(newChildren) && oldChildren.key != null && oldChildren.key === newChildren.key ) { return false; } return true; } /** * Enum of modes for SwitchTransition component * @enum { string } */ export const modes = { out: 'out-in', in: 'in-out', }; const callHook = (element, name, cb) => (...args) => { element.props[name] && element.props[name](...args); cb(); }; const leaveRenders = { [modes.out]: ({ current, changeState }) => React.cloneElement(current, { in: false, onExited: callHook(current, 'onExited', () => { changeState(ENTERING, null); }), }), [modes.in]: ({ current, changeState, children }) => [ current, React.cloneElement(children, { in: true, onEntered: callHook(children, 'onEntered', () => { changeState(ENTERING); }), }), ], }; const enterRenders = { [modes.out]: ({ children, changeState }) => React.cloneElement(children, { in: true, onEntered: callHook(children, 'onEntered', () => { changeState(ENTERED, React.cloneElement(children, { in: true })); }), }), [modes.in]: ({ current, children, changeState }) => [ React.cloneElement(current, { in: false, onExited: callHook(current, 'onExited', () => { changeState(ENTERED, React.cloneElement(children, { in: true })); }), }), React.cloneElement(children, { in: true, }), ], }; /** * 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. * Based on the selected mode and the child's key which is the `Transition` or `CSSTransition` component, the `SwitchTransition` makes a consistent transition between them. * * If the `out-in` mode is selected, the `SwitchTransition` waits until the old child leaves and then inserts a new child. * If the `in-out` mode is selected, the `SwitchTransition` inserts a new child first, waits for the new child to enter and then removes the old child. * * **Note**: If you want the animation to happen simultaneously * (that is, to have the old child removed and a new child inserted **at the same time**), * you should use * [`TransitionGroup`](https://reactcommunity.org/react-transition-group/transition-group) * instead. * * ```jsx * function App() { * const [state, setState] = useState(false); * return ( * * node.addEventListener("transitionend", done, false)} * classNames='fade' * > * * * * ); * } * ``` * * ```css * .fade-enter{ * opacity: 0; * } * .fade-exit{ * opacity: 1; * } * .fade-enter-active{ * opacity: 1; * } * .fade-exit-active{ * opacity: 0; * } * .fade-enter-active, * .fade-exit-active{ * transition: opacity 500ms; * } * ``` */ class SwitchTransition extends React.Component { state = { status: ENTERED, current: null, }; appeared = false; componentDidMount() { this.appeared = true; } static getDerivedStateFromProps(props, state) { if (props.children == null) { return { current: null, }; } if (state.status === ENTERING && props.mode === modes.in) { return { status: ENTERING, }; } if (state.current && areChildrenDifferent(state.current, props.children)) { return { status: EXITING, }; } return { current: React.cloneElement(props.children, { in: true, }), }; } changeState = (status, current = this.state.current) => { this.setState({ status, current, }); }; render() { const { props: { children, mode }, state: { status, current }, } = this; const data = { children, current, changeState: this.changeState, status }; let component; switch (status) { case ENTERING: component = enterRenders[mode](data); break; case EXITING: component = leaveRenders[mode](data); break; case ENTERED: component = current; } return ( {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; react-transition-group-4.4.5/src/Transition.js000066400000000000000000000423601427171024500214330ustar00rootroot00000000000000import 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'; import { forceReflow } from './utils/reflow'; 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) { if (this.props.unmountOnExit || this.props.mountOnEnter) { const node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM.findDOMNode(this); // https://github.com/reactjs/react-transition-group/pull/749 // With unmountOnExit or mountOnEnter, the enter animation should happen at the transition between `exited` and `entering`. // To make the animation happen, we have to separate each rendering and avoid being processed as batched. if (node) forceReflow(node); } 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; react-transition-group-4.4.5/src/TransitionGroup.js000066400000000000000000000124641427171024500224520ustar00rootroot00000000000000import PropTypes from 'prop-types'; import React from 'react'; import TransitionGroupContext from './TransitionGroupContext'; import { getChildMapping, getInitialChildMapping, getNextChildMapping, } from './utils/ChildMapping'; const values = Object.values || ((obj) => Object.keys(obj).map((k) => obj[k])); const defaultProps = { component: 'div', childFactory: (child) => child, }; /** * The `` component manages a set of transition components * (`` and ``) in a list. Like with the transition * components, `` is a state machine for managing the mounting * and unmounting of components over time. * * Consider the example below. As items are removed or added to the TodoList the * `in` prop is toggled automatically by the ``. * * Note that `` does not define any animation behavior! * Exactly _how_ a list item animates is up to the individual transition * component. This means you can mix and match animations across different list * items. */ class TransitionGroup extends React.Component { constructor(props, context) { super(props, context); const handleExited = this.handleExited.bind(this); // Initial children should all be entering, dependent on appear this.state = { contextValue: { isMounting: true }, handleExited, firstRender: true, }; } componentDidMount() { this.mounted = true; this.setState({ contextValue: { isMounting: false }, }); } componentWillUnmount() { this.mounted = false; } static getDerivedStateFromProps( nextProps, { children: prevChildMapping, handleExited, firstRender } ) { return { children: firstRender ? getInitialChildMapping(nextProps, handleExited) : getNextChildMapping(nextProps, prevChildMapping, handleExited), firstRender: false, }; } // node is `undefined` when user provided `nodeRef` prop handleExited(child, node) { let currentChildMapping = getChildMapping(this.props.children); if (child.key in currentChildMapping) return; if (child.props.onExited) { child.props.onExited(node); } if (this.mounted) { this.setState((state) => { let children = { ...state.children }; delete children[child.key]; return { children }; }); } } render() { const { component: Component, childFactory, ...props } = this.props; const { contextValue } = this.state; const children = values(this.state.children).map(childFactory); delete props.appear; delete props.enter; delete props.exit; if (Component === null) { return ( {children} ); } return ( {children} ); } } TransitionGroup.propTypes = { /** * `` renders a `
` by default. You can change this * behavior by providing a `component` prop. * If you use React v16+ and would like to avoid a wrapping `
` element * you can pass in `component={null}`. This is useful if the wrapping div * borks your css styles. */ component: PropTypes.any, /** * A set of `` components, that are toggled `in` and out as they * leave. the `` will inject specific transition props, so * remember to spread them through if you are wrapping the `` as * with our `` example. * * While this component is meant for multiple `Transition` or `CSSTransition` * children, sometimes you may want to have a single transition child with * content that you want to be transitioned out and in when you change it * (e.g. routes, images etc.) In that case you can change the `key` prop of * the transition child as you change its content, this will cause * `TransitionGroup` to transition the child out and back in. */ children: PropTypes.node, /** * A convenience prop that enables or disables appear animations * for all children. Note that specifying this will override any defaults set * on individual children Transitions. */ appear: PropTypes.bool, /** * A convenience prop that enables or disables enter animations * for all children. Note that specifying this will override any defaults set * on individual children Transitions. */ enter: PropTypes.bool, /** * A convenience prop that enables or disables exit animations * for all children. Note that specifying this will override any defaults set * on individual children Transitions. */ exit: PropTypes.bool, /** * You may need to apply reactive updates to a child as it is exiting. * This is generally done by using `cloneElement` however in the case of an exiting * child the element has already been removed and not accessible to the consumer. * * If you do need to update a child as it leaves you can provide a `childFactory` * to wrap every child, even the ones that are leaving. * * @type Function(child: ReactElement) -> ReactElement */ childFactory: PropTypes.func, }; TransitionGroup.defaultProps = defaultProps; export default TransitionGroup; react-transition-group-4.4.5/src/TransitionGroupContext.js000066400000000000000000000001061427171024500240050ustar00rootroot00000000000000import React from 'react'; export default React.createContext(null); react-transition-group-4.4.5/src/config.js000066400000000000000000000000471427171024500205420ustar00rootroot00000000000000export default { disabled: false, }; react-transition-group-4.4.5/src/index.js000066400000000000000000000005461427171024500204100ustar00rootroot00000000000000export { default as CSSTransition } from './CSSTransition'; export { default as ReplaceTransition } from './ReplaceTransition'; export { default as SwitchTransition } from './SwitchTransition'; export { default as TransitionGroup } from './TransitionGroup'; export { default as Transition } from './Transition'; export { default as config } from './config'; react-transition-group-4.4.5/src/utils/000077500000000000000000000000001427171024500200765ustar00rootroot00000000000000react-transition-group-4.4.5/src/utils/ChildMapping.js000066400000000000000000000110721427171024500227740ustar00rootroot00000000000000import { Children, cloneElement, isValidElement } from 'react'; /** * Given `this.props.children`, return an object mapping key to child. * * @param {*} children `this.props.children` * @return {object} Mapping of key to child */ export function getChildMapping(children, mapFn) { let mapper = (child) => mapFn && isValidElement(child) ? mapFn(child) : child; let result = Object.create(null); if (children) Children.map(children, (c) => c).forEach((child) => { // run the map function here instead so that the key is the computed one result[child.key] = mapper(child); }); return result; } /** * When you're adding or removing children some may be added or removed in the * same render pass. We want to show *both* since we want to simultaneously * animate elements in and out. This function takes a previous set of keys * and a new set of keys and merges them with its best guess of the correct * ordering. In the future we may expose some of the utilities in * ReactMultiChild to make this easy, but for now React itself does not * directly have this concept of the union of prevChildren and nextChildren * so we implement it here. * * @param {object} prev prev children as returned from * `ReactTransitionChildMapping.getChildMapping()`. * @param {object} next next children as returned from * `ReactTransitionChildMapping.getChildMapping()`. * @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) { prev = prev || {}; next = next || {}; function getValueForKey(key) { return key in next ? next[key] : prev[key]; } // For each key of `next`, the list of keys to insert before that key in // the combined list let nextKeysPending = Object.create(null); let pendingKeys = []; for (let prevKey in prev) { if (prevKey in next) { if (pendingKeys.length) { nextKeysPending[prevKey] = pendingKeys; pendingKeys = []; } } else { pendingKeys.push(prevKey); } } let i; let childMapping = {}; for (let nextKey in next) { if (nextKeysPending[nextKey]) { for (i = 0; i < nextKeysPending[nextKey].length; i++) { let pendingNextKey = nextKeysPending[nextKey][i]; childMapping[nextKeysPending[nextKey][i]] = getValueForKey(pendingNextKey); } } childMapping[nextKey] = getValueForKey(nextKey); } // Finally, add the keys which didn't appear before any key in `next` for (i = 0; i < pendingKeys.length; i++) { childMapping[pendingKeys[i]] = getValueForKey(pendingKeys[i]); } return childMapping; } function getProp(child, prop, props) { return props[prop] != null ? props[prop] : child.props[prop]; } export function getInitialChildMapping(props, onExited) { return getChildMapping(props.children, (child) => { return cloneElement(child, { onExited: onExited.bind(null, child), in: true, appear: getProp(child, 'appear', props), enter: getProp(child, 'enter', props), exit: getProp(child, 'exit', props), }); }); } export function getNextChildMapping(nextProps, prevChildMapping, onExited) { let nextChildMapping = getChildMapping(nextProps.children); let children = mergeChildMappings(prevChildMapping, nextChildMapping); Object.keys(children).forEach((key) => { let child = children[key]; if (!isValidElement(child)) return; const hasPrev = key in prevChildMapping; const hasNext = key in nextChildMapping; const prevChild = prevChildMapping[key]; const isLeaving = isValidElement(prevChild) && !prevChild.props.in; // item is new (entering) if (hasNext && (!hasPrev || isLeaving)) { // console.log('entering', key) children[key] = cloneElement(child, { onExited: onExited.bind(null, child), in: true, exit: getProp(child, 'exit', nextProps), enter: getProp(child, 'enter', nextProps), }); } else if (!hasNext && hasPrev && !isLeaving) { // item is old (exiting) // console.log('leaving', key) children[key] = cloneElement(child, { in: false }); } else if (hasNext && hasPrev && isValidElement(prevChild)) { // item hasn't changed transition states // copy over the last transition props; // console.log('unchanged', key) children[key] = cloneElement(child, { onExited: onExited.bind(null, child), in: prevChild.props.in, exit: getProp(child, 'exit', nextProps), enter: getProp(child, 'enter', nextProps), }); } }); return children; } react-transition-group-4.4.5/src/utils/PropTypes.js000066400000000000000000000015721427171024500224060ustar00rootroot00000000000000import PropTypes from 'prop-types'; export const timeoutsShape = process.env.NODE_ENV !== 'production' ? PropTypes.oneOfType([ PropTypes.number, PropTypes.shape({ enter: PropTypes.number, exit: PropTypes.number, appear: PropTypes.number, }).isRequired, ]) : null; export const classNamesShape = process.env.NODE_ENV !== 'production' ? PropTypes.oneOfType([ PropTypes.string, PropTypes.shape({ enter: PropTypes.string, exit: PropTypes.string, active: PropTypes.string, }), PropTypes.shape({ enter: PropTypes.string, enterDone: PropTypes.string, enterActive: PropTypes.string, exit: PropTypes.string, exitDone: PropTypes.string, exitActive: PropTypes.string, }), ]) : null; react-transition-group-4.4.5/src/utils/SimpleSet.js000066400000000000000000000005501427171024500223410ustar00rootroot00000000000000export 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; } } react-transition-group-4.4.5/src/utils/reflow.js000066400000000000000000000000651427171024500217330ustar00rootroot00000000000000export const forceReflow = (node) => node.scrollTop; react-transition-group-4.4.5/stories/000077500000000000000000000000001427171024500176375ustar00rootroot00000000000000react-transition-group-4.4.5/stories/.eslintrc.yml000066400000000000000000000002471427171024500222660ustar00rootroot00000000000000rules: react/prop-types: off no-unused-vars: - error - varsIgnorePattern: ^_$ import/no-extraneous-dependencies: - error - devDependencies: true react-transition-group-4.4.5/stories/CSSTransition.js000066400000000000000000000023101427171024500226740ustar00rootroot00000000000000import React, { useState } from 'react'; import { storiesOf } from '@storybook/react'; import StoryFixture from './StoryFixture'; import Fade from './transitions/CSSFade'; function ToggleFixture({ defaultIn, description, children }) { const [show, setShow] = useState(defaultIn || false); return (
{React.cloneElement(children, { in: show })}
); } storiesOf('CSSTransition', module) .add('Fade', () => ( asaghasg asgasg )) .add('Fade with appear', () => ( asaghasg asgasg )) .add('Fade with mountOnEnter', () => { return ( Fade with mountOnEnter ); }) .add('Fade with unmountOnExit', () => { return ( Fade with unmountOnExit ); }); react-transition-group-4.4.5/stories/CSSTransitionGroupFixture.js000066400000000000000000000032351427171024500252670ustar00rootroot00000000000000import React from 'react'; import TransitionGroup from '../src/TransitionGroup'; import StoryFixture from './StoryFixture'; class CSSTransitionGroupFixture extends React.Component { static defaultProps = { items: [], }; count = this.props.items.length; state = { items: this.props.items, }; handleAddItem = () => { this.setState(({ items }) => ({ items: [...items, `Item number: ${++this.count}`], })); }; handleRemoveItems = () => { this.setState(({ items }) => { items = items.slice(); items.splice(1, 3); return { items }; }); }; handleRemoveItem = (item) => { this.setState(({ items }) => ({ items: items.filter((i) => i !== item), })); }; render() { const { items: _, description, children, ...rest } = this.props; // e.g. `Fade`, see where `CSSTransitionGroupFixture` is used const { type: TransitionType, props: transitionTypeProps } = React.Children.only(children); return (
{' '}
{this.state.items.map((item) => ( {item} ))}
); } } export default CSSTransitionGroupFixture; react-transition-group-4.4.5/stories/NestedTransition.js000066400000000000000000000022761427171024500235010ustar00rootroot00000000000000import React, { useState } from 'react'; import StoryFixture from './StoryFixture'; import Fade from './transitions/CSSFadeForTransitionGroup'; import Scale from './transitions/Scale'; function FadeAndScale(props) { return (
I will fade
{/* We also want to scale in at the same time so we pass the `in` state here as well, so it enters at the same time as the Fade. Note also the `appear` since the Fade will happen when the item mounts, the Scale transition will mount at the same time as the div we want to scale, so we need to tell it to animate as it _appears_. */} I should scale
); } function Example() { const [showNested, setShowNested] = useState(false); return (

Nested Animations

); } export default Example; react-transition-group-4.4.5/stories/ReplaceTransition.js000066400000000000000000000041211427171024500236210ustar00rootroot00000000000000import { css } from 'astroturf'; import React, { useState } from 'react'; import { storiesOf } from '@storybook/react'; import ReplaceTransition from '../src/ReplaceTransition'; import CSSTransition from '../src/CSSTransition'; const FADE_TIMEOUT = 1000; const styles = css` .enter { opacity: 0.01; } .enter.enter-active { position: absolute; left: 0; right: 0; opacity: 1; transition: opacity ${FADE_TIMEOUT * 2}ms ease-in; transition-delay: ${FADE_TIMEOUT}ms; } .exit { opacity: 1; } .exit.exit-active { opacity: 0.01; transition: opacity ${FADE_TIMEOUT}ms ease-in; } .box { padding: 20px; background-color: #ccc; } .container { position: relative; } `; const defaultProps = { in: false, timeout: FADE_TIMEOUT * 2, }; function Fade(props) { return ( ); } Fade.defaultProps = defaultProps; function Example({ children }) { const [show, setShow] = useState(false); return (
{React.cloneElement(children, { in: show })}
); } storiesOf('Replace Transition', module).add('Animates on all', () => { const firstNodeRef = React.createRef(); const secondNodeRef = React.createRef(); return ( console.log('onEnter')} onEntering={() => console.log('onEntering')} onEntered={() => console.log('onEntered')} onExit={() => console.log('onExit')} onExiting={() => console.log('onExiting')} onExited={() => console.log('onExited')} >
in True
in False
); }); react-transition-group-4.4.5/stories/StoryFixture.js000066400000000000000000000005031427171024500226620ustar00rootroot00000000000000import PropTypes from 'prop-types'; import React from 'react'; const propTypes = { description: PropTypes.string, }; function StoryFixture({ description, children }) { return (

{description}

{children}
); } StoryFixture.propTypes = propTypes; export default StoryFixture; react-transition-group-4.4.5/stories/Transition.js000066400000000000000000000034251427171024500223330ustar00rootroot00000000000000import React, { useState } from 'react'; import { storiesOf } from '@storybook/react'; import StoryFixture from './StoryFixture'; import { Fade, Collapse, FadeForwardRef, FadeInnerRef, } from './transitions/Bootstrap'; function ToggleFixture({ defaultIn, description, children }) { const [show, setShow] = useState(defaultIn); return (
{React.cloneElement(children, { in: show })}
); } storiesOf('Transition', module) .add('Bootstrap Fade', () => ( asaghasg asgasg )) .add('Bootstrap Collapse', () => ( asaghasg asgasg
foo
bar
)) .add('Fade using React.forwardRef', () => { const nodeRef = React.createRef(); return ( Fade using React.forwardRef ); }) .add('Fade using innerRef', () => { const nodeRef = React.createRef(); return ( Fade using innerRef ); }) .add('Fade with mountOnEnter', () => { return ( Fade with mountOnEnter ); }) .add('Fade with unmountOnExit', () => { return ( Fade with unmountOnExit ); }); react-transition-group-4.4.5/stories/TransitionGroup.js000066400000000000000000000066551427171024500233600ustar00rootroot00000000000000import React, { useEffect, useState } from 'react'; import { storiesOf } from '@storybook/react'; import TransitionGroup from '../src/TransitionGroup'; import CSSTransitionGroupFixture from './CSSTransitionGroupFixture'; import NestedTransition from './NestedTransition'; import StoryFixture from './StoryFixture'; import Fade, { FADE_TIMEOUT } from './transitions/CSSFadeForTransitionGroup'; storiesOf('Css Transition Group', module) .add('Animates on all', () => ( )) .add('Animates on enter', () => ( )) .add('Animates on exit', () => ( )) .add('Animates on appear', () => ( )) .add('Dynamic props', () => ( )) .add('Re-entering while leaving', () => ( )) .add('Nested Transitions', () => ); class DynamicTransition extends React.Component { state = { count: 0 }; handleClick = () => { this.setState({ hide: !this.state.hide }); }; componentDidMount() { this.interval = setInterval(() => { this.setState({ count: this.state.count + 1 }); }, 700); } componentWillUnmount() { clearInterval(this.interval); } render() { const { hide, count } = this.state; return (
{!hide && Changing! {count}}
); } } function ReEnterTransition() { const [hide, setHide] = useState(false); useEffect(() => { if (hide) { setTimeout(() => { console.log('re-entering!'); setHide(false); }, 0.5 * FADE_TIMEOUT); } }, [hide]); return (
{!hide && I'm entering!}
); } react-transition-group-4.4.5/stories/index.js000066400000000000000000000001531427171024500213030ustar00rootroot00000000000000import './Transition'; import './CSSTransition'; import './TransitionGroup'; import './ReplaceTransition'; react-transition-group-4.4.5/stories/transitions/000077500000000000000000000000001427171024500222145ustar00rootroot00000000000000react-transition-group-4.4.5/stories/transitions/Bootstrap.js000066400000000000000000000074011427171024500245310ustar00rootroot00000000000000import { css } from 'astroturf'; import React, { useEffect, useRef } from 'react'; import style from 'dom-helpers/css'; import Transition, { EXITED, ENTERED, ENTERING, EXITING, } from '../../src/Transition'; const styles = css` .fade { opacity: 0; transition: opacity 0.5s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } .collapsing { position: relative; height: 0; overflow: hidden; transition: 0.35s ease; transition-property: height, visibility; } `; const fadeStyles = { [ENTERING]: styles.in, [ENTERED]: styles.in, }; export function Fade(props) { const nodeRef = useRef(); return ( {(status) => (
{props.children}
)}
); } function getHeight(elem) { let value = elem.offsetHeight; let margins = ['marginTop', 'marginBottom']; return ( value + parseInt(style(elem, margins[0]), 10) + parseInt(style(elem, margins[1]), 10) ); } const collapseStyles = { [EXITED]: styles.collapse, [EXITING]: styles.collapsing, [ENTERING]: styles.collapsing, [ENTERED]: `${styles.collapse} ${styles.in}`, }; export class Collapse extends React.Component { nodeRef = React.createRef(); /* -- Expanding -- */ handleEnter = () => { this.nodeRef.current.style.height = '0'; }; handleEntering = () => { this.nodeRef.current.style.height = `${this.nodeRef.current.scrollHeight}px`; }; handleEntered = () => { this.nodeRef.current.style.height = null; }; /* -- Collapsing -- */ handleExit = () => { this.nodeRef.current.style.height = getHeight(this.nodeRef.current) + 'px'; this.nodeRef.current.offsetHeight; // eslint-disable-line no-unused-expressions }; handleExiting = () => { this.nodeRef.current.style.height = '0'; }; render() { const { children, ...rest } = this.props; return ( {(state, props) => (
{children}
)}
); } } export function FadeInnerRef(props) { const nodeRef = useMergedRef(props.innerRef); return ( {(status) => (
{props.children}
)}
); } export const FadeForwardRef = React.forwardRef((props, ref) => { return ; }); /** * Compose multiple refs, there may be different implementations * This one is derived from * e.g. https://github.com/react-restart/hooks/blob/ed37bf3dfc8fc1d9234a6d8fe0af94d69fad3b74/src/useMergedRefs.ts * Also here are good discussion about this * https://github.com/facebook/react/issues/13029 * @param ref * @returns {React.MutableRefObject} */ function useMergedRef(ref) { const nodeRef = React.useRef(); useEffect(function () { if (ref) { if (typeof ref === 'function') { ref(nodeRef.current); } else { ref.current = nodeRef.current; } } }); return nodeRef; } react-transition-group-4.4.5/stories/transitions/CSSFade.js000066400000000000000000000016361427171024500237700ustar00rootroot00000000000000import { css } from 'astroturf'; import React, { useRef } from 'react'; import CSSTransition from '../../src/CSSTransition'; export const FADE_TIMEOUT = 1000; const styles = css` .default { opacity: 0; } .enter-done { opacity: 1; } .enter, .appear { opacity: 0.01; } .enter.enter-active, .appear.appear-active { opacity: 1; transition: opacity ${FADE_TIMEOUT}ms ease-in; } .exit { opacity: 1; } .exit.exit-active { opacity: 0.01; transition: opacity ${0.8 * FADE_TIMEOUT}ms ease-in; } `; const defaultProps = { in: false, timeout: FADE_TIMEOUT, }; function Fade(props) { const nodeRef = useRef(); return (
{props.children}
); } Fade.defaultProps = defaultProps; export default Fade; react-transition-group-4.4.5/stories/transitions/CSSFadeForTransitionGroup.js000066400000000000000000000014561427171024500275270ustar00rootroot00000000000000import { css } from 'astroturf'; import React, { useRef } from 'react'; import CSSTransition from '../../src/CSSTransition'; export const FADE_TIMEOUT = 1000; const styles = css` .enter, .appear { opacity: 0.01; } .enter.enter-active, .appear.appear-active { opacity: 1; transition: opacity ${FADE_TIMEOUT}ms ease-in; } .exit { opacity: 1; } .exit.exit-active { opacity: 0.01; transition: opacity ${0.8 * FADE_TIMEOUT}ms ease-in; } `; const defaultProps = { in: false, timeout: FADE_TIMEOUT, }; function Fade(props) { const nodeRef = useRef(); return (
{props.children}
); } Fade.defaultProps = defaultProps; export default Fade; react-transition-group-4.4.5/stories/transitions/Scale.js000066400000000000000000000015011427171024500235760ustar00rootroot00000000000000import { css } from 'astroturf'; import React, { useRef } from 'react'; import CSSTransition from '../../src/CSSTransition'; export const SCALE_TIMEOUT = 1000; const styles = css` .enter, .appear { transform: scale(0); } .enter.enter-active, .appear.appear-active { transform: scale(1); transition: transform ${SCALE_TIMEOUT}ms; } .exit { transform: scale(1); } .exit.exit-active { transform: scale(0); transition: transform ${SCALE_TIMEOUT}ms; } `; const defaultProps = { in: false, timeout: SCALE_TIMEOUT, }; function Scale(props) { const nodeRef = useRef(); return (
{props.children}
); } Scale.defaultProps = defaultProps; export default Scale; react-transition-group-4.4.5/test/000077500000000000000000000000001427171024500171265ustar00rootroot00000000000000react-transition-group-4.4.5/test/.eslintrc.yml000066400000000000000000000006031427171024500215510ustar00rootroot00000000000000env: jest: true es6: true rules: no-require: off global-require: off no-console: off react/no-multi-comp: off react/no-render-return-value: off react/no-find-dom-node: off react/prop-types: off react/prefer-stateless-function: off react/jsx-boolean-value: off react/no-string-refs: off import/no-extraneous-dependencies: - error - devDependencies: true react-transition-group-4.4.5/test/CSSTransition-test.js000066400000000000000000000301221427171024500231420ustar00rootroot00000000000000import React from 'react'; import { render, waitFor } from './utils'; import CSSTransition from '../src/CSSTransition'; import TransitionGroup from '../src/TransitionGroup'; describe('CSSTransition', () => { it('should flush new props to the DOM before initiating a transition', (done) => { const nodeRef = React.createRef(); const { setProps } = render( { expect(nodeRef.current.classList.contains('test-class')).toEqual( true ); expect(nodeRef.current.classList.contains('test-entering')).toEqual( false ); done(); }} >
); expect(nodeRef.current.classList.contains('test-class')).toEqual(false); setProps({ in: true, className: 'test-class', }); }); describe('entering', () => { it('should apply classes at each transition state', async () => { let count = 0; let done = false; const nodeRef = React.createRef(); const { setProps } = render(
); setProps({ in: true, onEnter() { count++; expect(nodeRef.current.className).toEqual('test-enter'); }, onEntering() { count++; expect(nodeRef.current.className).toEqual( 'test-enter test-enter-active' ); }, onEntered() { expect(nodeRef.current.className).toEqual('test-enter-done'); expect(count).toEqual(2); done = true; }, }); await waitFor(() => { expect(done).toBe(true); }); }); it('should apply custom classNames names', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render(
); setProps({ in: true, onEnter() { count++; expect(nodeRef.current.className).toEqual('custom'); }, onEntering() { count++; expect(nodeRef.current.className).toEqual( 'custom custom-super-active' ); }, onEntered() { expect(nodeRef.current.className).toEqual('custom-super-done'); }, }); await waitFor(() => { expect(count).toEqual(2); }); }); }); describe('appearing', () => { it('should apply appear classes at each transition state', async () => { let count = 0; const nodeRef = React.createRef(); render( { count++; expect(isAppearing).toEqual(true); expect(nodeRef.current.className).toEqual('appear-test-appear'); }} onEntering={(isAppearing) => { count++; expect(isAppearing).toEqual(true); expect(nodeRef.current.className).toEqual( 'appear-test-appear appear-test-appear-active' ); }} onEntered={(isAppearing) => { expect(isAppearing).toEqual(true); expect(nodeRef.current.className).toEqual( 'appear-test-appear-done appear-test-enter-done' ); }} >
); await waitFor(() => { expect(count).toEqual(2); }); }); it('should lose the "*-appear-done" class after leaving and entering again', async () => { const nodeRef = React.createRef(); let entered = false; let exited = false; const { setProps } = render( { entered = true; }} >
); await waitFor(() => { expect(entered).toEqual(true); }); setProps({ in: false, onEntered: () => {}, onExited: () => { exited = true; }, }); await waitFor(() => { expect(exited).toEqual(true); }); expect(nodeRef.current.className).toBe('appear-test-exit-done'); entered = false; setProps({ in: true, onEntered: () => { entered = true; }, }); await waitFor(() => { expect(entered).toEqual(true); }); expect(nodeRef.current.className).toBe('appear-test-enter-done'); }); it('should not add undefined when appearDone is not defined', async () => { const nodeRef = React.createRef(); let done = false; render( { expect(isAppearing).toEqual(true); expect(nodeRef.current.className).toEqual('appear-test'); }} onEntered={(isAppearing) => { expect(isAppearing).toEqual(true); expect(nodeRef.current.className).toEqual(''); done = true; }} >
); await waitFor(() => { expect(done).toEqual(true); }); }); it('should not be appearing in normal enter mode', async () => { let count = 0; const nodeRef = React.createRef(); render(
).setProps({ in: true, onEnter(isAppearing) { count++; expect(isAppearing).toEqual(false); expect(nodeRef.current.className).toEqual('not-appear-test-enter'); }, onEntering(isAppearing) { count++; expect(isAppearing).toEqual(false); expect(nodeRef.current.className).toEqual( 'not-appear-test-enter not-appear-test-enter-active' ); }, onEntered(isAppearing) { expect(isAppearing).toEqual(false); expect(nodeRef.current.className).toEqual( 'not-appear-test-enter-done' ); }, }); await waitFor(() => { expect(count).toEqual(2); }); }); it('should not enter the transition states when appear=false', () => { const nodeRef = React.createRef(); render( { throw Error('Enter called!'); }} onEntering={() => { throw Error('Entring called!'); }} onEntered={() => { throw Error('Entred called!'); }} >
); }); }); describe('exiting', () => { it('should apply classes at each transition state', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render(
); setProps({ in: false, onExit() { count++; expect(nodeRef.current.className).toEqual('test-exit'); }, onExiting() { count++; expect(nodeRef.current.className).toEqual( 'test-exit test-exit-active' ); }, onExited() { expect(nodeRef.current.className).toEqual('test-exit-done'); }, }); await waitFor(() => { expect(count).toEqual(2); }); }); it('should apply custom classNames names', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render(
); setProps({ in: false, onExit() { count++; expect(nodeRef.current.className).toEqual('custom'); }, onExiting() { count++; expect(nodeRef.current.className).toEqual( 'custom custom-super-active' ); }, onExited() { expect(nodeRef.current.className).toEqual('custom-super-done'); }, }); await waitFor(() => { expect(count).toEqual(2); }); }); it('should support empty prefix', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render(
); setProps({ in: false, onExit() { count++; expect(nodeRef.current.className).toEqual('exit'); }, onExiting() { count++; expect(nodeRef.current.className).toEqual('exit exit-active'); }, onExited() { expect(nodeRef.current.className).toEqual('exit-done'); }, }); await waitFor(() => { expect(count).toEqual(2); }); }); }); describe('reentering', () => { it('should remove dynamically applied classes', async () => { let count = 0; class Test extends React.Component { render() { const { direction, text, nodeRef, ...props } = this.props; return ( React.cloneElement(child, { classNames: direction, }) } > {text} ); } } const nodeRef = { foo: React.createRef(), bar: React.createRef(), }; const { setProps } = render( ); setProps({ direction: 'up', text: 'bar', nodeRef: nodeRef.bar, onEnter() { count++; expect(nodeRef.bar.current.className).toEqual('up-enter'); }, onEntering() { count++; expect(nodeRef.bar.current.className).toEqual( 'up-enter up-enter-active' ); }, }); await waitFor(() => { expect(count).toEqual(2); }); setProps({ direction: 'down', text: 'foo', nodeRef: nodeRef.foo, onEntering() { count++; expect(nodeRef.foo.current.className).toEqual( 'down-enter down-enter-active' ); }, onEntered() { count++; expect(nodeRef.foo.current.className).toEqual('down-enter-done'); }, }); await waitFor(() => { expect(count).toEqual(4); }); }); }); }); react-transition-group-4.4.5/test/CSSTransitionGroup-test.js000066400000000000000000000173551427171024500241740ustar00rootroot00000000000000import hasClass from 'dom-helpers/hasClass'; import CSSTransition from '../src/CSSTransition'; let React; let ReactDOM; let TransitionGroup; let act; let render; // Most of the real functionality is covered in other unit tests, this just // makes sure we're wired up correctly. describe('CSSTransitionGroup', () => { let container; let consoleErrorSpy; function YoloTransition({ id, ...props }) { const nodeRef = React.useRef(); return ( ); } beforeEach(() => { jest.resetModuleRegistry(); jest.useFakeTimers(); React = require('react'); ReactDOM = require('react-dom'); const testUtils = require('./utils'); act = testUtils.act; const baseRender = testUtils.render; render = (element, container) => baseRender({element}, { container }); TransitionGroup = require('../src/TransitionGroup'); container = document.createElement('div'); consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { consoleErrorSpy.mockRestore(); jest.useRealTimers(); }); it('should clean-up silently after the timeout elapses', () => { render( , container ); const transitionGroupDiv = container.childNodes[0]; expect(transitionGroupDiv.childNodes.length).toBe(1); render( , container ); expect(transitionGroupDiv.childNodes.length).toBe(2); expect(transitionGroupDiv.childNodes[0].id).toBe('two'); expect(transitionGroupDiv.childNodes[1].id).toBe('one'); act(() => { jest.runAllTimers(); }); // No warnings expect(consoleErrorSpy).not.toHaveBeenCalled(); // The leaving child has been removed expect(transitionGroupDiv.childNodes.length).toBe(1); expect(transitionGroupDiv.childNodes[0].id).toBe('two'); }); it('should keep both sets of DOM nodes around', () => { render( , container ); const transitionGroupDiv = container.childNodes[0]; expect(transitionGroupDiv.childNodes.length).toBe(1); render( , container ); expect(transitionGroupDiv.childNodes.length).toBe(2); expect(transitionGroupDiv.childNodes[0].id).toBe('two'); expect(transitionGroupDiv.childNodes[1].id).toBe('one'); }); it('should switch transitionLeave from false to true', () => { render( , container ); const transitionGroupDiv = container.childNodes[0]; expect(transitionGroupDiv.childNodes.length).toBe(1); render( , container ); act(() => { jest.runAllTimers(); }); expect(transitionGroupDiv.childNodes.length).toBe(1); render( , container ); expect(transitionGroupDiv.childNodes.length).toBe(2); expect(transitionGroupDiv.childNodes[0].id).toBe('three'); expect(transitionGroupDiv.childNodes[1].id).toBe('two'); }); it('should work with a null child', () => { render({[null]}, container); }); it('should work with a child which renders as null', () => { const NullComponent = () => null; // Testing the whole lifecycle of entering and exiting, // because those lifecycle methods used to fail when the DOM node was null. render(, container); render( , container ); render(, container); }); it('should transition from one to null', () => { render( , container ); const transitionGroupDiv = container.childNodes[0]; expect(transitionGroupDiv.childNodes.length).toBe(1); render({null}, container); // (Here, we expect the original child to stick around but test that no // exception is thrown) expect(transitionGroupDiv.childNodes.length).toBe(1); expect(transitionGroupDiv.childNodes[0].id).toBe('one'); }); it('should transition from false to one', () => { render({false}, container); const transitionGroupDiv = container.childNodes[0]; expect(transitionGroupDiv.childNodes.length).toBe(0); render( , container ); expect(transitionGroupDiv.childNodes.length).toBe(1); expect(transitionGroupDiv.childNodes[0].id).toBe('one'); }); it('should clear transition timeouts when unmounted', () => { class Component extends React.Component { render() { return {this.props.children}; } } render(, container); render( , container ); ReactDOM.unmountComponentAtNode(container); // Testing that no exception is thrown here, as the timeout has been cleared. act(() => { jest.runAllTimers(); }); }); it('should handle unmounted elements properly', () => { class Child extends React.Component { render() { if (!this.props.show) return null; return
; } } class Component extends React.Component { state = { showChild: true }; componentDidMount() { this.setState({ showChild: false }); } render() { return ( ); } } render(, container); // Testing that no exception is thrown here, as the timeout has been cleared. act(() => { jest.runAllTimers(); }); }); it('should work with custom component wrapper cloning children', () => { const extraClassNameProp = 'wrapper-item'; class Wrapper extends React.Component { render() { return (
{React.Children.map(this.props.children, (child) => React.cloneElement(child, { className: extraClassNameProp }) )}
); } } class Child extends React.Component { render() { return
; } } class Component extends React.Component { render() { return ( ); } } render(, container); const transitionGroupDiv = container.childNodes[0]; transitionGroupDiv.childNodes.forEach((child) => { expect(hasClass(child, extraClassNameProp)).toBe(true); }); // Testing that no exception is thrown here, as the timeout has been cleared. act(() => { jest.runAllTimers(); }); }); }); react-transition-group-4.4.5/test/ChildMapping-test.js000066400000000000000000000051261427171024500230040ustar00rootroot00000000000000let React; let ChildMapping; describe('ChildMapping', () => { beforeEach(() => { React = require('react'); ChildMapping = require('../src/utils/ChildMapping'); }); it('should support getChildMapping', () => { let oneone =
; let onetwo =
; let one = (
{oneone} {onetwo}
); let two =
foo
; let component = (
{one} {two}
); let mapping = ChildMapping.getChildMapping(component.props.children); expect(mapping['.$one'].props).toEqual(one.props); expect(mapping['.$two'].props).toEqual(two.props); }); it('should support mergeChildMappings for adding keys', () => { let prev = { one: true, two: true, }; let next = { one: true, two: true, three: true, }; expect(ChildMapping.mergeChildMappings(prev, next)).toEqual({ one: true, two: true, three: true, }); }); it('should support mergeChildMappings for removing keys', () => { let prev = { one: true, two: true, three: true, }; let next = { one: true, two: true, }; expect(ChildMapping.mergeChildMappings(prev, next)).toEqual({ one: true, two: true, three: true, }); }); it('should support mergeChildMappings for adding and removing', () => { let prev = { one: true, two: true, three: true, }; let next = { one: true, two: true, four: true, }; expect(ChildMapping.mergeChildMappings(prev, next)).toEqual({ one: true, two: true, three: true, four: true, }); }); it('should reconcile overlapping insertions and deletions', () => { let prev = { one: true, two: true, four: true, five: true, }; let next = { one: true, two: true, three: true, five: true, }; expect(ChildMapping.mergeChildMappings(prev, next)).toEqual({ one: true, two: true, three: true, four: true, five: true, }); }); it('should support mergeChildMappings with undefined input', () => { let prev = { one: true, two: true, }; let next; expect(ChildMapping.mergeChildMappings(prev, next)).toEqual({ one: true, two: true, }); prev = undefined; next = { three: true, four: true, }; expect(ChildMapping.mergeChildMappings(prev, next)).toEqual({ three: true, four: true, }); }); }); react-transition-group-4.4.5/test/SSR-test.js000066400000000000000000000003751427171024500211150ustar00rootroot00000000000000/** * @jest-environment node */ // test that import does not crash import * as ReactTransitionGroup from '../src'; // eslint-disable-line no-unused-vars describe('SSR', () => { it('should import react-transition-group in node env', () => {}); }); react-transition-group-4.4.5/test/SwitchTransition-test.js000066400000000000000000000100401427171024500237500ustar00rootroot00000000000000import React from 'react'; import { act, render } from './utils'; import Transition, { ENTERED } from '../src/Transition'; import SwitchTransition from '../src/SwitchTransition'; describe('SwitchTransition', () => { let log, Parent; beforeEach(() => { log = []; let events = { onEnter: (m) => log.push(m ? 'appear' : 'enter'), onEntering: (m) => log.push(m ? 'appearing' : 'entering'), onEntered: (m) => log.push(m ? 'appeared' : 'entered'), onExit: () => log.push('exit'), onExiting: () => log.push('exiting'), onExited: () => log.push('exited'), }; const nodeRef = React.createRef(); Parent = function Parent({ on, rendered = true }) { return ( {rendered ? ( {on ? 'first' : 'second'} ) : null} ); }; jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); }); it('should have default status ENTERED', () => { const nodeRef = React.createRef(); render( {(status) => { return status: {status}; }} ); expect(nodeRef.current.textContent).toBe(`status: ${ENTERED}`); }); it('should have default mode: out-in', () => { const firstNodeRef = React.createRef(); const secondNodeRef = React.createRef(); const { rerender } = render( {(status) => { return first status: {status}; }} ); rerender( {(status) => { return second status: {status}; }} ); expect(firstNodeRef.current.textContent).toBe('first status: exiting'); expect(secondNodeRef.current).toBe(null); }); it('should work without childs', () => { const nodeRef = React.createRef(); expect(() => { render( ); }).not.toThrow(); }); it('should switch between components on change state', () => { const { container, setProps } = render(); expect(container.textContent).toBe('first'); setProps({ on: false }); expect(log).toEqual(['exit', 'exiting']); act(() => { jest.runAllTimers(); }); act(() => { jest.runAllTimers(); }); expect(log).toEqual([ 'exit', 'exiting', 'exited', 'enter', 'entering', 'entered', ]); expect(container.textContent).toBe('second'); }); it('should switch between null and component', () => { const { container, setProps } = render( ); expect(container.textContent).toBe(''); jest.useFakeTimers(); setProps({ rendered: true }); act(() => { jest.runAllTimers(); }); expect(log).toEqual(['enter', 'entering', 'entered']); expect(container.textContent).toBe('first'); setProps({ on: false, rendered: true }); act(() => { jest.runAllTimers(); }); act(() => { jest.runAllTimers(); }); expect(log).toEqual([ 'enter', 'entering', 'entered', 'exit', 'exiting', 'exited', 'enter', 'entering', 'entered', ]); expect(container.textContent).toBe('second'); }); }); react-transition-group-4.4.5/test/Transition-test.js000066400000000000000000000345211427171024500226000ustar00rootroot00000000000000import React from 'react'; import ReactDOM from 'react-dom'; import { render, waitFor } from './utils'; import Transition, { UNMOUNTED, EXITED, ENTERING, ENTERED, EXITING, } from '../src/Transition'; expect.extend({ toExist(received) { const pass = received != null; return pass ? { message: () => `expected ${received} to be null or undefined`, pass: true, } : { message: () => `expected ${received} not to be null or undefined`, pass: false, }; }, }); describe('Transition', () => { it('should not transition on mount', () => { const nodeRef = React.createRef(); render( { throw new Error('should not Enter'); }} > {(status) =>
status: {status}
}
); expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); }); it('should transition on mount with `appear`', (done) => { const nodeRef = React.createRef(); render( { throw Error('Animated!'); }} >
); render( done()} >
); }); it('should pass filtered props to children', () => { class Child extends React.Component { render() { return (
foo: {this.props.foo}, bar: {this.props.bar}
); } } const nodeRef = React.createRef(); render( {}} onEnter={() => {}} onEntering={() => {}} onEntered={() => {}} onExit={() => {}} onExiting={() => {}} onExited={() => {}} > ); expect(nodeRef.current.textContent).toBe('foo: foo, bar: bar'); }); it('should allow addEndListener instead of timeouts', async () => { let listener = jest.fn((end) => setTimeout(end, 0)); let done = false; const nodeRef = React.createRef(); const { setProps } = render( { expect(listener).toHaveBeenCalledTimes(1); done = true; }} >
); setProps({ in: true }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should fallback to timeouts with addEndListener', async () => { let calledEnd = false; let done = false; let listener = (end) => setTimeout(() => { calledEnd = true; end(); }, 100); const nodeRef = React.createRef(); const { setProps } = render( { expect(calledEnd).toEqual(false); done = true; }} >
); setProps({ in: true }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should mount/unmount immediately if not have enter/exit timeout', async () => { const nodeRef = React.createRef(); let done = false; const { setProps } = render( {(status) =>
status: {status}
}
); expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); let calledAfterTimeout = false; setTimeout(() => { calledAfterTimeout = true; }, 10); setProps({ in: false, onExited() { expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); if (calledAfterTimeout) { throw new Error('wrong timeout'); } done = true; }, }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should use `React.findDOMNode` when `nodeRef` is not provided', () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); const findDOMNodeSpy = jest.spyOn(ReactDOM, 'findDOMNode'); render(
); expect(findDOMNodeSpy).toHaveBeenCalled(); findDOMNodeSpy.mockRestore(); consoleSpy.mockRestore(); }); it('should not use `React.findDOMNode` when `nodeRef` is provided', () => { const findDOMNodeSpy = jest.spyOn(ReactDOM, 'findDOMNode'); const nodeRef = React.createRef(); render(
); expect(findDOMNodeSpy).not.toHaveBeenCalled(); findDOMNodeSpy.mockRestore(); }); describe('appearing timeout', () => { it('should use enter timeout if appear not set', async () => { let calledBeforeEntered = false; let done = false; setTimeout(() => { calledBeforeEntered = true; }, 10); const nodeRef = React.createRef(); const { setProps } = render(
); setProps({ onEntered() { if (calledBeforeEntered) { done = true; } else { throw new Error('wrong timeout'); } }, }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should use appear timeout if appear is set', async () => { let done = false; const nodeRef = React.createRef(); const { setProps } = render(
); let isCausedLate = false; setTimeout(() => { isCausedLate = true; }, 15); setProps({ onEntered() { if (isCausedLate) { throw new Error('wrong timeout'); } else { done = true; } }, }); await waitFor(() => { expect(done).toEqual(true); }); }); }); describe('entering', () => { it('should fire callbacks', async () => { let callOrder = []; let done = false; let onEnter = jest.fn(() => callOrder.push('onEnter')); let onEntering = jest.fn(() => callOrder.push('onEntering')); const nodeRef = React.createRef(); const { setProps } = render( {(status) =>
status: {status}
}
); expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); setProps({ in: true, onEnter, onEntering, onEntered() { expect(onEnter).toHaveBeenCalledTimes(1); expect(onEntering).toHaveBeenCalledTimes(1); expect(callOrder).toEqual(['onEnter', 'onEntering']); done = true; }, }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should move to each transition state', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render( {(status) =>
status: {status}
}
); expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); setProps({ in: true, onEnter() { count++; expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); }, onEntering() { count++; expect(nodeRef.current.textContent).toEqual(`status: ${ENTERING}`); }, onEntered() { expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); }, }); await waitFor(() => { expect(count).toEqual(2); }); }); }); describe('exiting', () => { it('should fire callbacks', async () => { let callOrder = []; let done = false; let onExit = jest.fn(() => callOrder.push('onExit')); let onExiting = jest.fn(() => callOrder.push('onExiting')); const nodeRef = React.createRef(); const { setProps } = render( {(status) =>
status: {status}
}
); expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); setProps({ in: false, onExit, onExiting, onExited() { expect(onExit).toHaveBeenCalledTimes(1); expect(onExiting).toHaveBeenCalledTimes(1); expect(callOrder).toEqual(['onExit', 'onExiting']); done = true; }, }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should move to each transition state', async () => { let count = 0; let done = false; const nodeRef = React.createRef(); const { setProps } = render( {(status) =>
status: {status}
}
); expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); setProps({ in: false, onExit() { count++; expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); }, onExiting() { count++; expect(nodeRef.current.textContent).toEqual(`status: ${EXITING}`); }, onExited() { expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); expect(count).toEqual(2); done = true; }, }); await waitFor(() => { expect(done).toEqual(true); }); }); }); describe('mountOnEnter', () => { class MountTransition extends React.Component { nodeRef = React.createRef(); render() { const { ...props } = this.props; delete props.initialIn; return ( (this.transition = this.transition || transition) } nodeRef={this.nodeRef} mountOnEnter in={this.props.in} timeout={10} {...props} > {(status) =>
status: {status}
}
); } getStatus = () => { return this.transition.state.status; }; } it('should mount when entering', (done) => { const { container, setProps } = render( { expect(container.textContent).toEqual(`status: ${EXITED}`); done(); }} /> ); expect(container.textContent).toEqual(''); setProps({ in: true }); }); it('should stay mounted after exiting', async () => { let entered = false; let exited = false; const { container, setProps } = render( { entered = true; }} onExited={() => { exited = true; }} /> ); expect(container.textContent).toEqual(''); setProps({ in: true }); await waitFor(() => { expect(entered).toEqual(true); }); expect(container.textContent).toEqual(`status: ${ENTERED}`); setProps({ in: false }); await waitFor(() => { expect(exited).toEqual(true); }); expect(container.textContent).toEqual(`status: ${EXITED}`); }); }); describe('unmountOnExit', () => { class UnmountTransition extends React.Component { nodeRef = React.createRef(); render() { const { ...props } = this.props; delete props.initialIn; return ( (this.transition = this.transition || transition) } nodeRef={this.nodeRef} unmountOnExit in={this.props.in} timeout={10} {...props} >
); } getStatus = () => { return this.transition.state.status; }; } it('should mount when entering', async () => { let done = false; const instanceRef = React.createRef(); const { setProps } = render( { expect(instanceRef.current.getStatus()).toEqual(EXITED); expect(instanceRef.current.nodeRef.current).toExist(); done = true; }} /> ); expect(instanceRef.current.getStatus()).toEqual(UNMOUNTED); expect(instanceRef.current.nodeRef.current).toBeNull(); setProps({ in: true }); await waitFor(() => { expect(done).toEqual(true); }); }); it('should unmount after exiting', async () => { let exited = false; const instanceRef = React.createRef(); const { setProps } = render( { setTimeout(() => { exited = true; }); }} /> ); expect(instanceRef.current.getStatus()).toEqual(ENTERED); expect(instanceRef.current.nodeRef.current).toExist(); setProps({ in: false }); await waitFor(() => { expect(exited).toEqual(true); }); expect(instanceRef.current.getStatus()).toEqual(UNMOUNTED); expect(instanceRef.current.nodeRef.current).not.toExist(); }); }); }); react-transition-group-4.4.5/test/TransitionGroup-test.js000066400000000000000000000063001427171024500236070ustar00rootroot00000000000000let React; let TransitionGroup; let Transition; // Most of the real functionality is covered in other unit tests, this just // makes sure we're wired up correctly. describe('TransitionGroup', () => { let act, container, log, Child, renderStrict, render; beforeEach(() => { React = require('react'); Transition = require('../src/Transition').default; TransitionGroup = require('../src/TransitionGroup'); const testUtils = require('./utils'); act = testUtils.act; render = testUtils.render; renderStrict = (element, container) => render({element}, { container }); container = document.createElement('div'); log = []; let events = { onEnter: (m) => log.push(m ? 'appear' : 'enter'), onEntering: (m) => log.push(m ? 'appearing' : 'entering'), onEntered: (m) => log.push(m ? 'appeared' : 'entered'), onExit: () => log.push('exit'), onExiting: () => log.push('exiting'), onExited: () => log.push('exited'), }; const nodeRef = React.createRef(); Child = function Child(props) { return ( ); }; }); it('should allow null components', () => { function FirstChild(props) { const childrenArray = React.Children.toArray(props.children); return childrenArray[0] || null; } render( ); }); it('should allow callback refs', () => { const ref = jest.fn(); class Child extends React.Component { render() { return ; } } render( ); expect(ref).toHaveBeenCalled(); }); it('should work with no children', () => { renderStrict(, container); }); it('should handle transitioning correctly', () => { function Parent({ count = 1 }) { let children = []; for (let i = 0; i < count; i++) children.push(); return ( {children} ); } jest.useFakeTimers(); renderStrict(, container); act(() => { jest.runAllTimers(); }); expect(log).toEqual( // React 18 StrictEffects will call `componentDidMount` twice causing two `onEnter` calls. React.useTransition !== undefined ? ['appear', 'appear', 'appearing', 'appeared'] : ['appear', 'appearing', 'appeared'] ); log = []; renderStrict(, container); act(() => { jest.runAllTimers(); }); expect(log).toEqual( // React 18 StrictEffects will call `componentDidMount` twice causing two `onEnter` calls. React.useTransition !== undefined ? ['enter', 'enter', 'entering', 'entered'] : ['enter', 'entering', 'entered'] ); log = []; renderStrict(, container); act(() => { jest.runAllTimers(); }); expect(log).toEqual(['exit', 'exiting', 'exited']); }); }); react-transition-group-4.4.5/test/setup.js000066400000000000000000000001231427171024500206200ustar00rootroot00000000000000global.requestAnimationFrame = function (callback) { setTimeout(callback, 0); }; react-transition-group-4.4.5/test/setupAfterEnv.js000066400000000000000000000001331427171024500222540ustar00rootroot00000000000000import { cleanup } from '@testing-library/react/pure'; afterEach(() => { cleanup(); }); react-transition-group-4.4.5/test/utils.js000066400000000000000000000005341427171024500206260ustar00rootroot00000000000000import { render as baseRender } from '@testing-library/react/pure'; import React from 'react'; export * from '@testing-library/react'; export function render(element, options) { const result = baseRender(element, options); return { ...result, setProps(props) { result.rerender(React.cloneElement(element, props)); }, }; } react-transition-group-4.4.5/www/000077500000000000000000000000001427171024500167735ustar00rootroot00000000000000react-transition-group-4.4.5/www/.babelrc.js000066400000000000000000000000721427171024500210000ustar00rootroot00000000000000module.exports = { presets: ['babel-preset-gatsby'], }; react-transition-group-4.4.5/www/.gitignore000066400000000000000000000000151427171024500207570ustar00rootroot00000000000000.cache publicreact-transition-group-4.4.5/www/gatsby-config.js000066400000000000000000000024201427171024500220630ustar00rootroot00000000000000const path = require('path'); module.exports = { pathPrefix: `/react-transition-group`, siteMetadata: { title: 'React Transition Group Documentation', author: 'Jason Quense', componentPages: [ { path: '/transition', displayName: 'Transition', codeSandboxId: null, }, { path: '/css-transition', displayName: 'CSSTransition', codeSandboxId: 'm77l2vp00x', }, { path: '/switch-transition', displayName: 'SwitchTransition', codeSandboxId: 'switchtransition-component-iqm0d', }, { path: '/transition-group', displayName: 'TransitionGroup', codeSandboxId: '00rqyo26kn', }, ], }, plugins: [ 'gatsby-plugin-react-helmet', { resolve: 'gatsby-source-filesystem', options: { path: path.join(__dirname, 'src/pages'), name: 'pages', }, }, { resolve: 'gatsby-source-filesystem', options: { path: path.join(__dirname, '../src'), name: 'components', }, }, { resolve: 'gatsby-transformer-remark', options: { plugins: ['gatsby-remark-prismjs'], }, }, 'gatsby-transformer-react-docgen', 'gatsby-plugin-sass', ], }; react-transition-group-4.4.5/www/gatsby-node.js000066400000000000000000000022721427171024500215500ustar00rootroot00000000000000const path = require('path'); const config = require('./gatsby-config'); exports.createPages = ({ actions, graphql }) => { const { createPage } = actions; const componentTemplate = path.join( __dirname, 'src', 'templates', 'component.js' ); return new Promise((resolve, reject) => { resolve( graphql(` { allComponentMetadata { edges { node { displayName } } } } `).then((result) => { if (result.errors) { reject(result.errors); } const { componentPages } = config.siteMetadata; result.data.allComponentMetadata.edges .filter(({ node: { displayName } }) => componentPages.some((page) => page.displayName === displayName) ) .forEach(({ node: { displayName } }) => { createPage({ path: componentPages.find( (page) => page.displayName === displayName ).path, component: componentTemplate, context: { displayName, }, }); }); }) ); }); }; react-transition-group-4.4.5/www/package.json000066400000000000000000000020201427171024500212530ustar00rootroot00000000000000{ "private": true, "name": "react-transition-group-docs", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "NODE_ENV=production gatsby build --prefix-paths", "deploy": "npm run build && gh-pages -d public", "develop": "gatsby develop" }, "author": "", "license": "MIT", "dependencies": { "@babel/core": "^7.3.4", "babel-preset-gatsby": "^2.7.0", "bootstrap": "^4.3.1", "gatsby": "^2.1.22", "gatsby-plugin-react-helmet": "^3.0.10", "gatsby-plugin-sass": "^2.0.10", "gatsby-remark-prismjs": "^3.2.4", "gatsby-source-filesystem": "^2.0.23", "gatsby-transformer-react-docgen": "^3.0.5", "gatsby-transformer-remark": "^2.3.0", "lodash": "^4.17.19", "prismjs": "^1.25.0", "react": "^16.8.3", "react-bootstrap": "^1.0.0-beta.5", "react-dom": "^16.8.3", "react-helmet": "^5.2.0", "sass": "^1.49.7" }, "devDependencies": { "gh-pages": "^2.0.1" } } react-transition-group-4.4.5/www/src/000077500000000000000000000000001427171024500175625ustar00rootroot00000000000000react-transition-group-4.4.5/www/src/components/000077500000000000000000000000001427171024500217475ustar00rootroot00000000000000react-transition-group-4.4.5/www/src/components/Example.js000066400000000000000000000015171427171024500237040ustar00rootroot00000000000000import React from 'react'; import PropTypes from 'prop-types'; import { Container } from 'react-bootstrap'; const propTypes = { codeSandbox: PropTypes.shape({ title: PropTypes.string.isRequired, id: PropTypes.string.isRequired, }).isRequired, }; const Example = ({ codeSandbox }) => (

Example