@manuscripts/body-editor 3.13.15 → 3.13.17
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/cjs/lib/drag-drop-manager.js +128 -0
- package/dist/cjs/versions.js +1 -1
- package/dist/cjs/views/headshot_element.js +22 -0
- package/dist/cjs/views/headshot_grid.js +1 -0
- package/dist/es/lib/drag-drop-manager.js +124 -0
- package/dist/es/versions.js +1 -1
- package/dist/es/views/headshot_element.js +23 -1
- package/dist/es/views/headshot_grid.js +1 -0
- package/dist/types/lib/drag-drop-manager.d.ts +31 -0
- package/dist/types/versions.d.ts +1 -1
- package/dist/types/views/headshot_element.d.ts +3 -0
- package/dist/types/views/headshot_grid.d.ts +1 -0
- package/package.json +2 -2
- package/src/lib/drag-drop-manager.ts +198 -0
- package/src/versions.ts +1 -1
- package/src/views/headshot_element.ts +27 -1
- package/src/views/headshot_grid.ts +2 -0
- package/styles/AdvancedEditor.css +23 -7
- package/styles/Editor.css +13 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DragDropManager = void 0;
|
|
4
|
+
const doc_1 = require("./doc");
|
|
5
|
+
const prosemirror_state_1 = require("prosemirror-state");
|
|
6
|
+
class DragDropManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.handleDragStart = () => {
|
|
9
|
+
DragDropManager.currentNodeId = this.getContext().node.attrs.id;
|
|
10
|
+
this.element.classList.add('dragging');
|
|
11
|
+
};
|
|
12
|
+
this.handleDragEnd = () => {
|
|
13
|
+
DragDropManager.currentNodeId = null;
|
|
14
|
+
this.element.classList.remove('dragging');
|
|
15
|
+
this.clearDropClasses();
|
|
16
|
+
};
|
|
17
|
+
this.handleDragOver = (e) => {
|
|
18
|
+
const draggedId = DragDropManager.currentNodeId;
|
|
19
|
+
const nodeId = this.getContext().node.attrs.id;
|
|
20
|
+
if (DragDropManager.currentNodeId === null ||
|
|
21
|
+
draggedId === null ||
|
|
22
|
+
draggedId === nodeId) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
e.stopPropagation();
|
|
27
|
+
const movePosition = this.getMovePosition(e);
|
|
28
|
+
if (movePosition) {
|
|
29
|
+
const { noActualMove, side, fromPos, targetPos } = movePosition;
|
|
30
|
+
if (!noActualMove && this.canMoveOutsideParent(fromPos, targetPos)) {
|
|
31
|
+
this.element.classList.add(side === 'before' ? 'drop-target-above' : 'drop-target-below');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
this.handleDragLeave = (e) => {
|
|
36
|
+
if (!this.element.contains(e.relatedTarget)) {
|
|
37
|
+
this.clearDropClasses();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
this.handleDrop = (e) => {
|
|
41
|
+
if (DragDropManager.currentNodeId === null) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
e.stopPropagation();
|
|
46
|
+
const movePosition = this.getMovePosition(e);
|
|
47
|
+
if (movePosition) {
|
|
48
|
+
const { noActualMove, fromPos, targetPos, fromNode } = movePosition;
|
|
49
|
+
if (!noActualMove && this.canMoveOutsideParent(fromPos, targetPos)) {
|
|
50
|
+
this.moveNode(fromNode, fromPos, targetPos);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
this.clearDropClasses();
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
setup(config) {
|
|
57
|
+
const { element, restrictToParent = true, getContext, disabled } = config;
|
|
58
|
+
if (disabled) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.element = element;
|
|
62
|
+
this.element.draggable = true;
|
|
63
|
+
this.restrictToParent = restrictToParent;
|
|
64
|
+
this.getContext = getContext;
|
|
65
|
+
const abortController = new AbortController();
|
|
66
|
+
const signal = abortController.signal;
|
|
67
|
+
element.addEventListener('dragstart', this.handleDragStart, { signal });
|
|
68
|
+
element.addEventListener('dragend', this.handleDragEnd, { signal });
|
|
69
|
+
element.addEventListener('dragover', this.handleDragOver, { signal });
|
|
70
|
+
element.addEventListener('dragleave', this.handleDragLeave, { signal });
|
|
71
|
+
element.addEventListener('drop', this.handleDrop, { signal });
|
|
72
|
+
return () => {
|
|
73
|
+
DragDropManager.currentNodeId = null;
|
|
74
|
+
this.clearDropClasses();
|
|
75
|
+
this.element.classList.remove('dragging');
|
|
76
|
+
abortController.abort();
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
clearDropClasses() {
|
|
80
|
+
this.element.classList.remove('drop-target-above', 'drop-target-below');
|
|
81
|
+
}
|
|
82
|
+
getMovePosition(e) {
|
|
83
|
+
if (!DragDropManager.currentNodeId) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const { view, node, pos: toPos } = this.getContext();
|
|
87
|
+
const currentView = (0, doc_1.findNodeByID)(view.state.doc, DragDropManager.currentNodeId);
|
|
88
|
+
if (!currentView) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const side = this.getDropSide(this.element, e.clientY);
|
|
92
|
+
const targetPos = side === 'before' ? toPos : toPos + node.nodeSize;
|
|
93
|
+
const { node: fromNode, pos: fromPos } = currentView;
|
|
94
|
+
return {
|
|
95
|
+
noActualMove: targetPos === fromPos || targetPos === fromPos + fromNode.nodeSize,
|
|
96
|
+
side,
|
|
97
|
+
fromNode,
|
|
98
|
+
fromPos,
|
|
99
|
+
targetPos,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
getDropSide(element, clientY) {
|
|
103
|
+
const { top, bottom } = element.getBoundingClientRect();
|
|
104
|
+
const middleY = (top + bottom) / 2;
|
|
105
|
+
return clientY > middleY ? 'after' : 'before';
|
|
106
|
+
}
|
|
107
|
+
moveNode(fromNode, fromPos, targetPos) {
|
|
108
|
+
const view = this.getContext().view;
|
|
109
|
+
const { state } = view;
|
|
110
|
+
const { tr } = state;
|
|
111
|
+
tr.insert(targetPos, fromNode);
|
|
112
|
+
const mappedFrom = tr.mapping.map(fromPos, -1);
|
|
113
|
+
tr.delete(mappedFrom, mappedFrom + fromNode.nodeSize);
|
|
114
|
+
tr.setSelection(prosemirror_state_1.NodeSelection.near(tr.doc.resolve(tr.mapping.map(targetPos, -1))));
|
|
115
|
+
view.dispatch(tr);
|
|
116
|
+
}
|
|
117
|
+
canMoveOutsideParent(from, to) {
|
|
118
|
+
if (!this.restrictToParent) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
const { view } = this.getContext();
|
|
122
|
+
const $fromPos = view.state.doc.resolve(from);
|
|
123
|
+
const $toPos = view.state.doc.resolve(to);
|
|
124
|
+
return $fromPos.sameParent($toPos);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.DragDropManager = DragDropManager;
|
|
128
|
+
DragDropManager.currentNodeId = null;
|
package/dist/cjs/versions.js
CHANGED
|
@@ -21,10 +21,12 @@ const icons_1 = require("../icons");
|
|
|
21
21
|
const navigation_utils_1 = require("../lib/navigation-utils");
|
|
22
22
|
const base_node_view_1 = require("./base_node_view");
|
|
23
23
|
const creators_1 = require("./creators");
|
|
24
|
+
const drag_drop_manager_1 = require("../lib/drag-drop-manager");
|
|
24
25
|
class HeadshotElement extends base_node_view_1.BaseNodeView {
|
|
25
26
|
constructor() {
|
|
26
27
|
super(...arguments);
|
|
27
28
|
this.elementType = 'div';
|
|
29
|
+
this.ignoreMutation = () => true;
|
|
28
30
|
this.createElement = () => {
|
|
29
31
|
this.dom = document.createElement('div');
|
|
30
32
|
this.dom.classList.add('headshot-element');
|
|
@@ -46,6 +48,12 @@ class HeadshotElement extends base_node_view_1.BaseNodeView {
|
|
|
46
48
|
this.deleteHeadshotButton.addEventListener('click', this.handleDeleteHeadshot);
|
|
47
49
|
this.deleteHeadshotButton.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)(this.handleDeleteHeadshot));
|
|
48
50
|
this.container.appendChild(this.deleteHeadshotButton);
|
|
51
|
+
const dragIcon = document.createElement('div');
|
|
52
|
+
dragIcon.className = 'headshot-drag-icon';
|
|
53
|
+
dragIcon.contentEditable = 'false';
|
|
54
|
+
dragIcon.innerHTML = icons_1.draggableIcon;
|
|
55
|
+
dragIcon.draggable = false;
|
|
56
|
+
this.container.appendChild(dragIcon);
|
|
49
57
|
}
|
|
50
58
|
};
|
|
51
59
|
this.handleDeleteHeadshot = () => {
|
|
@@ -64,9 +72,23 @@ class HeadshotElement extends base_node_view_1.BaseNodeView {
|
|
|
64
72
|
initialise() {
|
|
65
73
|
this.createElement();
|
|
66
74
|
this.updateContents();
|
|
75
|
+
this.setupDragAndDrop();
|
|
76
|
+
}
|
|
77
|
+
setupDragAndDrop() {
|
|
78
|
+
const can = this.props.getCapabilities();
|
|
79
|
+
this.cleanupDragDrop = new drag_drop_manager_1.DragDropManager().setup({
|
|
80
|
+
element: this.dom,
|
|
81
|
+
getContext: () => ({
|
|
82
|
+
view: this.view,
|
|
83
|
+
node: this.node,
|
|
84
|
+
pos: this.getPos(),
|
|
85
|
+
}),
|
|
86
|
+
disabled: !can.editArticle,
|
|
87
|
+
});
|
|
67
88
|
}
|
|
68
89
|
destroy() {
|
|
69
90
|
this.deleteHeadshotButton?.removeEventListener('click', this.handleDeleteHeadshot);
|
|
91
|
+
this.cleanupDragDrop?.();
|
|
70
92
|
super.destroy();
|
|
71
93
|
}
|
|
72
94
|
}
|
|
@@ -29,6 +29,7 @@ class HeadshotGridView extends block_view_1.default {
|
|
|
29
29
|
constructor() {
|
|
30
30
|
super(...arguments);
|
|
31
31
|
this.elementType = 'div';
|
|
32
|
+
this.ignoreMutation = () => true;
|
|
32
33
|
this.createElement = () => {
|
|
33
34
|
this.container = document.createElement('div');
|
|
34
35
|
this.container.classList.add('block', 'headshot-grid-container');
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { findNodeByID } from './doc';
|
|
2
|
+
import { NodeSelection } from 'prosemirror-state';
|
|
3
|
+
export class DragDropManager {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.handleDragStart = () => {
|
|
6
|
+
DragDropManager.currentNodeId = this.getContext().node.attrs.id;
|
|
7
|
+
this.element.classList.add('dragging');
|
|
8
|
+
};
|
|
9
|
+
this.handleDragEnd = () => {
|
|
10
|
+
DragDropManager.currentNodeId = null;
|
|
11
|
+
this.element.classList.remove('dragging');
|
|
12
|
+
this.clearDropClasses();
|
|
13
|
+
};
|
|
14
|
+
this.handleDragOver = (e) => {
|
|
15
|
+
const draggedId = DragDropManager.currentNodeId;
|
|
16
|
+
const nodeId = this.getContext().node.attrs.id;
|
|
17
|
+
if (DragDropManager.currentNodeId === null ||
|
|
18
|
+
draggedId === null ||
|
|
19
|
+
draggedId === nodeId) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
e.stopPropagation();
|
|
24
|
+
const movePosition = this.getMovePosition(e);
|
|
25
|
+
if (movePosition) {
|
|
26
|
+
const { noActualMove, side, fromPos, targetPos } = movePosition;
|
|
27
|
+
if (!noActualMove && this.canMoveOutsideParent(fromPos, targetPos)) {
|
|
28
|
+
this.element.classList.add(side === 'before' ? 'drop-target-above' : 'drop-target-below');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
this.handleDragLeave = (e) => {
|
|
33
|
+
if (!this.element.contains(e.relatedTarget)) {
|
|
34
|
+
this.clearDropClasses();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
this.handleDrop = (e) => {
|
|
38
|
+
if (DragDropManager.currentNodeId === null) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
e.stopPropagation();
|
|
43
|
+
const movePosition = this.getMovePosition(e);
|
|
44
|
+
if (movePosition) {
|
|
45
|
+
const { noActualMove, fromPos, targetPos, fromNode } = movePosition;
|
|
46
|
+
if (!noActualMove && this.canMoveOutsideParent(fromPos, targetPos)) {
|
|
47
|
+
this.moveNode(fromNode, fromPos, targetPos);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
this.clearDropClasses();
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
setup(config) {
|
|
54
|
+
const { element, restrictToParent = true, getContext, disabled } = config;
|
|
55
|
+
if (disabled) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.element = element;
|
|
59
|
+
this.element.draggable = true;
|
|
60
|
+
this.restrictToParent = restrictToParent;
|
|
61
|
+
this.getContext = getContext;
|
|
62
|
+
const abortController = new AbortController();
|
|
63
|
+
const signal = abortController.signal;
|
|
64
|
+
element.addEventListener('dragstart', this.handleDragStart, { signal });
|
|
65
|
+
element.addEventListener('dragend', this.handleDragEnd, { signal });
|
|
66
|
+
element.addEventListener('dragover', this.handleDragOver, { signal });
|
|
67
|
+
element.addEventListener('dragleave', this.handleDragLeave, { signal });
|
|
68
|
+
element.addEventListener('drop', this.handleDrop, { signal });
|
|
69
|
+
return () => {
|
|
70
|
+
DragDropManager.currentNodeId = null;
|
|
71
|
+
this.clearDropClasses();
|
|
72
|
+
this.element.classList.remove('dragging');
|
|
73
|
+
abortController.abort();
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
clearDropClasses() {
|
|
77
|
+
this.element.classList.remove('drop-target-above', 'drop-target-below');
|
|
78
|
+
}
|
|
79
|
+
getMovePosition(e) {
|
|
80
|
+
if (!DragDropManager.currentNodeId) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const { view, node, pos: toPos } = this.getContext();
|
|
84
|
+
const currentView = findNodeByID(view.state.doc, DragDropManager.currentNodeId);
|
|
85
|
+
if (!currentView) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const side = this.getDropSide(this.element, e.clientY);
|
|
89
|
+
const targetPos = side === 'before' ? toPos : toPos + node.nodeSize;
|
|
90
|
+
const { node: fromNode, pos: fromPos } = currentView;
|
|
91
|
+
return {
|
|
92
|
+
noActualMove: targetPos === fromPos || targetPos === fromPos + fromNode.nodeSize,
|
|
93
|
+
side,
|
|
94
|
+
fromNode,
|
|
95
|
+
fromPos,
|
|
96
|
+
targetPos,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
getDropSide(element, clientY) {
|
|
100
|
+
const { top, bottom } = element.getBoundingClientRect();
|
|
101
|
+
const middleY = (top + bottom) / 2;
|
|
102
|
+
return clientY > middleY ? 'after' : 'before';
|
|
103
|
+
}
|
|
104
|
+
moveNode(fromNode, fromPos, targetPos) {
|
|
105
|
+
const view = this.getContext().view;
|
|
106
|
+
const { state } = view;
|
|
107
|
+
const { tr } = state;
|
|
108
|
+
tr.insert(targetPos, fromNode);
|
|
109
|
+
const mappedFrom = tr.mapping.map(fromPos, -1);
|
|
110
|
+
tr.delete(mappedFrom, mappedFrom + fromNode.nodeSize);
|
|
111
|
+
tr.setSelection(NodeSelection.near(tr.doc.resolve(tr.mapping.map(targetPos, -1))));
|
|
112
|
+
view.dispatch(tr);
|
|
113
|
+
}
|
|
114
|
+
canMoveOutsideParent(from, to) {
|
|
115
|
+
if (!this.restrictToParent) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
const { view } = this.getContext();
|
|
119
|
+
const $fromPos = view.state.doc.resolve(from);
|
|
120
|
+
const $toPos = view.state.doc.resolve(to);
|
|
121
|
+
return $fromPos.sameParent($toPos);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
DragDropManager.currentNodeId = null;
|
package/dist/es/versions.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '3.13.
|
|
1
|
+
export const VERSION = '3.13.17';
|
|
2
2
|
export const MATHJAX_VERSION = '3.2.2';
|
|
@@ -14,14 +14,16 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { TextSelection } from 'prosemirror-state';
|
|
17
|
-
import { plusIcon } from '../icons';
|
|
17
|
+
import { draggableIcon, plusIcon } from '../icons';
|
|
18
18
|
import { handleEnterKey } from '../lib/navigation-utils';
|
|
19
19
|
import { BaseNodeView } from './base_node_view';
|
|
20
20
|
import { createNodeView } from './creators';
|
|
21
|
+
import { DragDropManager } from '../lib/drag-drop-manager';
|
|
21
22
|
export class HeadshotElement extends BaseNodeView {
|
|
22
23
|
constructor() {
|
|
23
24
|
super(...arguments);
|
|
24
25
|
this.elementType = 'div';
|
|
26
|
+
this.ignoreMutation = () => true;
|
|
25
27
|
this.createElement = () => {
|
|
26
28
|
this.dom = document.createElement('div');
|
|
27
29
|
this.dom.classList.add('headshot-element');
|
|
@@ -43,6 +45,12 @@ export class HeadshotElement extends BaseNodeView {
|
|
|
43
45
|
this.deleteHeadshotButton.addEventListener('click', this.handleDeleteHeadshot);
|
|
44
46
|
this.deleteHeadshotButton.addEventListener('keydown', handleEnterKey(this.handleDeleteHeadshot));
|
|
45
47
|
this.container.appendChild(this.deleteHeadshotButton);
|
|
48
|
+
const dragIcon = document.createElement('div');
|
|
49
|
+
dragIcon.className = 'headshot-drag-icon';
|
|
50
|
+
dragIcon.contentEditable = 'false';
|
|
51
|
+
dragIcon.innerHTML = draggableIcon;
|
|
52
|
+
dragIcon.draggable = false;
|
|
53
|
+
this.container.appendChild(dragIcon);
|
|
46
54
|
}
|
|
47
55
|
};
|
|
48
56
|
this.handleDeleteHeadshot = () => {
|
|
@@ -61,9 +69,23 @@ export class HeadshotElement extends BaseNodeView {
|
|
|
61
69
|
initialise() {
|
|
62
70
|
this.createElement();
|
|
63
71
|
this.updateContents();
|
|
72
|
+
this.setupDragAndDrop();
|
|
73
|
+
}
|
|
74
|
+
setupDragAndDrop() {
|
|
75
|
+
const can = this.props.getCapabilities();
|
|
76
|
+
this.cleanupDragDrop = new DragDropManager().setup({
|
|
77
|
+
element: this.dom,
|
|
78
|
+
getContext: () => ({
|
|
79
|
+
view: this.view,
|
|
80
|
+
node: this.node,
|
|
81
|
+
pos: this.getPos(),
|
|
82
|
+
}),
|
|
83
|
+
disabled: !can.editArticle,
|
|
84
|
+
});
|
|
64
85
|
}
|
|
65
86
|
destroy() {
|
|
66
87
|
this.deleteHeadshotButton?.removeEventListener('click', this.handleDeleteHeadshot);
|
|
88
|
+
this.cleanupDragDrop?.();
|
|
67
89
|
super.destroy();
|
|
68
90
|
}
|
|
69
91
|
}
|
|
@@ -23,6 +23,7 @@ export class HeadshotGridView extends BlockView {
|
|
|
23
23
|
constructor() {
|
|
24
24
|
super(...arguments);
|
|
25
25
|
this.elementType = 'div';
|
|
26
|
+
this.ignoreMutation = () => true;
|
|
26
27
|
this.createElement = () => {
|
|
27
28
|
this.container = document.createElement('div');
|
|
28
29
|
this.container.classList.add('block', 'headshot-grid-container');
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ManuscriptNode } from '@manuscripts/transform';
|
|
2
|
+
import { EditorView } from 'prosemirror-view';
|
|
3
|
+
interface DragDropContext {
|
|
4
|
+
view: EditorView;
|
|
5
|
+
node: ManuscriptNode;
|
|
6
|
+
pos: number;
|
|
7
|
+
}
|
|
8
|
+
interface DragDropConfig {
|
|
9
|
+
element: HTMLElement;
|
|
10
|
+
getContext: () => DragDropContext;
|
|
11
|
+
restrictToParent?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class DragDropManager {
|
|
15
|
+
private static currentNodeId;
|
|
16
|
+
private element;
|
|
17
|
+
private restrictToParent;
|
|
18
|
+
private getContext;
|
|
19
|
+
setup(config: DragDropConfig): (() => void) | undefined;
|
|
20
|
+
private handleDragStart;
|
|
21
|
+
private handleDragEnd;
|
|
22
|
+
private handleDragOver;
|
|
23
|
+
private handleDragLeave;
|
|
24
|
+
private handleDrop;
|
|
25
|
+
private clearDropClasses;
|
|
26
|
+
private getMovePosition;
|
|
27
|
+
private getDropSide;
|
|
28
|
+
private moveNode;
|
|
29
|
+
private canMoveOutsideParent;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
package/dist/types/versions.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "3.13.
|
|
1
|
+
export declare const VERSION = "3.13.17";
|
|
2
2
|
export declare const MATHJAX_VERSION = "3.2.2";
|
|
@@ -20,9 +20,12 @@ export declare class HeadshotElement extends BaseNodeView<Trackable<HeadshotElem
|
|
|
20
20
|
elementType: string;
|
|
21
21
|
private container;
|
|
22
22
|
private deleteHeadshotButton;
|
|
23
|
+
private cleanupDragDrop;
|
|
24
|
+
ignoreMutation: () => boolean;
|
|
23
25
|
initialise(): void;
|
|
24
26
|
createElement: () => void;
|
|
25
27
|
private handleDeleteHeadshot;
|
|
28
|
+
private setupDragAndDrop;
|
|
26
29
|
destroy(): void;
|
|
27
30
|
}
|
|
28
31
|
declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<HeadshotElement>;
|
|
@@ -20,6 +20,7 @@ export declare class HeadshotGridView extends BlockView<HeadshotGridNode> {
|
|
|
20
20
|
private container;
|
|
21
21
|
private addHeadshotButton;
|
|
22
22
|
private removeKeydownListener?;
|
|
23
|
+
ignoreMutation: () => boolean;
|
|
23
24
|
createElement: () => void;
|
|
24
25
|
private handleAddHeadshot;
|
|
25
26
|
destroy(): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manuscripts/body-editor",
|
|
3
3
|
"description": "Prosemirror components for editing and viewing manuscripts",
|
|
4
|
-
"version": "3.13.
|
|
4
|
+
"version": "3.13.17",
|
|
5
5
|
"repository": "github:Atypon-OpenSource/manuscripts-body-editor",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"main": "dist/cjs",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@iarna/word-count": "1.1.2",
|
|
44
44
|
"@manuscripts/style-guide": "3.6.5",
|
|
45
45
|
"@manuscripts/track-changes-plugin": "2.4.0",
|
|
46
|
-
"@manuscripts/transform": "4.4.
|
|
46
|
+
"@manuscripts/transform": "4.4.6",
|
|
47
47
|
"@popperjs/core": "2.11.8",
|
|
48
48
|
"citeproc": "2.4.63",
|
|
49
49
|
"codemirror": "5.65.19",
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* © 2026 Atypon Systems LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { ManuscriptNode } from '@manuscripts/transform'
|
|
17
|
+
import { EditorView } from 'prosemirror-view'
|
|
18
|
+
|
|
19
|
+
import { findNodeByID } from './doc'
|
|
20
|
+
import { NodeSelection } from 'prosemirror-state'
|
|
21
|
+
|
|
22
|
+
interface DragDropContext {
|
|
23
|
+
view: EditorView
|
|
24
|
+
node: ManuscriptNode
|
|
25
|
+
pos: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface DragDropConfig {
|
|
29
|
+
element: HTMLElement
|
|
30
|
+
getContext: () => DragDropContext
|
|
31
|
+
restrictToParent?: boolean
|
|
32
|
+
disabled?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class DragDropManager {
|
|
36
|
+
private static currentNodeId: string | null = null
|
|
37
|
+
|
|
38
|
+
private element: HTMLElement
|
|
39
|
+
private restrictToParent: boolean
|
|
40
|
+
private getContext: () => DragDropContext
|
|
41
|
+
|
|
42
|
+
setup(config: DragDropConfig) {
|
|
43
|
+
const { element, restrictToParent = true, getContext, disabled } = config
|
|
44
|
+
if (disabled) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.element = element
|
|
49
|
+
this.element.draggable = true
|
|
50
|
+
this.restrictToParent = restrictToParent
|
|
51
|
+
this.getContext = getContext
|
|
52
|
+
|
|
53
|
+
const abortController = new AbortController()
|
|
54
|
+
const signal = abortController.signal
|
|
55
|
+
|
|
56
|
+
element.addEventListener('dragstart', this.handleDragStart, { signal })
|
|
57
|
+
element.addEventListener('dragend', this.handleDragEnd, { signal })
|
|
58
|
+
element.addEventListener('dragover', this.handleDragOver, { signal })
|
|
59
|
+
element.addEventListener('dragleave', this.handleDragLeave, { signal })
|
|
60
|
+
element.addEventListener('drop', this.handleDrop, { signal })
|
|
61
|
+
|
|
62
|
+
return () => {
|
|
63
|
+
DragDropManager.currentNodeId = null
|
|
64
|
+
this.clearDropClasses()
|
|
65
|
+
this.element.classList.remove('dragging')
|
|
66
|
+
abortController.abort()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private handleDragStart = () => {
|
|
71
|
+
DragDropManager.currentNodeId = this.getContext().node.attrs.id
|
|
72
|
+
this.element.classList.add('dragging')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private handleDragEnd = () => {
|
|
76
|
+
DragDropManager.currentNodeId = null
|
|
77
|
+
this.element.classList.remove('dragging')
|
|
78
|
+
this.clearDropClasses()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private handleDragOver = (e: DragEvent) => {
|
|
82
|
+
const draggedId = DragDropManager.currentNodeId
|
|
83
|
+
const nodeId = this.getContext().node.attrs.id
|
|
84
|
+
if (
|
|
85
|
+
DragDropManager.currentNodeId === null ||
|
|
86
|
+
draggedId === null ||
|
|
87
|
+
draggedId === nodeId
|
|
88
|
+
) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
e.preventDefault()
|
|
93
|
+
e.stopPropagation()
|
|
94
|
+
|
|
95
|
+
const movePosition = this.getMovePosition(e)
|
|
96
|
+
if (movePosition) {
|
|
97
|
+
const { noActualMove, side, fromPos, targetPos } = movePosition
|
|
98
|
+
if (!noActualMove && this.canMoveOutsideParent(fromPos, targetPos)) {
|
|
99
|
+
this.element.classList.add(
|
|
100
|
+
side === 'before' ? 'drop-target-above' : 'drop-target-below'
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private handleDragLeave = (e: DragEvent) => {
|
|
107
|
+
if (!this.element.contains(e.relatedTarget as Node)) {
|
|
108
|
+
this.clearDropClasses()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private handleDrop = (e: DragEvent) => {
|
|
113
|
+
if (DragDropManager.currentNodeId === null) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
e.preventDefault()
|
|
118
|
+
e.stopPropagation()
|
|
119
|
+
|
|
120
|
+
const movePosition = this.getMovePosition(e)
|
|
121
|
+
if (movePosition) {
|
|
122
|
+
const { noActualMove, fromPos, targetPos, fromNode } = movePosition
|
|
123
|
+
if (!noActualMove && this.canMoveOutsideParent(fromPos, targetPos)) {
|
|
124
|
+
this.moveNode(fromNode, fromPos, targetPos)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.clearDropClasses()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private clearDropClasses() {
|
|
131
|
+
this.element.classList.remove('drop-target-above', 'drop-target-below')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private getMovePosition(e: DragEvent) {
|
|
135
|
+
if (!DragDropManager.currentNodeId) {
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { view, node, pos: toPos } = this.getContext()
|
|
140
|
+
const currentView = findNodeByID(
|
|
141
|
+
view.state.doc,
|
|
142
|
+
DragDropManager.currentNodeId
|
|
143
|
+
)
|
|
144
|
+
if (!currentView) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const side = this.getDropSide(this.element, e.clientY)
|
|
149
|
+
const targetPos = side === 'before' ? toPos : toPos + node.nodeSize
|
|
150
|
+
|
|
151
|
+
const { node: fromNode, pos: fromPos } = currentView
|
|
152
|
+
return {
|
|
153
|
+
// No-move if dropping at the node’s start or end position
|
|
154
|
+
noActualMove:
|
|
155
|
+
targetPos === fromPos || targetPos === fromPos + fromNode.nodeSize,
|
|
156
|
+
side,
|
|
157
|
+
fromNode,
|
|
158
|
+
fromPos,
|
|
159
|
+
targetPos,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private getDropSide(
|
|
164
|
+
element: HTMLElement,
|
|
165
|
+
clientY: number
|
|
166
|
+
): 'before' | 'after' {
|
|
167
|
+
const { top, bottom } = element.getBoundingClientRect()
|
|
168
|
+
const middleY = (top + bottom) / 2
|
|
169
|
+
return clientY > middleY ? 'after' : 'before'
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private moveNode(
|
|
173
|
+
fromNode: ManuscriptNode,
|
|
174
|
+
fromPos: number,
|
|
175
|
+
targetPos: number
|
|
176
|
+
) {
|
|
177
|
+
const view = this.getContext().view
|
|
178
|
+
const { state } = view
|
|
179
|
+
const { tr } = state
|
|
180
|
+
tr.insert(targetPos, fromNode)
|
|
181
|
+
const mappedFrom = tr.mapping.map(fromPos, -1)
|
|
182
|
+
tr.delete(mappedFrom, mappedFrom + fromNode.nodeSize)
|
|
183
|
+
tr.setSelection(
|
|
184
|
+
NodeSelection.near(tr.doc.resolve(tr.mapping.map(targetPos, -1)))
|
|
185
|
+
)
|
|
186
|
+
view.dispatch(tr)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private canMoveOutsideParent(from: number, to: number) {
|
|
190
|
+
if (!this.restrictToParent) {
|
|
191
|
+
return true
|
|
192
|
+
}
|
|
193
|
+
const { view } = this.getContext()
|
|
194
|
+
const $fromPos = view.state.doc.resolve(from)
|
|
195
|
+
const $toPos = view.state.doc.resolve(to)
|
|
196
|
+
return $fromPos.sameParent($toPos)
|
|
197
|
+
}
|
|
198
|
+
}
|
package/src/versions.ts
CHANGED
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
import { HeadshotElementNode } from '@manuscripts/transform'
|
|
18
18
|
import { TextSelection } from 'prosemirror-state'
|
|
19
19
|
|
|
20
|
-
import { plusIcon } from '../icons'
|
|
20
|
+
import { draggableIcon, plusIcon } from '../icons'
|
|
21
21
|
import { handleEnterKey } from '../lib/navigation-utils'
|
|
22
22
|
import { Trackable } from '../types'
|
|
23
23
|
import { BaseNodeView } from './base_node_view'
|
|
24
24
|
import { createNodeView } from './creators'
|
|
25
|
+
import { DragDropManager } from '../lib/drag-drop-manager'
|
|
25
26
|
|
|
26
27
|
export class HeadshotElement extends BaseNodeView<
|
|
27
28
|
Trackable<HeadshotElementNode>
|
|
@@ -29,10 +30,14 @@ export class HeadshotElement extends BaseNodeView<
|
|
|
29
30
|
public elementType = 'div'
|
|
30
31
|
private container: HTMLElement
|
|
31
32
|
private deleteHeadshotButton: HTMLElement
|
|
33
|
+
private cleanupDragDrop: (() => void) | undefined
|
|
34
|
+
|
|
35
|
+
public ignoreMutation = () => true
|
|
32
36
|
|
|
33
37
|
public initialise() {
|
|
34
38
|
this.createElement()
|
|
35
39
|
this.updateContents()
|
|
40
|
+
this.setupDragAndDrop()
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
public createElement = () => {
|
|
@@ -66,6 +71,13 @@ export class HeadshotElement extends BaseNodeView<
|
|
|
66
71
|
handleEnterKey(this.handleDeleteHeadshot)
|
|
67
72
|
)
|
|
68
73
|
this.container.appendChild(this.deleteHeadshotButton)
|
|
74
|
+
|
|
75
|
+
const dragIcon = document.createElement('div')
|
|
76
|
+
dragIcon.className = 'headshot-drag-icon'
|
|
77
|
+
dragIcon.contentEditable = 'false'
|
|
78
|
+
dragIcon.innerHTML = draggableIcon
|
|
79
|
+
dragIcon.draggable = false
|
|
80
|
+
this.container.appendChild(dragIcon)
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
|
|
@@ -86,11 +98,25 @@ export class HeadshotElement extends BaseNodeView<
|
|
|
86
98
|
dispatch(tr)
|
|
87
99
|
}
|
|
88
100
|
|
|
101
|
+
private setupDragAndDrop() {
|
|
102
|
+
const can = this.props.getCapabilities()
|
|
103
|
+
this.cleanupDragDrop = new DragDropManager().setup({
|
|
104
|
+
element: this.dom,
|
|
105
|
+
getContext: () => ({
|
|
106
|
+
view: this.view,
|
|
107
|
+
node: this.node,
|
|
108
|
+
pos: this.getPos(),
|
|
109
|
+
}),
|
|
110
|
+
disabled: !can.editArticle,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
89
114
|
public destroy() {
|
|
90
115
|
this.deleteHeadshotButton?.removeEventListener(
|
|
91
116
|
'click',
|
|
92
117
|
this.handleDeleteHeadshot
|
|
93
118
|
)
|
|
119
|
+
this.cleanupDragDrop?.()
|
|
94
120
|
super.destroy()
|
|
95
121
|
}
|
|
96
122
|
}
|
|
@@ -35,6 +35,8 @@ export class HeadshotGridView extends BlockView<HeadshotGridNode> {
|
|
|
35
35
|
private addHeadshotButton: HTMLElement
|
|
36
36
|
private removeKeydownListener?: () => void
|
|
37
37
|
|
|
38
|
+
public ignoreMutation = () => true
|
|
39
|
+
|
|
38
40
|
public createElement = () => {
|
|
39
41
|
this.container = document.createElement('div')
|
|
40
42
|
this.container.classList.add('block', 'headshot-grid-container')
|
|
@@ -13,9 +13,6 @@
|
|
|
13
13
|
--box-element-bg-color: #fafafa;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
* {
|
|
17
|
-
scroll-behavior: smooth;
|
|
18
|
-
}
|
|
19
16
|
|
|
20
17
|
.button-reset {
|
|
21
18
|
border: none;
|
|
@@ -824,7 +821,7 @@ span.comment-marker {
|
|
|
824
821
|
.selected-suggestion:not(.block-container):not(.graphical-abstract):not(
|
|
825
822
|
.keywords
|
|
826
823
|
):not(figure):not(figure .equation):not(.inconsistency-highlight):not(
|
|
827
|
-
.supplement-item:is([data-track-op='move'])
|
|
824
|
+
.supplement-item:is([data-track-op='move']), .headshot-element:is([data-track-op='move'])
|
|
828
825
|
),
|
|
829
826
|
.footnote-marker-selected {
|
|
830
827
|
border-width: 2px 0 2px 0 !important;
|
|
@@ -1780,7 +1777,7 @@ th:hover > .table-context-menu-button,
|
|
|
1780
1777
|
|
|
1781
1778
|
.ProseMirror .headshot-grid-container .headshot-grid-block .headshot-element {
|
|
1782
1779
|
border-radius: 4px;
|
|
1783
|
-
border: 1px solid #E2E2E2
|
|
1780
|
+
border: 1px solid #E2E2E2;
|
|
1784
1781
|
background: #FFF;
|
|
1785
1782
|
position: relative;
|
|
1786
1783
|
}
|
|
@@ -1900,6 +1897,24 @@ th:hover > .table-context-menu-button,
|
|
|
1900
1897
|
z-index: 10;
|
|
1901
1898
|
}
|
|
1902
1899
|
|
|
1900
|
+
.ProseMirror .headshot-drag-icon {
|
|
1901
|
+
position: absolute;
|
|
1902
|
+
top: 41%;
|
|
1903
|
+
left: -6px;
|
|
1904
|
+
border-radius: 8px;
|
|
1905
|
+
border: 1px solid #E2E2E2;
|
|
1906
|
+
background: #FFF;
|
|
1907
|
+
display: flex;
|
|
1908
|
+
flex-direction: column;
|
|
1909
|
+
justify-content: center;
|
|
1910
|
+
align-items: center;
|
|
1911
|
+
width: 10px;
|
|
1912
|
+
height: 22px;
|
|
1913
|
+
padding: 3px 0;
|
|
1914
|
+
cursor: grab;
|
|
1915
|
+
visibility: hidden;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1903
1918
|
.ProseMirror .headshot-figure .headshot-remove-button {
|
|
1904
1919
|
margin: 0;
|
|
1905
1920
|
top: -6%;
|
|
@@ -1917,6 +1932,7 @@ th:hover > .table-context-menu-button,
|
|
|
1917
1932
|
height: 8px;
|
|
1918
1933
|
}
|
|
1919
1934
|
|
|
1935
|
+
.ProseMirror .headshot-grid-block .headshot-element:not(:only-child):hover .headshot-drag-icon,
|
|
1920
1936
|
.ProseMirror .headshot-element:hover .block > .headshot-remove-button,
|
|
1921
1937
|
.ProseMirror .headshot-figure:hover .headshot-remove-button,
|
|
1922
1938
|
.ProseMirror .headshot-element:focus-within .headshot-remove-button {
|
|
@@ -2044,8 +2060,8 @@ th:hover > .table-context-menu-button,
|
|
|
2044
2060
|
position: relative;
|
|
2045
2061
|
}
|
|
2046
2062
|
|
|
2047
|
-
/* Right padding so abstract titles do not run under translation widgets.
|
|
2048
|
-
* If we later block editing these titles, this padding can go away.
|
|
2063
|
+
/* Right padding so abstract titles do not run under translation widgets.
|
|
2064
|
+
* If we later block editing these titles, this padding can go away.
|
|
2049
2065
|
*/
|
|
2050
2066
|
.ProseMirror .abstracts .block-section .block-section_title h1,
|
|
2051
2067
|
.ProseMirror .abstracts .block-trans_abstract .block-section_title h1{
|
package/styles/Editor.css
CHANGED
|
@@ -1309,11 +1309,24 @@
|
|
|
1309
1309
|
font-style: normal;
|
|
1310
1310
|
}
|
|
1311
1311
|
|
|
1312
|
+
.ProseMirror .headshot-element.drop-target-above {
|
|
1313
|
+
border-top: 3px solid #20aedf !important;
|
|
1314
|
+
border-top-left-radius: 0 !important;
|
|
1315
|
+
border-top-right-radius: 0 !important;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
.ProseMirror .headshot-element.drop-target-below {
|
|
1319
|
+
border-bottom: 3px solid #20aedf !important;
|
|
1320
|
+
border-bottom-left-radius: 0 !important;
|
|
1321
|
+
border-bottom-right-radius: 0 !important;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1312
1324
|
.ProseMirror .supplement-item.drop-target-above,
|
|
1313
1325
|
.ProseMirror .supplement-item.drop-target-below {
|
|
1314
1326
|
border: 2px dotted #20aedf !important;
|
|
1315
1327
|
}
|
|
1316
1328
|
|
|
1329
|
+
.ProseMirror .headshot-element.dragging,
|
|
1317
1330
|
.ProseMirror .supplement-item.dragging {
|
|
1318
1331
|
background-color: #f2f2f2 !important;
|
|
1319
1332
|
opacity: 0.3;
|