react-native-enriched 0.4.0 → 0.4.1

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.
@@ -108,6 +108,9 @@ class ParagraphStyles(
108
108
  spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
109
109
  }
110
110
 
111
+ // Removes spans of the given type in the specified range.
112
+ // If the removed span intersects with the range, it will be split and the remaining part will be re-applied after the removal
113
+ // Returns true if any spans were removed, false otherwise
111
114
  private fun <T> removeSpansForRange(
112
115
  spannable: Spannable,
113
116
  start: Int,
@@ -115,21 +118,24 @@ class ParagraphStyles(
115
118
  clazz: Class<T>,
116
119
  ): Boolean {
117
120
  val ssb = spannable as SpannableStringBuilder
118
- var finalStart = start
119
- var finalEnd = end
120
-
121
121
  val spans = ssb.getSpans(start, end, clazz)
122
122
  if (spans.isEmpty()) return false
123
123
 
124
124
  for (span in spans) {
125
- finalStart = ssb.getSpanStart(span).coerceAtMost(finalStart)
126
- finalEnd = ssb.getSpanEnd(span).coerceAtLeast(finalEnd)
127
-
125
+ val spanStart = ssb.getSpanStart(span)
126
+ val spanEnd = ssb.getSpanEnd(span)
128
127
  ssb.removeSpan(span)
129
- }
130
128
 
131
- ssb.removeZWS(finalStart, finalEnd)
129
+ if (spanStart < start) {
130
+ setSpan(ssb, clazz, spanStart, start - 1)
131
+ }
132
132
 
133
+ if (spanEnd > end) {
134
+ setSpan(ssb, clazz, end + 1, spanEnd)
135
+ }
136
+ }
137
+
138
+ ssb.removeZWS(start, end)
133
139
  return true
134
140
  }
135
141
 
@@ -215,11 +221,7 @@ class ParagraphStyles(
215
221
  }
216
222
 
217
223
  val currSpan = currParagraphSpans[0]
218
- val nextSpan = getNextParagraphSpan(s, end, type)
219
-
220
- if (nextSpan == null) {
221
- return
222
- }
224
+ val nextSpan = getNextParagraphSpan(s, end, type) ?: return
223
225
 
224
226
  val newStart = s.getSpanStart(currSpan)
225
227
  val newEnd = s.getSpanEnd(nextSpan)
@@ -341,9 +343,12 @@ class ParagraphStyles(
341
343
  continue
342
344
  }
343
345
 
346
+ // If removing text at the beginning of the line, we want to remove the span for the whole paragraph
344
347
  if (isBackspace) {
345
- endCursorPosition -= 1
348
+ val currentParagraphBounds = s.getParagraphBounds(endCursorPosition)
349
+ removeSpansForRange(s, currentParagraphBounds.first, currentParagraphBounds.second, config.clazz)
346
350
  spanState.setStart(style, null)
351
+ continue
347
352
  } else {
348
353
  s.insert(endCursorPosition, EnrichedConstants.ZWS_STRING)
349
354
  endCursorPosition += 1
@@ -1884,6 +1884,14 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
1884
1884
  return NO;
1885
1885
  }
1886
1886
 
1887
+ // Tapping near a link causes iOS to re-derive typingAttributes from
1888
+ // character attributes after textViewDidChangeSelection returns, undoing
1889
+ // the cleanup in manageSelectionBasedChanges. Strip them again here, right
1890
+ // before insertion, so new text never inherits link styling.
1891
+ if (linkStyle != nullptr) {
1892
+ [linkStyle manageLinkTypingAttributes];
1893
+ }
1894
+
1887
1895
  return YES;
1888
1896
  }
1889
1897
 
@@ -19,6 +19,35 @@
19
19
  return self;
20
20
  }
21
21
 
22
+ - (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer
23
+ proposedLineFragment:(CGRect)lineFrag
24
+ glyphPosition:(CGPoint)position
25
+ characterIndex:(NSUInteger)charIndex {
26
+ CGRect baseBounds = self.bounds;
27
+
28
+ if (!textContainer.layoutManager.textStorage ||
29
+ charIndex >= textContainer.layoutManager.textStorage.length) {
30
+ return baseBounds;
31
+ }
32
+
33
+ UIFont *font =
34
+ [textContainer.layoutManager.textStorage attribute:NSFontAttributeName
35
+ atIndex:charIndex
36
+ effectiveRange:NULL];
37
+ if (!font) {
38
+ return baseBounds;
39
+ }
40
+
41
+ // Extend the layout bounds below the baseline by the font's descender.
42
+ // Without this, a line containing only the attachment has no descender space
43
+ // below the baseline, but adding a text character introduces it — causing
44
+ // the line height to jump. By reserving descender space upfront the line
45
+ // height stays consistent regardless of whether text is present.
46
+ CGFloat descender = font.descender;
47
+ return CGRectMake(baseBounds.origin.x, descender, baseBounds.size.width,
48
+ baseBounds.size.height - descender);
49
+ }
50
+
22
51
  - (void)loadAsync {
23
52
  NSURL *url = [NSURL URLWithString:self.uri];
24
53
  if (!url) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-enriched",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Rich Text Editor component for React Native",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",