@cocreate/selection 1.12.2 → 1.13.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/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## [1.13.1](https://github.com/CoCreate-app/CoCreate-selection/compare/v1.13.0...v1.13.1) (2024-06-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * handling empty contenteditable positioning and ensure parsing and positioning only incudes the contentedible elements content ([924bc45](https://github.com/CoCreate-app/CoCreate-selection/commit/924bc45e13d47d2a750ec2715a5b0ead9276f0ac))
7
+
8
+ # [1.13.0](https://github.com/CoCreate-app/CoCreate-selection/compare/v1.12.2...v1.13.0) (2024-06-12)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * bump cocreate dependencies ([338178a](https://github.com/CoCreate-app/CoCreate-selection/commit/338178a59acee9ac267c33e089388ee819220e79))
14
+ * conteneditable start end across multiple nodes ([ab1d6e2](https://github.com/CoCreate-app/CoCreate-selection/commit/ab1d6e2c5a8b00811e400e25550876c6a264ffe5))
15
+ * remove testing logs ([910e3ff](https://github.com/CoCreate-app/CoCreate-selection/commit/910e3ff5d08d843c4bdff28b7842f54c8358c61d))
16
+ * removed testing logs ([b2cf244](https://github.com/CoCreate-app/CoCreate-selection/commit/b2cf24406c14437f4c9082288649b890f52df51b))
17
+ * return if contenteditable not found ([8ecf10e](https://github.com/CoCreate-app/CoCreate-selection/commit/8ecf10e180ab46e2d36a7a435369201f0d71bc91))
18
+ * svg icon class ([20641ff](https://github.com/CoCreate-app/CoCreate-selection/commit/20641ff0426aa898153d99679069db01332f774e))
19
+ * textnode handling ([e22af17](https://github.com/CoCreate-app/CoCreate-selection/commit/e22af17799699d596c83b0974d3d9187fbc51f23))
20
+
21
+
22
+ ### Features
23
+
24
+ * parse text to reset selection ([7c367bb](https://github.com/CoCreate-app/CoCreate-selection/commit/7c367bbeccdaf07dfdec64d0811f5e5bd41fecb9))
25
+
1
26
  ## [1.12.2](https://github.com/CoCreate-app/CoCreate-selection/compare/v1.12.1...v1.12.2) (2024-04-29)
2
27
 
3
28
 
package/docs/index.html CHANGED
@@ -71,41 +71,31 @@
71
71
  href="https://github.com/CoCreate-app/CoCreate-selection"
72
72
  target="_blank"
73
73
  class="margin-right:15px"
74
- ><i
75
- class="height:20px fill:#505050"
76
- src="/assets/svg/github.svg"></i
74
+ ><i src="/assets/svg/github.svg"></i
77
75
  ></a>
78
76
  <a
79
77
  class="margin-right:15px share"
80
78
  share-network="twitter"
81
79
  title="Share on twitter"
82
- ><i
83
- class="height:20px fill:#505050"
84
- src="/assets/svg/twitter.svg"></i
80
+ ><i src="/assets/svg/twitter.svg"></i
85
81
  ></a>
86
82
  <a
87
83
  class="margin-right:15px share"
88
84
  share-network="facebook"
89
85
  title="Share on Facebook"
90
- ><i
91
- class="height:20px fill:#505050"
92
- src="/assets/svg/facebook.svg"></i
86
+ ><i src="/assets/svg/facebook.svg"></i
93
87
  ></a>
94
88
  <a
95
89
  class="margin-right:15px share"
96
90
  share-network="instagram"
97
91
  title="Share on instagram"
98
- ><i
99
- class="height:20px fill:#505050"
100
- src="/assets/svg/instagram.svg"></i
92
+ ><i src="/assets/svg/instagram.svg"></i
101
93
  ></a>
102
94
  <a
103
95
  class="margin-right:15px share"
104
96
  share-network="share"
105
97
  title="Share on share"
106
- ><i
107
- class="height:20px fill:#505050"
108
- src="/assets/svg/share-alt.svg"></i
98
+ ><i src="/assets/svg/share-alt.svg"></i
109
99
  ></a>
110
100
  </div>
111
101
  </div>
@@ -227,7 +217,7 @@
227
217
  toggle="code-height"
228
218
  toggle-selector="#demo-code"
229
219
  ><i
230
- class="height:18px fill:#505050"
220
+ class="height:18px"
231
221
  src="/assets/svg/eye.svg"></i
232
222
  ></a>
233
223
  <a
@@ -238,18 +228,14 @@
238
228
  hide="#eye-slash"
239
229
  toggle="code-height"
240
230
  toggle-selector="#demo-code"
241
- ><i
242
- class="height:20px fill:#505050"
243
- src="/assets/svg/eye-slash.svg"></i
231
+ ><i src="/assets/svg/eye-slash.svg"></i
244
232
  ></a>
245
233
  <a
246
234
  class="margin-right:10px"
247
235
  id="code"
248
236
  show="#code-slash"
249
237
  hide="#code, #demo-code"
250
- ><i
251
- class="height:20px fill:#505050"
252
- src="/assets/svg/code.svg"></i
238
+ ><i src="/assets/svg/code.svg"></i
253
239
  ></a>
254
240
  <a
255
241
  class="margin-right:10px"
@@ -258,7 +244,7 @@
258
244
  show="#code, #demo-code"
259
245
  hide="#code-slash"
260
246
  ><i
261
- class="display:flex height:18px fill:#505050"
247
+ class="height:18px"
262
248
  src="/assets/svg/code.svg"></i
263
249
  ></a>
264
250
  <a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/selection",
3
- "version": "1.12.2",
3
+ "version": "1.13.1",
4
4
  "description": "A simple selection component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "selection",
@@ -58,6 +58,6 @@
58
58
  "webpack-log": "^3.0.1"
59
59
  },
60
60
  "dependencies": {
61
- "@cocreate/utils": "^1.33.6"
61
+ "@cocreate/utils": "^1.33.7"
62
62
  }
63
63
  }
package/src/index.js CHANGED
@@ -10,48 +10,38 @@ export function getSelection(element) {
10
10
  start: element.selectionStart,
11
11
  end: element.selectionEnd
12
12
  };
13
- }
14
- else {
13
+
14
+ } else {
15
15
  let Document = element.ownerDocument;
16
16
  let selection = Document.getSelection();
17
- if (!selection.rangeCount) return { start: 0, end: 0 };
17
+ if (!selection.rangeCount)
18
+ return { start: 0, end: 0 };
18
19
 
19
20
  let range = selection.getRangeAt(0);
20
- let start = range.startOffset;
21
- let end = range.endOffset;
22
- let previousSibling = range.startContainer.previousSibling;
23
- if (element.innerHTML && previousSibling && previousSibling.nodeType == 3) {
24
- let length = 0;
25
- do {
26
- length += previousSibling.length;
27
- previousSibling = previousSibling.previousSibling;
28
- } while (previousSibling);
29
- start += length;
30
- end += length;
31
- }
32
21
 
33
- if (range.startContainer != range.endContainer) {
34
- // TODO: replace common ancestor value
35
- }
36
- let contenteditable = range.startContainer.parentElement.closest('[contenteditable][array][object][key]');
22
+ let contenteditable
23
+ if (range.startContainer.nodeType === 3)
24
+ contenteditable = range.startContainer.parentElement.closest('[contenteditable][array][object][key]');
25
+ else
26
+ contenteditable = range.startContainer.closest('[contenteditable][array][object][key]');
27
+
37
28
  if (contenteditable) {
38
29
  element = contenteditable;
39
- }
30
+ } else return
31
+
40
32
  let domTextEditor = element;
41
33
  if (!domTextEditor.htmlString) {
42
- domTextEditor = element.closest('[contenteditable]');
43
- }
44
- let elementStart = start, elementEnd = end;
45
- if (domTextEditor && domTextEditor.htmlString) {
46
- let nodePos = getStringPosition({ string: domTextEditor.htmlString, target: range.startContainer.parentElement, position: 'afterbegin' });
47
- if (nodePos) {
48
- elementStart = nodePos.start;
49
- elementEnd = nodePos.end;
50
- start = start + nodePos.start;
51
- end = end + nodePos.end;
52
- }
34
+ domTextEditor = domTextEditor.closest('[contenteditable]');
53
35
  }
54
36
 
37
+
38
+ let start = getNodePosition(range.startContainer, domTextEditor, range.startOffset)
39
+ let end = start
40
+ if (range.startContainer !== range.endContainer) {
41
+ end = getNodePosition(range.endContainer, domTextEditor, range.endOffset)
42
+ } else if (range.endOffset !== range.startOffset)
43
+ end = start + (range.endOffset - range.startOffset)
44
+
55
45
  let startContainer = range.startContainer;
56
46
  if (startContainer.nodeType == 3)
57
47
  startContainer = range.startContainer.parentElement;
@@ -60,21 +50,60 @@ export function getSelection(element) {
60
50
  if (endContainer.nodeType == 3)
61
51
  endContainer = range.endContainer.parentElement;
62
52
 
53
+ let textStart = 0, node = range.startContainer
54
+ while (node) {
55
+ textStart += node.textContent.length;
56
+ if (node === contenteditable)
57
+ node = undefined
58
+ else
59
+ node = node.previousSibling;
60
+ }
61
+ let textEnd = textStart + (range.endOffset - range.startOffset)
62
+
63
63
  let rangeObj = {
64
- element: contenteditable,
65
- // domTextEditor,
64
+ element,
65
+ domTextEditor,
66
66
  startOffset: range.startOffset,
67
67
  endOffset: range.endOffset,
68
68
  startContainer,
69
69
  endContainer,
70
- elementStart,
71
- elementEnd
70
+ elementStart: start,
71
+ elementEnd: end,
72
+ nodeStartContainer: range.startContainer,
73
+ nodeEndContainer: range.endContainer,
74
+ textStart,
75
+ textEnd
72
76
  };
73
- return { start, end, range: rangeObj };
77
+
78
+ return { element: contenteditable, value: selection.toString(), start, end, range: rangeObj };
74
79
  }
75
80
 
76
81
  }
77
82
 
83
+
84
+ function getNodePosition(container, domTextEditor, position) {
85
+ let string = domTextEditor.htmlString
86
+ if (!string)
87
+ return 0
88
+ let node = container.previousSibling
89
+ while (node && node.nodeType === 3) {
90
+ position += node.textContent.length;
91
+ node = node.previousSibling;
92
+ }
93
+
94
+ let nodePosition
95
+ if (node && node.nodeType === 1) {
96
+ nodePosition = getStringPosition({ string, target: node, position: 'afterend' });
97
+ position += nodePosition.end
98
+ } else if (container.parentElement !== domTextEditor && container !== domTextEditor) {
99
+ let parentElement = container.parentElement
100
+ nodePosition = getStringPosition({ string, target: parentElement, position: 'afterbegin' });
101
+ position += nodePosition.start
102
+ }
103
+
104
+ return position
105
+ }
106
+
78
107
  export function processSelection(element, value = "", prev_start, prev_end, start, end, range) {
79
108
  let prevStart = prev_start;
80
109
  let prevEnd = prev_end;
@@ -83,8 +112,7 @@ export function processSelection(element, value = "", prev_start, prev_end, star
83
112
  prev_start -= end - start;
84
113
  prev_end -= end - start;
85
114
  prev_start = prev_start < start ? start : prev_start;
86
- }
87
- else {
115
+ } else {
88
116
  prev_start += value.length;
89
117
  prev_end += value.length;
90
118
  }
@@ -98,8 +126,7 @@ export function processSelection(element, value = "", prev_start, prev_end, star
98
126
  let length = prevStart - prev_start;
99
127
  if (Math.sign(length) === 1 && range.startOffset >= length)
100
128
  range.startOffset -= length;
101
- }
102
- else if (prevStart < prev_start) {
129
+ } else if (prevStart < prev_start) {
103
130
  let length = prev_start - prevStart;
104
131
  if (Math.sign(length) === 1)
105
132
  if (range.startOffset == 0 || range.startOffset >= length)
@@ -109,8 +136,7 @@ export function processSelection(element, value = "", prev_start, prev_end, star
109
136
  let length = prevEnd - prev_end;
110
137
  if (Math.sign(length) === 1 && range.endOffset >= length)
111
138
  range.endOffset -= length;
112
- }
113
- else if (prevEnd < prev_end) {
139
+ } else if (prevEnd < prev_end) {
114
140
  let length = prev_end - prevEnd;
115
141
  if (Math.sign(length) === 1)
116
142
  if (range.endOffset == 0 || range.endOffset >= length)
@@ -126,13 +152,25 @@ export function setSelection(element, start, end, range) {
126
152
  if (element.tagName === "INPUT" || element.tagName === "TEXTAREA") {
127
153
  element.selectionStart = start;
128
154
  element.selectionEnd = end;
129
- }
130
- else {
155
+ } else {
131
156
  if (!range) return;
132
157
  let Document = element.ownerDocument;
133
158
 
134
- let startContainer = getContainer(range.startContainer, range.startOffset);
135
- let endContainer = getContainer(range.endContainer, range.endOffset);
159
+ let startContainer, endContainer
160
+ // if (range.startContainer.htmlString)
161
+ startContainer = getContainer(range.startContainer, range.textStart - range.startOffset);
162
+ // else
163
+ // startContainer = getContainer(range.startContainer, range.startOffset);
164
+
165
+ // if (range.endContainer.htmlString)
166
+ endContainer = getContainer(range.endContainer, range.textEnd - range.endOffset);
167
+ // else
168
+ // endContainer = getContainer(range.endContainer, range.endOffset);
169
+
170
+ // let startContainer = getContainer(range.startContainer, range.elementStart);
171
+ // let endContainer = getContainer(range.endContainer, range.elementEnd);
172
+ // let startContainer = getContainer(range.startContainer, range.startOffset);
173
+ // let endContainer = getContainer(range.endContainer, range.endOffset);
136
174
 
137
175
  if (!startContainer || !endContainer)
138
176
  return;
@@ -141,6 +179,7 @@ export function setSelection(element, start, end, range) {
141
179
  selection.removeAllRanges();
142
180
 
143
181
  const newRange = Document.createRange();
182
+
144
183
  newRange.setStart(startContainer, range.startOffset);
145
184
  newRange.setEnd(endContainer, range.endOffset);
146
185
 
@@ -150,14 +189,11 @@ export function setSelection(element, start, end, range) {
150
189
 
151
190
  function getContainer(element, offset) {
152
191
  let nodeLengths = 0;
153
- for (let node of element.childNodes) {
154
- if (node.nodeType == 3) {
155
- let length = node.length + nodeLengths;
156
- if (length >= offset)
157
- return node;
158
- else
159
- nodeLengths += length;
160
- }
192
+ let nodes = element.childNodes
193
+ for (let i = 0; i < nodes.length; i++) {
194
+ nodeLengths += nodes[i].textContent.length
195
+ if (nodeLengths >= offset)
196
+ return nodes[i];
161
197
  }
162
198
  }
163
199
 
@@ -175,7 +211,7 @@ export function getElementPosition(str, start, end) {
175
211
  let angleStart = startString.lastIndexOf("<");
176
212
  let angleEnd = startString.lastIndexOf(">");
177
213
  let endStringAngleEnd = endString.indexOf(">");
178
- let element, position, nodeStart, nodeEnd, startNode, type;
214
+ let element, path, position, nodeStart, nodeEnd, startNode, type;
179
215
  if (angleEnd > angleStart) {
180
216
  let length = 0;
181
217
  if (start != end)
@@ -192,16 +228,22 @@ export function getElementPosition(str, start, end) {
192
228
  type = 'textNode';
193
229
  if (position == 'beforeend' && findEl.previousSibling && findEl.previousSibling.nodeType == 3)
194
230
  type = 'textNode';
195
- if (type == 'textNode' || position == 'afterbegin')
231
+ if (position == 'afterbegin')
196
232
  nodeStart = start - angleEnd - 1;
233
+ if (type == 'textNode') {
234
+ if (element.tagName === 'DOM-PARSER') {
235
+ element = null
236
+ nodeStart = start
237
+ nodeEnd = end
238
+ } else
239
+ nodeStart = start - angleEnd - 1;
240
+ }
197
241
 
198
242
  findEl.remove();
199
243
  }
200
- }
201
- else if (angleStart == -1) {
244
+ } else if (angleStart == -1) {
202
245
  type = 'textNode';
203
- }
204
- else {
246
+ } else {
205
247
  let node = str.slice(angleStart, startString.length + endStringAngleEnd + 1);
206
248
  if (node.startsWith("</")) {
207
249
  startNode = node.slice(0, 1) + node.slice(2);
@@ -211,13 +253,13 @@ export function getElementPosition(str, start, end) {
211
253
  let end = endString1.indexOf(">");
212
254
  nodeEnd = nodeStart + end + 1;
213
255
  type = 'isEndTag';
214
- }
215
- else {
256
+ } else {
216
257
  nodeEnd = startString.length + endStringAngleEnd + 1;
217
258
  startNode = node;
218
259
  nodeStart = angleStart;
219
260
  type = 'isStartTag';
220
261
  }
262
+
221
263
  if (nodeEnd > 0) {
222
264
  let string = str.customSplice(nodeEnd - 1, 0, ' findelement');
223
265
  let newDom = domParser(string);
@@ -227,8 +269,7 @@ export function getElementPosition(str, start, end) {
227
269
  else if (type == "isEndTag")
228
270
  element = element.parentElement;
229
271
  element.removeAttribute('findelement');
230
- }
231
- else {
272
+ } else {
232
273
  let string = str.customSplice(angleStart, 0, '<findelement></findelement>');
233
274
  let newDom = domParser(string);
234
275
  element = newDom.querySelector('findelement');
@@ -246,14 +287,13 @@ export function getElementPosition(str, start, end) {
246
287
  }
247
288
  }
248
289
 
249
- if (element) {
250
- response.element = element;
290
+ response.element = element;
291
+ if (element)
251
292
  response.path = cssPath(element);
252
- response.position = position;
253
- response.start = nodeStart;
254
- response.end = nodeEnd;
255
- response.type = type;
256
- }
293
+ response.position = position;
294
+ response.start = nodeStart;
295
+ response.end = nodeEnd;
296
+ response.type = type;
257
297
 
258
298
  return response;
259
299
  }
@@ -266,24 +306,19 @@ function getInsertPosition(element) {
266
306
  if (!previousSibling) {
267
307
  target = element.parentElement;
268
308
  position = 'afterbegin';
269
- }
270
- else if (!nextSibling) {
309
+ } else if (!nextSibling) {
271
310
  target = element.parentElement;
272
311
  position = 'beforeend';
273
- }
274
- else if (previousSibling && previousSibling.nodeType == 1) {
312
+ } else if (previousSibling && previousSibling.nodeType == 1) {
275
313
  target = previousSibling;
276
314
  position = 'afterend';
277
- }
278
- else if (nextSibling && nextSibling.nodeType == 1) {
315
+ } else if (nextSibling && nextSibling.nodeType == 1) {
279
316
  target = element.parentElement;
280
317
  position = 'afterbegin';
281
- }
282
- else {
318
+ } else {
283
319
  target = element.parentElement;
284
320
  }
285
- }
286
- else {
321
+ } else {
287
322
  target = element.parentElement;
288
323
  position = 'afterbegin';
289
324
  }
@@ -315,13 +350,11 @@ export function getStringPosition({ string, target, position, attribute, propert
315
350
  else
316
351
  start = getElFromString(dom, string, element, position);
317
352
  end = start;
318
- }
319
- else if (attribute) {
353
+ } else if (attribute) {
320
354
  if (!element.hasAttribute(attribute)) {
321
355
  start = getElFromString(dom, string, element, 'afterbegin', true) - 1;
322
356
  end = start;
323
- }
324
- else {
357
+ } else {
325
358
  start = getElFromString(dom, string, element, 'beforebegin');
326
359
  let elString = string.substring(start);
327
360
  let attrValue = element.getAttribute(attribute);
@@ -333,8 +366,7 @@ export function getStringPosition({ string, target, position, attribute, propert
333
366
  else
334
367
  element.style[property] = value;
335
368
  value = element.getAttribute(attribute);
336
- }
337
- else if (attribute == 'class') {
369
+ } else if (attribute == 'class') {
338
370
  let [prop, val] = value.split(':');
339
371
  if (prop && val) {
340
372
  if (attrValue.includes(`${prop}:`)) {
@@ -349,20 +381,17 @@ export function getStringPosition({ string, target, position, attribute, propert
349
381
  if (!remove)
350
382
  element.classList.add(value);
351
383
  value = element.getAttribute(attribute);
352
- }
353
- else {
384
+ } else {
354
385
  element.setAttribute(attribute, value);
355
386
  value = element.getAttribute(attribute);
356
387
  }
357
388
  end = start + attribute.length + attrValue.length + 4;
358
389
  }
359
- }
360
- else if (value) {
390
+ } else if (value) {
361
391
  start = getElFromString(dom, string, element, 'afterbegin');
362
392
  let length = element.innerHTML.length;
363
393
  end = start + length;
364
- }
365
- else {
394
+ } else {
366
395
  start = getElFromString(dom, string, element, 'beforebegin');
367
396
  end = getElFromString(dom, string, element, 'afterend', true);
368
397
  }
@@ -380,19 +409,17 @@ function getElFromString(dom, string, element, position, isAttribute) {
380
409
  if (position == 'afterbegin') {
381
410
  element.insertAdjacentElement('afterbegin', findEl);
382
411
  angle = '>';
383
- }
384
- else if (position == 'afterend') {
412
+ } else if (position == 'afterend') {
385
413
  element.insertAdjacentElement('afterend', findEl);
386
414
  angle = '>';
387
- }
388
- else if (position == 'beforebegin') {
415
+ } else if (position == 'beforebegin') {
389
416
  element.insertAdjacentElement('afterbegin', findEl);
390
417
  angle = '<';
391
- }
392
- else if (position == 'beforeend') {
418
+ } else if (position == 'beforeend') {
393
419
  element.insertAdjacentElement('afterend', findEl);
394
420
  angle = '<';
395
421
  }
422
+
396
423
  if (dom.tagName == 'HTML')
397
424
  start = dom.outerHTML.indexOf("<findelement></findelement>");
398
425
  else
@@ -409,15 +436,19 @@ function getElFromString(dom, string, element, position, isAttribute) {
409
436
 
410
437
  findEl.remove();
411
438
 
412
- let domString = dom.outerHTML.substring(0, start);
439
+ let domString = dom.innerHTML.substring(0, start);
440
+ // let domString = dom.outerHTML.substring(0, start);
413
441
 
414
442
  if (dom.tagName == "HTML") {
443
+ domString = dom.outerHTML.substring(0, start);
415
444
  let htmlIndex = string.indexOf('<html');
416
445
  let documentType = string.substring(0, htmlIndex);
417
446
  documentTypeAngles = documentType.split(angle).length - 1;
418
447
  }
419
448
  let angles = domString.split(angle);
420
449
  let angleLength = angles.length - 1;
450
+ // if (position == 'afterend')
451
+ // angleLength += 1;
421
452
  if (documentTypeAngles)
422
453
  angleLength += documentTypeAngles;
423
454
  let elStart = getPosition(string, angle, angleLength);