react-native-enriched-markdown-scaffold 1.0.2-scaffold.3 → 1.0.2-scaffold.4
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/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +1 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +1 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +48 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +32 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +10 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +122 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +8 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +8 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLayoutEvent.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +37 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +13 -99
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/InlineChipPainter.kt +140 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkChipSpans.kt +177 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +1 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +36 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkVariantEntry.kt +7 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/HTMLGenerator.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/MarkdownExtractor.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt +100 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +4 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +14 -0
- package/docs/API_REFERENCE.md +53 -0
- package/docs/MENTIONS.md +8 -1
- package/docs/STYLES.md +27 -0
- package/docs/TEXT.md +2 -0
- package/ios/EnrichedMarkdown.mm +48 -0
- package/ios/EnrichedMarkdownText.mm +39 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +48 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +32 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +10 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +122 -0
- package/ios/internals/MeasurementCache.h +5 -0
- package/ios/renderer/LinkRenderer.m +47 -15
- package/ios/styles/StyleConfig.h +16 -0
- package/ios/styles/StyleConfig.mm +60 -0
- package/ios/utils/CodeBackground.m +3 -65
- package/ios/utils/ENRMChipDrawing.h +24 -0
- package/ios/utils/ENRMChipDrawing.m +83 -0
- package/ios/utils/ENRMLinkChip.h +70 -0
- package/ios/utils/ENRMLinkChip.m +194 -0
- package/ios/utils/ENRMLinkLayout.h +50 -0
- package/ios/utils/ENRMLinkLayout.m +45 -0
- package/ios/utils/EditMenuUtils+macOS.m +4 -1
- package/ios/utils/EditMenuUtils.m +4 -1
- package/ios/utils/HTMLGenerator.m +2 -1
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +4 -1
- package/ios/utils/MarkdownExtractor.m +2 -1
- package/ios/utils/RTFExportUtils.m +2 -0
- package/ios/utils/StylePropsUtils.h +37 -1
- package/ios/utils/TextViewLayoutManager.mm +3 -0
- package/ios/views/TableContainerView.h +3 -0
- package/ios/views/TableContainerView.m +21 -0
- package/lib/module/EnrichedMarkdownNativeComponent.ts +41 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +41 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +96 -7
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +13 -2
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +13 -2
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/web/EnrichedMarkdownText.js +6 -3
- package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/web/renderers/InlineRenderers.js +96 -3
- package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
- package/lib/module/web/styles.js +18 -0
- package/lib/module/web/styles.js.map +1 -1
- package/lib/module/web/utils.js +13 -0
- package/lib/module/web/utils.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +40 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +40 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +1 -1
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyle.d.ts +28 -5
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +10 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +41 -0
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +41 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/lib/typescript/src/web/types.d.ts +4 -0
- package/lib/typescript/src/web/types.d.ts.map +1 -1
- package/lib/typescript/src/web/utils.d.ts +4 -0
- package/lib/typescript/src/web/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +41 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +41 -0
- package/src/index.tsx +2 -0
- package/src/index.web.tsx +5 -1
- package/src/native/EnrichedMarkdownText.tsx +103 -6
- package/src/normalizeMarkdownStyle.ts +13 -0
- package/src/normalizeMarkdownStyle.web.ts +11 -0
- package/src/types/MarkdownStyle.ts +29 -5
- package/src/types/MarkdownStyleInternal.ts +10 -0
- package/src/types/MarkdownTextProps.ts +43 -0
- package/src/types/MarkdownTextProps.web.ts +43 -1
- package/src/web/EnrichedMarkdownText.tsx +10 -3
- package/src/web/renderers/InlineRenderers.tsx +111 -3
- package/src/web/styles.ts +25 -0
- package/src/web/types.ts +4 -0
- package/src/web/utils.ts +8 -0
package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.utils.text.view
|
|
2
|
+
|
|
3
|
+
import android.text.Layout
|
|
4
|
+
import android.text.Spanned
|
|
5
|
+
import android.widget.TextView
|
|
6
|
+
import com.facebook.react.uimanager.PixelUtil
|
|
7
|
+
import com.swmansion.enriched.markdown.spans.LinkChipBackgroundSpan
|
|
8
|
+
import com.swmansion.enriched.markdown.spans.LinkSpacers
|
|
9
|
+
import com.swmansion.enriched.markdown.spans.LinkSpan
|
|
10
|
+
import kotlin.math.max
|
|
11
|
+
import kotlin.math.min
|
|
12
|
+
|
|
13
|
+
/** One laid-out line of a link, in dp relative to the reporting view. Covers the drawn chip. */
|
|
14
|
+
data class LinkLayoutFragment(
|
|
15
|
+
val linkIndex: Int,
|
|
16
|
+
val url: String,
|
|
17
|
+
val text: String,
|
|
18
|
+
val x: Float,
|
|
19
|
+
val y: Float,
|
|
20
|
+
val width: Float,
|
|
21
|
+
val height: Float,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
object LinkLayoutCollector {
|
|
25
|
+
/**
|
|
26
|
+
* Collects the chip rect of every link fragment laid out in [textView], for `onLinkLayout`.
|
|
27
|
+
* Rects are offset by the text view's own padding plus [offsetXPx]/[offsetYPx] (a segment's
|
|
28
|
+
* position inside its container), then converted to dp. Links are numbered in document order
|
|
29
|
+
* from [indexBase]; returns the fragments and the number of links found.
|
|
30
|
+
*/
|
|
31
|
+
fun collect(
|
|
32
|
+
textView: TextView,
|
|
33
|
+
offsetXPx: Int = 0,
|
|
34
|
+
offsetYPx: Int = 0,
|
|
35
|
+
indexBase: Int = 0,
|
|
36
|
+
): Pair<List<LinkLayoutFragment>, Int> {
|
|
37
|
+
val layout = textView.layout ?: return emptyList<LinkLayoutFragment>() to 0
|
|
38
|
+
val text = textView.text as? Spanned ?: return emptyList<LinkLayoutFragment>() to 0
|
|
39
|
+
|
|
40
|
+
// LinkRenderer sets one LinkChipBackgroundSpan per link, spacers included.
|
|
41
|
+
val chips =
|
|
42
|
+
text
|
|
43
|
+
.getSpans(0, text.length, LinkChipBackgroundSpan::class.java)
|
|
44
|
+
.sortedBy { text.getSpanStart(it) }
|
|
45
|
+
val originX = offsetXPx + textView.totalPaddingLeft
|
|
46
|
+
val originY = offsetYPx + textView.totalPaddingTop
|
|
47
|
+
|
|
48
|
+
val fragments = mutableListOf<LinkLayoutFragment>()
|
|
49
|
+
chips.forEachIndexed { i, chipSpan ->
|
|
50
|
+
val start = text.getSpanStart(chipSpan)
|
|
51
|
+
val end = text.getSpanEnd(chipSpan)
|
|
52
|
+
if (start !in 0 until end) return@forEachIndexed
|
|
53
|
+
|
|
54
|
+
val url = text.getSpans(start, end, LinkSpan::class.java).firstOrNull()?.url ?: ""
|
|
55
|
+
val linkText = LinkSpacers.strip(text.subSequence(start, end)).toString()
|
|
56
|
+
val (ascent, descent) = LinkChipBackgroundSpan.linkFontMetrics(text, start, end, textView.paint)
|
|
57
|
+
val paddingVertical = chipSpan.chip.paddingVertical
|
|
58
|
+
|
|
59
|
+
for (line in layout.getLineForOffset(start)..layout.getLineForOffset(end - 1)) {
|
|
60
|
+
val fragmentStart = max(start, layout.getLineStart(line))
|
|
61
|
+
val fragmentEnd =
|
|
62
|
+
LinkChipBackgroundSpan.trimTrailingWhitespace(text, fragmentStart, min(end, layout.getLineEnd(line)))
|
|
63
|
+
if (fragmentEnd <= fragmentStart) continue
|
|
64
|
+
|
|
65
|
+
val left = layout.getPrimaryHorizontal(fragmentStart)
|
|
66
|
+
val right = lineEndHorizontal(layout, line, fragmentEnd)
|
|
67
|
+
val baseline = layout.getLineBaseline(line).toFloat()
|
|
68
|
+
val top = baseline + ascent - paddingVertical
|
|
69
|
+
val bottom = baseline + descent + paddingVertical
|
|
70
|
+
|
|
71
|
+
fragments +=
|
|
72
|
+
LinkLayoutFragment(
|
|
73
|
+
linkIndex = indexBase + i,
|
|
74
|
+
url = url,
|
|
75
|
+
text = linkText,
|
|
76
|
+
x = PixelUtil.toDIPFromPixel(min(left, right) + originX),
|
|
77
|
+
y = PixelUtil.toDIPFromPixel(top + originY),
|
|
78
|
+
width = PixelUtil.toDIPFromPixel(kotlin.math.abs(right - left)),
|
|
79
|
+
height = PixelUtil.toDIPFromPixel(bottom - top),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return fragments to chips.size
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* x of [offset] on [line]. At a line's end offset `getPrimaryHorizontal` reports the start
|
|
88
|
+
* of the next line (the same offset), so a fragment cut mid-word uses the line's right edge.
|
|
89
|
+
*/
|
|
90
|
+
private fun lineEndHorizontal(
|
|
91
|
+
layout: Layout,
|
|
92
|
+
line: Int,
|
|
93
|
+
offset: Int,
|
|
94
|
+
): Float =
|
|
95
|
+
if (offset >= layout.getLineEnd(line) && line < layout.lineCount - 1) {
|
|
96
|
+
layout.getLineRight(line)
|
|
97
|
+
} else {
|
|
98
|
+
layout.getPrimaryHorizontal(offset)
|
|
99
|
+
}
|
|
100
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt
CHANGED
|
@@ -12,6 +12,7 @@ import android.widget.TextView
|
|
|
12
12
|
import com.swmansion.enriched.markdown.EnrichedMarkdown
|
|
13
13
|
import com.swmansion.enriched.markdown.EnrichedMarkdownText
|
|
14
14
|
import com.swmansion.enriched.markdown.spans.ImageSpan
|
|
15
|
+
import com.swmansion.enriched.markdown.spans.LinkSpacers
|
|
15
16
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
16
17
|
import com.swmansion.enriched.markdown.utils.common.layout.isLayoutRTL
|
|
17
18
|
import com.swmansion.enriched.markdown.utils.text.conversion.HTMLGenerator
|
|
@@ -211,7 +212,8 @@ private fun TextView.copyWithHTML() {
|
|
|
211
212
|
if (start < 0 || end < 0 || start >= end) return
|
|
212
213
|
|
|
213
214
|
val spannable = text as? Spannable ?: return
|
|
214
|
-
|
|
215
|
+
// Link spacers only reserve layout space; keep them out of every copied flavor.
|
|
216
|
+
val selectedText = LinkSpacers.strip(spannable.subSequence(start, end))
|
|
215
217
|
val plainText = selectedText.toString()
|
|
216
218
|
|
|
217
219
|
val styleConfig =
|
|
@@ -243,7 +245,7 @@ private fun TextView.requestCopy(onCopyRequest: (markdown: String, text: String,
|
|
|
243
245
|
val end = selectionEnd
|
|
244
246
|
if (start < 0 || end < 0 || start >= end) return
|
|
245
247
|
|
|
246
|
-
val selectedText = text.subSequence(start, end).toString()
|
|
248
|
+
val selectedText = LinkSpacers.strip(text.subSequence(start, end)).toString()
|
|
247
249
|
val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: selectedText
|
|
248
250
|
// Same threshold MarkdownExtractor uses to return the original markdown.
|
|
249
251
|
onCopyRequest(markdown, selectedText, start == 0 && end >= text.length - 1)
|
|
@@ -24,6 +24,7 @@ import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
|
|
|
24
24
|
import com.swmansion.enriched.markdown.parser.MarkdownASTNode
|
|
25
25
|
import com.swmansion.enriched.markdown.parser.MarkdownASTNode.NodeType
|
|
26
26
|
import com.swmansion.enriched.markdown.renderer.Renderer
|
|
27
|
+
import com.swmansion.enriched.markdown.spans.LinkChipBackgroundSpan
|
|
27
28
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
28
29
|
import com.swmansion.enriched.markdown.styles.TableStyle
|
|
29
30
|
import com.swmansion.enriched.markdown.utils.common.layout.isLayoutRTL
|
|
@@ -81,6 +82,19 @@ class TableContainerView(
|
|
|
81
82
|
|
|
82
83
|
val rowCount: Int get() = rows.size
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Number of links across all cells. Tables are not reported by `onLinkLayout`, but their links
|
|
87
|
+
* still count so later link indices stay in document order.
|
|
88
|
+
*/
|
|
89
|
+
val linkCount: Int
|
|
90
|
+
get() =
|
|
91
|
+
rows.sumOf { row ->
|
|
92
|
+
row.sumOf { cell ->
|
|
93
|
+
// LinkRenderer attaches one LinkChipBackgroundSpan per link.
|
|
94
|
+
cell.attributedText.getSpans(0, cell.attributedText.length, LinkChipBackgroundSpan::class.java).size
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
fun animateNewRows(
|
|
85
99
|
previousRowCount: Int,
|
|
86
100
|
durationMs: Long,
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -69,6 +69,59 @@ Callback when a link is long pressed. Access URL via `event.url`. On iOS, automa
|
|
|
69
69
|
/>
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
### `renderLinkDecoration`
|
|
73
|
+
|
|
74
|
+
Renders a React Native view on top of each link — for example a badge or sparkle on a [link chip](STYLES.md#link-chips). Return `null` to leave a link undecorated.
|
|
75
|
+
|
|
76
|
+
| Type | Default Value | Platform |
|
|
77
|
+
| ------------------------------------------------------ | ------------- | -------- |
|
|
78
|
+
| `(info: LinkDecorationInfo) => ReactNode \| null` | - | Both |
|
|
79
|
+
|
|
80
|
+
`LinkDecorationInfo`:
|
|
81
|
+
|
|
82
|
+
| Field | Type | Description |
|
|
83
|
+
| ------- | ------------ | ----------- |
|
|
84
|
+
| `index` | `number` | Position of the link in the document (0-based). Stable key for the link |
|
|
85
|
+
| `url` | `string` | Link URL |
|
|
86
|
+
| `text` | `string` | Visible link text |
|
|
87
|
+
| `rect` | `LinkRect` | Anchor rect: the drawn chip (glyphs + horizontal/vertical padding) of the link's **last** line fragment |
|
|
88
|
+
| `rects` | `LinkRect[]` | Every line fragment, in order (several when the link wraps) |
|
|
89
|
+
|
|
90
|
+
`LinkRect` is `{ x, y, width, height }` in dp, relative to the component's content box (inside `containerStyle` padding).
|
|
91
|
+
|
|
92
|
+
The returned node is placed inside an absolutely positioned box matching `rect`, with `pointerEvents="box-none"`, so position it relative to that box. Negative offsets let it overflow the chip:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<EnrichedMarkdownText
|
|
96
|
+
markdown="Read the [guide](https://example.com/guide) first."
|
|
97
|
+
markdownStyle={{
|
|
98
|
+
link: {
|
|
99
|
+
backgroundColor: '#DCE2FF',
|
|
100
|
+
borderRadius: 5,
|
|
101
|
+
paddingHorizontal: 7,
|
|
102
|
+
underline: false,
|
|
103
|
+
},
|
|
104
|
+
}}
|
|
105
|
+
renderLinkDecoration={({ url }) =>
|
|
106
|
+
url.startsWith('https://') ? (
|
|
107
|
+
<View style={{ position: 'absolute', top: -8, right: -8 }}>
|
|
108
|
+
<SparkleIcon />
|
|
109
|
+
</View>
|
|
110
|
+
) : null
|
|
111
|
+
}
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Notes and limitations:
|
|
116
|
+
|
|
117
|
+
- When this prop is not set the component renders exactly as before: no wrapper view and no layout events.
|
|
118
|
+
- Setting it wraps the native view in a `View` (which takes `containerStyle`). Switching between set and unset remounts the native view.
|
|
119
|
+
- Rects are measured natively after layout and sent to JS, so decorations appear one frame after the text. Updates are deduplicated — streaming does not re-render decorations unless a link actually moves.
|
|
120
|
+
- Links inside tables (`flavor="github"`) are not reported.
|
|
121
|
+
- On Android, parts of a decoration that overflow the component's bounds do not receive touches.
|
|
122
|
+
- Web: `rect` is the link's whole inline box and `rects` is `[rect]`; for a wrapping link the decoration anchors to that box rather than the last line.
|
|
123
|
+
- Not supported by `EnrichedMarkdownTextInput`.
|
|
124
|
+
|
|
72
125
|
### `onTaskListItemPress`
|
|
73
126
|
|
|
74
127
|
Callback when a task list checkbox is tapped. Receives `index` (0-based), `checked` (new state after toggling), and `text` (item text).
|
package/docs/MENTIONS.md
CHANGED
|
@@ -69,7 +69,14 @@ linkVariants: {
|
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
Each key is a regex tested against the link URL. First match wins.
|
|
72
|
+
Each key is a regex tested against the link URL. First match wins. Patterns are auto-sorted longest-first.
|
|
73
|
+
|
|
74
|
+
Inheritance from the base `link` style is split in two groups:
|
|
75
|
+
|
|
76
|
+
- **Text** (`color`, `underline`): unspecified properties inherit from `link`.
|
|
77
|
+
- **Box** (`backgroundColor`, `borderRadius`, `paddingHorizontal`, `paddingVertical`, `borderColor`, `borderWidth`): never inherited. A variant that sets none of them renders without a chip, even when `link` is a chip.
|
|
78
|
+
|
|
79
|
+
`fontFamily` is only configurable on `link` and applies to every variant. See [Link chips](STYLES.md#link-chips) for the box properties.
|
|
73
80
|
|
|
74
81
|
## Positioning the Suggestion List
|
|
75
82
|
|
package/docs/STYLES.md
CHANGED
|
@@ -368,6 +368,33 @@ Per-token foreground colors for syntax highlighting, keyed on the highlight toke
|
|
|
368
368
|
| `fontFamily` | `string` | Font family for links. Overrides the parent block's font family when set |
|
|
369
369
|
| `color` | `string` | Link text color |
|
|
370
370
|
| `underline` | `boolean` | Show underline |
|
|
371
|
+
| `backgroundColor` | `string` | Chip background, drawn behind the link's glyphs (ascender to descender) plus padding |
|
|
372
|
+
| `borderRadius` | `number` | Chip corner radius |
|
|
373
|
+
| `paddingHorizontal` | `number` | Space added before and after the link text. It takes real room in the line, so it affects wrapping and measured size |
|
|
374
|
+
| `paddingVertical` | `number` | Extends the chip above and below the text. Drawn only: it does not change the line height, so it can overlap neighbouring lines (or be clipped at the component's top/bottom edge) if the line height is too small |
|
|
375
|
+
| `borderColor` | `string` | Chip border color |
|
|
376
|
+
| `borderWidth` | `number` | Chip border width, drawn inset (does not grow the chip) |
|
|
377
|
+
|
|
378
|
+
#### Link chips
|
|
379
|
+
|
|
380
|
+
Setting `backgroundColor`, `borderRadius`, padding or a border turns a link into a chip. When a link wraps, each line gets its own piece of the chip; only the outer ends are rounded and the border is left open where the link breaks.
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
link: {
|
|
384
|
+
color: '#3346C8',
|
|
385
|
+
underline: false,
|
|
386
|
+
backgroundColor: '#DCE2FF',
|
|
387
|
+
borderRadius: 5,
|
|
388
|
+
paddingHorizontal: 7,
|
|
389
|
+
paddingVertical: 4,
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Horizontal padding is reserved with an invisible non-breaking spacer at each end of the link, so the padding always stays attached to the text on wrap. The spacer is removed from copied text (plain, HTML, RTF, Markdown and `onCopy`) and is part of the link's tap area.
|
|
394
|
+
|
|
395
|
+
On iOS (and in RTF copies), links inside table cells fall back to a plain text background: `backgroundColor` and `paddingHorizontal` are kept (the background covers the horizontal padding), but `borderRadius`, `paddingVertical` and the border are not drawn.
|
|
396
|
+
|
|
397
|
+
Use [`renderLinkDecoration`](API_REFERENCE.md#renderlinkdecoration) to draw a React Native view on top of a chip (e.g. a badge or icon).
|
|
371
398
|
|
|
372
399
|
### Strong-specific
|
|
373
400
|
|
package/docs/TEXT.md
CHANGED
|
@@ -95,6 +95,8 @@ Links in Markdown are interactive and can be handled with the `onLinkPress` and
|
|
|
95
95
|
|
|
96
96
|
See the [API Reference](API_REFERENCE.md#onlinkpress) for detailed examples and usage.
|
|
97
97
|
|
|
98
|
+
Links can be styled as chips (background, radius, padding, border — see [Link chips](STYLES.md#link-chips)) and decorated with your own views via [`renderLinkDecoration`](API_REFERENCE.md#renderlinkdecoration).
|
|
99
|
+
|
|
98
100
|
## Supported Markdown Elements
|
|
99
101
|
|
|
100
102
|
`react-native-enriched-markdown-scaffold` supports a comprehensive set of Markdown elements. See [Element Structure](ELEMENTS_STRUCTURE.md) for a detailed overview of all supported elements, their syntax, block vs inline categorization, nesting behavior, and how elements inherit typography from their parent blocks.
|
package/ios/EnrichedMarkdown.mm
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
#import "SegmentReconciler.h"
|
|
39
39
|
#import "SegmentRenderer.h"
|
|
40
40
|
#import "SegmentViewRegistry.h"
|
|
41
|
+
#import "ENRMLinkLayout.h"
|
|
41
42
|
#import "SelectionColorUtils.h"
|
|
42
43
|
#import "StreamingMarkdownFilter.h"
|
|
43
44
|
#import "StyleConfig.h"
|
|
@@ -73,6 +74,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
73
74
|
+ (ENRMMd4cFlags *)flagsFromProps:(const EnrichedMarkdownMd4cFlagsStruct &)props;
|
|
74
75
|
- (void)emitLinkPress:(NSString *)url;
|
|
75
76
|
- (void)emitLinkLongPress:(NSString *)url;
|
|
77
|
+
- (void)emitLinkLayoutIfNeeded;
|
|
76
78
|
- (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text;
|
|
77
79
|
- (void)emitCopyPress:(NSString *)code language:(NSString *)language;
|
|
78
80
|
- (void)emitCopyRequest:(NSString *)source markdown:(NSString *)markdown text:(NSString *)text whole:(BOOL)whole;
|
|
@@ -123,6 +125,9 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
123
125
|
|
|
124
126
|
ENRMSpoilerOverlay _spoilerOverlay;
|
|
125
127
|
|
|
128
|
+
BOOL _emitLinkLayout;
|
|
129
|
+
NSArray<NSDictionary *> *_lastLinkLayout;
|
|
130
|
+
|
|
126
131
|
NSLineBreakStrategy _lineBreakStrategy;
|
|
127
132
|
|
|
128
133
|
ENRMWritingDirectionMode _writingDirectionMode;
|
|
@@ -941,6 +946,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
941
946
|
{
|
|
942
947
|
[super layoutSubviews];
|
|
943
948
|
[self computeSegmentLayoutForWidth:self.bounds.size.width applyFrames:YES];
|
|
949
|
+
[self emitLinkLayoutIfNeeded];
|
|
944
950
|
}
|
|
945
951
|
|
|
946
952
|
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
@@ -976,6 +982,12 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
976
982
|
|
|
977
983
|
_selectable = newViewProps.selectable;
|
|
978
984
|
|
|
985
|
+
if (newViewProps.emitLinkLayout != _emitLinkLayout) {
|
|
986
|
+
_emitLinkLayout = newViewProps.emitLinkLayout;
|
|
987
|
+
_lastLinkLayout = nil;
|
|
988
|
+
[self setNeedsLayout];
|
|
989
|
+
}
|
|
990
|
+
|
|
979
991
|
for (RCTUIView *segment in _segmentViews) {
|
|
980
992
|
if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
|
|
981
993
|
EnrichedMarkdownInternalText *textSegment = (EnrichedMarkdownInternalText *)segment;
|
|
@@ -1171,6 +1183,8 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
1171
1183
|
_cachedMarkdown = nil;
|
|
1172
1184
|
_renderedMarkdown = nil;
|
|
1173
1185
|
_config = nil;
|
|
1186
|
+
_emitLinkLayout = NO;
|
|
1187
|
+
_lastLinkLayout = nil;
|
|
1174
1188
|
_md4cFlags = [EnrichedMarkdown flagsFromProps:resetProps->md4cFlags];
|
|
1175
1189
|
_maxFontSizeMultiplier = 0;
|
|
1176
1190
|
_allowTrailingMargin = NO;
|
|
@@ -1281,6 +1295,40 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1281
1295
|
emitter->onLinkPress({.url = std::string(url.UTF8String)});
|
|
1282
1296
|
}
|
|
1283
1297
|
|
|
1298
|
+
/// Reports link rects of every text segment (offset by the segment origin) to
|
|
1299
|
+
/// JS for `renderLinkDecoration`. Tables are skipped: their cells scroll
|
|
1300
|
+
/// horizontally, so rects would go stale. Runs after each segment layout pass;
|
|
1301
|
+
/// deduplicated so an unchanged layout sends nothing.
|
|
1302
|
+
- (void)emitLinkLayoutIfNeeded
|
|
1303
|
+
{
|
|
1304
|
+
if (!_emitLinkLayout || self.bounds.size.width <= 0)
|
|
1305
|
+
return;
|
|
1306
|
+
|
|
1307
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
1308
|
+
if (!emitter)
|
|
1309
|
+
return;
|
|
1310
|
+
|
|
1311
|
+
NSMutableArray<NSDictionary *> *fragments = [NSMutableArray array];
|
|
1312
|
+
NSInteger linkCount = 0;
|
|
1313
|
+
for (RCTUIView *segment in _segmentViews) {
|
|
1314
|
+
if ([segment isKindOfClass:[TableContainerView class]]) {
|
|
1315
|
+
linkCount += ((TableContainerView *)segment).linkCount;
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
if (![segment isKindOfClass:[EnrichedMarkdownInternalText class]])
|
|
1319
|
+
continue;
|
|
1320
|
+
NSInteger segmentLinks = 0;
|
|
1321
|
+
[fragments addObjectsFromArray:ENRMCollectLinkLayoutFragments(((EnrichedMarkdownInternalText *)segment).textView,
|
|
1322
|
+
segment.frame.origin, linkCount, &segmentLinks)];
|
|
1323
|
+
linkCount += segmentLinks;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
if (_lastLinkLayout != nil && [fragments isEqualToArray:_lastLinkLayout])
|
|
1327
|
+
return;
|
|
1328
|
+
_lastLinkLayout = [fragments copy];
|
|
1329
|
+
emitter->onLinkLayout(ENRMLinkLayoutEventFromFragments<EnrichedMarkdownEventEmitter::OnLinkLayout>(fragments));
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1284
1332
|
- (void)emitLinkLongPress:(NSString *)url
|
|
1285
1333
|
{
|
|
1286
1334
|
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#import "ENRMAtomicSize.h"
|
|
7
7
|
#import "ENRMContextMenuTextView+macOS.h"
|
|
8
8
|
#import "ENRMImageAttachment.h"
|
|
9
|
+
#import "ENRMLinkLayout.h"
|
|
9
10
|
#import "ENRMMarkdownParser.h"
|
|
10
11
|
#import "ENRMSpoilerOverlayManager.h"
|
|
11
12
|
#import "ENRMSpoilerTapUtils.h"
|
|
@@ -56,6 +57,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
56
57
|
- (void)setupLayoutManager;
|
|
57
58
|
- (void)emitLinkPress:(NSString *)url;
|
|
58
59
|
- (void)emitLinkLongPress:(NSString *)url;
|
|
60
|
+
- (void)emitLinkLayoutIfNeeded;
|
|
59
61
|
- (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text;
|
|
60
62
|
- (void)emitContextMenuItemPress:(NSString *)itemText
|
|
61
63
|
selectedText:(NSString *)selectedText
|
|
@@ -110,6 +112,9 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
110
112
|
|
|
111
113
|
ENRMSpoilerOverlayManager *_spoilerManager;
|
|
112
114
|
|
|
115
|
+
BOOL _emitLinkLayout;
|
|
116
|
+
NSArray<NSDictionary *> *_lastLinkLayout;
|
|
117
|
+
|
|
113
118
|
NSLineBreakStrategy _lineBreakStrategy;
|
|
114
119
|
|
|
115
120
|
ENRMWritingDirectionMode _writingDirectionMode;
|
|
@@ -183,6 +188,9 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
183
188
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
184
189
|
|
|
185
190
|
_lastCommittedSize.store(CGSizeMake(layoutMetrics.frame.size.width, layoutMetrics.frame.size.height));
|
|
191
|
+
#if TARGET_OS_OSX
|
|
192
|
+
[self emitLinkLayoutIfNeeded];
|
|
193
|
+
#endif
|
|
186
194
|
|
|
187
195
|
NSWritingDirection resolved = _resolvedLayoutDirection;
|
|
188
196
|
if (layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) {
|
|
@@ -218,6 +226,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
218
226
|
if (needsHeightUpdate(measured, self.bounds)) {
|
|
219
227
|
[self requestHeightUpdate];
|
|
220
228
|
}
|
|
229
|
+
[self emitLinkLayoutIfNeeded];
|
|
221
230
|
}
|
|
222
231
|
}
|
|
223
232
|
|
|
@@ -464,6 +473,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
464
473
|
if (forceHeightUpdate || needsHeightUpdate(measured, self.bounds)) {
|
|
465
474
|
[self requestHeightUpdate];
|
|
466
475
|
}
|
|
476
|
+
|
|
477
|
+
[self emitLinkLayoutIfNeeded];
|
|
467
478
|
}
|
|
468
479
|
|
|
469
480
|
if (_textView.hidden) {
|
|
@@ -484,6 +495,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
484
495
|
{
|
|
485
496
|
[super layoutSubviews];
|
|
486
497
|
[_spoilerManager updateIfNeeded];
|
|
498
|
+
[self emitLinkLayoutIfNeeded];
|
|
487
499
|
}
|
|
488
500
|
#endif
|
|
489
501
|
|
|
@@ -521,6 +533,12 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
521
533
|
}
|
|
522
534
|
}
|
|
523
535
|
|
|
536
|
+
if (newViewProps.emitLinkLayout != _emitLinkLayout) {
|
|
537
|
+
_emitLinkLayout = newViewProps.emitLinkLayout;
|
|
538
|
+
_lastLinkLayout = nil;
|
|
539
|
+
[self emitLinkLayoutIfNeeded];
|
|
540
|
+
}
|
|
541
|
+
|
|
524
542
|
if (_textView.selectable != newViewProps.selectable) {
|
|
525
543
|
_textView.selectable = newViewProps.selectable;
|
|
526
544
|
}
|
|
@@ -697,6 +715,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
697
715
|
_accessibilityNeedsRebuild = NO;
|
|
698
716
|
[_spoilerManager removeAllOverlays];
|
|
699
717
|
_spoilerManager = nil;
|
|
718
|
+
_emitLinkLayout = NO;
|
|
719
|
+
_lastLinkLayout = nil;
|
|
700
720
|
if (_textView != nil) {
|
|
701
721
|
ENRMSetAttributedText(_textView, [[NSAttributedString alloc] initWithString:@""]);
|
|
702
722
|
_textView.hidden = YES;
|
|
@@ -742,6 +762,25 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
|
742
762
|
emitter->onLinkLongPress({.url = std::string(url.UTF8String)});
|
|
743
763
|
}
|
|
744
764
|
|
|
765
|
+
/// Reports link rects to JS (for `renderLinkDecoration`). Called after every
|
|
766
|
+
/// render and layout change; deduplicated so an unchanged layout — the common
|
|
767
|
+
/// case during streaming — sends nothing.
|
|
768
|
+
- (void)emitLinkLayoutIfNeeded
|
|
769
|
+
{
|
|
770
|
+
if (!_emitLinkLayout || _renderedMarkdown == nil || self.bounds.size.width <= 0)
|
|
771
|
+
return;
|
|
772
|
+
|
|
773
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
774
|
+
if (!emitter)
|
|
775
|
+
return;
|
|
776
|
+
|
|
777
|
+
NSArray<NSDictionary *> *fragments = ENRMCollectLinkLayoutFragments(_textView, CGPointZero, 0, NULL);
|
|
778
|
+
if (_lastLinkLayout != nil && [fragments isEqualToArray:_lastLinkLayout])
|
|
779
|
+
return;
|
|
780
|
+
_lastLinkLayout = fragments;
|
|
781
|
+
emitter->onLinkLayout(ENRMLinkLayoutEventFromFragments<EnrichedMarkdownTextEventEmitter::OnLinkLayout>(fragments));
|
|
782
|
+
}
|
|
783
|
+
|
|
745
784
|
- (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text
|
|
746
785
|
{
|
|
747
786
|
auto emitter = std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
@@ -31,6 +31,30 @@ void EnrichedMarkdownEventEmitter::onLinkLongPress(OnLinkLongPress event) const
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
void EnrichedMarkdownEventEmitter::onLinkLayout(OnLinkLayout event) const {
|
|
35
|
+
dispatchEvent("linkLayout", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
36
|
+
auto payload = jsi::Object(runtime);
|
|
37
|
+
|
|
38
|
+
auto fragments = jsi::Array(runtime, event.fragments.size());
|
|
39
|
+
size_t fragmentsIndex = 0;
|
|
40
|
+
for (auto fragmentsValue : event.fragments) {
|
|
41
|
+
auto fragmentsObject = jsi::Object(runtime);
|
|
42
|
+
fragmentsObject.setProperty(runtime, "linkIndex", fragmentsValue.linkIndex);
|
|
43
|
+
fragmentsObject.setProperty(runtime, "url", fragmentsValue.url);
|
|
44
|
+
fragmentsObject.setProperty(runtime, "text", fragmentsValue.text);
|
|
45
|
+
fragmentsObject.setProperty(runtime, "x", fragmentsValue.x);
|
|
46
|
+
fragmentsObject.setProperty(runtime, "y", fragmentsValue.y);
|
|
47
|
+
fragmentsObject.setProperty(runtime, "width", fragmentsValue.width);
|
|
48
|
+
fragmentsObject.setProperty(runtime, "height", fragmentsValue.height);
|
|
49
|
+
fragments.setValueAtIndex(runtime, fragmentsIndex++, fragmentsObject);
|
|
50
|
+
}
|
|
51
|
+
payload.setProperty(runtime, "fragments", fragments);
|
|
52
|
+
|
|
53
|
+
return payload;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
34
58
|
void EnrichedMarkdownEventEmitter::onTaskListItemPress(OnTaskListItemPress event) const {
|
|
35
59
|
dispatchEvent("taskListItemPress", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
36
60
|
auto payload = jsi::Object(runtime);
|
|
@@ -370,6 +394,30 @@ void EnrichedMarkdownTextEventEmitter::onLinkLongPress(OnLinkLongPress event) co
|
|
|
370
394
|
}
|
|
371
395
|
|
|
372
396
|
|
|
397
|
+
void EnrichedMarkdownTextEventEmitter::onLinkLayout(OnLinkLayout event) const {
|
|
398
|
+
dispatchEvent("linkLayout", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
399
|
+
auto payload = jsi::Object(runtime);
|
|
400
|
+
|
|
401
|
+
auto fragments = jsi::Array(runtime, event.fragments.size());
|
|
402
|
+
size_t fragmentsIndex = 0;
|
|
403
|
+
for (auto fragmentsValue : event.fragments) {
|
|
404
|
+
auto fragmentsObject = jsi::Object(runtime);
|
|
405
|
+
fragmentsObject.setProperty(runtime, "linkIndex", fragmentsValue.linkIndex);
|
|
406
|
+
fragmentsObject.setProperty(runtime, "url", fragmentsValue.url);
|
|
407
|
+
fragmentsObject.setProperty(runtime, "text", fragmentsValue.text);
|
|
408
|
+
fragmentsObject.setProperty(runtime, "x", fragmentsValue.x);
|
|
409
|
+
fragmentsObject.setProperty(runtime, "y", fragmentsValue.y);
|
|
410
|
+
fragmentsObject.setProperty(runtime, "width", fragmentsValue.width);
|
|
411
|
+
fragmentsObject.setProperty(runtime, "height", fragmentsValue.height);
|
|
412
|
+
fragments.setValueAtIndex(runtime, fragmentsIndex++, fragmentsObject);
|
|
413
|
+
}
|
|
414
|
+
payload.setProperty(runtime, "fragments", fragments);
|
|
415
|
+
|
|
416
|
+
return payload;
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
373
421
|
void EnrichedMarkdownTextEventEmitter::onTaskListItemPress(OnTaskListItemPress event) const {
|
|
374
422
|
dispatchEvent("taskListItemPress", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
375
423
|
auto payload = jsi::Object(runtime);
|
|
@@ -25,6 +25,20 @@ class EnrichedMarkdownEventEmitter : public ViewEventEmitter {
|
|
|
25
25
|
std::string url;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
struct OnLinkLayoutFragments {
|
|
29
|
+
int linkIndex;
|
|
30
|
+
std::string url;
|
|
31
|
+
std::string text;
|
|
32
|
+
double x;
|
|
33
|
+
double y;
|
|
34
|
+
double width;
|
|
35
|
+
double height;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
struct OnLinkLayout {
|
|
39
|
+
std::vector<OnLinkLayoutFragments> fragments;
|
|
40
|
+
};
|
|
41
|
+
|
|
28
42
|
struct OnTaskListItemPress {
|
|
29
43
|
int index;
|
|
30
44
|
bool checked;
|
|
@@ -53,6 +67,8 @@ class EnrichedMarkdownEventEmitter : public ViewEventEmitter {
|
|
|
53
67
|
|
|
54
68
|
void onLinkLongPress(OnLinkLongPress value) const;
|
|
55
69
|
|
|
70
|
+
void onLinkLayout(OnLinkLayout value) const;
|
|
71
|
+
|
|
56
72
|
void onTaskListItemPress(OnTaskListItemPress value) const;
|
|
57
73
|
|
|
58
74
|
void onCopyPress(OnCopyPress value) const;
|
|
@@ -301,6 +317,20 @@ class EnrichedMarkdownTextEventEmitter : public ViewEventEmitter {
|
|
|
301
317
|
std::string url;
|
|
302
318
|
};
|
|
303
319
|
|
|
320
|
+
struct OnLinkLayoutFragments {
|
|
321
|
+
int linkIndex;
|
|
322
|
+
std::string url;
|
|
323
|
+
std::string text;
|
|
324
|
+
double x;
|
|
325
|
+
double y;
|
|
326
|
+
double width;
|
|
327
|
+
double height;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
struct OnLinkLayout {
|
|
331
|
+
std::vector<OnLinkLayoutFragments> fragments;
|
|
332
|
+
};
|
|
333
|
+
|
|
304
334
|
struct OnTaskListItemPress {
|
|
305
335
|
int index;
|
|
306
336
|
bool checked;
|
|
@@ -329,6 +359,8 @@ class EnrichedMarkdownTextEventEmitter : public ViewEventEmitter {
|
|
|
329
359
|
|
|
330
360
|
void onLinkLongPress(OnLinkLongPress value) const;
|
|
331
361
|
|
|
362
|
+
void onLinkLayout(OnLinkLayout value) const;
|
|
363
|
+
|
|
332
364
|
void onTaskListItemPress(OnTaskListItemPress value) const;
|
|
333
365
|
|
|
334
366
|
void onCopyPress(OnCopyPress value) const;
|
|
@@ -21,6 +21,7 @@ EnrichedMarkdownProps::EnrichedMarkdownProps(
|
|
|
21
21
|
|
|
22
22
|
markdown(convertRawProp(context, rawProps, "markdown", sourceProps.markdown, {})),
|
|
23
23
|
markdownStyle(convertRawProp(context, rawProps, "markdownStyle", sourceProps.markdownStyle, {})),
|
|
24
|
+
emitLinkLayout(convertRawProp(context, rawProps, "emitLinkLayout", sourceProps.emitLinkLayout, {false})),
|
|
24
25
|
enableTaskListItemToggle(convertRawProp(context, rawProps, "enableTaskListItemToggle", sourceProps.enableTaskListItemToggle, {true})),
|
|
25
26
|
enableLinkPreview(convertRawProp(context, rawProps, "enableLinkPreview", sourceProps.enableLinkPreview, {true})),
|
|
26
27
|
selectable(convertRawProp(context, rawProps, "selectable", sourceProps.selectable, {false})),
|
|
@@ -66,6 +67,10 @@ folly::dynamic EnrichedMarkdownProps::getDiffProps(
|
|
|
66
67
|
result["markdownStyle"] = toDynamic(markdownStyle);
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
if (emitLinkLayout != oldProps->emitLinkLayout) {
|
|
71
|
+
result["emitLinkLayout"] = emitLinkLayout;
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
if (enableTaskListItemToggle != oldProps->enableTaskListItemToggle) {
|
|
70
75
|
result["enableTaskListItemToggle"] = enableTaskListItemToggle;
|
|
71
76
|
}
|
|
@@ -304,6 +309,7 @@ EnrichedMarkdownTextProps::EnrichedMarkdownTextProps(
|
|
|
304
309
|
|
|
305
310
|
markdown(convertRawProp(context, rawProps, "markdown", sourceProps.markdown, {})),
|
|
306
311
|
markdownStyle(convertRawProp(context, rawProps, "markdownStyle", sourceProps.markdownStyle, {})),
|
|
312
|
+
emitLinkLayout(convertRawProp(context, rawProps, "emitLinkLayout", sourceProps.emitLinkLayout, {false})),
|
|
307
313
|
enableTaskListItemToggle(convertRawProp(context, rawProps, "enableTaskListItemToggle", sourceProps.enableTaskListItemToggle, {true})),
|
|
308
314
|
enableLinkPreview(convertRawProp(context, rawProps, "enableLinkPreview", sourceProps.enableLinkPreview, {true})),
|
|
309
315
|
selectable(convertRawProp(context, rawProps, "selectable", sourceProps.selectable, {false})),
|
|
@@ -349,6 +355,10 @@ folly::dynamic EnrichedMarkdownTextProps::getDiffProps(
|
|
|
349
355
|
result["markdownStyle"] = toDynamic(markdownStyle);
|
|
350
356
|
}
|
|
351
357
|
|
|
358
|
+
if (emitLinkLayout != oldProps->emitLinkLayout) {
|
|
359
|
+
result["emitLinkLayout"] = emitLinkLayout;
|
|
360
|
+
}
|
|
361
|
+
|
|
352
362
|
if (enableTaskListItemToggle != oldProps->enableTaskListItemToggle) {
|
|
353
363
|
result["enableTaskListItemToggle"] = enableTaskListItemToggle;
|
|
354
364
|
}
|