@sikka/hawa 0.0.236 → 0.0.238
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 +6 -0
- package/docs/documentation/iframe.html +1 -1
- package/docs/documentation/{main.44fbaf91.iframe.bundle.js → main.0607e052.iframe.bundle.js} +1 -1
- package/docs/documentation/project.json +1 -1
- package/es/index.es.js +1 -1
- package/lib/index.js +1 -1
- package/package.json +1 -1
- package/src/elements/FloatingComment.tsx +306 -545
- package/src/styles.css +6 -0
|
@@ -27,11 +27,19 @@ const stylers = {
|
|
|
27
27
|
strike: { id: "strike", css: "line-through", content: "S" },
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// FIXME: Highlighting a part of styled text with a bit on the left with an overall length not equal to clipboard copied text will result in paste issues
|
|
30
|
+
// FIXME: ? Highlighting a part of styled text with a bit on the left with an overall length not equal to clipboard copied text will result in paste issues
|
|
31
31
|
|
|
32
32
|
// FIXME: Highlighting the beginning characters of styled text and then pasting text sometimes doesn't register as right intersecting
|
|
33
33
|
// This expecially happens when the selection is for example, [0, 2] and the styling is [0, 3], this might be failure of addition which doesn't offset the styling
|
|
34
34
|
|
|
35
|
+
// TODO: Refactor styling splicing into one method
|
|
36
|
+
// TODO: Refactor function that simplifies a list of stylings
|
|
37
|
+
// TODO: Turn stylings into a class, this should also change .finish to .end
|
|
38
|
+
|
|
39
|
+
// Possible logic changes:
|
|
40
|
+
// Paste = Removal + Addition -> Styling Removal + Styling Addition
|
|
41
|
+
// Drag & Drop = Removal + Addition -> Styling Removal + Styling Addition
|
|
42
|
+
|
|
35
43
|
export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
36
44
|
props
|
|
37
45
|
) => {
|
|
@@ -39,8 +47,11 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
39
47
|
content: "",
|
|
40
48
|
stylings: [], // A styling is an object with 2 indices specifying a substring of text, and the applied effect
|
|
41
49
|
revert: [0, 0],
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
clipboard: [],
|
|
51
|
+
events: {
|
|
52
|
+
paste: { is: false, length: null },
|
|
53
|
+
drop: { is: false, text: null, drag: null },
|
|
54
|
+
},
|
|
44
55
|
})
|
|
45
56
|
|
|
46
57
|
const field = useRef(null)
|
|
@@ -55,41 +66,57 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
55
66
|
return i
|
|
56
67
|
}
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
const getFieldSelection = () => {
|
|
60
|
-
if (document.activeElement != field.current) return [0, 0]
|
|
61
|
-
|
|
69
|
+
const getSelectionPrecedingSum = (name) => {
|
|
62
70
|
let selection = window.getSelection()
|
|
63
71
|
let nodes = Array.from(field.current.childNodes)
|
|
64
72
|
|
|
73
|
+
// All current occurences for text or br:
|
|
74
|
+
// Pasting on empty text (text)
|
|
75
|
+
// Cutting/removing all text (br)
|
|
76
|
+
// Typing the first character in empty text (text)
|
|
77
|
+
// Dragging text to the end of the text (text)
|
|
78
|
+
|
|
65
79
|
nodes = nodes.filter(
|
|
66
80
|
(item: any) => !["#text", "BR"].includes(item.nodeName)
|
|
67
81
|
)
|
|
68
82
|
|
|
69
|
-
let
|
|
83
|
+
let parent: any = selection[name].parentNode
|
|
84
|
+
let special = 0
|
|
85
|
+
|
|
86
|
+
// Special case for empty text
|
|
87
|
+
// if (parent == field.current) {
|
|
88
|
+
// console.log("hi")
|
|
89
|
+
// }
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
// Special case for dropping text near or inside styled text
|
|
92
|
+
if (!Array.from(parent.parentNode.classList).includes("selection-ignore")) {
|
|
93
|
+
parent = parent.parentNode
|
|
94
|
+
|
|
95
|
+
let index = getChildIndex(selection[name].parentNode)
|
|
96
|
+
special = Array.from(parent.childNodes)
|
|
97
|
+
.slice(0, index)
|
|
98
|
+
.map((e: any) => e.textContent.length)
|
|
99
|
+
.reduce((a, b) => a + b, 0)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let index = parent == field.current ? nodes.length : getChildIndex(parent)
|
|
103
|
+
|
|
104
|
+
let sum =
|
|
105
|
+
nodes
|
|
106
|
+
.slice(0, index)
|
|
107
|
+
.map((span: any) => span.textContent.length)
|
|
108
|
+
.reduce((a, b) => a + b, 0) + special
|
|
109
|
+
|
|
110
|
+
return sum
|
|
111
|
+
}
|
|
76
112
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.map((span: any) => span.textContent.length)
|
|
80
|
-
.reduce((a, b) => a + b, 0)
|
|
113
|
+
const getFieldSelection = () => {
|
|
114
|
+
if (document.activeElement != field.current) return [0, 0]
|
|
81
115
|
|
|
82
|
-
let
|
|
83
|
-
let endNodeIndex =
|
|
84
|
-
endParent == field.current
|
|
85
|
-
? nodes.length
|
|
86
|
-
: // : parseInt(endParent.dataset.childIndex)
|
|
87
|
-
getChildIndex(endParent)
|
|
116
|
+
let selection = window.getSelection()
|
|
88
117
|
|
|
89
|
-
let
|
|
90
|
-
|
|
91
|
-
.map((span: any) => span.textContent.length)
|
|
92
|
-
.reduce((a, b) => a + b, 0)
|
|
118
|
+
let startPrecedingSum = getSelectionPrecedingSum("anchorNode")
|
|
119
|
+
let endPrecedingSum = getSelectionPrecedingSum("focusNode")
|
|
93
120
|
|
|
94
121
|
let result = [
|
|
95
122
|
startPrecedingSum + selection.anchorOffset,
|
|
@@ -301,36 +328,6 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
301
328
|
})
|
|
302
329
|
}
|
|
303
330
|
|
|
304
|
-
const getRangeIntersectStylings = (start, end, startOffset, finishOffset) => {
|
|
305
|
-
// Get all stylings intersecting
|
|
306
|
-
let stylings = []
|
|
307
|
-
for (let i = start; i < end; i++) {
|
|
308
|
-
stylings.push(getIntersectStylings(i, startOffset, finishOffset))
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Remove duplicates
|
|
312
|
-
stylings = stylings.flat().filter((item) => item)
|
|
313
|
-
stylings = stylings.filter(
|
|
314
|
-
(item, index) =>
|
|
315
|
-
stylings.findIndex(
|
|
316
|
-
(_item) =>
|
|
317
|
-
_item.start == item.start &&
|
|
318
|
-
_item.finish == item.finish &&
|
|
319
|
-
_item.type == item.type
|
|
320
|
-
) == index
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
return stylings
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const compareStylings = (styling1, styling2) => {
|
|
327
|
-
return (
|
|
328
|
-
styling1.type == styling2.type &&
|
|
329
|
-
styling1.start == styling2.start &&
|
|
330
|
-
styling1.finish == styling2.finish
|
|
331
|
-
)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
331
|
// Get stylings encompassing an index within it's range
|
|
335
332
|
const getIntersectStylings = (index, startOffset = 0, finishOffset = 0) => {
|
|
336
333
|
// Find all stylings with encompassing range
|
|
@@ -342,437 +339,306 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
342
339
|
return matches
|
|
343
340
|
}
|
|
344
341
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const getStylingIndex = (styling) => {
|
|
354
|
-
return text.stylings.findIndex(
|
|
355
|
-
(item) =>
|
|
356
|
-
item.start == styling.start &&
|
|
357
|
-
item.finish == styling.finish &&
|
|
358
|
-
item.type == styling.type
|
|
359
|
-
)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const handleScenario = (stylings, predicate) => {
|
|
363
|
-
let matches = stylings.filter(({ start: _start, finish: _finish }) =>
|
|
364
|
-
predicate(_start, _finish)
|
|
342
|
+
const getStylingsInRange = (stylings, startIndex, endIndex) => {
|
|
343
|
+
// Get all intersecting stylings within range
|
|
344
|
+
let result = stylings.filter(
|
|
345
|
+
({ start, finish }) =>
|
|
346
|
+
(finish > startIndex && start < endIndex) ||
|
|
347
|
+
(start < endIndex && finish > startIndex)
|
|
365
348
|
)
|
|
366
349
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
type: styling.type,
|
|
375
|
-
start: styling.start,
|
|
376
|
-
finish: styling.finish,
|
|
377
|
-
})
|
|
350
|
+
// Clamp start and end values, and offset by start index to reach the relative minimum
|
|
351
|
+
result = result.map((styling) => {
|
|
352
|
+
return {
|
|
353
|
+
...styling,
|
|
354
|
+
start: getMaximum([styling.start, startIndex]) - startIndex,
|
|
355
|
+
finish: getMinimum([styling.finish, endIndex]) - startIndex,
|
|
356
|
+
}
|
|
378
357
|
})
|
|
379
358
|
|
|
380
|
-
return
|
|
359
|
+
return result
|
|
381
360
|
}
|
|
382
361
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
let
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
// changes.push(
|
|
391
|
-
// handleScenario(
|
|
392
|
-
// stylings,
|
|
393
|
-
// (_start, _finish) => start < _start && end <= start
|
|
394
|
-
// ).map((styling) => {
|
|
395
|
-
// return {
|
|
396
|
-
// ...styling,
|
|
397
|
-
// start: styling.start - length,
|
|
398
|
-
// finish: styling.finish - length,
|
|
399
|
-
// }
|
|
400
|
-
// })
|
|
401
|
-
// )
|
|
402
|
-
|
|
403
|
-
let succeeding = stylings.filter(
|
|
404
|
-
({ start: _start }) => start < _start && end <= _start
|
|
405
|
-
)
|
|
406
|
-
stylings.map((styling, index) => {
|
|
407
|
-
let result = succeeding.find((item) => compareStylings(styling, item))
|
|
408
|
-
if (!result) return
|
|
409
|
-
|
|
410
|
-
changes.push({
|
|
411
|
-
index: index,
|
|
412
|
-
start: styling.start - length,
|
|
413
|
-
finish: styling.finish - length,
|
|
414
|
-
})
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
// Handle complete encapsulation over styling
|
|
418
|
-
let encapsulating = stylings.filter(
|
|
419
|
-
({ start: _start, finish: _finish }) => start <= _start && end >= _finish
|
|
420
|
-
)
|
|
421
|
-
stylings.map((styling, index) => {
|
|
422
|
-
let result = encapsulating.find((item) => compareStylings(styling, item))
|
|
423
|
-
if (!result) return
|
|
424
|
-
|
|
425
|
-
// This will effectively remove the styling, since collapsed ranges are automatically removed
|
|
426
|
-
changes.push({
|
|
427
|
-
index: index,
|
|
428
|
-
start: styling.start,
|
|
429
|
-
finish: styling.start,
|
|
430
|
-
})
|
|
431
|
-
})
|
|
362
|
+
const splitStyling = (styling, index, offset = 0) => {
|
|
363
|
+
// Get first split
|
|
364
|
+
let first = {
|
|
365
|
+
...styling,
|
|
366
|
+
start: styling.start,
|
|
367
|
+
finish: index,
|
|
368
|
+
}
|
|
432
369
|
|
|
433
|
-
//
|
|
434
|
-
let
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
let result = encapsulated.find((item) => compareStylings(styling, item))
|
|
440
|
-
if (!result) return
|
|
441
|
-
|
|
442
|
-
changes.push({
|
|
443
|
-
index: index,
|
|
444
|
-
start: styling.start,
|
|
445
|
-
finish: styling.finish - length,
|
|
446
|
-
})
|
|
447
|
-
})
|
|
370
|
+
// Get second split
|
|
371
|
+
let second = {
|
|
372
|
+
...styling,
|
|
373
|
+
start: index + offset,
|
|
374
|
+
finish: styling.finish + offset,
|
|
375
|
+
}
|
|
448
376
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
({ start: _start, finish: _finish }) =>
|
|
452
|
-
end < _finish && end > _start && start < _start
|
|
453
|
-
)
|
|
454
|
-
stylings.map((styling, index) => {
|
|
455
|
-
let result = leftResumption.find((item) => compareStylings(styling, item))
|
|
456
|
-
if (!result) return
|
|
457
|
-
|
|
458
|
-
changes.push({
|
|
459
|
-
index: index,
|
|
460
|
-
start: getMaximum([end, styling.start]) - length,
|
|
461
|
-
finish: styling.finish - length,
|
|
462
|
-
})
|
|
463
|
-
})
|
|
377
|
+
return [first, second]
|
|
378
|
+
}
|
|
464
379
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
start > _start && start < _finish && end > _finish
|
|
468
|
-
)
|
|
469
|
-
stylings.map((styling, index) => {
|
|
470
|
-
let result = rightResumption.find((item) =>
|
|
471
|
-
compareStylings(styling, item)
|
|
472
|
-
)
|
|
473
|
-
if (!result) return
|
|
380
|
+
const additionTo = (stylings, startIndex, length, defaultBehavior = true) => {
|
|
381
|
+
// console.log(`Adding text of length ${length} at index ${startIndex}`)
|
|
474
382
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
})
|
|
480
|
-
})
|
|
383
|
+
// Required operations:
|
|
384
|
+
// Offset succeeding stylings
|
|
385
|
+
// Split intersecting stylings
|
|
386
|
+
// Styling continuation at end (only for normal addition)
|
|
481
387
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
388
|
+
stylings = stylings.map((styling) => {
|
|
389
|
+
// A succeeding styling
|
|
390
|
+
if (styling.start >= startIndex && styling.finish > startIndex) {
|
|
391
|
+
console.log("succeeding")
|
|
392
|
+
return {
|
|
393
|
+
...styling,
|
|
394
|
+
start: styling.start + length,
|
|
395
|
+
finish: styling.finish + length,
|
|
396
|
+
}
|
|
488
397
|
}
|
|
489
|
-
})
|
|
490
|
-
|
|
491
|
-
// Handle complete encapsulation by styling
|
|
492
|
-
// stylings = stylings.map((styling) => {
|
|
493
|
-
// if (start >= styling.start && end <= styling.finish) {
|
|
494
|
-
// console.log("hi")
|
|
495
|
-
// }
|
|
496
|
-
|
|
497
|
-
// return styling
|
|
498
|
-
// })
|
|
499
398
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
399
|
+
// An intersecting styling
|
|
400
|
+
if (styling.start < startIndex && styling.finish > startIndex) {
|
|
401
|
+
// Normal addition (non-drop & non-paste), adds to the length of the styling
|
|
402
|
+
if (defaultBehavior) {
|
|
403
|
+
return {
|
|
404
|
+
...styling,
|
|
405
|
+
finish: styling.finish + length,
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
// Special addition (drop & paste), splits the styling and offsets the second half by length of addition
|
|
409
|
+
return splitStyling(styling, startIndex, length)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
503
412
|
|
|
504
|
-
|
|
413
|
+
// A connected styling at the end
|
|
414
|
+
if (styling.finish == startIndex) {
|
|
415
|
+
// Normal addition (non-drop & non-paste), continues the styling by addition length
|
|
416
|
+
if (defaultBehavior) {
|
|
417
|
+
return {
|
|
418
|
+
...styling,
|
|
419
|
+
finish: styling.finish + length,
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
505
423
|
|
|
506
|
-
|
|
424
|
+
return styling
|
|
425
|
+
})
|
|
507
426
|
|
|
508
|
-
return stylings
|
|
427
|
+
return stylings.flat()
|
|
509
428
|
}
|
|
510
429
|
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
end -= length + 1
|
|
430
|
+
const deletionOf = (stylings, startIndex, endIndex) => {
|
|
431
|
+
// console.log(`Removing text from ${startIndex} to ${endIndex}`)
|
|
514
432
|
|
|
515
|
-
let
|
|
433
|
+
let length = Math.abs(endIndex - startIndex)
|
|
516
434
|
|
|
517
|
-
|
|
435
|
+
// Required operations:
|
|
436
|
+
// Offset succeeding stylings
|
|
437
|
+
// Remove stylings completely within range
|
|
438
|
+
// Shrink stylings surrounding range
|
|
439
|
+
// Clamp left resumptions and offset
|
|
440
|
+
// Clamp right resumptions
|
|
518
441
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
442
|
+
stylings = stylings.map((styling) => {
|
|
443
|
+
// A succeeding styling, but not a right resumption
|
|
444
|
+
if (
|
|
445
|
+
styling.start >= startIndex &&
|
|
446
|
+
styling.start >= endIndex &&
|
|
447
|
+
styling.finish >= startIndex &&
|
|
448
|
+
styling.finish >= endIndex
|
|
449
|
+
) {
|
|
450
|
+
return {
|
|
451
|
+
...styling,
|
|
452
|
+
start: styling.start - length,
|
|
453
|
+
finish: styling.finish - length,
|
|
454
|
+
}
|
|
527
455
|
}
|
|
528
|
-
})
|
|
529
|
-
changes.push(succeeding)
|
|
530
456
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
457
|
+
// A styling completely within deletion range
|
|
458
|
+
if (styling.start >= startIndex && styling.finish <= endIndex) {
|
|
459
|
+
return null
|
|
460
|
+
}
|
|
536
461
|
|
|
537
|
-
|
|
462
|
+
// A styling surrounding deletion range
|
|
463
|
+
if (styling.start <= startIndex && styling.finish >= endIndex) {
|
|
464
|
+
return {
|
|
465
|
+
...styling,
|
|
466
|
+
finish: styling.finish - length,
|
|
467
|
+
}
|
|
468
|
+
}
|
|
538
469
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
start: styling.start,
|
|
547
|
-
finish: styling.finish + length,
|
|
470
|
+
// A styling not fully within deletion range, while the range exceeds to the left
|
|
471
|
+
if (styling.start > startIndex && styling.start < endIndex) {
|
|
472
|
+
return {
|
|
473
|
+
...styling,
|
|
474
|
+
start: getMaximum([styling.start, endIndex]) - length,
|
|
475
|
+
finish: styling.finish - length,
|
|
476
|
+
}
|
|
548
477
|
}
|
|
549
|
-
})
|
|
550
|
-
changes.push(preceding)
|
|
551
478
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
finish: styling.finish + length,
|
|
479
|
+
// A styling not full within deletion range, while the range exceeds to the right
|
|
480
|
+
if (styling.finish > startIndex && styling.finish < endIndex) {
|
|
481
|
+
return {
|
|
482
|
+
...styling,
|
|
483
|
+
finish: getMinimum([styling.finish, startIndex]),
|
|
484
|
+
}
|
|
559
485
|
}
|
|
486
|
+
|
|
487
|
+
return styling
|
|
560
488
|
})
|
|
561
489
|
|
|
562
|
-
|
|
490
|
+
return stylings.flat().filter((styling) => styling)
|
|
491
|
+
}
|
|
563
492
|
|
|
564
|
-
|
|
493
|
+
const processNormal = (
|
|
494
|
+
stylings,
|
|
495
|
+
difference,
|
|
496
|
+
selectionStart,
|
|
497
|
+
selectionEnd
|
|
498
|
+
) => {
|
|
499
|
+
if (difference == 0) return stylings
|
|
500
|
+
|
|
501
|
+
if (difference > 0) {
|
|
502
|
+
stylings = additionTo(stylings, selectionStart - difference, difference)
|
|
503
|
+
} else {
|
|
504
|
+
stylings = deletionOf(
|
|
505
|
+
stylings,
|
|
506
|
+
selectionStart,
|
|
507
|
+
selectionEnd + Math.abs(difference)
|
|
508
|
+
)
|
|
509
|
+
}
|
|
565
510
|
|
|
566
|
-
|
|
511
|
+
return stylings
|
|
512
|
+
}
|
|
567
513
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
// console.log(preceding)
|
|
514
|
+
const processPaste = (stylings, difference, selectionStart, selectionEnd) => {
|
|
515
|
+
let pasteLength = _text.current.events.paste.length
|
|
571
516
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
517
|
+
// Get addition start index
|
|
518
|
+
let additionStart = selectionStart - pasteLength
|
|
519
|
+
|
|
520
|
+
// Get removal range
|
|
521
|
+
let removalStart = additionStart
|
|
522
|
+
let removalEnd = additionStart + pasteLength - difference
|
|
523
|
+
|
|
524
|
+
// Compute deletion
|
|
525
|
+
stylings = deletionOf(stylings, removalStart, removalEnd)
|
|
526
|
+
|
|
527
|
+
// Compute addition
|
|
528
|
+
stylings = additionTo(stylings, additionStart, pasteLength, false)
|
|
529
|
+
|
|
530
|
+
// Add rich copied stylings offset by paste start index
|
|
531
|
+
stylings = stylings.concat(
|
|
532
|
+
_text.current.clipboard.map((styling) => {
|
|
533
|
+
return {
|
|
534
|
+
...styling,
|
|
535
|
+
start: styling.start + additionStart,
|
|
536
|
+
finish: styling.finish + additionStart,
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
)
|
|
579
540
|
|
|
580
541
|
return stylings
|
|
581
542
|
}
|
|
582
543
|
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
console.log([start, end])
|
|
586
|
-
console.log(stylings)
|
|
587
|
-
let changes = []
|
|
544
|
+
const processDrop = (stylings, difference, dropStart, dropEnd) => {
|
|
545
|
+
let dropLength = _text.current.events.drop.text.length
|
|
588
546
|
|
|
589
|
-
|
|
547
|
+
let [dragStart, dragEnd] = _text.current.events.drop.drag
|
|
590
548
|
|
|
591
|
-
let
|
|
592
|
-
stylings,
|
|
593
|
-
(_start, _end) => start <= _start && end >= _end // This is needed because of conflict with addition/deletion
|
|
594
|
-
).map((styling) => {
|
|
595
|
-
// Effective removal
|
|
596
|
-
return {
|
|
597
|
-
...styling,
|
|
598
|
-
start: styling.start,
|
|
599
|
-
finish: styling.start,
|
|
600
|
-
}
|
|
601
|
-
})
|
|
602
|
-
changes.push(encapsulating)
|
|
603
|
-
|
|
604
|
-
// Get encapsulated stylings
|
|
605
|
-
let encapsulated = handleScenario(
|
|
606
|
-
stylings,
|
|
607
|
-
(_start, _end) => _start < start && _end > end
|
|
608
|
-
).map((styling) => {
|
|
609
|
-
// Right splice
|
|
610
|
-
changes.push({
|
|
611
|
-
...styling,
|
|
612
|
-
index: -1,
|
|
613
|
-
start: end,
|
|
614
|
-
finish: styling.finish,
|
|
615
|
-
})
|
|
549
|
+
let dropDifference = dropStart - dragStart
|
|
616
550
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
...styling,
|
|
620
|
-
index: -1,
|
|
621
|
-
start: styling.start,
|
|
622
|
-
finish: start,
|
|
623
|
-
})
|
|
551
|
+
// Get stylings at drag range
|
|
552
|
+
let dragStylings = getStylingsInRange(stylings, dragStart, dragEnd)
|
|
624
553
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
start: styling.start,
|
|
629
|
-
finish: styling.start,
|
|
630
|
-
}
|
|
631
|
-
})
|
|
632
|
-
changes.push(encapsulated)
|
|
633
|
-
|
|
634
|
-
// Get intersecting stylings from the left
|
|
635
|
-
let left = handleScenario(
|
|
636
|
-
stylings,
|
|
637
|
-
(_start, _end) =>
|
|
638
|
-
_end > start && _end <= end - difference + 1 && _start < start
|
|
639
|
-
).map((styling) => {
|
|
640
|
-
return {
|
|
641
|
-
...styling,
|
|
642
|
-
finish: start,
|
|
643
|
-
}
|
|
644
|
-
})
|
|
554
|
+
// Removal range
|
|
555
|
+
let removalStart = dragStart
|
|
556
|
+
let removalEnd = dragEnd
|
|
645
557
|
|
|
646
|
-
|
|
558
|
+
// If the drag precedes the drop (positive difference)
|
|
559
|
+
if (dropDifference > 0) {
|
|
560
|
+
// Addition start index
|
|
561
|
+
let additionStart = dropEnd
|
|
647
562
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
stylings,
|
|
651
|
-
(_start, _end) =>
|
|
652
|
-
_end > end - difference && _start >= start && _start < end - difference
|
|
653
|
-
).map((styling) => {
|
|
654
|
-
return {
|
|
655
|
-
...styling,
|
|
656
|
-
start: end,
|
|
657
|
-
}
|
|
658
|
-
})
|
|
563
|
+
// Compute addition first
|
|
564
|
+
stylings = additionTo(stylings, additionStart, dropLength, false)
|
|
659
565
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
console.log(`Encapsulated: ${encapsulated.length}`)
|
|
664
|
-
console.log(`Left Intersecting: ${left.length}`)
|
|
665
|
-
console.log(`Right Intersecting: ${right.length}`)
|
|
666
|
-
|
|
667
|
-
changes = changes.flat()
|
|
668
|
-
changes.map((styling) => {
|
|
669
|
-
let { index, start, finish } = styling
|
|
670
|
-
if (index == -1) {
|
|
671
|
-
stylings.push({
|
|
672
|
-
type: styling.type,
|
|
673
|
-
start: start,
|
|
674
|
-
finish: finish,
|
|
675
|
-
})
|
|
676
|
-
return
|
|
677
|
-
}
|
|
566
|
+
// Compute removal second
|
|
567
|
+
stylings = deletionOf(stylings, removalStart, removalEnd)
|
|
568
|
+
}
|
|
678
569
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
})
|
|
570
|
+
// If the drop precedes the drag (negative difference)
|
|
571
|
+
if (dropDifference < 0) {
|
|
572
|
+
// Addition start index
|
|
573
|
+
let additionStart = dropStart
|
|
685
574
|
|
|
686
|
-
|
|
575
|
+
// Compute removal first
|
|
576
|
+
stylings = deletionOf(stylings, removalStart, removalEnd)
|
|
577
|
+
|
|
578
|
+
// Compute addition second
|
|
579
|
+
stylings = additionTo(stylings, additionStart, dropLength, false)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Add rich dragged stylings offset
|
|
583
|
+
stylings = stylings.concat(
|
|
584
|
+
dragStylings.map((styling) => {
|
|
585
|
+
return {
|
|
586
|
+
...styling,
|
|
587
|
+
start: styling.start + dropStart,
|
|
588
|
+
finish: styling.finish + dropStart,
|
|
589
|
+
}
|
|
590
|
+
})
|
|
591
|
+
)
|
|
687
592
|
|
|
688
593
|
return stylings
|
|
689
594
|
}
|
|
690
595
|
|
|
691
596
|
const onChange = (value) => {
|
|
597
|
+
// Since drop events cause onChange to invoke twice, ignore the first incomplete event
|
|
598
|
+
if (
|
|
599
|
+
_text.current.events.drop.is &&
|
|
600
|
+
value.length != _text.current.content.length
|
|
601
|
+
)
|
|
602
|
+
return
|
|
603
|
+
|
|
692
604
|
setTimeout(function () {
|
|
693
605
|
let [selectionStart, selectionEnd] = getFieldSelection()
|
|
694
|
-
|
|
695
606
|
let difference = value.length - _text.current.content.length
|
|
607
|
+
let stylings = _text.current.stylings
|
|
696
608
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
let succeeding = getSucceedStylings(start)
|
|
703
|
-
let changes = []
|
|
704
|
-
|
|
705
|
-
for (let succeed of succeeding) {
|
|
706
|
-
let index = getStylingIndex(succeed)
|
|
707
|
-
let styling = stylings[index]
|
|
708
|
-
|
|
709
|
-
changes.push([
|
|
710
|
-
index,
|
|
711
|
-
styling.start + difference,
|
|
712
|
-
styling.finish + difference,
|
|
713
|
-
])
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
if (difference < 0) {
|
|
717
|
-
stylings = handleDeletion(
|
|
718
|
-
Math.abs(difference),
|
|
609
|
+
// Paste event
|
|
610
|
+
if (_text.current.events.paste.is) {
|
|
611
|
+
stylings = processPaste(
|
|
612
|
+
stylings,
|
|
613
|
+
difference,
|
|
719
614
|
selectionStart,
|
|
720
|
-
selectionEnd
|
|
615
|
+
selectionEnd
|
|
721
616
|
)
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
Math.abs(difference),
|
|
617
|
+
} else if (_text.current.events.drop.is) {
|
|
618
|
+
stylings = processDrop(
|
|
619
|
+
stylings,
|
|
620
|
+
difference,
|
|
727
621
|
selectionStart,
|
|
728
622
|
selectionEnd
|
|
729
623
|
)
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// Remove empty stylings and invisible stylings
|
|
733
|
-
stylings = stylings.filter(
|
|
734
|
-
(styling) =>
|
|
735
|
-
!(
|
|
736
|
-
styling.start == styling.finish ||
|
|
737
|
-
(styling.start >= value.length && styling.finish >= value.length)
|
|
738
|
-
)
|
|
739
|
-
)
|
|
740
|
-
|
|
741
|
-
if (_text.current.pasted.status) {
|
|
742
|
-
stylings = handlePaste(
|
|
624
|
+
} else {
|
|
625
|
+
stylings = processNormal(
|
|
743
626
|
stylings,
|
|
744
627
|
difference,
|
|
745
|
-
selectionStart
|
|
746
|
-
|
|
628
|
+
selectionStart,
|
|
629
|
+
selectionEnd
|
|
747
630
|
)
|
|
748
|
-
|
|
749
|
-
for (let styling of _text.current.lastCopy) {
|
|
750
|
-
styling = {
|
|
751
|
-
...styling,
|
|
752
|
-
start: styling.start + selectionStart - _text.current.pasted.length,
|
|
753
|
-
finish:
|
|
754
|
-
styling.finish + selectionStart - _text.current.pasted.length,
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
stylings.push(styling)
|
|
758
|
-
}
|
|
759
631
|
}
|
|
760
632
|
|
|
761
|
-
// Remove empty stylings and invisible stylings
|
|
762
|
-
stylings = stylings.filter(
|
|
763
|
-
(styling) =>
|
|
764
|
-
!(
|
|
765
|
-
styling.start == styling.finish ||
|
|
766
|
-
(styling.start >= value.length && styling.finish >= value.length)
|
|
767
|
-
)
|
|
768
|
-
)
|
|
769
|
-
|
|
770
633
|
setText({
|
|
771
634
|
..._text.current,
|
|
772
635
|
content: value,
|
|
773
636
|
stylings: stylings,
|
|
774
637
|
revert: [selectionStart, selectionEnd],
|
|
775
|
-
|
|
638
|
+
events: {
|
|
639
|
+
paste: { is: false, length: null },
|
|
640
|
+
drop: { is: false, text: null, drag: null },
|
|
641
|
+
},
|
|
776
642
|
})
|
|
777
643
|
}, 0)
|
|
778
644
|
}
|
|
@@ -809,37 +675,6 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
809
675
|
return result
|
|
810
676
|
}
|
|
811
677
|
|
|
812
|
-
const serializeStyleInRange = (start, end) => {
|
|
813
|
-
// Get all stylings intersecting
|
|
814
|
-
let stylings = []
|
|
815
|
-
for (let i = start; i < end; i++) {
|
|
816
|
-
stylings.push(getIntersectStylings(i))
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Remove duplicates
|
|
820
|
-
stylings = stylings.flat().filter((item) => item)
|
|
821
|
-
stylings = stylings.filter(
|
|
822
|
-
(item, index) =>
|
|
823
|
-
stylings.findIndex(
|
|
824
|
-
(_item) =>
|
|
825
|
-
_item.start == item.start &&
|
|
826
|
-
_item.finish == item.finish &&
|
|
827
|
-
_item.type == item.type
|
|
828
|
-
) == index
|
|
829
|
-
)
|
|
830
|
-
|
|
831
|
-
// Clamp start and finish values and offset by start index
|
|
832
|
-
stylings = stylings.map((styling) => {
|
|
833
|
-
return {
|
|
834
|
-
...styling,
|
|
835
|
-
start: getMaximum([styling.start, start]) - start,
|
|
836
|
-
finish: getMinimum([styling.finish, end]) - start,
|
|
837
|
-
}
|
|
838
|
-
})
|
|
839
|
-
|
|
840
|
-
return stylings
|
|
841
|
-
}
|
|
842
|
-
|
|
843
678
|
// dangerouslySetInnerHTML incorrectly renders when the entire text is highlighted, copied, and then pasted in succession
|
|
844
679
|
useEffect(() => {
|
|
845
680
|
let html = getContent()
|
|
@@ -848,8 +683,6 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
848
683
|
|
|
849
684
|
// Get stylings encompassing an index within it's range
|
|
850
685
|
let stylings = getIntersectStylings(start)
|
|
851
|
-
// console.log(data)
|
|
852
|
-
// console.log(stylings)
|
|
853
686
|
return `<span class="${stylings
|
|
854
687
|
.map((styling) => stylers[styling.type].css)
|
|
855
688
|
.join(" ")}" data-child-index="${index}">${data}</span>`
|
|
@@ -887,42 +720,13 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
887
720
|
/>
|
|
888
721
|
)
|
|
889
722
|
})}
|
|
890
|
-
|
|
891
|
-
{/* <Property
|
|
892
|
-
name="B"
|
|
893
|
-
onMouseDown={(event) => {
|
|
894
|
-
event.preventDefault() // This does not take focus away from field which allows the function to retrieve the current selection data
|
|
895
|
-
perform("bold")
|
|
896
|
-
}}
|
|
897
|
-
/>
|
|
898
|
-
<Property
|
|
899
|
-
name="I"
|
|
900
|
-
onMouseDown={(event) => {
|
|
901
|
-
event.preventDefault()
|
|
902
|
-
perform("italic")
|
|
903
|
-
}}
|
|
904
|
-
/>
|
|
905
|
-
<Property
|
|
906
|
-
name="U"
|
|
907
|
-
onMouseDown={(event) => {
|
|
908
|
-
event.preventDefault()
|
|
909
|
-
perform("under")
|
|
910
|
-
}}
|
|
911
|
-
/>
|
|
912
|
-
<Property
|
|
913
|
-
name="S"
|
|
914
|
-
onMouseDown={(event) => {
|
|
915
|
-
event.preventDefault()
|
|
916
|
-
perform("strike")
|
|
917
|
-
}}
|
|
918
|
-
/> */}
|
|
919
723
|
</div>
|
|
920
724
|
<div className="h-[1px] w-full bg-slate-600"> </div>
|
|
921
|
-
<div className="w-full">
|
|
725
|
+
<div className="selection-ignore box-border w-full p-2">
|
|
922
726
|
<div
|
|
923
727
|
ref={field}
|
|
924
728
|
contentEditable="true"
|
|
925
|
-
className="rtl h-[150px] w-full resize-none overflow-auto overflow-x-hidden border-none
|
|
729
|
+
className="selection-ignore rtl h-[150px] w-full resize-none overflow-auto overflow-x-hidden border-none font-['Arial'] text-[16px] outline-none"
|
|
926
730
|
style={{
|
|
927
731
|
direction: getTextDirection(),
|
|
928
732
|
}}
|
|
@@ -932,84 +736,41 @@ export const FloatingComment: React.FunctionComponent<ComponentTypes> = (
|
|
|
932
736
|
const data = event.clipboardData.getData("text/plain")
|
|
933
737
|
document.execCommand("insertHTML", false, data)
|
|
934
738
|
|
|
935
|
-
console.log(data)
|
|
936
|
-
|
|
937
739
|
setText({
|
|
938
740
|
..._text.current,
|
|
939
|
-
|
|
741
|
+
events: {
|
|
742
|
+
..._text.current.events,
|
|
743
|
+
paste: { is: true, length: data.length },
|
|
744
|
+
},
|
|
940
745
|
})
|
|
941
|
-
|
|
942
|
-
// let [start, end] = getFieldSelection()
|
|
943
|
-
|
|
944
|
-
// console.log(index)
|
|
945
|
-
// console.log(start)
|
|
946
|
-
|
|
947
|
-
// let index = start - data.length
|
|
948
|
-
// let stylings = text.stylings.slice()
|
|
949
|
-
|
|
950
|
-
// FIXME:
|
|
951
|
-
// stylings.push({
|
|
952
|
-
// type: "bold",
|
|
953
|
-
// start: 5,
|
|
954
|
-
// finish: 7,
|
|
955
|
-
// })
|
|
956
|
-
|
|
957
|
-
// let copy = text.lastCopy
|
|
958
|
-
// if (copy.length != 0) {
|
|
959
|
-
// for (let styling of copy) {
|
|
960
|
-
// stylings.push({
|
|
961
|
-
// type: styling.type,
|
|
962
|
-
// start: styling.start + start,
|
|
963
|
-
// finish: styling.finish + start,
|
|
964
|
-
// })
|
|
965
|
-
// }
|
|
966
|
-
// }
|
|
967
|
-
|
|
968
|
-
// let content: any = text.content
|
|
969
|
-
// let original = content.length
|
|
970
|
-
|
|
971
|
-
// // If not collapsed, insert text
|
|
972
|
-
// if (start == end) {
|
|
973
|
-
// content = content.split("")
|
|
974
|
-
// content.splice(start, 0, data)
|
|
975
|
-
// content = content.join("")
|
|
976
|
-
// } else {
|
|
977
|
-
// console.log(content)
|
|
978
|
-
// // Otherwise, replace substring
|
|
979
|
-
// content =
|
|
980
|
-
// content.substring(0, start) +
|
|
981
|
-
// data +
|
|
982
|
-
// content.substring(end, content.length)
|
|
983
|
-
// }
|
|
984
|
-
|
|
985
|
-
// let difference = content.length - original
|
|
986
|
-
|
|
987
|
-
// stylings = handleDeletion()
|
|
988
|
-
|
|
989
|
-
// setText({
|
|
990
|
-
// ...text,
|
|
991
|
-
// content: content,
|
|
992
|
-
// stylings: stylings,
|
|
993
|
-
// revert: [start + data.length, start + data.length],
|
|
994
|
-
// })
|
|
995
746
|
}}
|
|
996
747
|
onInput={(event: any) => {
|
|
997
748
|
onChange(event.target.textContent)
|
|
998
749
|
}}
|
|
999
|
-
onCopy={(
|
|
750
|
+
onCopy={() => {
|
|
1000
751
|
let [start, end] = getFieldSelection()
|
|
1001
752
|
|
|
1002
|
-
let
|
|
753
|
+
let stylings = _text.current.stylings.slice()
|
|
754
|
+
stylings = getStylingsInRange(stylings, start, end)
|
|
1003
755
|
|
|
1004
756
|
setText({
|
|
1005
757
|
...text,
|
|
1006
|
-
|
|
758
|
+
clipboard: stylings,
|
|
759
|
+
})
|
|
760
|
+
}}
|
|
761
|
+
onDrop={(event) => {
|
|
762
|
+
let text = event.dataTransfer.getData("text")
|
|
763
|
+
|
|
764
|
+
if (text.trim() == "") return
|
|
765
|
+
|
|
766
|
+
setText({
|
|
767
|
+
..._text.current,
|
|
768
|
+
events: {
|
|
769
|
+
..._text.current.events,
|
|
770
|
+
drop: { is: true, text: text, drag: getFieldSelection() },
|
|
771
|
+
},
|
|
1007
772
|
})
|
|
1008
773
|
}}
|
|
1009
|
-
// onKeyDown={(event: any) => {
|
|
1010
|
-
// event.preventDefault()
|
|
1011
|
-
// console.log(event)
|
|
1012
|
-
// }}
|
|
1013
774
|
></div>
|
|
1014
775
|
</div>
|
|
1015
776
|
<div className="h-[1px] w-full bg-slate-600"> </div>
|