react-native-enriched 0.1.6 → 0.2.0
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/README.md +3 -9
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +1 -1
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +1 -1
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +0 -45
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +19 -2
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +3 -3
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +36 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +132 -11
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +13 -0
- package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +2 -9
- package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +110 -3
- package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +57 -30
- package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +16 -13
- package/android/src/main/java/com/swmansion/enriched/utils/ResourceManager.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +3 -0
- package/android/src/main/res/drawable/broken_image.xml +10 -0
- package/ios/EnrichedTextInputView.h +3 -0
- package/ios/EnrichedTextInputView.mm +97 -29
- package/ios/config/InputConfig.h +6 -0
- package/ios/config/InputConfig.mm +32 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +0 -45
- package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +20 -4
- package/ios/inputParser/InputParser.mm +147 -24
- package/ios/internals/EnrichedTextInputViewShadowNode.h +1 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +29 -17
- package/ios/styles/BlockQuoteStyle.mm +5 -26
- package/ios/styles/BoldStyle.mm +2 -0
- package/ios/styles/CodeBlockStyle.mm +228 -0
- package/ios/styles/H1Style.mm +1 -0
- package/ios/styles/H2Style.mm +1 -0
- package/ios/styles/H3Style.mm +1 -0
- package/ios/styles/ImageStyle.mm +158 -0
- package/ios/styles/InlineCodeStyle.mm +2 -0
- package/ios/styles/ItalicStyle.mm +2 -0
- package/ios/styles/LinkStyle.mm +8 -0
- package/ios/styles/MentionStyle.mm +133 -36
- package/ios/styles/OrderedListStyle.mm +2 -0
- package/ios/styles/StrikethroughStyle.mm +2 -0
- package/ios/styles/UnderlineStyle.mm +2 -0
- package/ios/styles/UnorderedListStyle.mm +2 -0
- package/ios/utils/BaseStyleProtocol.h +1 -0
- package/ios/utils/ImageData.h +10 -0
- package/ios/utils/ImageData.mm +4 -0
- package/ios/utils/LayoutManagerExtension.mm +118 -3
- package/ios/utils/OccurenceUtils.h +4 -0
- package/ios/utils/OccurenceUtils.mm +47 -0
- package/ios/utils/ParagraphAttributesUtils.h +1 -0
- package/ios/utils/ParagraphAttributesUtils.mm +87 -20
- package/ios/utils/StyleHeaders.h +12 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +22 -10
- package/lib/module/EnrichedTextInput.js +2 -2
- package/lib/module/EnrichedTextInput.js.map +1 -1
- package/lib/module/EnrichedTextInputNativeComponent.ts +6 -5
- package/lib/module/normalizeHtmlStyle.js +0 -4
- package/lib/module/normalizeHtmlStyle.js.map +1 -1
- package/lib/typescript/src/EnrichedTextInput.d.ts +1 -5
- package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +1 -5
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedTextInput.tsx +3 -7
- package/src/EnrichedTextInputNativeComponent.ts +6 -5
- package/src/normalizeHtmlStyle.ts +0 -4
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.swmansion.enriched.styles
|
|
2
2
|
|
|
3
|
-
import android.net.Uri
|
|
4
3
|
import android.text.Editable
|
|
5
4
|
import android.text.Spannable
|
|
6
5
|
import android.text.SpannableStringBuilder
|
|
@@ -11,7 +10,6 @@ import com.swmansion.enriched.spans.EnrichedLinkSpan
|
|
|
11
10
|
import com.swmansion.enriched.spans.EnrichedMentionSpan
|
|
12
11
|
import com.swmansion.enriched.spans.EnrichedSpans
|
|
13
12
|
import com.swmansion.enriched.utils.getSafeSpanBoundaries
|
|
14
|
-
import java.io.File
|
|
15
13
|
|
|
16
14
|
class ParametrizedStyles(private val view: EnrichedTextInputView) {
|
|
17
15
|
private var mentionStart: Int? = null
|
|
@@ -85,7 +83,7 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
|
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
private fun getWordAtIndex(s:
|
|
86
|
+
private fun getWordAtIndex(s: CharSequence, index: Int): TextRange? {
|
|
89
87
|
if (index < 0 ) return null
|
|
90
88
|
|
|
91
89
|
var start = index
|
|
@@ -101,7 +99,7 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
|
|
|
101
99
|
|
|
102
100
|
val result = s.subSequence(start, end).toString()
|
|
103
101
|
|
|
104
|
-
return
|
|
102
|
+
return TextRange(result, start, end)
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
private fun canLinkBeApplied(): Boolean {
|
|
@@ -120,7 +118,7 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
|
|
|
120
118
|
return true
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
private fun afterTextChangedLinks(result:
|
|
121
|
+
private fun afterTextChangedLinks(result: TextRange) {
|
|
124
122
|
// Do not detect link if it's applied manually
|
|
125
123
|
if (isSettingLinkSpan || !canLinkBeApplied()) return
|
|
126
124
|
|
|
@@ -141,55 +139,80 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
|
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
private fun afterTextChangedMentions(
|
|
142
|
+
private fun afterTextChangedMentions(currentWord: TextRange) {
|
|
145
143
|
val mentionHandler = view.mentionHandler ?: return
|
|
146
144
|
val spannable = view.text as Spannable
|
|
147
|
-
val (word, start, end) = result
|
|
148
145
|
|
|
149
146
|
val indicatorsPattern = mentionIndicators.joinToString("|") { Regex.escape(it) }
|
|
150
147
|
val mentionIndicatorRegex = Regex("^($indicatorsPattern)")
|
|
151
148
|
val mentionRegex= Regex("^($indicatorsPattern)\\w*")
|
|
152
149
|
|
|
153
|
-
val spans = spannable.getSpans(start, end, EnrichedMentionSpan::class.java)
|
|
150
|
+
val spans = spannable.getSpans(currentWord.start, currentWord.end, EnrichedMentionSpan::class.java)
|
|
154
151
|
for (span in spans) {
|
|
155
152
|
spannable.removeSpan(span)
|
|
156
153
|
}
|
|
157
154
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
var indicator: String
|
|
156
|
+
var finalStart: Int
|
|
157
|
+
val finalEnd = currentWord.end
|
|
158
|
+
|
|
159
|
+
// No mention in the current word, check previous one
|
|
160
|
+
if (!mentionRegex.matches(currentWord.text)) {
|
|
161
|
+
val previousWord = getWordAtIndex(spannable, currentWord.start - 1)
|
|
161
162
|
|
|
162
|
-
//
|
|
163
|
-
if (
|
|
164
|
-
|
|
163
|
+
// No previous word -> no mention to be detected
|
|
164
|
+
if (previousWord == null) {
|
|
165
|
+
mentionHandler.endMention()
|
|
166
|
+
return
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
|
|
169
|
+
// Previous word is not a mention -> end mention
|
|
170
|
+
if (!mentionRegex.matches(previousWord.text)) {
|
|
171
|
+
mentionHandler.endMention()
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Previous word is a mention -> use it
|
|
176
|
+
finalStart = previousWord.start
|
|
177
|
+
indicator = mentionIndicatorRegex.find(previousWord.text)?.value ?: ""
|
|
168
178
|
} else {
|
|
169
|
-
|
|
179
|
+
// Current word is a mention -> use it
|
|
180
|
+
finalStart = currentWord.start
|
|
181
|
+
indicator = mentionIndicatorRegex.find(currentWord.text)?.value ?: ""
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Extract text without indicator
|
|
185
|
+
val text = spannable.subSequence(finalStart, finalEnd).toString().replaceFirst(indicator, "")
|
|
186
|
+
|
|
187
|
+
// Means we are starting mention
|
|
188
|
+
if (text.isEmpty()) {
|
|
189
|
+
mentionStart = finalStart
|
|
170
190
|
}
|
|
191
|
+
|
|
192
|
+
mentionHandler.onMention(indicator, text)
|
|
171
193
|
}
|
|
172
194
|
|
|
173
|
-
fun setImageSpan(src: String) {
|
|
195
|
+
fun setImageSpan(src: String, width: Float, height: Float) {
|
|
174
196
|
if (view.selection == null) return
|
|
175
|
-
|
|
176
197
|
val spannable = view.text as SpannableStringBuilder
|
|
177
|
-
|
|
178
|
-
val spans = spannable.getSpans(start, end, EnrichedImageSpan::class.java)
|
|
198
|
+
val (start, originalEnd) = view.selection.getInlineSelection()
|
|
179
199
|
|
|
180
|
-
|
|
181
|
-
spannable.removeSpan(s)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (start == end) {
|
|
200
|
+
if (start == originalEnd) {
|
|
185
201
|
spannable.insert(start, "\uFFFC")
|
|
186
|
-
|
|
202
|
+
} else {
|
|
203
|
+
val spans = spannable.getSpans(start, originalEnd, EnrichedImageSpan::class.java)
|
|
204
|
+
for (s in spans) {
|
|
205
|
+
spannable.removeSpan(s)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
spannable.replace(start, originalEnd, "\uFFFC")
|
|
187
209
|
}
|
|
188
210
|
|
|
189
|
-
val
|
|
190
|
-
val span = EnrichedImageSpan(
|
|
191
|
-
|
|
192
|
-
|
|
211
|
+
val (imageStart, imageEnd) = spannable.getSafeSpanBoundaries(start, start + 1)
|
|
212
|
+
val span = EnrichedImageSpan.createEnrichedImageSpan(src, width.toInt(), height.toInt())
|
|
213
|
+
span.observeAsyncDrawableLoaded(view.text)
|
|
214
|
+
|
|
215
|
+
spannable.setSpan(span, imageStart, imageEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
193
216
|
}
|
|
194
217
|
|
|
195
218
|
fun startMention(indicator: String) {
|
|
@@ -245,4 +268,8 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
|
|
|
245
268
|
val spannable = view.text as Spannable
|
|
246
269
|
return removeSpansForRange(spannable, start, end, config.clazz)
|
|
247
270
|
}
|
|
271
|
+
|
|
272
|
+
companion object {
|
|
273
|
+
data class TextRange(val text: String, val start: Int, val end: Int)
|
|
274
|
+
}
|
|
248
275
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
package com.swmansion.enriched.utils
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.res.Resources
|
|
5
|
+
import android.graphics.BitmapFactory
|
|
6
|
+
import android.graphics.Canvas
|
|
7
|
+
import android.graphics.Color
|
|
8
|
+
import android.graphics.ColorFilter
|
|
9
|
+
import android.graphics.PixelFormat
|
|
10
|
+
import android.graphics.drawable.Drawable
|
|
11
|
+
import android.os.Handler
|
|
12
|
+
import android.os.Looper
|
|
13
|
+
import android.util.Log
|
|
14
|
+
import androidx.core.content.res.ResourcesCompat
|
|
15
|
+
import androidx.core.graphics.drawable.DrawableCompat
|
|
16
|
+
import java.net.URL
|
|
17
|
+
import java.util.concurrent.Executors
|
|
18
|
+
import androidx.core.graphics.drawable.toDrawable
|
|
19
|
+
import com.swmansion.enriched.R
|
|
20
|
+
|
|
21
|
+
class AsyncDrawable (
|
|
22
|
+
private val url: String,
|
|
23
|
+
) : Drawable() {
|
|
24
|
+
private var internalDrawable: Drawable = Color.TRANSPARENT.toDrawable()
|
|
25
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
26
|
+
private val executor = Executors.newSingleThreadExecutor()
|
|
27
|
+
var isLoaded = false
|
|
28
|
+
|
|
29
|
+
init {
|
|
30
|
+
internalDrawable.bounds = bounds
|
|
31
|
+
|
|
32
|
+
load()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private fun load() {
|
|
36
|
+
executor.execute {
|
|
37
|
+
try {
|
|
38
|
+
isLoaded = false
|
|
39
|
+
val inputStream = URL(url).openStream()
|
|
40
|
+
val bitmap = BitmapFactory.decodeStream(inputStream)
|
|
41
|
+
|
|
42
|
+
// Switch to Main Thread to update UI
|
|
43
|
+
mainHandler.post {
|
|
44
|
+
if (bitmap != null) {
|
|
45
|
+
val d = bitmap.toDrawable(Resources.getSystem())
|
|
46
|
+
|
|
47
|
+
d.bounds = bounds
|
|
48
|
+
internalDrawable = d
|
|
49
|
+
} else {
|
|
50
|
+
loadPlaceholderImage()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch (e: Exception) {
|
|
54
|
+
Log.e("AsyncDrawable", "Failed to load: $url", e)
|
|
55
|
+
|
|
56
|
+
loadPlaceholderImage()
|
|
57
|
+
} finally {
|
|
58
|
+
isLoaded = true
|
|
59
|
+
onLoaded?.invoke()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private fun loadPlaceholderImage() {
|
|
65
|
+
internalDrawable = ResourceManager.getDrawableResource(R.drawable.broken_image)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override fun draw(canvas: Canvas) {
|
|
69
|
+
internalDrawable.draw(canvas)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override fun setAlpha(alpha: Int) {
|
|
73
|
+
internalDrawable.alpha = alpha
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
override fun setColorFilter(colorFilter: ColorFilter?) {
|
|
77
|
+
internalDrawable.colorFilter = colorFilter
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Deprecated("Deprecated in Java")
|
|
81
|
+
override fun getOpacity(): Int {
|
|
82
|
+
return PixelFormat.TRANSLUCENT
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
|
|
86
|
+
super.setBounds(left, top, right, bottom)
|
|
87
|
+
internalDrawable.setBounds(left, top, right, bottom)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var onLoaded: (() -> Unit)? = null
|
|
91
|
+
}
|
|
@@ -275,7 +275,16 @@ public class EnrichedParser {
|
|
|
275
275
|
if (style[j] instanceof EnrichedImageSpan) {
|
|
276
276
|
out.append("<img src=\"");
|
|
277
277
|
out.append(((EnrichedImageSpan) style[j]).getSource());
|
|
278
|
-
out.append("\"
|
|
278
|
+
out.append("\"");
|
|
279
|
+
|
|
280
|
+
out.append(" width=\"");
|
|
281
|
+
out.append(((EnrichedImageSpan) style[j]).getWidth());
|
|
282
|
+
out.append("\"");
|
|
283
|
+
|
|
284
|
+
out.append(" height=\"");
|
|
285
|
+
out.append(((EnrichedImageSpan) style[j]).getHeight());
|
|
286
|
+
|
|
287
|
+
out.append("\"/>");
|
|
279
288
|
// Don't output the placeholder character underlying the image.
|
|
280
289
|
i = next;
|
|
281
290
|
}
|
|
@@ -412,7 +421,6 @@ class HtmlToSpannedConverter implements ContentHandler {
|
|
|
412
421
|
}
|
|
413
422
|
|
|
414
423
|
private void handleStartTag(String tag, Attributes attributes) {
|
|
415
|
-
isEmptyTag = false;
|
|
416
424
|
if (tag.equalsIgnoreCase("br")) {
|
|
417
425
|
// We don't need to handle this. TagSoup will ensure that there's a </br> for each <br>
|
|
418
426
|
// so we can safely emit the linebreaks when we handle the close tag.
|
|
@@ -454,7 +462,7 @@ class HtmlToSpannedConverter implements ContentHandler {
|
|
|
454
462
|
} else if (tag.equalsIgnoreCase("h3")) {
|
|
455
463
|
startHeading(mSpannableStringBuilder, 3);
|
|
456
464
|
} else if (tag.equalsIgnoreCase("img")) {
|
|
457
|
-
startImg(mSpannableStringBuilder, attributes, mImageGetter
|
|
465
|
+
startImg(mSpannableStringBuilder, attributes, mImageGetter);
|
|
458
466
|
} else if (tag.equalsIgnoreCase("code")) {
|
|
459
467
|
start(mSpannableStringBuilder, new Code());
|
|
460
468
|
} else if (tag.equalsIgnoreCase("mention")) {
|
|
@@ -679,20 +687,15 @@ class HtmlToSpannedConverter implements ContentHandler {
|
|
|
679
687
|
}
|
|
680
688
|
}
|
|
681
689
|
|
|
682
|
-
private static void startImg(Editable text, Attributes attributes, EnrichedParser.ImageGetter img
|
|
690
|
+
private static void startImg(Editable text, Attributes attributes, EnrichedParser.ImageGetter img) {
|
|
683
691
|
String src = attributes.getValue("", "src");
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
d = img.getDrawable(src);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
if (d == null) {
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
+
String width = attributes.getValue("", "width");
|
|
693
|
+
String height = attributes.getValue("", "height");
|
|
692
694
|
|
|
693
695
|
int len = text.length();
|
|
696
|
+
EnrichedImageSpan span = EnrichedImageSpan.Companion.createEnrichedImageSpan(src, Integer.parseInt(width), Integer.parseInt(height));
|
|
694
697
|
text.append("");
|
|
695
|
-
text.setSpan(
|
|
698
|
+
text.setSpan(span, len, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
696
699
|
}
|
|
697
700
|
|
|
698
701
|
private static void startA(Editable text, Attributes attributes) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package com.swmansion.enriched.utils
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.res.Resources
|
|
6
|
+
import android.graphics.drawable.Drawable
|
|
7
|
+
import androidx.core.content.res.ResourcesCompat
|
|
8
|
+
import androidx.core.graphics.drawable.DrawableCompat
|
|
9
|
+
|
|
10
|
+
object ResourceManager {
|
|
11
|
+
private var appContext: Context? = null
|
|
12
|
+
|
|
13
|
+
fun init(context: Context) {
|
|
14
|
+
this.appContext = context.applicationContext
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@SuppressLint("UseCompatLoadingForDrawables")
|
|
18
|
+
fun getDrawableResource(id: Int): Drawable {
|
|
19
|
+
val context = appContext ?: throw IllegalStateException("ResourceManager not initialized! Call init() first.")
|
|
20
|
+
|
|
21
|
+
val image = ResourcesCompat.getDrawable(context.resources, id, null)
|
|
22
|
+
val finalImage = image ?: Resources.getSystem().getDrawable(android.R.drawable.ic_menu_report_image)
|
|
23
|
+
|
|
24
|
+
return DrawableCompat.wrap(finalImage)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -52,6 +52,9 @@ class EnrichedSpanWatcher(private val view: EnrichedTextInputView) : SpanWatcher
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
fun emitEvent(s: Spannable, what: Any?) {
|
|
55
|
+
// Do not parse spannable and emit event if onChangeHtml is not provided
|
|
56
|
+
if (!view.shouldEmitHtml) return
|
|
57
|
+
|
|
55
58
|
// Emit event only if we change one of ours spans
|
|
56
59
|
if (what != null && what !is EnrichedSpan) return
|
|
57
60
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:width="24dp"
|
|
3
|
+
android:height="24dp"
|
|
4
|
+
android:viewportWidth="960"
|
|
5
|
+
android:viewportHeight="960"
|
|
6
|
+
android:tint="?attr/colorControlNormal">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="@android:color/white"
|
|
9
|
+
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM240,503L400,343L560,503L720,343L760,383L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,463L240,503ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,496L720,456L560,616L400,456L240,616L200,576L200,760Q200,760 200,760Q200,760 200,760ZM200,760L200,760Q200,760 200,760Q200,760 200,760L200,496L200,576L200,463L200,383L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,463L200,463L200,576L200,576L200,760Q200,760 200,760Q200,760 200,760Z"/>
|
|
10
|
+
</vector>
|
|
@@ -18,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
18
18
|
@public InputParser *parser;
|
|
19
19
|
@public NSMutableDictionary<NSAttributedStringKey, id> *defaultTypingAttributes;
|
|
20
20
|
@public NSDictionary<NSNumber *, id<BaseStyleProtocol>> *stylesDict;
|
|
21
|
+
NSDictionary<NSNumber *, NSArray<NSNumber *> *> *conflictingStyles;
|
|
22
|
+
NSDictionary<NSNumber *, NSArray<NSNumber *> *> *blockingStyles;
|
|
21
23
|
@public BOOL blockEmitting;
|
|
22
24
|
}
|
|
23
25
|
- (CGSize)measureSize:(CGFloat)maxWidth;
|
|
@@ -25,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
25
27
|
- (void)emitOnMentionEvent:(NSString *)indicator text:(nullable NSString *)text;
|
|
26
28
|
- (void)anyTextMayHaveBeenModified;
|
|
27
29
|
- (BOOL)handleStyleBlocksAndConflicts:(StyleType)type range:(NSRange)range;
|
|
30
|
+
- (NSArray<NSNumber *> *)getPresentStyleTypesFrom:(NSArray<NSNumber *> *)types range:(NSRange)range;
|
|
28
31
|
@end
|
|
29
32
|
|
|
30
33
|
NS_ASSUME_NONNULL_END
|
|
@@ -26,8 +26,6 @@ using namespace facebook::react;
|
|
|
26
26
|
EnrichedTextInputViewShadowNode::ConcreteState::Shared _state;
|
|
27
27
|
int _componentViewHeightUpdateCounter;
|
|
28
28
|
NSMutableSet<NSNumber *> *_activeStyles;
|
|
29
|
-
NSDictionary<NSNumber *, NSArray<NSNumber *> *> *_conflictingStyles;
|
|
30
|
-
NSDictionary<NSNumber *, NSArray<NSNumber *> *> *_blockingStyles;
|
|
31
29
|
LinkData *_recentlyActiveLinkData;
|
|
32
30
|
NSRange _recentlyActiveLinkRange;
|
|
33
31
|
NSString *_recentlyEmittedString;
|
|
@@ -95,10 +93,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
95
93
|
@([H3Style getStyleType]): [[H3Style alloc] initWithInput:self],
|
|
96
94
|
@([UnorderedListStyle getStyleType]): [[UnorderedListStyle alloc] initWithInput:self],
|
|
97
95
|
@([OrderedListStyle getStyleType]): [[OrderedListStyle alloc] initWithInput:self],
|
|
98
|
-
@([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self]
|
|
96
|
+
@([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self],
|
|
97
|
+
@([CodeBlockStyle getStyleType]): [[CodeBlockStyle alloc] initWithInput:self],
|
|
98
|
+
@([ImageStyle getStyleType]): [[ImageStyle alloc] initWithInput:self]
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
conflictingStyles = @{
|
|
102
102
|
@([BoldStyle getStyleType]) : @[],
|
|
103
103
|
@([ItalicStyle getStyleType]) : @[],
|
|
104
104
|
@([UnderlineStyle getStyleType]) : @[],
|
|
@@ -106,28 +106,33 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
106
106
|
@([InlineCodeStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])],
|
|
107
107
|
@([LinkStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType]), @([MentionStyle getStyleType])],
|
|
108
108
|
@([MentionStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType])],
|
|
109
|
-
@([H1Style getStyleType]): @[@([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
|
|
110
|
-
@([H2Style getStyleType]): @[@([H1Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
|
|
111
|
-
@([H3Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
|
|
112
|
-
@([UnorderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
|
|
113
|
-
@([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])],
|
|
114
|
-
@([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType])]
|
|
109
|
+
@([H1Style getStyleType]): @[@([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
|
|
110
|
+
@([H2Style getStyleType]): @[@([H1Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
|
|
111
|
+
@([H3Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
|
|
112
|
+
@([UnorderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
|
|
113
|
+
@([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])],
|
|
114
|
+
@([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([CodeBlockStyle getStyleType])],
|
|
115
|
+
@([CodeBlockStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]),
|
|
116
|
+
@([BoldStyle getStyleType]), @([ItalicStyle getStyleType]), @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]), @([MentionStyle getStyleType]), @([LinkStyle getStyleType])],
|
|
117
|
+
@([ImageStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])]
|
|
115
118
|
};
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
@([BoldStyle getStyleType]) : @[],
|
|
119
|
-
@([ItalicStyle getStyleType]) : @[],
|
|
120
|
-
@([UnderlineStyle getStyleType]) : @[],
|
|
121
|
-
@([StrikethroughStyle getStyleType]) : @[],
|
|
122
|
-
@([InlineCodeStyle getStyleType]) : @[],
|
|
123
|
-
@([LinkStyle getStyleType]): @[],
|
|
124
|
-
@([MentionStyle getStyleType]): @[],
|
|
120
|
+
blockingStyles = @{
|
|
121
|
+
@([BoldStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
|
|
122
|
+
@([ItalicStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
|
|
123
|
+
@([UnderlineStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
|
|
124
|
+
@([StrikethroughStyle getStyleType]) : @[@([CodeBlockStyle getStyleType])],
|
|
125
|
+
@([InlineCodeStyle getStyleType]) : @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
|
|
126
|
+
@([LinkStyle getStyleType]): @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
|
|
127
|
+
@([MentionStyle getStyleType]): @[@([CodeBlockStyle getStyleType]), @([ImageStyle getStyleType])],
|
|
125
128
|
@([H1Style getStyleType]): @[],
|
|
126
129
|
@([H2Style getStyleType]): @[],
|
|
127
130
|
@([H3Style getStyleType]): @[],
|
|
128
131
|
@([UnorderedListStyle getStyleType]): @[],
|
|
129
132
|
@([OrderedListStyle getStyleType]): @[],
|
|
130
133
|
@([BlockQuoteStyle getStyleType]): @[],
|
|
134
|
+
@([CodeBlockStyle getStyleType]): @[],
|
|
135
|
+
@([ImageStyle getStyleType]) : @[@([InlineCodeStyle getStyleType])]
|
|
131
136
|
};
|
|
132
137
|
|
|
133
138
|
parser = [[InputParser alloc] initWithInput:self];
|
|
@@ -347,6 +352,25 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
347
352
|
}
|
|
348
353
|
}
|
|
349
354
|
|
|
355
|
+
if(newViewProps.htmlStyle.codeblock.color != oldViewProps.htmlStyle.codeblock.color) {
|
|
356
|
+
if(isColorMeaningful(newViewProps.htmlStyle.codeblock.color)) {
|
|
357
|
+
[newConfig setCodeBlockFgColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.codeblock.color)];
|
|
358
|
+
stylePropChanged = YES;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if(newViewProps.htmlStyle.codeblock.backgroundColor != oldViewProps.htmlStyle.codeblock.backgroundColor) {
|
|
363
|
+
if(isColorMeaningful(newViewProps.htmlStyle.codeblock.backgroundColor)) {
|
|
364
|
+
[newConfig setCodeBlockBgColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.codeblock.backgroundColor)];
|
|
365
|
+
stylePropChanged = YES;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if(newViewProps.htmlStyle.codeblock.borderRadius != oldViewProps.htmlStyle.codeblock.borderRadius) {
|
|
370
|
+
[newConfig setCodeBlockBorderRadius:newViewProps.htmlStyle.codeblock.borderRadius];
|
|
371
|
+
stylePropChanged = YES;
|
|
372
|
+
}
|
|
373
|
+
|
|
350
374
|
if(newViewProps.htmlStyle.a.textDecorationLine != oldViewProps.htmlStyle.a.textDecorationLine) {
|
|
351
375
|
NSString *objcString = [NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
|
|
352
376
|
if([objcString isEqualToString:DecorationUnderline]) {
|
|
@@ -512,9 +536,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
512
536
|
_emitHtml = newViewProps.isOnChangeHtmlSet;
|
|
513
537
|
|
|
514
538
|
[super updateProps:props oldProps:oldProps];
|
|
515
|
-
//
|
|
539
|
+
// run the changes callback
|
|
516
540
|
[self anyTextMayHaveBeenModified];
|
|
517
|
-
[self tryUpdatingHeight];
|
|
518
541
|
|
|
519
542
|
// autofocus - needs to be done at the very end
|
|
520
543
|
if(isFirstMount && newViewProps.autoFocus) {
|
|
@@ -575,7 +598,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
575
598
|
options: NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
|
576
599
|
context: nullptr
|
|
577
600
|
];
|
|
578
|
-
|
|
601
|
+
|
|
579
602
|
return CGSizeMake(maxWidth, ceil(boundingBox.size.height));
|
|
580
603
|
}
|
|
581
604
|
|
|
@@ -702,8 +725,8 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
702
725
|
.isUnorderedList = [_activeStyles containsObject: @([UnorderedListStyle getStyleType])],
|
|
703
726
|
.isOrderedList = [_activeStyles containsObject: @([OrderedListStyle getStyleType])],
|
|
704
727
|
.isBlockQuote = [_activeStyles containsObject: @([BlockQuoteStyle getStyleType])],
|
|
705
|
-
.isCodeBlock =
|
|
706
|
-
.isImage =
|
|
728
|
+
.isCodeBlock = [_activeStyles containsObject: @([CodeBlockStyle getStyleType])],
|
|
729
|
+
.isImage = [_activeStyles containsObject: @([ImageStyle getStyleType])],
|
|
707
730
|
});
|
|
708
731
|
}
|
|
709
732
|
}
|
|
@@ -771,6 +794,14 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
771
794
|
[self toggleParagraphStyle:[OrderedListStyle getStyleType]];
|
|
772
795
|
} else if([commandName isEqualToString:@"toggleBlockQuote"]) {
|
|
773
796
|
[self toggleParagraphStyle:[BlockQuoteStyle getStyleType]];
|
|
797
|
+
} else if([commandName isEqualToString:@"toggleCodeBlock"]) {
|
|
798
|
+
[self toggleParagraphStyle:[CodeBlockStyle getStyleType]];
|
|
799
|
+
} else if([commandName isEqualToString:@"addImage"]) {
|
|
800
|
+
NSString *uri = (NSString *)args[0];
|
|
801
|
+
CGFloat imgWidth = [(NSNumber*)args[1] floatValue];
|
|
802
|
+
CGFloat imgHeight = [(NSNumber*)args[2] floatValue];
|
|
803
|
+
|
|
804
|
+
[self addImage:uri width:imgWidth height:imgHeight];
|
|
774
805
|
}
|
|
775
806
|
}
|
|
776
807
|
|
|
@@ -917,11 +948,22 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
917
948
|
}
|
|
918
949
|
}
|
|
919
950
|
|
|
951
|
+
- (void)addImage:(NSString *)uri width:(float)width height:(float)height
|
|
952
|
+
{
|
|
953
|
+
ImageStyle *imageStyleClass = (ImageStyle *)stylesDict[@([ImageStyle getStyleType])];
|
|
954
|
+
if(imageStyleClass == nullptr) { return; }
|
|
955
|
+
|
|
956
|
+
if([self handleStyleBlocksAndConflicts:[ImageStyle getStyleType] range:textView.selectedRange]) {
|
|
957
|
+
[imageStyleClass addImage:uri width:width height:height];
|
|
958
|
+
[self anyTextMayHaveBeenModified];
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
920
962
|
- (void)startMentionWithIndicator:(NSString *)indicator {
|
|
921
963
|
MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
|
|
922
964
|
if(mentionStyleClass == nullptr) { return; }
|
|
923
965
|
|
|
924
|
-
if([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType] range:
|
|
966
|
+
if([self handleStyleBlocksAndConflicts:[MentionStyle getStyleType] range:textView.selectedRange]) {
|
|
925
967
|
[mentionStyleClass startMentionWithIndicator:indicator];
|
|
926
968
|
[self anyTextMayHaveBeenModified];
|
|
927
969
|
}
|
|
@@ -930,13 +972,13 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
930
972
|
// returns false when style shouldn't be applied and true when it can be
|
|
931
973
|
- (BOOL)handleStyleBlocksAndConflicts:(StyleType)type range:(NSRange)range {
|
|
932
974
|
// handle blocking styles: if any is present we do not apply the toggled style
|
|
933
|
-
NSArray<NSNumber *> *blocking = [self getPresentStyleTypesFrom:
|
|
975
|
+
NSArray<NSNumber *> *blocking = [self getPresentStyleTypesFrom: blockingStyles[@(type)] range:range];
|
|
934
976
|
if(blocking.count != 0) {
|
|
935
977
|
return NO;
|
|
936
978
|
}
|
|
937
979
|
|
|
938
980
|
// handle conflicting styles: all of their occurences have to be removed
|
|
939
|
-
NSArray<NSNumber *> *conflicting = [self getPresentStyleTypesFrom:
|
|
981
|
+
NSArray<NSNumber *> *conflicting = [self getPresentStyleTypesFrom: conflictingStyles[@(type)] range:range];
|
|
940
982
|
if(conflicting.count != 0) {
|
|
941
983
|
for(NSNumber *style in conflicting) {
|
|
942
984
|
id<BaseStyleProtocol> styleClass = stylesDict[style];
|
|
@@ -986,7 +1028,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
986
1028
|
MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
|
|
987
1029
|
if(mentionStyleClass != nullptr) {
|
|
988
1030
|
[mentionStyleClass manageMentionTypingAttributes];
|
|
989
|
-
|
|
1031
|
+
|
|
1032
|
+
// mention editing runs if only a selection was done (no text change)
|
|
1033
|
+
// otherwise we would double-emit with a second call in the anyTextMayHaveBeenModified method
|
|
1034
|
+
if([_recentlyEmittedString isEqualToString:[textView.textStorage.string copy]]) {
|
|
1035
|
+
[mentionStyleClass manageMentionEditing];
|
|
1036
|
+
}
|
|
990
1037
|
}
|
|
991
1038
|
|
|
992
1039
|
// typing attributes for empty lines selection reset
|
|
@@ -1012,6 +1059,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
1012
1059
|
- (void)handleWordModificationBasedChanges:(NSString*)word inRange:(NSRange)range {
|
|
1013
1060
|
// manual links refreshing and automatic links detection handling
|
|
1014
1061
|
LinkStyle* linkStyle = [stylesDict objectForKey:@([LinkStyle getStyleType])];
|
|
1062
|
+
|
|
1015
1063
|
if(linkStyle != nullptr) {
|
|
1016
1064
|
// manual links need to be handled first because they can block automatic links after being refreshed
|
|
1017
1065
|
[linkStyle handleManualLinks:word inRange:range];
|
|
@@ -1046,6 +1094,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
1046
1094
|
[bqStyle manageBlockquoteColor];
|
|
1047
1095
|
}
|
|
1048
1096
|
|
|
1097
|
+
// codeblock font and color management
|
|
1098
|
+
CodeBlockStyle *codeBlockStyle = stylesDict[@([CodeBlockStyle getStyleType])];
|
|
1099
|
+
if(codeBlockStyle != nullptr) {
|
|
1100
|
+
[codeBlockStyle manageCodeBlockFontAndColor];
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1049
1103
|
// improper headings fix
|
|
1050
1104
|
H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
|
|
1051
1105
|
H2Style *h2Style = stylesDict[@([H2Style getStyleType])];
|
|
@@ -1056,10 +1110,11 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
1056
1110
|
[h3Style handleImproperHeadings];
|
|
1057
1111
|
}
|
|
1058
1112
|
|
|
1059
|
-
// mentions removal
|
|
1113
|
+
// mentions management: removal and editing
|
|
1060
1114
|
MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
|
|
1061
1115
|
if(mentionStyleClass != nullptr) {
|
|
1062
1116
|
[mentionStyleClass handleExistingMentions];
|
|
1117
|
+
[mentionStyleClass manageMentionEditing];
|
|
1063
1118
|
}
|
|
1064
1119
|
|
|
1065
1120
|
// placholder management
|
|
@@ -1127,6 +1182,12 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
1127
1182
|
[self->textView.layoutManager invalidateLayoutForCharacterRange:wholeRange actualCharacterRange:&actualRange];
|
|
1128
1183
|
[self->textView.layoutManager ensureLayoutForCharacterRange:actualRange];
|
|
1129
1184
|
[self->textView.layoutManager invalidateDisplayForCharacterRange:wholeRange];
|
|
1185
|
+
|
|
1186
|
+
// We have to explicitly set contentSize
|
|
1187
|
+
// That way textView knows if content overflows and if should be scrollable
|
|
1188
|
+
// We recall measureSize here because value returned from previous measureSize may not be up-to date at that point
|
|
1189
|
+
CGSize measuredSize = [self measureSize:self->textView.frame.size.width];
|
|
1190
|
+
self->textView.contentSize = measuredSize;
|
|
1130
1191
|
});
|
|
1131
1192
|
}
|
|
1132
1193
|
|
|
@@ -1171,6 +1232,7 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
1171
1232
|
UnorderedListStyle *uStyle = stylesDict[@([UnorderedListStyle getStyleType])];
|
|
1172
1233
|
OrderedListStyle *oStyle = stylesDict[@([OrderedListStyle getStyleType])];
|
|
1173
1234
|
BlockQuoteStyle *bqStyle = stylesDict[@([BlockQuoteStyle getStyleType])];
|
|
1235
|
+
CodeBlockStyle *cbStyle = stylesDict[@([CodeBlockStyle getStyleType])];
|
|
1174
1236
|
LinkStyle *linkStyle = stylesDict[@([LinkStyle getStyleType])];
|
|
1175
1237
|
MentionStyle *mentionStyle = stylesDict[@([MentionStyle getStyleType])];
|
|
1176
1238
|
H1Style *h1Style = stylesDict[@([H1Style getStyleType])];
|
|
@@ -1186,13 +1248,19 @@ Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
|
1186
1248
|
[oStyle handleBackspaceInRange:range replacementText:text] ||
|
|
1187
1249
|
[oStyle tryHandlingListShorcutInRange:range replacementText:text] ||
|
|
1188
1250
|
[bqStyle handleBackspaceInRange:range replacementText:text] ||
|
|
1251
|
+
[cbStyle handleBackspaceInRange:range replacementText:text] ||
|
|
1189
1252
|
[linkStyle handleLeadingLinkReplacement:range replacementText:text] ||
|
|
1190
1253
|
[mentionStyle handleLeadingMentionReplacement:range replacementText:text] ||
|
|
1191
1254
|
[h1Style handleNewlinesInRange:range replacementText:text] ||
|
|
1192
1255
|
[h2Style handleNewlinesInRange:range replacementText:text] ||
|
|
1193
1256
|
[h3Style handleNewlinesInRange:range replacementText:text] ||
|
|
1194
1257
|
[ZeroWidthSpaceUtils handleBackspaceInRange:range replacementText:text input:self] ||
|
|
1195
|
-
[ParagraphAttributesUtils handleBackspaceInRange:range replacementText:text input:self]
|
|
1258
|
+
[ParagraphAttributesUtils handleBackspaceInRange:range replacementText:text input:self] ||
|
|
1259
|
+
// CRITICAL: This callback HAS TO be always evaluated last.
|
|
1260
|
+
//
|
|
1261
|
+
// This function is the "Generic Fallback": if no specific style claims the backspace action
|
|
1262
|
+
// to change its state, only then do we proceed to physically delete the newline and merge paragraphs.
|
|
1263
|
+
[ParagraphAttributesUtils handleNewlineBackspaceInRange:range replacementText:text input:self]
|
|
1196
1264
|
) {
|
|
1197
1265
|
[self anyTextMayHaveBeenModified];
|
|
1198
1266
|
return NO;
|
package/ios/config/InputConfig.h
CHANGED
|
@@ -64,4 +64,10 @@
|
|
|
64
64
|
- (void)setLinkDecorationLine:(TextDecorationLineEnum)newValue;
|
|
65
65
|
- (void)setMentionStyleProps:(NSDictionary *)newValue;
|
|
66
66
|
- (MentionStyleProps *)mentionStylePropsForIndicator:(NSString *)indicator;
|
|
67
|
+
- (UIColor *)codeBlockFgColor;
|
|
68
|
+
- (void)setCodeBlockFgColor:(UIColor *)newValue;
|
|
69
|
+
- (UIColor *)codeBlockBgColor;
|
|
70
|
+
- (void)setCodeBlockBgColor:(UIColor *)newValue;
|
|
71
|
+
- (CGFloat)codeBlockBorderRadius;
|
|
72
|
+
- (void)setCodeBlockBorderRadius:(CGFloat)newValue;
|
|
67
73
|
@end
|