react-native-enriched-markdown 0.1.1 → 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 +80 -8
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +17 -2
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +6 -1
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +9 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +6 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +28 -3
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +225 -1
- package/android/src/main/cpp/jni-adapter.cpp +28 -11
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +132 -15
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +1 -16
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +67 -13
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +241 -21
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +279 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +17 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +13 -18
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +23 -24
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +18 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +22 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +16 -9
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +23 -9
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +24 -10
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrikethroughRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/UnderlineRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +8 -17
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +19 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +3 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +32 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +22 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +23 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/LinkLongPressMovementMethod.kt +121 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +58 -56
- package/android/src/main/jni/CMakeLists.txt +1 -13
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +0 -13
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +2 -14
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +3 -0
- package/cpp/parser/MD4CParser.cpp +21 -8
- package/cpp/parser/MD4CParser.hpp +5 -1
- package/cpp/parser/MarkdownASTNode.hpp +2 -0
- package/ios/EnrichedMarkdownText.mm +356 -29
- package/ios/attachments/{ImageAttachment.h → EnrichedMarkdownImageAttachment.h} +1 -1
- package/ios/attachments/{ImageAttachment.m → EnrichedMarkdownImageAttachment.m} +4 -4
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +9 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +6 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +28 -3
- package/ios/generated/EnrichedMarkdownTextSpec/Props.h +225 -1
- package/ios/parser/MarkdownASTNode.h +2 -0
- package/ios/parser/MarkdownParser.h +9 -0
- package/ios/parser/MarkdownParser.mm +31 -2
- package/ios/parser/MarkdownParserBridge.mm +13 -3
- package/ios/renderer/AttributedRenderer.h +2 -0
- package/ios/renderer/AttributedRenderer.m +52 -19
- package/ios/renderer/BlockquoteRenderer.m +7 -6
- package/ios/renderer/CodeBlockRenderer.m +9 -8
- package/ios/renderer/HeadingRenderer.m +31 -24
- package/ios/renderer/ImageRenderer.m +31 -10
- package/ios/renderer/ListItemRenderer.m +51 -39
- package/ios/renderer/ListRenderer.m +21 -18
- package/ios/renderer/ParagraphRenderer.m +27 -16
- package/ios/renderer/RenderContext.h +17 -0
- package/ios/renderer/RenderContext.m +66 -2
- package/ios/renderer/RendererFactory.m +6 -0
- package/ios/renderer/StrikethroughRenderer.h +6 -0
- package/ios/renderer/StrikethroughRenderer.m +40 -0
- package/ios/renderer/UnderlineRenderer.h +6 -0
- package/ios/renderer/UnderlineRenderer.m +39 -0
- package/ios/styles/StyleConfig.h +46 -0
- package/ios/styles/StyleConfig.mm +351 -12
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/CodeBlockBackground.m +4 -9
- package/ios/utils/FontUtils.h +5 -0
- package/ios/utils/FontUtils.m +14 -0
- package/ios/utils/HTMLGenerator.m +21 -7
- package/ios/utils/MarkdownAccessibilityElementBuilder.h +45 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +323 -0
- package/ios/utils/MarkdownExtractor.m +18 -5
- package/ios/utils/ParagraphStyleUtils.h +10 -2
- package/ios/utils/ParagraphStyleUtils.m +57 -2
- package/ios/utils/PasteboardUtils.h +1 -1
- package/ios/utils/PasteboardUtils.m +3 -3
- package/lib/module/EnrichedMarkdownText.js +33 -2
- package/lib/module/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +83 -3
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +58 -14
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownText.d.ts +85 -3
- package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +75 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownText.tsx +133 -5
- package/src/EnrichedMarkdownTextNativeComponent.ts +83 -3
- package/src/index.tsx +5 -2
- package/src/normalizeMarkdownStyle.ts +46 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +0 -9
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +0 -25
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
package com.swmansion.enriched.markdown
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
4
5
|
import android.graphics.Color
|
|
6
|
+
import android.os.Build
|
|
5
7
|
import android.os.Handler
|
|
6
8
|
import android.os.Looper
|
|
7
|
-
import android.text.
|
|
9
|
+
import android.text.Layout
|
|
8
10
|
import android.util.AttributeSet
|
|
9
11
|
import android.util.Log
|
|
10
12
|
import androidx.appcompat.widget.AppCompatTextView
|
|
13
|
+
import androidx.core.view.ViewCompat
|
|
11
14
|
import com.facebook.react.bridge.ReadableMap
|
|
12
15
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
|
+
import com.swmansion.enriched.markdown.accessibility.MarkdownAccessibilityHelper
|
|
17
|
+
import com.swmansion.enriched.markdown.events.LinkLongPressEvent
|
|
13
18
|
import com.swmansion.enriched.markdown.events.LinkPressEvent
|
|
19
|
+
import com.swmansion.enriched.markdown.parser.Md4cFlags
|
|
14
20
|
import com.swmansion.enriched.markdown.parser.Parser
|
|
15
21
|
import com.swmansion.enriched.markdown.renderer.Renderer
|
|
16
22
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
23
|
+
import com.swmansion.enriched.markdown.utils.LinkLongPressMovementMethod
|
|
17
24
|
import com.swmansion.enriched.markdown.utils.createSelectionActionModeCallback
|
|
18
25
|
import java.util.concurrent.Executors
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* EnrichedMarkdownText that handles Markdown parsing and rendering on a background thread.
|
|
22
|
-
*
|
|
29
|
+
* View starts invisible and becomes visible after render completes to avoid layout shift.
|
|
23
30
|
*/
|
|
24
31
|
class EnrichedMarkdownText
|
|
25
32
|
@JvmOverloads
|
|
@@ -31,26 +38,45 @@ class EnrichedMarkdownText
|
|
|
31
38
|
private val parser = Parser.shared
|
|
32
39
|
private val renderer = Renderer()
|
|
33
40
|
private var onLinkPressCallback: ((String) -> Unit)? = null
|
|
41
|
+
private var onLinkLongPressCallback: ((String) -> Unit)? = null
|
|
34
42
|
|
|
35
|
-
// Background processing tools
|
|
36
43
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
37
44
|
private val executor = Executors.newSingleThreadExecutor()
|
|
38
45
|
private var currentRenderId = 0L
|
|
39
46
|
|
|
40
47
|
val layoutManager = EnrichedMarkdownTextLayoutManager(this)
|
|
41
48
|
|
|
49
|
+
// Accessibility helper for TalkBack support
|
|
50
|
+
private val accessibilityHelper = MarkdownAccessibilityHelper(this)
|
|
51
|
+
|
|
42
52
|
var markdownStyle: StyleConfig? = null
|
|
43
53
|
private set
|
|
44
54
|
|
|
45
55
|
var currentMarkdown: String = ""
|
|
46
56
|
private set
|
|
47
57
|
|
|
58
|
+
var md4cFlags: Md4cFlags = Md4cFlags.DEFAULT
|
|
59
|
+
private set
|
|
60
|
+
|
|
61
|
+
private var lastKnownFontScale: Float = context.resources.configuration.fontScale
|
|
62
|
+
private var markdownStyleMap: ReadableMap? = null
|
|
63
|
+
|
|
64
|
+
private var allowFontScaling: Boolean = true
|
|
65
|
+
private var maxFontSizeMultiplier: Float = 0f
|
|
66
|
+
private var allowTrailingMargin: Boolean = false
|
|
67
|
+
|
|
48
68
|
init {
|
|
49
69
|
setBackgroundColor(Color.TRANSPARENT)
|
|
50
70
|
includeFontPadding = false // Must match setIncludePad(false) in MeasurementStore
|
|
51
|
-
movementMethod =
|
|
71
|
+
movementMethod = LinkLongPressMovementMethod.createInstance()
|
|
52
72
|
setTextIsSelectable(true)
|
|
53
73
|
customSelectionActionModeCallback = createSelectionActionModeCallback(this)
|
|
74
|
+
isVerticalScrollBarEnabled = false
|
|
75
|
+
isHorizontalScrollBarEnabled = false
|
|
76
|
+
|
|
77
|
+
// Set up accessibility for TalkBack
|
|
78
|
+
// This enables virtual view hierarchy for semantic navigation (headings, links, etc.)
|
|
79
|
+
ViewCompat.setAccessibilityDelegate(this, accessibilityHelper)
|
|
54
80
|
}
|
|
55
81
|
|
|
56
82
|
fun setMarkdownContent(markdown: String) {
|
|
@@ -60,25 +86,100 @@ class EnrichedMarkdownText
|
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
fun setMarkdownStyle(style: ReadableMap?) {
|
|
63
|
-
|
|
89
|
+
markdownStyleMap = style
|
|
90
|
+
// Register font scaling settings when style is set (view should have ID by now)
|
|
91
|
+
updateMeasurementStoreFontScaling()
|
|
92
|
+
val newStyle = style?.let { StyleConfig(it, context, allowFontScaling, maxFontSizeMultiplier) }
|
|
64
93
|
if (markdownStyle == newStyle) return
|
|
65
|
-
|
|
66
94
|
markdownStyle = newStyle
|
|
95
|
+
updateJustificationMode(newStyle)
|
|
67
96
|
scheduleRender()
|
|
68
97
|
}
|
|
69
98
|
|
|
99
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
100
|
+
super.onConfigurationChanged(newConfig)
|
|
101
|
+
|
|
102
|
+
if (!allowFontScaling) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
val newFontScale = newConfig.fontScale
|
|
107
|
+
if (newFontScale != lastKnownFontScale) {
|
|
108
|
+
lastKnownFontScale = newFontScale
|
|
109
|
+
recreateStyleConfig()
|
|
110
|
+
scheduleRenderIfNeeded()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fun setMd4cFlags(flags: Md4cFlags) {
|
|
115
|
+
if (md4cFlags == flags) return
|
|
116
|
+
md4cFlags = flags
|
|
117
|
+
scheduleRenderIfNeeded()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fun setAllowFontScaling(allow: Boolean) {
|
|
121
|
+
if (allowFontScaling == allow) return
|
|
122
|
+
allowFontScaling = allow
|
|
123
|
+
updateMeasurementStoreFontScaling()
|
|
124
|
+
recreateStyleConfig()
|
|
125
|
+
scheduleRenderIfNeeded()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fun setMaxFontSizeMultiplier(multiplier: Float) {
|
|
129
|
+
if (maxFontSizeMultiplier == multiplier) return
|
|
130
|
+
maxFontSizeMultiplier = multiplier
|
|
131
|
+
updateMeasurementStoreFontScaling()
|
|
132
|
+
recreateStyleConfig()
|
|
133
|
+
scheduleRenderIfNeeded()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fun setAllowTrailingMargin(allow: Boolean) {
|
|
137
|
+
if (allowTrailingMargin == allow) return
|
|
138
|
+
allowTrailingMargin = allow
|
|
139
|
+
scheduleRenderIfNeeded()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private fun updateMeasurementStoreFontScaling() {
|
|
143
|
+
MeasurementStore.updateFontScalingSettings(id, allowFontScaling, maxFontSizeMultiplier)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fun scheduleRenderIfNeeded() {
|
|
147
|
+
if (currentMarkdown.isNotEmpty()) {
|
|
148
|
+
scheduleRender()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private fun recreateStyleConfig() {
|
|
153
|
+
markdownStyleMap?.let { styleMap ->
|
|
154
|
+
markdownStyle = StyleConfig(styleMap, context, allowFontScaling, maxFontSizeMultiplier)
|
|
155
|
+
updateJustificationMode(markdownStyle)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private fun updateJustificationMode(style: StyleConfig?) {
|
|
160
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
161
|
+
justificationMode =
|
|
162
|
+
if (style?.needsJustify == true) {
|
|
163
|
+
Layout.JUSTIFICATION_MODE_INTER_WORD
|
|
164
|
+
} else {
|
|
165
|
+
Layout.JUSTIFICATION_MODE_NONE
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
70
170
|
private fun scheduleRender() {
|
|
71
171
|
val style = markdownStyle ?: return
|
|
72
172
|
val markdown = currentMarkdown
|
|
173
|
+
if (markdown.isEmpty()) return
|
|
174
|
+
|
|
73
175
|
val renderId = ++currentRenderId
|
|
74
|
-
val scheduleStart = System.currentTimeMillis()
|
|
75
176
|
|
|
76
177
|
executor.execute {
|
|
77
178
|
try {
|
|
78
179
|
// 1. Parse Markdown → AST (C++ md4c parser)
|
|
79
180
|
val parseStart = System.currentTimeMillis()
|
|
80
181
|
val ast =
|
|
81
|
-
parser.parseMarkdown(markdown) ?: run {
|
|
182
|
+
parser.parseMarkdown(markdown, md4cFlags) ?: run {
|
|
82
183
|
mainHandler.post { if (renderId == currentRenderId) text = "" }
|
|
83
184
|
return@execute
|
|
84
185
|
}
|
|
@@ -87,7 +188,7 @@ class EnrichedMarkdownText
|
|
|
87
188
|
// 2. Render AST → Spannable
|
|
88
189
|
val renderStart = System.currentTimeMillis()
|
|
89
190
|
renderer.configure(style, context)
|
|
90
|
-
val styledText = renderer.renderDocument(ast, onLinkPressCallback)
|
|
191
|
+
val styledText = renderer.renderDocument(ast, onLinkPressCallback, onLinkLongPressCallback)
|
|
91
192
|
val renderTime = System.currentTimeMillis() - renderStart
|
|
92
193
|
|
|
93
194
|
Log.i(TAG, "┌──────────────────────────────────────────────")
|
|
@@ -96,10 +197,10 @@ class EnrichedMarkdownText
|
|
|
96
197
|
Log.i(TAG, "│ 🎨 Spannable render: ${renderTime}ms → ${styledText.length} styled chars")
|
|
97
198
|
Log.i(TAG, "└──────────────────────────────────────────────")
|
|
98
199
|
|
|
200
|
+
// 3. Apply to view on main thread
|
|
99
201
|
mainHandler.post {
|
|
100
202
|
if (renderId == currentRenderId) {
|
|
101
203
|
applyRenderedText(styledText)
|
|
102
|
-
Log.i(TAG, "✅ Total time to display: ${System.currentTimeMillis() - scheduleStart}ms")
|
|
103
204
|
}
|
|
104
205
|
}
|
|
105
206
|
} catch (e: Exception) {
|
|
@@ -110,12 +211,11 @@ class EnrichedMarkdownText
|
|
|
110
211
|
}
|
|
111
212
|
|
|
112
213
|
private fun applyRenderedText(styledText: CharSequence) {
|
|
113
|
-
// Sets the text. If it's PrecomputedText, the UI thread skips the measure pass.
|
|
114
214
|
text = styledText
|
|
115
215
|
|
|
116
|
-
//
|
|
117
|
-
if (movementMethod !is
|
|
118
|
-
movementMethod =
|
|
216
|
+
// setText on a selectable TextView can reset movementMethod, so re-apply if needed
|
|
217
|
+
if (movementMethod !is LinkLongPressMovementMethod) {
|
|
218
|
+
movementMethod = LinkLongPressMovementMethod.createInstance()
|
|
119
219
|
}
|
|
120
220
|
|
|
121
221
|
// Register ImageSpans from the collector
|
|
@@ -124,12 +224,15 @@ class EnrichedMarkdownText
|
|
|
124
224
|
}
|
|
125
225
|
|
|
126
226
|
layoutManager.invalidateLayout()
|
|
227
|
+
|
|
228
|
+
// Update accessibility items for TalkBack navigation
|
|
229
|
+
accessibilityHelper.invalidateAccessibilityItems()
|
|
127
230
|
}
|
|
128
231
|
|
|
129
232
|
fun setIsSelectable(selectable: Boolean) {
|
|
130
233
|
if (isTextSelectable == selectable) return
|
|
131
234
|
setTextIsSelectable(selectable)
|
|
132
|
-
movementMethod =
|
|
235
|
+
movementMethod = LinkLongPressMovementMethod.createInstance()
|
|
133
236
|
if (!selectable && !isClickable) isClickable = true
|
|
134
237
|
}
|
|
135
238
|
|
|
@@ -143,10 +246,24 @@ class EnrichedMarkdownText
|
|
|
143
246
|
)
|
|
144
247
|
}
|
|
145
248
|
|
|
249
|
+
fun emitOnLinkLongPress(url: String) {
|
|
250
|
+
val reactContext = context as? com.facebook.react.bridge.ReactContext ?: return
|
|
251
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
252
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
253
|
+
|
|
254
|
+
dispatcher?.dispatchEvent(
|
|
255
|
+
LinkLongPressEvent(surfaceId, id, url),
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
146
259
|
fun setOnLinkPressCallback(callback: (String) -> Unit) {
|
|
147
260
|
onLinkPressCallback = callback
|
|
148
261
|
}
|
|
149
262
|
|
|
263
|
+
fun setOnLinkLongPressCallback(callback: (String) -> Unit) {
|
|
264
|
+
onLinkLongPressCallback = callback
|
|
265
|
+
}
|
|
266
|
+
|
|
150
267
|
companion object {
|
|
151
268
|
private const val TAG = "EnrichedMarkdownMeasure"
|
|
152
269
|
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt
CHANGED
|
@@ -1,27 +1,12 @@
|
|
|
1
1
|
package com.swmansion.enriched.markdown
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.bridge.Arguments
|
|
4
|
-
import com.facebook.react.uimanager.StateWrapper
|
|
5
|
-
|
|
6
3
|
class EnrichedMarkdownTextLayoutManager(
|
|
7
4
|
private val view: EnrichedMarkdownText,
|
|
8
5
|
) {
|
|
9
|
-
private var forceHeightRecalculationCounter: Int = 0
|
|
10
|
-
|
|
11
|
-
var stateWrapper: StateWrapper? = null
|
|
12
|
-
|
|
13
6
|
fun invalidateLayout() {
|
|
14
7
|
val text = view.text
|
|
15
8
|
val paint = view.paint
|
|
16
|
-
|
|
17
|
-
val needUpdate = MeasurementStore.store(view.id, text, paint)
|
|
18
|
-
if (!needUpdate) return
|
|
19
|
-
|
|
20
|
-
val counter = forceHeightRecalculationCounter
|
|
21
|
-
forceHeightRecalculationCounter++
|
|
22
|
-
val state = Arguments.createMap()
|
|
23
|
-
state.putInt("forceHeightRecalculationCounter", counter)
|
|
24
|
-
stateWrapper?.updateState(state)
|
|
9
|
+
MeasurementStore.store(view.id, text, paint)
|
|
25
10
|
}
|
|
26
11
|
|
|
27
12
|
fun releaseMeasurementStore() {
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt
CHANGED
|
@@ -3,9 +3,7 @@ package com.swmansion.enriched.markdown
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import com.facebook.react.bridge.ReadableMap
|
|
5
5
|
import com.facebook.react.module.annotations.ReactModule
|
|
6
|
-
import com.facebook.react.uimanager.ReactStylesDiffMap
|
|
7
6
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
8
|
-
import com.facebook.react.uimanager.StateWrapper
|
|
9
7
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
10
8
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
11
9
|
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
@@ -13,7 +11,9 @@ import com.facebook.react.uimanager.annotations.ReactProp
|
|
|
13
11
|
import com.facebook.react.viewmanagers.EnrichedMarkdownTextManagerDelegate
|
|
14
12
|
import com.facebook.react.viewmanagers.EnrichedMarkdownTextManagerInterface
|
|
15
13
|
import com.facebook.yoga.YogaMeasureMode
|
|
14
|
+
import com.swmansion.enriched.markdown.events.LinkLongPressEvent
|
|
16
15
|
import com.swmansion.enriched.markdown.events.LinkPressEvent
|
|
16
|
+
import com.swmansion.enriched.markdown.parser.Md4cFlags
|
|
17
17
|
|
|
18
18
|
@ReactModule(name = EnrichedMarkdownTextManager.NAME)
|
|
19
19
|
class EnrichedMarkdownTextManager :
|
|
@@ -29,21 +29,14 @@ class EnrichedMarkdownTextManager :
|
|
|
29
29
|
|
|
30
30
|
override fun onDropViewInstance(view: EnrichedMarkdownText) {
|
|
31
31
|
super.onDropViewInstance(view)
|
|
32
|
+
MeasurementStore.clearFontScalingSettings(view.id)
|
|
32
33
|
view.layoutManager.releaseMeasurementStore()
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
override fun updateState(
|
|
36
|
-
view: EnrichedMarkdownText,
|
|
37
|
-
props: ReactStylesDiffMap?,
|
|
38
|
-
stateWrapper: StateWrapper?,
|
|
39
|
-
): Any? {
|
|
40
|
-
view.layoutManager.stateWrapper = stateWrapper
|
|
41
|
-
return super.updateState(view, props, stateWrapper)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
36
|
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
|
|
45
37
|
val map = mutableMapOf<String, Any>()
|
|
46
38
|
map.put(LinkPressEvent.EVENT_NAME, mapOf("registrationName" to LinkPressEvent.EVENT_NAME))
|
|
39
|
+
map.put(LinkLongPressEvent.EVENT_NAME, mapOf("registrationName" to LinkLongPressEvent.EVENT_NAME))
|
|
47
40
|
return map
|
|
48
41
|
}
|
|
49
42
|
|
|
@@ -56,6 +49,10 @@ class EnrichedMarkdownTextManager :
|
|
|
56
49
|
emitOnLinkPress(view, url)
|
|
57
50
|
}
|
|
58
51
|
|
|
52
|
+
view?.setOnLinkLongPressCallback { url ->
|
|
53
|
+
emitOnLinkLongPress(view, url)
|
|
54
|
+
}
|
|
55
|
+
|
|
59
56
|
view?.setMarkdownContent(markdown ?: "No markdown content")
|
|
60
57
|
}
|
|
61
58
|
|
|
@@ -67,14 +64,59 @@ class EnrichedMarkdownTextManager :
|
|
|
67
64
|
view?.setMarkdownStyle(style)
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
@ReactProp(name = "
|
|
71
|
-
override fun
|
|
67
|
+
@ReactProp(name = "selectable", defaultBoolean = true)
|
|
68
|
+
override fun setSelectable(
|
|
72
69
|
view: EnrichedMarkdownText?,
|
|
73
70
|
selectable: Boolean,
|
|
74
71
|
) {
|
|
75
72
|
view?.setIsSelectable(selectable)
|
|
76
73
|
}
|
|
77
74
|
|
|
75
|
+
@ReactProp(name = "md4cFlags")
|
|
76
|
+
override fun setMd4cFlags(
|
|
77
|
+
view: EnrichedMarkdownText?,
|
|
78
|
+
flags: ReadableMap?,
|
|
79
|
+
) {
|
|
80
|
+
val md4cFlags =
|
|
81
|
+
Md4cFlags(
|
|
82
|
+
underline = flags?.getBoolean("underline") ?: false,
|
|
83
|
+
)
|
|
84
|
+
view?.setMd4cFlags(md4cFlags)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@ReactProp(name = "allowFontScaling", defaultBoolean = true)
|
|
88
|
+
override fun setAllowFontScaling(
|
|
89
|
+
view: EnrichedMarkdownText?,
|
|
90
|
+
allowFontScaling: Boolean,
|
|
91
|
+
) {
|
|
92
|
+
view?.setAllowFontScaling(allowFontScaling)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@ReactProp(name = "maxFontSizeMultiplier", defaultFloat = 0f)
|
|
96
|
+
override fun setMaxFontSizeMultiplier(
|
|
97
|
+
view: EnrichedMarkdownText?,
|
|
98
|
+
maxFontSizeMultiplier: Float,
|
|
99
|
+
) {
|
|
100
|
+
view?.setMaxFontSizeMultiplier(maxFontSizeMultiplier)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@ReactProp(name = "allowTrailingMargin", defaultBoolean = false)
|
|
104
|
+
override fun setAllowTrailingMargin(
|
|
105
|
+
view: EnrichedMarkdownText?,
|
|
106
|
+
allowTrailingMargin: Boolean,
|
|
107
|
+
) {
|
|
108
|
+
view?.setAllowTrailingMargin(allowTrailingMargin)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@ReactProp(name = "enableLinkPreview", defaultBoolean = true)
|
|
112
|
+
override fun setEnableLinkPreview(
|
|
113
|
+
view: EnrichedMarkdownText?,
|
|
114
|
+
enableLinkPreview: Boolean,
|
|
115
|
+
) {
|
|
116
|
+
// This prop is only used on iOS (to control the system link preview on long press).
|
|
117
|
+
// Required by the codegen interface but is a no-op on Android.
|
|
118
|
+
}
|
|
119
|
+
|
|
78
120
|
override fun setPadding(
|
|
79
121
|
view: EnrichedMarkdownText,
|
|
80
122
|
left: Int,
|
|
@@ -98,6 +140,18 @@ class EnrichedMarkdownTextManager :
|
|
|
98
140
|
eventDispatcher?.dispatchEvent(event)
|
|
99
141
|
}
|
|
100
142
|
|
|
143
|
+
private fun emitOnLinkLongPress(
|
|
144
|
+
view: EnrichedMarkdownText,
|
|
145
|
+
url: String,
|
|
146
|
+
) {
|
|
147
|
+
val context = view.context as com.facebook.react.bridge.ReactContext
|
|
148
|
+
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
149
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
150
|
+
val event = LinkLongPressEvent(surfaceId, view.id, url)
|
|
151
|
+
|
|
152
|
+
eventDispatcher?.dispatchEvent(event)
|
|
153
|
+
}
|
|
154
|
+
|
|
101
155
|
override fun measure(
|
|
102
156
|
context: Context,
|
|
103
157
|
localData: ReadableMap?,
|