@livingdocs/editable.js 3.2.0 → 3.2.1
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 +10 -6
- package/src/selection.js +25 -3
- package/src/util/dom.js +88 -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.2.
|
|
4
|
+
"version": "3.2.1",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@babel/runtime": "^7.16.7",
|
|
7
7
|
"bowser": "2.11.0",
|
|
@@ -75,18 +75,22 @@
|
|
|
75
75
|
"test:watch": "npm run test:karma -s -- --single-run false",
|
|
76
76
|
"lint": "eslint --ignore-path .gitignore ."
|
|
77
77
|
},
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"tag": "3.x"
|
|
80
|
+
},
|
|
78
81
|
"release": {
|
|
79
|
-
"extends": "@livingdocs/semantic-release-presets/npm-github-verify"
|
|
82
|
+
"extends": "@livingdocs/semantic-release-presets/npm-github-verify",
|
|
83
|
+
"branch": "3.x"
|
|
80
84
|
},
|
|
81
85
|
"ci": {
|
|
82
86
|
"isCi": true,
|
|
83
87
|
"name": "Drone",
|
|
84
88
|
"service": "drone",
|
|
85
|
-
"commit": "
|
|
86
|
-
"build": "
|
|
87
|
-
"branch": "
|
|
89
|
+
"commit": "ca878f0f32cf4eaf0ebd53f2467973e5c59e8b97",
|
|
90
|
+
"build": "1407",
|
|
91
|
+
"branch": "3.x",
|
|
88
92
|
"isPr": false,
|
|
89
93
|
"slug": "livingdocsIO/editable.js",
|
|
90
|
-
"date": "
|
|
94
|
+
"date": "2024-01-10T22:49:33.014Z"
|
|
91
95
|
}
|
|
92
96
|
}
|
package/src/selection.js
CHANGED
|
@@ -7,6 +7,10 @@ import * as block from './block'
|
|
|
7
7
|
import config from './config'
|
|
8
8
|
import highlightSupport from './highlight-support'
|
|
9
9
|
import highlightText from './highlight-text'
|
|
10
|
+
import {
|
|
11
|
+
findStartExcludingWhitespace,
|
|
12
|
+
findEndExcludingWhitespace
|
|
13
|
+
} from './util/dom'
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* The Selection module provides a cross-browser abstraction layer for range
|
|
@@ -98,9 +102,25 @@ export default class Selection extends Cursor {
|
|
|
98
102
|
const textToTrim = this.range.toString()
|
|
99
103
|
const whitespacesOnTheLeft = textToTrim.search(/\S|$/)
|
|
100
104
|
const lastNonWhitespace = textToTrim.search(/\S[\s]+$/)
|
|
101
|
-
const whitespacesOnTheRight = lastNonWhitespace === -1
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
const whitespacesOnTheRight = lastNonWhitespace === -1
|
|
106
|
+
? 0
|
|
107
|
+
: textToTrim.length - (lastNonWhitespace + 1)
|
|
108
|
+
|
|
109
|
+
const [startContainer, startOffset] = findStartExcludingWhitespace({
|
|
110
|
+
root: this.range.commonAncestorContainer,
|
|
111
|
+
startContainer: this.range.startContainer,
|
|
112
|
+
startOffset: this.range.startOffset,
|
|
113
|
+
whitespacesOnTheLeft
|
|
114
|
+
})
|
|
115
|
+
this.range.setStart(startContainer, startOffset)
|
|
116
|
+
|
|
117
|
+
const [endContainer, endOffset] = findEndExcludingWhitespace({
|
|
118
|
+
root: this.range.commonAncestorContainer,
|
|
119
|
+
endContainer: this.range.endContainer,
|
|
120
|
+
endOffset: this.range.endOffset,
|
|
121
|
+
whitespacesOnTheRight
|
|
122
|
+
})
|
|
123
|
+
this.range.setEnd(endContainer, endOffset)
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
unlink () {
|
|
@@ -146,6 +166,7 @@ export default class Selection extends Cursor {
|
|
|
146
166
|
// toggle('<em>')
|
|
147
167
|
toggle (elem) {
|
|
148
168
|
if (block.isPlainTextBlock(this.host)) return
|
|
169
|
+
if (this.range.collapsed) return
|
|
149
170
|
elem = this.adoptElement(elem)
|
|
150
171
|
this.range = content.toggleTag(this.host, this.range, elem)
|
|
151
172
|
this.setSelection()
|
|
@@ -310,6 +331,7 @@ export default class Selection extends Cursor {
|
|
|
310
331
|
// remove first.
|
|
311
332
|
forceWrap (elem) {
|
|
312
333
|
if (block.isPlainTextBlock(this.host)) return
|
|
334
|
+
if (this.range.collapsed) return
|
|
313
335
|
elem = this.adoptElement(elem)
|
|
314
336
|
this.range = content.forceWrap(this.host, this.range, elem)
|
|
315
337
|
this.setSelection()
|
package/src/util/dom.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import NodeIterator from '../node-iterator'
|
|
2
|
+
import {textNode} from '../node-type'
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* @param {HTMLElement | Array | String} target
|
|
3
6
|
* @param {Document} document
|
|
@@ -42,3 +45,88 @@ export const closest = (elem, selector) => {
|
|
|
42
45
|
if (!elem.closest) elem = elem.parentNode
|
|
43
46
|
if (elem && elem.closest) return elem.closest(selector)
|
|
44
47
|
}
|
|
48
|
+
|
|
49
|
+
export function findStartExcludingWhitespace ({root, startContainer, startOffset, whitespacesOnTheLeft}) {
|
|
50
|
+
const isTextNode = startContainer.nodeType === textNode
|
|
51
|
+
if (!isTextNode) {
|
|
52
|
+
return findStartExcludingWhitespace({
|
|
53
|
+
root,
|
|
54
|
+
startContainer: startContainer.childNodes[startOffset],
|
|
55
|
+
startOffset: 0,
|
|
56
|
+
whitespacesOnTheLeft
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const offsetAfterWhitespace = startOffset + whitespacesOnTheLeft
|
|
61
|
+
if (startContainer.length > offsetAfterWhitespace) {
|
|
62
|
+
return [startContainer, offsetAfterWhitespace]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Pass the root so that the iterator can traverse to siblings
|
|
66
|
+
const iterator = new NodeIterator(root)
|
|
67
|
+
// Set the position to the node which is selected
|
|
68
|
+
iterator.nextNode = startContainer
|
|
69
|
+
// Iterate once to avoid returning self
|
|
70
|
+
iterator.getNextTextNode()
|
|
71
|
+
|
|
72
|
+
const container = iterator.getNextTextNode()
|
|
73
|
+
if (!container) {
|
|
74
|
+
// No more text nodes - use the end of the last text node
|
|
75
|
+
const previousTextNode = iterator.getPreviousTextNode()
|
|
76
|
+
return [previousTextNode, previousTextNode.length]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return findStartExcludingWhitespace({
|
|
80
|
+
root,
|
|
81
|
+
startContainer: container,
|
|
82
|
+
startOffset: 0,
|
|
83
|
+
whitespacesOnTheLeft: offsetAfterWhitespace - startContainer.length
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function findEndExcludingWhitespace ({root, endContainer, endOffset, whitespacesOnTheRight}) {
|
|
88
|
+
const isTextNode = endContainer.nodeType === textNode
|
|
89
|
+
if (!isTextNode) {
|
|
90
|
+
const isFirstNode = !endContainer.childNodes[endOffset - 1]
|
|
91
|
+
const container = isFirstNode
|
|
92
|
+
? endContainer.childNodes[endOffset]
|
|
93
|
+
: endContainer.childNodes[endOffset - 1]
|
|
94
|
+
let offset = 0
|
|
95
|
+
if (!isFirstNode) {
|
|
96
|
+
offset = container.nodeType === textNode
|
|
97
|
+
? container.length
|
|
98
|
+
: container.childNodes.length
|
|
99
|
+
}
|
|
100
|
+
return findEndExcludingWhitespace({
|
|
101
|
+
root,
|
|
102
|
+
endContainer: container,
|
|
103
|
+
endOffset: offset,
|
|
104
|
+
whitespacesOnTheRight
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const offsetBeforeWhitespace = endOffset - whitespacesOnTheRight
|
|
109
|
+
if (offsetBeforeWhitespace > 0) {
|
|
110
|
+
return [endContainer, offsetBeforeWhitespace]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Pass the root so that the iterator can traverse to siblings
|
|
114
|
+
const iterator = new NodeIterator(root)
|
|
115
|
+
// Set the position to the node which is selected
|
|
116
|
+
iterator.previous = endContainer
|
|
117
|
+
// Iterate once to avoid returning self
|
|
118
|
+
iterator.getPreviousTextNode()
|
|
119
|
+
|
|
120
|
+
const container = iterator.getPreviousTextNode()
|
|
121
|
+
if (!container) {
|
|
122
|
+
// No more text nodes - use the start of the first text node
|
|
123
|
+
return [iterator.getNextTextNode(), 0]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return findEndExcludingWhitespace({
|
|
127
|
+
root,
|
|
128
|
+
endContainer: container,
|
|
129
|
+
endOffset: container.length,
|
|
130
|
+
whitespacesOnTheRight: whitespacesOnTheRight - endOffset
|
|
131
|
+
})
|
|
132
|
+
}
|