react-native-enriched-markdown-scaffold 0.6.0-scaffold.8 → 0.7.4-scaffold.10
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/ReactNativeEnrichedMarkdown.podspec +62 -11
- package/android/build.gradle +5 -0
- 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/EnrichedMarkdownTextInputManagerDelegate.java +12 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +4 -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/Props.cpp +25 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +793 -0
- package/android/src/main/baseline-prof.txt +75 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +10 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +9 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +6 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/AccessibilityLabels.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +44 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +72 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +110 -12
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockSyntaxSentinel.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +84 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputRemend.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputLayoutManager.kt +30 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +13 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +54 -13
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +4 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +27 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathFontWeight.kt +25 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +12 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +37 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/SpannableExtensions.kt +12 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkEvents.kt +22 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +36 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +74 -4
- package/android/src/main/jni/CMakeLists.txt +15 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +2 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlinePlaceholderSpan.kt +7 -3
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +8 -5
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathTypeface.kt +30 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +27 -3
- package/app.plugin.js +2 -1
- package/cpp/parser/MD4CParser.cpp +1 -1
- package/cpp/wasm/build.sh +9 -9
- package/docs/ACCESSIBILITY.md +172 -0
- package/docs/API_REFERENCE.md +957 -0
- package/docs/COPY_OPTIONS.md +127 -0
- package/docs/ELEMENTS_STRUCTURE.md +112 -0
- package/docs/IMAGE_CACHING.md +25 -0
- package/docs/INPUT.md +281 -0
- package/docs/LATEX_MATH.md +198 -0
- package/docs/MACOS.md +19 -0
- package/docs/MARKDOWN_STREAMING.md +36 -0
- package/docs/MENTIONS.md +97 -0
- package/docs/RTL.md +82 -0
- package/docs/STYLES.md +522 -0
- package/docs/TEXT.md +110 -0
- package/docs/WEB.md +49 -0
- package/ios/EnrichedMarkdown.mm +126 -37
- package/ios/EnrichedMarkdownText.mm +84 -16
- package/ios/attachments/ENRMMathInlineAttachment+macOS.m +5 -1
- package/ios/attachments/ENRMMathInlineAttachment.h +3 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +2 -1
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +25 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +793 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +15 -0
- package/ios/input/ENRMBlockSyntaxSentinel.h +25 -0
- package/ios/input/ENRMBlockSyntaxSentinel.mm +80 -0
- package/ios/input/ENRMFormattingStore.h +5 -0
- package/ios/input/ENRMFormattingStore.mm +34 -0
- package/ios/input/ENRMInputFormatter.h +4 -0
- package/ios/input/ENRMInputFormatter.mm +2 -0
- package/ios/input/ENRMInputParser.h +5 -0
- package/ios/input/ENRMInputParser.mm +79 -6
- package/ios/input/ENRMInputTextView.h +2 -0
- package/ios/input/ENRMInputTextView.mm +8 -17
- package/ios/input/ENRMMarkdownSerializer.mm +1 -1
- package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +71 -53
- package/ios/input/EnrichedMarkdownTextInput+Internal.h +26 -0
- package/ios/input/EnrichedMarkdownTextInput.h +1 -0
- package/ios/input/EnrichedMarkdownTextInput.mm +249 -31
- package/ios/input/InputStylePropsUtils.h +13 -0
- package/ios/input/styles/ENRMBoldStyleHandler.mm +3 -2
- package/ios/input/styles/ENRMItalicStyleHandler.mm +3 -2
- package/ios/internals/MeasurementCache.h +1 -0
- package/ios/math/ENRMRaTeXBridge.swift +52 -3
- package/ios/parser/ENRMMarkdownParser.h +8 -0
- package/ios/parser/ENRMMarkdownParser.mm +11 -0
- package/ios/renderer/AttributedRenderer.m +51 -6
- package/ios/renderer/ENRMMathInlineRenderer.m +8 -1
- package/ios/renderer/ListItemRenderer.m +8 -5
- package/ios/styles/ENRMFontSlot.h +1 -1
- package/ios/styles/StyleConfig.h +6 -0
- package/ios/styles/StyleConfig.mm +36 -0
- package/ios/utils/ENRMAccessibilityLabels.h +36 -0
- package/ios/utils/ENRMAccessibilityLabels.m +5 -0
- package/ios/utils/ENRMMathFontWeight.h +18 -0
- package/ios/utils/EditMenuUtils+macOS.m +4 -5
- package/ios/utils/EditMenuUtils.h +70 -0
- package/ios/utils/EditMenuUtils.m +14 -11
- package/ios/utils/LastElementUtils.h +39 -2
- package/ios/utils/MarkdownAccessibilityElementBuilder.h +18 -16
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +64 -32
- package/ios/utils/ParagraphStyleUtils.m +21 -2
- package/ios/utils/StylePropsUtils.h +21 -0
- package/ios/views/ENRMMathContainerView.h +8 -0
- package/ios/views/ENRMMathContainerView.m +10 -6
- package/ios/views/ENRMTableIOSGridView.h +40 -0
- package/ios/views/ENRMTableIOSGridView.m +216 -0
- package/ios/views/EnrichedMarkdownInternalText.h +2 -0
- package/ios/views/EnrichedMarkdownInternalText.m +2 -0
- package/ios/views/TableContainerView.h +8 -0
- package/ios/views/TableContainerView.m +72 -128
- package/lib/module/EnrichedMarkdownNativeComponent.ts +40 -0
- package/lib/module/EnrichedMarkdownTextInput.js +50 -1
- package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +47 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +40 -0
- package/lib/module/accessibilityLabelDefaults.js +64 -0
- package/lib/module/accessibilityLabelDefaults.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +72 -4
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +5 -2
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +5 -2
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/normalizeMarkdownTextInputStyle.js +7 -3
- package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
- package/lib/module/normalizeMenuItem.js +38 -0
- package/lib/module/normalizeMenuItem.js.map +1 -0
- package/lib/module/types/AccessibilityLabels.js +2 -0
- package/lib/module/types/AccessibilityLabels.js.map +1 -0
- package/lib/module/web/globalStyles.js +8 -1
- package/lib/module/web/globalStyles.js.map +1 -1
- package/lib/module/web/styles.js +3 -1
- package/lib/module/web/styles.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +37 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +57 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +42 -0
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +37 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/accessibilityLabelDefaults.d.ts +16 -0
- package/lib/typescript/src/accessibilityLabelDefaults.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -2
- package/lib/typescript/src/index.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/normalizeMarkdownTextInputStyle.d.ts +1 -0
- package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMenuItem.d.ts +7 -0
- package/lib/typescript/src/normalizeMenuItem.d.ts.map +1 -0
- package/lib/typescript/src/types/AccessibilityLabels.d.ts +114 -0
- package/lib/typescript/src/types/AccessibilityLabels.d.ts.map +1 -0
- package/lib/typescript/src/types/MarkdownStyle.d.ts +32 -0
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +3 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +78 -9
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/package.json +4 -2
- package/plugin/build/withAndroidMath.js +19 -0
- package/plugin/build/withIosMath.js +28 -0
- package/plugin/build/withReactNativeEnrichedMarkdown.js +11 -0
- package/plugin/tsconfig.build.json +16 -0
- package/src/EnrichedMarkdownNativeComponent.ts +40 -0
- package/src/EnrichedMarkdownTextInput.tsx +121 -1
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +47 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +40 -0
- package/src/accessibilityLabelDefaults.ts +83 -0
- package/src/index.tsx +11 -0
- package/src/native/EnrichedMarkdownText.tsx +120 -6
- package/src/normalizeMarkdownStyle.ts +6 -1
- package/src/normalizeMarkdownStyle.web.ts +6 -1
- package/src/normalizeMarkdownTextInputStyle.ts +5 -0
- package/src/normalizeMenuItem.ts +52 -0
- package/src/types/AccessibilityLabels.ts +118 -0
- package/src/types/MarkdownStyle.ts +32 -0
- package/src/types/MarkdownStyleInternal.ts +3 -0
- package/src/types/MarkdownTextProps.ts +74 -9
- package/src/web/globalStyles.ts +7 -0
- package/src/web/styles.ts +5 -1
- package/lib/module/plugin/withAndroidMath.js +0 -20
- package/lib/module/plugin/withAndroidMath.js.map +0 -1
- package/lib/module/plugin/withIosMath.js +0 -23
- package/lib/module/plugin/withIosMath.js.map +0 -1
- package/lib/module/plugin/withReactNativeEnrichedMarkdown.js +0 -16
- package/lib/module/plugin/withReactNativeEnrichedMarkdown.js.map +0 -1
- package/lib/typescript/src/plugin/withAndroidMath.d.ts +0 -5
- package/lib/typescript/src/plugin/withAndroidMath.d.ts.map +0 -1
- package/lib/typescript/src/plugin/withIosMath.d.ts +0 -5
- package/lib/typescript/src/plugin/withIosMath.d.ts.map +0 -1
- package/lib/typescript/src/plugin/withReactNativeEnrichedMarkdown.d.ts +0 -6
- package/lib/typescript/src/plugin/withReactNativeEnrichedMarkdown.d.ts.map +0 -1
- /package/{src/plugin → plugin/src}/withAndroidMath.ts +0 -0
- /package/{src/plugin → plugin/src}/withIosMath.ts +0 -0
- /package/{src/plugin → plugin/src}/withReactNativeEnrichedMarkdown.ts +0 -0
package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt
CHANGED
|
@@ -14,12 +14,34 @@ import com.swmansion.enriched.markdown.input.formatting.MarkdownSerializer
|
|
|
14
14
|
import com.swmansion.enriched.markdown.input.model.FormattingRange
|
|
15
15
|
import com.swmansion.enriched.markdown.input.model.StyleType
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
data class InputSelectionMenuConfig(
|
|
18
|
+
val format: Boolean = true,
|
|
19
|
+
val formatLabel: String = "",
|
|
20
|
+
val copyAsMarkdown: Boolean = true,
|
|
21
|
+
val copyAsMarkdownLabel: String = "",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
data class FormatMenuConfig(
|
|
25
|
+
val bold: Boolean = true,
|
|
26
|
+
val boldLabel: String = "",
|
|
27
|
+
val italic: Boolean = true,
|
|
28
|
+
val italicLabel: String = "",
|
|
29
|
+
val underline: Boolean = true,
|
|
30
|
+
val underlineLabel: String = "",
|
|
31
|
+
val strikethrough: Boolean = true,
|
|
32
|
+
val strikethroughLabel: String = "",
|
|
33
|
+
val spoiler: Boolean = true,
|
|
34
|
+
val spoilerLabel: String = "",
|
|
35
|
+
val link: Boolean = true,
|
|
36
|
+
val linkLabel: String = "",
|
|
37
|
+
)
|
|
18
38
|
|
|
19
39
|
class InputContextMenu(
|
|
20
40
|
private val view: EnrichedMarkdownTextInputView,
|
|
21
41
|
) {
|
|
22
42
|
private var customItemTexts: List<String> = emptyList()
|
|
43
|
+
var selectionMenuConfig: InputSelectionMenuConfig = InputSelectionMenuConfig()
|
|
44
|
+
var formatMenuConfig: FormatMenuConfig = FormatMenuConfig()
|
|
23
45
|
|
|
24
46
|
fun setContextMenuItems(items: List<String>) {
|
|
25
47
|
customItemTexts = items
|
|
@@ -42,13 +64,20 @@ class InputContextMenu(
|
|
|
42
64
|
menu.removeGroup(FORMAT_MENU_GROUP_ID)
|
|
43
65
|
menu.removeGroup(CUSTOM_MENU_GROUP_ID)
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
67
|
+
if (selectionMenuConfig.format) {
|
|
68
|
+
val formatSubMenu =
|
|
69
|
+
menu.addSubMenu(FORMAT_MENU_GROUP_ID, MENU_FORMAT_ID, 100, selectionMenuConfig.formatLabel)
|
|
70
|
+
FORMAT_ITEMS.forEachIndexed { index, (styleType, labelOf) ->
|
|
71
|
+
if (isFormatItemVisible(styleType)) {
|
|
72
|
+
formatSubMenu.add(Menu.NONE, MENU_FORMAT_ITEM_BASE + index, index, labelOf(formatMenuConfig))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
48
75
|
}
|
|
49
76
|
|
|
50
77
|
if (view.selectionStart < view.selectionEnd) {
|
|
51
|
-
|
|
78
|
+
if (selectionMenuConfig.copyAsMarkdown) {
|
|
79
|
+
menu.add(FORMAT_MENU_GROUP_ID, MENU_COPY_MARKDOWN_ID, 101, selectionMenuConfig.copyAsMarkdownLabel)
|
|
80
|
+
}
|
|
52
81
|
|
|
53
82
|
customItemTexts.forEachIndexed { index, text ->
|
|
54
83
|
menu
|
|
@@ -74,7 +103,7 @@ class InputContextMenu(
|
|
|
74
103
|
|
|
75
104
|
val formatIndex = itemId - MENU_FORMAT_ITEM_BASE
|
|
76
105
|
if (formatIndex in FORMAT_ITEMS.indices) {
|
|
77
|
-
applyFormat(FORMAT_ITEMS[formatIndex].
|
|
106
|
+
applyFormat(FORMAT_ITEMS[formatIndex].first)
|
|
78
107
|
mode.finish()
|
|
79
108
|
return true
|
|
80
109
|
}
|
|
@@ -96,6 +125,16 @@ class InputContextMenu(
|
|
|
96
125
|
}
|
|
97
126
|
}
|
|
98
127
|
|
|
128
|
+
private fun isFormatItemVisible(styleType: StyleType): Boolean =
|
|
129
|
+
when (styleType) {
|
|
130
|
+
StyleType.BOLD -> formatMenuConfig.bold
|
|
131
|
+
StyleType.ITALIC -> formatMenuConfig.italic
|
|
132
|
+
StyleType.UNDERLINE -> formatMenuConfig.underline
|
|
133
|
+
StyleType.STRIKETHROUGH -> formatMenuConfig.strikethrough
|
|
134
|
+
StyleType.SPOILER -> formatMenuConfig.spoiler
|
|
135
|
+
StyleType.LINK -> formatMenuConfig.link
|
|
136
|
+
}
|
|
137
|
+
|
|
99
138
|
private fun applyFormat(styleType: StyleType) {
|
|
100
139
|
val start = view.selectionStart
|
|
101
140
|
val end = view.selectionEnd
|
|
@@ -168,14 +207,16 @@ class InputContextMenu(
|
|
|
168
207
|
private const val CUSTOM_MENU_GROUP_ID = 2000
|
|
169
208
|
private const val MENU_CUSTOM_BASE = 2001
|
|
170
209
|
|
|
171
|
-
|
|
210
|
+
// Order here defines submenu order and the index used by
|
|
211
|
+
// `MENU_FORMAT_ITEM_BASE + index` when handling clicks.
|
|
212
|
+
private val FORMAT_ITEMS: List<Pair<StyleType, (FormatMenuConfig) -> String>> =
|
|
172
213
|
listOf(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
StyleType.BOLD to { it.boldLabel },
|
|
215
|
+
StyleType.ITALIC to { it.italicLabel },
|
|
216
|
+
StyleType.UNDERLINE to { it.underlineLabel },
|
|
217
|
+
StyleType.STRIKETHROUGH to { it.strikethroughLabel },
|
|
218
|
+
StyleType.SPOILER to { it.spoilerLabel },
|
|
219
|
+
StyleType.LINK to { it.linkLabel },
|
|
179
220
|
)
|
|
180
221
|
}
|
|
181
222
|
}
|
|
@@ -41,3 +41,20 @@ data class MarkdownASTNode(
|
|
|
41
41
|
|
|
42
42
|
fun getAttribute(key: String): String? = attributes[key]
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
// A node type that md4c emits as a standalone block stacked vertically, as opposed to an inline span.
|
|
46
|
+
internal fun MarkdownASTNode.NodeType.isTopLevelBlock(): Boolean =
|
|
47
|
+
when (this) {
|
|
48
|
+
MarkdownASTNode.NodeType.Paragraph,
|
|
49
|
+
MarkdownASTNode.NodeType.Heading,
|
|
50
|
+
MarkdownASTNode.NodeType.Blockquote,
|
|
51
|
+
MarkdownASTNode.NodeType.UnorderedList,
|
|
52
|
+
MarkdownASTNode.NodeType.OrderedList,
|
|
53
|
+
MarkdownASTNode.NodeType.CodeBlock,
|
|
54
|
+
MarkdownASTNode.NodeType.ThematicBreak,
|
|
55
|
+
MarkdownASTNode.NodeType.Table,
|
|
56
|
+
MarkdownASTNode.NodeType.LatexMathDisplay,
|
|
57
|
+
-> true
|
|
58
|
+
|
|
59
|
+
else -> false
|
|
60
|
+
}
|
|
@@ -52,10 +52,10 @@ class SpanStyleCache(
|
|
|
52
52
|
val paragraphColor = style.paragraphStyle.color
|
|
53
53
|
return buildList {
|
|
54
54
|
style.strongStyle.color
|
|
55
|
-
?.takeIf { it != 0 }
|
|
55
|
+
?.takeIf { it != 0 && it != paragraphColor }
|
|
56
56
|
?.let { add(it) }
|
|
57
57
|
style.emphasisStyle.color
|
|
58
|
-
?.takeIf { it != 0 }
|
|
58
|
+
?.takeIf { it != 0 && it != paragraphColor }
|
|
59
59
|
?.let { add(it) }
|
|
60
60
|
style.linkStyle.color
|
|
61
61
|
.takeIf { it != 0 && it != paragraphColor }
|
|
@@ -68,10 +68,10 @@ class SpanStyleCache(
|
|
|
68
68
|
style
|
|
69
69
|
.codeStyle
|
|
70
70
|
.color
|
|
71
|
-
.takeIf { it != 0 }
|
|
71
|
+
.takeIf { it != 0 && it != paragraphColor }
|
|
72
72
|
?.let { add(it) }
|
|
73
73
|
style.taskListStyle.checkedTextColor
|
|
74
|
-
.takeIf { it != 0 }
|
|
74
|
+
.takeIf { it != 0 && it != paragraphColor }
|
|
75
75
|
?.let { add(it) }
|
|
76
76
|
}.toIntArray()
|
|
77
77
|
}
|
|
@@ -4,11 +4,14 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.graphics.Typeface
|
|
5
5
|
import android.text.TextPaint
|
|
6
6
|
import android.text.style.MetricAffectingSpan
|
|
7
|
+
import com.swmansion.enriched.markdown.renderer.SpanStyleCache
|
|
7
8
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
9
|
+
import com.swmansion.enriched.markdown.utils.text.extensions.applyColorPreserving
|
|
8
10
|
|
|
9
11
|
class HeadingSpan(
|
|
10
12
|
val level: Int,
|
|
11
13
|
styleConfig: StyleConfig,
|
|
14
|
+
private val styleCache: SpanStyleCache,
|
|
12
15
|
) : MetricAffectingSpan() {
|
|
13
16
|
private val fontSize: Float = styleConfig.headingStyles[level]!!.fontSize
|
|
14
17
|
private val color: Int = styleConfig.headingStyles[level]!!.color
|
|
@@ -16,7 +19,11 @@ class HeadingSpan(
|
|
|
16
19
|
|
|
17
20
|
override fun updateDrawState(tp: TextPaint) {
|
|
18
21
|
applyHeadingStyle(tp)
|
|
19
|
-
|
|
22
|
+
// Inline spans (link, code, strong/em with their own color) are attached to the range
|
|
23
|
+
// BEFORE this block span, and TextLine replays every CharacterStyle in insertion order,
|
|
24
|
+
// so an unconditional `tp.color = color` here would repaint them. Same guard used by
|
|
25
|
+
// BlockquoteSpan, BaseListSpan, CodeBlockSpan, StrongSpan and EmphasisSpan.
|
|
26
|
+
tp.applyColorPreserving(color, *styleCache.colorsToPreserve)
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
override fun updateMeasureState(tp: TextPaint) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package com.swmansion.enriched.markdown.spans
|
|
2
2
|
|
|
3
3
|
import android.graphics.Paint
|
|
4
|
+
import android.text.Spanned
|
|
5
|
+
import android.text.style.ReplacementSpan
|
|
4
6
|
import kotlin.math.ceil
|
|
5
7
|
import kotlin.math.floor
|
|
6
8
|
import android.text.style.LineHeightSpan as AndroidLineHeightSpan
|
|
@@ -18,10 +20,34 @@ class LineHeightSpan(
|
|
|
18
20
|
v: Int,
|
|
19
21
|
fm: Paint.FontMetricsInt?,
|
|
20
22
|
) {
|
|
21
|
-
if (fm == null) return
|
|
23
|
+
if (fm == null || lineHeight <= 0) return
|
|
22
24
|
|
|
23
25
|
val leading = lineHeight - ((-fm.ascent) + fm.descent)
|
|
26
|
+
|
|
27
|
+
// A ReplacementSpan on this line — inline math, or the placeholder MeasurementStore
|
|
28
|
+
// swaps in to measure it off the main thread — reports its own metrics from getSize().
|
|
29
|
+
// Shrinking those back down to the configured line height cuts the line box below what
|
|
30
|
+
// the span actually draws, and can drive descent negative so the next line starts above
|
|
31
|
+
// this one's baseline. Those lines are expanded only, never compressed; every other line
|
|
32
|
+
// keeps the exact clamp it had before. The lookup only runs on the rare negative-leading
|
|
33
|
+
// path, so the common case stays allocation-free.
|
|
34
|
+
if (leading <= 0 && hasSelfMeasuringSpan(text, start, end)) return
|
|
35
|
+
|
|
24
36
|
fm.ascent -= ceil(leading / 2.0f).toInt()
|
|
25
37
|
fm.descent += floor(leading / 2.0f).toInt()
|
|
26
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Images are excluded on purpose. Block images already keep this span off their range via
|
|
42
|
+
* `applyLineHeightSkippingImages`, and inline ones never touch the metrics at all, so
|
|
43
|
+
* letting them through here would change image layout instead of math layout.
|
|
44
|
+
*/
|
|
45
|
+
private fun hasSelfMeasuringSpan(
|
|
46
|
+
text: CharSequence?,
|
|
47
|
+
start: Int,
|
|
48
|
+
end: Int,
|
|
49
|
+
): Boolean {
|
|
50
|
+
val spanned = text as? Spanned ?: return false
|
|
51
|
+
return spanned.getSpans(start, end, ReplacementSpan::class.java).any { it !is ImageSpan }
|
|
52
|
+
}
|
|
27
53
|
}
|
|
@@ -4,6 +4,8 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
4
4
|
|
|
5
5
|
data class InlineMathStyle(
|
|
6
6
|
val color: Int,
|
|
7
|
+
val paddingVertical: Float,
|
|
8
|
+
val fontWeight: String,
|
|
7
9
|
) {
|
|
8
10
|
companion object {
|
|
9
11
|
fun fromReadableMap(
|
|
@@ -11,7 +13,9 @@ data class InlineMathStyle(
|
|
|
11
13
|
parser: StyleParser,
|
|
12
14
|
): InlineMathStyle {
|
|
13
15
|
val color = parser.parseColor(map, "color")
|
|
14
|
-
|
|
16
|
+
val paddingVertical = parser.toPixelFromDIP(map.getDouble("paddingVertical").toFloat())
|
|
17
|
+
val fontWeight = parser.parseString(map, "fontWeight", "normal")
|
|
18
|
+
return InlineMathStyle(color, paddingVertical, fontWeight)
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.styles
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Weight a formula can actually be typeset at.
|
|
5
|
+
*
|
|
6
|
+
* The style model keeps `fontWeight` as a [String], the way every other
|
|
7
|
+
* `fontWeight` prop in the codebase does, so a value like `"600"` survives as
|
|
8
|
+
* far as the renderer. This enum is the narrowing step at that last hop — see
|
|
9
|
+
* `ratexTypefaceLoader`, the only caller — so adding a third weight is a
|
|
10
|
+
* compile error at the draw site rather than a value that silently falls back
|
|
11
|
+
* to regular. Mirrors ENRMMathFontWeight.h on iOS.
|
|
12
|
+
*/
|
|
13
|
+
enum class MathFontWeight {
|
|
14
|
+
NORMAL,
|
|
15
|
+
BOLD,
|
|
16
|
+
;
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
fun fromString(value: String): MathFontWeight =
|
|
20
|
+
when (value.lowercase()) {
|
|
21
|
+
"bold" -> BOLD
|
|
22
|
+
else -> NORMAL
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -10,6 +10,7 @@ data class MathStyle(
|
|
|
10
10
|
val marginTop: Float,
|
|
11
11
|
val marginBottom: Float,
|
|
12
12
|
val textAlign: String,
|
|
13
|
+
val fontWeight: String,
|
|
13
14
|
) {
|
|
14
15
|
companion object {
|
|
15
16
|
fun fromReadableMap(
|
|
@@ -23,7 +24,17 @@ data class MathStyle(
|
|
|
23
24
|
val marginTop = parser.toPixelFromDIP(map.getDouble("marginTop").toFloat())
|
|
24
25
|
val marginBottom = parser.toPixelFromDIP(map.getDouble("marginBottom").toFloat())
|
|
25
26
|
val textAlign = parser.parseString(map, "textAlign", "center")
|
|
26
|
-
|
|
27
|
+
val fontWeight = parser.parseString(map, "fontWeight", "normal")
|
|
28
|
+
return MathStyle(
|
|
29
|
+
fontSize,
|
|
30
|
+
color,
|
|
31
|
+
backgroundColor,
|
|
32
|
+
padding,
|
|
33
|
+
marginTop,
|
|
34
|
+
marginBottom,
|
|
35
|
+
textAlign,
|
|
36
|
+
fontWeight,
|
|
37
|
+
)
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
40
|
}
|
|
@@ -4,6 +4,7 @@ import android.view.View
|
|
|
4
4
|
import com.facebook.react.bridge.ReadableArray
|
|
5
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
6
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
+
import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
|
|
7
8
|
import com.swmansion.enriched.markdown.events.ContextMenuItemPressEvent
|
|
8
9
|
import com.swmansion.enriched.markdown.events.LinkLongPressEvent
|
|
9
10
|
import com.swmansion.enriched.markdown.events.LinkPressEvent
|
|
@@ -81,11 +82,11 @@ fun emitContextMenuItemPress(
|
|
|
81
82
|
|
|
82
83
|
fun parseMd4cFlags(flags: ReadableMap?): Md4cFlags =
|
|
83
84
|
Md4cFlags(
|
|
84
|
-
underline = flags
|
|
85
|
-
latexMath = FeatureFlags.IS_MATH_ENABLED &&
|
|
86
|
-
superscript = flags
|
|
87
|
-
subscript = flags
|
|
88
|
-
highlight = flags
|
|
85
|
+
underline = flags.getBooleanOrDefault("underline", false),
|
|
86
|
+
latexMath = FeatureFlags.IS_MATH_ENABLED && flags.getBooleanOrDefault("latexMath", true),
|
|
87
|
+
superscript = flags.getBooleanOrDefault("superscript", false),
|
|
88
|
+
subscript = flags.getBooleanOrDefault("subscript", false),
|
|
89
|
+
highlight = flags.getBooleanOrDefault("highlight", false),
|
|
89
90
|
)
|
|
90
91
|
|
|
91
92
|
fun parseContextMenuItems(value: ReadableArray?): List<String> =
|
|
@@ -94,7 +95,36 @@ fun parseContextMenuItems(value: ReadableArray?): List<String> =
|
|
|
94
95
|
fun parseSelectionMenuConfig(value: ReadableMap?): SelectionMenuConfig {
|
|
95
96
|
if (value == null) return SelectionMenuConfig()
|
|
96
97
|
return SelectionMenuConfig(
|
|
97
|
-
copyAsMarkdown = value.
|
|
98
|
-
copyImageUrl = value.
|
|
98
|
+
copyAsMarkdown = value.getBooleanOrDefault("copyAsMarkdown", true),
|
|
99
|
+
copyImageUrl = value.getBooleanOrDefault("copyImageUrl", true),
|
|
100
|
+
copyLabel = value.getString("copyLabel") ?: "",
|
|
101
|
+
copyAsMarkdownLabel = value.getString("copyAsMarkdownLabel") ?: "",
|
|
102
|
+
copyImageUrlLabel = value.getString("copyImageUrlLabel") ?: "",
|
|
103
|
+
copyImageUrlsLabel = value.getString("copyImageUrlsLabel") ?: "",
|
|
104
|
+
copyImageUrlPluralTemplates = parseStringList(value.getArray("copyImageUrlPluralTemplates")),
|
|
99
105
|
)
|
|
100
106
|
}
|
|
107
|
+
|
|
108
|
+
fun parseAccessibilityLabels(value: ReadableMap?): AccessibilityLabels {
|
|
109
|
+
if (value == null) return AccessibilityLabels()
|
|
110
|
+
val list = value.getMap("list")
|
|
111
|
+
val blockquote = value.getMap("blockquote")
|
|
112
|
+
val table = value.getMap("table")
|
|
113
|
+
val math = value.getMap("math")
|
|
114
|
+
val rotor = value.getMap("rotor")
|
|
115
|
+
return AccessibilityLabels(
|
|
116
|
+
bulletPoint = list?.getString("bulletPoint") ?: "",
|
|
117
|
+
nestedBulletPoint = list?.getString("nestedBulletPoint") ?: "",
|
|
118
|
+
orderedItem = list?.getString("orderedItem") ?: "",
|
|
119
|
+
nestedOrderedItem = list?.getString("nestedOrderedItem") ?: "",
|
|
120
|
+
blockquote = blockquote?.getString("quote") ?: "",
|
|
121
|
+
nestedBlockquote = blockquote?.getString("nestedQuote") ?: "",
|
|
122
|
+
tableRow = table?.getString("row") ?: "",
|
|
123
|
+
mathEquation = math?.getString("equation") ?: "",
|
|
124
|
+
rotorHeadings = rotor?.getString("headings") ?: "",
|
|
125
|
+
rotorLinks = rotor?.getString("links") ?: "",
|
|
126
|
+
rotorImages = rotor?.getString("images") ?: "",
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private fun parseStringList(value: ReadableArray?): List<String> = (0 until (value?.size() ?: 0)).mapNotNull { value?.getString(it) }
|
package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt
CHANGED
|
@@ -76,6 +76,8 @@ object MarkdownStyleParser {
|
|
|
76
76
|
if (mathMap?.hasKey("selectedBackgroundColor") == true) mathMap.getInt("selectedBackgroundColor") else null
|
|
77
77
|
val selectedBorderColor =
|
|
78
78
|
if (mathMap?.hasKey("selectedBorderColor") == true) mathMap.getInt("selectedBorderColor") else null
|
|
79
|
+
val fontWeight =
|
|
80
|
+
(if (mathMap?.hasKey("fontWeight") == true) mathMap.getString("fontWeight") else null) ?: "normal"
|
|
79
81
|
|
|
80
82
|
return MathChipStyle(
|
|
81
83
|
color = color,
|
|
@@ -88,6 +90,7 @@ object MarkdownStyleParser {
|
|
|
88
90
|
paddingVertical = paddingVertical,
|
|
89
91
|
selectedBackgroundColor = nullIfTransparent(selectedBackgroundColor),
|
|
90
92
|
selectedBorderColor = nullIfTransparent(selectedBorderColor),
|
|
93
|
+
fontWeight = fontWeight,
|
|
91
94
|
)
|
|
92
95
|
}
|
|
93
96
|
|
|
@@ -143,6 +146,12 @@ object MarkdownStyleParser {
|
|
|
143
146
|
} else {
|
|
144
147
|
inlineFallback.selectedBorderColor
|
|
145
148
|
}
|
|
149
|
+
val fontWeight =
|
|
150
|
+
if (mathBlockMap?.hasKey("fontWeight") == true) {
|
|
151
|
+
mathBlockMap.getString("fontWeight") ?: "normal"
|
|
152
|
+
} else {
|
|
153
|
+
inlineFallback.fontWeight
|
|
154
|
+
}
|
|
146
155
|
val marginTop =
|
|
147
156
|
PixelUtil.toPixelFromDIP(
|
|
148
157
|
if (mathBlockMap?.hasKey("marginTop") == true) mathBlockMap.getDouble("marginTop").toFloat() else 8f,
|
|
@@ -170,6 +179,7 @@ object MarkdownStyleParser {
|
|
|
170
179
|
paddingVertical = paddingVertical,
|
|
171
180
|
selectedBackgroundColor = nullIfTransparent(selectedBackgroundColor),
|
|
172
181
|
selectedBorderColor = nullIfTransparent(selectedBorderColor),
|
|
182
|
+
fontWeight = fontWeight,
|
|
173
183
|
marginTop = marginTop,
|
|
174
184
|
marginBottom = marginBottom,
|
|
175
185
|
alignment = alignment,
|
|
@@ -14,8 +14,16 @@ fun SpannableStringBuilder.isInlineImage(): Boolean {
|
|
|
14
14
|
return lastChar != '\n' && lastChar != '\u200B'
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Swaps MathInlineSpans for MathInlinePlaceholderSpans safe for background-thread measurement.
|
|
19
|
+
*
|
|
20
|
+
* [paddingVertical] must be the same `inlineMath.paddingVertical` (in px) the real spans were
|
|
21
|
+
* built with, or the measured line box won't match the drawn one.
|
|
22
|
+
*/
|
|
23
|
+
fun SpannableString.replaceMathSpansWithPlaceholders(
|
|
24
|
+
context: Context,
|
|
25
|
+
paddingVertical: Float,
|
|
26
|
+
) {
|
|
19
27
|
if (!FeatureFlags.IS_MATH_ENABLED) return
|
|
20
28
|
|
|
21
29
|
try {
|
|
@@ -42,7 +50,7 @@ fun SpannableString.replaceMathSpansWithPlaceholders(context: Context) {
|
|
|
42
50
|
@Suppress("UNCHECKED_CAST")
|
|
43
51
|
val results = measureMethod.invoke(null, context, requests) as? List<MathMetrics> ?: return
|
|
44
52
|
|
|
45
|
-
val placeholderCtor = placeholderClass.getConstructor(MathMetrics::class.java)
|
|
53
|
+
val placeholderCtor = placeholderClass.getConstructor(MathMetrics::class.java, Float::class.javaPrimitiveType)
|
|
46
54
|
|
|
47
55
|
mathSpans.forEachIndexed { i, oldSpan ->
|
|
48
56
|
val metrics = results.getOrNull(i) ?: return@forEachIndexed
|
|
@@ -53,7 +61,7 @@ fun SpannableString.replaceMathSpansWithPlaceholders(context: Context) {
|
|
|
53
61
|
|
|
54
62
|
if (start != -1 && end != -1) {
|
|
55
63
|
removeSpan(oldSpan)
|
|
56
|
-
val newSpan = placeholderCtor.newInstance(metrics)
|
|
64
|
+
val newSpan = placeholderCtor.newInstance(metrics, paddingVertical)
|
|
57
65
|
setSpan(newSpan, start, end, flags)
|
|
58
66
|
}
|
|
59
67
|
}
|
|
@@ -26,18 +26,40 @@ fun View.emitLinkLongPressEvent(url: String) {
|
|
|
26
26
|
/**
|
|
27
27
|
* Cancels the JS touch for an active link tap, preventing parent
|
|
28
28
|
* Pressable/TouchableOpacity from firing onPress for the same tap.
|
|
29
|
+
*
|
|
30
|
+
* Also requests that parent ViewGroups do not intercept subsequent touch
|
|
31
|
+
* events in this gesture. This is necessary for react-native-gesture-handler's
|
|
32
|
+
* Pressable, which intercepts at the native ViewGroup level and would otherwise
|
|
33
|
+
* steal the gesture before ACTION_UP reaches the TextView.
|
|
29
34
|
*/
|
|
30
35
|
fun TextView.cancelJSTouchForLinkTap(event: MotionEvent) {
|
|
31
36
|
val currentMovementMethod = movementMethod
|
|
32
37
|
if (currentMovementMethod is LinkLongPressMovementMethod && currentMovementMethod.isLinkTouchActive) {
|
|
38
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
33
39
|
NativeGestureUtil.notifyNativeGestureStarted(this, event)
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Re-allows parent ViewGroups to intercept touch events once the link
|
|
45
|
+
* touch is no longer active (slop exceeded, gesture ended, or cancelled).
|
|
46
|
+
* This restores normal scrolling behavior for parent ScrollView/RecyclerView.
|
|
47
|
+
*
|
|
48
|
+
* Must be called after [LinkLongPressMovementMethod.onTouchEvent] has
|
|
49
|
+
* processed the event, since that is where [isLinkTouchActive] is updated.
|
|
50
|
+
*/
|
|
51
|
+
fun TextView.reallowParentInterceptIfLinkReleased() {
|
|
52
|
+
val currentMovementMethod = movementMethod
|
|
53
|
+
if (currentMovementMethod is LinkLongPressMovementMethod && !currentMovementMethod.isLinkTouchActive) {
|
|
54
|
+
parent?.requestDisallowInterceptTouchEvent(false)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
37
58
|
/**
|
|
38
59
|
* Cancels the JS touch unconditionally, preventing parent
|
|
39
60
|
* Pressable/TouchableOpacity from firing onPress for the same gesture.
|
|
40
61
|
*/
|
|
41
62
|
fun View.cancelJSTouchForCheckboxTap(event: MotionEvent) {
|
|
63
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
42
64
|
NativeGestureUtil.notifyNativeGestureStarted(this, event)
|
|
43
65
|
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt
CHANGED
|
@@ -25,8 +25,30 @@ private const val MENU_ITEM_CUSTOM_GROUP = 2001
|
|
|
25
25
|
data class SelectionMenuConfig(
|
|
26
26
|
val copyAsMarkdown: Boolean = true,
|
|
27
27
|
val copyImageUrl: Boolean = true,
|
|
28
|
+
val copyLabel: String = "",
|
|
29
|
+
val copyAsMarkdownLabel: String = "",
|
|
30
|
+
val copyImageUrlLabel: String = "",
|
|
31
|
+
val copyImageUrlsLabel: String = "",
|
|
32
|
+
// Plural templates indexed by image count (0..100). Empty when no pluralLabels
|
|
33
|
+
// are set; counts > 100 use copyImageUrlsLabel (the "other" form).
|
|
34
|
+
val copyImageUrlPluralTemplates: List<String> = emptyList(),
|
|
28
35
|
)
|
|
29
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Resolves the "Copy Image URL(s)" menu title for the given image count. Uses the
|
|
39
|
+
* precomputed plural templates when present (counts > 100 use the "other" form),
|
|
40
|
+
* otherwise the singular/`{count}` templates. All labels are resolved JS-side.
|
|
41
|
+
*/
|
|
42
|
+
private fun SelectionMenuConfig.imageUrlsTitle(count: Int): String {
|
|
43
|
+
val template =
|
|
44
|
+
when {
|
|
45
|
+
copyImageUrlPluralTemplates.isNotEmpty() && count in 0..100 -> copyImageUrlPluralTemplates[count]
|
|
46
|
+
count == 1 -> copyImageUrlLabel
|
|
47
|
+
else -> copyImageUrlsLabel
|
|
48
|
+
}
|
|
49
|
+
return template.replace("{count}", count.toString())
|
|
50
|
+
}
|
|
51
|
+
|
|
30
52
|
/**
|
|
31
53
|
* Creates an ActionMode.Callback that adds custom copy options and
|
|
32
54
|
* overrides the default "Copy" action to include HTML for rich text support.
|
|
@@ -56,12 +78,24 @@ fun createSelectionActionModeCallback(
|
|
|
56
78
|
|
|
57
79
|
val selectionMenuConfig = getSelectionMenuConfig()
|
|
58
80
|
|
|
81
|
+
// The system Copy item is added by the platform; relabel it so it
|
|
82
|
+
// matches the rest of the localized menu (iOS rebuilds the menu so
|
|
83
|
+
// gets this for free; Android only intercepts the click).
|
|
84
|
+
if (selectionMenuConfig.copyLabel.isNotEmpty()) {
|
|
85
|
+
menu.findItem(android.R.id.copy)?.title = selectionMenuConfig.copyLabel
|
|
86
|
+
}
|
|
87
|
+
|
|
59
88
|
if (
|
|
60
89
|
selectionMenuConfig.copyAsMarkdown &&
|
|
61
90
|
textView.selectionStart >= 0 &&
|
|
62
91
|
textView.selectionEnd > textView.selectionStart
|
|
63
92
|
) {
|
|
64
|
-
menu.add(
|
|
93
|
+
menu.add(
|
|
94
|
+
Menu.NONE,
|
|
95
|
+
MENU_ITEM_COPY_MARKDOWN,
|
|
96
|
+
Menu.NONE,
|
|
97
|
+
selectionMenuConfig.copyAsMarkdownLabel,
|
|
98
|
+
)
|
|
65
99
|
}
|
|
66
100
|
|
|
67
101
|
if (textView.selectionStart >= 0 && textView.selectionEnd > textView.selectionStart) {
|
|
@@ -80,12 +114,7 @@ fun createSelectionActionModeCallback(
|
|
|
80
114
|
emptyList()
|
|
81
115
|
}
|
|
82
116
|
if (imageUrls.isNotEmpty()) {
|
|
83
|
-
val title =
|
|
84
|
-
if (imageUrls.size == 1) {
|
|
85
|
-
"Copy Image URL"
|
|
86
|
-
} else {
|
|
87
|
-
"Copy ${imageUrls.size} Image URLs"
|
|
88
|
-
}
|
|
117
|
+
val title = selectionMenuConfig.imageUrlsTitle(imageUrls.size)
|
|
89
118
|
menu.add(Menu.NONE, MENU_ITEM_COPY_IMAGE_URL, Menu.NONE, title)
|
|
90
119
|
}
|
|
91
120
|
|