diff --git a/Makefile b/Makefile
index 8000fe2c..018f4677 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ TESTS = $(wildcard src/__tests__/*-test.js)
BABEL_OPTS = \
--sourceMaps=inline
-build: $(LIB) $(ES)
+build: $(LIB) $(ES) build-min
test::
@echo No tests...
@@ -16,7 +16,7 @@ lint::
@$(BIN)/eslint $(SRC)
clean:
- @rm -f $(LIB) $(ES)
+ @rm -rf $(LIB) $(ES)
sloc:
@$(BIN)/sloc ./src
@@ -36,6 +36,10 @@ publish: build
@git push --tags origin HEAD:master
@npm publish
+build-min: $(LIB)
+ @echo "building dist/TextareaAutosize.min.js"
+ @$(BIN)/cross-env BABEL_ENV=es NODE_ENV=production $(BIN)/rollup -c
+
lib/%.js: src/%.js
@echo "building cjs $@"
@mkdir -p $(@D)
diff --git a/dist/TextareaAutosize.min.js b/dist/TextareaAutosize.min.js
new file mode 100644
index 00000000..2e53d188
--- /dev/null
+++ b/dist/TextareaAutosize.min.js
@@ -0,0 +1,550 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react'), require('prop-types')) :
+ typeof define === 'function' && define.amd ? define(['react', 'prop-types'], factory) :
+ (global.TextareaAutosize = factory(global.React,global.PropTypes));
+}(this, (function (React,PropTypes) { 'use strict';
+
+React = 'default' in React ? React['default'] : React;
+PropTypes = 'default' in PropTypes ? PropTypes['default'] : PropTypes;
+
+/**
+ * calculateNodeHeight(uiTextNode, useCache = false)
+ */
+
+var HIDDEN_TEXTAREA_STYLE = {
+ 'min-height': '0',
+ 'max-height': 'none',
+ 'height': '0',
+ 'visibility': 'hidden',
+ 'overflow': 'hidden',
+ 'position': 'absolute',
+ 'z-index': '-1000',
+ 'top': '0',
+ 'right': '0'
+};
+
+var SIZING_STYLE = ['letter-spacing', 'line-height', 'padding-top', 'padding-bottom', 'font-family', 'font-weight', 'font-size', 'text-rendering', 'text-transform', 'width', 'text-indent', 'padding-left', 'padding-right', 'border-width', 'box-sizing'];
+
+var computedStyleCache = {};
+var hiddenTextarea = void 0;
+
+function calculateNodeHeight(uiTextNode) {
+ var useCache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+ var minRows = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
+ var maxRows = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
+
+ if (!hiddenTextarea) {
+ hiddenTextarea = document.createElement('textarea');
+ document.body.appendChild(hiddenTextarea);
+ } else if (hiddenTextarea.parentNode === null) {
+ document.body.appendChild(hiddenTextarea);
+ }
+
+ // Copy all CSS properties that have an impact on the height of the content in
+ // the textbox
+
+ var _calculateNodeStyling = calculateNodeStyling(uiTextNode, useCache),
+ paddingSize = _calculateNodeStyling.paddingSize,
+ borderSize = _calculateNodeStyling.borderSize,
+ boxSizing = _calculateNodeStyling.boxSizing,
+ sizingStyle = _calculateNodeStyling.sizingStyle;
+
+ // Need to have the overflow attribute to hide the scrollbar otherwise
+ // text-lines will not calculated properly as the shadow will technically be
+ // narrower for content
+
+
+ Object.keys(sizingStyle).map(function (key) {
+ hiddenTextarea.style[key] = sizingStyle[key];
+ });
+ Object.keys(HIDDEN_TEXTAREA_STYLE).map(function (key) {
+ hiddenTextarea.style.setProperty(key, HIDDEN_TEXTAREA_STYLE[key], 'important');
+ });
+ hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || 'x';
+
+ var minHeight = -Infinity;
+ var maxHeight = Infinity;
+ var height = hiddenTextarea.scrollHeight;
+
+ if (boxSizing === 'border-box') {
+ // border-box: add border, since height = content + padding + border
+ height = height + borderSize;
+ } else if (boxSizing === 'content-box') {
+ // remove padding, since height = content
+ height = height - paddingSize;
+ }
+
+ if (minRows !== null || maxRows !== null) {
+ // measure height of a textarea with a single row
+ hiddenTextarea.value = 'x';
+ var singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
+ if (minRows !== null) {
+ minHeight = singleRowHeight * minRows;
+ if (boxSizing === 'border-box') {
+ minHeight = minHeight + paddingSize + borderSize;
+ }
+ height = Math.max(minHeight, height);
+ }
+ if (maxRows !== null) {
+ maxHeight = singleRowHeight * maxRows;
+ if (boxSizing === 'border-box') {
+ maxHeight = maxHeight + paddingSize + borderSize;
+ }
+ height = Math.min(maxHeight, height);
+ }
+ }
+ return { height: height, minHeight: minHeight, maxHeight: maxHeight };
+}
+
+function calculateNodeStyling(node) {
+ var useCache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+
+ var nodeRef = node.getAttribute('id') || node.getAttribute('data-reactid') || node.getAttribute('name');
+
+ if (useCache && computedStyleCache[nodeRef]) {
+ return computedStyleCache[nodeRef];
+ }
+
+ var style = window.getComputedStyle(node);
+
+ var boxSizing = style.getPropertyValue('box-sizing') || style.getPropertyValue('-moz-box-sizing') || style.getPropertyValue('-webkit-box-sizing');
+
+ var paddingSize = parseFloat(style.getPropertyValue('padding-bottom')) + parseFloat(style.getPropertyValue('padding-top'));
+
+ var borderSize = parseFloat(style.getPropertyValue('border-bottom-width')) + parseFloat(style.getPropertyValue('border-top-width'));
+
+ var sizingStyle = SIZING_STYLE.reduce(function (obj, name) {
+ obj[name] = style.getPropertyValue(name);
+ return obj;
+ }, {});
+
+ var nodeInfo = {
+ sizingStyle: sizingStyle,
+ paddingSize: paddingSize,
+ borderSize: borderSize,
+ boxSizing: boxSizing
+ };
+
+ if (useCache && nodeRef) {
+ computedStyleCache[nodeRef] = nodeInfo;
+ }
+
+ return nodeInfo;
+}
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+} : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+};
+
+
+
+
+
+
+
+
+
+
+
+var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+};
+
+var createClass = function () {
+ function defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+
+ return function (Constructor, protoProps, staticProps) {
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) defineProperties(Constructor, staticProps);
+ return Constructor;
+ };
+}();
+
+
+
+
+
+
+
+var _extends = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+};
+
+var get$1 = function get$1(object, property, receiver) {
+ if (object === null) object = Function.prototype;
+ var desc = Object.getOwnPropertyDescriptor(object, property);
+
+ if (desc === undefined) {
+ var parent = Object.getPrototypeOf(object);
+
+ if (parent === null) {
+ return undefined;
+ } else {
+ return get$1(parent, property, receiver);
+ }
+ } else if ("value" in desc) {
+ return desc.value;
+ } else {
+ var getter = desc.get;
+
+ if (getter === undefined) {
+ return undefined;
+ }
+
+ return getter.call(receiver);
+ }
+};
+
+var inherits = function (subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }
+
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+};
+
+
+
+
+
+
+
+
+
+var objectWithoutProperties = function (obj, keys) {
+ var target = {};
+
+ for (var i in obj) {
+ if (keys.indexOf(i) >= 0) continue;
+ if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
+ target[i] = obj[i];
+ }
+
+ return target;
+};
+
+var possibleConstructorReturn = function (self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }
+
+ return call && (typeof call === "object" || typeof call === "function") ? call : self;
+};
+
+
+
+var set$1 = function set$1(object, property, value, receiver) {
+ var desc = Object.getOwnPropertyDescriptor(object, property);
+
+ if (desc === undefined) {
+ var parent = Object.getPrototypeOf(object);
+
+ if (parent !== null) {
+ set$1(parent, property, value, receiver);
+ }
+ } else if ("value" in desc && desc.writable) {
+ desc.value = value;
+ } else {
+ var setter = desc.set;
+
+ if (setter !== undefined) {
+ setter.call(receiver, value);
+ }
+ }
+
+ return value;
+};
+
+/**
+ *
+ */
+
+var noop = function noop() {};
+
+var TextareaAutosize = function (_React$Component) {
+ inherits(TextareaAutosize, _React$Component);
+
+ function TextareaAutosize(props) {
+ classCallCheck(this, TextareaAutosize);
+
+ var _this = possibleConstructorReturn(this, _React$Component.call(this, props));
+
+ _this._onRootDOMNode = function (node) {
+ _this._rootDOMNode = node;
+ if (_this.props.inputRef) _this.props.inputRef(node);
+ };
+
+ _this._onChange = function (event) {
+ if (!_this._controlled) {
+ _this._resizeComponent();
+ }
+ var _this$props = _this.props,
+ valueLink = _this$props.valueLink,
+ onChange = _this$props.onChange;
+
+ if (valueLink) {
+ valueLink.requestChange(event.target.value);
+ } else {
+ onChange(event);
+ }
+ };
+
+ _this._resizeComponent = function () {
+ if (!_this._rootDOMNode) {
+ return;
+ }
+
+ var _calculateNodeHeight = calculateNodeHeight(_this._rootDOMNode, _this.props.useCacheForDOMMeasurements, _this.props.rows || _this.props.minRows, _this.props.maxRows),
+ height = _calculateNodeHeight.height,
+ minHeight = _calculateNodeHeight.minHeight,
+ maxHeight = _calculateNodeHeight.maxHeight;
+
+ if (_this.state.height !== height || _this.state.minHeight !== minHeight || _this.state.maxHeight !== maxHeight) {
+ _this.setState({ height: height, minHeight: minHeight, maxHeight: maxHeight });
+ }
+ };
+
+ _this.state = {
+ height: props.style && props.style.height || 0,
+ minHeight: -Infinity,
+ maxHeight: Infinity
+ };
+
+ _this._controlled = typeof props.value === 'string';
+ return _this;
+ }
+
+ TextareaAutosize.prototype.render = function render() {
+ var _props = this.props,
+ valueLink = _props.valueLink,
+ _minRows = _props.minRows,
+ _maxRows = _props.maxRows,
+ _onHeightChange = _props.onHeightChange,
+ _useCacheForDOMMeasurements = _props.useCacheForDOMMeasurements,
+ _inputRef = _props.inputRef,
+ props = objectWithoutProperties(_props, ['valueLink', 'minRows', 'maxRows', 'onHeightChange', 'useCacheForDOMMeasurements', 'inputRef']);
+
+
+ if ((typeof valueLink === 'undefined' ? 'undefined' : _typeof(valueLink)) === 'object') {
+ props.value = valueLink.value;
+ }
+
+ props.style = _extends({}, props.style, {
+ height: this.state.height
+ });
+
+ var maxHeight = Math.max(props.style.maxHeight || Infinity, this.state.maxHeight);
+
+ if (maxHeight < this.state.height) {
+ props.style.overflow = 'hidden';
+ }
+
+ return React.createElement('textarea', _extends({}, props, {
+ onChange: this._onChange,
+ ref: this._onRootDOMNode
+ }));
+ };
+
+ TextareaAutosize.prototype.componentDidMount = function componentDidMount() {
+ this._resizeComponent();
+ window.addEventListener('resize', this._resizeComponent);
+ };
+
+ TextareaAutosize.prototype.componentWillReceiveProps = function componentWillReceiveProps() {
+ // Re-render with the new content then recalculate the height as required.
+ this._clearNextFrame();
+ this._onNextFrameActionId = onNextFrame(this._resizeComponent);
+ };
+
+ TextareaAutosize.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
+ // Invoke callback when old height does not equal to new one.
+ if (this.state.height !== prevState.height) {
+ this.props.onHeightChange(this.state.height);
+ }
+ };
+
+ TextareaAutosize.prototype.componentWillUnmount = function componentWillUnmount() {
+ // Remove any scheduled events to prevent manipulating the node after it's
+ // been unmounted.
+ this._clearNextFrame();
+ window.removeEventListener('resize', this._resizeComponent);
+ };
+
+ TextareaAutosize.prototype._clearNextFrame = function _clearNextFrame() {
+ if (this._onNextFrameActionId) {
+ clearNextFrameAction(this._onNextFrameActionId);
+ }
+ };
+
+ /**
+ * Put focus on a DOM element.
+ */
+ TextareaAutosize.prototype.focus = function focus() {
+ this._rootDOMNode.focus();
+ };
+
+ /**
+ * Shifts focus away from a DOM element.
+ */
+
+
+ TextareaAutosize.prototype.blur = function blur() {
+ this._rootDOMNode.blur();
+ };
+
+ createClass(TextareaAutosize, [{
+ key: 'value',
+
+
+ /**
+ * Read the current value of from DOM.
+ */
+ get: function get() {
+ return this._rootDOMNode.value;
+ }
+
+ /**
+ * Set the current value of DOM node.
+ */
+ ,
+ set: function set(val) {
+ this._rootDOMNode.value = val;
+ }
+
+ /**
+ * Read the current selectionStart of from DOM.
+ */
+
+ }, {
+ key: 'selectionStart',
+ get: function get() {
+ return this._rootDOMNode.selectionStart;
+ }
+
+ /**
+ * Set the current selectionStart of DOM node.
+ */
+ ,
+ set: function set(selectionStart) {
+ this._rootDOMNode.selectionStart = selectionStart;
+ }
+
+ /**
+ * Read the current selectionEnd of from DOM.
+ */
+
+ }, {
+ key: 'selectionEnd',
+ get: function get() {
+ return this._rootDOMNode.selectionEnd;
+ }
+
+ /**
+ * Set the current selectionEnd of DOM node.
+ */
+ ,
+ set: function set(selectionEnd) {
+ this._rootDOMNode.selectionEnd = selectionEnd;
+ }
+ }]);
+ return TextareaAutosize;
+}(React.Component);
+
+TextareaAutosize.propTypes = {
+ /**
+ * Current textarea value.
+ */
+ value: PropTypes.string,
+
+ /**
+ * Callback on value change.
+ */
+ onChange: PropTypes.func,
+
+ /**
+ * Callback on height changes.
+ */
+ onHeightChange: PropTypes.func,
+
+ /**
+ * Try to cache DOM measurements performed by component so that we don't
+ * touch DOM when it's not needed.
+ *
+ * This optimization doesn't work if we dynamically style
+ * component.
+ */
+ useCacheForDOMMeasurements: PropTypes.bool,
+
+ /**
+ * Minimal numbder of rows to show.
+ */
+ rows: PropTypes.number,
+
+ /**
+ * Alias for `rows`.
+ */
+ minRows: PropTypes.number,
+
+ /**
+ * Maximum number of rows to show.
+ */
+ maxRows: PropTypes.number,
+
+ /**
+ * Allows an owner to retrieve the DOM node.
+ */
+ inputRef: PropTypes.func
+};
+TextareaAutosize.defaultProps = {
+ onChange: noop,
+ onHeightChange: noop,
+ useCacheForDOMMeasurements: false
+};
+function onNextFrame(cb) {
+ if (window.requestAnimationFrame) {
+ return window.requestAnimationFrame(cb);
+ }
+ return window.setTimeout(cb, 1);
+}
+
+function clearNextFrameAction(nextFrameId) {
+ if (window.cancelAnimationFrame) {
+ window.cancelAnimationFrame(nextFrameId);
+ } else {
+ window.clearTimeout(nextFrameId);
+ }
+}
+
+return TextareaAutosize;
+
+})));
diff --git a/package.json b/package.json
index 756e90ef..2f165904 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
},
"devDependencies": {
"babel-cli": "^6.24.1",
+ "babel-plugin-external-helpers": "^6.22.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
@@ -26,11 +27,14 @@
"eslint-plugin-react": "^6.1.2",
"react": "^15.3.1",
"react-dom": "^15.3.1",
+ "rollup": "^0.36.3",
+ "rollup-plugin-babel": "^2.7.1",
"watchify": "^3.7.0"
},
"files": [
"es",
- "lib"
+ "lib",
+ "dist"
],
"dependencies": {
"prop-types": "^15.5.8"
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 00000000..7d4056fd
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,21 @@
+import babel from 'rollup-plugin-babel';
+
+export default {
+ entry: 'src/TextareaAutosize.js',
+ dest: 'dist/TextareaAutosize.min.js',
+ format: 'umd',
+ moduleName: 'TextareaAutosize',
+ plugins: [
+ babel({
+ exclude: 'node_modules/**',
+ plugins: ['external-helpers'],
+ }),
+ ],
+ external: [
+ 'react', 'prop-types'
+ ],
+ globals: {
+ react: 'React',
+ 'prop-types': 'PropTypes'
+ },
+};