@pie-lib/editable-html-tip-tap 1.0.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.
Files changed (167) hide show
  1. package/CHANGELOG.json +32 -0
  2. package/CHANGELOG.md +2280 -0
  3. package/lib/__tests__/editor.test.js +470 -0
  4. package/lib/__tests__/serialization.test.js +246 -0
  5. package/lib/__tests__/utils.js +106 -0
  6. package/lib/block-tags.js +25 -0
  7. package/lib/constants.js +16 -0
  8. package/lib/editor.js +1356 -0
  9. package/lib/extensions/MediaView.js +112 -0
  10. package/lib/extensions/characters.js +65 -0
  11. package/lib/extensions/component.js +325 -0
  12. package/lib/extensions/css.js +252 -0
  13. package/lib/extensions/custom-toolbar-wrapper.js +124 -0
  14. package/lib/extensions/image.js +106 -0
  15. package/lib/extensions/math.js +330 -0
  16. package/lib/extensions/media.js +276 -0
  17. package/lib/extensions/responseArea.js +278 -0
  18. package/lib/index.js +1213 -0
  19. package/lib/old-index.js +269 -0
  20. package/lib/parse-html.js +16 -0
  21. package/lib/plugins/characters/custom-popper.js +73 -0
  22. package/lib/plugins/characters/index.js +305 -0
  23. package/lib/plugins/characters/utils.js +381 -0
  24. package/lib/plugins/css/icons/index.js +37 -0
  25. package/lib/plugins/css/index.js +390 -0
  26. package/lib/plugins/customPlugin/index.js +114 -0
  27. package/lib/plugins/html/icons/index.js +38 -0
  28. package/lib/plugins/html/index.js +81 -0
  29. package/lib/plugins/image/__tests__/component.test.js +51 -0
  30. package/lib/plugins/image/__tests__/image-toolbar-logic.test.js +56 -0
  31. package/lib/plugins/image/__tests__/image-toolbar.test.js +26 -0
  32. package/lib/plugins/image/__tests__/index.test.js +98 -0
  33. package/lib/plugins/image/__tests__/insert-image-handler.test.js +125 -0
  34. package/lib/plugins/image/__tests__/mock-change.js +25 -0
  35. package/lib/plugins/image/alt-dialog.js +129 -0
  36. package/lib/plugins/image/component.js +419 -0
  37. package/lib/plugins/image/image-toolbar.js +177 -0
  38. package/lib/plugins/image/index.js +263 -0
  39. package/lib/plugins/image/insert-image-handler.js +117 -0
  40. package/lib/plugins/index.js +413 -0
  41. package/lib/plugins/list/__tests__/index.test.js +79 -0
  42. package/lib/plugins/list/index.js +334 -0
  43. package/lib/plugins/math/__tests__/index.test.js +300 -0
  44. package/lib/plugins/math/index.js +454 -0
  45. package/lib/plugins/media/__tests__/index.test.js +71 -0
  46. package/lib/plugins/media/index.js +387 -0
  47. package/lib/plugins/media/media-dialog.js +709 -0
  48. package/lib/plugins/media/media-toolbar.js +101 -0
  49. package/lib/plugins/media/media-wrapper.js +93 -0
  50. package/lib/plugins/rendering/index.js +46 -0
  51. package/lib/plugins/respArea/drag-in-the-blank/choice.js +289 -0
  52. package/lib/plugins/respArea/drag-in-the-blank/index.js +94 -0
  53. package/lib/plugins/respArea/explicit-constructed-response/index.js +120 -0
  54. package/lib/plugins/respArea/icons/index.js +95 -0
  55. package/lib/plugins/respArea/index.js +341 -0
  56. package/lib/plugins/respArea/inline-dropdown/index.js +126 -0
  57. package/lib/plugins/respArea/math-templated/index.js +130 -0
  58. package/lib/plugins/respArea/utils.js +125 -0
  59. package/lib/plugins/table/CustomTablePlugin.js +133 -0
  60. package/lib/plugins/table/__tests__/index.test.js +442 -0
  61. package/lib/plugins/table/__tests__/table-toolbar.test.js +54 -0
  62. package/lib/plugins/table/icons/index.js +69 -0
  63. package/lib/plugins/table/index.js +483 -0
  64. package/lib/plugins/table/table-toolbar.js +187 -0
  65. package/lib/plugins/textAlign/icons/index.js +194 -0
  66. package/lib/plugins/textAlign/index.js +34 -0
  67. package/lib/plugins/toolbar/__tests__/default-toolbar.test.js +128 -0
  68. package/lib/plugins/toolbar/__tests__/editor-and-toolbar.test.js +51 -0
  69. package/lib/plugins/toolbar/__tests__/toolbar-buttons.test.js +54 -0
  70. package/lib/plugins/toolbar/__tests__/toolbar.test.js +120 -0
  71. package/lib/plugins/toolbar/default-toolbar.js +229 -0
  72. package/lib/plugins/toolbar/done-button.js +53 -0
  73. package/lib/plugins/toolbar/editor-and-toolbar.js +286 -0
  74. package/lib/plugins/toolbar/index.js +34 -0
  75. package/lib/plugins/toolbar/toolbar-buttons.js +194 -0
  76. package/lib/plugins/toolbar/toolbar.js +376 -0
  77. package/lib/plugins/utils.js +62 -0
  78. package/lib/serialization.js +677 -0
  79. package/lib/shared/alert-dialog.js +75 -0
  80. package/lib/theme.js +9 -0
  81. package/package.json +69 -0
  82. package/src/__tests__/editor.test.jsx +363 -0
  83. package/src/__tests__/serialization.test.js +291 -0
  84. package/src/__tests__/utils.js +36 -0
  85. package/src/block-tags.js +17 -0
  86. package/src/constants.js +7 -0
  87. package/src/editor.jsx +1197 -0
  88. package/src/extensions/characters.js +46 -0
  89. package/src/extensions/component.jsx +294 -0
  90. package/src/extensions/css.js +217 -0
  91. package/src/extensions/custom-toolbar-wrapper.jsx +100 -0
  92. package/src/extensions/image.js +55 -0
  93. package/src/extensions/math.js +259 -0
  94. package/src/extensions/media.js +182 -0
  95. package/src/extensions/responseArea.js +205 -0
  96. package/src/index.jsx +1462 -0
  97. package/src/old-index.jsx +162 -0
  98. package/src/parse-html.js +8 -0
  99. package/src/plugins/README.md +27 -0
  100. package/src/plugins/characters/custom-popper.js +48 -0
  101. package/src/plugins/characters/index.jsx +284 -0
  102. package/src/plugins/characters/utils.js +447 -0
  103. package/src/plugins/css/icons/index.jsx +17 -0
  104. package/src/plugins/css/index.jsx +340 -0
  105. package/src/plugins/customPlugin/index.jsx +85 -0
  106. package/src/plugins/html/icons/index.jsx +19 -0
  107. package/src/plugins/html/index.jsx +72 -0
  108. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +51 -0
  109. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +27 -0
  110. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +44 -0
  111. package/src/plugins/image/__tests__/component.test.jsx +41 -0
  112. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +42 -0
  113. package/src/plugins/image/__tests__/image-toolbar.test.jsx +11 -0
  114. package/src/plugins/image/__tests__/index.test.js +95 -0
  115. package/src/plugins/image/__tests__/insert-image-handler.test.js +113 -0
  116. package/src/plugins/image/__tests__/mock-change.js +15 -0
  117. package/src/plugins/image/alt-dialog.jsx +82 -0
  118. package/src/plugins/image/component.jsx +343 -0
  119. package/src/plugins/image/image-toolbar.jsx +100 -0
  120. package/src/plugins/image/index.jsx +227 -0
  121. package/src/plugins/image/insert-image-handler.js +79 -0
  122. package/src/plugins/index.jsx +377 -0
  123. package/src/plugins/list/__tests__/index.test.js +54 -0
  124. package/src/plugins/list/index.jsx +305 -0
  125. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +48 -0
  126. package/src/plugins/math/__tests__/index.test.jsx +245 -0
  127. package/src/plugins/math/index.jsx +379 -0
  128. package/src/plugins/media/__tests__/index.test.js +75 -0
  129. package/src/plugins/media/index.jsx +325 -0
  130. package/src/plugins/media/media-dialog.js +624 -0
  131. package/src/plugins/media/media-toolbar.jsx +56 -0
  132. package/src/plugins/media/media-wrapper.jsx +43 -0
  133. package/src/plugins/rendering/index.js +31 -0
  134. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +215 -0
  135. package/src/plugins/respArea/drag-in-the-blank/index.jsx +70 -0
  136. package/src/plugins/respArea/explicit-constructed-response/index.jsx +92 -0
  137. package/src/plugins/respArea/icons/index.jsx +71 -0
  138. package/src/plugins/respArea/index.jsx +299 -0
  139. package/src/plugins/respArea/inline-dropdown/index.jsx +108 -0
  140. package/src/plugins/respArea/math-templated/index.jsx +104 -0
  141. package/src/plugins/respArea/utils.jsx +90 -0
  142. package/src/plugins/table/CustomTablePlugin.js +113 -0
  143. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +44 -0
  144. package/src/plugins/table/__tests__/index.test.jsx +401 -0
  145. package/src/plugins/table/__tests__/table-toolbar.test.jsx +42 -0
  146. package/src/plugins/table/icons/index.jsx +53 -0
  147. package/src/plugins/table/index.jsx +427 -0
  148. package/src/plugins/table/table-toolbar.jsx +136 -0
  149. package/src/plugins/textAlign/icons/index.jsx +114 -0
  150. package/src/plugins/textAlign/index.jsx +23 -0
  151. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +923 -0
  152. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +20 -0
  153. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +36 -0
  154. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +46 -0
  155. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +94 -0
  156. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +37 -0
  157. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +51 -0
  158. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +106 -0
  159. package/src/plugins/toolbar/default-toolbar.jsx +206 -0
  160. package/src/plugins/toolbar/done-button.jsx +38 -0
  161. package/src/plugins/toolbar/editor-and-toolbar.jsx +257 -0
  162. package/src/plugins/toolbar/index.jsx +23 -0
  163. package/src/plugins/toolbar/toolbar-buttons.jsx +138 -0
  164. package/src/plugins/toolbar/toolbar.jsx +338 -0
  165. package/src/plugins/utils.js +31 -0
  166. package/src/serialization.jsx +621 -0
  167. package/src/theme.js +1 -0
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import classNames from 'classnames';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+
6
+ const useStyles = withStyles(() => ({
7
+ root: {
8
+ position: 'relative',
9
+ },
10
+ editor: {
11
+ display: 'inline-block',
12
+ overflow: 'hidden',
13
+ },
14
+ }));
15
+
16
+ class MediaWrapper extends React.Component {
17
+ static propTypes = {
18
+ classes: PropTypes.object,
19
+ children: PropTypes.array,
20
+ editor: PropTypes.bool,
21
+ width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
22
+ };
23
+
24
+ render() {
25
+ const { editor, classes, children, width, ...rest } = this.props;
26
+
27
+ return (
28
+ <span
29
+ className={classNames(classes.root, {
30
+ [classes.editor]: editor,
31
+ })}
32
+ {...rest}
33
+ style={{
34
+ width: width || 300,
35
+ }}
36
+ >
37
+ {children}
38
+ </span>
39
+ );
40
+ }
41
+ }
42
+
43
+ export default useStyles(MediaWrapper);
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Plugin to render the normal divs and spans with attributes (if they are provided)
5
+ */
6
+ export default () => {
7
+ return {
8
+ name: 'renderingPlugin',
9
+ renderNode: (props) => {
10
+ const { attributes, children, node } = props;
11
+
12
+ if (node.object !== 'block' && node.object !== 'inline') {
13
+ return;
14
+ }
15
+
16
+ const Tag = node.object === 'block' ? 'div' : 'span';
17
+ const style = { position: 'relative' };
18
+ const extraAttributes = node.data.get('attributes');
19
+
20
+ return React.createElement(
21
+ Tag,
22
+ {
23
+ ...attributes,
24
+ ...extraAttributes,
25
+ style: style,
26
+ },
27
+ children,
28
+ );
29
+ },
30
+ };
31
+ };
@@ -0,0 +1,215 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import isUndefined from 'lodash/isUndefined';
4
+ import { DragSource, DropTarget } from '@pie-lib/drag';
5
+ import { color } from '@pie-lib/render-ui';
6
+ import { renderMath } from '@pie-lib/math-rendering';
7
+ import { withStyles } from '@material-ui/core/styles';
8
+ import classnames from 'classnames';
9
+
10
+ import { GripIcon } from '../icons';
11
+
12
+ const useStyles = withStyles((theme) => ({
13
+ content: {
14
+ border: `solid 0px ${theme.palette.primary.main}`,
15
+ '& mjx-frac': {
16
+ fontSize: '120% !important',
17
+ },
18
+ },
19
+ chip: {
20
+ minWidth: '90px',
21
+ },
22
+ correct: {
23
+ border: `solid 1px ${color.correct()}`,
24
+ },
25
+ incorrect: {
26
+ border: `solid 1px ${theme.palette.error.main}`,
27
+ },
28
+ selected: {
29
+ border: `2px solid ${color.primaryDark()} !important`,
30
+ },
31
+ }));
32
+
33
+ export class BlankContent extends React.Component {
34
+ static propTypes = {
35
+ n: PropTypes.object,
36
+ children: PropTypes.func,
37
+ isDragging: PropTypes.bool,
38
+ isOver: PropTypes.bool,
39
+ dragItem: PropTypes.object,
40
+ value: PropTypes.object,
41
+ classes: PropTypes.object,
42
+ };
43
+
44
+ constructor(props) {
45
+ super(props);
46
+
47
+ this.handleClick = this.handleClick.bind(this);
48
+ this.state = { hoveredElementSize: null };
49
+ }
50
+
51
+ componentDidMount() {
52
+ document.addEventListener('click', this.handleClick);
53
+ }
54
+
55
+ componentWillUnmount() {
56
+ document.removeEventListener('click', this.handleClick);
57
+ }
58
+
59
+ handleClick(event) {
60
+ const { classes } = this.props;
61
+
62
+ if (this.elementRef) {
63
+ this.elementRef.className = this.elementRef.contains(event.target) ? classes.selected : '';
64
+ }
65
+ }
66
+
67
+ getSnapshotBeforeUpdate(prevProps) {
68
+ if (!prevProps.isOver && this.props.isOver && this.elementRef) {
69
+ const node = this.elementRef;
70
+ return { width: node.offsetWidth, height: node.offsetHeight };
71
+ }
72
+ return null;
73
+ }
74
+
75
+ componentDidUpdate(prevProps, prevState, snapshot) {
76
+ if (this.elementRef && typeof renderMath === 'function') {
77
+ renderMath(this.elementRef);
78
+ }
79
+
80
+ if (
81
+ snapshot &&
82
+ (!this.state.hoveredElementSize ||
83
+ this.state.hoveredElementSize.width !== snapshot.width ||
84
+ this.state.hoveredElementSize.height !== snapshot.height)
85
+ ) {
86
+ this.setState({ hoveredElementSize: snapshot });
87
+ return;
88
+ }
89
+
90
+ if (prevProps.isOver && !this.props.isOver && this.state.hoveredElementSize) {
91
+ this.setState({ hoveredElementSize: null });
92
+ }
93
+ }
94
+
95
+ render() {
96
+ const { n, children, isDragging, dragItem, isOver, value } = this.props;
97
+ const { hoveredElementSize } = this.state;
98
+
99
+ const label = dragItem && isOver ? dragItem.value.value : value.value || '\u00A0';
100
+ const finalLabel = isDragging ? '\u00A0' : label;
101
+ const hasGrip = finalLabel !== '\u00A0';
102
+ const isPreview = dragItem && isOver;
103
+
104
+ return (
105
+ <div
106
+ ref={(ref) => (this.elementRef = ref)}
107
+ style={{
108
+ display: 'inline-flex',
109
+ minWidth: '178px',
110
+ minHeight: '36px',
111
+ background: isPreview ? `${color.defaults.BORDER_LIGHT}` : `${color.defaults.WHITE}`,
112
+ border: isPreview ? `1px solid ${color.defaults.BORDER_DARK}` : `1px solid ${color.defaults.BORDER_LIGHT}`,
113
+ boxSizing: 'border-box',
114
+ borderRadius: '3px',
115
+ overflow: 'hidden',
116
+ position: 'relative',
117
+ padding: '8px 8px 8px 35px',
118
+ width: hoveredElementSize ? hoveredElementSize.width : undefined,
119
+ height: hoveredElementSize ? hoveredElementSize.height : undefined,
120
+ }}
121
+ data-key={n.key}
122
+ contentEditable={false}
123
+ >
124
+ {hasGrip && (
125
+ <GripIcon
126
+ style={{
127
+ position: 'absolute',
128
+ top: '6px',
129
+ left: '15px',
130
+ color: '#9B9B9B',
131
+ }}
132
+ contentEditable={false}
133
+ />
134
+ )}
135
+ <span
136
+ dangerouslySetInnerHTML={{
137
+ __html: finalLabel,
138
+ }}
139
+ />
140
+ {children}
141
+ </div>
142
+ );
143
+ }
144
+ }
145
+
146
+ const StyledBlankContent = useStyles(BlankContent);
147
+
148
+ const connectedBlankContent = useStyles(({ connectDropTarget, connectDragSource, ...props }) => {
149
+ const { classes, isOver, value } = props;
150
+ const dragContent = <StyledBlankContent {...props} />;
151
+ const dragEl = !value ? dragContent : connectDragSource(<span>{dragContent}</span>);
152
+ const content = <span className={classnames(classes.content, isOver && classes.over)}>{dragEl}</span>;
153
+
154
+ return connectDropTarget ? connectDropTarget(content) : content;
155
+ });
156
+
157
+ export const tileTarget = {
158
+ drop(props, monitor) {
159
+ const draggedItem = monitor.getItem();
160
+ const shouldDrop =
161
+ isUndefined(draggedItem.value.index) ||
162
+ isUndefined(props.value.index) ||
163
+ draggedItem.value.index !== props.value.index;
164
+
165
+ if (shouldDrop) {
166
+ props.onChange(draggedItem.value);
167
+ }
168
+
169
+ return {
170
+ dropped: shouldDrop,
171
+ };
172
+ },
173
+ canDrop(props, monitor) {
174
+ const draggedItem = monitor.getItem();
175
+
176
+ return draggedItem.instanceId === props.instanceId;
177
+ },
178
+ };
179
+
180
+ const DropTile = DropTarget('drag-in-the-blank-choice', tileTarget, (connect, monitor) => ({
181
+ connectDropTarget: connect.dropTarget(),
182
+ isOver: monitor.isOver({ shallow: true }),
183
+ dragItem: monitor.getItem(),
184
+ }))(connectedBlankContent);
185
+
186
+ export const tileSource = {
187
+ canDrag(props) {
188
+ return !props.disabled && !!props.value;
189
+ },
190
+ beginDrag(props) {
191
+ return {
192
+ id: props.targetId,
193
+ value: props.value,
194
+ instanceId: props.instanceId,
195
+ fromChoice: true,
196
+ };
197
+ },
198
+ endDrag(props, monitor) {
199
+ // this will be null if it did not drop
200
+ const dropResult = monitor.getDropResult();
201
+
202
+ if (!dropResult || (dropResult.dropped && !props.duplicates)) {
203
+ const draggedItem = monitor.getItem();
204
+
205
+ if (draggedItem.fromChoice) {
206
+ props.removeResponse(draggedItem.value);
207
+ }
208
+ }
209
+ },
210
+ };
211
+
212
+ export default DragSource('drag-in-the-blank-choice', tileSource, (connect, monitor) => ({
213
+ connectDragSource: connect.dragSource(),
214
+ isDragging: monitor.isDragging(),
215
+ }))(DropTile);
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { NodeViewWrapper } from '@tiptap/react';
4
+ import DragDropTile from './choice';
5
+ import omit from 'lodash/omit';
6
+
7
+ export const onValueChange = (editor, node, pos, choice) => {
8
+ const { tr } = editor.state;
9
+
10
+ // Merge old and new attributes
11
+ tr.setNodeMarkup(pos, undefined, {
12
+ ...node.attrs,
13
+ ...choice,
14
+ });
15
+ tr.isDone = true;
16
+ editor.view.dispatch(tr);
17
+ };
18
+
19
+ export const onRemoveResponse = (editor, node, pos) => {
20
+ const { tr } = editor.state;
21
+
22
+ // Merge old and new attributes
23
+ tr.setNodeMarkup(pos, undefined, omit(node.attrs, ['value', 'id']));
24
+ tr.isDone = true;
25
+ editor.view.dispatch(tr);
26
+ };
27
+
28
+ const DragDrop = (props) => {
29
+ const { editor, node, getPos, options, selected } = props;
30
+ const { attrs: attributes } = node;
31
+ const { inTable } = attributes;
32
+ const pos = getPos();
33
+
34
+ // console.log({nodeProps.children})
35
+ return (
36
+ <NodeViewWrapper className="drag-in-the-blank" data-selected={selected}>
37
+ <span
38
+ {...attributes}
39
+ style={{
40
+ display: 'inline-flex',
41
+ minHeight: '50px',
42
+ minWidth: '178px',
43
+ position: 'relative',
44
+ margin: inTable ? '10px' : '0 10px',
45
+ cursor: 'pointer',
46
+ }}
47
+ >
48
+ <DragDropTile
49
+ n={attributes}
50
+ dragKey={attributes.id}
51
+ targetId="0"
52
+ value={attributes}
53
+ duplicates={options.duplicates}
54
+ onChange={(choice) => onValueChange(editor, node, pos, choice)}
55
+ removeResponse={() => onRemoveResponse(editor, node, pos)}
56
+ ></DragDropTile>
57
+ </span>
58
+ </NodeViewWrapper>
59
+ );
60
+ };
61
+
62
+ DragDrop.propTypes = {
63
+ attributes: PropTypes.object,
64
+ data: PropTypes.object,
65
+ n: PropTypes.object,
66
+ nodeProps: PropTypes.object,
67
+ opts: PropTypes.object,
68
+ };
69
+
70
+ export default DragDrop;
@@ -0,0 +1,92 @@
1
+ import React, { useEffect, useState, useRef } from 'react';
2
+ import { NodeViewWrapper } from '@tiptap/react';
3
+ import PropTypes from 'prop-types';
4
+
5
+ const ExplicitConstructedResponse = (props) => {
6
+ const { editor, node, getPos, options, selected } = props;
7
+ const { attrs: attributes } = node;
8
+ const { value, error } = attributes;
9
+ const pos = getPos();
10
+ const [showToolbar, setShowToolbar] = useState(false);
11
+ const EcrToolbar = options.respAreaToolbar(node, editor, () => {});
12
+ const toolbarRef = useRef(null);
13
+
14
+ const handleDone = (newLatex) => {
15
+ updateAttributes({ latex: newLatex });
16
+ setShowToolbar(false);
17
+ editor.commands.focus();
18
+ };
19
+
20
+ useEffect(() => {
21
+ setShowToolbar(selected);
22
+ }, [selected]);
23
+
24
+ useEffect(() => {
25
+ const handleClickOutside = (event) => {
26
+ if (
27
+ toolbarRef.current &&
28
+ !toolbarRef.current.contains(event.target) &&
29
+ !event.target.closest('[data-inline-node]')
30
+ ) {
31
+ setShowToolbar(false);
32
+ }
33
+ };
34
+
35
+ if (showToolbar) {
36
+ document.addEventListener('mousedown', handleClickOutside);
37
+ } else {
38
+ document.removeEventListener('mousedown', handleClickOutside);
39
+ }
40
+
41
+ return () => document.removeEventListener('mousedown', handleClickOutside);
42
+ }, [showToolbar]);
43
+
44
+ return (
45
+ <NodeViewWrapper
46
+ className="drag-in-the-blank"
47
+ data-selected={selected}
48
+ style={{
49
+ display: 'inline-flex',
50
+ minHeight: '55px',
51
+ position: 'relative',
52
+ cursor: 'pointer',
53
+ }}
54
+ >
55
+ <div
56
+ {...attributes}
57
+ style={{
58
+ display: 'inline-flex',
59
+ width: '100%',
60
+ minHeight: '46px',
61
+ height: '46px',
62
+ backgroundColor: '#FFF',
63
+ border: `1px solid ${error ? 'red' : '#C0C3CF'}`,
64
+ boxSizing: 'border-box',
65
+ borderRadius: '4px',
66
+ overflow: 'hidden',
67
+ padding: '12px 21px',
68
+ margin: '0 4px',
69
+ visibility: showToolbar ? 'hidden' : 'visible',
70
+ }}
71
+ onClick={() => setShowToolbar(true)}
72
+ dangerouslySetInnerHTML={{
73
+ __html: value || '<div>&nbsp;</div>',
74
+ }}
75
+ />
76
+ {showToolbar && (
77
+ <div ref={toolbarRef} className="absolute z-50 bg-white shadow-lg rounded p-2" style={{ zIndex: 1 }}>
78
+ <EcrToolbar />
79
+ </div>
80
+ )}
81
+ </NodeViewWrapper>
82
+ );
83
+ };
84
+
85
+ ExplicitConstructedResponse.propTypes = {
86
+ attributes: PropTypes.object,
87
+ error: PropTypes.any,
88
+ value: PropTypes.string,
89
+ isFocused: PropTypes.bool,
90
+ };
91
+
92
+ export default ExplicitConstructedResponse;
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import ChevronRight from '@material-ui/icons/ChevronRight';
4
+ import MoreVert from '@material-ui/icons/MoreVert';
5
+ import { withStyles } from '@material-ui/core/styles';
6
+
7
+ const getRotate = (direction) => {
8
+ switch (direction) {
9
+ case 'down':
10
+ return 90;
11
+
12
+ case 'up':
13
+ return -90;
14
+
15
+ case 'left':
16
+ return 180;
17
+
18
+ default:
19
+ return 0;
20
+ }
21
+ };
22
+
23
+ export const Chevron = (props) => {
24
+ const { direction, style } = props;
25
+ const rotate = getRotate(direction);
26
+
27
+ return (
28
+ <ChevronRight
29
+ style={{
30
+ transform: `rotate(${rotate}deg)`,
31
+ ...style,
32
+ }}
33
+ />
34
+ );
35
+ };
36
+
37
+ Chevron.propTypes = {
38
+ direction: PropTypes.string,
39
+ style: PropTypes.object,
40
+ };
41
+
42
+ export const GripIcon = ({ style }) => {
43
+ return (
44
+ <span style={style}>
45
+ <MoreVert
46
+ style={{
47
+ margin: '0 -16px',
48
+ }}
49
+ />
50
+ <MoreVert />
51
+ </span>
52
+ );
53
+ };
54
+
55
+ GripIcon.propTypes = {
56
+ style: PropTypes.object,
57
+ };
58
+
59
+ export const ToolbarIcon = withStyles((theme) => ({
60
+ icon: {
61
+ fontFamily: 'Cerebri Sans !important',
62
+ fontSize: theme.typography.fontSize,
63
+ fontWeight: 'bold',
64
+ lineHeight: '14px',
65
+ position: 'relative',
66
+ top: '7px',
67
+ width: '110px',
68
+ height: '28px',
69
+ whiteSpace: 'nowrap',
70
+ },
71
+ }))(({ classes }) => <div className={classes.icon}>+ Response Area</div>);