lakelib 0.1.7 → 0.1.8
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/lake.css +21 -2
- package/dist/lake.min.js +15 -9
- package/dist/lake.min.js.map +1 -1
- package/lib/lake.css +21 -2
- package/lib/lake.js +317 -18
- package/lib/lake.js.map +1 -1
- package/lib/types/editor.d.ts +4 -0
- package/lib/types/models/nodes.d.ts +1 -0
- package/lib/types/models/range.d.ts +1 -0
- package/lib/types/plugins/drop.d.ts +3 -0
- package/package.json +1 -1
package/lib/lake.css
CHANGED
|
@@ -32,9 +32,11 @@
|
|
|
32
32
|
--input-outline: 2px solid #69b1ff;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
.lake-container-wrapper {
|
|
36
|
+
position: relative;
|
|
37
|
+
}
|
|
35
38
|
.lake-container {
|
|
36
39
|
box-sizing: content-box;
|
|
37
|
-
position: relative;
|
|
38
40
|
font-family: var(--font-family);
|
|
39
41
|
font-size: 16px;
|
|
40
42
|
font-weight: normal;
|
|
@@ -73,6 +75,23 @@
|
|
|
73
75
|
color: var(--link-hover-color);
|
|
74
76
|
text-decoration: underline;
|
|
75
77
|
}
|
|
78
|
+
.lake-drop-indication {
|
|
79
|
+
position: absolute;
|
|
80
|
+
height: 2px;
|
|
81
|
+
background-color: #1677ff;
|
|
82
|
+
z-index: 1;
|
|
83
|
+
pointer-events: none;
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
86
|
+
.lake-drop-indication svg {
|
|
87
|
+
position: absolute;
|
|
88
|
+
top: -7.5px;
|
|
89
|
+
left: -10px;
|
|
90
|
+
width: 16px;
|
|
91
|
+
height: 16px;
|
|
92
|
+
fill: #1677ff;
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
}
|
|
76
95
|
|
|
77
96
|
.lake-container strong {
|
|
78
97
|
font-weight: 600;
|
|
@@ -862,7 +881,7 @@ lake-box[name="image"] .lake-box-selected .lake-image-error {
|
|
|
862
881
|
|
|
863
882
|
/* code block */
|
|
864
883
|
lake-box[name="codeBlock"] {
|
|
865
|
-
margin: 16px
|
|
884
|
+
margin-bottom: 16px;
|
|
866
885
|
}
|
|
867
886
|
lake-box[name="codeBlock"] .lake-box-focused .lake-code-block,
|
|
868
887
|
lake-box[name="codeBlock"] .lake-box-activated .lake-code-block {
|
package/lib/lake.js
CHANGED
|
@@ -446,7 +446,7 @@ function debug(...data) {
|
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
-
//
|
|
449
|
+
// A key-value object for storing all events.
|
|
450
450
|
// value is an array which include types and listeners.
|
|
451
451
|
const eventData = {};
|
|
452
452
|
let lastNodeId = 0;
|
|
@@ -733,6 +733,17 @@ class Nodes {
|
|
|
733
733
|
closestContainer() {
|
|
734
734
|
return this.closest('div[contenteditable="true"]');
|
|
735
735
|
}
|
|
736
|
+
// Traverses the first node and its parents until it finds an element which can scroll.
|
|
737
|
+
closestScroller() {
|
|
738
|
+
let parent = this.eq(0);
|
|
739
|
+
while (parent.length > 0 && parent.isElement) {
|
|
740
|
+
if (['scroll', 'auto'].indexOf(parent.computedCSS('overflow-y')) >= 0) {
|
|
741
|
+
return parent;
|
|
742
|
+
}
|
|
743
|
+
parent = parent.parent();
|
|
744
|
+
}
|
|
745
|
+
return new Nodes();
|
|
746
|
+
}
|
|
736
747
|
// Returns the parent of the first node.
|
|
737
748
|
parent() {
|
|
738
749
|
const node = this.get(0);
|
|
@@ -1299,6 +1310,59 @@ class Range {
|
|
|
1299
1310
|
get() {
|
|
1300
1311
|
return this.range;
|
|
1301
1312
|
}
|
|
1313
|
+
// Returns the size and position of the range.
|
|
1314
|
+
getRect() {
|
|
1315
|
+
const range = this.clone();
|
|
1316
|
+
let rect;
|
|
1317
|
+
let x;
|
|
1318
|
+
let width;
|
|
1319
|
+
if (range.isCollapsed) {
|
|
1320
|
+
let reference = 'left';
|
|
1321
|
+
if (range.startNode.isElement) {
|
|
1322
|
+
const children = range.startNode.children();
|
|
1323
|
+
if (children.length === 0) {
|
|
1324
|
+
range.selectNode(range.startNode);
|
|
1325
|
+
}
|
|
1326
|
+
else if (range.startOffset < children.length) {
|
|
1327
|
+
range.setEnd(range.startNode, range.startOffset + 1);
|
|
1328
|
+
}
|
|
1329
|
+
else {
|
|
1330
|
+
range.setStart(range.startNode, range.startOffset - 1);
|
|
1331
|
+
reference = 'right';
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
const text = range.startNode.text();
|
|
1336
|
+
if (range.startOffset < text.length) {
|
|
1337
|
+
range.setEnd(range.startNode, range.startOffset + 1);
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
range.setStart(range.startNode, range.startOffset - 1);
|
|
1341
|
+
reference = 'right';
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
rect = range.get().getBoundingClientRect();
|
|
1345
|
+
if (reference === 'left') {
|
|
1346
|
+
x = rect.x;
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
x = rect.right;
|
|
1350
|
+
}
|
|
1351
|
+
width = 1;
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
rect = range.get().getBoundingClientRect();
|
|
1355
|
+
x = rect.x;
|
|
1356
|
+
width = rect.width;
|
|
1357
|
+
}
|
|
1358
|
+
const height = rect.height;
|
|
1359
|
+
return DOMRect.fromRect({
|
|
1360
|
+
x,
|
|
1361
|
+
y: rect.y,
|
|
1362
|
+
width: width > 0 ? width : 1,
|
|
1363
|
+
height: height > 0 ? height : 1,
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1302
1366
|
// Returns −1 if the point is before the range, 0 if the point is in the range, and 1 if the point is after the range.
|
|
1303
1367
|
comparePoint(node, offset) {
|
|
1304
1368
|
return this.range.comparePoint(node.get(0), offset);
|
|
@@ -2797,13 +2861,13 @@ const boxes = new Map();
|
|
|
2797
2861
|
|
|
2798
2862
|
const editors = new Map();
|
|
2799
2863
|
|
|
2800
|
-
//
|
|
2864
|
+
// A key-value object for storing data about box.
|
|
2801
2865
|
const boxData = {};
|
|
2802
|
-
//
|
|
2866
|
+
// A key-value object for storing all effects.
|
|
2803
2867
|
const effectData = {};
|
|
2804
2868
|
const framework = safeTemplate `
|
|
2805
2869
|
<span class="lake-box-strip"><br /></span>
|
|
2806
|
-
<div class="lake-box-container" contenteditable="false"></div>
|
|
2870
|
+
<div class="lake-box-container" contenteditable="false" draggable="true"></div>
|
|
2807
2871
|
<span class="lake-box-strip"><br /></span>
|
|
2808
2872
|
`;
|
|
2809
2873
|
class Box {
|
|
@@ -2853,10 +2917,6 @@ class Box {
|
|
|
2853
2917
|
container.off('mouseleave');
|
|
2854
2918
|
container.off('click');
|
|
2855
2919
|
}
|
|
2856
|
-
// fix: should not activate box when clicking box
|
|
2857
|
-
container.on('mousedown', event => {
|
|
2858
|
-
event.preventDefault();
|
|
2859
|
-
});
|
|
2860
2920
|
container.on('mouseenter', () => {
|
|
2861
2921
|
if (container.hasClass('lake-box-selected') ||
|
|
2862
2922
|
container.hasClass('lake-box-focused') ||
|
|
@@ -4613,7 +4673,7 @@ class Dropdown {
|
|
|
4613
4673
|
}
|
|
4614
4674
|
}
|
|
4615
4675
|
|
|
4616
|
-
var version = "0.1.
|
|
4676
|
+
var version = "0.1.8";
|
|
4617
4677
|
|
|
4618
4678
|
// Inserts a box into the specified range.
|
|
4619
4679
|
function insertBox(range, boxName, boxValue) {
|
|
@@ -5231,6 +5291,27 @@ class Editor {
|
|
|
5231
5291
|
this.isComposing = false;
|
|
5232
5292
|
this.event = new EventEmitter();
|
|
5233
5293
|
this.box = Editor.box;
|
|
5294
|
+
this.copyListener = event => {
|
|
5295
|
+
const range = this.selection.range;
|
|
5296
|
+
if (range.commonAncestor.closestContainer().get(0) !== this.container.get(0)) {
|
|
5297
|
+
return;
|
|
5298
|
+
}
|
|
5299
|
+
this.event.emit('copy', event);
|
|
5300
|
+
};
|
|
5301
|
+
this.cutListener = event => {
|
|
5302
|
+
const range = this.selection.range;
|
|
5303
|
+
if (range.commonAncestor.closestContainer().get(0) !== this.container.get(0)) {
|
|
5304
|
+
return;
|
|
5305
|
+
}
|
|
5306
|
+
this.event.emit('cut', event);
|
|
5307
|
+
};
|
|
5308
|
+
this.pasteListener = event => {
|
|
5309
|
+
const range = this.selection.range;
|
|
5310
|
+
if (range.commonAncestor.closestContainer().get(0) !== this.container.get(0)) {
|
|
5311
|
+
return;
|
|
5312
|
+
}
|
|
5313
|
+
this.event.emit('paste', event);
|
|
5314
|
+
};
|
|
5234
5315
|
this.beforeunloadListener = () => {
|
|
5235
5316
|
this.history.save();
|
|
5236
5317
|
};
|
|
@@ -5345,6 +5426,7 @@ class Editor {
|
|
|
5345
5426
|
this.rectifyContent();
|
|
5346
5427
|
this.emitStateChangeEvent();
|
|
5347
5428
|
this.togglePlaceholderClass(value);
|
|
5429
|
+
this.scrollToCaret();
|
|
5348
5430
|
this.event.emit('change', value);
|
|
5349
5431
|
};
|
|
5350
5432
|
if (!config.root) {
|
|
@@ -5560,6 +5642,66 @@ class Editor {
|
|
|
5560
5642
|
blur() {
|
|
5561
5643
|
this.container.blur();
|
|
5562
5644
|
}
|
|
5645
|
+
// Scrolls to the caret or the range of the selection.
|
|
5646
|
+
scrollToCaret() {
|
|
5647
|
+
// Creates an artificial caret that is the same size as the caret at the current caret position.
|
|
5648
|
+
const rangeRect = this.selection.range.getRect();
|
|
5649
|
+
const containerRect = this.container.get(0).getBoundingClientRect();
|
|
5650
|
+
const artificialCaret = query('<div class="lake-artificial-caret" />');
|
|
5651
|
+
const left = rangeRect.x - containerRect.x;
|
|
5652
|
+
const top = rangeRect.y - containerRect.y;
|
|
5653
|
+
artificialCaret.css({
|
|
5654
|
+
position: 'absolute',
|
|
5655
|
+
top: `${top}px`,
|
|
5656
|
+
left: `${left}px`,
|
|
5657
|
+
width: `${rangeRect.width}px`,
|
|
5658
|
+
height: `${rangeRect.height}px`,
|
|
5659
|
+
// background: 'red',
|
|
5660
|
+
'z-index': '-1',
|
|
5661
|
+
});
|
|
5662
|
+
this.overlayContainer.find('.lake-artificial-caret').remove();
|
|
5663
|
+
this.overlayContainer.append(artificialCaret);
|
|
5664
|
+
// Scrolls the artificial caret element into the visible area of the browser window
|
|
5665
|
+
// if it's not already within the visible area of the browser window.
|
|
5666
|
+
// If the element is already within the visible area of the browser window, then no scrolling takes place.
|
|
5667
|
+
let scrollX;
|
|
5668
|
+
let scrollY;
|
|
5669
|
+
let viewportWidth;
|
|
5670
|
+
let viewportHeight;
|
|
5671
|
+
const viewport = this.container.closestScroller();
|
|
5672
|
+
if (viewport.length > 0) {
|
|
5673
|
+
const nativeViewport = viewport.get(0);
|
|
5674
|
+
const viewportRect = nativeViewport.getBoundingClientRect();
|
|
5675
|
+
scrollX = nativeViewport.scrollLeft;
|
|
5676
|
+
scrollY = nativeViewport.scrollTop;
|
|
5677
|
+
viewportWidth = viewportRect.width;
|
|
5678
|
+
viewportHeight = viewportRect.height;
|
|
5679
|
+
}
|
|
5680
|
+
else {
|
|
5681
|
+
const nativeContainerWrapper = this.containerWrapper.get(0);
|
|
5682
|
+
scrollX = window.scrollX;
|
|
5683
|
+
scrollY = window.scrollY;
|
|
5684
|
+
viewportWidth = window.innerWidth - nativeContainerWrapper.offsetLeft;
|
|
5685
|
+
viewportHeight = window.innerHeight - nativeContainerWrapper.offsetTop;
|
|
5686
|
+
}
|
|
5687
|
+
let needScroll = false;
|
|
5688
|
+
let alignToTop = true;
|
|
5689
|
+
if (left < scrollX || left > scrollX + viewportWidth) {
|
|
5690
|
+
needScroll = true;
|
|
5691
|
+
}
|
|
5692
|
+
if (top < scrollY) {
|
|
5693
|
+
needScroll = true;
|
|
5694
|
+
alignToTop = true;
|
|
5695
|
+
}
|
|
5696
|
+
else if (top > scrollY + viewportHeight) {
|
|
5697
|
+
needScroll = true;
|
|
5698
|
+
alignToTop = false;
|
|
5699
|
+
}
|
|
5700
|
+
if (needScroll) {
|
|
5701
|
+
artificialCaret.get(0).scrollIntoView(alignToTop);
|
|
5702
|
+
}
|
|
5703
|
+
artificialCaret.remove();
|
|
5704
|
+
}
|
|
5563
5705
|
// Sets the specified HTML string to the editor area.
|
|
5564
5706
|
setValue(value) {
|
|
5565
5707
|
value = normalizeValue(value);
|
|
@@ -5619,7 +5761,10 @@ class Editor {
|
|
|
5619
5761
|
if (this.toolbar) {
|
|
5620
5762
|
this.toolbar.render(this);
|
|
5621
5763
|
}
|
|
5764
|
+
document.addEventListener('copy', this.copyListener);
|
|
5622
5765
|
if (!this.readonly) {
|
|
5766
|
+
document.addEventListener('cut', this.cutListener);
|
|
5767
|
+
document.addEventListener('paste', this.pasteListener);
|
|
5623
5768
|
window.addEventListener('beforeunload', this.beforeunloadListener);
|
|
5624
5769
|
document.addEventListener('selectionchange', this.selectionchangeListener);
|
|
5625
5770
|
document.addEventListener('click', this.clickListener);
|
|
@@ -5635,7 +5780,10 @@ class Editor {
|
|
|
5635
5780
|
this.history.event.removeAllListeners();
|
|
5636
5781
|
this.root.empty();
|
|
5637
5782
|
this.popupContainer.remove();
|
|
5783
|
+
document.removeEventListener('copy', this.copyListener);
|
|
5638
5784
|
if (!this.readonly) {
|
|
5785
|
+
document.removeEventListener('cut', this.cutListener);
|
|
5786
|
+
document.removeEventListener('paste', this.pasteListener);
|
|
5639
5787
|
window.removeEventListener('beforeunload', this.beforeunloadListener);
|
|
5640
5788
|
document.removeEventListener('selectionchange', this.selectionchangeListener);
|
|
5641
5789
|
document.removeEventListener('click', this.clickListener);
|
|
@@ -6764,11 +6912,18 @@ function openFullScreen(box) {
|
|
|
6764
6912
|
}
|
|
6765
6913
|
return placeholderSrc;
|
|
6766
6914
|
});
|
|
6915
|
+
let savedRange;
|
|
6767
6916
|
lightbox.on('openingAnimationEnd', () => {
|
|
6917
|
+
savedRange = editor.selection.range;
|
|
6768
6918
|
box.event.emit('openfullscreen');
|
|
6769
6919
|
});
|
|
6770
6920
|
lightbox.on('destroy', () => {
|
|
6771
6921
|
window.setTimeout(() => {
|
|
6922
|
+
if (savedRange) {
|
|
6923
|
+
// fix(image): lose focus when zooming in the iOS
|
|
6924
|
+
editor.selection.range = savedRange;
|
|
6925
|
+
editor.selection.addRangeToNativeSelection();
|
|
6926
|
+
}
|
|
6772
6927
|
box.event.emit('closefullscreen');
|
|
6773
6928
|
}, 0);
|
|
6774
6929
|
});
|
|
@@ -6980,7 +7135,7 @@ const imageBox = {
|
|
|
6980
7135
|
}
|
|
6981
7136
|
}
|
|
6982
7137
|
if (container.first().length === 0) {
|
|
6983
|
-
// The code
|
|
7138
|
+
// The following code is for unit testing because some test cases need to
|
|
6984
7139
|
// select the content of the box before it is completely loaded.
|
|
6985
7140
|
// Example:
|
|
6986
7141
|
// range.setStart(box.getContainer(), 1);
|
|
@@ -7219,10 +7374,7 @@ const codeBlockBox = {
|
|
|
7219
7374
|
};
|
|
7220
7375
|
|
|
7221
7376
|
var copy = (editor) => {
|
|
7222
|
-
|
|
7223
|
-
return;
|
|
7224
|
-
}
|
|
7225
|
-
editor.container.on('copy', event => {
|
|
7377
|
+
editor.event.on('copy', event => {
|
|
7226
7378
|
const range = editor.selection.range;
|
|
7227
7379
|
if (range.isInsideBox) {
|
|
7228
7380
|
return;
|
|
@@ -7249,7 +7401,7 @@ var cut = (editor) => {
|
|
|
7249
7401
|
if (editor.readonly) {
|
|
7250
7402
|
return;
|
|
7251
7403
|
}
|
|
7252
|
-
editor.
|
|
7404
|
+
editor.event.on('cut', event => {
|
|
7253
7405
|
const range = editor.selection.range;
|
|
7254
7406
|
if (range.isInsideBox) {
|
|
7255
7407
|
return;
|
|
@@ -7433,7 +7585,7 @@ var paste = (editor) => {
|
|
|
7433
7585
|
if (editor.readonly) {
|
|
7434
7586
|
return;
|
|
7435
7587
|
}
|
|
7436
|
-
editor.
|
|
7588
|
+
editor.event.on('paste', event => {
|
|
7437
7589
|
const { requestTypes } = editor.config.image;
|
|
7438
7590
|
const range = editor.selection.range;
|
|
7439
7591
|
if (range.isInsideBox) {
|
|
@@ -7478,6 +7630,154 @@ var paste = (editor) => {
|
|
|
7478
7630
|
});
|
|
7479
7631
|
};
|
|
7480
7632
|
|
|
7633
|
+
var drop = (editor) => {
|
|
7634
|
+
if (editor.readonly) {
|
|
7635
|
+
return;
|
|
7636
|
+
}
|
|
7637
|
+
let draggedNode = null;
|
|
7638
|
+
let dropIndication = null;
|
|
7639
|
+
let targetBlock = null;
|
|
7640
|
+
let dropPosition = 'bottom';
|
|
7641
|
+
// The dragstart event is fired when the user starts dragging an element or text selection.
|
|
7642
|
+
editor.container.on('dragstart', event => {
|
|
7643
|
+
draggedNode = null;
|
|
7644
|
+
const dragEvent = event;
|
|
7645
|
+
const dataTransfer = dragEvent.dataTransfer;
|
|
7646
|
+
if (!dataTransfer) {
|
|
7647
|
+
return;
|
|
7648
|
+
}
|
|
7649
|
+
dataTransfer.effectAllowed = 'move';
|
|
7650
|
+
// set the dragged node
|
|
7651
|
+
const targetNode = query(dragEvent.target);
|
|
7652
|
+
const boxNode = targetNode.closest('lake-box');
|
|
7653
|
+
if (boxNode.length === 0) {
|
|
7654
|
+
dragEvent.preventDefault();
|
|
7655
|
+
return;
|
|
7656
|
+
}
|
|
7657
|
+
const box = new Box(boxNode);
|
|
7658
|
+
if (box.type === 'inline') {
|
|
7659
|
+
dragEvent.preventDefault();
|
|
7660
|
+
return;
|
|
7661
|
+
}
|
|
7662
|
+
draggedNode = boxNode;
|
|
7663
|
+
// prepare an indication rod
|
|
7664
|
+
dropIndication = query(safeTemplate `
|
|
7665
|
+
<div class="lake-drop-indication">
|
|
7666
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#000000" viewBox="0 0 256 256">
|
|
7667
|
+
<path d="M181.66,133.66l-80,80A8,8,0,0,1,88,208V48a8,8,0,0,1,13.66-5.66l80,80A8,8,0,0,1,181.66,133.66Z"></path>
|
|
7668
|
+
</svg>
|
|
7669
|
+
</div>
|
|
7670
|
+
`);
|
|
7671
|
+
editor.overlayContainer.append(dropIndication);
|
|
7672
|
+
});
|
|
7673
|
+
// The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds).
|
|
7674
|
+
editor.container.on('dragover', event => {
|
|
7675
|
+
const dragEvent = event;
|
|
7676
|
+
dragEvent.preventDefault();
|
|
7677
|
+
const dataTransfer = dragEvent.dataTransfer;
|
|
7678
|
+
if (!dataTransfer) {
|
|
7679
|
+
return;
|
|
7680
|
+
}
|
|
7681
|
+
dataTransfer.dropEffect = 'move';
|
|
7682
|
+
if (!dropIndication) {
|
|
7683
|
+
return;
|
|
7684
|
+
}
|
|
7685
|
+
const targetNode = query(dragEvent.target);
|
|
7686
|
+
if (targetNode.isContainer) {
|
|
7687
|
+
return;
|
|
7688
|
+
}
|
|
7689
|
+
const targetBoxNode = targetNode.closest('lake-box');
|
|
7690
|
+
if (targetBoxNode.length > 0) {
|
|
7691
|
+
if (targetBoxNode.isBlockBox) {
|
|
7692
|
+
targetBlock = targetBoxNode;
|
|
7693
|
+
}
|
|
7694
|
+
else {
|
|
7695
|
+
targetBlock = targetBoxNode.closestBlock();
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
else {
|
|
7699
|
+
targetBlock = targetNode.closestBlock();
|
|
7700
|
+
}
|
|
7701
|
+
const containerRect = editor.container.get(0).getBoundingClientRect();
|
|
7702
|
+
let targetBlcokRect = targetBlock.get(0).getBoundingClientRect();
|
|
7703
|
+
dropPosition = 'bottom';
|
|
7704
|
+
let left = targetBlcokRect.x - containerRect.x;
|
|
7705
|
+
let top = targetBlcokRect.y + targetBlcokRect.height - containerRect.y + (parseInt(targetBlock.computedCSS('margin-bottom'), 10) / 2);
|
|
7706
|
+
if (dragEvent.clientY < targetBlcokRect.y + (targetBlcokRect.height / 2)) {
|
|
7707
|
+
const prevBlock = targetBlock.prev();
|
|
7708
|
+
if (prevBlock.length > 0 && prevBlock.isBlock || prevBlock.isBlockBox) {
|
|
7709
|
+
targetBlock = prevBlock;
|
|
7710
|
+
targetBlcokRect = targetBlock.get(0).getBoundingClientRect();
|
|
7711
|
+
left = targetBlcokRect.x - containerRect.x;
|
|
7712
|
+
top = targetBlcokRect.y + targetBlcokRect.height - containerRect.y + (parseInt(targetBlock.computedCSS('margin-bottom'), 10) / 2);
|
|
7713
|
+
}
|
|
7714
|
+
else {
|
|
7715
|
+
dropPosition = 'top';
|
|
7716
|
+
top = targetBlcokRect.y - containerRect.y - (parseInt(editor.container.computedCSS('padding-top'), 10) / 2);
|
|
7717
|
+
}
|
|
7718
|
+
}
|
|
7719
|
+
dropIndication.css({
|
|
7720
|
+
top: `${top}px`,
|
|
7721
|
+
left: `${left}px`,
|
|
7722
|
+
width: `${targetBlcokRect.width}px`,
|
|
7723
|
+
display: 'block',
|
|
7724
|
+
});
|
|
7725
|
+
});
|
|
7726
|
+
// The dragend event is fired when a drag operation ends (by releasing a mouse button or hitting the escape key).
|
|
7727
|
+
editor.container.on('dragend', () => {
|
|
7728
|
+
if (!dropIndication) {
|
|
7729
|
+
return;
|
|
7730
|
+
}
|
|
7731
|
+
dropIndication.remove();
|
|
7732
|
+
dropIndication = null;
|
|
7733
|
+
});
|
|
7734
|
+
// The drop event is fired when an element or text selection is dropped on a valid drop target.
|
|
7735
|
+
editor.container.on('drop', event => {
|
|
7736
|
+
const dragEvent = event;
|
|
7737
|
+
const dataTransfer = dragEvent.dataTransfer;
|
|
7738
|
+
if (!dataTransfer) {
|
|
7739
|
+
return;
|
|
7740
|
+
}
|
|
7741
|
+
if (!dropIndication) {
|
|
7742
|
+
return;
|
|
7743
|
+
}
|
|
7744
|
+
dropIndication.remove();
|
|
7745
|
+
dropIndication = null;
|
|
7746
|
+
// drop a box
|
|
7747
|
+
if (draggedNode && targetBlock && draggedNode.isBox) {
|
|
7748
|
+
if (draggedNode.get(0) === targetBlock.get(0)) {
|
|
7749
|
+
return;
|
|
7750
|
+
}
|
|
7751
|
+
if (dropPosition === 'bottom' && draggedNode.get(0) === targetBlock.next().get(0)) {
|
|
7752
|
+
return;
|
|
7753
|
+
}
|
|
7754
|
+
dragEvent.preventDefault();
|
|
7755
|
+
const draggedBox = new Box(draggedNode);
|
|
7756
|
+
const range = editor.selection.range;
|
|
7757
|
+
if (targetBlock.isBox) {
|
|
7758
|
+
if (dropPosition === 'top') {
|
|
7759
|
+
range.selectBoxStart(targetBlock);
|
|
7760
|
+
}
|
|
7761
|
+
else {
|
|
7762
|
+
range.selectBoxEnd(targetBlock);
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
else {
|
|
7766
|
+
range.selectNodeContents(targetBlock);
|
|
7767
|
+
if (dropPosition === 'top') {
|
|
7768
|
+
range.collapseToStart();
|
|
7769
|
+
}
|
|
7770
|
+
else {
|
|
7771
|
+
range.collapseToEnd();
|
|
7772
|
+
}
|
|
7773
|
+
}
|
|
7774
|
+
editor.insertBox(draggedBox.name, draggedBox.value);
|
|
7775
|
+
draggedNode.remove();
|
|
7776
|
+
editor.history.save();
|
|
7777
|
+
}
|
|
7778
|
+
});
|
|
7779
|
+
};
|
|
7780
|
+
|
|
7481
7781
|
var undo = (editor) => {
|
|
7482
7782
|
if (editor.readonly) {
|
|
7483
7783
|
return;
|
|
@@ -8740,12 +9040,10 @@ function splitBlock(editor, block) {
|
|
|
8740
9040
|
editor.selection.splitBlock();
|
|
8741
9041
|
block = range.getBlocks()[0];
|
|
8742
9042
|
if (!block) {
|
|
8743
|
-
editor.history.save();
|
|
8744
9043
|
return;
|
|
8745
9044
|
}
|
|
8746
9045
|
if (endText === '' && (block.isHeading || block.name === 'blockquote')) {
|
|
8747
9046
|
editor.selection.setBlocks('<p />');
|
|
8748
|
-
editor.history.save();
|
|
8749
9047
|
return;
|
|
8750
9048
|
}
|
|
8751
9049
|
if (block.isList && block.attr('type') === 'checklist') {
|
|
@@ -9308,6 +9606,7 @@ Editor.box.add(codeBlockBox);
|
|
|
9308
9606
|
Editor.plugin.add(copy);
|
|
9309
9607
|
Editor.plugin.add(cut);
|
|
9310
9608
|
Editor.plugin.add(paste);
|
|
9609
|
+
Editor.plugin.add(drop);
|
|
9311
9610
|
Editor.plugin.add(undo);
|
|
9312
9611
|
Editor.plugin.add(redo);
|
|
9313
9612
|
Editor.plugin.add(selectAll);
|