@sikka/hawa 0.0.232 → 0.0.234

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.
@@ -17,8 +17,6 @@ type ComponentTypes = {
17
17
  foo?: string
18
18
  }
19
19
 
20
- // TODO: Handle copy pasting by assigning to local storage ?
21
-
22
20
  const styleClasses = {
23
21
  bold: "font-bold",
24
22
  italic: "italic",
@@ -26,97 +24,121 @@ const styleClasses = {
26
24
  strike: "line-through",
27
25
  }
28
26
 
27
+ // FIXME: Deleting a styled part of text while selecting characters on the borders or more
28
+ // FIXME: Deleting more than one character of a styled part of text
29
+ // FIXME: Pasting styled text doesn't offset succeeding stylings
30
+ // FIXME: Pasting styled text inside other stylings
31
+ // FIXME: Typing characters behind and after stylings
32
+
33
+ // FIXME: Pasting behind the second character in styled text
34
+ // FIXME: Pasting over styled text with resumptions and/or highlighted text exceeding pasted text length
35
+
29
36
  export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
30
37
  props
31
38
  ) => {
32
- const [text, setText] = useState({
39
+ const [text, _setText] = useState({
33
40
  content: "",
34
41
  stylings: [], // A styling is an object with 2 indices specifying a substring of text, and the applied effect
35
42
  revert: [0, 0],
43
+ lastCopy: [],
44
+ pasted: { status: false, length: 0 },
36
45
  })
37
46
 
38
47
  const field = useRef(null)
48
+ const _text = useRef(text)
49
+ const setText = (data) => {
50
+ _text.current = data
51
+ _setText(data)
52
+ }
39
53
 
54
+ const getChildIndex = (child) => {
55
+ for (var i = 0; (child = child.previousSibling); i++);
56
+ return i
57
+ }
58
+
59
+ // Full reversion achieved !
40
60
  const getFieldSelection = () => {
41
61
  if (document.activeElement != field.current) return [0, 0]
42
62
 
43
63
  let selection = window.getSelection()
44
- let start
45
- let end
46
-
47
- console.log(selection)
48
- console.log(document.activeElement)
49
- console.log(`Range count when fetching selection: ${selection.rangeCount}`)
50
- if (selection.rangeCount) {
51
- let range = selection.getRangeAt(0)
52
- start = range.startOffset
53
- end = range.endOffset
54
- }
55
-
56
- return [start, end]
57
- }
64
+ let nodes = Array.from(field.current.childNodes)
58
65
 
59
- useEffect(() => {
60
- // return
61
- let [start, end] = text.revert
66
+ nodes = nodes.filter(
67
+ (item: any) => !["#text", "BR"].includes(item.nodeName)
68
+ )
62
69
 
63
- if (start == 0 && end == 0) return
70
+ let startParent: any = selection.anchorNode.parentNode
71
+
72
+ let startNodeIndex =
73
+ startParent == field.current
74
+ ? nodes.length
75
+ : // : parseInt(startParent.dataset.childIndex)
76
+ getChildIndex(startParent)
77
+
78
+ let startPrecedingSum = nodes
79
+ .slice(0, startNodeIndex)
80
+ .map((span: any) => span.textContent.length)
81
+ .reduce((a, b) => a + b, 0)
82
+
83
+ let endParent: any = selection.focusNode.parentNode
84
+ let endNodeIndex =
85
+ endParent == field.current
86
+ ? nodes.length
87
+ : // : parseInt(endParent.dataset.childIndex)
88
+ getChildIndex(endParent)
89
+
90
+ let endPrecedingSum = nodes
91
+ .slice(0, endNodeIndex)
92
+ .map((span: any) => span.textContent.length)
93
+ .reduce((a, b) => a + b, 0)
94
+
95
+ let result = [
96
+ startPrecedingSum + selection.anchorOffset,
97
+ endPrecedingSum + selection.focusOffset,
98
+ ]
64
99
 
65
- let oldStart = start
66
- let oldEnd = end
100
+ // Sort to make the minimum selection the start selection
101
+ return result.sort((a, b) => a - b)
102
+ }
67
103
 
68
- let [newStart, newEnd] = getFieldSelection()
104
+ useEffect(() => {
105
+ setTimeout(function () {
106
+ let [start, end] = _text.current.revert
69
107
 
70
- // TODO: To get the child node based on the index, get lengths of each child node
71
- // add to each child node the sum of all previous child nodes
72
- // Create ranges for 0 - n, n - n1, n - n2, ... where nx is the length of a child node
73
- // Add 1 to the index
74
- // Iterate through each range, and check if the index is greater than the start of the range, and less than or equal the end of the range
75
- let startNode = null
76
- let endNode = null
108
+ if (start == 0 && end == 0) return
77
109
 
78
- let startNodeIndex = 0
79
- let endNodeIndex = 0
110
+ let startNode = null
111
+ let endNode = null
80
112
 
81
- let total = 0
82
- let nodes = Array.from(field.current.childNodes)
113
+ let total = 0
114
+ let nodes = Array.from(field.current.childNodes)
83
115
 
84
- console.log(
85
- `Current nodes: ${nodes.map((e: any) => e.textContent.length).join(", ")}`
86
- )
116
+ for (let i = 0; i < nodes.length; i++) {
117
+ let node: any = nodes[i]
118
+ let sum = node.textContent.length + total
87
119
 
88
- for (let i = 0; i < nodes.length; i++) {
89
- let node: any = nodes[i]
90
- let sum = node.textContent.length + total
120
+ if (startNode == null && start >= total && start <= sum) {
121
+ startNode = nodes[i]
122
+ start -= total
123
+ }
91
124
 
92
- if (startNode == null && start > total && start <= sum) {
93
- startNode = nodes[i]
94
- startNodeIndex = i
95
- start -= total
96
- }
125
+ if (endNode == null && end > total && end <= sum) {
126
+ endNode = nodes[i]
127
+ end -= total
128
+ }
97
129
 
98
- if (endNode == null && end > total && end <= sum) {
99
- endNode = nodes[i]
100
- endNodeIndex = i
101
- end -= total
130
+ total += node.textContent.length
102
131
  }
103
132
 
104
- total += node.textContent.length
105
- }
106
-
107
- console.log(
108
- `Reverting (${newStart} -> ${newEnd}) to (${oldStart} - ${start} - [node ${startNodeIndex}] -> ${oldEnd} - ${end} - [node ${endNodeIndex}])`
109
- )
110
-
111
- var range = document.createRange()
112
- var sel = window.getSelection()
133
+ var range = document.createRange()
134
+ var sel = window.getSelection()
113
135
 
114
- range.setStart(startNode.firstChild, start)
115
- range.setEnd(endNode.firstChild, end)
116
- // range.collapse(true)
136
+ range.setStart(startNode.firstChild, start)
137
+ range.setEnd(endNode.firstChild, end)
117
138
 
118
- sel.removeAllRanges()
119
- sel.addRange(range)
139
+ sel.removeAllRanges()
140
+ sel.addRange(range)
141
+ }, 1)
120
142
  }, [text.revert])
121
143
 
122
144
  // utility functions
@@ -239,7 +261,7 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
239
261
  }
240
262
 
241
263
  const perform = (id) => {
242
- let stylings = text.stylings.slice()
264
+ let stylings = _text.current.stylings.slice()
243
265
  let [selectionStart, selectionEnd] = getFieldSelection()
244
266
 
245
267
  if (selectionStart == selectionEnd) return
@@ -280,6 +302,36 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
280
302
  })
281
303
  }
282
304
 
305
+ const getRangeIntersectStylings = (start, end, startOffset, finishOffset) => {
306
+ // Get all stylings intersecting
307
+ let stylings = []
308
+ for (let i = start; i < end; i++) {
309
+ stylings.push(getIntersectStylings(i, startOffset, finishOffset))
310
+ }
311
+
312
+ // Remove duplicates
313
+ stylings = stylings.flat().filter((item) => item)
314
+ stylings = stylings.filter(
315
+ (item, index) =>
316
+ stylings.findIndex(
317
+ (_item) =>
318
+ _item.start == item.start &&
319
+ _item.finish == item.finish &&
320
+ _item.type == item.type
321
+ ) == index
322
+ )
323
+
324
+ return stylings
325
+ }
326
+
327
+ const compareStylings = (styling1, styling2) => {
328
+ return (
329
+ styling1.type == styling2.type &&
330
+ styling1.start == styling2.start &&
331
+ styling1.finish == styling2.finish
332
+ )
333
+ }
334
+
283
335
  // Get stylings encompassing an index within it's range
284
336
  const getIntersectStylings = (index, startOffset = 0, finishOffset = 0) => {
285
337
  // Find all stylings with encompassing range
@@ -308,56 +360,288 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
308
360
  )
309
361
  }
310
362
 
311
- const onChange = (value) => {
312
- let difference = value.length - text.content.length
363
+ const handleScenario = (stylings, predicate) => {
364
+ let matches = stylings.filter(({ start: _start, finish: _finish }) =>
365
+ predicate(_start, _finish)
366
+ )
313
367
 
314
- let selection = field.current.selectionStart - difference
368
+ let indices = []
315
369
 
316
- let stylings = text.stylings.slice()
317
- let succeeding = getSucceedStylings(selection)
370
+ stylings.map((styling, index) => {
371
+ let result = matches.find((item) => compareStylings(styling, item))
372
+ if (!result) return
373
+ indices.push({
374
+ index: index,
375
+ start: styling.start,
376
+ finish: styling.finish,
377
+ })
378
+ })
379
+
380
+ return indices
381
+ }
382
+
383
+ const handleDeletion = (length, start, end) => {
384
+ let stylings = _text.current.stylings.slice()
318
385
  let changes = []
319
386
 
320
- for (let succeed of succeeding) {
321
- let index = getStylingIndex(succeed)
322
- let styling = stylings[index]
387
+ console.log(`Deletion`)
388
+ console.log([start, end])
389
+
390
+ // TODO: Refactor
391
+
392
+ // Offset all succeeding stylings by length
393
+ // changes.push(
394
+ // handleScenario(
395
+ // stylings,
396
+ // (_start, _finish) => start < _start && end <= start
397
+ // ).map((styling) => {
398
+ // return {
399
+ // ...styling,
400
+ // start: styling.start - length,
401
+ // finish: styling.finish - length,
402
+ // }
403
+ // })
404
+ // )
405
+
406
+ let succeeding = stylings.filter(
407
+ ({ start: _start }) => start < _start && end <= _start
408
+ )
409
+ stylings.map((styling, index) => {
410
+ let result = succeeding.find((item) => compareStylings(styling, item))
411
+ if (!result) return
412
+
413
+ changes.push({
414
+ index: index,
415
+ start: styling.start - length,
416
+ finish: styling.finish - length,
417
+ })
418
+ })
323
419
 
324
- changes.push([
325
- index,
326
- styling.start + difference,
327
- styling.finish + difference,
328
- ])
329
- }
420
+ // Handle complete encapsulation over styling
421
+ let encapsulating = stylings.filter(
422
+ ({ start: _start, finish: _finish }) => start <= _start && end >= _finish
423
+ )
424
+ stylings.map((styling, index) => {
425
+ let result = encapsulating.find((item) => compareStylings(styling, item))
426
+ if (!result) return
427
+
428
+ // This will effectively remove the styling, since collapsed ranges are automatically removed
429
+ changes.push({
430
+ index: index,
431
+ start: styling.start,
432
+ finish: styling.start,
433
+ })
434
+ })
330
435
 
331
- let intersecting = getIntersectStylings(selection, 1, 1)
436
+ // Handle deletion being encapsulated by styling
437
+ let encapsulated = stylings.filter(
438
+ ({ start: _start, finish: _finish }) =>
439
+ (start > _start && end <= _finish) || (start >= _start && end < _finish)
440
+ )
441
+ stylings.map((styling, index) => {
442
+ let result = encapsulated.find((item) => compareStylings(styling, item))
443
+ if (!result) return
444
+
445
+ changes.push({
446
+ index: index,
447
+ start: styling.start,
448
+ finish: styling.finish - length,
449
+ })
450
+ })
332
451
 
333
- for (let intersect of intersecting) {
334
- let index = getStylingIndex(intersect)
335
- let styling = stylings[index]
452
+ // Handle deletion being encapsulated by styling with left resumption
453
+ let leftResumption = stylings.filter(
454
+ ({ start: _start, finish: _finish }) =>
455
+ end < _finish && end > _start && start < _start
456
+ )
457
+ stylings.map((styling, index) => {
458
+ let result = leftResumption.find((item) => compareStylings(styling, item))
459
+ if (!result) return
460
+
461
+ changes.push({
462
+ index: index,
463
+ start: getMaximum([end, styling.start]) - length,
464
+ finish: styling.finish - length,
465
+ })
466
+ })
336
467
 
337
- changes.push([index, styling.start, styling.finish + difference])
338
- }
468
+ let rightResumption = stylings.filter(
469
+ ({ start: _start, finish: _finish }) =>
470
+ start > _start && start < _finish && end > _finish
471
+ )
472
+ stylings.map((styling, index) => {
473
+ let result = rightResumption.find((item) =>
474
+ compareStylings(styling, item)
475
+ )
476
+ if (!result) return
477
+
478
+ changes.push({
479
+ index: index,
480
+ start: styling.start,
481
+ finish: getMinimum([start, styling.finish]),
482
+ })
483
+ })
339
484
 
340
- let spliced = []
341
- for (let [index, start, finish] of changes) {
485
+ // Apply changes
486
+ changes.map(({ index, start, finish }) => {
342
487
  stylings[index] = {
343
488
  ...stylings[index],
344
489
  start: start,
345
490
  finish: finish,
346
491
  }
492
+ })
347
493
 
348
- if (start >= finish) spliced.push(index)
349
- }
494
+ // Handle complete encapsulation by styling
495
+ // stylings = stylings.map((styling) => {
496
+ // if (start >= styling.start && end <= styling.finish) {
497
+ // console.log("hi")
498
+ // }
499
+
500
+ // return styling
501
+ // })
350
502
 
351
- stylings = stylings.filter((_, index) => !spliced.includes(index))
503
+ // If deletion is surrounded by styling, decrease finish by length (this might not include first character, but try first)
504
+ // 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
505
+ // If deletion is intersecting wit ha styling after it, get minimum between deletion start and styling finish, and set the finish to it
352
506
 
353
- setText({ ...text, content: value, stylings: stylings })
507
+ // console.log(succeeding)
508
+
509
+ // If the start and finish of any styling is greater than the new length, remove it
510
+
511
+ return stylings
512
+ }
513
+
514
+ const handleAddition = (length, start, end) => {
515
+ start -= length
516
+ end -= length
517
+
518
+ let stylings = _text.current.stylings.slice()
519
+
520
+ let changes = []
521
+
522
+ let succeeding = handleScenario(
523
+ stylings,
524
+ (_start, _finish) => _start + 1 >= start && _start + 1 >= end
525
+ ).map((styling) => {
526
+ return {
527
+ ...styling,
528
+ start: styling.start + length,
529
+ finish: styling.finish + length,
530
+ }
531
+ })
532
+ changes.push(succeeding)
533
+
534
+ console.log(`Addition (${length})`)
535
+ console.log([start, end])
536
+ console.log(`Succeeding:`)
537
+ console.log(succeeding)
538
+
539
+ let preceding = handleScenario(
540
+ stylings,
541
+ (_start, _finish) => start <= _finish && start >= _start + 2
542
+ ).map((styling) => {
543
+ return {
544
+ ...styling,
545
+ start: styling.start,
546
+ finish: styling.finish + length,
547
+ }
548
+ })
549
+ changes.push(preceding)
550
+
551
+ console.log(`Preceding:`)
552
+ console.log(preceding)
553
+
554
+ changes.flat().map(({ index, start, finish }) => {
555
+ stylings[index] = {
556
+ ...stylings[index],
557
+ start: start,
558
+ finish: finish,
559
+ }
560
+ })
561
+
562
+ return stylings
563
+ }
564
+
565
+ const handlePaste = (stylings, start, end) => {
566
+ console.log([start, end])
567
+ console.log(stylings)
568
+
569
+ return stylings
570
+ }
571
+
572
+ const onChange = (value) => {
573
+ setTimeout(function () {
574
+ let [selectionStart, selectionEnd] = getFieldSelection()
575
+
576
+ let difference = value.length - _text.current.content.length
577
+
578
+ let start = selectionStart - difference
579
+ let end = selectionEnd - difference
580
+
581
+ let stylings = _text.current.stylings.slice()
582
+
583
+ let succeeding = getSucceedStylings(start)
584
+ let changes = []
585
+
586
+ for (let succeed of succeeding) {
587
+ let index = getStylingIndex(succeed)
588
+ let styling = stylings[index]
589
+
590
+ changes.push([
591
+ index,
592
+ styling.start + difference,
593
+ styling.finish + difference,
594
+ ])
595
+ }
596
+
597
+ if (difference < 0) {
598
+ stylings = handleDeletion(
599
+ Math.abs(difference),
600
+ selectionStart,
601
+ selectionEnd + Math.abs(difference)
602
+ )
603
+ }
604
+
605
+ if (difference > 0) {
606
+ stylings = handleAddition(
607
+ Math.abs(difference),
608
+ selectionStart,
609
+ selectionEnd
610
+ )
611
+ }
612
+
613
+ // Remove empty stylings and invisible stylings
614
+ stylings = stylings.filter(
615
+ (styling) =>
616
+ !(
617
+ styling.start == styling.finish ||
618
+ (styling.start >= value.length && styling.finish >= value.length)
619
+ )
620
+ )
621
+
622
+ if (_text.current.pasted.status) {
623
+ stylings = handlePaste(
624
+ stylings,
625
+ selectionStart - _text.current.pasted.length,
626
+ selectionStart
627
+ )
628
+ }
629
+
630
+ setText({
631
+ ..._text.current,
632
+ content: value,
633
+ stylings: stylings,
634
+ revert: [selectionStart, selectionEnd],
635
+ pasted: { status: false, length: 0 },
636
+ })
637
+ }, 0)
354
638
  }
355
639
 
356
640
  const getContent = () => {
357
- let content = text.content
641
+ let content = _text.current.content
358
642
 
359
643
  // Get all styling indices
360
- let indices = text.stylings
644
+ let indices = _text.current.stylings
361
645
  .map(({ start, finish }) => [start, finish])
362
646
  .flat()
363
647
 
@@ -373,7 +657,7 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
373
657
  if (indices[0] != 0) indices.unshift(0)
374
658
 
375
659
  // Add last index if not present
376
- let last = text.content.length
660
+ let last = content.length
377
661
  if (indices[indices.length - 1] != last) indices.push(last)
378
662
 
379
663
  let result = []
@@ -385,6 +669,56 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
385
669
  return result
386
670
  }
387
671
 
672
+ const serializeStyleInRange = (start, end) => {
673
+ // Get all stylings intersecting
674
+ let stylings = []
675
+ for (let i = start; i < end; i++) {
676
+ stylings.push(getIntersectStylings(i))
677
+ }
678
+
679
+ // Remove duplicates
680
+ stylings = stylings.flat().filter((item) => item)
681
+ stylings = stylings.filter(
682
+ (item, index) =>
683
+ stylings.findIndex(
684
+ (_item) =>
685
+ _item.start == item.start &&
686
+ _item.finish == item.finish &&
687
+ _item.type == item.type
688
+ ) == index
689
+ )
690
+
691
+ // Clamp start and finish values and offset by start index
692
+ stylings = stylings.map((styling) => {
693
+ return {
694
+ ...styling,
695
+ start: getMaximum([styling.start, start]) - start,
696
+ finish: getMinimum([styling.finish, end]) - start,
697
+ }
698
+ })
699
+
700
+ return stylings
701
+ }
702
+
703
+ // dangerouslySetInnerHTML incorrectly renders when the entire text is highlighted, copied, and then pasted in succession
704
+ useEffect(() => {
705
+ let html = getContent()
706
+ .map((_data, index) => {
707
+ let [start, data] = _data
708
+
709
+ // Get stylings encompassing an index within it's range
710
+ let stylings = getIntersectStylings(start)
711
+ // console.log(data)
712
+ // console.log(stylings)
713
+ return `<span class="${stylings
714
+ .map((styling) => styleClasses[styling.type])
715
+ .join(" ")}" data-child-index="${index}">${data}</span>`
716
+ })
717
+ .join("")
718
+
719
+ field.current.innerHTML = html
720
+ }, [text.content, text.stylings, text.revert])
721
+
388
722
  return (
389
723
  <div className="align-center box-border flex h-min w-[400px] flex-col items-center justify-center rounded bg-blue-300 shadow-md">
390
724
  <div className={clsx("flex w-full flex-row justify-start p-2")}>
@@ -397,19 +731,22 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
397
731
  />
398
732
  <Property
399
733
  name="I"
400
- onClick={() => {
734
+ onMouseDown={(event) => {
735
+ event.preventDefault()
401
736
  perform("italic")
402
737
  }}
403
738
  />
404
739
  <Property
405
740
  name="U"
406
- onClick={() => {
741
+ onMouseDown={(event) => {
742
+ event.preventDefault()
407
743
  perform("under")
408
744
  }}
409
745
  />
410
746
  <Property
411
747
  name="S"
412
- onClick={() => {
748
+ onMouseDown={(event) => {
749
+ event.preventDefault()
413
750
  perform("strike")
414
751
  }}
415
752
  />
@@ -419,106 +756,92 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
419
756
  <div
420
757
  ref={field}
421
758
  contentEditable="true"
422
- role="textbox"
423
- dangerouslySetInnerHTML={{
424
- __html: getContent()
425
- .map((_data, index) => {
426
- let [start, data] = _data
427
-
428
- // Get stylings encompassing an index within it's range
429
- let stylings = getIntersectStylings(start)
430
-
431
- return `<span class="${stylings
432
- .map((styling) => styleClasses[styling.type])
433
- .join("")}">${data}</span>`
434
- // return `<span
435
- // class="${stylings
436
- // .map((styling) => styleClasses[styling.type])
437
- // .join(" ")}"
438
-
439
- // >
440
- // ${data}
441
- // </span>`
442
- })
443
- .join(""),
444
- }}
445
- className="h-[150px] w-full resize-none border-none p-2 outline-none"
446
- onInput={(event) => {
447
- let target: any = event.target
759
+ className="h-[150px] w-full resize-none overflow-auto overflow-x-hidden border-none p-2 outline-none"
760
+ onPaste={(event) => {
761
+ // pastes all copied text from the content editable as plain text
762
+ event.preventDefault()
763
+ const data = event.clipboardData.getData("text/plain")
764
+ document.execCommand("insertHTML", false, data)
765
+
766
+ console.log(data)
767
+
768
+ setText({
769
+ ..._text.current,
770
+ pasted: { status: true, length: data.length },
771
+ })
448
772
 
773
+ // let [start, end] = getFieldSelection()
774
+
775
+ // console.log(index)
776
+ // console.log(start)
777
+
778
+ // let index = start - data.length
779
+ // let stylings = text.stylings.slice()
780
+
781
+ // FIXME:
782
+ // stylings.push({
783
+ // type: "bold",
784
+ // start: 5,
785
+ // finish: 7,
786
+ // })
787
+
788
+ // let copy = text.lastCopy
789
+ // if (copy.length != 0) {
790
+ // for (let styling of copy) {
791
+ // stylings.push({
792
+ // type: styling.type,
793
+ // start: styling.start + start,
794
+ // finish: styling.finish + start,
795
+ // })
796
+ // }
797
+ // }
798
+
799
+ // let content: any = text.content
800
+ // let original = content.length
801
+
802
+ // // If not collapsed, insert text
803
+ // if (start == end) {
804
+ // content = content.split("")
805
+ // content.splice(start, 0, data)
806
+ // content = content.join("")
807
+ // } else {
808
+ // console.log(content)
809
+ // // Otherwise, replace substring
810
+ // content =
811
+ // content.substring(0, start) +
812
+ // data +
813
+ // content.substring(end, content.length)
814
+ // }
815
+
816
+ // let difference = content.length - original
817
+
818
+ // stylings = handleDeletion()
819
+
820
+ // setText({
821
+ // ...text,
822
+ // content: content,
823
+ // stylings: stylings,
824
+ // revert: [start + data.length, start + data.length],
825
+ // })
826
+ }}
827
+ onInput={(event: any) => {
828
+ onChange(event.target.textContent)
829
+ }}
830
+ onCopy={(event) => {
449
831
  let [start, end] = getFieldSelection()
450
832
 
451
- // console.log(target)
452
- // console.log(target.selectionStart)
453
- // console.log(target.selectionEnd)
833
+ let data = serializeStyleInRange(start, end)
454
834
 
455
835
  setText({
456
836
  ...text,
457
- content: target.textContent,
458
- revert: [start, end],
837
+ lastCopy: data,
459
838
  })
460
839
  }}
840
+ // onKeyDown={(event: any) => {
841
+ // event.preventDefault()
842
+ // console.log(event)
843
+ // }}
461
844
  ></div>
462
-
463
- {/* <RichTextarea
464
- ref={field}
465
- value={text.content}
466
- style={{ width: "100%" }} // tailwind w-full does not work
467
- className="order-none h-[150px] resize-none p-2 outline-none"
468
- onChange={(e) => {
469
- onChange(e.target.value)
470
- }}
471
- >
472
- {(value) => {
473
- // Get all styling indices
474
- let indices = text.stylings
475
- .map(({ start, finish }) => [start, finish])
476
- .flat()
477
-
478
- // Sort ascendingly
479
- indices = indices.sort((a, b) => a - b)
480
-
481
- // Remove duplicates
482
- indices = indices.filter(
483
- (element, index) => indices.indexOf(element) == index
484
- )
485
-
486
- // Add first index if not present
487
- if (indices[0] != 0) indices.unshift(0)
488
-
489
- // Add last index if not present
490
- let last = text.content.length
491
- if (indices[indices.length - 1] != last) indices.push(last)
492
-
493
- let result = []
494
-
495
- for (let i = 0; i < indices.length - 1; i++) {
496
- result.push([
497
- indices[i],
498
- value.substring(indices[i], indices[i + 1]),
499
- ])
500
- }
501
-
502
- return result.map((_data, index) => {
503
- let [start, data] = _data
504
-
505
- // Get stylings encompassing an index within it's range
506
- let stylings = getIntersectStylings(start)
507
-
508
- return (
509
- <span
510
- key={index}
511
- className={`${stylings
512
- .map((styling) => styleClasses[styling.type])
513
- .join(" ")}
514
- `}
515
- >
516
- {data}
517
- </span>
518
- )
519
- })
520
- }}
521
- </RichTextarea> */}
522
845
  </div>
523
846
  <div className="h-[1px] w-full bg-slate-600">&nbsp;</div>
524
847
  <button className="my-1 rounded bg-cyan-800 p-2 py-1 text-white">