Skip to content

Commit

Permalink
Refactor API (#24)
Browse files Browse the repository at this point in the history
* Initial

* clean up prop names

* more exploration

* fun children

* fix tests

* some comment docs

* Incorporated development + production builds into /dist (#64) (#66)

* Incorporated development + production builds into /dist (#64) and
updated Readme to include CDN/External information.

* Dropping references to Webpack specific build configurations in Readme
and adding clarity for CDN/external.

* Enhancement for best practice prop types. (#70)

* Fixes #69: Adding capability to reduce production build output size and
allow users that import this package to include prop types in
development and optionally remove them using Webpacks UglifyJS + Define
plugin.

* Updated .babelrc that properly wraps prop-types.

* Restoring the original constant declaration for prop types as uglify
does find dead code and remove it.

* Updating changelog.
  • Loading branch information
jquense committed Jun 12, 2017
1 parent 6ffd0e2 commit 5701645
Show file tree
Hide file tree
Showing 24 changed files with 3,942 additions and 1,137 deletions.
24 changes: 5 additions & 19 deletions .eslintrc
@@ -1,24 +1,10 @@

parser: babel-eslint
extends: airbnb
extends:
- jason/react
- plugin:jsx-a11y/recommended
env:
node: true
browser: true

rules:
prefer-const: off
prefer-template: off
no-prototype-builtins: off
no-underscore-dangle: off
import/prefer-default-export: off
no-param-reassign: off
react/jsx-filename-extension: off
react/no-did-mount-set-state: off
react/require-default-props: off
no-plusplus: off
guard-for-in: off
no-restricted-syntax:
- error
- ForOfStatement
- LabeledStatement
- WithStatement
plugins:
- jsx-a11y
7 changes: 7 additions & 0 deletions .storybook/config.js
@@ -0,0 +1,7 @@
import { configure } from '@kadira/storybook';

function loadStories() {
require('../stories');
}

configure(loadStories, module);
20 changes: 20 additions & 0 deletions .storybook/webpack.config.js
@@ -0,0 +1,20 @@
module.exports = (config) => {
config.plugins = config.plugins
.filter(p => p.constructor.name !== 'CaseSensitivePathsPlugin');

config.module = {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader!css-literal-loader',
},
{
test: /\.css$/,
loader: 'style-loader!css-loader',
},
],
};

return config;
};
23 changes: 15 additions & 8 deletions package.json
Expand Up @@ -9,7 +9,9 @@
"build": "rimraf lib && babel src --out-dir lib && npm run build:dist",
"build:dist": "rimraf lib/dist && webpack && NODE_ENV=production webpack -p",
"lint": "eslint src test",
"release": "release"
"release": "release",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"repository": {
"type": "git",
Expand All @@ -34,25 +36,26 @@
"testRegex": "-test\\.js",
"roots": [
"<rootDir>/test"
],
"timers": "fake"
]
},
"peerDependencies": {
"react": "^15.0.0",
"react-dom": "^15.0.0"
},
"dependencies": {
"chain-function": "^1.0.0",
"classnames": "^2.2.5",
"dom-helpers": "^3.2.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.5.6",
"prop-types": "^15.5.8",
"warning": "^3.0.0"
},
"devDependencies": {
"@kadira/storybook": "^2.21.0",
"babel-cli": "^6.24.0",
"babel-core": "^6.24.0",
"babel-eslint": "^7.1.1",
"babel-jest": "^19.0.0",
"babel-jest": "^20.0.3",
"babel-loader": "^6.4.1",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-dev-expression": "^0.2.1",
Expand All @@ -61,16 +64,20 @@
"babel-preset-latest": "^6.24.0",
"babel-preset-react": "^6.23.0",
"babel-preset-stage-2": "^6.18.0",
"css-literal-loader": "^0.2.0",
"css-loader": "^0.28.0",
"eslint": "^3.17.1",
"eslint-config-airbnb": "^14.1.0",
"eslint-config-jason": "^4.0.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.0",
"jest-cli": "^19.0.2",
"eslint-plugin-react": "^6.10.3",
"jest": "^20.0.4",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"release-script": "^1.0.2",
"rimraf": "^2.6.1",
"sinon": "^2.1.0",
"style-loader": "^0.16.1",
"teaspoon": "^6.4.3",
"webpack": "^2.2.1"
},
Expand Down
223 changes: 223 additions & 0 deletions src/CSSTransition.js
@@ -0,0 +1,223 @@
import * as PropTypes from 'prop-types';
import addClass from 'dom-helpers/class/addClass';
import removeClass from 'dom-helpers/class/removeClass';
import React from 'react';

import Transition from './Transition';
import { classNamesShape } from './utils/PropTypes';

const propTypes = {
...Transition.propTypes,

/**
* The animation classNames applied to the component as it enters or exits.
* A single name can be provided and it will be suffixed for each stage: e.g.
*
* `classNames="fade"` applies `fade-enter`, `fade-enter-active`,
* `fade-exit`, `fade-exit-active`, `fade-appear`, and `fade-appear-active`.
* Each individual classNames can also be specified independently like:
*
* ```js
* classNames={{
* appear: 'my-appear',
* appearActive: 'my-active-appear',
* enter: 'my-enter',
* enterActive: 'my-active-enter',
* exit: 'my-exit',
* exitActive: 'my-active-exit',
* }}
* ```
*/
classNames: classNamesShape,

/**
* A `<Transition>` callback fired immediately after the 'enter' or 'appear' class is
* applied.
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onEnter: PropTypes.func,

/**
* A `<Transition>` callback fired immediately after the 'enter-active' or
* 'appear-active' class is applied.
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onEntering: PropTypes.func,

/**
* A `<Transition>` callback fired immediately after the 'enter' or
* 'appear' classes are **removed** from the DOM node.
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onEntered: PropTypes.func,


/**
* A `<Transition>` callback fired immediately after the 'exit' class is
* applied.
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onExit: PropTypes.func,

/**
* A `<Transition>` callback fired immediately after the 'exit-active' is
* class is applied.
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onExiting: PropTypes.func,

/**
* A `<Transition>` callback fired immediately after the 'exit' classes
* are **removed** from the DOM node.
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onExited: PropTypes.func,
};

/**
* A `Transition` component using CSS transitions and animations.
* It's inspired by the excellent [ng-animate](http://www.nganimate.org/) libary.
*
* `CSSTransition` applies a pair of class names during the `appear`, `enter`,
* and `exit` stages of the transition. The first class is applied and then a
* second "active" class in order to activate the css animation.
*
* When the `in` prop is toggled to `true` the Component will get
* the `example-enter` CSS class and the `example-enter-active` CSS class
* added in the next tick. This is a convention based on the `classNames` prop.
*
* ```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;
* }
* ```
*/
class CSSTransition extends React.Component {
onEnter = (node, appearing) => {
const { className } = this.getClassNames(appearing ? 'appear' : 'enter')

this.removeClasses(node, 'exit');
addClass(node, className)

if (this.props.onEnter) {
this.props.onEnter(node)
}
}

onEntering = (node, appearing) => {
const { activeClassName } = this.getClassNames(
appearing ? 'appear' : 'enter'
);

this.reflowAndAddClass(node, activeClassName)

if (this.props.onEntering) {
this.props.onEntering(node)
}
}

onEntered = (node, appearing) => {
this.removeClasses(node, appearing ? 'appear' : 'enter');

if (this.props.onEntered) {
this.props.onEntered(node)
}
}

onExit = (node) => {
const { className } = this.getClassNames('exit')

this.removeClasses(node, 'appear');
this.removeClasses(node, 'exit');
addClass(node, className)

if (this.props.onExit) {
this.props.onExit(node)
}
}

onExiting = (node) => {
const { activeClassName } = this.getClassNames('exit')

this.reflowAndAddClass(node, activeClassName)

if (this.props.onExiting) {
this.props.onExiting(node)
}
}

onExited = (node) => {
this.removeClasses(node, 'exit');

if (this.props.onExited) {
this.props.onExited(node)
}
}

getClassNames = (type) => {
const { classNames } = this.props

let className = typeof classNames !== 'string' ?
classNames[type] : classNames + '-' + type;

let activeClassName = typeof classNames !== 'string' ?
classNames[type + 'Active'] : className + '-active';

return { className, activeClassName }
}

removeClasses(node, type) {
const { className, activeClassName } = this.getClassNames(type)
className && removeClass(node, className);
activeClassName && removeClass(node, activeClassName);
}

reflowAndAddClass(node, className) {
// This is for to force a repaint,
// which is necessary in order to transition styles when adding a class name.
/* eslint-disable no-unused-expressions */
node.scrollTop;
/* eslint-enable no-unused-expressions */
addClass(node, className);
}

render() {
const props = { ...this.props };
Object.keys(propTypes).forEach(key => delete props[key]);
return (
<Transition
{...props}
onEnter={this.onEnter}
onEntered={this.onEntered}
onEntering={this.onEntering}
onExit={this.onExit}
onExiting={this.onExiting}
onExited={this.onExited}
/>
);
}
}

CSSTransition.propTypes = propTypes;

export default CSSTransition

0 comments on commit 5701645

Please sign in to comment.