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.
Files changed (127) hide show
  1. package/README.md +80 -8
  2. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +17 -2
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +6 -1
  4. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +9 -0
  5. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +6 -0
  6. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +28 -3
  7. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +225 -1
  8. package/android/src/main/cpp/jni-adapter.cpp +28 -11
  9. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +132 -15
  10. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +1 -16
  11. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +67 -13
  12. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +241 -21
  13. package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +279 -0
  14. package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLongPressEvent.kt +23 -0
  15. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +2 -0
  16. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +17 -3
  17. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +13 -18
  18. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +23 -24
  19. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +1 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +2 -1
  21. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +2 -1
  22. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +18 -2
  23. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +22 -6
  24. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +1 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +3 -2
  26. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +2 -1
  27. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +16 -9
  28. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +5 -1
  29. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +23 -9
  30. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +24 -10
  31. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +1 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrikethroughRenderer.kt +27 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +2 -1
  34. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +1 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +1 -0
  36. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/UnderlineRenderer.kt +27 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +1 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +8 -17
  39. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +19 -5
  40. package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +1 -1
  41. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrikethroughSpan.kt +12 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +1 -0
  43. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +3 -0
  44. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +3 -0
  45. package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +5 -1
  46. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +3 -1
  47. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +3 -0
  48. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +5 -1
  49. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrikethroughStyle.kt +17 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +32 -1
  51. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +22 -5
  52. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TextAlignment.kt +32 -0
  53. package/android/src/main/java/com/swmansion/enriched/markdown/styles/UnderlineStyle.kt +17 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +23 -5
  55. package/android/src/main/java/com/swmansion/enriched/markdown/utils/LinkLongPressMovementMethod.kt +121 -0
  56. package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +10 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +58 -56
  58. package/android/src/main/jni/CMakeLists.txt +1 -13
  59. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +0 -13
  60. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +2 -14
  61. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +3 -0
  62. package/cpp/parser/MD4CParser.cpp +21 -8
  63. package/cpp/parser/MD4CParser.hpp +5 -1
  64. package/cpp/parser/MarkdownASTNode.hpp +2 -0
  65. package/ios/EnrichedMarkdownText.mm +356 -29
  66. package/ios/attachments/{ImageAttachment.h → EnrichedMarkdownImageAttachment.h} +1 -1
  67. package/ios/attachments/{ImageAttachment.m → EnrichedMarkdownImageAttachment.m} +4 -4
  68. package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +9 -0
  69. package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +6 -0
  70. package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +28 -3
  71. package/ios/generated/EnrichedMarkdownTextSpec/Props.h +225 -1
  72. package/ios/parser/MarkdownASTNode.h +2 -0
  73. package/ios/parser/MarkdownParser.h +9 -0
  74. package/ios/parser/MarkdownParser.mm +31 -2
  75. package/ios/parser/MarkdownParserBridge.mm +13 -3
  76. package/ios/renderer/AttributedRenderer.h +2 -0
  77. package/ios/renderer/AttributedRenderer.m +52 -19
  78. package/ios/renderer/BlockquoteRenderer.m +7 -6
  79. package/ios/renderer/CodeBlockRenderer.m +9 -8
  80. package/ios/renderer/HeadingRenderer.m +31 -24
  81. package/ios/renderer/ImageRenderer.m +31 -10
  82. package/ios/renderer/ListItemRenderer.m +51 -39
  83. package/ios/renderer/ListRenderer.m +21 -18
  84. package/ios/renderer/ParagraphRenderer.m +27 -16
  85. package/ios/renderer/RenderContext.h +17 -0
  86. package/ios/renderer/RenderContext.m +66 -2
  87. package/ios/renderer/RendererFactory.m +6 -0
  88. package/ios/renderer/StrikethroughRenderer.h +6 -0
  89. package/ios/renderer/StrikethroughRenderer.m +40 -0
  90. package/ios/renderer/UnderlineRenderer.h +6 -0
  91. package/ios/renderer/UnderlineRenderer.m +39 -0
  92. package/ios/styles/StyleConfig.h +46 -0
  93. package/ios/styles/StyleConfig.mm +351 -12
  94. package/ios/utils/AccessibilityInfo.h +35 -0
  95. package/ios/utils/AccessibilityInfo.m +24 -0
  96. package/ios/utils/CodeBlockBackground.m +4 -9
  97. package/ios/utils/FontUtils.h +5 -0
  98. package/ios/utils/FontUtils.m +14 -0
  99. package/ios/utils/HTMLGenerator.m +21 -7
  100. package/ios/utils/MarkdownAccessibilityElementBuilder.h +45 -0
  101. package/ios/utils/MarkdownAccessibilityElementBuilder.m +323 -0
  102. package/ios/utils/MarkdownExtractor.m +18 -5
  103. package/ios/utils/ParagraphStyleUtils.h +10 -2
  104. package/ios/utils/ParagraphStyleUtils.m +57 -2
  105. package/ios/utils/PasteboardUtils.h +1 -1
  106. package/ios/utils/PasteboardUtils.m +3 -3
  107. package/lib/module/EnrichedMarkdownText.js +33 -2
  108. package/lib/module/EnrichedMarkdownText.js.map +1 -1
  109. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +83 -3
  110. package/lib/module/index.js +0 -1
  111. package/lib/module/index.js.map +1 -1
  112. package/lib/module/normalizeMarkdownStyle.js +58 -14
  113. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  114. package/lib/typescript/src/EnrichedMarkdownText.d.ts +85 -3
  115. package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -1
  116. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +75 -1
  117. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  118. package/lib/typescript/src/index.d.ts +2 -3
  119. package/lib/typescript/src/index.d.ts.map +1 -1
  120. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  121. package/package.json +1 -1
  122. package/src/EnrichedMarkdownText.tsx +133 -5
  123. package/src/EnrichedMarkdownTextNativeComponent.ts +83 -3
  124. package/src/index.tsx +5 -2
  125. package/src/normalizeMarkdownStyle.ts +46 -0
  126. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +0 -9
  127. 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.method.LinkMovementMethod
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
- * Utilizes PrecomputedText for smoother UI updates on supported Android versions.
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 = LinkMovementMethod.getInstance()
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
- val newStyle = style?.let { StyleConfig(it, context) }
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
- // LinkMovementMethod check (setText can sometimes reset it)
117
- if (movementMethod !is LinkMovementMethod) {
118
- movementMethod = LinkMovementMethod.getInstance()
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 = LinkMovementMethod.getInstance()
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
  }
@@ -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() {
@@ -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 = "isSelectable", defaultBoolean = true)
71
- override fun setIsSelectable(
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?,