@pie-lib/text-select 0.1.0
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/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/legend.d.ts +13 -0
- package/dist/legend.d.ts.map +1 -0
- package/dist/legend.js +64 -0
- package/dist/text-select.d.ts +35 -0
- package/dist/text-select.d.ts.map +1 -0
- package/dist/text-select.js +53 -0
- package/dist/token-select/index.d.ts +39 -0
- package/dist/token-select/index.d.ts.map +1 -0
- package/dist/token-select/index.js +102 -0
- package/dist/token-select/token.d.ts +33 -0
- package/dist/token-select/token.d.ts.map +1 -0
- package/dist/token-select/token.js +134 -0
- package/dist/tokenizer/builder.d.ts +28 -0
- package/dist/tokenizer/builder.d.ts.map +1 -0
- package/dist/tokenizer/builder.js +124 -0
- package/dist/tokenizer/controls.d.ts +24 -0
- package/dist/tokenizer/controls.d.ts.map +1 -0
- package/dist/tokenizer/controls.js +68 -0
- package/dist/tokenizer/index.d.ts +36 -0
- package/dist/tokenizer/index.d.ts.map +1 -0
- package/dist/tokenizer/index.js +91 -0
- package/dist/tokenizer/selection-utils.d.ts +11 -0
- package/dist/tokenizer/selection-utils.d.ts.map +1 -0
- package/dist/tokenizer/selection-utils.js +18 -0
- package/dist/tokenizer/token-text.d.ts +28 -0
- package/dist/tokenizer/token-text.d.ts.map +1 -0
- package/dist/tokenizer/token-text.js +85 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +21 -0
- package/package.json +39 -0
- package/src/index.ts +18 -0
- package/src/legend.tsx +112 -0
- package/src/text-select.tsx +89 -0
- package/src/token-select/index.tsx +181 -0
- package/src/token-select/token.tsx +233 -0
- package/src/tokenizer/builder.ts +268 -0
- package/src/tokenizer/controls.tsx +81 -0
- package/src/tokenizer/index.tsx +154 -0
- package/src/tokenizer/selection-utils.ts +59 -0
- package/src/tokenizer/token-text.tsx +145 -0
- package/src/utils.tsx +66 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/text-select/src/text-select.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import PropTypes from 'prop-types';
|
|
13
|
+
|
|
14
|
+
import TokenSelect from './token-select/index.js';
|
|
15
|
+
import { normalize } from './tokenizer/builder.js';
|
|
16
|
+
import { TokenTypes } from './token-select/token.js';
|
|
17
|
+
import debug from 'debug';
|
|
18
|
+
|
|
19
|
+
const log = debug('@pie-lib:text-select');
|
|
20
|
+
/**
|
|
21
|
+
* Built on TokenSelect uses build.normalize to build the token set.
|
|
22
|
+
*/
|
|
23
|
+
export default class TextSelect extends React.Component {
|
|
24
|
+
static propTypes = {
|
|
25
|
+
onChange: PropTypes.func,
|
|
26
|
+
disabled: PropTypes.bool,
|
|
27
|
+
tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
|
|
28
|
+
selectedTokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
|
|
29
|
+
text: PropTypes.string.isRequired,
|
|
30
|
+
className: PropTypes.string,
|
|
31
|
+
highlightChoices: PropTypes.bool,
|
|
32
|
+
animationsDisabled: PropTypes.bool,
|
|
33
|
+
maxNoOfSelections: PropTypes.number,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
change: any = (tokens) => {
|
|
37
|
+
const { onChange } = this.props;
|
|
38
|
+
|
|
39
|
+
if (!onChange) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const out = tokens.filter((t) => t.selected).map((t) => ({ start: t.start, end: t.end }));
|
|
43
|
+
|
|
44
|
+
onChange(out);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
render() {
|
|
48
|
+
const {
|
|
49
|
+
text,
|
|
50
|
+
disabled,
|
|
51
|
+
tokens,
|
|
52
|
+
selectedTokens,
|
|
53
|
+
className,
|
|
54
|
+
highlightChoices,
|
|
55
|
+
maxNoOfSelections,
|
|
56
|
+
animationsDisabled,
|
|
57
|
+
} = this.props;
|
|
58
|
+
|
|
59
|
+
const normalized = normalize(text, tokens);
|
|
60
|
+
log('normalized: ', normalized);
|
|
61
|
+
const prepped = normalized.map((t) => {
|
|
62
|
+
const selectedIndex = selectedTokens.findIndex((s) => {
|
|
63
|
+
return s.start === t.start && s.end === t.end;
|
|
64
|
+
});
|
|
65
|
+
const selected = selectedIndex !== -1;
|
|
66
|
+
const correct = selected ? t.correct : undefined;
|
|
67
|
+
const isMissing = t.isMissing;
|
|
68
|
+
return {
|
|
69
|
+
...t,
|
|
70
|
+
selectable: !disabled && t.predefined,
|
|
71
|
+
selected,
|
|
72
|
+
correct,
|
|
73
|
+
isMissing,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<TokenSelect
|
|
79
|
+
highlightChoices={!disabled && highlightChoices}
|
|
80
|
+
className={className}
|
|
81
|
+
tokens={prepped}
|
|
82
|
+
disabled={disabled}
|
|
83
|
+
onChange={this.change}
|
|
84
|
+
maxNoOfSelections={maxNoOfSelections}
|
|
85
|
+
animationsDisabled={animationsDisabled}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/text-select/src/token-select/index.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import PropTypes from 'prop-types';
|
|
13
|
+
import Token, { TokenTypes } from './token.js';
|
|
14
|
+
import { styled } from '@mui/material/styles';
|
|
15
|
+
import { clone, isEqual } from 'lodash-es';
|
|
16
|
+
import debug from 'debug';
|
|
17
|
+
import { noSelect } from '@pie-lib/style-utils';
|
|
18
|
+
|
|
19
|
+
const log = debug('@pie-lib:text-select:token-select');
|
|
20
|
+
|
|
21
|
+
const StyledTokenSelect: any = styled('div')(() => ({
|
|
22
|
+
backgroundColor: 'none',
|
|
23
|
+
whiteSpace: 'pre',
|
|
24
|
+
...noSelect(),
|
|
25
|
+
'& p': {
|
|
26
|
+
whiteSpace: 'break-spaces',
|
|
27
|
+
margin: 0,
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
// strip HTML tags for plain text rendering
|
|
32
|
+
const stripHtmlTags = (text) => {
|
|
33
|
+
if (!text) {
|
|
34
|
+
return text;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return text.replace(/<[^>]+>/g, '');
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export class TokenSelect extends React.Component {
|
|
41
|
+
static propTypes = {
|
|
42
|
+
tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
|
|
43
|
+
className: PropTypes.string,
|
|
44
|
+
onChange: PropTypes.func.isRequired,
|
|
45
|
+
disabled: PropTypes.bool,
|
|
46
|
+
highlightChoices: PropTypes.bool,
|
|
47
|
+
animationsDisabled: PropTypes.bool,
|
|
48
|
+
maxNoOfSelections: PropTypes.number,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
static defaultProps = {
|
|
52
|
+
highlightChoices: false,
|
|
53
|
+
maxNoOfSelections: 0,
|
|
54
|
+
tokens: [],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
selectedCount = () => this.props.tokens.filter((t) => t.selected).length;
|
|
58
|
+
|
|
59
|
+
canSelectMore: any = (selectedCount) => {
|
|
60
|
+
const { maxNoOfSelections } = this.props;
|
|
61
|
+
|
|
62
|
+
if (maxNoOfSelections === 1) return true;
|
|
63
|
+
|
|
64
|
+
log('[canSelectMore] maxNoOfSelections: ', maxNoOfSelections, 'selectedCount: ', selectedCount);
|
|
65
|
+
return maxNoOfSelections <= 0 || (isFinite(maxNoOfSelections) && selectedCount < maxNoOfSelections);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
toggleToken: any = (event) => {
|
|
69
|
+
const { target } = event;
|
|
70
|
+
const { tokens, animationsDisabled } = this.props;
|
|
71
|
+
const tokensCloned = clone(tokens);
|
|
72
|
+
|
|
73
|
+
const targetSpanWrapper = target.closest?.(`.${Token.rootClassName}`);
|
|
74
|
+
const targetedTokenIndex = targetSpanWrapper?.dataset?.indexkey;
|
|
75
|
+
const t = targetedTokenIndex !== undefined ? tokensCloned[targetedTokenIndex] : undefined;
|
|
76
|
+
|
|
77
|
+
// don't toggle if in print mode, correctness is defined, or is missing
|
|
78
|
+
if (t && t.correct === undefined && !animationsDisabled && !t.isMissing) {
|
|
79
|
+
const { onChange, maxNoOfSelections } = this.props;
|
|
80
|
+
const selected = !t.selected;
|
|
81
|
+
|
|
82
|
+
if (maxNoOfSelections === 1 && this.selectedCount() === 1) {
|
|
83
|
+
const selectedToken = (tokens || []).filter((tk) => tk.selected);
|
|
84
|
+
const updatedTokens = tokensCloned.map((token) => {
|
|
85
|
+
if (isEqual(token, selectedToken[0])) {
|
|
86
|
+
return { ...token, selected: false };
|
|
87
|
+
}
|
|
88
|
+
return { ...token, selectable: true };
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const update = { ...t, selected };
|
|
92
|
+
updatedTokens.splice(targetedTokenIndex, 1, update);
|
|
93
|
+
onChange(updatedTokens);
|
|
94
|
+
} else {
|
|
95
|
+
if (selected && maxNoOfSelections > 0 && this.selectedCount() >= maxNoOfSelections) {
|
|
96
|
+
log('skip toggle max reached');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const update = { ...t, selected };
|
|
100
|
+
tokensCloned.splice(targetedTokenIndex, 1, update);
|
|
101
|
+
onChange(tokensCloned);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Build a React tree instead of an HTML string so Emotion can inject CSS */
|
|
107
|
+
generateTokensNodes: any = () => {
|
|
108
|
+
const { tokens, disabled, highlightChoices, animationsDisabled } = this.props;
|
|
109
|
+
const selectedCount = this.selectedCount();
|
|
110
|
+
|
|
111
|
+
const isLineBreak = (text) => text === '\n';
|
|
112
|
+
const isNewParagraph = (text) => text === '\n\n';
|
|
113
|
+
|
|
114
|
+
const paragraphs = [];
|
|
115
|
+
let currentChildren = [];
|
|
116
|
+
|
|
117
|
+
const flushParagraph = () => {
|
|
118
|
+
// Always push a <p>, even if empty, to mirror previous behavior
|
|
119
|
+
paragraphs.push(<p key={`p-${paragraphs.length}`}>{currentChildren}</p>);
|
|
120
|
+
currentChildren = [];
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
(tokens || []).forEach((t, index) => {
|
|
124
|
+
const selectable = t.selected || (t.selectable && this.canSelectMore(selectedCount));
|
|
125
|
+
const showCorrectAnswer = t.correct !== undefined && (t.selectable || t.selected);
|
|
126
|
+
|
|
127
|
+
if (isNewParagraph(t.text)) {
|
|
128
|
+
flushParagraph();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isLineBreak(t.text)) {
|
|
133
|
+
currentChildren.push(<br key={`br-${index}`} />);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
(selectable && !disabled) ||
|
|
139
|
+
showCorrectAnswer ||
|
|
140
|
+
t.selected ||
|
|
141
|
+
t.isMissing ||
|
|
142
|
+
(animationsDisabled && t.predefined) // print mode
|
|
143
|
+
) {
|
|
144
|
+
currentChildren.push(
|
|
145
|
+
<Token
|
|
146
|
+
key={index}
|
|
147
|
+
disabled={disabled}
|
|
148
|
+
index={index}
|
|
149
|
+
{...t}
|
|
150
|
+
text={stripHtmlTags(t.text)}
|
|
151
|
+
selectable={selectable}
|
|
152
|
+
highlight={highlightChoices}
|
|
153
|
+
animationsDisabled={animationsDisabled}
|
|
154
|
+
/>,
|
|
155
|
+
);
|
|
156
|
+
} else {
|
|
157
|
+
// raw text node – React will escape as needed
|
|
158
|
+
currentChildren.push(<React.Fragment key={index}>{stripHtmlTags(t.text)}</React.Fragment>);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// flush last paragraph
|
|
163
|
+
flushParagraph();
|
|
164
|
+
|
|
165
|
+
return paragraphs;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
render() {
|
|
169
|
+
const { className: classNameProp } = this.props;
|
|
170
|
+
const nodes = this.generateTokensNodes();
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<StyledTokenSelect className={classNameProp} onClick={this.toggleToken}>
|
|
174
|
+
{nodes}
|
|
175
|
+
</StyledTokenSelect>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export default TokenSelect;
|
|
181
|
+
export { TokenTypes };
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @synced-from pie-lib/packages/text-select/src/token-select/token.jsx
|
|
4
|
+
* @auto-generated
|
|
5
|
+
*
|
|
6
|
+
* This file is automatically synced from pie-elements and converted to TypeScript.
|
|
7
|
+
* Manual edits will be overwritten on next sync.
|
|
8
|
+
* To make changes, edit the upstream JavaScript file and run sync again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import PropTypes from 'prop-types';
|
|
13
|
+
import { styled } from '@mui/material/styles';
|
|
14
|
+
import classNames from 'classnames';
|
|
15
|
+
import Check from '@mui/icons-material/Check';
|
|
16
|
+
import Close from '@mui/icons-material/Close';
|
|
17
|
+
|
|
18
|
+
import { color } from '@pie-lib/render-ui';
|
|
19
|
+
|
|
20
|
+
// we need to use a larger line height for the token to be more readable
|
|
21
|
+
const LINE_HEIGHT_MULTIPLIER = 3.2;
|
|
22
|
+
// we need a bit more space for correctness indicators
|
|
23
|
+
const CORRECTNESS_LINE_HEIGHT_MULTIPLIER = 3.4;
|
|
24
|
+
const CORRECTNESS_PADDING = 2;
|
|
25
|
+
|
|
26
|
+
// Styled components for different token states
|
|
27
|
+
const StyledToken: any = styled('span')(({ theme }) => ({
|
|
28
|
+
cursor: 'pointer',
|
|
29
|
+
textIndent: 0,
|
|
30
|
+
'&.disabled': {
|
|
31
|
+
cursor: 'inherit',
|
|
32
|
+
color: color.disabled(),
|
|
33
|
+
},
|
|
34
|
+
'&.disabledBlack': {
|
|
35
|
+
cursor: 'inherit',
|
|
36
|
+
pointerEvents: 'none',
|
|
37
|
+
},
|
|
38
|
+
'&.disabledAndSelected': {
|
|
39
|
+
backgroundColor: color.blueGrey100(),
|
|
40
|
+
},
|
|
41
|
+
[`@media (min-width: ${theme.breakpoints.values.md}px)`]: {
|
|
42
|
+
'&.selectable:hover': {
|
|
43
|
+
backgroundColor: color.blueGrey300(),
|
|
44
|
+
color: theme.palette.common.black,
|
|
45
|
+
'& > *': {
|
|
46
|
+
backgroundColor: color.blueGrey300(),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'&.selected': {
|
|
51
|
+
backgroundColor: color.blueGrey100(),
|
|
52
|
+
color: theme.palette.common.black,
|
|
53
|
+
lineHeight: `${parseFloat(theme.spacing(1)) * LINE_HEIGHT_MULTIPLIER}px`,
|
|
54
|
+
border: `solid 2px ${color.blueGrey900()}`,
|
|
55
|
+
borderRadius: '4px',
|
|
56
|
+
'& > *': {
|
|
57
|
+
backgroundColor: color.blueGrey100(),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
'&.highlight': {
|
|
61
|
+
border: `dashed 2px ${color.blueGrey600()}`,
|
|
62
|
+
borderRadius: '4px',
|
|
63
|
+
lineHeight: `${parseFloat(theme.spacing(1)) * LINE_HEIGHT_MULTIPLIER}px`,
|
|
64
|
+
},
|
|
65
|
+
'&.print': {
|
|
66
|
+
border: `dashed 2px ${color.blueGrey600()}`,
|
|
67
|
+
borderRadius: '4px',
|
|
68
|
+
lineHeight: `${parseFloat(theme.spacing(1)) * LINE_HEIGHT_MULTIPLIER}px`,
|
|
69
|
+
color: color.text(),
|
|
70
|
+
},
|
|
71
|
+
'&.custom': {
|
|
72
|
+
display: 'initial',
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
const StyledCommonTokenStyle: any = styled('span')(({ theme }) => ({
|
|
77
|
+
position: 'relative',
|
|
78
|
+
borderRadius: '4px',
|
|
79
|
+
color: theme.palette.common.black,
|
|
80
|
+
lineHeight: `${parseFloat(theme.spacing(1)) * CORRECTNESS_LINE_HEIGHT_MULTIPLIER + CORRECTNESS_PADDING}px`,
|
|
81
|
+
padding: `${CORRECTNESS_PADDING}px`,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
const StyledCorrectContainer: any = styled(StyledCommonTokenStyle)(() => ({
|
|
85
|
+
border: `${color.correctTertiary()} solid 2px`,
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
const StyledIncorrectContainer: any = styled(StyledCommonTokenStyle)(() => ({
|
|
89
|
+
border: `${color.incorrectWithIcon()} solid 2px`,
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
const StyledMissingContainer: any = styled(StyledCommonTokenStyle)(() => ({
|
|
93
|
+
border: `${color.incorrectWithIcon()} dashed 2px`,
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
const baseIconStyles = {
|
|
97
|
+
color: color.white(),
|
|
98
|
+
position: 'absolute',
|
|
99
|
+
top: '-8px',
|
|
100
|
+
left: '-8px',
|
|
101
|
+
borderRadius: '50%',
|
|
102
|
+
fontSize: '12px',
|
|
103
|
+
padding: '2px',
|
|
104
|
+
display: 'inline-block',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const StyledCorrectCheckIcon: any = styled(Check)(() => ({
|
|
108
|
+
...baseIconStyles,
|
|
109
|
+
backgroundColor: color.correctTertiary(),
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
const StyledIncorrectCloseIcon: any = styled(Close)(() => ({
|
|
113
|
+
...baseIconStyles,
|
|
114
|
+
backgroundColor: color.incorrectWithIcon(),
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
const Wrapper = ({ useWrapper, children, Container, Icon }) =>
|
|
118
|
+
useWrapper ? (
|
|
119
|
+
<Container>
|
|
120
|
+
{children}
|
|
121
|
+
{Icon ? <Icon /> : null}
|
|
122
|
+
</Container>
|
|
123
|
+
) : (
|
|
124
|
+
children
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
Wrapper.propTypes = {
|
|
128
|
+
useWrapper: PropTypes.bool,
|
|
129
|
+
Container: PropTypes.elementType,
|
|
130
|
+
Icon: PropTypes.elementType,
|
|
131
|
+
children: PropTypes.node,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const TokenTypes = {
|
|
135
|
+
text: PropTypes.string,
|
|
136
|
+
selectable: PropTypes.bool,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export class Token extends React.Component {
|
|
140
|
+
static rootClassName = 'tokenRootClass';
|
|
141
|
+
|
|
142
|
+
static propTypes = {
|
|
143
|
+
...TokenTypes,
|
|
144
|
+
text: PropTypes.string.isRequired,
|
|
145
|
+
className: PropTypes.string,
|
|
146
|
+
disabled: PropTypes.bool,
|
|
147
|
+
highlight: PropTypes.bool,
|
|
148
|
+
correct: PropTypes.bool,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
static defaultProps = {
|
|
152
|
+
selectable: false,
|
|
153
|
+
text: '',
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
getClassAndIconConfig: any = () => {
|
|
157
|
+
const {
|
|
158
|
+
selectable,
|
|
159
|
+
selected,
|
|
160
|
+
className: classNameProp,
|
|
161
|
+
disabled,
|
|
162
|
+
highlight,
|
|
163
|
+
correct,
|
|
164
|
+
animationsDisabled,
|
|
165
|
+
isMissing,
|
|
166
|
+
} = this.props;
|
|
167
|
+
const isTouchEnabled = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
|
168
|
+
const baseClassName = Token.rootClassName;
|
|
169
|
+
let Container;
|
|
170
|
+
let Icon;
|
|
171
|
+
|
|
172
|
+
if (correct === undefined && selected && disabled) {
|
|
173
|
+
return {
|
|
174
|
+
className: classNames(baseClassName, 'selected', 'disabledBlack', classNameProp),
|
|
175
|
+
Component: StyledToken,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (correct !== undefined) {
|
|
180
|
+
const isCorrect = correct === true;
|
|
181
|
+
return {
|
|
182
|
+
className: classNames(baseClassName, 'custom', classNameProp),
|
|
183
|
+
Component: StyledToken,
|
|
184
|
+
Container: isCorrect ? StyledCorrectContainer : StyledIncorrectContainer,
|
|
185
|
+
Icon: isCorrect ? StyledCorrectCheckIcon : StyledIncorrectCloseIcon,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (isMissing) {
|
|
190
|
+
return {
|
|
191
|
+
className: classNames(baseClassName, 'custom', 'missing', classNameProp),
|
|
192
|
+
Component: StyledToken,
|
|
193
|
+
Container: StyledMissingContainer,
|
|
194
|
+
Icon: StyledIncorrectCloseIcon,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
className: classNames(
|
|
200
|
+
baseClassName,
|
|
201
|
+
disabled && 'disabled',
|
|
202
|
+
selectable && !disabled && !isTouchEnabled && 'selectable',
|
|
203
|
+
selected && !disabled && 'selected',
|
|
204
|
+
selected && disabled && 'disabledAndSelected',
|
|
205
|
+
highlight && selectable && !disabled && !selected && 'highlight',
|
|
206
|
+
animationsDisabled && 'print',
|
|
207
|
+
classNameProp,
|
|
208
|
+
),
|
|
209
|
+
Component: StyledToken,
|
|
210
|
+
Container,
|
|
211
|
+
Icon,
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
render() {
|
|
216
|
+
const { text, index, correct, isMissing } = this.props;
|
|
217
|
+
const { className, Component, Container, Icon } = this.getClassAndIconConfig();
|
|
218
|
+
|
|
219
|
+
const TokenComponent = Component || StyledToken;
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<Wrapper useWrapper={correct !== undefined || isMissing} Container={Container} Icon={Icon}>
|
|
223
|
+
<TokenComponent
|
|
224
|
+
className={className}
|
|
225
|
+
dangerouslySetInnerHTML={{ __html: (text || '').replace(/\n/g, '<br>') }}
|
|
226
|
+
data-indexkey={index}
|
|
227
|
+
/>
|
|
228
|
+
</Wrapper>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export default Token;
|