@pie-lib/editable-html 11.18.6-esmbeta.0 → 11.19.0-mui-update.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 (136) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/lib/block-tags.js +2 -3
  3. package/lib/block-tags.js.map +1 -1
  4. package/lib/constants.js +3 -6
  5. package/lib/constants.js.map +1 -1
  6. package/lib/editor.js +302 -450
  7. package/lib/editor.js.map +1 -1
  8. package/lib/index.js +19 -77
  9. package/lib/index.js.map +1 -1
  10. package/lib/parse-html.js +7 -7
  11. package/lib/parse-html.js.map +1 -1
  12. package/lib/plugins/characters/custom-popper.js +24 -44
  13. package/lib/plugins/characters/custom-popper.js.map +1 -1
  14. package/lib/plugins/characters/index.js +9 -60
  15. package/lib/plugins/characters/index.js.map +1 -1
  16. package/lib/plugins/characters/utils.js +3 -6
  17. package/lib/plugins/characters/utils.js.map +1 -1
  18. package/lib/plugins/css/icons/index.js +13 -25
  19. package/lib/plugins/css/icons/index.js.map +1 -1
  20. package/lib/plugins/css/index.js +22 -88
  21. package/lib/plugins/css/index.js.map +1 -1
  22. package/lib/plugins/customPlugin/index.js +10 -26
  23. package/lib/plugins/customPlugin/index.js.map +1 -1
  24. package/lib/plugins/html/icons/index.js +14 -26
  25. package/lib/plugins/html/icons/index.js.map +1 -1
  26. package/lib/plugins/html/index.js +4 -13
  27. package/lib/plugins/html/index.js.map +1 -1
  28. package/lib/plugins/image/alt-dialog.js +20 -49
  29. package/lib/plugins/image/alt-dialog.js.map +1 -1
  30. package/lib/plugins/image/component.js +119 -190
  31. package/lib/plugins/image/component.js.map +1 -1
  32. package/lib/plugins/image/image-toolbar.js +44 -86
  33. package/lib/plugins/image/image-toolbar.js.map +1 -1
  34. package/lib/plugins/image/index.js +6 -46
  35. package/lib/plugins/image/index.js.map +1 -1
  36. package/lib/plugins/image/insert-image-handler.js +10 -31
  37. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  38. package/lib/plugins/index.js +44 -106
  39. package/lib/plugins/index.js.map +1 -1
  40. package/lib/plugins/list/index.js +27 -73
  41. package/lib/plugins/list/index.js.map +1 -1
  42. package/lib/plugins/math/index.js +64 -116
  43. package/lib/plugins/math/index.js.map +1 -1
  44. package/lib/plugins/media/index.js +23 -81
  45. package/lib/plugins/media/index.js.map +1 -1
  46. package/lib/plugins/media/media-dialog.js +192 -307
  47. package/lib/plugins/media/media-dialog.js.map +1 -1
  48. package/lib/plugins/media/media-toolbar.js +40 -65
  49. package/lib/plugins/media/media-toolbar.js.map +1 -1
  50. package/lib/plugins/media/media-wrapper.js +20 -49
  51. package/lib/plugins/media/media-wrapper.js.map +1 -1
  52. package/lib/plugins/rendering/index.js +5 -15
  53. package/lib/plugins/rendering/index.js.map +1 -1
  54. package/lib/plugins/respArea/drag-in-the-blank/choice.js +175 -249
  55. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  56. package/lib/plugins/respArea/drag-in-the-blank/index.js +39 -29
  57. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  58. package/lib/plugins/respArea/explicit-constructed-response/index.js +3 -10
  59. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  60. package/lib/plugins/respArea/icons/index.js +22 -45
  61. package/lib/plugins/respArea/icons/index.js.map +1 -1
  62. package/lib/plugins/respArea/index.js +5 -59
  63. package/lib/plugins/respArea/index.js.map +1 -1
  64. package/lib/plugins/respArea/inline-dropdown/index.js +2 -10
  65. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  66. package/lib/plugins/respArea/math-templated/index.js +92 -109
  67. package/lib/plugins/respArea/math-templated/index.js.map +1 -1
  68. package/lib/plugins/respArea/utils.js +8 -40
  69. package/lib/plugins/respArea/utils.js.map +1 -1
  70. package/lib/plugins/table/CustomTablePlugin.js +24 -41
  71. package/lib/plugins/table/CustomTablePlugin.js.map +1 -1
  72. package/lib/plugins/table/icons/index.js +19 -35
  73. package/lib/plugins/table/icons/index.js.map +1 -1
  74. package/lib/plugins/table/index.js +41 -118
  75. package/lib/plugins/table/index.js.map +1 -1
  76. package/lib/plugins/table/table-toolbar.js +37 -87
  77. package/lib/plugins/table/table-toolbar.js.map +1 -1
  78. package/lib/plugins/textAlign/icons/index.js +18 -64
  79. package/lib/plugins/textAlign/icons/index.js.map +1 -1
  80. package/lib/plugins/textAlign/index.js +1 -6
  81. package/lib/plugins/textAlign/index.js.map +1 -1
  82. package/lib/plugins/toolbar/default-toolbar.js +30 -79
  83. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  84. package/lib/plugins/toolbar/done-button.js +16 -34
  85. package/lib/plugins/toolbar/done-button.js.map +1 -1
  86. package/lib/plugins/toolbar/editor-and-toolbar.js +174 -201
  87. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  88. package/lib/plugins/toolbar/index.js +0 -5
  89. package/lib/plugins/toolbar/index.js.map +1 -1
  90. package/lib/plugins/toolbar/toolbar-buttons.js +57 -107
  91. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  92. package/lib/plugins/toolbar/toolbar.js +95 -161
  93. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  94. package/lib/plugins/utils.js +5 -25
  95. package/lib/plugins/utils.js.map +1 -1
  96. package/lib/serialization.js +44 -150
  97. package/lib/serialization.js.map +1 -1
  98. package/lib/shared/alert-dialog.js +23 -42
  99. package/lib/theme.js +1 -2
  100. package/lib/theme.js.map +1 -1
  101. package/package.json +15 -21
  102. package/src/__tests__/utils.js +1 -1
  103. package/src/editor.jsx +110 -108
  104. package/src/plugins/characters/custom-popper.js +20 -25
  105. package/src/plugins/css/icons/index.jsx +11 -13
  106. package/src/plugins/css/index.jsx +3 -5
  107. package/src/plugins/html/icons/index.jsx +12 -14
  108. package/src/plugins/image/alt-dialog.jsx +9 -8
  109. package/src/plugins/image/component.jsx +67 -87
  110. package/src/plugins/image/image-toolbar.jsx +26 -26
  111. package/src/plugins/image/index.jsx +1 -1
  112. package/src/plugins/index.jsx +10 -10
  113. package/src/plugins/math/index.jsx +1 -1
  114. package/src/plugins/media/index.jsx +2 -2
  115. package/src/plugins/media/media-dialog.js +65 -76
  116. package/src/plugins/media/media-toolbar.jsx +32 -33
  117. package/src/plugins/media/media-wrapper.jsx +10 -13
  118. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +193 -180
  119. package/src/plugins/respArea/drag-in-the-blank/index.jsx +58 -22
  120. package/src/plugins/respArea/icons/index.jsx +16 -16
  121. package/src/plugins/respArea/math-templated/index.jsx +88 -89
  122. package/src/plugins/respArea/utils.jsx +1 -1
  123. package/src/plugins/table/icons/index.jsx +14 -16
  124. package/src/plugins/table/index.jsx +27 -19
  125. package/src/plugins/table/table-toolbar.jsx +17 -19
  126. package/src/plugins/textAlign/icons/index.jsx +3 -3
  127. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +1 -1
  128. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +1 -1
  129. package/src/plugins/toolbar/default-toolbar.jsx +18 -21
  130. package/src/plugins/toolbar/done-button.jsx +16 -22
  131. package/src/plugins/toolbar/editor-and-toolbar.jsx +134 -157
  132. package/src/plugins/toolbar/toolbar-buttons.jsx +29 -46
  133. package/src/plugins/toolbar/toolbar.jsx +60 -78
  134. package/esm/index.js +0 -111560
  135. package/esm/index.js.map +0 -1
  136. package/esm/package.json +0 -1
@@ -1,56 +1,55 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { withStyles } from '@material-ui/core/styles';
3
+ import { styled } from '@mui/material/styles';
4
4
 
5
- const useStyles = withStyles((theme) => ({
6
- root: {
7
- position: 'relative',
8
- bottom: '5px',
9
- left: 0,
10
- width: '100%',
11
- background: theme.palette.common.white,
12
- display: 'inline-flex',
13
- padding: '5px',
14
- boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
15
- },
16
- editContainer: {
17
- cursor: 'pointer',
18
- flex: 3,
19
- border: `solid ${theme.palette.common.black}`,
20
- textAlign: 'right',
21
- borderWidth: '0 2px 0 0',
22
- marginRight: '5px',
23
- paddingRight: '5px',
24
- },
25
- removeContainer: {
26
- cursor: 'pointer',
27
- },
5
+ const StyledMediaToolbar = styled('span')(({ theme }) => ({
6
+ position: 'relative',
7
+ bottom: '5px',
8
+ left: 0,
9
+ width: '100%',
10
+ background: theme.palette.common.white,
11
+ display: 'inline-flex',
12
+ padding: '5px',
13
+ boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
14
+ }));
15
+
16
+ const StyledEditContainer = styled('span')(({ theme }) => ({
17
+ cursor: 'pointer',
18
+ flex: 3,
19
+ border: `solid ${theme.palette.common.black}`,
20
+ textAlign: 'right',
21
+ borderWidth: '0 2px 0 0',
22
+ marginRight: '5px',
23
+ paddingRight: '5px',
24
+ }));
25
+
26
+ const StyledRemoveContainer = styled('span')(() => ({
27
+ cursor: 'pointer',
28
28
  }));
29
29
 
30
30
  class MediaToolbar extends React.Component {
31
31
  static propTypes = {
32
- classes: PropTypes.object,
33
32
  onEdit: PropTypes.func,
34
33
  hideEdit: PropTypes.bool,
35
34
  onRemove: PropTypes.func,
36
35
  };
37
36
 
38
37
  render() {
39
- const { classes, hideEdit, onEdit, onRemove } = this.props;
38
+ const { hideEdit, onEdit, onRemove } = this.props;
40
39
 
41
40
  return (
42
- <span className={classes.root}>
41
+ <StyledMediaToolbar>
43
42
  {hideEdit ? null : (
44
- <span className={classes.editContainer} onClick={onEdit}>
43
+ <StyledEditContainer onClick={onEdit}>
45
44
  Edit Settings
46
- </span>
45
+ </StyledEditContainer>
47
46
  )}
48
- <span className={classes.removeContainer} onClick={onRemove}>
47
+ <StyledRemoveContainer onClick={onRemove}>
49
48
  Remove
50
- </span>
51
- </span>
49
+ </StyledRemoveContainer>
50
+ </StyledMediaToolbar>
52
51
  );
53
52
  }
54
53
  }
55
54
 
56
- export default useStyles(MediaToolbar);
55
+ export default MediaToolbar;
@@ -1,13 +1,11 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import classNames from 'classnames';
4
- import { withStyles } from '@material-ui/core/styles';
4
+ import { styled } from '@mui/material/styles';
5
5
 
6
- const useStyles = withStyles(() => ({
7
- root: {
8
- position: 'relative',
9
- },
10
- editor: {
6
+ const StyledMediaWrapper = styled('span')(() => ({
7
+ position: 'relative',
8
+ '&.editor': {
11
9
  display: 'inline-block',
12
10
  overflow: 'hidden',
13
11
  },
@@ -15,19 +13,18 @@ const useStyles = withStyles(() => ({
15
13
 
16
14
  class MediaWrapper extends React.Component {
17
15
  static propTypes = {
18
- classes: PropTypes.object,
19
16
  children: PropTypes.array,
20
17
  editor: PropTypes.bool,
21
18
  width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
22
19
  };
23
20
 
24
21
  render() {
25
- const { editor, classes, children, width, ...rest } = this.props;
22
+ const { editor, children, width, ...rest } = this.props;
26
23
 
27
24
  return (
28
- <span
29
- className={classNames(classes.root, {
30
- [classes.editor]: editor,
25
+ <StyledMediaWrapper
26
+ className={classNames({
27
+ editor: editor,
31
28
  })}
32
29
  {...rest}
33
30
  style={{
@@ -35,9 +32,9 @@ class MediaWrapper extends React.Component {
35
32
  }}
36
33
  >
37
34
  {children}
38
- </span>
35
+ </StyledMediaWrapper>
39
36
  );
40
37
  }
41
38
  }
42
39
 
43
- export default useStyles(MediaWrapper);
40
+ export default MediaWrapper;
@@ -1,215 +1,228 @@
1
- import React from 'react';
1
+ import React, { useRef, useEffect, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import isUndefined from 'lodash/isUndefined';
4
- import { DragSource, DropTarget } from '@pie-lib/drag';
4
+ import { useDraggable, useDroppable } from '@dnd-kit/core';
5
+ import { CSS } from '@dnd-kit/utilities';
5
6
  import { color } from '@pie-lib/render-ui';
6
7
  import { renderMath } from '@pie-lib/math-rendering';
7
- import { withStyles } from '@material-ui/core/styles';
8
+ import { styled } from '@mui/material/styles';
8
9
  import classnames from 'classnames';
9
10
 
10
11
  import { GripIcon } from '../icons';
11
12
 
12
- const useStyles = withStyles((theme) => ({
13
- content: {
14
- border: `solid 0px ${theme.palette.primary.main}`,
15
- '& mjx-frac': {
16
- fontSize: '120% !important',
17
- },
13
+ const StyledContent = styled('span')(({ theme }) => ({
14
+ border: `solid 0px ${theme.palette.primary.main}`,
15
+ '& mjx-frac': {
16
+ fontSize: '120% !important',
18
17
  },
19
- chip: {
18
+ '&.chip': {
20
19
  minWidth: '90px',
21
20
  },
22
- correct: {
21
+ '&.correct': {
23
22
  border: `solid 1px ${color.correct()}`,
24
23
  },
25
- incorrect: {
24
+ '&.incorrect': {
26
25
  border: `solid 1px ${theme.palette.error.main}`,
27
26
  },
28
- selected: {
27
+ '&.selected': {
29
28
  border: `2px solid ${color.primaryDark()} !important`,
30
29
  },
31
30
  }));
32
31
 
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 };
32
+ export function BlankContent({
33
+ n,
34
+ children,
35
+ isDragging,
36
+ isOver,
37
+ dragItem,
38
+ value,
39
+ disabled,
40
+ style: externalStyle
41
+ }) {
42
+ const [hoveredElementSize, setHoveredElementSize] = useState(null);
43
+ const elementRef = useRef(null);
44
+
45
+ const handleClick = (event) => {
46
+ if (elementRef.current) {
47
+ elementRef.current.className = elementRef.current.contains(event.target) ? 'selected' : '';
71
48
  }
72
- return null;
73
- }
49
+ };
74
50
 
75
- componentDidUpdate(prevProps, prevState, snapshot) {
76
- if (this.elementRef && typeof renderMath === 'function') {
77
- renderMath(this.elementRef);
78
- }
51
+ useEffect(() => {
52
+ document.addEventListener('click', handleClick);
53
+ return () => {
54
+ document.removeEventListener('click', handleClick);
55
+ };
56
+ }, []);
79
57
 
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;
58
+ useEffect(() => {
59
+ if (elementRef.current && typeof renderMath === 'function') {
60
+ renderMath(elementRef.current);
88
61
  }
89
-
90
- if (prevProps.isOver && !this.props.isOver && this.state.hoveredElementSize) {
91
- this.setState({ hoveredElementSize: null });
62
+ });
63
+
64
+ useEffect(() => {
65
+ if (isOver && elementRef.current && !hoveredElementSize) {
66
+ const node = elementRef.current;
67
+ setHoveredElementSize({ width: node.offsetWidth, height: node.offsetHeight });
68
+ } else if (!isOver && hoveredElementSize) {
69
+ setHoveredElementSize(null);
92
70
  }
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,
71
+ }, [isOver, hoveredElementSize]);
72
+
73
+ const label = dragItem && isOver ? dragItem.value.value : value.value || '\u00A0';
74
+ const finalLabel = isDragging ? '\u00A0' : label;
75
+ const hasGrip = finalLabel !== '\u00A0';
76
+ const isPreview = dragItem && isOver;
77
+
78
+ return (
79
+ <div
80
+ ref={elementRef}
81
+ style={{
82
+ display: 'inline-flex',
83
+ minWidth: '178px',
84
+ minHeight: '36px',
85
+ background: isPreview ? `${color.defaults.BORDER_LIGHT}` : `${color.defaults.WHITE}`,
86
+ border: isPreview ? `1px solid ${color.defaults.BORDER_DARK}` : `1px solid ${color.defaults.BORDER_LIGHT}`,
87
+ boxSizing: 'border-box',
88
+ borderRadius: '3px',
89
+ overflow: 'hidden',
90
+ position: 'relative',
91
+ padding: '8px 8px 8px 35px',
92
+ width: hoveredElementSize ? hoveredElementSize.width : undefined,
93
+ height: hoveredElementSize ? hoveredElementSize.height : undefined,
94
+ ...externalStyle,
95
+ }}
96
+ data-key={n.key}
97
+ contentEditable={false}
98
+ >
99
+ {hasGrip && (
100
+ <GripIcon
101
+ style={{
102
+ position: 'absolute',
103
+ top: '6px',
104
+ left: '15px',
105
+ color: '#9B9B9B',
138
106
  }}
107
+ contentEditable={false}
139
108
  />
140
- {children}
141
- </div>
142
- );
143
- }
109
+ )}
110
+ <span
111
+ dangerouslySetInnerHTML={{
112
+ __html: finalLabel,
113
+ }}
114
+ />
115
+ {children}
116
+ </div>
117
+ );
144
118
  }
145
119
 
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
- },
120
+ BlankContent.propTypes = {
121
+ n: PropTypes.object,
122
+ children: PropTypes.func,
123
+ isDragging: PropTypes.bool,
124
+ isOver: PropTypes.bool,
125
+ dragItem: PropTypes.object,
126
+ value: PropTypes.object,
127
+ disabled: PropTypes.bool,
128
+ style: PropTypes.object,
178
129
  };
179
130
 
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,
131
+ function DragDropChoice({
132
+ value,
133
+ disabled,
134
+ onChange,
135
+ removeResponse,
136
+ instanceId,
137
+ targetId,
138
+ duplicates,
139
+ n,
140
+ children
141
+ }) {
142
+ // Setup draggable functionality
143
+ const {
144
+ attributes: dragAttributes,
145
+ listeners: dragListeners,
146
+ setNodeRef: setDragNodeRef,
147
+ transform: dragTransform,
148
+ isDragging,
149
+ } = useDraggable({
150
+ id: `drag-${n.key}`,
151
+ disabled: disabled || !value,
152
+ data: {
153
+ id: targetId,
154
+ value: value,
155
+ instanceId: instanceId,
195
156
  fromChoice: true,
196
- };
197
- },
198
- endDrag(props, monitor) {
199
- // this will be null if it did not drop
200
- const dropResult = monitor.getDropResult();
157
+ },
158
+ });
159
+
160
+ // Setup droppable functionality
161
+ const {
162
+ setNodeRef: setDropNodeRef,
163
+ isOver,
164
+ active: dragItem,
165
+ } = useDroppable({
166
+ id: `drop-${n.key}`,
167
+ data: {
168
+ accepts: ['drag-in-the-blank-choice'],
169
+ instanceId: instanceId,
170
+ value: value,
171
+ },
172
+ });
201
173
 
202
- if (!dropResult || (dropResult.dropped && !props.duplicates)) {
203
- const draggedItem = monitor.getItem();
174
+ // Combine refs for both drag and drop
175
+ const setNodeRef = (node) => {
176
+ setDragNodeRef(node);
177
+ setDropNodeRef(node);
178
+ };
204
179
 
205
- if (draggedItem.fromChoice) {
206
- props.removeResponse(draggedItem.value);
207
- }
208
- }
209
- },
180
+ const dragStyle = {
181
+ transform: CSS.Translate.toString(dragTransform),
182
+ opacity: isDragging ? 0.5 : 1,
183
+ };
184
+
185
+ const dragContent = (
186
+ <BlankContent
187
+ n={n}
188
+ isDragging={isDragging}
189
+ isOver={isOver}
190
+ dragItem={dragItem?.data?.current}
191
+ value={value}
192
+ disabled={disabled}
193
+ style={dragStyle}
194
+ >
195
+ {children}
196
+ </BlankContent>
197
+ );
198
+
199
+ const dragEl = !value ? (
200
+ <span ref={setDropNodeRef}>{dragContent}</span>
201
+ ) : (
202
+ <span ref={setNodeRef} {...dragAttributes} {...dragListeners}>
203
+ {dragContent}
204
+ </span>
205
+ );
206
+
207
+ const content = (
208
+ <StyledContent className={classnames(isOver && 'over')}>
209
+ {dragEl}
210
+ </StyledContent>
211
+ );
212
+
213
+ return content;
214
+ }
215
+
216
+ DragDropChoice.propTypes = {
217
+ value: PropTypes.object,
218
+ disabled: PropTypes.bool,
219
+ onChange: PropTypes.func,
220
+ removeResponse: PropTypes.func,
221
+ instanceId: PropTypes.string,
222
+ targetId: PropTypes.string,
223
+ duplicates: PropTypes.bool,
224
+ n: PropTypes.object,
225
+ children: PropTypes.node,
210
226
  };
211
227
 
212
- export default DragSource('drag-in-the-blank-choice', tileSource, (connect, monitor) => ({
213
- connectDragSource: connect.dragSource(),
214
- isDragging: monitor.isDragging(),
215
- }))(DropTile);
228
+ export default DragDropChoice;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { DragProvider } from '@pie-lib/drag';
3
4
  import DragDropTile from './choice';
4
5
 
5
6
  export const onValueChange = (nodeProps, n, value) => {
@@ -38,30 +39,65 @@ const DragDrop = (props) => {
38
39
  const { attributes, data, n, nodeProps, opts } = props;
39
40
  const { inTable } = data;
40
41
 
42
+ const handleDragEnd = (event) => {
43
+ const { active, over } = event;
44
+
45
+ if (!over || !active) return;
46
+
47
+ const draggedData = active.data.current;
48
+ const dropData = over.data.current;
49
+
50
+ // Check if this is a valid drop for drag-in-the-blank
51
+ if (
52
+ draggedData &&
53
+ dropData &&
54
+ dropData.instanceId === draggedData.instanceId &&
55
+ over.id.startsWith('drop-')
56
+ ) {
57
+ // Handle the drop
58
+ const shouldDrop =
59
+ !draggedData.value?.index ||
60
+ !data.index ||
61
+ draggedData.value.index !== data.index;
62
+
63
+ if (shouldDrop) {
64
+ onValueChange(nodeProps, n, draggedData.value);
65
+ }
66
+
67
+ // Remove from source if not duplicates
68
+ if (!opts.options.duplicates && draggedData.fromChoice) {
69
+ onRemoveResponse(nodeProps, draggedData.value);
70
+ }
71
+ }
72
+ };
73
+
41
74
  return (
42
- <span
43
- {...attributes}
44
- style={{
45
- display: 'inline-flex',
46
- minHeight: '50px',
47
- minWidth: '178px',
48
- position: 'relative',
49
- margin: inTable ? '10px' : '0 10px',
50
- cursor: 'pointer',
51
- }}
52
- >
53
- <DragDropTile
54
- n={n}
55
- dragKey={n.key}
56
- targetId="0"
57
- value={data}
58
- duplicates={opts.options.duplicates}
59
- onChange={(value) => onValueChange(nodeProps, n, value)}
60
- removeResponse={(value) => onRemoveResponse(nodeProps, value)}
75
+ <DragProvider onDragEnd={handleDragEnd}>
76
+ <span
77
+ {...attributes}
78
+ style={{
79
+ display: 'inline-flex',
80
+ minHeight: '50px',
81
+ minWidth: '178px',
82
+ position: 'relative',
83
+ margin: inTable ? '10px' : '0 10px',
84
+ cursor: 'pointer',
85
+ }}
61
86
  >
62
- {nodeProps.children}
63
- </DragDropTile>
64
- </span>
87
+ <DragDropTile
88
+ n={n}
89
+ dragKey={n.key}
90
+ targetId="0"
91
+ value={data}
92
+ duplicates={opts.options.duplicates}
93
+ onChange={(value) => onValueChange(nodeProps, n, value)}
94
+ removeResponse={(value) => onRemoveResponse(nodeProps, value)}
95
+ instanceId={nodeProps.editor?.props?.instanceId || 'default'}
96
+ >
97
+ {nodeProps.children}
98
+ </DragDropTile>
99
+ </span>
100
+ </DragProvider>
65
101
  );
66
102
  };
67
103