@pie-lib/text-select 1.29.0 → 1.29.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.js ADDED
@@ -0,0 +1,1764 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Button from '@material-ui/core/Button';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+ import Switch from '@material-ui/core/Switch';
6
+ import FormControlLabel from '@material-ui/core/FormControlLabel';
7
+ import { color } from '@pie-lib/render-ui';
8
+ import classNames from 'classnames';
9
+ import compact from 'lodash/compact';
10
+ import English from '@pie-framework/parse-english';
11
+ import clone from 'lodash/clone';
12
+ import isEqual from 'lodash/isEqual';
13
+ import differenceWith from 'lodash/differenceWith';
14
+ import { noSelect } from '@pie-lib/style-utils';
15
+ import yellow from '@material-ui/core/colors/yellow';
16
+ import green from '@material-ui/core/colors/green';
17
+ import debug from 'debug';
18
+ import require$$3 from '@material-ui/core/SvgIcon';
19
+ import { renderToString } from 'react-dom/server';
20
+ import Translator from '@pie-lib/translator';
21
+
22
+ function _extends() {
23
+ _extends = Object.assign || function (target) {
24
+ for (var i = 1; i < arguments.length; i++) {
25
+ var source = arguments[i];
26
+
27
+ for (var key in source) {
28
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
29
+ target[key] = source[key];
30
+ }
31
+ }
32
+ }
33
+
34
+ return target;
35
+ };
36
+
37
+ return _extends.apply(this, arguments);
38
+ }
39
+
40
+ class Controls extends React.Component {
41
+ render() {
42
+ const {
43
+ classes,
44
+ onClear,
45
+ onWords,
46
+ onSentences,
47
+ onParagraphs,
48
+ setCorrectMode,
49
+ onToggleCorrectMode
50
+ } = this.props;
51
+ return /*#__PURE__*/React.createElement("div", {
52
+ className: classes.controls
53
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Button, {
54
+ onClick: onWords,
55
+ className: classes.button,
56
+ size: "small",
57
+ color: "primary",
58
+ disabled: setCorrectMode
59
+ }, "Words"), /*#__PURE__*/React.createElement(Button, {
60
+ onClick: onSentences,
61
+ className: classes.button,
62
+ size: "small",
63
+ color: "primary",
64
+ disabled: setCorrectMode
65
+ }, "Sentences"), /*#__PURE__*/React.createElement(Button, {
66
+ onClick: onParagraphs,
67
+ className: classes.button,
68
+ size: "small",
69
+ color: "primary",
70
+ disabled: setCorrectMode
71
+ }, "Paragraphs"), /*#__PURE__*/React.createElement(Button, {
72
+ className: classes.button,
73
+ size: "small",
74
+ color: "secondary",
75
+ onClick: onClear,
76
+ disabled: setCorrectMode
77
+ }, "Clear")), /*#__PURE__*/React.createElement(FormControlLabel, {
78
+ control: /*#__PURE__*/React.createElement(Switch, {
79
+ classes: {
80
+ checked: classes.checkedThumb,
81
+ bar: classNames({
82
+ [classes.checkedBar]: setCorrectMode
83
+ })
84
+ },
85
+ checked: setCorrectMode,
86
+ onChange: onToggleCorrectMode
87
+ }),
88
+ label: "Set correct answers"
89
+ }));
90
+ }
91
+
92
+ }
93
+ Controls.propTypes = {
94
+ classes: PropTypes.object.isRequired,
95
+ onClear: PropTypes.func.isRequired,
96
+ onWords: PropTypes.func.isRequired,
97
+ onSentences: PropTypes.func.isRequired,
98
+ onParagraphs: PropTypes.func.isRequired,
99
+ setCorrectMode: PropTypes.bool.isRequired,
100
+ onToggleCorrectMode: PropTypes.func.isRequired
101
+ };
102
+ Controls.defaultProps = {};
103
+ var Controls$1 = withStyles(theme => ({
104
+ button: {
105
+ marginRight: theme.spacing.unit
106
+ },
107
+ controls: {
108
+ display: 'flex',
109
+ alignItems: 'center',
110
+ justifyContent: 'space-between'
111
+ },
112
+ checkedThumb: {
113
+ color: `${color.tertiary()} !important`
114
+ },
115
+ checkedBar: {
116
+ backgroundColor: `${color.tertiaryLight()} !important`
117
+ }
118
+ }))(Controls);
119
+
120
+ const g = (str, node) => {
121
+ if (node.children) {
122
+ return node.children.reduce(g, str);
123
+ } else if (node.value) {
124
+ return str + node.value;
125
+ } else {
126
+ return str;
127
+ }
128
+ };
129
+
130
+ const getParagraph = p => g('', p);
131
+
132
+ const getSentence = s => g('', s);
133
+
134
+ const getWord = w => g('', w);
135
+
136
+ const paragraphs = text => {
137
+ const tree = new English().parse(text);
138
+ const out = tree.children.reduce((acc, child) => {
139
+ if (child.type === 'ParagraphNode') {
140
+ const paragraph = {
141
+ text: getParagraph(child),
142
+ start: child.position.start.offset,
143
+ end: child.position.end.offset
144
+ };
145
+ return acc.concat([paragraph]);
146
+ } else {
147
+ return acc;
148
+ }
149
+ }, []);
150
+ return out;
151
+ };
152
+ const handleSentence = (child, acc) => {
153
+ const sentenceChilds = []; // we parse the children of the sentence
154
+
155
+ let newAcc = child.children.reduce(function (acc, child) {
156
+ // if we find a whitespace node that's \n, we end the sentence
157
+ if (child.type === 'WhiteSpaceNode' && child.value === '\n') {
158
+ if (sentenceChilds.length) {
159
+ const firstWord = sentenceChilds[0]; // we create a sentence starting from the first word until the new line
160
+
161
+ const sentence = {
162
+ text: sentenceChilds.map(d => getSentence(d)).join(''),
163
+ start: firstWord.position.start.offset,
164
+ end: child.position.start.offset
165
+ }; // we remove all the elements from the array
166
+
167
+ sentenceChilds.splice(0, sentenceChilds.length);
168
+ return acc.concat([sentence]);
169
+ }
170
+ } else {
171
+ // otherwise we add it to the array that contains the child forming a sentence
172
+ sentenceChilds.push(child);
173
+ }
174
+
175
+ return acc;
176
+ }, acc); // we treat the case when no \n character is found at the end
177
+ // so we create a sentence from the last words or white spaces found
178
+
179
+ if (sentenceChilds.length) {
180
+ const firstWord = sentenceChilds[0];
181
+ const lastWord = sentenceChilds[sentenceChilds.length - 1];
182
+ const sentence = {
183
+ text: sentenceChilds.map(d => getSentence(d)).join(''),
184
+ start: firstWord.position.start.offset,
185
+ end: lastWord.position.end.offset
186
+ };
187
+ newAcc = newAcc.concat([sentence]);
188
+ sentenceChilds.splice(0, sentenceChilds.length);
189
+ }
190
+
191
+ return newAcc;
192
+ };
193
+ const sentences = text => {
194
+ const tree = new English().parse(text);
195
+ const out = tree.children.reduce((acc, child) => {
196
+ if (child.type === 'ParagraphNode') {
197
+ return child.children.reduce((acc, child) => {
198
+ if (child.type === 'SentenceNode') {
199
+ const newAcc = handleSentence(child, acc);
200
+ return newAcc || acc;
201
+ } else {
202
+ return acc;
203
+ }
204
+ }, acc);
205
+ } else {
206
+ return acc;
207
+ }
208
+ }, []);
209
+ return out;
210
+ };
211
+ const words = text => {
212
+ const tree = new English().parse(text);
213
+ const out = tree.children.reduce((acc, child) => {
214
+ if (child.type === 'ParagraphNode') {
215
+ return child.children.reduce((acc, child) => {
216
+ if (child.type === 'SentenceNode') {
217
+ return child.children.reduce((acc, child) => {
218
+ if (child.type === 'WordNode') {
219
+ const node = {
220
+ text: getWord(child),
221
+ start: child.position.start.offset,
222
+ end: child.position.end.offset
223
+ };
224
+ return acc.concat([node]);
225
+ } else {
226
+ return acc;
227
+ }
228
+ }, acc);
229
+ } else {
230
+ return acc;
231
+ }
232
+ }, acc);
233
+ } else {
234
+ return acc;
235
+ }
236
+ }, []);
237
+ return out;
238
+ };
239
+
240
+ class Intersection {
241
+ constructor(results) {
242
+ this.results = results;
243
+ }
244
+
245
+ get hasOverlap() {
246
+ return this.results.filter(r => r.type === 'overlap').length > 0;
247
+ }
248
+
249
+ get surroundedTokens() {
250
+ return this.results.filter(r => r.type === 'within-selection').map(t => t.token);
251
+ }
252
+
253
+ }
254
+ /**
255
+ * get intersection info for the selection in relation to tokens.
256
+ * @param {{start: number, end: number}} selection
257
+ * @param {{start: number, end: number}[]} tokens
258
+ * @return {tokens: [], type: 'overlap|no-overlap|contains'}
259
+ */
260
+
261
+
262
+ const intersection = (selection, tokens) => {
263
+ const {
264
+ start,
265
+ end
266
+ } = selection;
267
+
268
+ const startsWithin = t => start >= t.start && start < t.end;
269
+
270
+ const endsWithin = t => end > t.start && end <= t.end;
271
+
272
+ const mapped = tokens.map(t => {
273
+ if (start === t.start && end === t.end) {
274
+ return {
275
+ token: t,
276
+ type: 'exact-fit'
277
+ };
278
+ } else if (start <= t.start && end >= t.end) {
279
+ return {
280
+ token: t,
281
+ type: 'within-selection'
282
+ };
283
+ } else if (startsWithin(t) || endsWithin(t)) {
284
+ return {
285
+ token: t,
286
+ type: 'overlap'
287
+ };
288
+ }
289
+ });
290
+ return new Intersection(compact(mapped));
291
+ };
292
+ const sort = tokens => {
293
+ if (!Array.isArray(tokens)) {
294
+ return tokens;
295
+ } else {
296
+ const out = clone(tokens);
297
+ out.sort((a, b) => {
298
+ const s = a.start < b.start ? -1 : a.start > b.start ? 1 : 0;
299
+ const e = a.end < b.end ? -1 : a.end > b.end ? 1 : 0;
300
+
301
+ if (s === -1 && e !== -1) {
302
+ throw new Error(`sort does not support intersecting tokens. a: ${a.start}-${a.end}, b: ${b.start}-${b.end}`);
303
+ }
304
+
305
+ return s;
306
+ });
307
+ return out;
308
+ }
309
+ };
310
+ const normalize = (textToNormalize, tokens) => {
311
+ // making sure text provided is a string
312
+ const text = textToNormalize || '';
313
+
314
+ if (!Array.isArray(tokens) || tokens.length === 0) {
315
+ return [{
316
+ text,
317
+ start: 0,
318
+ end: text.length
319
+ }];
320
+ }
321
+
322
+ const out = sort(tokens).reduce((acc, t, index, outer) => {
323
+ let tokens = [];
324
+ const lastIndex = acc.lastIndex;
325
+
326
+ if (t.start === lastIndex) {
327
+ tokens = [{
328
+ text: text.substring(lastIndex, t.end),
329
+ start: lastIndex,
330
+ end: t.end,
331
+ predefined: true,
332
+ correct: t.correct,
333
+ isMissing: t.isMissing
334
+ }];
335
+ } else if (lastIndex < t.start) {
336
+ tokens = [{
337
+ text: text.substring(lastIndex, t.start),
338
+ start: lastIndex,
339
+ end: t.start
340
+ }, {
341
+ text: text.substring(t.start, t.end),
342
+ start: t.start,
343
+ end: t.end,
344
+ predefined: true,
345
+ correct: t.correct,
346
+ isMissing: t.isMissing
347
+ }];
348
+ }
349
+
350
+ if (index === outer.length - 1 && t.end < text.length) {
351
+ const last = {
352
+ text: text.substring(t.end),
353
+ start: t.end,
354
+ end: text.length
355
+ };
356
+ tokens.push(last);
357
+ }
358
+
359
+ return {
360
+ lastIndex: tokens.length ? tokens[tokens.length - 1].end : lastIndex,
361
+ result: acc.result.concat(tokens)
362
+ };
363
+ }, {
364
+ result: [],
365
+ lastIndex: 0
366
+ });
367
+ return out.result;
368
+ };
369
+
370
+ const clearSelection = () => {
371
+ if (document.getSelection) {
372
+ // for all new browsers (IE9+, Chrome, Firefox)
373
+ document.getSelection().removeAllRanges();
374
+ document.getSelection().addRange(document.createRange());
375
+ } else if (window.getSelection) {
376
+ // equals with the document.getSelection (MSDN info)
377
+ if (window.getSelection().removeAllRanges) {
378
+ // for all new browsers (IE9+, Chrome, Firefox)
379
+ window.getSelection().removeAllRanges();
380
+ window.getSelection().addRange(document.createRange());
381
+ } else if (window.getSelection().empty) {
382
+ // Chrome supports this as well
383
+ window.getSelection().empty();
384
+ }
385
+ } else if (document.selection) {
386
+ // IE8-
387
+ document.selection.empty();
388
+ }
389
+ };
390
+ const getCaretCharacterOffsetWithin = element => {
391
+ var caretOffset = 0;
392
+ var doc = element.ownerDocument || element.document;
393
+ var win = doc.defaultView || doc.parentWindow;
394
+ var sel;
395
+
396
+ if (typeof win.getSelection !== 'undefined') {
397
+ sel = win.getSelection();
398
+
399
+ if (sel.rangeCount > 0) {
400
+ var range = win.getSelection().getRangeAt(0);
401
+ var selected = range.toString().length;
402
+ var preCaretRange = range.cloneRange();
403
+ preCaretRange.selectNodeContents(element);
404
+ preCaretRange.setEnd(range.endContainer, range.endOffset);
405
+
406
+ if (selected) {
407
+ caretOffset = preCaretRange.toString().length - selected;
408
+ } else {
409
+ caretOffset = preCaretRange.toString().length;
410
+ }
411
+ }
412
+ } else if ((sel = doc.selection) && sel.type !== 'Control') {
413
+ var textRange = sel.createRange();
414
+ var preCaretTextRange = doc.body.createTextRange();
415
+ preCaretTextRange.moveToElementText(element);
416
+ preCaretTextRange.setEndPoint('EndToEnd', textRange);
417
+ caretOffset = preCaretTextRange.text.length;
418
+ }
419
+
420
+ return caretOffset;
421
+ };
422
+
423
+ const log$2 = debug('@pie-lib:text-select:token-text');
424
+ const Text = withStyles(() => ({
425
+ predefined: {
426
+ cursor: 'pointer',
427
+ backgroundColor: yellow[100],
428
+ border: `dashed 0px ${yellow[700]}`,
429
+ // we need this for nested tokenized elements like paragraphs, where p is inside span
430
+ '& *': {
431
+ cursor: 'pointer',
432
+ backgroundColor: yellow[100],
433
+ border: `dashed 0px ${yellow[700]}`
434
+ }
435
+ },
436
+ correct: {
437
+ backgroundColor: green[500],
438
+ '& *': {
439
+ backgroundColor: green[500]
440
+ }
441
+ }
442
+ }))(({
443
+ text,
444
+ predefined,
445
+ classes,
446
+ onClick,
447
+ correct
448
+ }) => {
449
+ const formattedText = (text || '').replace(/\n/g, '<br>');
450
+
451
+ if (predefined) {
452
+ const className = classNames(classes.predefined, correct && classes.correct);
453
+ return /*#__PURE__*/React.createElement("span", {
454
+ onClick: onClick,
455
+ className: className,
456
+ dangerouslySetInnerHTML: {
457
+ __html: formattedText
458
+ }
459
+ });
460
+ } else {
461
+ return /*#__PURE__*/React.createElement("span", {
462
+ dangerouslySetInnerHTML: {
463
+ __html: formattedText
464
+ }
465
+ });
466
+ }
467
+ });
468
+ const notAllowedCharacters = ['\n', ' ', '\t'];
469
+ class TokenText extends React.Component {
470
+ constructor(...args) {
471
+ super(...args);
472
+
473
+ this.onClick = event => {
474
+ const {
475
+ onSelectToken,
476
+ text,
477
+ tokens
478
+ } = this.props;
479
+ event.preventDefault();
480
+
481
+ if (typeof window === 'undefined') {
482
+ return;
483
+ }
484
+
485
+ const selection = window.getSelection();
486
+ const textSelected = selection.toString();
487
+
488
+ if (textSelected.length > 0 && notAllowedCharacters.indexOf(textSelected) < 0) {
489
+ if (this.root) {
490
+ let offset = getCaretCharacterOffsetWithin(this.root);
491
+ /*
492
+ Since we implemented new line functionality (\n) using <br /> dom elements
493
+ and window.getSelection is not taking that into consideration, the offset might
494
+ be off by a few characters.
495
+ To combat that, we check if the selected text is right at the beginning of the offset.
496
+ If it's not, we add the additional offset in order for that to be accurate
497
+ */
498
+
499
+ const newLineOffset = text.slice(offset).indexOf(textSelected);
500
+ offset += newLineOffset;
501
+
502
+ if (offset !== undefined) {
503
+ const endIndex = offset + textSelected.length;
504
+
505
+ if (endIndex <= text.length) {
506
+ const i = intersection({
507
+ start: offset,
508
+ end: endIndex
509
+ }, tokens);
510
+
511
+ if (i.hasOverlap) {
512
+ log$2('hasOverlap - do nothing');
513
+ clearSelection();
514
+ } else {
515
+ const tokensToRemove = i.surroundedTokens;
516
+ const token = {
517
+ text: textSelected,
518
+ start: offset,
519
+ end: endIndex
520
+ };
521
+ onSelectToken(token, tokensToRemove);
522
+ clearSelection();
523
+ }
524
+ }
525
+ }
526
+ }
527
+ }
528
+ };
529
+ }
530
+
531
+ render() {
532
+ const {
533
+ text,
534
+ tokens,
535
+ className,
536
+ onTokenClick
537
+ } = this.props;
538
+ const normalized = normalize(text, tokens);
539
+ return /*#__PURE__*/React.createElement("div", {
540
+ className: className,
541
+ ref: r => this.root = r,
542
+ onClick: this.onClick
543
+ }, normalized.map((t, index) => {
544
+ return /*#__PURE__*/React.createElement(Text, _extends({
545
+ key: index
546
+ }, t, {
547
+ onClick: () => onTokenClick(t)
548
+ }));
549
+ }));
550
+ }
551
+
552
+ }
553
+ TokenText.propTypes = {
554
+ text: PropTypes.string.isRequired,
555
+ tokens: PropTypes.array.isRequired,
556
+ onTokenClick: PropTypes.func.isRequired,
557
+ onSelectToken: PropTypes.func.isRequired,
558
+ className: PropTypes.string
559
+ };
560
+
561
+ class Tokenizer extends React.Component {
562
+ constructor(props) {
563
+ super(props);
564
+
565
+ this.onChangeHandler = (token, mode) => {
566
+ this.props.onChange(token, mode);
567
+ this.setState({
568
+ mode
569
+ });
570
+ };
571
+
572
+ this.toggleCorrectMode = () => this.setState({
573
+ setCorrectMode: !this.state.setCorrectMode
574
+ });
575
+
576
+ this.clear = () => {
577
+ this.onChangeHandler([], '');
578
+ };
579
+
580
+ this.buildTokens = (type, fn) => {
581
+ const {
582
+ text
583
+ } = this.props;
584
+ const tokens = fn(text);
585
+ this.onChangeHandler(tokens, type);
586
+ };
587
+
588
+ this.selectToken = (newToken, tokensToRemove) => {
589
+ const {
590
+ tokens
591
+ } = this.props;
592
+ const update = differenceWith(clone(tokens), tokensToRemove, isEqual);
593
+ update.push(newToken);
594
+ this.onChangeHandler(update, this.state.mode);
595
+ };
596
+
597
+ this.tokenClick = token => {
598
+ const {
599
+ setCorrectMode
600
+ } = this.state;
601
+
602
+ if (setCorrectMode) {
603
+ this.setCorrect(token);
604
+ } else {
605
+ this.removeToken(token);
606
+ }
607
+ };
608
+
609
+ this.tokenIndex = token => {
610
+ const {
611
+ tokens
612
+ } = this.props;
613
+ return tokens.findIndex(t => {
614
+ return t.text == token.text && t.start == token.start && t.end == token.end;
615
+ });
616
+ };
617
+
618
+ this.setCorrect = token => {
619
+ const {
620
+ tokens
621
+ } = this.props;
622
+ const index = this.tokenIndex(token);
623
+
624
+ if (index !== -1) {
625
+ const t = tokens[index];
626
+ t.correct = !t.correct;
627
+ const update = clone(tokens);
628
+ update.splice(index, 1, t);
629
+ this.onChangeHandler(update, this.state.mode);
630
+ }
631
+ };
632
+
633
+ this.removeToken = token => {
634
+ const {
635
+ tokens
636
+ } = this.props;
637
+ const index = this.tokenIndex(token);
638
+
639
+ if (index !== -1) {
640
+ const update = clone(tokens);
641
+ update.splice(index, 1);
642
+ this.onChangeHandler(update, this.state.mode);
643
+ }
644
+ };
645
+
646
+ this.state = {
647
+ setCorrectMode: false,
648
+ mode: ''
649
+ };
650
+ }
651
+
652
+ render() {
653
+ const {
654
+ text,
655
+ tokens,
656
+ classes,
657
+ className
658
+ } = this.props;
659
+ const {
660
+ setCorrectMode
661
+ } = this.state;
662
+ const tokenClassName = classNames(classes.text, setCorrectMode && classes.noselect);
663
+ const rootName = classNames(classes.tokenizer, className);
664
+ return /*#__PURE__*/React.createElement("div", {
665
+ className: rootName
666
+ }, /*#__PURE__*/React.createElement(Controls$1, {
667
+ onClear: this.clear,
668
+ onWords: () => this.buildTokens('words', words),
669
+ onSentences: () => this.buildTokens('sentence', sentences),
670
+ onParagraphs: () => this.buildTokens('paragraphs', paragraphs),
671
+ setCorrectMode: setCorrectMode,
672
+ onToggleCorrectMode: this.toggleCorrectMode
673
+ }), /*#__PURE__*/React.createElement(TokenText, {
674
+ className: tokenClassName,
675
+ text: text,
676
+ tokens: tokens,
677
+ onTokenClick: this.tokenClick,
678
+ onSelectToken: this.selectToken
679
+ }));
680
+ }
681
+
682
+ }
683
+ Tokenizer.propTypes = {
684
+ text: PropTypes.string.isRequired,
685
+ tokens: PropTypes.arrayOf(PropTypes.shape({
686
+ text: PropTypes.string,
687
+ correct: PropTypes.bool,
688
+ start: PropTypes.number,
689
+ end: PropTypes.number
690
+ })),
691
+ classes: PropTypes.object.isRequired,
692
+ className: PropTypes.string,
693
+ onChange: PropTypes.func.isRequired
694
+ };
695
+ Tokenizer.defaultProps = {};
696
+ var index = withStyles(() => ({
697
+ text: {
698
+ whiteSpace: 'pre-wrap'
699
+ },
700
+ noselect: _extends({}, noSelect())
701
+ }))(Tokenizer);
702
+
703
+ function getDefaultExportFromCjs (x) {
704
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
705
+ }
706
+
707
+ var Check$1 = {};
708
+
709
+ var interopRequireDefault = {exports: {}};
710
+
711
+ var hasRequiredInteropRequireDefault;
712
+
713
+ function requireInteropRequireDefault () {
714
+ if (hasRequiredInteropRequireDefault) return interopRequireDefault.exports;
715
+ hasRequiredInteropRequireDefault = 1;
716
+ (function (module) {
717
+ function _interopRequireDefault(obj) {
718
+ return obj && obj.__esModule ? obj : {
719
+ "default": obj
720
+ };
721
+ }
722
+
723
+ module.exports = _interopRequireDefault, module.exports.__esModule = true, module.exports["default"] = module.exports;
724
+ } (interopRequireDefault));
725
+ return interopRequireDefault.exports;
726
+ }
727
+
728
+ var createSvgIcon = {};
729
+
730
+ var pure = {};
731
+
732
+ var shouldUpdate = {};
733
+
734
+ var inheritsLoose = {exports: {}};
735
+
736
+ var setPrototypeOf = {exports: {}};
737
+
738
+ var hasRequiredSetPrototypeOf;
739
+
740
+ function requireSetPrototypeOf () {
741
+ if (hasRequiredSetPrototypeOf) return setPrototypeOf.exports;
742
+ hasRequiredSetPrototypeOf = 1;
743
+ (function (module) {
744
+ function _setPrototypeOf(o, p) {
745
+ module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
746
+ o.__proto__ = p;
747
+ return o;
748
+ }, module.exports.__esModule = true, module.exports["default"] = module.exports;
749
+ return _setPrototypeOf(o, p);
750
+ }
751
+
752
+ module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports;
753
+ } (setPrototypeOf));
754
+ return setPrototypeOf.exports;
755
+ }
756
+
757
+ var hasRequiredInheritsLoose;
758
+
759
+ function requireInheritsLoose () {
760
+ if (hasRequiredInheritsLoose) return inheritsLoose.exports;
761
+ hasRequiredInheritsLoose = 1;
762
+ (function (module) {
763
+ var setPrototypeOf = requireSetPrototypeOf();
764
+
765
+ function _inheritsLoose(subClass, superClass) {
766
+ subClass.prototype = Object.create(superClass.prototype);
767
+ subClass.prototype.constructor = subClass;
768
+ setPrototypeOf(subClass, superClass);
769
+ }
770
+
771
+ module.exports = _inheritsLoose, module.exports.__esModule = true, module.exports["default"] = module.exports;
772
+ } (inheritsLoose));
773
+ return inheritsLoose.exports;
774
+ }
775
+
776
+ var setDisplayName = {};
777
+
778
+ var setStatic = {};
779
+
780
+ var hasRequiredSetStatic;
781
+
782
+ function requireSetStatic () {
783
+ if (hasRequiredSetStatic) return setStatic;
784
+ hasRequiredSetStatic = 1;
785
+
786
+ setStatic.__esModule = true;
787
+ setStatic.default = void 0;
788
+
789
+ var setStatic$1 = function setStatic(key, value) {
790
+ return function (BaseComponent) {
791
+ /* eslint-disable no-param-reassign */
792
+ BaseComponent[key] = value;
793
+ /* eslint-enable no-param-reassign */
794
+
795
+ return BaseComponent;
796
+ };
797
+ };
798
+
799
+ var _default = setStatic$1;
800
+ setStatic.default = _default;
801
+ return setStatic;
802
+ }
803
+
804
+ var hasRequiredSetDisplayName;
805
+
806
+ function requireSetDisplayName () {
807
+ if (hasRequiredSetDisplayName) return setDisplayName;
808
+ hasRequiredSetDisplayName = 1;
809
+
810
+ var _interopRequireDefault = requireInteropRequireDefault();
811
+
812
+ setDisplayName.__esModule = true;
813
+ setDisplayName.default = void 0;
814
+
815
+ var _setStatic = _interopRequireDefault(requireSetStatic());
816
+
817
+ var setDisplayName$1 = function setDisplayName(displayName) {
818
+ return (0, _setStatic.default)('displayName', displayName);
819
+ };
820
+
821
+ var _default = setDisplayName$1;
822
+ setDisplayName.default = _default;
823
+ return setDisplayName;
824
+ }
825
+
826
+ var wrapDisplayName = {};
827
+
828
+ var getDisplayName = {};
829
+
830
+ var hasRequiredGetDisplayName;
831
+
832
+ function requireGetDisplayName () {
833
+ if (hasRequiredGetDisplayName) return getDisplayName;
834
+ hasRequiredGetDisplayName = 1;
835
+
836
+ getDisplayName.__esModule = true;
837
+ getDisplayName.default = void 0;
838
+
839
+ var getDisplayName$1 = function getDisplayName(Component) {
840
+ if (typeof Component === 'string') {
841
+ return Component;
842
+ }
843
+
844
+ if (!Component) {
845
+ return undefined;
846
+ }
847
+
848
+ return Component.displayName || Component.name || 'Component';
849
+ };
850
+
851
+ var _default = getDisplayName$1;
852
+ getDisplayName.default = _default;
853
+ return getDisplayName;
854
+ }
855
+
856
+ var hasRequiredWrapDisplayName;
857
+
858
+ function requireWrapDisplayName () {
859
+ if (hasRequiredWrapDisplayName) return wrapDisplayName;
860
+ hasRequiredWrapDisplayName = 1;
861
+
862
+ var _interopRequireDefault = requireInteropRequireDefault();
863
+
864
+ wrapDisplayName.__esModule = true;
865
+ wrapDisplayName.default = void 0;
866
+
867
+ var _getDisplayName = _interopRequireDefault(requireGetDisplayName());
868
+
869
+ var wrapDisplayName$1 = function wrapDisplayName(BaseComponent, hocName) {
870
+ return hocName + "(" + (0, _getDisplayName.default)(BaseComponent) + ")";
871
+ };
872
+
873
+ var _default = wrapDisplayName$1;
874
+ wrapDisplayName.default = _default;
875
+ return wrapDisplayName;
876
+ }
877
+
878
+ var hasRequiredShouldUpdate;
879
+
880
+ function requireShouldUpdate () {
881
+ if (hasRequiredShouldUpdate) return shouldUpdate;
882
+ hasRequiredShouldUpdate = 1;
883
+
884
+ var _interopRequireDefault = requireInteropRequireDefault();
885
+
886
+ shouldUpdate.__esModule = true;
887
+ shouldUpdate.default = void 0;
888
+
889
+ var _inheritsLoose2 = _interopRequireDefault(requireInheritsLoose());
890
+
891
+ var _react = React;
892
+
893
+ var _setDisplayName = _interopRequireDefault(requireSetDisplayName());
894
+
895
+ var _wrapDisplayName = _interopRequireDefault(requireWrapDisplayName());
896
+
897
+ var shouldUpdate$1 = function shouldUpdate(test) {
898
+ return function (BaseComponent) {
899
+ var factory = (0, _react.createFactory)(BaseComponent);
900
+
901
+ var ShouldUpdate =
902
+ /*#__PURE__*/
903
+ function (_Component) {
904
+ (0, _inheritsLoose2.default)(ShouldUpdate, _Component);
905
+
906
+ function ShouldUpdate() {
907
+ return _Component.apply(this, arguments) || this;
908
+ }
909
+
910
+ var _proto = ShouldUpdate.prototype;
911
+
912
+ _proto.shouldComponentUpdate = function shouldComponentUpdate(nextProps) {
913
+ return test(this.props, nextProps);
914
+ };
915
+
916
+ _proto.render = function render() {
917
+ return factory(this.props);
918
+ };
919
+
920
+ return ShouldUpdate;
921
+ }(_react.Component);
922
+
923
+ if (process.env.NODE_ENV !== 'production') {
924
+ return (0, _setDisplayName.default)((0, _wrapDisplayName.default)(BaseComponent, 'shouldUpdate'))(ShouldUpdate);
925
+ }
926
+
927
+ return ShouldUpdate;
928
+ };
929
+ };
930
+
931
+ var _default = shouldUpdate$1;
932
+ shouldUpdate.default = _default;
933
+ return shouldUpdate;
934
+ }
935
+
936
+ var shallowEqual = {};
937
+
938
+ /**
939
+ * Copyright (c) 2013-present, Facebook, Inc.
940
+ *
941
+ * This source code is licensed under the MIT license found in the
942
+ * LICENSE file in the root directory of this source tree.
943
+ *
944
+ * @typechecks
945
+ *
946
+ */
947
+
948
+ var shallowEqual_1;
949
+ var hasRequiredShallowEqual$1;
950
+
951
+ function requireShallowEqual$1 () {
952
+ if (hasRequiredShallowEqual$1) return shallowEqual_1;
953
+ hasRequiredShallowEqual$1 = 1;
954
+
955
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
956
+
957
+ /**
958
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
959
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
960
+ */
961
+ function is(x, y) {
962
+ // SameValue algorithm
963
+ if (x === y) {
964
+ // Steps 1-5, 7-10
965
+ // Steps 6.b-6.e: +0 != -0
966
+ // Added the nonzero y check to make Flow happy, but it is redundant
967
+ return x !== 0 || y !== 0 || 1 / x === 1 / y;
968
+ } else {
969
+ // Step 6.a: NaN == NaN
970
+ return x !== x && y !== y;
971
+ }
972
+ }
973
+
974
+ /**
975
+ * Performs equality by iterating through keys on an object and returning false
976
+ * when any key has values which are not strictly equal between the arguments.
977
+ * Returns true when the values of all keys are strictly equal.
978
+ */
979
+ function shallowEqual(objA, objB) {
980
+ if (is(objA, objB)) {
981
+ return true;
982
+ }
983
+
984
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
985
+ return false;
986
+ }
987
+
988
+ var keysA = Object.keys(objA);
989
+ var keysB = Object.keys(objB);
990
+
991
+ if (keysA.length !== keysB.length) {
992
+ return false;
993
+ }
994
+
995
+ // Test for A's keys different from B.
996
+ for (var i = 0; i < keysA.length; i++) {
997
+ if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
998
+ return false;
999
+ }
1000
+ }
1001
+
1002
+ return true;
1003
+ }
1004
+
1005
+ shallowEqual_1 = shallowEqual;
1006
+ return shallowEqual_1;
1007
+ }
1008
+
1009
+ var hasRequiredShallowEqual;
1010
+
1011
+ function requireShallowEqual () {
1012
+ if (hasRequiredShallowEqual) return shallowEqual;
1013
+ hasRequiredShallowEqual = 1;
1014
+
1015
+ var _interopRequireDefault = requireInteropRequireDefault();
1016
+
1017
+ shallowEqual.__esModule = true;
1018
+ shallowEqual.default = void 0;
1019
+
1020
+ var _shallowEqual = _interopRequireDefault(requireShallowEqual$1());
1021
+
1022
+ var _default = _shallowEqual.default;
1023
+ shallowEqual.default = _default;
1024
+ return shallowEqual;
1025
+ }
1026
+
1027
+ var hasRequiredPure;
1028
+
1029
+ function requirePure () {
1030
+ if (hasRequiredPure) return pure;
1031
+ hasRequiredPure = 1;
1032
+
1033
+ var _interopRequireDefault = requireInteropRequireDefault();
1034
+
1035
+ pure.__esModule = true;
1036
+ pure.default = void 0;
1037
+
1038
+ var _shouldUpdate = _interopRequireDefault(requireShouldUpdate());
1039
+
1040
+ var _shallowEqual = _interopRequireDefault(requireShallowEqual());
1041
+
1042
+ var _setDisplayName = _interopRequireDefault(requireSetDisplayName());
1043
+
1044
+ var _wrapDisplayName = _interopRequireDefault(requireWrapDisplayName());
1045
+
1046
+ var pure$1 = function pure(BaseComponent) {
1047
+ var hoc = (0, _shouldUpdate.default)(function (props, nextProps) {
1048
+ return !(0, _shallowEqual.default)(props, nextProps);
1049
+ });
1050
+
1051
+ if (process.env.NODE_ENV !== 'production') {
1052
+ return (0, _setDisplayName.default)((0, _wrapDisplayName.default)(BaseComponent, 'pure'))(hoc(BaseComponent));
1053
+ }
1054
+
1055
+ return hoc(BaseComponent);
1056
+ };
1057
+
1058
+ var _default = pure$1;
1059
+ pure.default = _default;
1060
+ return pure;
1061
+ }
1062
+
1063
+ var hasRequiredCreateSvgIcon;
1064
+
1065
+ function requireCreateSvgIcon () {
1066
+ if (hasRequiredCreateSvgIcon) return createSvgIcon;
1067
+ hasRequiredCreateSvgIcon = 1;
1068
+
1069
+ var _interopRequireDefault = requireInteropRequireDefault();
1070
+
1071
+ Object.defineProperty(createSvgIcon, "__esModule", {
1072
+ value: true
1073
+ });
1074
+ createSvgIcon.default = void 0;
1075
+
1076
+ var _react = _interopRequireDefault(React);
1077
+
1078
+ var _pure = _interopRequireDefault(requirePure());
1079
+
1080
+ var _SvgIcon = _interopRequireDefault(require$$3);
1081
+
1082
+ function createSvgIcon$1(path, displayName) {
1083
+ var Icon = function Icon(props) {
1084
+ return _react.default.createElement(_SvgIcon.default, props, path);
1085
+ };
1086
+
1087
+ Icon.displayName = "".concat(displayName, "Icon");
1088
+ Icon = (0, _pure.default)(Icon);
1089
+ Icon.muiName = 'SvgIcon';
1090
+ return Icon;
1091
+ }
1092
+ var _default = createSvgIcon$1;
1093
+ createSvgIcon.default = _default;
1094
+ return createSvgIcon;
1095
+ }
1096
+
1097
+ var hasRequiredCheck;
1098
+
1099
+ function requireCheck () {
1100
+ if (hasRequiredCheck) return Check$1;
1101
+ hasRequiredCheck = 1;
1102
+
1103
+ var _interopRequireDefault = requireInteropRequireDefault();
1104
+
1105
+ Object.defineProperty(Check$1, "__esModule", {
1106
+ value: true
1107
+ });
1108
+ Check$1.default = void 0;
1109
+
1110
+ var _react = _interopRequireDefault(React);
1111
+
1112
+ var _createSvgIcon = _interopRequireDefault(/*@__PURE__*/ requireCreateSvgIcon());
1113
+
1114
+ var _default = (0, _createSvgIcon.default)(_react.default.createElement(_react.default.Fragment, null, _react.default.createElement("path", {
1115
+ fill: "none",
1116
+ d: "M0 0h24v24H0z"
1117
+ }), _react.default.createElement("path", {
1118
+ d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
1119
+ })), 'Check');
1120
+
1121
+ Check$1.default = _default;
1122
+ return Check$1;
1123
+ }
1124
+
1125
+ var CheckExports = /*@__PURE__*/ requireCheck();
1126
+ var Check = /*@__PURE__*/getDefaultExportFromCjs(CheckExports);
1127
+
1128
+ var Close$1 = {};
1129
+
1130
+ var hasRequiredClose;
1131
+
1132
+ function requireClose () {
1133
+ if (hasRequiredClose) return Close$1;
1134
+ hasRequiredClose = 1;
1135
+
1136
+ var _interopRequireDefault = requireInteropRequireDefault();
1137
+
1138
+ Object.defineProperty(Close$1, "__esModule", {
1139
+ value: true
1140
+ });
1141
+ Close$1.default = void 0;
1142
+
1143
+ var _react = _interopRequireDefault(React);
1144
+
1145
+ var _createSvgIcon = _interopRequireDefault(/*@__PURE__*/ requireCreateSvgIcon());
1146
+
1147
+ var _default = (0, _createSvgIcon.default)(_react.default.createElement(_react.default.Fragment, null, _react.default.createElement("path", {
1148
+ d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
1149
+ }), _react.default.createElement("path", {
1150
+ fill: "none",
1151
+ d: "M0 0h24v24H0z"
1152
+ })), 'Close');
1153
+
1154
+ Close$1.default = _default;
1155
+ return Close$1;
1156
+ }
1157
+
1158
+ var CloseExports = /*@__PURE__*/ requireClose();
1159
+ var Close = /*@__PURE__*/getDefaultExportFromCjs(CloseExports);
1160
+
1161
+ const LINE_HEIGHT_MULTIPLIER = 3.2; // we need a bit more space for correctness indicators
1162
+
1163
+ const CORRECTNESS_LINE_HEIGHT_MULTIPLIER = 3.4;
1164
+ const CORRECTNESS_PADDING = 2;
1165
+
1166
+ const Wrapper = ({
1167
+ useWrapper,
1168
+ children,
1169
+ classNameContainer,
1170
+ iconClass,
1171
+ Icon
1172
+ }) => useWrapper ? /*#__PURE__*/React.createElement("span", {
1173
+ className: classNameContainer
1174
+ }, children, /*#__PURE__*/React.createElement(Icon, {
1175
+ className: iconClass
1176
+ })) : children;
1177
+
1178
+ Wrapper.propTypes = {
1179
+ useWrapper: PropTypes.bool,
1180
+ classNameContainer: PropTypes.string,
1181
+ iconClass: PropTypes.string,
1182
+ Icon: PropTypes.func,
1183
+ children: PropTypes.element
1184
+ };
1185
+ const TokenTypes = {
1186
+ text: PropTypes.string,
1187
+ selectable: PropTypes.bool
1188
+ };
1189
+ class Token extends React.Component {
1190
+ constructor(...args) {
1191
+ super(...args);
1192
+
1193
+ this.getClassAndIconConfig = () => {
1194
+ const {
1195
+ selectable,
1196
+ selected,
1197
+ classes,
1198
+ className: classNameProp,
1199
+ disabled,
1200
+ highlight,
1201
+ correct,
1202
+ animationsDisabled,
1203
+ isMissing
1204
+ } = this.props;
1205
+ const isTouchEnabled = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
1206
+ const baseClassName = Token.rootClassName;
1207
+ let classNameContainer;
1208
+ let Icon;
1209
+ let iconClass;
1210
+
1211
+ if (correct === undefined && selected && disabled) {
1212
+ return {
1213
+ className: classNames(classes.token, classes.selected, classes.disabledBlack)
1214
+ };
1215
+ }
1216
+
1217
+ if (correct !== undefined) {
1218
+ const isCorrect = correct === true;
1219
+ return {
1220
+ className: classNames(baseClassName, classes.custom),
1221
+ classNameContainer: classNames(isCorrect ? classes.correct : classes.incorrect, classes.commonTokenStyle),
1222
+ Icon: isCorrect ? Check : Close,
1223
+ iconClass: classNames(classes.correctnessIndicatorIcon, isCorrect ? classes.correctIcon : classes.incorrectIcon)
1224
+ };
1225
+ }
1226
+
1227
+ if (isMissing) {
1228
+ return {
1229
+ className: classNames(baseClassName, classes.custom, classes.missing, classes.commonTokenStyle),
1230
+ classNameContainer: classes.commonTokenStyle,
1231
+ Icon: Close,
1232
+ iconClass: classNames(classes.correctnessIndicatorIcon, classes.incorrectIcon)
1233
+ };
1234
+ }
1235
+
1236
+ return {
1237
+ className: classNames(baseClassName, classes.token, disabled && classes.disabled, selectable && !disabled && !isTouchEnabled && classes.selectable, selected && !disabled && classes.selected, selected && disabled && classes.disabledAndSelected, highlight && selectable && !disabled && !selected && classes.highlight, animationsDisabled && classes.print, classNameProp),
1238
+ classNameContainer,
1239
+ Icon,
1240
+ iconClass
1241
+ };
1242
+ };
1243
+ }
1244
+
1245
+ render() {
1246
+ const {
1247
+ text,
1248
+ index,
1249
+ correct,
1250
+ isMissing
1251
+ } = this.props;
1252
+ const {
1253
+ className,
1254
+ classNameContainer,
1255
+ Icon,
1256
+ iconClass
1257
+ } = this.getClassAndIconConfig();
1258
+ return /*#__PURE__*/React.createElement(Wrapper, {
1259
+ useWrapper: correct !== undefined || isMissing,
1260
+ classNameContainer: classNameContainer,
1261
+ iconClass: iconClass,
1262
+ Icon: Icon
1263
+ }, /*#__PURE__*/React.createElement("span", {
1264
+ className: className,
1265
+ dangerouslySetInnerHTML: {
1266
+ __html: (text || '').replace(/\n/g, '<br>')
1267
+ },
1268
+ "data-indexkey": index
1269
+ }));
1270
+ }
1271
+
1272
+ }
1273
+ Token.rootClassName = 'tokenRootClass';
1274
+ Token.propTypes = _extends({}, TokenTypes, {
1275
+ classes: PropTypes.object.isRequired,
1276
+ text: PropTypes.string.isRequired,
1277
+ className: PropTypes.string,
1278
+ disabled: PropTypes.bool,
1279
+ highlight: PropTypes.bool,
1280
+ correct: PropTypes.bool
1281
+ });
1282
+ Token.defaultProps = {
1283
+ selectable: false,
1284
+ text: ''
1285
+ };
1286
+ var Token$1 = withStyles(theme => {
1287
+ return {
1288
+ token: {
1289
+ cursor: 'pointer',
1290
+ textIndent: 0
1291
+ },
1292
+ disabled: {
1293
+ cursor: 'inherit',
1294
+ color: color.disabled()
1295
+ },
1296
+ disabledBlack: {
1297
+ cursor: 'inherit'
1298
+ },
1299
+ disabledAndSelected: {
1300
+ backgroundColor: color.blueGrey100()
1301
+ },
1302
+ selectable: {
1303
+ [theme.breakpoints.up(769)]: {
1304
+ '&:hover': {
1305
+ backgroundColor: color.blueGrey300(),
1306
+ color: theme.palette.common.black,
1307
+ '& > *': {
1308
+ backgroundColor: color.blueGrey300()
1309
+ }
1310
+ }
1311
+ }
1312
+ },
1313
+ selected: {
1314
+ backgroundColor: color.blueGrey100(),
1315
+ color: theme.palette.common.black,
1316
+ lineHeight: `${theme.spacing.unit * LINE_HEIGHT_MULTIPLIER}px`,
1317
+ border: `solid 2px ${color.blueGrey900()}`,
1318
+ borderRadius: '4px',
1319
+ '& > *': {
1320
+ backgroundColor: color.blueGrey100()
1321
+ }
1322
+ },
1323
+ highlight: {
1324
+ border: `dashed 2px ${color.blueGrey600()}`,
1325
+ borderRadius: '4px',
1326
+ lineHeight: `${theme.spacing.unit * LINE_HEIGHT_MULTIPLIER}px`
1327
+ },
1328
+ print: {
1329
+ border: `dashed 2px ${color.blueGrey600()}`,
1330
+ borderRadius: '4px',
1331
+ lineHeight: `${theme.spacing.unit * LINE_HEIGHT_MULTIPLIER}px`,
1332
+ color: color.text()
1333
+ },
1334
+ custom: {
1335
+ display: 'initial'
1336
+ },
1337
+ commonTokenStyle: {
1338
+ position: 'relative',
1339
+ borderRadius: '4px',
1340
+ color: theme.palette.common.black,
1341
+ lineHeight: `${theme.spacing.unit * CORRECTNESS_LINE_HEIGHT_MULTIPLIER + CORRECTNESS_PADDING}px`,
1342
+ padding: `${CORRECTNESS_PADDING}px`
1343
+ },
1344
+ correct: {
1345
+ border: `${color.correctTertiary()} solid 2px`
1346
+ },
1347
+ incorrect: {
1348
+ border: `${color.incorrectWithIcon()} solid 2px`
1349
+ },
1350
+ missing: {
1351
+ border: `${color.incorrectWithIcon()} dashed 2px`
1352
+ },
1353
+ incorrectIcon: {
1354
+ backgroundColor: color.incorrectWithIcon()
1355
+ },
1356
+ correctIcon: {
1357
+ backgroundColor: color.correctTertiary()
1358
+ },
1359
+ correctnessIndicatorIcon: {
1360
+ color: color.white(),
1361
+ position: 'absolute',
1362
+ top: '-8px',
1363
+ left: '-8px',
1364
+ borderRadius: '50%',
1365
+ fontSize: '12px',
1366
+ padding: '2px'
1367
+ }
1368
+ };
1369
+ })(Token);
1370
+
1371
+ const log$1 = debug('@pie-lib:text-select:token-select');
1372
+ class TokenSelect extends React.Component {
1373
+ constructor(...args) {
1374
+ super(...args);
1375
+
1376
+ this.selectedCount = () => this.props.tokens.filter(t => t.selected).length;
1377
+
1378
+ this.canSelectMore = selectedCount => {
1379
+ const {
1380
+ maxNoOfSelections
1381
+ } = this.props;
1382
+
1383
+ if (maxNoOfSelections === 1) {
1384
+ return true;
1385
+ }
1386
+
1387
+ log$1('[canSelectMore] maxNoOfSelections: ', maxNoOfSelections, 'selectedCount: ', selectedCount);
1388
+ return maxNoOfSelections <= 0 || isFinite(maxNoOfSelections) && selectedCount < maxNoOfSelections;
1389
+ };
1390
+
1391
+ this.toggleToken = event => {
1392
+ const {
1393
+ target
1394
+ } = event;
1395
+ const {
1396
+ tokens,
1397
+ animationsDisabled
1398
+ } = this.props;
1399
+ const tokensCloned = clone(tokens);
1400
+ const targetSpanWrapper = target.closest(`.${Token$1.rootClassName}`);
1401
+ const targetedTokenIndex = targetSpanWrapper && targetSpanWrapper.dataset && targetSpanWrapper.dataset.indexkey;
1402
+ const t = targetedTokenIndex && tokensCloned[targetedTokenIndex]; // don't toggle if we are in print mode, token correctness is defined or if it's missing
1403
+ // (missing means that it was evaluated as correct and not selected)
1404
+
1405
+ if (t && t.correct === undefined && !animationsDisabled && !t.isMissing) {
1406
+ const {
1407
+ onChange,
1408
+ maxNoOfSelections
1409
+ } = this.props;
1410
+ const selected = !t.selected;
1411
+
1412
+ if (maxNoOfSelections === 1 && this.selectedCount() === 1) {
1413
+ const selectedToken = (tokens || []).filter(t => t.selected);
1414
+ const updatedTokens = tokensCloned.map(token => {
1415
+ if (isEqual(token, selectedToken[0])) {
1416
+ return _extends({}, token, {
1417
+ selected: false
1418
+ });
1419
+ }
1420
+
1421
+ return _extends({}, token, {
1422
+ selectable: true
1423
+ });
1424
+ });
1425
+
1426
+ const update = _extends({}, t, {
1427
+ selected: !t.selected
1428
+ });
1429
+
1430
+ updatedTokens.splice(targetedTokenIndex, 1, update);
1431
+ onChange(updatedTokens);
1432
+ } else {
1433
+ if (selected && maxNoOfSelections > 0 && this.selectedCount() >= maxNoOfSelections) {
1434
+ log$1('skip toggle max reached');
1435
+ return;
1436
+ }
1437
+
1438
+ const update = _extends({}, t, {
1439
+ selected: !t.selected
1440
+ });
1441
+
1442
+ tokensCloned.splice(targetedTokenIndex, 1, update);
1443
+ onChange(tokensCloned);
1444
+ }
1445
+ }
1446
+ };
1447
+
1448
+ this.generateTokensInHtml = () => {
1449
+ const {
1450
+ tokens,
1451
+ disabled,
1452
+ highlightChoices,
1453
+ animationsDisabled
1454
+ } = this.props;
1455
+ const selectedCount = this.selectedCount();
1456
+
1457
+ const isLineBreak = text => text === '\n';
1458
+
1459
+ const isNewParagraph = text => text === '\n\n';
1460
+
1461
+ const reducer = (accumulator, t, index) => {
1462
+ const selectable = t.selected || t.selectable && this.canSelectMore(selectedCount);
1463
+ const showCorrectAnswer = t.correct !== undefined && (t.selectable || t.selected);
1464
+ let finalAcc = accumulator;
1465
+
1466
+ if (isNewParagraph(t.text)) {
1467
+ return finalAcc + '</p><p>';
1468
+ }
1469
+
1470
+ if (isLineBreak(t.text)) {
1471
+ return finalAcc + '<br>';
1472
+ }
1473
+
1474
+ if (selectable && !disabled || showCorrectAnswer || t.selected || t.isMissing || animationsDisabled && t.predefined // if we are in print mode
1475
+ ) {
1476
+ return finalAcc + renderToString( /*#__PURE__*/React.createElement(Token$1, _extends({
1477
+ key: index,
1478
+ disabled: disabled,
1479
+ index: index
1480
+ }, t, {
1481
+ selectable: selectable,
1482
+ highlight: highlightChoices,
1483
+ animationsDisabled: animationsDisabled
1484
+ })));
1485
+ } else {
1486
+ return accumulator + t.text;
1487
+ }
1488
+ };
1489
+
1490
+ const reduceResult = (tokens || []).reduce(reducer, '<p>');
1491
+ return reduceResult + '</p>';
1492
+ };
1493
+ }
1494
+
1495
+ render() {
1496
+ const {
1497
+ classes,
1498
+ className: classNameProp
1499
+ } = this.props;
1500
+ const className = classNames(classes.tokenSelect, classNameProp);
1501
+ const html = this.generateTokensInHtml();
1502
+ return /*#__PURE__*/React.createElement("div", {
1503
+ className: className,
1504
+ dangerouslySetInnerHTML: {
1505
+ __html: html
1506
+ },
1507
+ onClick: this.toggleToken
1508
+ });
1509
+ }
1510
+
1511
+ }
1512
+ TokenSelect.propTypes = {
1513
+ tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
1514
+ className: PropTypes.string,
1515
+ classes: PropTypes.object.isRequired,
1516
+ onChange: PropTypes.func.isRequired,
1517
+ disabled: PropTypes.bool,
1518
+ highlightChoices: PropTypes.bool,
1519
+ animationsDisabled: PropTypes.bool,
1520
+ maxNoOfSelections: PropTypes.number
1521
+ };
1522
+ TokenSelect.defaultProps = {
1523
+ highlightChoices: false,
1524
+ maxNoOfSelections: 0,
1525
+ tokens: []
1526
+ };
1527
+ var TokenSelect$1 = withStyles(() => ({
1528
+ tokenSelect: _extends({
1529
+ backgroundColor: 'none',
1530
+ whiteSpace: 'pre'
1531
+ }, noSelect(), {
1532
+ '& p': {
1533
+ whiteSpace: 'break-spaces'
1534
+ }
1535
+ })
1536
+ }))(TokenSelect); // Re-export TokenTypes for external use
1537
+
1538
+ const log = debug('@pie-lib:text-select');
1539
+ /**
1540
+ * Built on TokenSelect uses build.normalize to build the token set.
1541
+ */
1542
+
1543
+ class TextSelect extends React.Component {
1544
+ constructor(...args) {
1545
+ super(...args);
1546
+
1547
+ this.change = tokens => {
1548
+ const {
1549
+ onChange
1550
+ } = this.props;
1551
+
1552
+ if (!onChange) {
1553
+ return;
1554
+ }
1555
+
1556
+ const out = tokens.filter(t => t.selected).map(t => ({
1557
+ start: t.start,
1558
+ end: t.end
1559
+ }));
1560
+ onChange(out);
1561
+ };
1562
+ }
1563
+
1564
+ render() {
1565
+ const {
1566
+ text,
1567
+ disabled,
1568
+ tokens,
1569
+ selectedTokens,
1570
+ className,
1571
+ highlightChoices,
1572
+ maxNoOfSelections,
1573
+ animationsDisabled
1574
+ } = this.props;
1575
+ const normalized = normalize(text, tokens);
1576
+ log('normalized: ', normalized);
1577
+ const prepped = normalized.map(t => {
1578
+ const selectedIndex = selectedTokens.findIndex(s => {
1579
+ return s.start === t.start && s.end === t.end;
1580
+ });
1581
+ const selected = selectedIndex !== -1;
1582
+ const correct = selected ? t.correct : undefined;
1583
+ const isMissing = t.isMissing;
1584
+ return _extends({}, t, {
1585
+ selectable: !disabled && t.predefined,
1586
+ selected,
1587
+ correct,
1588
+ isMissing
1589
+ });
1590
+ });
1591
+ return /*#__PURE__*/React.createElement(TokenSelect$1, {
1592
+ highlightChoices: !disabled && highlightChoices,
1593
+ className: className,
1594
+ tokens: prepped,
1595
+ disabled: disabled,
1596
+ onChange: this.change,
1597
+ maxNoOfSelections: maxNoOfSelections,
1598
+ animationsDisabled: animationsDisabled
1599
+ });
1600
+ }
1601
+
1602
+ }
1603
+ TextSelect.propTypes = {
1604
+ onChange: PropTypes.func,
1605
+ disabled: PropTypes.bool,
1606
+ tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
1607
+ selectedTokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
1608
+ text: PropTypes.string.isRequired,
1609
+ className: PropTypes.string,
1610
+ highlightChoices: PropTypes.bool,
1611
+ animationsDisabled: PropTypes.bool,
1612
+ maxNoOfSelections: PropTypes.number
1613
+ };
1614
+
1615
+ const {
1616
+ translator
1617
+ } = Translator;
1618
+ const Legend = withStyles(theme => ({
1619
+ flexContainer: {
1620
+ display: 'flex',
1621
+ flexDirection: 'row',
1622
+ alignItems: 'center',
1623
+ gap: `${2 * theme.spacing.unit}px`,
1624
+ borderBottom: '1px solid lightgrey',
1625
+ borderTop: '1px solid lightgrey',
1626
+ paddingBottom: theme.spacing.unit,
1627
+ paddingTop: theme.spacing.unit,
1628
+ marginBottom: theme.spacing.unit
1629
+ },
1630
+ key: {
1631
+ fontSize: '14px',
1632
+ fontWeight: 'bold',
1633
+ color: color.black(),
1634
+ marginLeft: theme.spacing.unit
1635
+ },
1636
+ container: {
1637
+ position: 'relative',
1638
+ padding: '4px',
1639
+ fontSize: '14px',
1640
+ borderRadius: '4px'
1641
+ },
1642
+ correct: {
1643
+ border: `${color.correctTertiary()} solid 2px`
1644
+ },
1645
+ incorrect: {
1646
+ border: `${color.incorrectWithIcon()} solid 2px`
1647
+ },
1648
+ missing: {
1649
+ border: `${color.incorrectWithIcon()} dashed 2px`
1650
+ },
1651
+ incorrectIcon: {
1652
+ backgroundColor: color.incorrectWithIcon()
1653
+ },
1654
+ correctIcon: {
1655
+ backgroundColor: color.correctTertiary()
1656
+ },
1657
+ icon: {
1658
+ color: color.white(),
1659
+ position: 'absolute',
1660
+ top: '-8px',
1661
+ left: '-8px',
1662
+ borderRadius: '50%',
1663
+ fontSize: '12px',
1664
+ padding: '2px'
1665
+ }
1666
+ }))(({
1667
+ classes,
1668
+ language,
1669
+ showOnlyCorrect
1670
+ }) => {
1671
+ const legendItems = [{
1672
+ Icon: Check,
1673
+ label: translator.t('selectText.correctAnswerSelected', {
1674
+ lng: language
1675
+ }),
1676
+ containerClass: classNames(classes.correct, classes.container),
1677
+ iconClass: classNames(classes.correctIcon, classes.icon)
1678
+ }, {
1679
+ Icon: Close,
1680
+ label: translator.t('selectText.incorrectSelection', {
1681
+ lng: language
1682
+ }),
1683
+ containerClass: classNames(classes.incorrect, classes.container),
1684
+ iconClass: classNames(classes.incorrectIcon, classes.icon)
1685
+ }, {
1686
+ Icon: Close,
1687
+ label: translator.t('selectText.correctAnswerNotSelected', {
1688
+ lng: language
1689
+ }),
1690
+ containerClass: classNames(classes.missing, classes.container),
1691
+ iconClass: classNames(classes.incorrectIcon, classes.icon)
1692
+ }];
1693
+
1694
+ if (showOnlyCorrect) {
1695
+ legendItems.splice(1, 2);
1696
+ }
1697
+
1698
+ return /*#__PURE__*/React.createElement("div", {
1699
+ className: classes.flexContainer
1700
+ }, /*#__PURE__*/React.createElement("span", {
1701
+ className: classes.key
1702
+ }, translator.t('selectText.key', {
1703
+ lng: language
1704
+ })), legendItems.map(({
1705
+ Icon,
1706
+ label,
1707
+ containerClass,
1708
+ iconClass
1709
+ }, idx) => /*#__PURE__*/React.createElement("div", {
1710
+ key: idx,
1711
+ className: containerClass
1712
+ }, /*#__PURE__*/React.createElement(Icon, {
1713
+ className: iconClass
1714
+ }), /*#__PURE__*/React.createElement("span", null, label))));
1715
+ });
1716
+
1717
+ const createElementFromHTML = (htmlString = '') => {
1718
+ const div = document.createElement('div');
1719
+ div.innerHTML = htmlString.trim();
1720
+ return div;
1721
+ };
1722
+
1723
+ const parseBrs = dom => {
1724
+ const brs = dom.querySelectorAll('br');
1725
+ brs.forEach(br => br.replaceWith('\n'));
1726
+ dom.innerHTML = dom.innerHTML.replace(/\n\n/g, '\n');
1727
+ };
1728
+ const parseParagraph = (paragraph, end) => {
1729
+ if (end) {
1730
+ return paragraph.innerHTML;
1731
+ }
1732
+
1733
+ return `${paragraph.innerHTML}\n\n`;
1734
+ };
1735
+ const parseParagraphs = dom => {
1736
+ const paragraphs = dom.querySelectorAll('p'); // separate variable for easily debugging, if needed
1737
+
1738
+ let str = '';
1739
+ paragraphs.forEach((par, index) => {
1740
+ str += parseParagraph(par, index === paragraphs.length - 1);
1741
+ });
1742
+ return str || null;
1743
+ };
1744
+ const prepareText = text => {
1745
+ let txtDom = createElementFromHTML(text);
1746
+ const allDomElements = Array.from(txtDom.querySelectorAll('*'));
1747
+
1748
+ if (txtDom.querySelectorAll('p').length === 0) {
1749
+ const div = document.createElement('div');
1750
+ div.innerHTML = `<p>${txtDom.innerHTML}</p>`;
1751
+ txtDom = div;
1752
+ } // if no dom elements, we just return the text
1753
+
1754
+
1755
+ if (allDomElements.length === 0) {
1756
+ return text;
1757
+ }
1758
+
1759
+ parseBrs(txtDom);
1760
+ return parseParagraphs(txtDom);
1761
+ };
1762
+
1763
+ export { Legend, TextSelect, Token$1 as Token, TokenSelect$1 as TokenSelect, TokenTypes, index as Tokenizer, prepareText };
1764
+ //# sourceMappingURL=index.js.map