@livingdocs/editable.js 3.1.0 → 3.1.3
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/editable.js +1 -1
- package/dist/editable.js.map +1 -1
- package/package.json +4 -4
- package/src/clipboard.js +5 -1
- package/src/content.js +6 -0
- package/src/core.js +1 -1
- package/src/cursor.js +5 -0
- package/src/dispatcher.js +4 -5
- package/src/highlight-support.js +26 -4
- package/src/selection.js +25 -2
- package/src/util/string.js +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livingdocs/editable.js",
|
|
3
3
|
"description": "Friendly contenteditable API",
|
|
4
|
-
"version": "3.1.
|
|
4
|
+
"version": "3.1.3",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@babel/runtime": "^7.16.7",
|
|
7
7
|
"bowser": "2.11.0",
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"isCi": true,
|
|
84
84
|
"name": "Drone",
|
|
85
85
|
"service": "drone",
|
|
86
|
-
"commit": "
|
|
87
|
-
"build": "
|
|
86
|
+
"commit": "e1b2285d9dc4dd070a0151dbbb4af3f14b97e39b",
|
|
87
|
+
"build": "515",
|
|
88
88
|
"branch": "master",
|
|
89
89
|
"isPr": false,
|
|
90
90
|
"slug": "livingdocsIO/editable.js",
|
|
91
|
-
"date": "2022-
|
|
91
|
+
"date": "2022-08-09T11:28:27.271Z"
|
|
92
92
|
}
|
|
93
93
|
}
|
package/src/clipboard.js
CHANGED
|
@@ -29,7 +29,11 @@ export function paste (element, cursor, clipboardContent) {
|
|
|
29
29
|
const document = element.ownerDocument
|
|
30
30
|
element.setAttribute(config.pastingAttribute, true)
|
|
31
31
|
|
|
32
|
-
if (cursor.isSelection)
|
|
32
|
+
if (cursor.isSelection) {
|
|
33
|
+
cursor = cursor.deleteExactSurroundingTags()
|
|
34
|
+
.deleteContainedTags()
|
|
35
|
+
.deleteContent()
|
|
36
|
+
}
|
|
33
37
|
|
|
34
38
|
// Create a placeholder to help parse HTML
|
|
35
39
|
const pasteHolder = document.createElement('div')
|
package/src/content.js
CHANGED
|
@@ -244,6 +244,12 @@ export function getInnerTags (range, filterFunc) {
|
|
|
244
244
|
return range.getNodes([nodeType.elementNode], filterFunc)
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
// Get all tags whose text is completely within the current selection.
|
|
248
|
+
export function getContainedTags (range, filterFunc) {
|
|
249
|
+
return range.getNodes([nodeType.elementNode], filterFunc)
|
|
250
|
+
.filter(elem => range.containsNodeText(elem))
|
|
251
|
+
}
|
|
252
|
+
|
|
247
253
|
// Transform an array of elements into an array
|
|
248
254
|
// of tagnames in uppercase
|
|
249
255
|
//
|
package/src/core.js
CHANGED
|
@@ -400,7 +400,7 @@ export class Editable {
|
|
|
400
400
|
)
|
|
401
401
|
return -1
|
|
402
402
|
}
|
|
403
|
-
return highlightSupport.highlightRange(editableHost, highlightId, textRange.start, textRange.end, raiseEvents ? this.dispatcher : undefined, type)
|
|
403
|
+
return highlightSupport.highlightRange(editableHost, text, highlightId, textRange.start, textRange.end, raiseEvents ? this.dispatcher : undefined, type)
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
/**
|
package/src/cursor.js
CHANGED
|
@@ -69,6 +69,11 @@ export default class Cursor {
|
|
|
69
69
|
return content.getInnerTags(this.range, filterFunc)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
// Get all tags whose text is completely within the current selection.
|
|
73
|
+
getContainedTags (filterFunc) {
|
|
74
|
+
return content.getContainedTags(this.range, filterFunc)
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
// Get all tags that surround the current selection.
|
|
73
78
|
getAncestorTags (filterFunc) {
|
|
74
79
|
return content.getAncestorTags(this.host, this.range, filterFunc)
|
package/src/dispatcher.js
CHANGED
|
@@ -136,12 +136,11 @@ export default class Dispatcher {
|
|
|
136
136
|
const {blocks, cursor} = clipboard.paste(block, selection, clipboardContent)
|
|
137
137
|
if (blocks.length) {
|
|
138
138
|
if (endsWithSingleSpace(evt.target.innerText)) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
this.notify('paste', block, blocks, cursor)
|
|
139
|
+
cursor.retainVisibleSelection(() => {
|
|
140
|
+
block.innerHTML = replaceLast(block.innerHTML, ' ', ' ')
|
|
141
|
+
})
|
|
143
142
|
}
|
|
144
|
-
|
|
143
|
+
this.notify('paste', block, blocks, cursor)
|
|
145
144
|
// The input event does not fire when we process the content manually
|
|
146
145
|
// and insert it via script
|
|
147
146
|
this.notify('change', block)
|
package/src/highlight-support.js
CHANGED
|
@@ -12,7 +12,6 @@ const highlightSupport = {
|
|
|
12
12
|
|
|
13
13
|
highlightText (editableHost, text, highlightId, type) {
|
|
14
14
|
if (this.hasHighlight(editableHost, highlightId)) return
|
|
15
|
-
|
|
16
15
|
const blockText = highlightText.extractText(editableHost)
|
|
17
16
|
|
|
18
17
|
const marker = `<span class="highlight-${type}"></span>`
|
|
@@ -28,12 +27,23 @@ const highlightSupport = {
|
|
|
28
27
|
}
|
|
29
28
|
},
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
|
|
31
|
+
// This function was changed to track matches when text is added to the start of a component, but multiple white spaces break it in a strict sense
|
|
32
|
+
// The function works in the editor and in browsers, but tests with multiple white spaces will fail.
|
|
33
|
+
// Browsers change the white spaces to   and the function works, and the tests in highlight.spec.js have been updated to represent this.
|
|
34
|
+
|
|
35
|
+
highlightRange (editableHost, text, highlightId, startIndex, endIndex, dispatcher, type = 'comment') {
|
|
32
36
|
if (this.hasHighlight(editableHost, highlightId)) {
|
|
33
37
|
this.removeHighlight(editableHost, highlightId)
|
|
34
38
|
}
|
|
39
|
+
const firstMarker = `<span class="highlight-${type}"></span>`
|
|
40
|
+
const markerNode = highlightSupport.createMarkerNode(firstMarker, type, this.win)
|
|
41
|
+
const textSearch = new TextHighlighting(markerNode, 'text')
|
|
42
|
+
const blockText = highlightText.extractText(editableHost)
|
|
43
|
+
const matchesArray = textSearch.findMatches(blockText, [text])
|
|
44
|
+
const {actualStartIndex, actualEndIndex} = this.getIndex(matchesArray, startIndex, endIndex)
|
|
35
45
|
const range = rangy.createRange()
|
|
36
|
-
range.selectCharacters(editableHost,
|
|
46
|
+
range.selectCharacters(editableHost, actualStartIndex, actualEndIndex)
|
|
37
47
|
|
|
38
48
|
if (!isInHost(range.commonAncestorContainer, editableHost)) {
|
|
39
49
|
return -1
|
|
@@ -52,7 +62,7 @@ const highlightSupport = {
|
|
|
52
62
|
if (dispatcher) {
|
|
53
63
|
dispatcher.notify('change', editableHost)
|
|
54
64
|
}
|
|
55
|
-
return
|
|
65
|
+
return actualStartIndex
|
|
56
66
|
},
|
|
57
67
|
|
|
58
68
|
updateHighlight (editableHost, highlightId, addCssClass, removeCssClass) {
|
|
@@ -136,6 +146,18 @@ const highlightSupport = {
|
|
|
136
146
|
marker.setAttribute('data-editable', 'ui-unwrap')
|
|
137
147
|
marker.setAttribute('data-highlight', highlightType)
|
|
138
148
|
return marker
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// This function checks to see if text has been added to the component before the comment
|
|
152
|
+
// If it has, the start index is updated, otherwise it remains the same
|
|
153
|
+
getIndex (matchesArray, startIndex, endIndex) {
|
|
154
|
+
const newStartIndex = matchesArray.find((match) => {
|
|
155
|
+
return match.startIndex >= startIndex // checks if the startIndex has increased
|
|
156
|
+
})
|
|
157
|
+
const actualStartIndex = newStartIndex ? newStartIndex.startIndex : startIndex
|
|
158
|
+
const actualEndIndex = actualStartIndex ? (actualStartIndex + (endIndex - startIndex)) : endIndex
|
|
159
|
+
|
|
160
|
+
return {actualStartIndex, actualEndIndex}
|
|
139
161
|
}
|
|
140
162
|
|
|
141
163
|
}
|
package/src/selection.js
CHANGED
|
@@ -245,8 +245,31 @@ export default class Selection extends Cursor {
|
|
|
245
245
|
this.setSelection()
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
// Delete the
|
|
249
|
-
//
|
|
248
|
+
// Delete the farest ancestor that is an exact selection
|
|
249
|
+
//
|
|
250
|
+
// @return Selection instance
|
|
251
|
+
deleteExactSurroundingTags () {
|
|
252
|
+
const ancestorTags = this.getAncestorTags().reverse()
|
|
253
|
+
for (const ancestorTag of ancestorTags) {
|
|
254
|
+
if (this.isExactSelection(ancestorTag)) {
|
|
255
|
+
ancestorTag.remove()
|
|
256
|
+
break
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return new Selection(this.host, this.range)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Delete all the tags whose text is completely within the current selection.
|
|
263
|
+
//
|
|
264
|
+
// @return Selection instance
|
|
265
|
+
deleteContainedTags () {
|
|
266
|
+
const containedTags = this.getContainedTags()
|
|
267
|
+
containedTags.forEach(containedTag => containedTag.remove())
|
|
268
|
+
return new Selection(this.host, this.range)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Delete the contents inside the range and exact surrounding markups.
|
|
272
|
+
// After that the selection will be a cursor.
|
|
250
273
|
//
|
|
251
274
|
// @return Cursor instance
|
|
252
275
|
deleteContent () {
|
package/src/util/string.js
CHANGED
|
@@ -67,6 +67,7 @@ export function replaceLast (text, searchValue, replaceValue) {
|
|
|
67
67
|
text = `${text}`
|
|
68
68
|
if (!searchValue || replaceValue == null) return text
|
|
69
69
|
const lastOccurrenceIndex = text.lastIndexOf(searchValue)
|
|
70
|
+
if (lastOccurrenceIndex === -1) return text
|
|
70
71
|
return `${
|
|
71
72
|
text.slice(0, lastOccurrenceIndex)
|
|
72
73
|
}${
|