diff --git a/.size-snapshot.json b/.size-snapshot.json index 7baaabc..8329358 100644 --- a/.size-snapshot.json +++ b/.size-snapshot.json @@ -1,25 +1,25 @@ { "dist/index.umd.js": { - "bundled": 15914, - "minified": 6880, - "gzipped": 2594 + "bundled": 16295, + "minified": 7136, + "gzipped": 2643 }, "dist/index.umd.min.js": { - "bundled": 15914, - "minified": 6880, - "gzipped": 2594 + "bundled": 16295, + "minified": 7136, + "gzipped": 2643 }, "dist/index.esm.js": { - "bundled": 14601, - "minified": 7845, - "gzipped": 2641, + "bundled": 14952, + "minified": 7975, + "gzipped": 2690, "treeshaked": { "rollup": { - "code": 6566, + "code": 6822, "import_statements": 63 }, "webpack": { - "code": 7644 + "code": 7914 } } } diff --git a/lib/cjs/react-sane-contenteditable.js b/lib/cjs/react-sane-contenteditable.js index 79df954..4504390 100644 --- a/lib/cjs/react-sane-contenteditable.js +++ b/lib/cjs/react-sane-contenteditable.js @@ -20,7 +20,9 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat function _extends() { _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; }; return _extends.apply(this, arguments); } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } @@ -36,12 +38,12 @@ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } -function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } - function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var propTypes = { @@ -76,7 +78,7 @@ function (_Component) { _this = _possibleConstructorReturn(this, _getPrototypeOf(ContentEditable).call(this)); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "insertAtCaret", function (prevValue, valueToInsert) { + _defineProperty(_assertThisInitialized(_this), "insertAtCaret", function (prevValue, valueToInsert) { var _this$getRange = _this.getRange(), startOffset = _this$getRange.startOffset, endOffset = _this$getRange.endOffset; @@ -86,7 +88,7 @@ function (_Component) { return [prefix, valueToInsert, suffix].join(''); }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onBlur", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onBlur", function (ev) { var value = _this.state.value; var onBlur = _this.props.onBlur; @@ -95,7 +97,7 @@ function (_Component) { } }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onInput", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onInput", function (ev) { var maxLength = _this.props.maxLength; var innerText = ev.target.innerText; @@ -116,7 +118,7 @@ function (_Component) { }); }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onKeyDown", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onKeyDown", function (ev) { var innerText = ev.target.innerText; var _this$props = _this.props, maxLength = _this$props.maxLength, @@ -153,7 +155,7 @@ function (_Component) { } }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onKeyUp", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onKeyUp", function (ev) { var innerText = ev.target.innerText; var onKeyUp = _this.props.onKeyUp; @@ -162,7 +164,7 @@ function (_Component) { } }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onPaste", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onPaste", function (ev) { ev.preventDefault(); var pastedValue = ev.clipboardData.getData('text'); @@ -183,7 +185,7 @@ function (_Component) { }); }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setRef", function (ref) { + _defineProperty(_assertThisInitialized(_this), "setRef", function (ref) { var innerRef = _this.props.innerRef; _this.ref = ref; @@ -252,15 +254,17 @@ function (_Component) { var _this$state = this.state, caretPosition = _this$state.caretPosition, value = _this$state.value; + var node = this.ref.childNodes[0] || this.ref; var val = nextValue || value; var pos = position || caretPosition; - return Math.min(pos, val.length); + return Math.min(pos, val.length, node.length); } }, { key: "setCaretPosition", value: function setCaretPosition() { var range = this.getRange(); - range.setStart(this.ref.childNodes[0] || this.ref, this.getSafeCaretPosition()); + var node = this.ref.childNodes[0] || this.ref; + range.setStart(node, this.getSafeCaretPosition()); range.collapse(); this.selection.removeAllRanges(); this.selection.addRange(range); diff --git a/lib/cjs/utils.js b/lib/cjs/utils.js index 223d808..7e76c5a 100644 --- a/lib/cjs/utils.js +++ b/lib/cjs/utils.js @@ -4,7 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } diff --git a/lib/esm/react-sane-contenteditable.js b/lib/esm/react-sane-contenteditable.js index 5e69a85..826b6cd 100644 --- a/lib/esm/react-sane-contenteditable.js +++ b/lib/esm/react-sane-contenteditable.js @@ -1,13 +1,17 @@ import _extends from "@babel/runtime/helpers/extends"; -import _objectSpread from "@babel/runtime/helpers/objectSpread"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; -import _inherits from "@babel/runtime/helpers/inherits"; import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; +import _inherits from "@babel/runtime/helpers/inherits"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { isFunction, omit } from './utils'; @@ -43,7 +47,7 @@ function (_Component) { _this = _possibleConstructorReturn(this, _getPrototypeOf(ContentEditable).call(this)); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "insertAtCaret", function (prevValue, valueToInsert) { + _defineProperty(_assertThisInitialized(_this), "insertAtCaret", function (prevValue, valueToInsert) { var _this$getRange = _this.getRange(), startOffset = _this$getRange.startOffset, endOffset = _this$getRange.endOffset; @@ -53,7 +57,7 @@ function (_Component) { return [prefix, valueToInsert, suffix].join(''); }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onBlur", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onBlur", function (ev) { var value = _this.state.value; var onBlur = _this.props.onBlur; @@ -62,7 +66,7 @@ function (_Component) { } }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onInput", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onInput", function (ev) { var maxLength = _this.props.maxLength; var innerText = ev.target.innerText; @@ -83,7 +87,7 @@ function (_Component) { }); }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onKeyDown", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onKeyDown", function (ev) { var innerText = ev.target.innerText; var _this$props = _this.props, maxLength = _this$props.maxLength, @@ -120,7 +124,7 @@ function (_Component) { } }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onKeyUp", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onKeyUp", function (ev) { var innerText = ev.target.innerText; var onKeyUp = _this.props.onKeyUp; @@ -129,7 +133,7 @@ function (_Component) { } }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onPaste", function (ev) { + _defineProperty(_assertThisInitialized(_this), "onPaste", function (ev) { ev.preventDefault(); var pastedValue = ev.clipboardData.getData('text'); @@ -150,7 +154,7 @@ function (_Component) { }); }); - _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setRef", function (ref) { + _defineProperty(_assertThisInitialized(_this), "setRef", function (ref) { var innerRef = _this.props.innerRef; _this.ref = ref; @@ -219,15 +223,17 @@ function (_Component) { var _this$state = this.state, caretPosition = _this$state.caretPosition, value = _this$state.value; + var node = this.ref.childNodes[0] || this.ref; var val = nextValue || value; var pos = position || caretPosition; - return Math.min(pos, val.length); + return Math.min(pos, val.length, node.length); } }, { key: "setCaretPosition", value: function setCaretPosition() { var range = this.getRange(); - range.setStart(this.ref.childNodes[0] || this.ref, this.getSafeCaretPosition()); + var node = this.ref.childNodes[0] || this.ref; + range.setStart(node, this.getSafeCaretPosition()); range.collapse(); this.selection.removeAllRanges(); this.selection.addRange(range); diff --git a/lib/esm/utils.js b/lib/esm/utils.js index 1db8b1f..8d532b4 100644 --- a/lib/esm/utils.js +++ b/lib/esm/utils.js @@ -1,5 +1,8 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty"; -import _objectSpread from "@babel/runtime/helpers/objectSpread"; + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var reduceTargetKeys = function reduceTargetKeys(target, keys, predicate) { return Object.keys(target).reduce(predicate, {}); diff --git a/src/__tests__/react-sane-contenteditable.test.js b/src/__tests__/react-sane-contenteditable.test.js index 3b08b88..2afd7d3 100644 --- a/src/__tests__/react-sane-contenteditable.test.js +++ b/src/__tests__/react-sane-contenteditable.test.js @@ -8,6 +8,8 @@ import ContentEditable from '../react-sane-contenteditable'; // jsDOM const doc = new JSDOM(''); +const addRange = jest.fn(); + const mockedRange = { cloneRange: jest.fn(() => mockedRange), collapse: jest.fn(), @@ -20,7 +22,7 @@ const mockedRange = { global.document = doc; global.window = doc.defaultView; global.document.getSelection = jest.fn(() => ({ - addRange: jest.fn(), + addRange: addRange, getRangeAt: jest.fn(() => mockedRange), rangeCount: 0, removeAllRanges: jest.fn(), @@ -214,6 +216,15 @@ describe('Handles selections', () => { expect(wrapper.state('caretPosition')).toEqual(startOffset); expect(wrapper.text()).toEqual(nextContent); }); + + it('sets a new range without error when length of new value exceeds existing ref childNode', () => { + const content = 'Foo bar.'; + const wrapper = mount(); + const ref = wrapper.instance().ref; + ref.innerText = content; + wrapper.setProps({ content: 'Foo bar' }); + expect(addRange).toHaveBeenCalled(); + }); }); describe('Sanitisation', () => { diff --git a/src/react-sane-contenteditable.js b/src/react-sane-contenteditable.js index e9fa892..e362ff3 100644 --- a/src/react-sane-contenteditable.js +++ b/src/react-sane-contenteditable.js @@ -100,16 +100,18 @@ class ContentEditable extends Component { getSafeCaretPosition(position, nextValue) { const { caretPosition, value } = this.state; + const node = this.ref.childNodes[0] || this.ref; const val = nextValue || value; let pos = position || caretPosition; - return Math.min(pos, val.length); + return Math.min(pos, val.length, node.length); } setCaretPosition() { let range = this.getRange(); - range.setStart(this.ref.childNodes[0] || this.ref, this.getSafeCaretPosition()); + const node = this.ref.childNodes[0] || this.ref; + range.setStart(node, this.getSafeCaretPosition()); range.collapse(); this.selection.removeAllRanges(); this.selection.addRange(range);