@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.
@@ -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
- lastCopy: [],
51
- pasted: { status: false, length: 0 },
52
- dropped: { status: false, text: "", previous: [0, 0] },
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 parent: any = selection[name].parentNode
82
- let special = 0
121
+ let node = selection[name]
122
+ let parent: any = node.parentNode
123
+ let sum = 0
124
+ // let special = 0
83
125
 
84
- // Special case for empty text
85
- // if (parent == field.current) {
86
- // console.log("hi")
87
- // }
126
+ const isNodeLine =
127
+ node.nodeName == "DIV" && Array.from(node.classList).includes("line")
88
128
 
89
- // Special case for dropping text near or inside styled text
90
- if (!Array.from(parent.parentNode.classList).includes("selection-ignore")) {
91
- parent = parent.parentNode
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
- let index = getChildIndex(selection[name].parentNode)
94
- special = Array.from(parent.childNodes)
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
- let index = parent == field.current ? nodes.length : getChildIndex(parent)
137
+ // Get parent line index
138
+ let lineIndex = getChildIndex(parent.parentNode)
101
139
 
102
- let sum =
103
- nodes
104
- .slice(0, index)
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
- const getFieldSelection = () => {
112
- if (document.activeElement != field.current) return [0, 0]
150
+ sum += getRelativePrecedingSum(parent, spanIndex)
151
+ }
113
152
 
114
- let selection = window.getSelection()
115
- // let nodes = Array.from(field.current.childNodes)
153
+ // Get line element index
154
+ let lineIndex = getChildIndex(parent)
116
155
 
117
- // console.log(nodes)
156
+ // Get relative sum within entire field
157
+ sum += getLinePrecedingSum(lineIndex)
158
+ }
118
159
 
119
- // nodes = nodes.filter(
120
- // (item: any) => !["#text", "BR"].includes(item.nodeName)
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
- // console.log(selection.anchorNode)
124
- // console.log(selection.focusNode)
125
- // console.log(selection.anchorNode.parentNode)
126
- // console.log(selection.focusNode.parentNode)
127
- // console.log(selection.anchorNode.parentNode.parentNode)
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
- // let startParent: any = selection.anchorNode.parentNode
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
- // let startNodeIndex =
135
- // startParent == field.current
136
- // ? nodes.length
137
- // : // : parseInt(startParent.dataset.childIndex)
138
- // getChildIndex(startParent)
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 startPrecedingSum = nodes
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 startPrecedingSum = getSelectionPrecedingSum("anchorNode")
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
- // let endParent: any = selection.focusNode.parentNode
148
- // let endNodeIndex =
149
- // endParent == field.current
150
- // ? nodes.length
151
- // : // : parseInt(endParent.dataset.childIndex)
152
- // getChildIndex(endParent)
193
+ return sum
194
+ }
153
195
 
154
- // let endPrecedingSum = nodes
155
- // .slice(0, endNodeIndex)
156
- // .map((span: any) => span.textContent.length)
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 = (index, startOffset = 0, finishOffset = 0) => {
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 = text.stylings.filter(
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
- // Get stylings after an index
414
- const getSucceedStylings = (index) => {
415
- // Find all stylings after the index
416
- let matches = text.stylings.filter(({ start, finish }) => start >= index)
417
-
418
- return matches
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
- // Apply changes
551
- changes.map(({ index, start, finish }) => {
552
- stylings[index] = {
553
- ...stylings[index],
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
- // Handle complete encapsulation by styling
560
- // stylings = stylings.map((styling) => {
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
- // console.log(succeeding)
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
- // If the start and finish of any styling is greater than the new length, remove it
458
+ // Get second split
459
+ let second = {
460
+ ...styling,
461
+ start: index + offset,
462
+ finish: styling.finish + offset,
463
+ }
575
464
 
576
- return stylings
465
+ return [first, second]
577
466
  }
578
467
 
579
- const handleAddition = (length, start, end) => {
580
- start -= length + 1
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
- let changes = []
471
+ // Required operations:
472
+ // Offset succeeding stylings
473
+ // Split intersecting stylings
474
+ // Styling continuation at end (only for normal addition)
586
475
 
587
- let succeeding = handleScenario(
588
- stylings,
589
- (_start, _finish) => _start > start && _start + 1 >= end
590
- ).map((styling) => {
591
- return {
592
- ...styling,
593
- start: styling.start + length,
594
- finish: styling.finish + length,
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
- // console.log(succeeding)
606
-
607
- let preceding = handleScenario(
608
- stylings,
609
- // (_start, _finish) => start < _finish && start >= _start + 2 && length == 1 // This is to not style pasted text
610
- (_start, _finish) => start + 1 == _finish && length == 1 // This is to not style pasted text
611
- ).map((styling) => {
612
- return {
613
- ...styling,
614
- start: styling.start,
615
- finish: styling.finish + length,
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
- let encapsulated = handleScenario(
621
- stylings,
622
- (_start, _finish) => start >= _start && end + 1 < _finish
623
- ).map((styling) => {
624
- return {
625
- ...styling,
626
- finish: styling.finish + length,
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
- changes.push(encapsulated)
515
+ return stylings.flat()
516
+ }
631
517
 
632
- // console.log(length)
518
+ const deletionOf = (stylings, startIndex, endIndex) => {
519
+ // console.log(`Removing text from ${startIndex} to ${endIndex}`)
633
520
 
634
- // console.log(`Encapsulated: ${encapsulated.length}`)
521
+ let length = Math.abs(endIndex - startIndex)
635
522
 
636
- // console.log(`Succeeding: ${succeeding.length}`)
637
- // console.log(`Preceding: ${preceding.length}`)
638
- // console.log(preceding)
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
- changes.flat().map(({ index, start, finish }) => {
641
- stylings[index] = {
642
- ...stylings[index],
643
- start: start,
644
- finish: finish,
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
- return stylings
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
- // Effective removal
693
- return {
694
- ...styling,
695
- start: styling.start,
696
- finish: styling.start,
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
- // Get intersecting stylings from the right
716
- let right = handleScenario(
717
- stylings,
718
- (_start, _end) =>
719
- _end > end - difference && _start >= start && _start < end - difference
720
- ).map((styling) => {
721
- return {
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
- changes.push(right)
728
-
729
- // console.log(`Encapsulating: ${encapsulating.length}`)
730
- // console.log(`Encapsulated: ${encapsulated.length}`)
731
- // console.log(`Left Intersecting: ${left.length}`)
732
- // console.log(`Right Intersecting: ${right.length}`)
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
- stylings[index] = {
747
- ...stylings[index],
748
- start: start,
749
- finish: finish,
750
- }
575
+ return styling
751
576
  })
752
577
 
753
- // console.log(stylings)
754
-
755
- return stylings
578
+ return stylings.flat().filter((styling) => styling)
756
579
  }
757
580
 
758
- const handleDrop = (value) => {
759
- let [dropStart, dropEnd] = getFieldSelection()
760
-
761
- let stylings = _text.current.stylings.slice()
762
- let dropped = _text.current.dropped.text
763
-
764
- let changes = []
765
-
766
- let [dragStart, dragEnd] = _text.current.dropped.previous
767
-
768
- console.log(`Drag: ${[dragStart, dragEnd]}`)
769
- console.log(`Drop: ${[dropStart, dropEnd]}`)
770
- console.log(stylings)
771
-
772
- // negative - right, positive - left
773
- // let direction = dragStart - dropStart < 0 ? -1 : 1
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
- // Possible refactorization:
776
- // Since the drop invokes the onInput event twice, the first is the removal of dragged text, and the second is the addition of dropped text
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
- // offset: length * direction
602
+ const processPaste = (stylings, difference, selectionStart, selectionEnd) => {
603
+ let pasteLength = _text.current.events.paste.length
780
604
 
781
- // To be offset in right conditions:
782
- // (_start >= dg.end && _end > dg.end)
783
- // (_end <= dp.start && _start < dp.start)
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
- // To be offset in left conditions:
786
- // (_end <= dg.start && _start < dg.start)
787
- // (_start >= dp.end && _end > dp.end)
616
+ // Get addition start index
617
+ let additionStart = selectionStart - pasteLength
788
618
 
789
- // Get all intersecting stylings
790
- let dragStylings = stylings.filter(
791
- ({ start, finish }) =>
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
- // Offset stylings between drag and drop locations
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
- // Handle complete encapsulation over styling
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
- // Handle drag range being encapsulated by styling
823
- stylings
824
- .filter(
825
- ({ start, finish }) =>
826
- (dragStart > start && dragEnd <= finish) ||
827
- (dragStart >= start && dragEnd < finish)
828
- )
829
- .map((styling) => {
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
- resumptions.map((styling) => {
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
- changes.push({
861
- original: styling,
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
- stylings[index] = changed
881
- })
645
+ const processDrop = (stylings, difference, dropStart, dropEnd) => {
646
+ console.log(`Dropped at: ${[dropStart, dropEnd]}`)
882
647
 
883
- changes = []
648
+ let dropLength = _text.current.events.drop.text.length
884
649
 
885
- // Positive offset succeeding stylings
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
- // Splice intersecting stylings
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
- changes.push({
913
- original: null,
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
- // Apply drop changes
923
- changes.map(({ original, changed }) => {
924
- if (original == null) {
925
- stylings.push(changed)
926
- return
927
- }
657
+ // Removal range
658
+ let removalStart = dragStart
659
+ let removalEnd = dragEnd
928
660
 
929
- let index = stylings.findIndex((styling) =>
930
- compareStylings(original, styling)
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
- stylings[index] = changed
934
- })
666
+ // Compute addition first
667
+ stylings = additionTo(stylings, additionStart, dropLength, false)
935
668
 
936
- // Remove empty stylings
937
- stylings = stylings.filter((styling) => styling.start != styling.finish)
669
+ // Compute removal second
670
+ stylings = deletionOf(stylings, removalStart, removalEnd)
671
+ }
938
672
 
939
- // Clamp start and end values, negative offset by dragStart, and positive offset by dropStart
940
- dragStylings = dragStylings.map((styling) => {
941
- return {
942
- ...styling,
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
- // Push to current stylings
949
- stylings = stylings.concat(dragStylings)
678
+ // Compute removal first
679
+ stylings = deletionOf(stylings, removalStart, removalEnd)
950
680
 
951
- setText({
952
- ..._text.current,
953
- content: value,
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
- const onChange = (value) => {
962
- setTimeout(function () {
963
- if (_text.current.dropped.status) {
964
- // Drops from text in the content editable invoke the onChange function twice
965
- if (value.length == _text.current.content.length) {
966
- handleDrop(value)
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
- return
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
- let stylings = _text.current.stylings.slice()
696
+ return stylings
697
+ }
978
698
 
979
- let succeeding = getSucceedStylings(start)
980
- let changes = []
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
- for (let succeed of succeeding) {
983
- let index = getStylingIndex(succeed)
984
- let styling = stylings[index]
707
+ console.log(`Changed at: ${[selectionStart, selectionEnd]}`)
985
708
 
986
- changes.push([
987
- index,
988
- styling.start + difference,
989
- styling.finish + difference,
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
- if (difference < 0) {
994
- stylings = handleDeletion(
995
- Math.abs(difference),
714
+ // Paste event
715
+ if (_text.current.events.paste.is) {
716
+ stylings = processPaste(
717
+ stylings,
718
+ difference,
996
719
  selectionStart,
997
- selectionEnd + Math.abs(difference)
720
+ selectionEnd
998
721
  )
999
- }
1000
-
1001
- if (difference > 0) {
1002
- stylings = handleAddition(
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 - _text.current.pasted.length,
1023
- selectionStart
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
- pasted: { status: false, length: 0 },
1053
- dropped: { status: false, text: "", previous: [0, 0] },
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
- // Get all styling indices
1062
- let indices = _text.current.stylings
1063
- .map(({ start, finish }) => [start, finish])
1064
- .flat()
756
+ let result = []
1065
757
 
1066
- // Sort ascendingly
1067
- indices = indices.sort((a, b) => a - b)
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
- // Remove duplicates
1070
- indices = indices.filter(
1071
- (element, index) => indices.indexOf(element) == index
1072
- )
767
+ // Add end point
768
+ lineIndices.unshift(0)
769
+ lineIndices.push(content.length)
1073
770
 
1074
- // Add first index if not present
1075
- if (indices[0] != 0) indices.unshift(0)
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
- // Add last index if not present
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
- let result = []
778
+ // console.log(
779
+ // `Line: ${lineStart} -> ${lineEnd}, with content: ${lineContent}`
780
+ // )
1082
781
 
1083
- for (let i = 0; i < indices.length - 1; i++) {
1084
- result.push([indices[i], content.substring(indices[i], indices[i + 1])])
1085
- }
782
+ // Get all stylings within line
783
+ let lineStylings = getStylingsInRange(stylings, lineStart, lineEnd)
1086
784
 
1087
- return result
1088
- }
785
+ // Collect all spans relative to stylings within line
786
+ let spans = []
1089
787
 
1090
- const serializeStyleInRange = (start, end) => {
1091
- // Get all stylings intersecting
1092
- let stylings = []
1093
- for (let i = start; i < end; i++) {
1094
- stylings.push(getIntersectStylings(i))
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
- // Remove duplicates
1098
- stylings = stylings.flat().filter((item) => item)
1099
- stylings = stylings.filter(
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
- // Clamp start and finish values and offset by start index
1110
- stylings = stylings.map((styling) => {
1111
- return {
1112
- ...styling,
1113
- start: getMaximum([styling.start, start]) - start,
1114
- finish: getMinimum([styling.finish, end]) - start,
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
- return stylings
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((_data, index) => {
1125
- let [start, data] = _data
1126
-
1127
- // Get stylings encompassing an index within it's range
1128
- let stylings = getIntersectStylings(start)
1129
- // console.log(data)
1130
- // console.log(stylings)
1131
- return `<span class="${stylings
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">&nbsp;</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
- const data = event.clipboardData.getData("text/plain")
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
- console.log(data)
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
- pasted: { status: true, length: data.length },
972
+ events: {
973
+ ..._text.current.events,
974
+ paste: { is: true, length: data.length },
975
+ },
1205
976
  })
1206
977
  }}
1207
978
  onInput={(event: any) => {
1208
- onChange(event.target.textContent)
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={(event) => {
1068
+ onCopy={() => {
1211
1069
  let [start, end] = getFieldSelection()
1212
1070
 
1213
- let data = serializeStyleInRange(start, end)
1071
+ let stylings = _text.current.stylings.slice()
1072
+ stylings = getStylingsInRange(stylings, start, end)
1214
1073
 
1215
1074
  setText({
1216
1075
  ...text,
1217
- lastCopy: data,
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
- dropped: {
1232
- status: true,
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
  }}