UNPKG

8.81 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _extends2 = require('babel-runtime/helpers/extends');
8
9var _extends3 = _interopRequireDefault(_extends2);
10
11var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
12
13var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
14
15var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
16
17var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
18
19var _createClass2 = require('babel-runtime/helpers/createClass');
20
21var _createClass3 = _interopRequireDefault(_createClass2);
22
23var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
24
25var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
26
27var _inherits2 = require('babel-runtime/helpers/inherits');
28
29var _inherits3 = _interopRequireDefault(_inherits2);
30
31var _react = require('react');
32
33var _react2 = _interopRequireDefault(_react);
34
35var _propTypes = require('prop-types');
36
37var _propTypes2 = _interopRequireDefault(_propTypes);
38
39var _substyle = require('substyle');
40
41var _isEqual = require('lodash/isEqual');
42
43var _isEqual2 = _interopRequireDefault(_isEqual);
44
45var _utils = require('./utils');
46
47var _utils2 = _interopRequireDefault(_utils);
48
49var _Mention = require('./Mention');
50
51var _Mention2 = _interopRequireDefault(_Mention);
52
53function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
54
55var _generateComponentKey = function _generateComponentKey(usedKeys, id) {
56 if (!usedKeys.hasOwnProperty(id)) {
57 usedKeys[id] = 0;
58 } else {
59 usedKeys[id]++;
60 }
61 return id + "_" + usedKeys[id];
62};
63
64var Highlighter = function (_Component) {
65 (0, _inherits3.default)(Highlighter, _Component);
66
67 function Highlighter() {
68 (0, _classCallCheck3.default)(this, Highlighter);
69
70 var _this = (0, _possibleConstructorReturn3.default)(this, (Highlighter.__proto__ || (0, _getPrototypeOf2.default)(Highlighter)).apply(this, arguments));
71
72 _this.state = { lastPosition: {} };
73 return _this;
74 }
75
76 (0, _createClass3.default)(Highlighter, [{
77 key: 'componentDidMount',
78 value: function componentDidMount() {
79 this.notifyCaretPosition();
80 }
81 }, {
82 key: 'componentDidUpdate',
83 value: function componentDidUpdate() {
84 this.notifyCaretPosition();
85 }
86 }, {
87 key: 'notifyCaretPosition',
88 value: function notifyCaretPosition() {
89 var caret = this.refs.caret;
90
91
92 if (!caret) {
93 return;
94 }
95
96 var position = {
97 left: caret.offsetLeft,
98 top: caret.offsetTop
99 };
100
101 var lastPosition = this.state.lastPosition;
102
103
104 if ((0, _isEqual2.default)(lastPosition, position)) {
105 return;
106 }
107
108 this.setState({
109 lastPosition: position
110 });
111
112 this.props.onCaretPositionChange(position);
113 }
114 }, {
115 key: 'render',
116 value: function render() {
117 var _this2 = this;
118
119 var _props = this.props,
120 selection = _props.selection,
121 value = _props.value,
122 markup = _props.markup,
123 displayTransform = _props.displayTransform,
124 style = _props.style,
125 inputStyle = _props.inputStyle;
126
127 // If there's a caret (i.e. no range selection), map the caret position into the marked up value
128
129 var caretPositionInMarkup;
130 if (selection.start === selection.end) {
131 caretPositionInMarkup = _utils2.default.mapPlainTextIndex(value, markup, selection.start, 'START', displayTransform);
132 }
133
134 var resultComponents = [];
135 var componentKeys = {};
136
137 // start by appending directly to the resultComponents
138 var components = resultComponents;
139
140 var substringComponentKey = 0;
141
142 var textIteratee = function textIteratee(substr, index, indexInPlainText) {
143 // check whether the caret element has to be inserted inside the current plain substring
144 if (_utils2.default.isNumber(caretPositionInMarkup) && caretPositionInMarkup >= index && caretPositionInMarkup <= index + substr.length) {
145 // if yes, split substr at the caret position and insert the caret component
146 var splitIndex = caretPositionInMarkup - index;
147 components.push(_this2.renderSubstring(substr.substring(0, splitIndex), substringComponentKey));
148
149 // add all following substrings and mention components as children of the caret component
150 components = [_this2.renderSubstring(substr.substring(splitIndex), substringComponentKey)];
151 } else {
152 // otherwise just push the plain text substring
153 components.push(_this2.renderSubstring(substr, substringComponentKey));
154 }
155
156 substringComponentKey++;
157 };
158
159 var mentionIteratee = function (markup, index, indexInPlainText, id, display, type, lastMentionEndIndex) {
160 // generate a component key based on the id
161 var key = _generateComponentKey(componentKeys, id);
162 components.push(this.getMentionComponentForMatch(id, display, type, key));
163 }.bind(this);
164 _utils2.default.iterateMentionsMarkup(value, markup, textIteratee, mentionIteratee, displayTransform);
165
166 // append a span containing a space, to ensure the last text line has the correct height
167 components.push(" ");
168
169 if (components !== resultComponents) {
170 // if a caret component is to be rendered, add all components that followed as its children
171 resultComponents.push(this.renderHighlighterCaret(components));
172 }
173
174 return _react2.default.createElement(
175 'div',
176 (0, _extends3.default)({}, style, {
177 style: (0, _extends3.default)({}, inputStyle, style.style) }),
178 resultComponents
179 );
180 }
181 }, {
182 key: 'renderSubstring',
183 value: function renderSubstring(string, key) {
184 // set substring span to hidden, so that Emojis are not shown double in Mobile Safari
185 return _react2.default.createElement(
186 'span',
187 (0, _extends3.default)({}, this.props.style("substring"), { key: key }),
188 string
189 );
190 }
191
192 // Returns a clone of the Mention child applicable for the specified type to be rendered inside the highlighter
193
194 }, {
195 key: 'getMentionComponentForMatch',
196 value: function getMentionComponentForMatch(id, display, type, key) {
197 var childrenCount = _react.Children.count(this.props.children);
198 var props = { id: id, display: display, key: key };
199
200 if (childrenCount > 1) {
201 if (!type) {
202 throw new Error("Since multiple Mention components have been passed as children, the markup has to define the __type__ placeholder");
203 }
204
205 // detect the Mention child to be cloned
206 var foundChild = null;
207 _react.Children.forEach(this.props.children, function (child) {
208 if (!child) {
209 return;
210 }
211
212 if (child.props.type === type) {
213 foundChild = child;
214 }
215 });
216
217 // clone the Mention child that is applicable for the given type
218 return _react2.default.cloneElement(foundChild, props);
219 }
220
221 if (childrenCount === 1) {
222 // clone single Mention child
223 var child = this.props.children.length ? this.props.children[0] : _react.Children.only(this.props.children);
224 return _react2.default.cloneElement(child, props);
225 }
226
227 // no children, use default configuration
228 return (0, _Mention2.default)(props);
229 }
230
231 // Renders an component to be inserted in the highlighter at the current caret position
232
233 }, {
234 key: 'renderHighlighterCaret',
235 value: function renderHighlighterCaret(children) {
236 return _react2.default.createElement(
237 'span',
238 (0, _extends3.default)({}, this.props.style("caret"), { ref: 'caret', key: 'caret' }),
239 children
240 );
241 }
242 }]);
243 return Highlighter;
244}(_react.Component);
245
246Highlighter.propTypes = {
247 selection: _propTypes2.default.shape({
248 start: _propTypes2.default.number,
249 end: _propTypes2.default.number
250 }).isRequired,
251
252 markup: _propTypes2.default.string.isRequired,
253 value: _propTypes2.default.string.isRequired,
254
255 displayTransform: _propTypes2.default.func.isRequired,
256 onCaretPositionChange: _propTypes2.default.func.isRequired,
257 inputStyle: _propTypes2.default.object
258};
259Highlighter.defaultProps = {
260 value: "",
261 inputStyle: {}
262};
263
264
265var styled = (0, _substyle.defaultStyle)({
266 position: 'relative',
267 width: 'inherit',
268 color: 'transparent',
269
270 overflow: 'hidden',
271
272 whiteSpace: 'pre-wrap',
273 wordWrap: 'break-word',
274
275 '&singleLine': {
276 whiteSpace: 'pre',
277 wordWrap: null
278 },
279
280 substring: {
281 visibility: 'hidden'
282 }
283}, function (props) {
284 return {
285 '&singleLine': props.singleLine
286 };
287});
288
289exports.default = styled(Highlighter);
\No newline at end of file