react-native-enriched-markdown-scaffold 0.7.4-scaffold.10 → 0.7.4-scaffold.12
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/EnrichedMarkdownTextInputManagerDelegate.java +6 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +2 -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 +10 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +7 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +15 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +39 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +11 -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 +14 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +8 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +18 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +128 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +8 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +19 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +2 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +2 -0
- package/docs/API_REFERENCE.md +64 -0
- package/docs/COPY_OPTIONS.md +13 -0
- package/docs/INPUT.md +39 -0
- package/docs/WEB.md +1 -0
- package/ios/EnrichedMarkdown.mm +14 -0
- package/ios/EnrichedMarkdownText.mm +10 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +10 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +7 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +15 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +3 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +29 -0
- package/ios/input/ENRMInputTextView.mm +11 -1
- package/ios/input/EnrichedMarkdownTextInput.h +5 -0
- package/ios/input/EnrichedMarkdownTextInput.mm +33 -0
- package/ios/views/ENRMMathContainerView.h +4 -0
- package/ios/views/ENRMMathContainerView.m +11 -0
- package/ios/views/TableContainerView.h +4 -0
- package/ios/views/TableContainerView.m +7 -0
- package/lib/module/EnrichedMarkdownNativeComponent.ts +4 -0
- package/lib/module/EnrichedMarkdownTextInput.js +15 -0
- package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +22 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +2 -0
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +4 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +24 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +16 -0
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +4 -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/native/EnrichedMarkdownText.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +16 -0
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +15 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +4 -0
- package/src/EnrichedMarkdownTextInput.tsx +37 -0
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +22 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +4 -0
- package/src/index.tsx +1 -0
- package/src/native/EnrichedMarkdownText.tsx +2 -0
- package/src/types/MarkdownTextProps.ts +16 -0
- package/src/types/events.ts +16 -0
package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java
CHANGED
|
@@ -65,6 +65,9 @@ public class EnrichedMarkdownManagerDelegate<T extends View, U extends BaseViewM
|
|
|
65
65
|
case "spoilerOverlay":
|
|
66
66
|
mViewManager.setSpoilerOverlay(view, value == null ? "particles" : (String) value);
|
|
67
67
|
break;
|
|
68
|
+
case "contextMenuHidden":
|
|
69
|
+
mViewManager.setContextMenuHidden(view, value == null ? false : (boolean) value);
|
|
70
|
+
break;
|
|
68
71
|
case "contextMenuItems":
|
|
69
72
|
mViewManager.setContextMenuItems(view, (ReadableArray) value);
|
|
70
73
|
break;
|
package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java
CHANGED
|
@@ -29,6 +29,7 @@ public interface EnrichedMarkdownManagerInterface<T extends View> extends ViewMa
|
|
|
29
29
|
void setStreamingAnimation(T view, boolean value);
|
|
30
30
|
void setStreamingConfig(T view, @Nullable ReadableMap value);
|
|
31
31
|
void setSpoilerOverlay(T view, @Nullable String value);
|
|
32
|
+
void setContextMenuHidden(T view, boolean value);
|
|
32
33
|
void setContextMenuItems(T view, @Nullable ReadableArray value);
|
|
33
34
|
void setSelectionMenuConfig(T view, @Nullable ReadableMap value);
|
|
34
35
|
void setAccessibilityLabels(T view, @Nullable ReadableMap value);
|
|
@@ -50,6 +50,9 @@ public class EnrichedMarkdownTextInputManagerDelegate<T extends View, U extends
|
|
|
50
50
|
case "autoCapitalize":
|
|
51
51
|
mViewManager.setAutoCapitalize(view, value == null ? null : (String) value);
|
|
52
52
|
break;
|
|
53
|
+
case "interceptPaste":
|
|
54
|
+
mViewManager.setInterceptPaste(view, value == null ? false : (boolean) value);
|
|
55
|
+
break;
|
|
53
56
|
case "multiline":
|
|
54
57
|
mViewManager.setMultiline(view, value == null ? false : (boolean) value);
|
|
55
58
|
break;
|
|
@@ -160,6 +163,9 @@ public class EnrichedMarkdownTextInputManagerDelegate<T extends View, U extends
|
|
|
160
163
|
case "copyToClipboard":
|
|
161
164
|
mViewManager.copyToClipboard(view);
|
|
162
165
|
break;
|
|
166
|
+
case "resolvePaste":
|
|
167
|
+
mViewManager.resolvePaste(view, args.getString(0), args.getString(1));
|
|
168
|
+
break;
|
|
163
169
|
case "requestMarkdown":
|
|
164
170
|
mViewManager.requestMarkdown(view, args.getInt(0));
|
|
165
171
|
break;
|
|
@@ -24,6 +24,7 @@ public interface EnrichedMarkdownTextInputManagerInterface<T extends View> exten
|
|
|
24
24
|
void setScrollEnabled(T view, boolean value);
|
|
25
25
|
void setBlockSyntax(T view, boolean value);
|
|
26
26
|
void setAutoCapitalize(T view, @Nullable String value);
|
|
27
|
+
void setInterceptPaste(T view, boolean value);
|
|
27
28
|
void setMultiline(T view, boolean value);
|
|
28
29
|
void setCursorColor(T view, @Nullable Integer value);
|
|
29
30
|
void setSelectionColor(T view, @Nullable Integer value);
|
|
@@ -58,6 +59,7 @@ public interface EnrichedMarkdownTextInputManagerInterface<T extends View> exten
|
|
|
58
59
|
void updateMath(T view, int start, String latex, boolean displayMode);
|
|
59
60
|
void removeMath(T view, int start);
|
|
60
61
|
void copyToClipboard(T view);
|
|
62
|
+
void resolvePaste(T view, String text, String markdown);
|
|
61
63
|
void requestMarkdown(T view, int requestId);
|
|
62
64
|
void requestCaretRect(T view, int requestId);
|
|
63
65
|
}
|
|
@@ -65,6 +65,9 @@ public class EnrichedMarkdownTextManagerDelegate<T extends View, U extends BaseV
|
|
|
65
65
|
case "spoilerOverlay":
|
|
66
66
|
mViewManager.setSpoilerOverlay(view, value == null ? "particles" : (String) value);
|
|
67
67
|
break;
|
|
68
|
+
case "contextMenuHidden":
|
|
69
|
+
mViewManager.setContextMenuHidden(view, value == null ? false : (boolean) value);
|
|
70
|
+
break;
|
|
68
71
|
case "contextMenuItems":
|
|
69
72
|
mViewManager.setContextMenuItems(view, (ReadableArray) value);
|
|
70
73
|
break;
|
|
@@ -29,6 +29,7 @@ public interface EnrichedMarkdownTextManagerInterface<T extends View> extends Vi
|
|
|
29
29
|
void setStreamingAnimation(T view, boolean value);
|
|
30
30
|
void setStreamingConfig(T view, @Nullable ReadableMap value);
|
|
31
31
|
void setSpoilerOverlay(T view, @Nullable String value);
|
|
32
|
+
void setContextMenuHidden(T view, boolean value);
|
|
32
33
|
void setContextMenuItems(T view, @Nullable ReadableArray value);
|
|
33
34
|
void setSelectionMenuConfig(T view, @Nullable ReadableMap value);
|
|
34
35
|
void setAccessibilityLabels(T view, @Nullable ReadableMap value);
|
package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp
CHANGED
|
@@ -247,6 +247,16 @@ payload.setProperty(runtime, "height", event.height);
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
|
|
250
|
+
void EnrichedMarkdownTextInputEventEmitter::onPaste(OnPaste event) const {
|
|
251
|
+
dispatchEvent("paste", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
252
|
+
auto payload = jsi::Object(runtime);
|
|
253
|
+
payload.setProperty(runtime, "text", event.text);
|
|
254
|
+
payload.setProperty(runtime, "markdown", event.markdown);
|
|
255
|
+
return payload;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
250
260
|
void EnrichedMarkdownTextInputEventEmitter::onStartMention(OnStartMention event) const {
|
|
251
261
|
dispatchEvent("startMention", [event=std::move(event)](jsi::Runtime &runtime) {
|
|
252
262
|
auto payload = jsi::Object(runtime);
|
package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h
CHANGED
|
@@ -182,6 +182,11 @@ class EnrichedMarkdownTextInputEventEmitter : public ViewEventEmitter {
|
|
|
182
182
|
double height;
|
|
183
183
|
};
|
|
184
184
|
|
|
185
|
+
struct OnPaste {
|
|
186
|
+
std::string text;
|
|
187
|
+
std::string markdown;
|
|
188
|
+
};
|
|
189
|
+
|
|
185
190
|
struct OnStartMention {
|
|
186
191
|
std::string indicator;
|
|
187
192
|
};
|
|
@@ -218,6 +223,8 @@ class EnrichedMarkdownTextInputEventEmitter : public ViewEventEmitter {
|
|
|
218
223
|
|
|
219
224
|
void onMathPress(OnMathPress value) const;
|
|
220
225
|
|
|
226
|
+
void onPaste(OnPaste value) const;
|
|
227
|
+
|
|
221
228
|
void onStartMention(OnStartMention value) const;
|
|
222
229
|
|
|
223
230
|
void onChangeMention(OnChangeMention value) const;
|
|
@@ -32,6 +32,7 @@ EnrichedMarkdownProps::EnrichedMarkdownProps(
|
|
|
32
32
|
streamingAnimation(convertRawProp(context, rawProps, "streamingAnimation", sourceProps.streamingAnimation, {false})),
|
|
33
33
|
streamingConfig(convertRawProp(context, rawProps, "streamingConfig", sourceProps.streamingConfig, {})),
|
|
34
34
|
spoilerOverlay(convertRawProp(context, rawProps, "spoilerOverlay", sourceProps.spoilerOverlay, {std::string{"particles"}})),
|
|
35
|
+
contextMenuHidden(convertRawProp(context, rawProps, "contextMenuHidden", sourceProps.contextMenuHidden, {false})),
|
|
35
36
|
contextMenuItems(convertRawProp(context, rawProps, "contextMenuItems", sourceProps.contextMenuItems, {})),
|
|
36
37
|
selectionMenuConfig(convertRawProp(context, rawProps, "selectionMenuConfig", sourceProps.selectionMenuConfig, {})),
|
|
37
38
|
accessibilityLabels(convertRawProp(context, rawProps, "accessibilityLabels", sourceProps.accessibilityLabels, {})),
|
|
@@ -107,6 +108,10 @@ folly::dynamic EnrichedMarkdownProps::getDiffProps(
|
|
|
107
108
|
result["spoilerOverlay"] = spoilerOverlay;
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
if (contextMenuHidden != oldProps->contextMenuHidden) {
|
|
112
|
+
result["contextMenuHidden"] = contextMenuHidden;
|
|
113
|
+
}
|
|
114
|
+
|
|
110
115
|
if (contextMenuItems != oldProps->contextMenuItems) {
|
|
111
116
|
result["contextMenuItems"] = toDynamic(contextMenuItems);
|
|
112
117
|
}
|
|
@@ -146,6 +151,7 @@ EnrichedMarkdownTextInputProps::EnrichedMarkdownTextInputProps(
|
|
|
146
151
|
scrollEnabled(convertRawProp(context, rawProps, "scrollEnabled", sourceProps.scrollEnabled, {false})),
|
|
147
152
|
blockSyntax(convertRawProp(context, rawProps, "blockSyntax", sourceProps.blockSyntax, {false})),
|
|
148
153
|
autoCapitalize(convertRawProp(context, rawProps, "autoCapitalize", sourceProps.autoCapitalize, {})),
|
|
154
|
+
interceptPaste(convertRawProp(context, rawProps, "interceptPaste", sourceProps.interceptPaste, {false})),
|
|
149
155
|
multiline(convertRawProp(context, rawProps, "multiline", sourceProps.multiline, {false})),
|
|
150
156
|
cursorColor(convertRawProp(context, rawProps, "cursorColor", sourceProps.cursorColor, {})),
|
|
151
157
|
selectionColor(convertRawProp(context, rawProps, "selectionColor", sourceProps.selectionColor, {})),
|
|
@@ -211,6 +217,10 @@ folly::dynamic EnrichedMarkdownTextInputProps::getDiffProps(
|
|
|
211
217
|
result["autoCapitalize"] = autoCapitalize;
|
|
212
218
|
}
|
|
213
219
|
|
|
220
|
+
if (interceptPaste != oldProps->interceptPaste) {
|
|
221
|
+
result["interceptPaste"] = interceptPaste;
|
|
222
|
+
}
|
|
223
|
+
|
|
214
224
|
if (multiline != oldProps->multiline) {
|
|
215
225
|
result["multiline"] = multiline;
|
|
216
226
|
}
|
|
@@ -295,6 +305,7 @@ EnrichedMarkdownTextProps::EnrichedMarkdownTextProps(
|
|
|
295
305
|
streamingAnimation(convertRawProp(context, rawProps, "streamingAnimation", sourceProps.streamingAnimation, {false})),
|
|
296
306
|
streamingConfig(convertRawProp(context, rawProps, "streamingConfig", sourceProps.streamingConfig, {})),
|
|
297
307
|
spoilerOverlay(convertRawProp(context, rawProps, "spoilerOverlay", sourceProps.spoilerOverlay, {std::string{"particles"}})),
|
|
308
|
+
contextMenuHidden(convertRawProp(context, rawProps, "contextMenuHidden", sourceProps.contextMenuHidden, {false})),
|
|
298
309
|
contextMenuItems(convertRawProp(context, rawProps, "contextMenuItems", sourceProps.contextMenuItems, {})),
|
|
299
310
|
selectionMenuConfig(convertRawProp(context, rawProps, "selectionMenuConfig", sourceProps.selectionMenuConfig, {})),
|
|
300
311
|
accessibilityLabels(convertRawProp(context, rawProps, "accessibilityLabels", sourceProps.accessibilityLabels, {})),
|
|
@@ -370,6 +381,10 @@ folly::dynamic EnrichedMarkdownTextProps::getDiffProps(
|
|
|
370
381
|
result["spoilerOverlay"] = spoilerOverlay;
|
|
371
382
|
}
|
|
372
383
|
|
|
384
|
+
if (contextMenuHidden != oldProps->contextMenuHidden) {
|
|
385
|
+
result["contextMenuHidden"] = contextMenuHidden;
|
|
386
|
+
}
|
|
387
|
+
|
|
373
388
|
if (contextMenuItems != oldProps->contextMenuItems) {
|
|
374
389
|
result["contextMenuItems"] = toDynamic(contextMenuItems);
|
|
375
390
|
}
|
|
@@ -2579,6 +2579,7 @@ class EnrichedMarkdownProps final : public ViewProps {
|
|
|
2579
2579
|
bool streamingAnimation{false};
|
|
2580
2580
|
EnrichedMarkdownStreamingConfigStruct streamingConfig{};
|
|
2581
2581
|
std::string spoilerOverlay{std::string{"particles"}};
|
|
2582
|
+
bool contextMenuHidden{false};
|
|
2582
2583
|
std::vector<EnrichedMarkdownContextMenuItemsStruct> contextMenuItems{};
|
|
2583
2584
|
EnrichedMarkdownSelectionMenuConfigStruct selectionMenuConfig{};
|
|
2584
2585
|
EnrichedMarkdownAccessibilityLabelsStruct accessibilityLabels{};
|
|
@@ -3361,6 +3362,7 @@ class EnrichedMarkdownTextInputProps final : public ViewProps {
|
|
|
3361
3362
|
bool scrollEnabled{false};
|
|
3362
3363
|
bool blockSyntax{false};
|
|
3363
3364
|
std::string autoCapitalize{};
|
|
3365
|
+
bool interceptPaste{false};
|
|
3364
3366
|
bool multiline{false};
|
|
3365
3367
|
SharedColor cursorColor{};
|
|
3366
3368
|
SharedColor selectionColor{};
|
|
@@ -5948,6 +5950,7 @@ class EnrichedMarkdownTextProps final : public ViewProps {
|
|
|
5948
5950
|
bool streamingAnimation{false};
|
|
5949
5951
|
EnrichedMarkdownTextStreamingConfigStruct streamingConfig{};
|
|
5950
5952
|
std::string spoilerOverlay{std::string{"particles"}};
|
|
5953
|
+
bool contextMenuHidden{false};
|
|
5951
5954
|
std::vector<EnrichedMarkdownTextContextMenuItemsStruct> contextMenuItems{};
|
|
5952
5955
|
EnrichedMarkdownTextSelectionMenuConfigStruct selectionMenuConfig{};
|
|
5953
5956
|
EnrichedMarkdownTextAccessibilityLabelsStruct accessibilityLabels{};
|
|
@@ -80,6 +80,7 @@ class EnrichedMarkdown
|
|
|
80
80
|
private var maxFontSizeMultiplier: Float = 0f
|
|
81
81
|
private var allowTrailingMargin: Boolean = false
|
|
82
82
|
private var selectable: Boolean = true
|
|
83
|
+
private var contextMenuHidden: Boolean = false
|
|
83
84
|
private var selectionColor: Int? = null
|
|
84
85
|
private var selectionHandleColor: Int? = null
|
|
85
86
|
private var selectionMenuConfig = SelectionMenuConfig()
|
|
@@ -177,6 +178,41 @@ class EnrichedMarkdown
|
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
|
|
181
|
+
fun setContextMenuHidden(value: Boolean) {
|
|
182
|
+
if (contextMenuHidden == value) return
|
|
183
|
+
contextMenuHidden = value
|
|
184
|
+
applyContextMenuHiddenToSegments()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Fans the flag out to every segment that owns a copy menu: text segments
|
|
189
|
+
* (floating ActionMode toolbar), tables and math blocks (both use their own
|
|
190
|
+
* long-press popup, so neither is covered by the ActionMode callback).
|
|
191
|
+
*/
|
|
192
|
+
private fun applyContextMenuHiddenToSegments() {
|
|
193
|
+
segmentViews.forEach { view ->
|
|
194
|
+
when {
|
|
195
|
+
view is EnrichedMarkdownInternalText -> view.contextMenuHidden = contextMenuHidden
|
|
196
|
+
view is TableContainerView -> view.contextMenuHidden = contextMenuHidden
|
|
197
|
+
isMathContainerView(view) -> setMathContextMenuHidden(view)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* MathContainerView lives in the optional `math` source set, so it is only
|
|
204
|
+
* reachable reflectively. The Kotlin `var` compiles to a setter taking a
|
|
205
|
+
* primitive boolean — `Boolean::class.java` would resolve to
|
|
206
|
+
* `java.lang.Boolean` and silently miss.
|
|
207
|
+
*/
|
|
208
|
+
private fun setMathContextMenuHidden(view: View) {
|
|
209
|
+
runCatching {
|
|
210
|
+
view.javaClass
|
|
211
|
+
.getMethod("setContextMenuHidden", Boolean::class.javaPrimitiveType)
|
|
212
|
+
.invoke(view, contextMenuHidden)
|
|
213
|
+
}.onFailure { Log.e(TAG, "Failed to set contextMenuHidden on math view", it) }
|
|
214
|
+
}
|
|
215
|
+
|
|
180
216
|
fun setSelectionColor(color: Int?) {
|
|
181
217
|
if (selectionColor == color) return
|
|
182
218
|
selectionColor = color
|
|
@@ -480,6 +516,7 @@ class EnrichedMarkdown
|
|
|
480
516
|
EnrichedMarkdownInternalText(context).apply {
|
|
481
517
|
spoilerOverlay = this@EnrichedMarkdown.spoilerOverlay
|
|
482
518
|
selectionMenuConfig = this@EnrichedMarkdown.selectionMenuConfig
|
|
519
|
+
contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
|
|
483
520
|
accessibilityLabels = this@EnrichedMarkdown.accessibilityLabels
|
|
484
521
|
setIsSelectable(selectable)
|
|
485
522
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
@@ -514,6 +551,7 @@ class EnrichedMarkdown
|
|
|
514
551
|
onLinkLongPress = onLinkLongPressCallback
|
|
515
552
|
copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
|
|
516
553
|
copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
|
|
554
|
+
contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
|
|
517
555
|
applyTableNode(segment.node)
|
|
518
556
|
}
|
|
519
557
|
|
|
@@ -539,6 +577,7 @@ class EnrichedMarkdown
|
|
|
539
577
|
resolvedClass
|
|
540
578
|
.getMethod("setCopyAsMarkdownLabel", String::class.java)
|
|
541
579
|
.invoke(view, selectionMenuConfig.copyAsMarkdownLabel)
|
|
580
|
+
setMathContextMenuHidden(view)
|
|
542
581
|
resolvedClass.getMethod("applyLatex", String::class.java).invoke(view, segment.latex)
|
|
543
582
|
view
|
|
544
583
|
} catch (e: Exception) {
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt
CHANGED
|
@@ -5,6 +5,7 @@ import android.graphics.Canvas
|
|
|
5
5
|
import android.os.Build
|
|
6
6
|
import android.text.Layout
|
|
7
7
|
import android.util.AttributeSet
|
|
8
|
+
import android.view.ActionMode
|
|
8
9
|
import android.view.MotionEvent
|
|
9
10
|
import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
|
|
10
11
|
import com.swmansion.enriched.markdown.accessibility.AccessibleMarkdownTextView
|
|
@@ -49,6 +50,14 @@ class EnrichedMarkdownInternalText
|
|
|
49
50
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
50
51
|
private var onContextMenuItemPress: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
51
52
|
var selectionMenuConfig: SelectionMenuConfig = SelectionMenuConfig()
|
|
53
|
+
private var activeActionMode: ActionMode? = null
|
|
54
|
+
var contextMenuHidden: Boolean = false
|
|
55
|
+
set(value) {
|
|
56
|
+
if (field == value) return
|
|
57
|
+
field = value
|
|
58
|
+
// See EnrichedMarkdownText.setContextMenuHidden.
|
|
59
|
+
activeActionMode?.invalidate()
|
|
60
|
+
}
|
|
52
61
|
var accessibilityLabels: AccessibilityLabels = AccessibilityLabels()
|
|
53
62
|
set(value) {
|
|
54
63
|
field = value
|
|
@@ -62,6 +71,8 @@ class EnrichedMarkdownInternalText
|
|
|
62
71
|
this,
|
|
63
72
|
getCustomItemTexts = { contextMenuItemTexts },
|
|
64
73
|
getSelectionMenuConfig = { selectionMenuConfig },
|
|
74
|
+
isContextMenuHidden = { contextMenuHidden },
|
|
75
|
+
onActionModeChanged = { activeActionMode = it },
|
|
65
76
|
onCustomItemPress = { itemText, selectedText, start, end ->
|
|
66
77
|
onContextMenuItemPress?.invoke(itemText, selectedText, start, end)
|
|
67
78
|
},
|
|
@@ -144,6 +144,14 @@ class EnrichedMarkdownManager :
|
|
|
144
144
|
view?.setAllowTrailingMargin(allowTrailingMargin)
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
@ReactProp(name = "contextMenuHidden", defaultBoolean = false)
|
|
148
|
+
override fun setContextMenuHidden(
|
|
149
|
+
view: EnrichedMarkdown?,
|
|
150
|
+
contextMenuHidden: Boolean,
|
|
151
|
+
) {
|
|
152
|
+
view?.setContextMenuHidden(contextMenuHidden)
|
|
153
|
+
}
|
|
154
|
+
|
|
147
155
|
@ReactProp(name = "enableLinkPreview", defaultBoolean = true)
|
|
148
156
|
override fun setEnableLinkPreview(
|
|
149
157
|
view: EnrichedMarkdown?,
|
|
@@ -9,6 +9,7 @@ import android.os.Looper
|
|
|
9
9
|
import android.text.Layout
|
|
10
10
|
import android.util.AttributeSet
|
|
11
11
|
import android.util.Log
|
|
12
|
+
import android.view.ActionMode
|
|
12
13
|
import android.view.MotionEvent
|
|
13
14
|
import com.facebook.react.bridge.ReadableMap
|
|
14
15
|
import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
|
|
@@ -91,6 +92,8 @@ class EnrichedMarkdownText
|
|
|
91
92
|
private var selectionColor: Int? = null
|
|
92
93
|
private var selectionHandleColor: Int? = null
|
|
93
94
|
private var selectionMenuConfig = SelectionMenuConfig()
|
|
95
|
+
private var contextMenuHidden = false
|
|
96
|
+
private var activeActionMode: ActionMode? = null
|
|
94
97
|
|
|
95
98
|
init {
|
|
96
99
|
setupAsMarkdownTextView()
|
|
@@ -99,12 +102,23 @@ class EnrichedMarkdownText
|
|
|
99
102
|
this,
|
|
100
103
|
getCustomItemTexts = { contextMenuItemTexts },
|
|
101
104
|
getSelectionMenuConfig = { selectionMenuConfig },
|
|
105
|
+
isContextMenuHidden = { contextMenuHidden },
|
|
106
|
+
onActionModeChanged = { activeActionMode = it },
|
|
102
107
|
onCustomItemPress = { itemText, selectedText, start, end ->
|
|
103
108
|
onContextMenuItemPressCallback?.invoke(itemText, selectedText, start, end)
|
|
104
109
|
},
|
|
105
110
|
)
|
|
106
111
|
}
|
|
107
112
|
|
|
113
|
+
fun setContextMenuHidden(hidden: Boolean) {
|
|
114
|
+
if (contextMenuHidden == hidden) return
|
|
115
|
+
contextMenuHidden = hidden
|
|
116
|
+
// Rebuild a toolbar that is already on screen; without this the flip
|
|
117
|
+
// would only take effect on the next selection, leaving a working Copy
|
|
118
|
+
// menu up after the app asked for it to be hidden.
|
|
119
|
+
activeActionMode?.invalidate()
|
|
120
|
+
}
|
|
121
|
+
|
|
108
122
|
fun setMarkdownContent(markdown: String) {
|
|
109
123
|
if (currentMarkdown == markdown) return
|
|
110
124
|
currentMarkdown = markdown
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt
CHANGED
|
@@ -148,6 +148,14 @@ class EnrichedMarkdownTextManager :
|
|
|
148
148
|
view?.setAllowTrailingMargin(allowTrailingMargin)
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
@ReactProp(name = "contextMenuHidden", defaultBoolean = false)
|
|
152
|
+
override fun setContextMenuHidden(
|
|
153
|
+
view: EnrichedMarkdownText?,
|
|
154
|
+
contextMenuHidden: Boolean,
|
|
155
|
+
) {
|
|
156
|
+
view?.setContextMenuHidden(contextMenuHidden)
|
|
157
|
+
}
|
|
158
|
+
|
|
151
159
|
@ReactProp(name = "enableLinkPreview", defaultBoolean = true)
|
|
152
160
|
override fun setEnableLinkPreview(
|
|
153
161
|
view: EnrichedMarkdownText?,
|
|
@@ -27,6 +27,7 @@ import com.swmansion.enriched.markdown.input.events.OnInputBlurEvent
|
|
|
27
27
|
import com.swmansion.enriched.markdown.input.events.OnInputFocusEvent
|
|
28
28
|
import com.swmansion.enriched.markdown.input.events.OnLinkDetectedEvent
|
|
29
29
|
import com.swmansion.enriched.markdown.input.events.OnMathPressEvent
|
|
30
|
+
import com.swmansion.enriched.markdown.input.events.OnPasteEvent
|
|
30
31
|
import com.swmansion.enriched.markdown.input.events.OnRequestCaretRectResultEvent
|
|
31
32
|
import com.swmansion.enriched.markdown.input.events.OnRequestMarkdownResultEvent
|
|
32
33
|
import com.swmansion.enriched.markdown.input.events.OnStartMentionEvent
|
|
@@ -112,6 +113,7 @@ class EnrichedMarkdownTextInputManager :
|
|
|
112
113
|
OnContextMenuItemPressEvent.EVENT_NAME,
|
|
113
114
|
OnLinkDetectedEvent.EVENT_NAME,
|
|
114
115
|
OnMathPressEvent.EVENT_NAME,
|
|
116
|
+
OnPasteEvent.EVENT_NAME,
|
|
115
117
|
OnStartMentionEvent.EVENT_NAME,
|
|
116
118
|
OnChangeMentionEvent.EVENT_NAME,
|
|
117
119
|
OnEndMentionEvent.EVENT_NAME,
|
|
@@ -186,6 +188,14 @@ class EnrichedMarkdownTextInputManager :
|
|
|
186
188
|
view?.setAutoCapitalize(value)
|
|
187
189
|
}
|
|
188
190
|
|
|
191
|
+
@ReactProp(name = "interceptPaste", defaultBoolean = false)
|
|
192
|
+
override fun setInterceptPaste(
|
|
193
|
+
view: EnrichedMarkdownTextInputView?,
|
|
194
|
+
value: Boolean,
|
|
195
|
+
) {
|
|
196
|
+
view?.interceptPaste = value
|
|
197
|
+
}
|
|
198
|
+
|
|
189
199
|
@ReactProp(name = "multiline", defaultBoolean = true)
|
|
190
200
|
override fun setMultiline(
|
|
191
201
|
view: EnrichedMarkdownTextInputView?,
|
|
@@ -517,6 +527,14 @@ class EnrichedMarkdownTextInputManager :
|
|
|
517
527
|
view?.copyToClipboard()
|
|
518
528
|
}
|
|
519
529
|
|
|
530
|
+
override fun resolvePaste(
|
|
531
|
+
view: EnrichedMarkdownTextInputView?,
|
|
532
|
+
text: String?,
|
|
533
|
+
markdown: String?,
|
|
534
|
+
) {
|
|
535
|
+
view?.resolvePaste(text ?: "", markdown ?: "")
|
|
536
|
+
}
|
|
537
|
+
|
|
520
538
|
override fun requestMarkdown(
|
|
521
539
|
view: EnrichedMarkdownTextInputView?,
|
|
522
540
|
requestId: Int,
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt
CHANGED
|
@@ -20,6 +20,9 @@ import android.view.inputmethod.EditorInfo
|
|
|
20
20
|
import android.view.inputmethod.InputConnection
|
|
21
21
|
import android.view.inputmethod.InputMethodManager
|
|
22
22
|
import androidx.appcompat.widget.AppCompatEditText
|
|
23
|
+
import androidx.core.view.ContentInfoCompat
|
|
24
|
+
import androidx.core.view.OnReceiveContentListener
|
|
25
|
+
import androidx.core.view.ViewCompat
|
|
23
26
|
import com.facebook.react.common.ReactConstants
|
|
24
27
|
import com.facebook.react.uimanager.BackgroundStyleApplicator
|
|
25
28
|
import com.facebook.react.uimanager.PixelUtil
|
|
@@ -44,6 +47,7 @@ import com.swmansion.enriched.markdown.input.model.MathChipStyle
|
|
|
44
47
|
import com.swmansion.enriched.markdown.input.model.StyleType
|
|
45
48
|
import com.swmansion.enriched.markdown.input.toolbar.InputContextMenu
|
|
46
49
|
import com.swmansion.enriched.markdown.utils.input.AutoCapitalizeUtils
|
|
50
|
+
import com.swmansion.enriched.markdown.utils.input.PasteTextUtils
|
|
47
51
|
import kotlin.math.ceil
|
|
48
52
|
|
|
49
53
|
private fun Char.isLineBreak(): Boolean = this == '\n' || this == '\r' || this == '\u0085' || this == '\u2028' || this == '\u2029'
|
|
@@ -97,6 +101,25 @@ class EnrichedMarkdownTextInputView(
|
|
|
97
101
|
private var detectScrollMovement = false
|
|
98
102
|
var scrollEnabled: Boolean = true
|
|
99
103
|
|
|
104
|
+
// When on, a paste is reported to JS instead of inserted. Native never touches
|
|
105
|
+
// the buffer here (see resolvePaste): if JS dies mid-handler the paste is a
|
|
106
|
+
// visible no-op rather than a selection that silently vanished.
|
|
107
|
+
//
|
|
108
|
+
// The listener is installed only while the prop is on. Registering it
|
|
109
|
+
// unconditionally would route every consumer's paste through AppCompat's
|
|
110
|
+
// receive-content path even with the feature off, which is a behavior change
|
|
111
|
+
// nobody asked for.
|
|
112
|
+
var interceptPaste: Boolean = false
|
|
113
|
+
set(value) {
|
|
114
|
+
if (field == value) return
|
|
115
|
+
field = value
|
|
116
|
+
ViewCompat.setOnReceiveContentListener(
|
|
117
|
+
this,
|
|
118
|
+
if (value) PASTE_MIME_TYPES else null,
|
|
119
|
+
if (value) receiveContentListener else null,
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
100
123
|
// The markdown last handed over by JS, kept so `blockSyntax` can re-parse it. @ReactProp
|
|
101
124
|
// setters fire in undefined order, so `defaultValue` regularly lands before the flag does.
|
|
102
125
|
private var lastMarkdownFromJS: String? = null
|
|
@@ -261,6 +284,10 @@ class EnrichedMarkdownTextInputView(
|
|
|
261
284
|
x: Int,
|
|
262
285
|
y: Int,
|
|
263
286
|
) {
|
|
287
|
+
if (text.isNullOrEmpty()) {
|
|
288
|
+
super.scrollTo(x, 0)
|
|
289
|
+
return
|
|
290
|
+
}
|
|
264
291
|
if (!scrollEnabled) return
|
|
265
292
|
super.scrollTo(x, y)
|
|
266
293
|
}
|
|
@@ -406,6 +433,11 @@ class EnrichedMarkdownTextInputView(
|
|
|
406
433
|
}
|
|
407
434
|
|
|
408
435
|
private fun forceScrollToSelection() {
|
|
436
|
+
if (text.isNullOrEmpty()) {
|
|
437
|
+
super.scrollTo(scrollX, 0)
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
|
|
409
441
|
val textLayout = layout ?: return
|
|
410
442
|
val cursorOffset = selectionStart
|
|
411
443
|
if (cursorOffset <= 0) return
|
|
@@ -488,6 +520,98 @@ class EnrichedMarkdownTextInputView(
|
|
|
488
520
|
clipboard.setPrimaryClip(ClipData.newPlainText(null, content))
|
|
489
521
|
}
|
|
490
522
|
|
|
523
|
+
// Paste arrives by two different routes and only the first one is a menu.
|
|
524
|
+
// `onTextContextMenuItem` covers the context menu and the hardware Ctrl+V /
|
|
525
|
+
// KEYCODE_PASTE shortcuts; drag-and-drop, autofill and IME `commitContent`
|
|
526
|
+
// reach the view through `onReceiveContent` instead. Hooking only one of them
|
|
527
|
+
// leaves the other pasting straight into the buffer with nobody intercepting.
|
|
528
|
+
override fun onTextContextMenuItem(id: Int): Boolean {
|
|
529
|
+
val isPaste = id == android.R.id.paste || id == android.R.id.pasteAsPlainText
|
|
530
|
+
if (isPaste && interceptPaste) {
|
|
531
|
+
val text = readClipboardText()
|
|
532
|
+
if (text.isNotEmpty()) {
|
|
533
|
+
eventEmitter.emitPaste(text, "")
|
|
534
|
+
// Stops super, which is what keeps this single-emit: AppCompat routes
|
|
535
|
+
// its own paste handling through the receive-content listener below.
|
|
536
|
+
return true
|
|
537
|
+
}
|
|
538
|
+
// Nothing textual in the clip: hand it to the platform, matching what the
|
|
539
|
+
// listener does with the items it does not claim. Both routes then decide
|
|
540
|
+
// from the same rule, so a clip never behaves differently depending on
|
|
541
|
+
// whether it came from the menu or from Ctrl+V.
|
|
542
|
+
}
|
|
543
|
+
return super.onTextContextMenuItem(id)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
private val receiveContentListener =
|
|
547
|
+
OnReceiveContentListener { _, payload ->
|
|
548
|
+
// The listener also sees autofill, drag-and-drop, IME commitContent and
|
|
549
|
+
// PROCESS_TEXT. Without this filter an autofill would turn into a paste
|
|
550
|
+
// event — and a consumer would turn that into an attachment chip.
|
|
551
|
+
if (!interceptPaste || payload.source != ContentInfoCompat.SOURCE_CLIPBOARD) {
|
|
552
|
+
return@OnReceiveContentListener payload
|
|
553
|
+
}
|
|
554
|
+
val parts = payload.partition { it.text != null }
|
|
555
|
+
parts.first?.let { accepted ->
|
|
556
|
+
val clip = accepted.clip
|
|
557
|
+
val text = PasteTextUtils.joinClipTexts((0 until clip.itemCount).map { clip.getItemAt(it).text })
|
|
558
|
+
if (text.isNotEmpty()) {
|
|
559
|
+
eventEmitter.emitPaste(text, "")
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
// Hand back only what we did not claim, so the buffer stays untouched.
|
|
563
|
+
parts.second
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private fun readClipboardText(): String {
|
|
567
|
+
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return ""
|
|
568
|
+
val clip = clipboard.primaryClip ?: return ""
|
|
569
|
+
// `text`, not `coerceToText`: the listener can only claim items that already
|
|
570
|
+
// carry text, and the two routes have to agree on what counts as a paste.
|
|
571
|
+
return PasteTextUtils.joinClipTexts((0 until clip.itemCount).map { clip.getItemAt(it).text })
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Completes a paste reported through `onPaste`. `markdown` is accepted for
|
|
576
|
+
* parity with iOS but ignored here: Android's clipboard is plain text only
|
|
577
|
+
* (see `copyToClipboard`), so there is no flavor to restore.
|
|
578
|
+
*
|
|
579
|
+
* Resolving with an empty string and an empty selection is a zero-length edit
|
|
580
|
+
* — it still emits, which keeps the JS contract identical on both platforms.
|
|
581
|
+
*/
|
|
582
|
+
fun resolvePaste(
|
|
583
|
+
pastedText: String,
|
|
584
|
+
@Suppress("UNUSED_PARAMETER") markdown: String,
|
|
585
|
+
) {
|
|
586
|
+
val editable = text ?: return
|
|
587
|
+
val selStart = selectionStart.coerceAtLeast(0)
|
|
588
|
+
val selEnd = selectionEnd.coerceAtLeast(selStart)
|
|
589
|
+
val safeStart = selStart.coerceAtMost(editable.length)
|
|
590
|
+
val safeEnd = selEnd.coerceAtMost(editable.length)
|
|
591
|
+
|
|
592
|
+
isProcessingTextChange = true
|
|
593
|
+
try {
|
|
594
|
+
editable.replace(safeStart, safeEnd, pastedText)
|
|
595
|
+
formattingStore.adjustForEdit(safeStart, safeEnd - safeStart, pastedText.length)
|
|
596
|
+
lastProcessedText = editable.toString()
|
|
597
|
+
setSelection(safeStart + pastedText.length)
|
|
598
|
+
} finally {
|
|
599
|
+
isProcessingTextChange = false
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
applyFormatting()
|
|
603
|
+
// Parity with the un-intercepted paste, which reached the detectors through
|
|
604
|
+
// the text watcher: a pasted URL still autolinks.
|
|
605
|
+
detectorPipeline.processTextChange(editable, editable.toString(), safeStart, pastedText.length)
|
|
606
|
+
invalidate()
|
|
607
|
+
requestLayout()
|
|
608
|
+
eventEmitter.emitChangeText()
|
|
609
|
+
if (emitMarkdown) eventEmitter.emitChangeMarkdown()
|
|
610
|
+
eventEmitter.emitSelection(selectionStart, selectionEnd)
|
|
611
|
+
eventEmitter.emitState()
|
|
612
|
+
eventEmitter.emitCaretRectChangeIfNeeded()
|
|
613
|
+
}
|
|
614
|
+
|
|
491
615
|
fun setLinkForSelection(url: String) {
|
|
492
616
|
val selStart = selectionStart
|
|
493
617
|
val selEnd = selectionEnd
|
|
@@ -1155,6 +1279,10 @@ class EnrichedMarkdownTextInputView(
|
|
|
1155
1279
|
.replace("(", "%28")
|
|
1156
1280
|
.replace(")", "%29")
|
|
1157
1281
|
|
|
1282
|
+
private companion object {
|
|
1283
|
+
private val PASTE_MIME_TYPES = arrayOf("text/*")
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1158
1286
|
private data class MentionCandidate(
|
|
1159
1287
|
val indicator: String,
|
|
1160
1288
|
val start: Int,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.input.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
class OnPasteEvent(
|
|
8
|
+
surfaceId: Int,
|
|
9
|
+
viewId: Int,
|
|
10
|
+
private val text: String,
|
|
11
|
+
private val markdown: String,
|
|
12
|
+
) : Event<OnPasteEvent>(surfaceId, viewId) {
|
|
13
|
+
override fun getEventName(): String = EVENT_NAME
|
|
14
|
+
|
|
15
|
+
override fun getEventData(): WritableMap =
|
|
16
|
+
Arguments.createMap().apply {
|
|
17
|
+
putString("text", text)
|
|
18
|
+
putString("markdown", markdown)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
companion object {
|
|
22
|
+
const val EVENT_NAME = "onPaste"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt
CHANGED
|
@@ -16,6 +16,7 @@ import com.swmansion.enriched.markdown.input.events.OnInputBlurEvent
|
|
|
16
16
|
import com.swmansion.enriched.markdown.input.events.OnInputFocusEvent
|
|
17
17
|
import com.swmansion.enriched.markdown.input.events.OnLinkDetectedEvent
|
|
18
18
|
import com.swmansion.enriched.markdown.input.events.OnMathPressEvent
|
|
19
|
+
import com.swmansion.enriched.markdown.input.events.OnPasteEvent
|
|
19
20
|
import com.swmansion.enriched.markdown.input.events.OnRequestCaretRectResultEvent
|
|
20
21
|
import com.swmansion.enriched.markdown.input.events.OnRequestMarkdownResultEvent
|
|
21
22
|
import com.swmansion.enriched.markdown.input.events.OnStartMentionEvent
|
|
@@ -99,6 +100,13 @@ class InputEventEmitter(
|
|
|
99
100
|
dispatch(OnMathPressEvent(surfaceId(), view.id, latex, start, end, displayMode, x, y, width, height))
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
fun emitPaste(
|
|
104
|
+
text: String,
|
|
105
|
+
markdown: String,
|
|
106
|
+
) {
|
|
107
|
+
dispatch(OnPasteEvent(surfaceId(), view.id, text, markdown))
|
|
108
|
+
}
|
|
109
|
+
|
|
102
110
|
fun emitStartMention(indicator: String) {
|
|
103
111
|
dispatch(OnStartMentionEvent(surfaceId(), view.id, indicator))
|
|
104
112
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.utils.input
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The pure half of the paste interception path. A `ClipData` cannot be built on
|
|
5
|
+
* the JVM, so the joining rule lives here where a unit test can reach it; the
|
|
6
|
+
* two call sites (context menu and `onReceiveContent`) only extract the items.
|
|
7
|
+
*/
|
|
8
|
+
object PasteTextUtils {
|
|
9
|
+
/**
|
|
10
|
+
* Flattens a clip's items into one string. A clip regularly carries more than
|
|
11
|
+
* one item — reading only `getItemAt(0)` silently drops items 2..n. Items
|
|
12
|
+
* without text (a URI or intent) are skipped, and the rest join on newlines,
|
|
13
|
+
* which is what the platform's own paste does.
|
|
14
|
+
*/
|
|
15
|
+
fun joinClipTexts(texts: List<CharSequence?>): String =
|
|
16
|
+
texts
|
|
17
|
+
.mapNotNull { it?.toString() }
|
|
18
|
+
.filter { it.isNotEmpty() }
|
|
19
|
+
.joinToString("\n")
|
|
20
|
+
}
|