@sikka/hawa 0.0.237 → 0.0.239
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/styles.css +0 -6
- package/docs/documentation/iframe.html +1 -1
- package/docs/documentation/{main.53a90744.iframe.bundle.js → main.1d8c7d46.iframe.bundle.js} +1 -1
- package/docs/documentation/project.json +1 -1
- package/es/index.es.js +2 -2
- package/lib/index.js +2 -2
- package/package.json +1 -1
- package/src/elements/FloatingComment.tsx +596 -742
- package/src/styles.css +0 -6
|
@@ -40,6 +40,22 @@ const stylers = {
|
|
|
40
40
|
// Paste = Removal + Addition -> Styling Removal + Styling Addition
|
|
41
41
|
// Drag & Drop = Removal + Addition -> Styling Removal + Styling Addition
|
|
42
42
|
|
|
43
|
+
// FIXME:
|
|
44
|
+
// - Creating a new line, and typing data in it, and then setting two different stylings on the same text
|
|
45
|
+
// FIXME:
|
|
46
|
+
// - Type characters, create new line, remove the new line immediately
|
|
47
|
+
|
|
48
|
+
// FIXME: Line at first index of content editable
|
|
49
|
+
|
|
50
|
+
// FIXME: Typing behind a line break identifier
|
|
51
|
+
// One way to prevent it, is to check in the onChange event if the data added (removed might not be needed here), is behind a line break identifier
|
|
52
|
+
// if so, move the start and end index by the length of the added, and replace the added, and then add the added back in the offset index
|
|
53
|
+
|
|
54
|
+
// FIXME:
|
|
55
|
+
// Deleting all text in a line removes that line entirely
|
|
56
|
+
|
|
57
|
+
const lineBreakIdentifier = ""
|
|
58
|
+
|
|
43
59
|
export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
44
60
|
props
|
|
45
61
|
) => {
|
|
@@ -47,9 +63,11 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
47
63
|
content: "",
|
|
48
64
|
stylings: [], // A styling is an object with 2 indices specifying a substring of text, and the applied effect
|
|
49
65
|
revert: [0, 0],
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
clipboard: [],
|
|
67
|
+
events: {
|
|
68
|
+
paste: { is: false, length: null },
|
|
69
|
+
drop: { is: false, text: null, drag: null },
|
|
70
|
+
},
|
|
53
71
|
})
|
|
54
72
|
|
|
55
73
|
const field = useRef(null)
|
|
@@ -64,6 +82,28 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
64
82
|
return i
|
|
65
83
|
}
|
|
66
84
|
|
|
85
|
+
const getRelativePrecedingSum = (element, endIndex) => {
|
|
86
|
+
let nodes: any = Array.from(element.childNodes)
|
|
87
|
+
let sum = nodes
|
|
88
|
+
.slice(0, endIndex)
|
|
89
|
+
.map((node) => node.textContent.length)
|
|
90
|
+
.reduce((a, b) => a + b, 0)
|
|
91
|
+
return sum
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const getLinePrecedingSum = (endIndex) => {
|
|
95
|
+
let fieldNodes = Array.from(field.current.childNodes)
|
|
96
|
+
let sum = fieldNodes
|
|
97
|
+
.slice(0, endIndex)
|
|
98
|
+
.map((lineNode: any) => {
|
|
99
|
+
let lineNodes = Array.from(lineNode.childNodes)
|
|
100
|
+
return getRelativePrecedingSum(lineNode, lineNodes.length)
|
|
101
|
+
})
|
|
102
|
+
.reduce((a, b) => a + b, 0)
|
|
103
|
+
|
|
104
|
+
return sum
|
|
105
|
+
}
|
|
106
|
+
|
|
67
107
|
const getSelectionPrecedingSum = (name) => {
|
|
68
108
|
let selection = window.getSelection()
|
|
69
109
|
let nodes = Array.from(field.current.childNodes)
|
|
@@ -78,87 +118,88 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
78
118
|
(item: any) => !["#text", "BR"].includes(item.nodeName)
|
|
79
119
|
)
|
|
80
120
|
|
|
81
|
-
let
|
|
82
|
-
let
|
|
121
|
+
let node = selection[name]
|
|
122
|
+
let parent: any = node.parentNode
|
|
123
|
+
let sum = 0
|
|
124
|
+
// let special = 0
|
|
83
125
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// console.log("hi")
|
|
87
|
-
// }
|
|
126
|
+
const isNodeLine =
|
|
127
|
+
node.nodeName == "DIV" && Array.from(node.classList).includes("line")
|
|
88
128
|
|
|
89
|
-
//
|
|
90
|
-
if (
|
|
91
|
-
|
|
129
|
+
// If the parent node is a span, this must be a text node
|
|
130
|
+
if (parent.nodeName == "SPAN") {
|
|
131
|
+
// Get index of span within line
|
|
132
|
+
let spanIndex = getChildIndex(parent)
|
|
92
133
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.slice(0, index)
|
|
96
|
-
.map((e: any) => e.textContent.length)
|
|
97
|
-
.reduce((a, b) => a + b, 0)
|
|
98
|
-
}
|
|
134
|
+
// Get relative sum within line
|
|
135
|
+
sum += getRelativePrecedingSum(parent.parentNode, spanIndex)
|
|
99
136
|
|
|
100
|
-
|
|
137
|
+
// Get parent line index
|
|
138
|
+
let lineIndex = getChildIndex(parent.parentNode)
|
|
101
139
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.map((span: any) => span.textContent.length)
|
|
106
|
-
.reduce((a, b) => a + b, 0) + special
|
|
140
|
+
// Get relative sum within entire field
|
|
141
|
+
sum += getLinePrecedingSum(lineIndex)
|
|
142
|
+
}
|
|
107
143
|
|
|
108
|
-
return sum
|
|
109
|
-
|
|
144
|
+
// If the parent node is a line element, this must be a new line, so return the preceding sum
|
|
145
|
+
if (Array.from(parent.classList).includes("line")) {
|
|
146
|
+
// If the node is a text node, then that must mean this is a non-styled drop
|
|
147
|
+
if (node.nodeName == "#text") {
|
|
148
|
+
let spanIndex = getChildIndex(node)
|
|
110
149
|
|
|
111
|
-
|
|
112
|
-
|
|
150
|
+
sum += getRelativePrecedingSum(parent, spanIndex)
|
|
151
|
+
}
|
|
113
152
|
|
|
114
|
-
|
|
115
|
-
|
|
153
|
+
// Get line element index
|
|
154
|
+
let lineIndex = getChildIndex(parent)
|
|
116
155
|
|
|
117
|
-
|
|
156
|
+
// Get relative sum within entire field
|
|
157
|
+
sum += getLinePrecedingSum(lineIndex)
|
|
158
|
+
}
|
|
118
159
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
160
|
+
if (isNodeLine) {
|
|
161
|
+
// Get line element index
|
|
162
|
+
let lineIndex = getChildIndex(node)
|
|
163
|
+
|
|
164
|
+
// Get relative sum within entire field
|
|
165
|
+
sum = getLinePrecedingSum(lineIndex)
|
|
166
|
+
}
|
|
122
167
|
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// console.log(selection.focusNode.parentNode.parentNode)
|
|
129
|
-
// console.log(selection.anchorOffset)
|
|
130
|
-
// console.log(selection.focusOffset)
|
|
168
|
+
// If the parent is the field, return zero
|
|
169
|
+
// FIXME: Should we return zero here?
|
|
170
|
+
if (parent == field.current && !isNodeLine) {
|
|
171
|
+
return 0
|
|
172
|
+
}
|
|
131
173
|
|
|
132
|
-
//
|
|
174
|
+
// // Special case for dropping text near or inside styled text
|
|
175
|
+
// if (!Array.from(parent.parentNode.classList).includes("selection-ignore")) {
|
|
176
|
+
// parent = parent.parentNode
|
|
133
177
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// :
|
|
138
|
-
//
|
|
178
|
+
// let index = getChildIndex(selection[name].parentNode)
|
|
179
|
+
// special = Array.from(parent.childNodes)
|
|
180
|
+
// .slice(0, index)
|
|
181
|
+
// .map((e: any) => e.textContent.length)
|
|
182
|
+
// .reduce((a, b) => a + b, 0)
|
|
183
|
+
// }
|
|
139
184
|
|
|
140
|
-
// let
|
|
141
|
-
// .slice(0, startNodeIndex)
|
|
142
|
-
// .map((span: any) => span.textContent.length)
|
|
143
|
-
// .reduce((a, b) => a + b, 0)
|
|
185
|
+
// let index = parent == field.current ? nodes.length : getChildIndex(parent)
|
|
144
186
|
|
|
145
|
-
let
|
|
187
|
+
// let sum =
|
|
188
|
+
// nodes
|
|
189
|
+
// .slice(0, index)
|
|
190
|
+
// .map((span: any) => span.textContent.length)
|
|
191
|
+
// .reduce((a, b) => a + b, 0) + special
|
|
146
192
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// endParent == field.current
|
|
150
|
-
// ? nodes.length
|
|
151
|
-
// : // : parseInt(endParent.dataset.childIndex)
|
|
152
|
-
// getChildIndex(endParent)
|
|
193
|
+
return sum
|
|
194
|
+
}
|
|
153
195
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// .reduce((a, b) => a + b, 0)
|
|
196
|
+
const getFieldSelection = () => {
|
|
197
|
+
if (document.activeElement != field.current) return [0, 0]
|
|
198
|
+
let selection = window.getSelection()
|
|
158
199
|
|
|
200
|
+
let startPrecedingSum = getSelectionPrecedingSum("anchorNode")
|
|
159
201
|
let endPrecedingSum = getSelectionPrecedingSum("focusNode")
|
|
160
202
|
|
|
161
|
-
console.log([startPrecedingSum, endPrecedingSum])
|
|
162
203
|
let result = [
|
|
163
204
|
startPrecedingSum + selection.anchorOffset,
|
|
164
205
|
endPrecedingSum + selection.focusOffset,
|
|
@@ -178,7 +219,8 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
178
219
|
let endNode = null
|
|
179
220
|
|
|
180
221
|
let total = 0
|
|
181
|
-
let nodes = Array.from(field.current.childNodes)
|
|
222
|
+
// let nodes = Array.from(field.current.childNodes)
|
|
223
|
+
let nodes = Array.from(field.current.getElementsByTagName("span"))
|
|
182
224
|
|
|
183
225
|
for (let i = 0; i < nodes.length; i++) {
|
|
184
226
|
let node: any = nodes[i]
|
|
@@ -369,40 +411,15 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
369
411
|
})
|
|
370
412
|
}
|
|
371
413
|
|
|
372
|
-
const getRangeIntersectStylings = (start, end, startOffset, finishOffset) => {
|
|
373
|
-
// Get all stylings intersecting
|
|
374
|
-
let stylings = []
|
|
375
|
-
for (let i = start; i < end; i++) {
|
|
376
|
-
stylings.push(getIntersectStylings(i, startOffset, finishOffset))
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Remove duplicates
|
|
380
|
-
stylings = stylings.flat().filter((item) => item)
|
|
381
|
-
stylings = stylings.filter(
|
|
382
|
-
(item, index) =>
|
|
383
|
-
stylings.findIndex(
|
|
384
|
-
(_item) =>
|
|
385
|
-
_item.start == item.start &&
|
|
386
|
-
_item.finish == item.finish &&
|
|
387
|
-
_item.type == item.type
|
|
388
|
-
) == index
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
return stylings
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const compareStylings = (styling1, styling2) => {
|
|
395
|
-
return (
|
|
396
|
-
styling1.type == styling2.type &&
|
|
397
|
-
styling1.start == styling2.start &&
|
|
398
|
-
styling1.finish == styling2.finish
|
|
399
|
-
)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
414
|
// Get stylings encompassing an index within it's range
|
|
403
|
-
const getIntersectStylings = (
|
|
415
|
+
const getIntersectStylings = (
|
|
416
|
+
stylings,
|
|
417
|
+
index,
|
|
418
|
+
startOffset = 0,
|
|
419
|
+
finishOffset = 0
|
|
420
|
+
) => {
|
|
404
421
|
// Find all stylings with encompassing range
|
|
405
|
-
let matches =
|
|
422
|
+
let matches = stylings.filter(
|
|
406
423
|
({ start, finish }) =>
|
|
407
424
|
index >= start + startOffset && index < finish + finishOffset
|
|
408
425
|
)
|
|
@@ -410,730 +427,452 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
410
427
|
return matches
|
|
411
428
|
}
|
|
412
429
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const getStylingIndex = (styling) => {
|
|
422
|
-
return text.stylings.findIndex(
|
|
423
|
-
(item) =>
|
|
424
|
-
item.start == styling.start &&
|
|
425
|
-
item.finish == styling.finish &&
|
|
426
|
-
item.type == styling.type
|
|
427
|
-
)
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const handleScenario = (stylings, predicate) => {
|
|
431
|
-
let matches = stylings.filter(({ start: _start, finish: _finish }) =>
|
|
432
|
-
predicate(_start, _finish)
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
let indices = []
|
|
436
|
-
|
|
437
|
-
stylings.map((styling, index) => {
|
|
438
|
-
let result = matches.find((item) => compareStylings(styling, item))
|
|
439
|
-
if (!result) return
|
|
440
|
-
indices.push({
|
|
441
|
-
index: index,
|
|
442
|
-
type: styling.type,
|
|
443
|
-
start: styling.start,
|
|
444
|
-
finish: styling.finish,
|
|
445
|
-
})
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
return indices
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const handleDeletion = (length, start, end) => {
|
|
452
|
-
let stylings = _text.current.stylings.slice()
|
|
453
|
-
let changes = []
|
|
454
|
-
|
|
455
|
-
// TODO: Refactor
|
|
456
|
-
|
|
457
|
-
// Offset all succeeding stylings by length
|
|
458
|
-
// changes.push(
|
|
459
|
-
// handleScenario(
|
|
460
|
-
// stylings,
|
|
461
|
-
// (_start, _finish) => start < _start && end <= start
|
|
462
|
-
// ).map((styling) => {
|
|
463
|
-
// return {
|
|
464
|
-
// ...styling,
|
|
465
|
-
// start: styling.start - length,
|
|
466
|
-
// finish: styling.finish - length,
|
|
467
|
-
// }
|
|
468
|
-
// })
|
|
469
|
-
// )
|
|
470
|
-
|
|
471
|
-
let succeeding = stylings.filter(
|
|
472
|
-
({ start: _start }) => start < _start && end <= _start
|
|
473
|
-
)
|
|
474
|
-
stylings.map((styling, index) => {
|
|
475
|
-
let result = succeeding.find((item) => compareStylings(styling, item))
|
|
476
|
-
if (!result) return
|
|
477
|
-
|
|
478
|
-
changes.push({
|
|
479
|
-
index: index,
|
|
480
|
-
start: styling.start - length,
|
|
481
|
-
finish: styling.finish - length,
|
|
482
|
-
})
|
|
483
|
-
})
|
|
484
|
-
|
|
485
|
-
// Handle complete encapsulation over styling
|
|
486
|
-
let encapsulating = stylings.filter(
|
|
487
|
-
({ start: _start, finish: _finish }) => start <= _start && end >= _finish
|
|
488
|
-
)
|
|
489
|
-
stylings.map((styling, index) => {
|
|
490
|
-
let result = encapsulating.find((item) => compareStylings(styling, item))
|
|
491
|
-
if (!result) return
|
|
492
|
-
|
|
493
|
-
// This will effectively remove the styling, since collapsed ranges are automatically removed
|
|
494
|
-
changes.push({
|
|
495
|
-
index: index,
|
|
496
|
-
start: styling.start,
|
|
497
|
-
finish: styling.start,
|
|
498
|
-
})
|
|
499
|
-
})
|
|
500
|
-
|
|
501
|
-
// Handle deletion being encapsulated by styling
|
|
502
|
-
let encapsulated = stylings.filter(
|
|
503
|
-
({ start: _start, finish: _finish }) =>
|
|
504
|
-
(start > _start && end <= _finish) || (start >= _start && end < _finish)
|
|
505
|
-
)
|
|
506
|
-
stylings.map((styling, index) => {
|
|
507
|
-
let result = encapsulated.find((item) => compareStylings(styling, item))
|
|
508
|
-
if (!result) return
|
|
509
|
-
|
|
510
|
-
changes.push({
|
|
511
|
-
index: index,
|
|
512
|
-
start: styling.start,
|
|
513
|
-
finish: styling.finish - length,
|
|
514
|
-
})
|
|
515
|
-
})
|
|
516
|
-
|
|
517
|
-
// Handle deletion being encapsulated by styling with left resumption
|
|
518
|
-
let leftResumption = stylings.filter(
|
|
519
|
-
({ start: _start, finish: _finish }) =>
|
|
520
|
-
end < _finish && end > _start && start < _start
|
|
521
|
-
)
|
|
522
|
-
stylings.map((styling, index) => {
|
|
523
|
-
let result = leftResumption.find((item) => compareStylings(styling, item))
|
|
524
|
-
if (!result) return
|
|
525
|
-
|
|
526
|
-
changes.push({
|
|
527
|
-
index: index,
|
|
528
|
-
start: getMaximum([end, styling.start]) - length,
|
|
529
|
-
finish: styling.finish - length,
|
|
530
|
-
})
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
let rightResumption = stylings.filter(
|
|
534
|
-
({ start: _start, finish: _finish }) =>
|
|
535
|
-
start > _start && start < _finish && end > _finish
|
|
430
|
+
const getStylingsInRange = (stylings, startIndex, endIndex) => {
|
|
431
|
+
// Get all intersecting stylings within range
|
|
432
|
+
let result = stylings.filter(
|
|
433
|
+
({ start, finish }) =>
|
|
434
|
+
(finish > startIndex && start < endIndex) ||
|
|
435
|
+
(start < endIndex && finish > startIndex)
|
|
536
436
|
)
|
|
537
|
-
stylings.map((styling, index) => {
|
|
538
|
-
let result = rightResumption.find((item) =>
|
|
539
|
-
compareStylings(styling, item)
|
|
540
|
-
)
|
|
541
|
-
if (!result) return
|
|
542
|
-
|
|
543
|
-
changes.push({
|
|
544
|
-
index: index,
|
|
545
|
-
start: styling.start,
|
|
546
|
-
finish: getMinimum([start, styling.finish]),
|
|
547
|
-
})
|
|
548
|
-
})
|
|
549
437
|
|
|
550
|
-
//
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
...
|
|
554
|
-
start: start,
|
|
555
|
-
finish: finish,
|
|
438
|
+
// Clamp start and end values, and offset by start index to reach the relative minimum
|
|
439
|
+
result = result.map((styling) => {
|
|
440
|
+
return {
|
|
441
|
+
...styling,
|
|
442
|
+
start: getMaximum([styling.start, startIndex]) - startIndex,
|
|
443
|
+
finish: getMinimum([styling.finish, endIndex]) - startIndex,
|
|
556
444
|
}
|
|
557
445
|
})
|
|
558
446
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// if (start >= styling.start && end <= styling.finish) {
|
|
562
|
-
// console.log("hi")
|
|
563
|
-
// }
|
|
564
|
-
|
|
565
|
-
// return styling
|
|
566
|
-
// })
|
|
567
|
-
|
|
568
|
-
// If deletion is surrounded by styling, decrease finish by length (this might not include first character, but try first)
|
|
569
|
-
// If deletion is intersecting with a styling behind it, get maximum between deletion finish and styling start, and set the start to it, then offset styling by length
|
|
570
|
-
// If deletion is intersecting wit ha styling after it, get minimum between deletion start and styling finish, and set the finish to it
|
|
447
|
+
return result
|
|
448
|
+
}
|
|
571
449
|
|
|
572
|
-
|
|
450
|
+
const splitStyling = (styling, index, offset = 0) => {
|
|
451
|
+
// Get first split
|
|
452
|
+
let first = {
|
|
453
|
+
...styling,
|
|
454
|
+
start: styling.start,
|
|
455
|
+
finish: index,
|
|
456
|
+
}
|
|
573
457
|
|
|
574
|
-
//
|
|
458
|
+
// Get second split
|
|
459
|
+
let second = {
|
|
460
|
+
...styling,
|
|
461
|
+
start: index + offset,
|
|
462
|
+
finish: styling.finish + offset,
|
|
463
|
+
}
|
|
575
464
|
|
|
576
|
-
return
|
|
465
|
+
return [first, second]
|
|
577
466
|
}
|
|
578
467
|
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
end -= length + 1
|
|
582
|
-
|
|
583
|
-
let stylings = _text.current.stylings.slice()
|
|
468
|
+
const additionTo = (stylings, startIndex, length, defaultBehavior = true) => {
|
|
469
|
+
// console.log(`Adding text of length ${length} at index ${startIndex}`)
|
|
584
470
|
|
|
585
|
-
|
|
471
|
+
// Required operations:
|
|
472
|
+
// Offset succeeding stylings
|
|
473
|
+
// Split intersecting stylings
|
|
474
|
+
// Styling continuation at end (only for normal addition)
|
|
586
475
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
476
|
+
stylings = stylings.map((styling) => {
|
|
477
|
+
// A succeeding styling
|
|
478
|
+
if (styling.start >= startIndex && styling.finish > startIndex) {
|
|
479
|
+
console.log("succeeding")
|
|
480
|
+
return {
|
|
481
|
+
...styling,
|
|
482
|
+
start: styling.start + length,
|
|
483
|
+
finish: styling.finish + length,
|
|
484
|
+
}
|
|
595
485
|
}
|
|
596
|
-
})
|
|
597
|
-
changes.push(succeeding)
|
|
598
|
-
|
|
599
|
-
// console.log(`Addition (${length})`)
|
|
600
|
-
// console.log([start, end])
|
|
601
|
-
// if (stylings.length > 0) {
|
|
602
|
-
// console.log([stylings[0].start, stylings[0].finish])
|
|
603
|
-
// }
|
|
604
486
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
487
|
+
// An intersecting styling
|
|
488
|
+
if (styling.start < startIndex && styling.finish > startIndex) {
|
|
489
|
+
// Normal addition (non-drop & non-paste), adds to the length of the styling
|
|
490
|
+
if (defaultBehavior) {
|
|
491
|
+
return {
|
|
492
|
+
...styling,
|
|
493
|
+
finish: styling.finish + length,
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
// Special addition (drop & paste), splits the styling and offsets the second half by length of addition
|
|
497
|
+
return splitStyling(styling, startIndex, length)
|
|
498
|
+
}
|
|
616
499
|
}
|
|
617
|
-
})
|
|
618
|
-
changes.push(preceding)
|
|
619
500
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
501
|
+
// A connected styling at the end
|
|
502
|
+
if (styling.finish == startIndex) {
|
|
503
|
+
// Normal addition (non-drop & non-paste), continues the styling by addition length
|
|
504
|
+
if (defaultBehavior) {
|
|
505
|
+
return {
|
|
506
|
+
...styling,
|
|
507
|
+
finish: styling.finish + length,
|
|
508
|
+
}
|
|
509
|
+
}
|
|
627
510
|
}
|
|
511
|
+
|
|
512
|
+
return styling
|
|
628
513
|
})
|
|
629
514
|
|
|
630
|
-
|
|
515
|
+
return stylings.flat()
|
|
516
|
+
}
|
|
631
517
|
|
|
632
|
-
|
|
518
|
+
const deletionOf = (stylings, startIndex, endIndex) => {
|
|
519
|
+
// console.log(`Removing text from ${startIndex} to ${endIndex}`)
|
|
633
520
|
|
|
634
|
-
|
|
521
|
+
let length = Math.abs(endIndex - startIndex)
|
|
635
522
|
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
//
|
|
523
|
+
// Required operations:
|
|
524
|
+
// Offset succeeding stylings
|
|
525
|
+
// Remove stylings completely within range
|
|
526
|
+
// Shrink stylings surrounding range
|
|
527
|
+
// Clamp left resumptions and offset
|
|
528
|
+
// Clamp right resumptions
|
|
639
529
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
start
|
|
644
|
-
|
|
530
|
+
stylings = stylings.map((styling) => {
|
|
531
|
+
// A succeeding styling, but not a right resumption
|
|
532
|
+
if (
|
|
533
|
+
styling.start >= startIndex &&
|
|
534
|
+
styling.start >= endIndex &&
|
|
535
|
+
styling.finish >= startIndex &&
|
|
536
|
+
styling.finish >= endIndex
|
|
537
|
+
) {
|
|
538
|
+
return {
|
|
539
|
+
...styling,
|
|
540
|
+
start: styling.start - length,
|
|
541
|
+
finish: styling.finish - length,
|
|
542
|
+
}
|
|
645
543
|
}
|
|
646
|
-
})
|
|
647
544
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const handlePaste = (stylings, difference, start, end) => {
|
|
652
|
-
// console.log(`Paste:`)
|
|
653
|
-
// console.log([start, end])
|
|
654
|
-
// console.log(stylings)
|
|
655
|
-
let changes = []
|
|
656
|
-
|
|
657
|
-
// Get stylings being encapsulated by pasting range
|
|
658
|
-
let encapsulating = handleScenario(
|
|
659
|
-
stylings,
|
|
660
|
-
(_start, _end) => start <= _start && end >= _end // This is needed because of conflict with addition/deletion
|
|
661
|
-
).map((styling) => {
|
|
662
|
-
// Effective removal
|
|
663
|
-
return {
|
|
664
|
-
...styling,
|
|
665
|
-
start: styling.start,
|
|
666
|
-
finish: styling.start,
|
|
545
|
+
// A styling completely within deletion range
|
|
546
|
+
if (styling.start >= startIndex && styling.finish <= endIndex) {
|
|
547
|
+
return null
|
|
667
548
|
}
|
|
668
|
-
})
|
|
669
|
-
changes.push(encapsulating)
|
|
670
|
-
|
|
671
|
-
// Get encapsulated stylings
|
|
672
|
-
let encapsulated = handleScenario(
|
|
673
|
-
stylings,
|
|
674
|
-
(_start, _end) => _start < start && _end > end
|
|
675
|
-
).map((styling) => {
|
|
676
|
-
// Right splice
|
|
677
|
-
changes.push({
|
|
678
|
-
...styling,
|
|
679
|
-
index: -1,
|
|
680
|
-
start: end,
|
|
681
|
-
finish: styling.finish,
|
|
682
|
-
})
|
|
683
|
-
|
|
684
|
-
// Left splice
|
|
685
|
-
changes.push({
|
|
686
|
-
...styling,
|
|
687
|
-
index: -1,
|
|
688
|
-
start: styling.start,
|
|
689
|
-
finish: start,
|
|
690
|
-
})
|
|
691
549
|
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
})
|
|
699
|
-
changes.push(encapsulated)
|
|
700
|
-
|
|
701
|
-
// Get intersecting stylings from the left
|
|
702
|
-
let left = handleScenario(
|
|
703
|
-
stylings,
|
|
704
|
-
(_start, _end) =>
|
|
705
|
-
_end > start && _end <= end - difference + 1 && _start < start
|
|
706
|
-
).map((styling) => {
|
|
707
|
-
return {
|
|
708
|
-
...styling,
|
|
709
|
-
finish: start,
|
|
550
|
+
// A styling surrounding deletion range
|
|
551
|
+
if (styling.start <= startIndex && styling.finish >= endIndex) {
|
|
552
|
+
return {
|
|
553
|
+
...styling,
|
|
554
|
+
finish: styling.finish - length,
|
|
555
|
+
}
|
|
710
556
|
}
|
|
711
|
-
})
|
|
712
|
-
|
|
713
|
-
changes.push(left)
|
|
714
557
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
...styling,
|
|
723
|
-
start: end,
|
|
558
|
+
// A styling not fully within deletion range, while the range exceeds to the left
|
|
559
|
+
if (styling.start > startIndex && styling.start < endIndex) {
|
|
560
|
+
return {
|
|
561
|
+
...styling,
|
|
562
|
+
start: getMaximum([styling.start, endIndex]) - length,
|
|
563
|
+
finish: styling.finish - length,
|
|
564
|
+
}
|
|
724
565
|
}
|
|
725
|
-
})
|
|
726
566
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
changes = changes.flat()
|
|
735
|
-
changes.map((styling) => {
|
|
736
|
-
let { index, start, finish } = styling
|
|
737
|
-
if (index == -1) {
|
|
738
|
-
stylings.push({
|
|
739
|
-
type: styling.type,
|
|
740
|
-
start: start,
|
|
741
|
-
finish: finish,
|
|
742
|
-
})
|
|
743
|
-
return
|
|
567
|
+
// A styling not full within deletion range, while the range exceeds to the right
|
|
568
|
+
if (styling.finish > startIndex && styling.finish < endIndex) {
|
|
569
|
+
return {
|
|
570
|
+
...styling,
|
|
571
|
+
finish: getMinimum([styling.finish, startIndex]),
|
|
572
|
+
}
|
|
744
573
|
}
|
|
745
574
|
|
|
746
|
-
|
|
747
|
-
...stylings[index],
|
|
748
|
-
start: start,
|
|
749
|
-
finish: finish,
|
|
750
|
-
}
|
|
575
|
+
return styling
|
|
751
576
|
})
|
|
752
577
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
return stylings
|
|
578
|
+
return stylings.flat().filter((styling) => styling)
|
|
756
579
|
}
|
|
757
580
|
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
581
|
+
const processNormal = (
|
|
582
|
+
stylings,
|
|
583
|
+
difference,
|
|
584
|
+
selectionStart,
|
|
585
|
+
selectionEnd
|
|
586
|
+
) => {
|
|
587
|
+
if (difference == 0) return stylings
|
|
588
|
+
|
|
589
|
+
if (difference > 0) {
|
|
590
|
+
stylings = additionTo(stylings, selectionStart - difference, difference)
|
|
591
|
+
} else {
|
|
592
|
+
stylings = deletionOf(
|
|
593
|
+
stylings,
|
|
594
|
+
selectionStart,
|
|
595
|
+
selectionEnd + Math.abs(difference)
|
|
596
|
+
)
|
|
597
|
+
}
|
|
774
598
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
// If the handling occurs at the second occurence of onInput, the first could be handled naturally by the handleDeletion function
|
|
599
|
+
return stylings
|
|
600
|
+
}
|
|
778
601
|
|
|
779
|
-
|
|
602
|
+
const processPaste = (stylings, difference, selectionStart, selectionEnd) => {
|
|
603
|
+
let pasteLength = _text.current.events.paste.length
|
|
780
604
|
|
|
781
|
-
//
|
|
782
|
-
//
|
|
783
|
-
//
|
|
605
|
+
// console.log(
|
|
606
|
+
// `Pasting for length ${pasteLength} at [${selectionStart}, ${selectionEnd}]`
|
|
607
|
+
// )
|
|
608
|
+
// console.log(
|
|
609
|
+
// `Accompanied stylings: ${JSON.stringify(
|
|
610
|
+
// _text.current.clipboard,
|
|
611
|
+
// null,
|
|
612
|
+
// 2
|
|
613
|
+
// )}`
|
|
614
|
+
// )
|
|
784
615
|
|
|
785
|
-
//
|
|
786
|
-
|
|
787
|
-
// (_start >= dp.end && _end > dp.end)
|
|
616
|
+
// Get addition start index
|
|
617
|
+
let additionStart = selectionStart - pasteLength
|
|
788
618
|
|
|
789
|
-
// Get
|
|
790
|
-
let
|
|
791
|
-
|
|
792
|
-
!(
|
|
793
|
-
(start < dragStart && finish <= dragStart) ||
|
|
794
|
-
(start >= dragEnd && finish > dragEnd)
|
|
795
|
-
)
|
|
796
|
-
)
|
|
619
|
+
// Get removal range
|
|
620
|
+
let removalStart = additionStart
|
|
621
|
+
let removalEnd = additionStart + pasteLength - difference
|
|
797
622
|
|
|
798
|
-
//
|
|
799
|
-
stylings
|
|
800
|
-
.filter(({ start, finish }) => start >= dragEnd && finish > dragEnd)
|
|
801
|
-
.map((styling) => {
|
|
802
|
-
changes.push({
|
|
803
|
-
original: styling,
|
|
804
|
-
changed: {
|
|
805
|
-
...styling,
|
|
806
|
-
start: styling.start - dropped.length,
|
|
807
|
-
finish: styling.finish - dropped.length,
|
|
808
|
-
},
|
|
809
|
-
})
|
|
810
|
-
})
|
|
623
|
+
// Compute deletion
|
|
624
|
+
stylings = deletionOf(stylings, removalStart, removalEnd)
|
|
811
625
|
|
|
812
|
-
//
|
|
813
|
-
stylings
|
|
814
|
-
.filter(({ start, finish }) => dragStart <= start && dragEnd >= finish)
|
|
815
|
-
.map((styling) => {
|
|
816
|
-
changes.push({
|
|
817
|
-
original: styling,
|
|
818
|
-
changed: { ...styling, start: styling.start, finish: styling.start },
|
|
819
|
-
})
|
|
820
|
-
})
|
|
626
|
+
// Compute addition
|
|
627
|
+
stylings = additionTo(stylings, additionStart, pasteLength, false)
|
|
821
628
|
|
|
822
|
-
//
|
|
823
|
-
stylings
|
|
824
|
-
.
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
changes.push({
|
|
831
|
-
original: styling,
|
|
832
|
-
changed: {
|
|
833
|
-
...styling,
|
|
834
|
-
start: styling.start,
|
|
835
|
-
finish: styling.finish - dropped.length,
|
|
836
|
-
},
|
|
837
|
-
})
|
|
629
|
+
// Add rich copied stylings offset by paste start index
|
|
630
|
+
stylings = stylings.concat(
|
|
631
|
+
_text.current.clipboard.map((styling) => {
|
|
632
|
+
return {
|
|
633
|
+
...styling,
|
|
634
|
+
start: styling.start + additionStart,
|
|
635
|
+
finish: styling.finish + additionStart,
|
|
636
|
+
}
|
|
838
637
|
})
|
|
839
|
-
|
|
840
|
-
// Handle resumptions
|
|
841
|
-
let resumptions = stylings.filter(
|
|
842
|
-
({ start, finish }) =>
|
|
843
|
-
(finish > dragStart &&
|
|
844
|
-
start > dragStart &&
|
|
845
|
-
start < dragEnd &&
|
|
846
|
-
finish > dragEnd) ||
|
|
847
|
-
(start < dragEnd &&
|
|
848
|
-
finish < dragEnd &&
|
|
849
|
-
finish > dragStart &&
|
|
850
|
-
start < dragStart)
|
|
851
638
|
)
|
|
852
639
|
|
|
853
|
-
|
|
854
|
-
let right =
|
|
855
|
-
styling.start < dragEnd &&
|
|
856
|
-
styling.finish < dragEnd &&
|
|
857
|
-
styling.finish > dragStart &&
|
|
858
|
-
styling.start < dragStart
|
|
640
|
+
// console.log(`End result: ${JSON.stringify(stylings, null, 2)}`)
|
|
859
641
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
changed: {
|
|
863
|
-
...styling,
|
|
864
|
-
start: !right
|
|
865
|
-
? getMaximum([dragEnd, styling.start]) - dropped.length // Only decrease if the drag
|
|
866
|
-
: styling.start,
|
|
867
|
-
finish: right
|
|
868
|
-
? getMinimum([dragStart, styling.finish])
|
|
869
|
-
: styling.finish - dropped.length,
|
|
870
|
-
},
|
|
871
|
-
})
|
|
872
|
-
})
|
|
873
|
-
|
|
874
|
-
// Apply drag changes
|
|
875
|
-
changes.map(({ original, changed }) => {
|
|
876
|
-
let index = stylings.findIndex((styling) =>
|
|
877
|
-
compareStylings(original, styling)
|
|
878
|
-
)
|
|
642
|
+
return stylings
|
|
643
|
+
}
|
|
879
644
|
|
|
880
|
-
|
|
881
|
-
})
|
|
645
|
+
const processDrop = (stylings, difference, dropStart, dropEnd) => {
|
|
646
|
+
console.log(`Dropped at: ${[dropStart, dropEnd]}`)
|
|
882
647
|
|
|
883
|
-
|
|
648
|
+
let dropLength = _text.current.events.drop.text.length
|
|
884
649
|
|
|
885
|
-
|
|
886
|
-
stylings
|
|
887
|
-
.filter(({ start, finish }) => start >= dropStart && finish > dropStart)
|
|
888
|
-
.map((styling) => {
|
|
889
|
-
changes.push({
|
|
890
|
-
original: styling,
|
|
891
|
-
changed: {
|
|
892
|
-
...styling,
|
|
893
|
-
start: styling.start + dropped.length,
|
|
894
|
-
finish: styling.finish + dropped.length,
|
|
895
|
-
},
|
|
896
|
-
})
|
|
897
|
-
})
|
|
650
|
+
let [dragStart, dragEnd] = _text.current.events.drop.drag
|
|
898
651
|
|
|
899
|
-
|
|
900
|
-
stylings
|
|
901
|
-
.filter(({ start, finish }) => start < dropStart && finish > dropStart)
|
|
902
|
-
.map((styling) => {
|
|
903
|
-
changes.push({
|
|
904
|
-
original: styling,
|
|
905
|
-
changed: {
|
|
906
|
-
...styling,
|
|
907
|
-
start: styling.start,
|
|
908
|
-
finish: dropStart,
|
|
909
|
-
},
|
|
910
|
-
})
|
|
652
|
+
let dropDifference = dropStart - dragStart
|
|
911
653
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
changed: {
|
|
915
|
-
...styling,
|
|
916
|
-
start: dropEnd,
|
|
917
|
-
finish: styling.finish + dropped.length,
|
|
918
|
-
},
|
|
919
|
-
})
|
|
920
|
-
})
|
|
654
|
+
// Get stylings at drag range
|
|
655
|
+
let dragStylings = getStylingsInRange(stylings, dragStart, dragEnd)
|
|
921
656
|
|
|
922
|
-
//
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
stylings.push(changed)
|
|
926
|
-
return
|
|
927
|
-
}
|
|
657
|
+
// Removal range
|
|
658
|
+
let removalStart = dragStart
|
|
659
|
+
let removalEnd = dragEnd
|
|
928
660
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
661
|
+
// If the drag precedes the drop (positive difference)
|
|
662
|
+
if (dropDifference > 0) {
|
|
663
|
+
// Addition start index
|
|
664
|
+
let additionStart = dropEnd
|
|
932
665
|
|
|
933
|
-
|
|
934
|
-
|
|
666
|
+
// Compute addition first
|
|
667
|
+
stylings = additionTo(stylings, additionStart, dropLength, false)
|
|
935
668
|
|
|
936
|
-
|
|
937
|
-
|
|
669
|
+
// Compute removal second
|
|
670
|
+
stylings = deletionOf(stylings, removalStart, removalEnd)
|
|
671
|
+
}
|
|
938
672
|
|
|
939
|
-
//
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
start: getMaximum([styling.start, dragStart]) - dragStart + dropStart,
|
|
944
|
-
finish: getMinimum([styling.finish, dragEnd]) - dragStart + dropStart,
|
|
945
|
-
}
|
|
946
|
-
})
|
|
673
|
+
// If the drop precedes the drag (negative difference)
|
|
674
|
+
if (dropDifference < 0) {
|
|
675
|
+
// Addition start index
|
|
676
|
+
let additionStart = dropStart
|
|
947
677
|
|
|
948
|
-
|
|
949
|
-
|
|
678
|
+
// Compute removal first
|
|
679
|
+
stylings = deletionOf(stylings, removalStart, removalEnd)
|
|
950
680
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
stylings: stylings,
|
|
955
|
-
revert: [dropStart, dropEnd],
|
|
956
|
-
pasted: { status: false, length: 0 },
|
|
957
|
-
dropped: { status: false, text: "", previous: [0, 0] },
|
|
958
|
-
})
|
|
959
|
-
}
|
|
681
|
+
// Compute addition second
|
|
682
|
+
stylings = additionTo(stylings, additionStart, dropLength, false)
|
|
683
|
+
}
|
|
960
684
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
685
|
+
// Add rich dragged stylings offset
|
|
686
|
+
stylings = stylings.concat(
|
|
687
|
+
dragStylings.map((styling) => {
|
|
688
|
+
return {
|
|
689
|
+
...styling,
|
|
690
|
+
start: styling.start + dropStart,
|
|
691
|
+
finish: styling.finish + dropStart,
|
|
967
692
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
let [selectionStart, selectionEnd] = getFieldSelection()
|
|
972
|
-
let difference = value.length - _text.current.content.length
|
|
973
|
-
|
|
974
|
-
let start = selectionStart - difference
|
|
975
|
-
let end = selectionEnd - difference
|
|
693
|
+
})
|
|
694
|
+
)
|
|
976
695
|
|
|
977
|
-
|
|
696
|
+
return stylings
|
|
697
|
+
}
|
|
978
698
|
|
|
979
|
-
|
|
980
|
-
|
|
699
|
+
const onChange = (value, selectionStart, selectionEnd) => {
|
|
700
|
+
// Since drop events cause onChange to invoke twice, ignore the first incomplete event
|
|
701
|
+
if (
|
|
702
|
+
_text.current.events.drop.is &&
|
|
703
|
+
value.length != _text.current.content.length
|
|
704
|
+
)
|
|
705
|
+
return
|
|
981
706
|
|
|
982
|
-
|
|
983
|
-
let index = getStylingIndex(succeed)
|
|
984
|
-
let styling = stylings[index]
|
|
707
|
+
console.log(`Changed at: ${[selectionStart, selectionEnd]}`)
|
|
985
708
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
])
|
|
991
|
-
}
|
|
709
|
+
setTimeout(function () {
|
|
710
|
+
// let [selectionStart, selectionEnd] = getFieldSelection()
|
|
711
|
+
let difference = value.length - _text.current.content.length
|
|
712
|
+
let stylings = _text.current.stylings
|
|
992
713
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
714
|
+
// Paste event
|
|
715
|
+
if (_text.current.events.paste.is) {
|
|
716
|
+
stylings = processPaste(
|
|
717
|
+
stylings,
|
|
718
|
+
difference,
|
|
996
719
|
selectionStart,
|
|
997
|
-
selectionEnd
|
|
720
|
+
selectionEnd
|
|
998
721
|
)
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
Math.abs(difference),
|
|
722
|
+
} else if (_text.current.events.drop.is) {
|
|
723
|
+
stylings = processDrop(
|
|
724
|
+
stylings,
|
|
725
|
+
difference,
|
|
1004
726
|
selectionStart,
|
|
1005
727
|
selectionEnd
|
|
1006
728
|
)
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
// Remove empty stylings and invisible stylings
|
|
1010
|
-
stylings = stylings.filter(
|
|
1011
|
-
(styling) =>
|
|
1012
|
-
!(
|
|
1013
|
-
styling.start == styling.finish ||
|
|
1014
|
-
(styling.start >= value.length && styling.finish >= value.length)
|
|
1015
|
-
)
|
|
1016
|
-
)
|
|
1017
|
-
|
|
1018
|
-
if (_text.current.pasted.status) {
|
|
1019
|
-
stylings = handlePaste(
|
|
729
|
+
} else {
|
|
730
|
+
stylings = processNormal(
|
|
1020
731
|
stylings,
|
|
1021
732
|
difference,
|
|
1022
|
-
selectionStart
|
|
1023
|
-
|
|
733
|
+
selectionStart,
|
|
734
|
+
selectionEnd
|
|
1024
735
|
)
|
|
1025
|
-
|
|
1026
|
-
for (let styling of _text.current.lastCopy) {
|
|
1027
|
-
styling = {
|
|
1028
|
-
...styling,
|
|
1029
|
-
start: styling.start + selectionStart - _text.current.pasted.length,
|
|
1030
|
-
finish:
|
|
1031
|
-
styling.finish + selectionStart - _text.current.pasted.length,
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
stylings.push(styling)
|
|
1035
|
-
}
|
|
1036
736
|
}
|
|
1037
737
|
|
|
1038
|
-
// Remove empty stylings and invisible stylings
|
|
1039
|
-
stylings = stylings.filter(
|
|
1040
|
-
(styling) =>
|
|
1041
|
-
!(
|
|
1042
|
-
styling.start == styling.finish ||
|
|
1043
|
-
(styling.start >= value.length && styling.finish >= value.length)
|
|
1044
|
-
)
|
|
1045
|
-
)
|
|
1046
|
-
|
|
1047
738
|
setText({
|
|
1048
739
|
..._text.current,
|
|
1049
740
|
content: value,
|
|
1050
741
|
stylings: stylings,
|
|
1051
742
|
revert: [selectionStart, selectionEnd],
|
|
1052
|
-
|
|
1053
|
-
|
|
743
|
+
events: {
|
|
744
|
+
paste: { is: false, length: null },
|
|
745
|
+
drop: { is: false, text: null, drag: null },
|
|
746
|
+
},
|
|
1054
747
|
})
|
|
1055
748
|
}, 0)
|
|
1056
749
|
}
|
|
1057
750
|
|
|
1058
751
|
const getContent = () => {
|
|
752
|
+
// console.clear()
|
|
1059
753
|
let content = _text.current.content
|
|
754
|
+
let stylings = [..._text.current.stylings]
|
|
1060
755
|
|
|
1061
|
-
|
|
1062
|
-
let indices = _text.current.stylings
|
|
1063
|
-
.map(({ start, finish }) => [start, finish])
|
|
1064
|
-
.flat()
|
|
756
|
+
let result = []
|
|
1065
757
|
|
|
1066
|
-
//
|
|
1067
|
-
|
|
758
|
+
// Get line indices
|
|
759
|
+
let lineIndices = content
|
|
760
|
+
.split("")
|
|
761
|
+
.map((character, index) => {
|
|
762
|
+
if (character == lineBreakIdentifier) return index
|
|
763
|
+
return null
|
|
764
|
+
})
|
|
765
|
+
.filter((item) => item)
|
|
1068
766
|
|
|
1069
|
-
//
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
)
|
|
767
|
+
// Add end point
|
|
768
|
+
lineIndices.unshift(0)
|
|
769
|
+
lineIndices.push(content.length)
|
|
1073
770
|
|
|
1074
|
-
//
|
|
1075
|
-
|
|
771
|
+
// Ignore last element
|
|
772
|
+
for (let i = 0; i < lineIndices.length - 1; i++) {
|
|
773
|
+
let lineStart = lineIndices[i]
|
|
774
|
+
let lineEnd = lineIndices[i + 1]
|
|
1076
775
|
|
|
1077
|
-
|
|
1078
|
-
let last = content.length
|
|
1079
|
-
if (indices[indices.length - 1] != last) indices.push(last)
|
|
776
|
+
let lineContent = content.slice(lineStart, lineEnd)
|
|
1080
777
|
|
|
1081
|
-
|
|
778
|
+
// console.log(
|
|
779
|
+
// `Line: ${lineStart} -> ${lineEnd}, with content: ${lineContent}`
|
|
780
|
+
// )
|
|
1082
781
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
}
|
|
782
|
+
// Get all stylings within line
|
|
783
|
+
let lineStylings = getStylingsInRange(stylings, lineStart, lineEnd)
|
|
1086
784
|
|
|
1087
|
-
|
|
1088
|
-
|
|
785
|
+
// Collect all spans relative to stylings within line
|
|
786
|
+
let spans = []
|
|
1089
787
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
788
|
+
// Acquire all flattened unique indices
|
|
789
|
+
let indices = lineStylings
|
|
790
|
+
.map(({ start, finish }) => [start, finish])
|
|
791
|
+
.flat()
|
|
792
|
+
.sort((a, b) => a - b)
|
|
1096
793
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
(item, index) =>
|
|
1101
|
-
stylings.findIndex(
|
|
1102
|
-
(_item) =>
|
|
1103
|
-
_item.start == item.start &&
|
|
1104
|
-
_item.finish == item.finish &&
|
|
1105
|
-
_item.type == item.type
|
|
1106
|
-
) == index
|
|
1107
|
-
)
|
|
794
|
+
indices = indices.filter(
|
|
795
|
+
(element, index) => indices.indexOf(element) == index
|
|
796
|
+
)
|
|
1108
797
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
798
|
+
// Add first index if not present
|
|
799
|
+
if (indices[0] != 0) indices.unshift(0)
|
|
800
|
+
|
|
801
|
+
// Add last index if not present
|
|
802
|
+
let last = lineContent.length
|
|
803
|
+
if (indices[indices.length - 1] != last) indices.push(last)
|
|
804
|
+
|
|
805
|
+
// Iterate through all indices except the last
|
|
806
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
807
|
+
let startIndex = indices[i]
|
|
808
|
+
let endIndex = indices[i + 1]
|
|
809
|
+
|
|
810
|
+
spans.push({
|
|
811
|
+
content: lineContent.substring(startIndex, endIndex),
|
|
812
|
+
types: getIntersectStylings(lineStylings, startIndex).map(
|
|
813
|
+
(styling) => styling.type
|
|
814
|
+
),
|
|
815
|
+
})
|
|
1115
816
|
}
|
|
1116
|
-
})
|
|
1117
817
|
|
|
1118
|
-
|
|
818
|
+
result.push(spans)
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// // Get all styling indices
|
|
822
|
+
// let indices = _text.current.stylings
|
|
823
|
+
// .map(({ start, finish }) => [start, finish])
|
|
824
|
+
// .flat()
|
|
825
|
+
|
|
826
|
+
// // Sort ascendingly
|
|
827
|
+
// indices = indices.sort((a, b) => a - b)
|
|
828
|
+
|
|
829
|
+
// // Remove duplicates
|
|
830
|
+
// indices = indices.filter(
|
|
831
|
+
// (element, index) => indices.indexOf(element) == index
|
|
832
|
+
// )
|
|
833
|
+
|
|
834
|
+
// // Add first index if not present
|
|
835
|
+
// if (indices[0] != 0) indices.unshift(0)
|
|
836
|
+
|
|
837
|
+
// // Add last index if not present
|
|
838
|
+
// let last = content.length
|
|
839
|
+
// if (indices[indices.length - 1] != last) indices.push(last)
|
|
840
|
+
|
|
841
|
+
// // let result = []
|
|
842
|
+
|
|
843
|
+
// for (let i = 0; i < indices.length - 1; i++) {
|
|
844
|
+
// result.push([indices[i], content.substring(indices[i], indices[i + 1])])
|
|
845
|
+
// }
|
|
846
|
+
|
|
847
|
+
return result
|
|
1119
848
|
}
|
|
1120
849
|
|
|
1121
850
|
// dangerouslySetInnerHTML incorrectly renders when the entire text is highlighted, copied, and then pasted in succession
|
|
1122
851
|
useEffect(() => {
|
|
1123
852
|
let html = getContent()
|
|
1124
|
-
.map((
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
.map((styling) => stylers[styling.type].css)
|
|
1133
|
-
.join(" ")}" data-child-index="${index}">${data}</span>`
|
|
853
|
+
.map((line, index) => {
|
|
854
|
+
return `<div class="line" data-line-index="${index}">${line
|
|
855
|
+
.map((span, _index) => {
|
|
856
|
+
return `<span class="${span.types
|
|
857
|
+
.map((type) => stylers[type].css)
|
|
858
|
+
.join(" ")}" data-child-index="${_index}">${span.content}</span>`
|
|
859
|
+
})
|
|
860
|
+
.join("")}</div>`
|
|
1134
861
|
})
|
|
1135
862
|
.join("")
|
|
1136
863
|
|
|
864
|
+
// let html = getContent()
|
|
865
|
+
// .map((_data, index) => {
|
|
866
|
+
// let [start, data] = _data
|
|
867
|
+
|
|
868
|
+
// // Get stylings encompassing an index within it's range
|
|
869
|
+
// let stylings = getIntersectStylings(_text.current.stylings, start)
|
|
870
|
+
// return `<span class="${stylings
|
|
871
|
+
// .map((styling) => stylers[styling.type].css)
|
|
872
|
+
// .join(" ")}" data-child-index="${index}">${data}</span>`
|
|
873
|
+
// })
|
|
874
|
+
// .join("")
|
|
875
|
+
|
|
1137
876
|
field.current.innerHTML = html
|
|
1138
877
|
}, [text.content, text.stylings, text.revert])
|
|
1139
878
|
|
|
@@ -1150,22 +889,6 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
1150
889
|
}
|
|
1151
890
|
}
|
|
1152
891
|
|
|
1153
|
-
const calculateTextWidth = (
|
|
1154
|
-
text: string,
|
|
1155
|
-
font: { size: number; family: string }
|
|
1156
|
-
) => {
|
|
1157
|
-
let element = document.createElement("div")
|
|
1158
|
-
let { size, family } = font
|
|
1159
|
-
|
|
1160
|
-
element.className = `text-[${size}px] font-['${family}'] absolute float-left whitespace-nowrap invisible`
|
|
1161
|
-
element.innerHTML = text
|
|
1162
|
-
|
|
1163
|
-
document.body.appendChild(element)
|
|
1164
|
-
|
|
1165
|
-
let rect = element.getBoundingClientRect()
|
|
1166
|
-
return rect.width
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
892
|
return (
|
|
1170
893
|
<div className="align-center box-border flex h-min w-[400px] flex-col items-center justify-center rounded shadow-md">
|
|
1171
894
|
<div className={clsx("flex w-full flex-row justify-start p-2")}>
|
|
@@ -1183,6 +906,17 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
1183
906
|
})}
|
|
1184
907
|
</div>
|
|
1185
908
|
<div className="h-[1px] w-full bg-slate-600"> </div>
|
|
909
|
+
{/* <div className="selection-ignore rtl h-[150px] w-full resize-none overflow-auto overflow-x-hidden border-none font-['Arial'] text-[16px] outline-none">
|
|
910
|
+
<div className="line" data-line-index="0">
|
|
911
|
+
<span className="" data-child-index="0">
|
|
912
|
+
asd
|
|
913
|
+
</span>
|
|
914
|
+
</div>
|
|
915
|
+
<div className="line" data-line-index="1">
|
|
916
|
+
<br />
|
|
917
|
+
</div>
|
|
918
|
+
</div>
|
|
919
|
+
*/}
|
|
1186
920
|
<div className="selection-ignore box-border w-full p-2">
|
|
1187
921
|
<div
|
|
1188
922
|
ref={field}
|
|
@@ -1194,27 +928,152 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
1194
928
|
onPaste={(event) => {
|
|
1195
929
|
// pastes all copied text from the content editable as plain text
|
|
1196
930
|
event.preventDefault()
|
|
1197
|
-
|
|
931
|
+
let data = event.clipboardData.getData("text/plain")
|
|
932
|
+
data = data.replaceAll("\n", "")
|
|
933
|
+
data = data.replaceAll("\r", "")
|
|
934
|
+
|
|
1198
935
|
document.execCommand("insertHTML", false, data)
|
|
1199
936
|
|
|
1200
|
-
|
|
937
|
+
// selection.anchorOffset <= 1 &&
|
|
938
|
+
// selection.focusOffset <= 1
|
|
939
|
+
// ) {
|
|
940
|
+
// let lineIndex = getChildIndex(anchorNode.parentNode.parentNode)
|
|
941
|
+
|
|
942
|
+
// let _content = content
|
|
943
|
+
// if (content.startsWith(lineBreakIdentifier)) {
|
|
944
|
+
// _content = _content.slice(1)
|
|
945
|
+
// }
|
|
946
|
+
|
|
947
|
+
// let originalLine: any =
|
|
948
|
+
// _content.split(lineBreakIdentifier)[lineIndex]
|
|
949
|
+
|
|
950
|
+
// console.log(_content.split(lineBreakIdentifier))
|
|
951
|
+
|
|
952
|
+
// originalLine = originalLine.split("")
|
|
953
|
+
// originalLine.splice(0, 0, eventData)
|
|
954
|
+
// originalLine = originalLine.join("")
|
|
955
|
+
|
|
956
|
+
// let lines = _content.split(lineBreakIdentifier)
|
|
957
|
+
|
|
958
|
+
// lines[lineIndex] = originalLine
|
|
959
|
+
|
|
960
|
+
// textContent = lines.join(lineBreakIdentifier)
|
|
961
|
+
|
|
962
|
+
// if (content.startsWith(lineBreakIdentifier)) {
|
|
963
|
+
// textContent = lineBreakIdentifier + textContent
|
|
964
|
+
// }
|
|
965
|
+
|
|
966
|
+
// start += 1
|
|
967
|
+
// end += 1
|
|
968
|
+
// }
|
|
1201
969
|
|
|
1202
970
|
setText({
|
|
1203
971
|
..._text.current,
|
|
1204
|
-
|
|
972
|
+
events: {
|
|
973
|
+
..._text.current.events,
|
|
974
|
+
paste: { is: true, length: data.length },
|
|
975
|
+
},
|
|
1205
976
|
})
|
|
1206
977
|
}}
|
|
1207
978
|
onInput={(event: any) => {
|
|
1208
|
-
|
|
979
|
+
// console.log(event)
|
|
980
|
+
// console.log(event.target.textContent)
|
|
981
|
+
// console.log(event.target.textContent.split(""))
|
|
982
|
+
|
|
983
|
+
let [start, end] = getFieldSelection()
|
|
984
|
+
let textContent = event.target.textContent
|
|
985
|
+
let content = _text.current.content
|
|
986
|
+
// Try creating twice the lines if the index is at 0,0
|
|
987
|
+
// if (!textContent.startsWith(lineBreakIdentifier)) {
|
|
988
|
+
// textContent = lineBreakIdentifier + textContent
|
|
989
|
+
// }
|
|
990
|
+
|
|
991
|
+
let eventType = event.nativeEvent.inputType
|
|
992
|
+
|
|
993
|
+
if (eventType == "insertParagraph") {
|
|
994
|
+
console.log(`Inserted new line at [${start}, ${end}]`)
|
|
995
|
+
|
|
996
|
+
let split = textContent.split("")
|
|
997
|
+
|
|
998
|
+
split.splice(start, 0, lineBreakIdentifier)
|
|
999
|
+
if (start == 0 && !content.startsWith(lineBreakIdentifier)) {
|
|
1000
|
+
split.splice(start, 0, lineBreakIdentifier)
|
|
1001
|
+
start += 1
|
|
1002
|
+
end += 1
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
textContent = split.join("")
|
|
1006
|
+
|
|
1007
|
+
// Increase selection by one to accomodate for the new line
|
|
1008
|
+
start += 1
|
|
1009
|
+
end += 1
|
|
1010
|
+
} else {
|
|
1011
|
+
let selection = window.getSelection()
|
|
1012
|
+
let { anchorNode, focusNode } = selection
|
|
1013
|
+
|
|
1014
|
+
let eventData = event.nativeEvent.data
|
|
1015
|
+
|
|
1016
|
+
// Handle typing behind lines
|
|
1017
|
+
if (
|
|
1018
|
+
anchorNode.parentNode == focusNode.parentNode &&
|
|
1019
|
+
anchorNode.parentNode.nodeName == "SPAN" &&
|
|
1020
|
+
selection.anchorOffset <= 1 &&
|
|
1021
|
+
selection.focusOffset <= 1 &&
|
|
1022
|
+
eventType == "insertText"
|
|
1023
|
+
) {
|
|
1024
|
+
let lineIndex = getChildIndex(anchorNode.parentNode.parentNode)
|
|
1025
|
+
|
|
1026
|
+
let _content = content
|
|
1027
|
+
if (content.startsWith(lineBreakIdentifier)) {
|
|
1028
|
+
_content = _content.slice(1)
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
let originalLine: any =
|
|
1032
|
+
_content.split(lineBreakIdentifier)[lineIndex]
|
|
1033
|
+
|
|
1034
|
+
console.log(_content.split(lineBreakIdentifier))
|
|
1035
|
+
|
|
1036
|
+
originalLine = originalLine.split("")
|
|
1037
|
+
originalLine.splice(0, 0, eventData)
|
|
1038
|
+
originalLine = originalLine.join("")
|
|
1039
|
+
|
|
1040
|
+
let lines = _content.split(lineBreakIdentifier)
|
|
1041
|
+
|
|
1042
|
+
lines[lineIndex] = originalLine
|
|
1043
|
+
|
|
1044
|
+
textContent = lines.join(lineBreakIdentifier)
|
|
1045
|
+
|
|
1046
|
+
if (content.startsWith(lineBreakIdentifier)) {
|
|
1047
|
+
textContent = lineBreakIdentifier + textContent
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
start += 1
|
|
1051
|
+
end += 1
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// if (
|
|
1056
|
+
// eventData != null &&
|
|
1057
|
+
// start - eventData.length == 0 &&
|
|
1058
|
+
// end - eventData.length == 0 &&
|
|
1059
|
+
// content.startsWith(lineBreakIdentifier)
|
|
1060
|
+
// ) {
|
|
1061
|
+
// console.log("STOP")
|
|
1062
|
+
// }
|
|
1063
|
+
|
|
1064
|
+
// console.log(event.nativeEvent.inputType)
|
|
1065
|
+
|
|
1066
|
+
onChange(textContent, start, end)
|
|
1209
1067
|
}}
|
|
1210
|
-
onCopy={(
|
|
1068
|
+
onCopy={() => {
|
|
1211
1069
|
let [start, end] = getFieldSelection()
|
|
1212
1070
|
|
|
1213
|
-
let
|
|
1071
|
+
let stylings = _text.current.stylings.slice()
|
|
1072
|
+
stylings = getStylingsInRange(stylings, start, end)
|
|
1214
1073
|
|
|
1215
1074
|
setText({
|
|
1216
1075
|
...text,
|
|
1217
|
-
|
|
1076
|
+
clipboard: stylings,
|
|
1218
1077
|
})
|
|
1219
1078
|
}}
|
|
1220
1079
|
onDrop={(event) => {
|
|
@@ -1222,16 +1081,11 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
1222
1081
|
|
|
1223
1082
|
if (text.trim() == "") return
|
|
1224
1083
|
|
|
1225
|
-
// console.log(getSelectionPrecedingSum("anchorNode"))
|
|
1226
|
-
// console.log(getSelectionPrecedingSum("focusNode"))
|
|
1227
|
-
// console.log(window.getSelection())
|
|
1228
|
-
|
|
1229
1084
|
setText({
|
|
1230
1085
|
..._text.current,
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
text: text,
|
|
1234
|
-
previous: getFieldSelection(),
|
|
1086
|
+
events: {
|
|
1087
|
+
..._text.current.events,
|
|
1088
|
+
drop: { is: true, text: text, drag: getFieldSelection() },
|
|
1235
1089
|
},
|
|
1236
1090
|
})
|
|
1237
1091
|
}}
|