@sikka/hawa 0.0.232 → 0.0.233

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sikka/hawa",
3
- "version": "0.0.232",
3
+ "version": "0.0.233",
4
4
  "description": "SaaS Oriented UI Kit",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.es.js",
@@ -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",
@@ -29,94 +27,98 @@ const styleClasses = {
29
27
  export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
30
28
  props
31
29
  ) => {
32
- const [text, setText] = useState({
30
+ const [text, _setText] = useState({
33
31
  content: "",
34
32
  stylings: [], // A styling is an object with 2 indices specifying a substring of text, and the applied effect
35
33
  revert: [0, 0],
36
34
  })
37
35
 
38
36
  const field = useRef(null)
37
+ const _text = useRef(text)
38
+ const setText = (data) => {
39
+ _text.current = data
40
+ _setText(data)
41
+ }
39
42
 
43
+ // Full reversion achieved !
40
44
  const getFieldSelection = () => {
41
45
  if (document.activeElement != field.current) return [0, 0]
42
46
 
43
47
  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
- }
48
+ let nodes = Array.from(field.current.childNodes)
49
+ nodes = nodes.filter(
50
+ (item: any) => !["#text", "BR"].includes(item.nodeName)
51
+ )
55
52
 
56
- return [start, end]
53
+ let startParent: any = selection.anchorNode.parentNode
54
+ let startNodeIndex =
55
+ startParent == field.current
56
+ ? nodes.length
57
+ : parseInt(startParent.dataset.childIndex)
58
+
59
+ let startPrecedingSum = nodes
60
+ .slice(0, startNodeIndex)
61
+ .map((span: any) => span.textContent.length)
62
+ .reduce((a, b) => a + b, 0)
63
+
64
+ let endParent: any = selection.anchorNode.parentNode
65
+ let endNodeIndex =
66
+ endParent == field.current
67
+ ? nodes.length
68
+ : parseInt(endParent.dataset.childIndex)
69
+
70
+ let endPrecedingSum = nodes
71
+ .slice(0, endNodeIndex)
72
+ .map((span: any) => span.textContent.length)
73
+ .reduce((a, b) => a + b, 0)
74
+
75
+ let result = [
76
+ startPrecedingSum + selection.anchorOffset,
77
+ endPrecedingSum + selection.focusOffset,
78
+ ]
79
+
80
+ // Sort to make the minimum selection the start selection
81
+ return result.sort((a, b) => a - b)
57
82
  }
58
83
 
59
84
  useEffect(() => {
60
- // return
61
- let [start, end] = text.revert
62
-
63
- if (start == 0 && end == 0) return
85
+ setTimeout(function () {
86
+ let [start, end] = text.revert
64
87
 
65
- let oldStart = start
66
- let oldEnd = end
88
+ if (start == 0 && end == 0) return
67
89
 
68
- let [newStart, newEnd] = getFieldSelection()
90
+ let startNode = null
91
+ let endNode = null
69
92
 
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
93
+ let total = 0
94
+ let nodes = Array.from(field.current.childNodes)
77
95
 
78
- let startNodeIndex = 0
79
- let endNodeIndex = 0
96
+ for (let i = 0; i < nodes.length; i++) {
97
+ let node: any = nodes[i]
98
+ let sum = node.textContent.length + total
80
99
 
81
- let total = 0
82
- let nodes = Array.from(field.current.childNodes)
83
-
84
- console.log(
85
- `Current nodes: ${nodes.map((e: any) => e.textContent.length).join(", ")}`
86
- )
100
+ if (startNode == null && start >= total && start <= sum) {
101
+ startNode = nodes[i]
102
+ start -= total
103
+ }
87
104
 
88
- for (let i = 0; i < nodes.length; i++) {
89
- let node: any = nodes[i]
90
- let sum = node.textContent.length + total
105
+ if (endNode == null && end > total && end <= sum) {
106
+ endNode = nodes[i]
107
+ end -= total
108
+ }
91
109
 
92
- if (startNode == null && start > total && start <= sum) {
93
- startNode = nodes[i]
94
- startNodeIndex = i
95
- start -= total
110
+ total += node.textContent.length
96
111
  }
97
112
 
98
- if (endNode == null && end > total && end <= sum) {
99
- endNode = nodes[i]
100
- endNodeIndex = i
101
- end -= total
102
- }
103
-
104
- total += node.textContent.length
105
- }
106
-
107
- console.log(
108
- `Reverting (${newStart} -> ${newEnd}) to (${oldStart} - ${start} - [node ${startNodeIndex}] -> ${oldEnd} - ${end} - [node ${endNodeIndex}])`
109
- )
113
+ var range = document.createRange()
114
+ var sel = window.getSelection()
110
115
 
111
- var range = document.createRange()
112
- var sel = window.getSelection()
116
+ range.setStart(startNode.firstChild, start)
117
+ range.setEnd(endNode.firstChild, end)
113
118
 
114
- range.setStart(startNode.firstChild, start)
115
- range.setEnd(endNode.firstChild, end)
116
- // range.collapse(true)
117
-
118
- sel.removeAllRanges()
119
- sel.addRange(range)
119
+ sel.removeAllRanges()
120
+ sel.addRange(range)
121
+ }, 0)
120
122
  }, [text.revert])
121
123
 
122
124
  // utility functions
@@ -309,9 +311,11 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
309
311
  }
310
312
 
311
313
  const onChange = (value) => {
314
+ let [selectionStart, selectionEnd] = getFieldSelection()
315
+
312
316
  let difference = value.length - text.content.length
313
317
 
314
- let selection = field.current.selectionStart - difference
318
+ let selection = selectionStart - difference
315
319
 
316
320
  let stylings = text.stylings.slice()
317
321
  let succeeding = getSucceedStylings(selection)
@@ -350,14 +354,21 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
350
354
 
351
355
  stylings = stylings.filter((_, index) => !spliced.includes(index))
352
356
 
353
- setText({ ...text, content: value, stylings: stylings })
357
+ // if (difference == 1) difference = 0
358
+
359
+ setText({
360
+ ...text,
361
+ content: value,
362
+ stylings: stylings,
363
+ revert: [selectionStart, selectionEnd],
364
+ })
354
365
  }
355
366
 
356
367
  const getContent = () => {
357
- let content = text.content
368
+ let content = _text.current.content
358
369
 
359
370
  // Get all styling indices
360
- let indices = text.stylings
371
+ let indices = _text.current.stylings
361
372
  .map(({ start, finish }) => [start, finish])
362
373
  .flat()
363
374
 
@@ -373,7 +384,7 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
373
384
  if (indices[0] != 0) indices.unshift(0)
374
385
 
375
386
  // Add last index if not present
376
- let last = text.content.length
387
+ let last = content.length
377
388
  if (indices[indices.length - 1] != last) indices.push(last)
378
389
 
379
390
  let result = []
@@ -385,6 +396,24 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
385
396
  return result
386
397
  }
387
398
 
399
+ // dangerouslySetInnerHTML incorrectly renders when the entire text is highlighted, copied, and then pasted in succession
400
+ useEffect(() => {
401
+ let html = getContent()
402
+ .map((_data, index) => {
403
+ let [start, data] = _data
404
+
405
+ // Get stylings encompassing an index within it's range
406
+ let stylings = getIntersectStylings(start)
407
+
408
+ return `<span class="${stylings
409
+ .map((styling) => styleClasses[styling.type])
410
+ .join(" ")}" data-child-index="${index}">${data}</span>`
411
+ })
412
+ .join("")
413
+
414
+ field.current.innerHTML = html
415
+ }, [text])
416
+
388
417
  return (
389
418
  <div className="align-center box-border flex h-min w-[400px] flex-col items-center justify-center rounded bg-blue-300 shadow-md">
390
419
  <div className={clsx("flex w-full flex-row justify-start p-2")}>
@@ -397,19 +426,22 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
397
426
  />
398
427
  <Property
399
428
  name="I"
400
- onClick={() => {
429
+ onMouseDown={(event) => {
430
+ event.preventDefault()
401
431
  perform("italic")
402
432
  }}
403
433
  />
404
434
  <Property
405
435
  name="U"
406
- onClick={() => {
436
+ onMouseDown={(event) => {
437
+ event.preventDefault()
407
438
  perform("under")
408
439
  }}
409
440
  />
410
441
  <Property
411
442
  name="S"
412
- onClick={() => {
443
+ onMouseDown={(event) => {
444
+ event.preventDefault()
413
445
  perform("strike")
414
446
  }}
415
447
  />
@@ -419,106 +451,11 @@ export const FloatingCommentCE: React.FunctionComponent<ComponentTypes> = (
419
451
  <div
420
452
  ref={field}
421
453
  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
454
  className="h-[150px] w-full resize-none border-none p-2 outline-none"
446
- onInput={(event) => {
447
- let target: any = event.target
448
-
449
- let [start, end] = getFieldSelection()
450
-
451
- // console.log(target)
452
- // console.log(target.selectionStart)
453
- // console.log(target.selectionEnd)
454
-
455
- setText({
456
- ...text,
457
- content: target.textContent,
458
- revert: [start, end],
459
- })
455
+ onInput={(event: any) => {
456
+ onChange(event.target.textContent)
460
457
  }}
461
458
  ></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
459
  </div>
523
460
  <div className="h-[1px] w-full bg-slate-600">&nbsp;</div>
524
461
  <button className="my-1 rounded bg-cyan-800 p-2 py-1 text-white">