@expensify/react-native-live-markdown 0.1.45 → 0.1.46
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/RNLiveMarkdown.podspec +7 -1
- package/android/build.gradle +12 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/java/com/expensify/livemarkdown/CustomFabricUIManager.java +47 -0
- package/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java +248 -0
- package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java +27 -0
- package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownPackage.java +30 -3
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownFontFamilySpan.java +7 -1
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownFontSizeSpan.java +3 -1
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +2 -6
- package/android/src/main/new_arch/CMakeLists.txt +82 -0
- package/android/src/main/new_arch/MarkdownCommitHook.cpp +173 -0
- package/android/src/main/new_arch/MarkdownCommitHook.h +52 -0
- package/android/src/main/new_arch/NativeProxy.cpp +38 -0
- package/android/src/main/new_arch/NativeProxy.h +37 -0
- package/android/src/main/new_arch/OnLoad.cpp +8 -0
- package/android/src/main/new_arch/RNLiveMarkdownSpec.cpp +44 -0
- package/android/src/main/new_arch/RNLiveMarkdownSpec.h +37 -0
- package/android/src/newarch/NativeProxy.java +24 -0
- package/android/src/oldarch/NativeLiveMarkdownModuleSpec.java +37 -0
- package/android/src/oldarch/NativeProxy.java +9 -0
- package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownShadowFamilyRegistry.cpp +1 -1
- package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownShadowFamilyRegistry.h +2 -2
- package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownTextInputDecoratorShadowNode.cpp +1 -1
- package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownTextInputDecoratorShadowNode.h +1 -2
- package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownTextInputDecoratorState.h +13 -0
- package/{ios/MarkdownTextInputDecoratorComponentDescriptor.h → cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorViewComponentDescriptor.h} +1 -1
- package/ios/MarkdownTextInputDecoratorComponentView.mm +2 -2
- package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js +3 -1
- package/lib/commonjs/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
- package/lib/module/MarkdownTextInputDecoratorViewNativeComponent.js +3 -1
- package/lib/module/MarkdownTextInputDecoratorViewNativeComponent.js.map +1 -1
- package/lib/typescript/src/MarkdownTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/package.json +2 -1
- package/react-native.config.js +12 -0
- package/src/MarkdownTextInputDecoratorViewNativeComponent.ts +3 -1
package/RNLiveMarkdown.podspec
CHANGED
|
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
|
|
|
14
14
|
s.platforms = { :ios => "11.0" }
|
|
15
15
|
s.source = { :git => "https://github.com/expensify/react-native-live-markdown.git", :tag => "#{s.version}" }
|
|
16
16
|
|
|
17
|
-
s.source_files = "ios/**/*.{h,m,mm
|
|
17
|
+
s.source_files = "ios/**/*.{h,m,mm}"
|
|
18
18
|
|
|
19
19
|
s.resources = "parser/react-native-live-markdown-parser.js"
|
|
20
20
|
|
|
@@ -26,4 +26,10 @@ Pod::Spec.new do |s|
|
|
|
26
26
|
"react/renderer/components/textinput/iostextinput",
|
|
27
27
|
])
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
s.subspec "common" do |ss|
|
|
31
|
+
ss.source_files = "cpp/**/*.{cpp,h}"
|
|
32
|
+
ss.header_dir = "RNLiveMarkdown"
|
|
33
|
+
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/cpp\"" }
|
|
34
|
+
end
|
|
29
35
|
end
|
package/android/build.gradle
CHANGED
|
@@ -53,6 +53,9 @@ android {
|
|
|
53
53
|
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
54
54
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
55
55
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
56
|
+
|
|
57
|
+
consumerProguardFiles "proguard-rules.pro"
|
|
58
|
+
|
|
56
59
|
externalNativeBuild {
|
|
57
60
|
cmake {
|
|
58
61
|
arguments "-DANDROID_STL=c++_shared", "-DANDROID_TOOLCHAIN=clang"
|
|
@@ -112,6 +115,15 @@ android {
|
|
|
112
115
|
"**/libreactnativejni.so",
|
|
113
116
|
]
|
|
114
117
|
}
|
|
118
|
+
|
|
119
|
+
packagingOptions {
|
|
120
|
+
// For some reason gradle only complains about the duplicated version of librrc_root and libreact_render libraries
|
|
121
|
+
// while there are more libraries copied in intermediates folder of the lib build directory, we exclude
|
|
122
|
+
// only the ones that make the build fail (ideally we should only include libreanimated but we
|
|
123
|
+
// are only allowed to specify exlude patterns)
|
|
124
|
+
exclude "**/libreact_render*.so"
|
|
125
|
+
exclude "**/librrc_root.so"
|
|
126
|
+
}
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
repositories {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-keep class com.expensify.livemarkdown.** { *; }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
package com.expensify.livemarkdown;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
7
|
+
import com.facebook.react.fabric.FabricUIManager;
|
|
8
|
+
import com.facebook.react.fabric.mounting.MountingManager;
|
|
9
|
+
import com.facebook.react.uimanager.ViewManagerRegistry;
|
|
10
|
+
import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
|
|
11
|
+
|
|
12
|
+
import java.lang.reflect.Field;
|
|
13
|
+
|
|
14
|
+
public class CustomFabricUIManager {
|
|
15
|
+
|
|
16
|
+
public static FabricUIManager create(FabricUIManager source, ReadableMap markdownProps) {
|
|
17
|
+
Class<? extends FabricUIManager> uiManagerClass = source.getClass();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
Field mountingManagerField = uiManagerClass.getDeclaredField("mMountingManager");
|
|
21
|
+
mountingManagerField.setAccessible(true);
|
|
22
|
+
|
|
23
|
+
ReactApplicationContext reactContext = readPrivateField(source, "mReactApplicationContext");
|
|
24
|
+
ViewManagerRegistry viewManagerRegistry = readPrivateField(source, "mViewManagerRegistry");
|
|
25
|
+
BatchEventDispatchedListener batchEventDispatchedListener = readPrivateField(source, "mBatchEventDispatchedListener");
|
|
26
|
+
MountingManager.MountItemExecutor mountItemExecutor = readPrivateField(source, "mMountItemExecutor");
|
|
27
|
+
|
|
28
|
+
FabricUIManager customFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, batchEventDispatchedListener);
|
|
29
|
+
|
|
30
|
+
mountingManagerField.set(customFabricUIManager, new CustomMountingManager(viewManagerRegistry, mountItemExecutor, reactContext, markdownProps));
|
|
31
|
+
|
|
32
|
+
return customFabricUIManager;
|
|
33
|
+
} catch (NoSuchFieldException | IllegalAccessException e) {
|
|
34
|
+
throw new RuntimeException("[LiveMarkdown] Cannot read data from FabricUIManager");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private static <T> T readPrivateField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
|
39
|
+
Class<?> clazz = obj.getClass();
|
|
40
|
+
|
|
41
|
+
Field field = clazz.getDeclaredField(name);
|
|
42
|
+
field.setAccessible(true);
|
|
43
|
+
T value = (T) field.get(obj);
|
|
44
|
+
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
package com.expensify.livemarkdown;
|
|
2
|
+
|
|
3
|
+
import static com.facebook.react.views.text.TextAttributeProps.UNSET;
|
|
4
|
+
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.res.AssetManager;
|
|
7
|
+
import android.text.BoringLayout;
|
|
8
|
+
import android.text.Layout;
|
|
9
|
+
import android.text.Spannable;
|
|
10
|
+
import android.text.SpannableStringBuilder;
|
|
11
|
+
import android.text.TextPaint;
|
|
12
|
+
|
|
13
|
+
import androidx.annotation.NonNull;
|
|
14
|
+
import androidx.annotation.Nullable;
|
|
15
|
+
|
|
16
|
+
import com.facebook.common.logging.FLog;
|
|
17
|
+
import com.facebook.react.bridge.ReactContext;
|
|
18
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
19
|
+
import com.facebook.react.common.mapbuffer.MapBuffer;
|
|
20
|
+
import com.facebook.react.fabric.mounting.MountingManager;
|
|
21
|
+
import com.facebook.react.uimanager.PixelUtil;
|
|
22
|
+
import com.facebook.react.uimanager.ViewManagerRegistry;
|
|
23
|
+
import com.facebook.react.views.text.TextAttributeProps;
|
|
24
|
+
import com.facebook.react.views.text.TextInlineViewPlaceholderSpan;
|
|
25
|
+
import com.facebook.react.views.text.TextLayoutManagerMapBuffer;
|
|
26
|
+
import com.facebook.yoga.YogaMeasureMode;
|
|
27
|
+
import com.facebook.yoga.YogaMeasureOutput;
|
|
28
|
+
|
|
29
|
+
import java.lang.reflect.InvocationTargetException;
|
|
30
|
+
import java.lang.reflect.Method;
|
|
31
|
+
|
|
32
|
+
public class CustomMountingManager extends MountingManager {
|
|
33
|
+
private static final boolean DEFAULT_INCLUDE_FONT_PADDING = true;
|
|
34
|
+
private static final TextPaint sTextPaintInstance = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
|
|
35
|
+
|
|
36
|
+
private MarkdownUtils markdownUtils;
|
|
37
|
+
|
|
38
|
+
public CustomMountingManager(
|
|
39
|
+
@NonNull ViewManagerRegistry viewManagerRegistry,
|
|
40
|
+
@NonNull MountItemExecutor mountItemExecutor,
|
|
41
|
+
@NonNull Context context,
|
|
42
|
+
@NonNull ReadableMap decoratorProps) {
|
|
43
|
+
super(viewManagerRegistry, mountItemExecutor);
|
|
44
|
+
|
|
45
|
+
AssetManager assetManager = context.getAssets();
|
|
46
|
+
MarkdownUtils.maybeInitializeRuntime(assetManager);
|
|
47
|
+
|
|
48
|
+
this.markdownUtils = new MarkdownUtils(assetManager);
|
|
49
|
+
this.markdownUtils.setMarkdownStyle(new MarkdownStyle(decoratorProps, context));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Override
|
|
53
|
+
public long measureMapBuffer(
|
|
54
|
+
@NonNull ReactContext context,
|
|
55
|
+
@NonNull String componentName,
|
|
56
|
+
@NonNull MapBuffer attributedString,
|
|
57
|
+
@NonNull MapBuffer paragraphAttributes,
|
|
58
|
+
@Nullable MapBuffer state,
|
|
59
|
+
float width,
|
|
60
|
+
@NonNull YogaMeasureMode widthYogaMeasureMode,
|
|
61
|
+
float height,
|
|
62
|
+
@NonNull YogaMeasureMode heightYogaMeasureMode,
|
|
63
|
+
@Nullable float[] attachmentsPositions) {
|
|
64
|
+
|
|
65
|
+
Spannable text =
|
|
66
|
+
TextLayoutManagerMapBuffer.getOrCreateSpannableForText(context, attributedString, null);
|
|
67
|
+
|
|
68
|
+
if (text == null) {
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
int textBreakStrategy =
|
|
73
|
+
TextAttributeProps.getTextBreakStrategy(
|
|
74
|
+
paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_TEXT_BREAK_STRATEGY));
|
|
75
|
+
boolean includeFontPadding =
|
|
76
|
+
paragraphAttributes.contains(TextLayoutManagerMapBuffer.PA_KEY_INCLUDE_FONT_PADDING)
|
|
77
|
+
? paragraphAttributes.getBoolean(TextLayoutManagerMapBuffer.PA_KEY_INCLUDE_FONT_PADDING)
|
|
78
|
+
: DEFAULT_INCLUDE_FONT_PADDING;
|
|
79
|
+
int hyphenationFrequency =
|
|
80
|
+
TextAttributeProps.getHyphenationFrequency(
|
|
81
|
+
paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_HYPHENATION_FREQUENCY));
|
|
82
|
+
|
|
83
|
+
// StaticLayout returns wrong metrics for the last line if it's empty, add something to the
|
|
84
|
+
// last line so it's measured correctly
|
|
85
|
+
if (text.toString().endsWith("\n")) {
|
|
86
|
+
SpannableStringBuilder sb = new SpannableStringBuilder(text);
|
|
87
|
+
sb.append("I");
|
|
88
|
+
|
|
89
|
+
text = sb;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
markdownUtils.applyMarkdownFormatting((SpannableStringBuilder)text);
|
|
93
|
+
|
|
94
|
+
BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance);
|
|
95
|
+
|
|
96
|
+
Class<TextLayoutManagerMapBuffer> mapBufferClass = TextLayoutManagerMapBuffer.class;
|
|
97
|
+
try {
|
|
98
|
+
Method createLayoutMethod = mapBufferClass.getDeclaredMethod("createLayout", Spannable.class, BoringLayout.Metrics.class, float.class, YogaMeasureMode.class, boolean.class, int.class, int.class);
|
|
99
|
+
createLayoutMethod.setAccessible(true);
|
|
100
|
+
|
|
101
|
+
Layout layout = (Layout)createLayoutMethod.invoke(
|
|
102
|
+
null,
|
|
103
|
+
text,
|
|
104
|
+
boring,
|
|
105
|
+
width,
|
|
106
|
+
widthYogaMeasureMode,
|
|
107
|
+
includeFontPadding,
|
|
108
|
+
textBreakStrategy,
|
|
109
|
+
hyphenationFrequency);
|
|
110
|
+
|
|
111
|
+
int maximumNumberOfLines =
|
|
112
|
+
paragraphAttributes.contains(TextLayoutManagerMapBuffer.PA_KEY_MAX_NUMBER_OF_LINES)
|
|
113
|
+
? paragraphAttributes.getInt(TextLayoutManagerMapBuffer.PA_KEY_MAX_NUMBER_OF_LINES)
|
|
114
|
+
: UNSET;
|
|
115
|
+
|
|
116
|
+
int calculatedLineCount =
|
|
117
|
+
maximumNumberOfLines == UNSET || maximumNumberOfLines == 0
|
|
118
|
+
? layout.getLineCount()
|
|
119
|
+
: Math.min(maximumNumberOfLines, layout.getLineCount());
|
|
120
|
+
|
|
121
|
+
// Instead of using `layout.getWidth()` (which may yield a significantly larger width for
|
|
122
|
+
// text that is wrapping), compute width using the longest line.
|
|
123
|
+
float calculatedWidth = 0;
|
|
124
|
+
if (widthYogaMeasureMode == YogaMeasureMode.EXACTLY) {
|
|
125
|
+
calculatedWidth = width;
|
|
126
|
+
} else {
|
|
127
|
+
for (int lineIndex = 0; lineIndex < calculatedLineCount; lineIndex++) {
|
|
128
|
+
boolean endsWithNewLine =
|
|
129
|
+
text.length() > 0 && text.charAt(layout.getLineEnd(lineIndex) - 1) == '\n';
|
|
130
|
+
float lineWidth =
|
|
131
|
+
endsWithNewLine ? layout.getLineMax(lineIndex) : layout.getLineWidth(lineIndex);
|
|
132
|
+
if (lineWidth > calculatedWidth) {
|
|
133
|
+
calculatedWidth = lineWidth;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (widthYogaMeasureMode == YogaMeasureMode.AT_MOST && calculatedWidth > width) {
|
|
137
|
+
calculatedWidth = width;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Android 11+ introduces changes in text width calculation which leads to cases
|
|
142
|
+
// where the container is measured smaller than text. Math.ceil prevents it
|
|
143
|
+
// See T136756103 for investigation
|
|
144
|
+
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.Q) {
|
|
145
|
+
calculatedWidth = (float) Math.ceil(calculatedWidth);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
float calculatedHeight = height;
|
|
149
|
+
if (heightYogaMeasureMode != YogaMeasureMode.EXACTLY) {
|
|
150
|
+
calculatedHeight = layout.getLineBottom(calculatedLineCount - 1);
|
|
151
|
+
if (heightYogaMeasureMode == YogaMeasureMode.AT_MOST && calculatedHeight > height) {
|
|
152
|
+
calculatedHeight = height;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Calculate the positions of the attachments (views) that will be rendered inside the
|
|
157
|
+
// Spanned Text. The following logic is only executed when a text contains views inside.
|
|
158
|
+
// This follows a similar logic than used in pre-fabric (see ReactTextView.onLayout method).
|
|
159
|
+
int attachmentIndex = 0;
|
|
160
|
+
int lastAttachmentFoundInSpan;
|
|
161
|
+
for (int i = 0; i < text.length(); i = lastAttachmentFoundInSpan) {
|
|
162
|
+
lastAttachmentFoundInSpan =
|
|
163
|
+
text.nextSpanTransition(i, text.length(), TextInlineViewPlaceholderSpan.class);
|
|
164
|
+
TextInlineViewPlaceholderSpan[] placeholders =
|
|
165
|
+
text.getSpans(i, lastAttachmentFoundInSpan, TextInlineViewPlaceholderSpan.class);
|
|
166
|
+
for (TextInlineViewPlaceholderSpan placeholder : placeholders) {
|
|
167
|
+
int start = text.getSpanStart(placeholder);
|
|
168
|
+
int line = layout.getLineForOffset(start);
|
|
169
|
+
boolean isLineTruncated = layout.getEllipsisCount(line) > 0;
|
|
170
|
+
// This truncation check works well on recent versions of Android (tested on 5.1.1 and
|
|
171
|
+
// 6.0.1) but not on Android 4.4.4. The reason is that getEllipsisCount is buggy on
|
|
172
|
+
// Android 4.4.4. Specifically, it incorrectly returns 0 if an inline view is the
|
|
173
|
+
// first thing to be truncated.
|
|
174
|
+
if (!(isLineTruncated && start >= layout.getLineStart(line) + layout.getEllipsisStart(line))
|
|
175
|
+
|| start >= layout.getLineEnd(line)) {
|
|
176
|
+
float placeholderWidth = placeholder.getWidth();
|
|
177
|
+
float placeholderHeight = placeholder.getHeight();
|
|
178
|
+
// Calculate if the direction of the placeholder character is Right-To-Left.
|
|
179
|
+
boolean isRtlChar = layout.isRtlCharAt(start);
|
|
180
|
+
boolean isRtlParagraph = layout.getParagraphDirection(line) == Layout.DIR_RIGHT_TO_LEFT;
|
|
181
|
+
float placeholderLeftPosition;
|
|
182
|
+
// There's a bug on Samsung devices where calling getPrimaryHorizontal on
|
|
183
|
+
// the last offset in the layout will result in an endless loop. Work around
|
|
184
|
+
// this bug by avoiding getPrimaryHorizontal in that case.
|
|
185
|
+
if (start == text.length() - 1) {
|
|
186
|
+
boolean endsWithNewLine =
|
|
187
|
+
text.length() > 0 && text.charAt(layout.getLineEnd(line) - 1) == '\n';
|
|
188
|
+
float lineWidth = endsWithNewLine ? layout.getLineMax(line) : layout.getLineWidth(line);
|
|
189
|
+
placeholderLeftPosition =
|
|
190
|
+
isRtlParagraph
|
|
191
|
+
// Equivalent to `layout.getLineLeft(line)` but `getLineLeft` returns
|
|
192
|
+
// incorrect
|
|
193
|
+
// values when the paragraph is RTL and `setSingleLine(true)`.
|
|
194
|
+
? calculatedWidth - lineWidth
|
|
195
|
+
: layout.getLineRight(line) - placeholderWidth;
|
|
196
|
+
} else {
|
|
197
|
+
// The direction of the paragraph may not be exactly the direction the string is
|
|
198
|
+
// heading
|
|
199
|
+
// in at the
|
|
200
|
+
// position of the placeholder. So, if the direction of the character is the same
|
|
201
|
+
// as the
|
|
202
|
+
// paragraph
|
|
203
|
+
// use primary, secondary otherwise.
|
|
204
|
+
boolean characterAndParagraphDirectionMatch = isRtlParagraph == isRtlChar;
|
|
205
|
+
placeholderLeftPosition =
|
|
206
|
+
characterAndParagraphDirectionMatch
|
|
207
|
+
? layout.getPrimaryHorizontal(start)
|
|
208
|
+
: layout.getSecondaryHorizontal(start);
|
|
209
|
+
if (isRtlParagraph) {
|
|
210
|
+
// Adjust `placeholderLeftPosition` to work around an Android bug.
|
|
211
|
+
// The bug is when the paragraph is RTL and `setSingleLine(true)`, some layout
|
|
212
|
+
// methods such as `getPrimaryHorizontal`, `getSecondaryHorizontal`, and
|
|
213
|
+
// `getLineRight` return incorrect values. Their return values seem to be off
|
|
214
|
+
// by the same number of pixels so subtracting these values cancels out the
|
|
215
|
+
// error.
|
|
216
|
+
//
|
|
217
|
+
// The result is equivalent to bugless versions of
|
|
218
|
+
// `getPrimaryHorizontal`/`getSecondaryHorizontal`.
|
|
219
|
+
placeholderLeftPosition =
|
|
220
|
+
calculatedWidth - (layout.getLineRight(line) - placeholderLeftPosition);
|
|
221
|
+
}
|
|
222
|
+
if (isRtlChar) {
|
|
223
|
+
placeholderLeftPosition -= placeholderWidth;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Vertically align the inline view to the baseline of the line of text.
|
|
227
|
+
float placeholderTopPosition = layout.getLineBaseline(line) - placeholderHeight;
|
|
228
|
+
int attachmentPosition = attachmentIndex * 2;
|
|
229
|
+
|
|
230
|
+
// The attachment array returns the positions of each of the attachments as
|
|
231
|
+
attachmentsPositions[attachmentPosition] =
|
|
232
|
+
PixelUtil.toDIPFromPixel(placeholderTopPosition);
|
|
233
|
+
attachmentsPositions[attachmentPosition + 1] =
|
|
234
|
+
PixelUtil.toDIPFromPixel(placeholderLeftPosition);
|
|
235
|
+
attachmentIndex++;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
float widthInSP = PixelUtil.toDIPFromPixel(calculatedWidth);
|
|
241
|
+
float heightInSP = PixelUtil.toDIPFromPixel(calculatedHeight);
|
|
242
|
+
|
|
243
|
+
return YogaMeasureOutput.make(widthInSP, heightInSP);
|
|
244
|
+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
|
245
|
+
throw new RuntimeException(e);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.expensify.livemarkdown;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
+
import com.facebook.react.bridge.UIManager;
|
|
5
|
+
import com.facebook.react.fabric.FabricUIManager;
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerHelper;
|
|
7
|
+
import com.facebook.react.uimanager.common.UIManagerType;
|
|
8
|
+
|
|
9
|
+
public class LiveMarkdownModule extends NativeLiveMarkdownModuleSpec {
|
|
10
|
+
private NativeProxy mNativeProxy;
|
|
11
|
+
public LiveMarkdownModule(ReactApplicationContext reactContext) {
|
|
12
|
+
super(reactContext);
|
|
13
|
+
|
|
14
|
+
this.mNativeProxy = new NativeProxy();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Override
|
|
18
|
+
public boolean install() {
|
|
19
|
+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
20
|
+
FabricUIManager uiManager =
|
|
21
|
+
(FabricUIManager) UIManagerHelper.getUIManager(getReactApplicationContext(), UIManagerType.FABRIC);
|
|
22
|
+
mNativeProxy.createCommitHook(uiManager);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
package com.expensify.livemarkdown;
|
|
2
2
|
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
3
6
|
import com.facebook.react.ReactPackage;
|
|
7
|
+
import com.facebook.react.TurboReactPackage;
|
|
4
8
|
import com.facebook.react.bridge.NativeModule;
|
|
5
9
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
10
|
+
import com.facebook.react.module.model.ReactModuleInfo;
|
|
11
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
6
12
|
import com.facebook.react.uimanager.ViewManager;
|
|
7
13
|
|
|
8
14
|
import java.util.ArrayList;
|
|
9
15
|
import java.util.Collections;
|
|
10
16
|
import java.util.List;
|
|
17
|
+
import java.util.Map;
|
|
11
18
|
|
|
12
|
-
public class LiveMarkdownPackage
|
|
19
|
+
public class LiveMarkdownPackage extends TurboReactPackage {
|
|
13
20
|
@Override
|
|
14
21
|
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
15
22
|
List<ViewManager> viewManagers = new ArrayList<>();
|
|
@@ -18,7 +25,27 @@ public class LiveMarkdownPackage implements ReactPackage {
|
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
@Override
|
|
21
|
-
public
|
|
22
|
-
return
|
|
28
|
+
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
29
|
+
return new ReactModuleInfoProvider() {
|
|
30
|
+
@Override
|
|
31
|
+
public Map<String, ReactModuleInfo> getReactModuleInfos() {
|
|
32
|
+
return Map.of(LiveMarkdownModule.NAME, new ReactModuleInfo(
|
|
33
|
+
LiveMarkdownModule.NAME,
|
|
34
|
+
LiveMarkdownModule.class.getName(),
|
|
35
|
+
false, // canOverrideExistingModule
|
|
36
|
+
false, // needsEagerInit
|
|
37
|
+
false, // isCxxModule
|
|
38
|
+
true // isTurboModule
|
|
39
|
+
));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
@Nullable
|
|
44
|
+
@Override
|
|
45
|
+
public NativeModule getModule(@NonNull String s, @NonNull ReactApplicationContext reactApplicationContext) {
|
|
46
|
+
if (s.equals(LiveMarkdownModule.NAME)) {
|
|
47
|
+
return new LiveMarkdownModule(reactApplicationContext);
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
23
50
|
}
|
|
24
51
|
}
|
|
@@ -8,6 +8,7 @@ import android.text.style.MetricAffectingSpan;
|
|
|
8
8
|
|
|
9
9
|
import androidx.annotation.NonNull;
|
|
10
10
|
|
|
11
|
+
import com.facebook.react.common.assets.ReactFontManager.TypefaceStyle;
|
|
11
12
|
import com.facebook.react.views.text.ReactFontManager;
|
|
12
13
|
|
|
13
14
|
public class MarkdownFontFamilySpan extends MetricAffectingSpan implements MarkdownSpan {
|
|
@@ -31,7 +32,12 @@ public class MarkdownFontFamilySpan extends MetricAffectingSpan implements Markd
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
private void apply(@NonNull TextPaint textPaint) {
|
|
34
|
-
int style =
|
|
35
|
+
int style = TypefaceStyle.NORMAL;
|
|
36
|
+
if (textPaint.getTypeface() != null) {
|
|
37
|
+
style = textPaint.getTypeface().getStyle();
|
|
38
|
+
} else {
|
|
39
|
+
style = TypefaceStyle.NORMAL;
|
|
40
|
+
}
|
|
35
41
|
Typeface typeface = ReactFontManager.getInstance().getTypeface(mFontFamily, style, mAssetManager);
|
|
36
42
|
textPaint.setTypeface(typeface);
|
|
37
43
|
textPaint.setFlags(textPaint.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
|
|
@@ -2,8 +2,10 @@ package com.expensify.livemarkdown;
|
|
|
2
2
|
|
|
3
3
|
import android.text.style.AbsoluteSizeSpan;
|
|
4
4
|
|
|
5
|
+
import com.facebook.react.uimanager.PixelUtil;
|
|
6
|
+
|
|
5
7
|
public class MarkdownFontSizeSpan extends AbsoluteSizeSpan implements MarkdownSpan {
|
|
6
8
|
public MarkdownFontSizeSpan(float fontSize) {
|
|
7
|
-
super((int) fontSize,
|
|
9
|
+
super((int) PixelUtil.toPixelFromDIP(fontSize), false);
|
|
8
10
|
}
|
|
9
11
|
}
|
|
@@ -29,9 +29,7 @@ public class MarkdownUtils {
|
|
|
29
29
|
|
|
30
30
|
private static boolean IS_RUNTIME_INITIALIZED = false;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
public static void maybeInitializeRuntime(AssetManager assetManager) {
|
|
34
|
-
UiThreadUtil.assertOnUiThread();
|
|
32
|
+
public static synchronized void maybeInitializeRuntime(AssetManager assetManager) {
|
|
35
33
|
if (IS_RUNTIME_INITIALIZED) {
|
|
36
34
|
return;
|
|
37
35
|
}
|
|
@@ -50,9 +48,7 @@ public class MarkdownUtils {
|
|
|
50
48
|
|
|
51
49
|
private static native void nativeInitializeRuntime(String code);
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
private static String parseMarkdown(String input) {
|
|
55
|
-
UiThreadUtil.assertOnUiThread();
|
|
51
|
+
private synchronized static String parseMarkdown(String input) {
|
|
56
52
|
return nativeParseMarkdown(input);
|
|
57
53
|
}
|
|
58
54
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.13)
|
|
2
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
3
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
4
|
+
|
|
5
|
+
set(LIB_LITERAL RNLiveMarkdownSpec)
|
|
6
|
+
set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL})
|
|
7
|
+
|
|
8
|
+
set(LIB_ANDROID_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
|
|
9
|
+
set(LIB_CPP_DIR ${LIB_ANDROID_DIR}/../cpp)
|
|
10
|
+
set(LIB_CUSTOM_SOURCES_DIR ${LIB_CPP_DIR}/react/renderer/components/${LIB_LITERAL})
|
|
11
|
+
set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/build/generated/source/codegen/jni)
|
|
12
|
+
set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})
|
|
13
|
+
|
|
14
|
+
file(GLOB LIB_MODULE_SRCS CONFIGURE_DEPENDS *.cpp)
|
|
15
|
+
file(GLOB LIB_CUSTOM_SRCS CONFIGURE_DEPENDS ${LIB_CUSTOM_SOURCES_DIR}/*.cpp)
|
|
16
|
+
file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp)
|
|
17
|
+
|
|
18
|
+
set(RN_DIR ${LIB_ANDROID_DIR}/../example/node_modules/react-native)
|
|
19
|
+
|
|
20
|
+
add_library(
|
|
21
|
+
${LIB_TARGET_NAME}
|
|
22
|
+
SHARED
|
|
23
|
+
${LIB_MODULE_SRCS}
|
|
24
|
+
${LIB_CUSTOM_SRCS}
|
|
25
|
+
${LIB_CODEGEN_SRCS}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
target_include_directories(
|
|
29
|
+
${LIB_TARGET_NAME}
|
|
30
|
+
PUBLIC
|
|
31
|
+
.
|
|
32
|
+
${LIB_ANDROID_GENERATED_JNI_DIR}
|
|
33
|
+
${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
|
|
34
|
+
${LIB_CPP_DIR}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
find_package(ReactAndroid REQUIRED CONFIG)
|
|
38
|
+
|
|
39
|
+
target_link_libraries(
|
|
40
|
+
${LIB_TARGET_NAME}
|
|
41
|
+
ReactAndroid::rrc_text
|
|
42
|
+
ReactAndroid::rrc_textinput
|
|
43
|
+
ReactAndroid::react_render_textlayoutmanager
|
|
44
|
+
ReactAndroid::react_render_imagemanager
|
|
45
|
+
fabricjni
|
|
46
|
+
fbjni
|
|
47
|
+
folly_runtime
|
|
48
|
+
glog
|
|
49
|
+
jsi
|
|
50
|
+
react_codegen_rncore
|
|
51
|
+
react_debug
|
|
52
|
+
react_nativemodule_core
|
|
53
|
+
react_render_core
|
|
54
|
+
react_render_debug
|
|
55
|
+
react_render_graphics
|
|
56
|
+
react_render_mapbuffer
|
|
57
|
+
ReactAndroid::react_render_uimanager
|
|
58
|
+
ReactAndroid::react_render_scheduler
|
|
59
|
+
react_utils
|
|
60
|
+
runtimeexecutor
|
|
61
|
+
rrc_view
|
|
62
|
+
turbomodulejsijni
|
|
63
|
+
yoga
|
|
64
|
+
android
|
|
65
|
+
log
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
target_compile_options(
|
|
69
|
+
${LIB_TARGET_NAME}
|
|
70
|
+
PRIVATE
|
|
71
|
+
-DLOG_TAG=\"ReactNative\"
|
|
72
|
+
-fexceptions
|
|
73
|
+
-frtti
|
|
74
|
+
-Wall
|
|
75
|
+
-std=c++20
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
target_include_directories(
|
|
79
|
+
${CMAKE_PROJECT_NAME}
|
|
80
|
+
PUBLIC
|
|
81
|
+
${CMAKE_CURRENT_SOURCE_DIR}
|
|
82
|
+
)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#include <react/fabric/Binding.h>
|
|
2
|
+
#include <react/jni/ReadableNativeMap.h>
|
|
3
|
+
#include <react/renderer/core/ComponentDescriptor.h>
|
|
4
|
+
#include <react/renderer/scheduler/Scheduler.h>
|
|
5
|
+
|
|
6
|
+
#include "MarkdownCommitHook.h"
|
|
7
|
+
#include "react/renderer/components/RNLiveMarkdownSpec/MarkdownShadowFamilyRegistry.h"
|
|
8
|
+
|
|
9
|
+
using namespace facebook;
|
|
10
|
+
using namespace react;
|
|
11
|
+
|
|
12
|
+
namespace livemarkdown {
|
|
13
|
+
|
|
14
|
+
MarkdownCommitHook::MarkdownCommitHook(
|
|
15
|
+
jni::global_ref<facebook::react::JFabricUIManager::javaobject>
|
|
16
|
+
fabricUIManager)
|
|
17
|
+
: fabricUIManager_(fabricUIManager),
|
|
18
|
+
uiManager_(
|
|
19
|
+
fabricUIManager->getBinding()->getScheduler()->getUIManager()) {
|
|
20
|
+
uiManager_->registerCommitHook(*this);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
MarkdownCommitHook::~MarkdownCommitHook() noexcept {
|
|
24
|
+
uiManager_->unregisterCommitHook(*this);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
28
|
+
ShadowTree const &, RootShadowNode::Shared const &,
|
|
29
|
+
RootShadowNode::Unshared const &newRootShadowNode) noexcept {
|
|
30
|
+
auto rootNode = newRootShadowNode->ShadowNode::clone(ShadowNodeFragment{});
|
|
31
|
+
|
|
32
|
+
// A preface to why we do the weird thing below:
|
|
33
|
+
// On the new architecture there are two ways of measuring text on Android: by
|
|
34
|
+
// passing a cache key, or by passing a measured text with attributes by a map
|
|
35
|
+
// buffer. We could implement both, but that would increase the complexity of
|
|
36
|
+
// the code and duplication between here and RN core, so we implement
|
|
37
|
+
// measurement for map buffers since it's the independent one, and force RN to
|
|
38
|
+
// use this path every time. It shouldn't have a negative performance impact,
|
|
39
|
+
// since there is a cpp cache layer anyway.
|
|
40
|
+
//
|
|
41
|
+
// AndroidTextInputShadowNode is closed pretty tightly, but there's a one
|
|
42
|
+
// place where we can insert ourselves (well, there are two but we really
|
|
43
|
+
// shouldn't mess with vtable). The path to measurement looks like this:
|
|
44
|
+
// AndroidTextInputShadowNode::measureContent ->
|
|
45
|
+
// TextLayoutManager::measureAndroidComponentMapBuffer
|
|
46
|
+
// -> (jni) -> FabricUIManager::measureMapBuffer ->
|
|
47
|
+
// MountingManager::measureMapBuffer
|
|
48
|
+
// We cannot modify the shadow node directly, but we can replace its
|
|
49
|
+
// TextLayoutManager. Literally every method it has is linked statically so we
|
|
50
|
+
// cannot override anything, but we can replace its ContextContainer where a
|
|
51
|
+
// jni reference to the FabricUIManager is stored. Now `measureMapBuffer` is
|
|
52
|
+
// private in the ui manager, but the only thing it does is calling
|
|
53
|
+
// `measureMapBuffer` on MountingManager where it's public. At this point the
|
|
54
|
+
// path forward is clear: we make a custom MountingManager that will perform
|
|
55
|
+
// our measurement, then create a custom FabricUIManager which will direct
|
|
56
|
+
// measurement to our MountingManager. Then we only need to create the
|
|
57
|
+
// ContextContainer with our FabricUIManager, create the TextLayoutManager
|
|
58
|
+
// with the newly created ContextContainer and replace the pointer to
|
|
59
|
+
// TextLayoutManager inside the AndroidTextInputShadowNode.
|
|
60
|
+
|
|
61
|
+
// In order to properly apply markdown formatting to the text input, we need
|
|
62
|
+
// to update the TextInputShadowNode's state to reset the cache key and update
|
|
63
|
+
// its TextLayoutManager reference, but we only have access to the
|
|
64
|
+
// ShadowNodeFamilies of the decorator components. We also know that a
|
|
65
|
+
// markdown decorator is always preceded with the TextInput to decorate, so we
|
|
66
|
+
// need to take the sibling.
|
|
67
|
+
std::vector<MarkdownTextInputDecoratorPair> nodesToUpdate;
|
|
68
|
+
MarkdownShadowFamilyRegistry::runForEveryFamily([&rootNode, &nodesToUpdate](
|
|
69
|
+
ShadowNodeFamily::Shared
|
|
70
|
+
family) {
|
|
71
|
+
// get the path from the root to the node from the decorator family
|
|
72
|
+
const auto ancestors = family->getAncestors(*rootNode);
|
|
73
|
+
|
|
74
|
+
if (!ancestors.empty()) {
|
|
75
|
+
auto &parentNode = ancestors.back().first.get();
|
|
76
|
+
auto index = ancestors.back().second;
|
|
77
|
+
|
|
78
|
+
// this is node represented by one of the registered families and since we
|
|
79
|
+
// only register markdown decorator shadow families, static casting should
|
|
80
|
+
// be safe here
|
|
81
|
+
const auto &decoratorNode =
|
|
82
|
+
std::static_pointer_cast<const MarkdownTextInputDecoratorShadowNode>(
|
|
83
|
+
parentNode.getChildren().at(index));
|
|
84
|
+
// text input always precedes the decorator component
|
|
85
|
+
const auto &previousSibling = parentNode.getChildren().at(index - 1);
|
|
86
|
+
|
|
87
|
+
if (const auto &textInputNode =
|
|
88
|
+
std::dynamic_pointer_cast<const AndroidTextInputShadowNode>(
|
|
89
|
+
previousSibling)) {
|
|
90
|
+
// store the pair of text input and decorator to update in the next step
|
|
91
|
+
// we need both, decorator to get markdown style and text input to
|
|
92
|
+
// update it
|
|
93
|
+
nodesToUpdate.push_back({
|
|
94
|
+
textInputNode,
|
|
95
|
+
decoratorNode,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
for (const auto &nodes : nodesToUpdate) {
|
|
102
|
+
const auto &textInputState =
|
|
103
|
+
*std::static_pointer_cast<const ConcreteState<AndroidTextInputState>>(
|
|
104
|
+
nodes.textInput->getState());
|
|
105
|
+
const auto &stateData = textInputState.getData();
|
|
106
|
+
|
|
107
|
+
rootNode = rootNode->cloneTree(
|
|
108
|
+
nodes.textInput->getFamily(),
|
|
109
|
+
[this, &stateData, &textInputState, &nodes](ShadowNode const &node) {
|
|
110
|
+
auto newStateData =
|
|
111
|
+
std::make_shared<AndroidTextInputState>(stateData);
|
|
112
|
+
// force measurement of a map buffer
|
|
113
|
+
newStateData->cachedAttributedStringId = 0;
|
|
114
|
+
|
|
115
|
+
// clone the text input with the new state
|
|
116
|
+
auto newNode = node.clone({
|
|
117
|
+
.state =
|
|
118
|
+
std::make_shared<const ConcreteState<AndroidTextInputState>>(
|
|
119
|
+
newStateData, textInputState),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const auto currentDecoratorProps =
|
|
123
|
+
nodes.decorator->getProps()->rawProps["markdownStyle"];
|
|
124
|
+
|
|
125
|
+
// if it's the first time we encounter this particular input or the
|
|
126
|
+
// markdown styles have changed (in which case we need to reset the
|
|
127
|
+
// cpp cache, to which we don't have a direct access), create a new
|
|
128
|
+
// instance of TextLayoutManager that will be performing measurement
|
|
129
|
+
// for this particular input
|
|
130
|
+
if (!textLayoutManagers_.contains(nodes.textInput->getTag()) ||
|
|
131
|
+
previousDecoratorProps_[nodes.textInput->getTag()] !=
|
|
132
|
+
currentDecoratorProps) {
|
|
133
|
+
static auto customUIManagerClass = jni::findClassStatic(
|
|
134
|
+
"com/expensify/livemarkdown/CustomFabricUIManager");
|
|
135
|
+
static auto createCustomUIManager =
|
|
136
|
+
customUIManagerClass
|
|
137
|
+
->getStaticMethod<JFabricUIManager::javaobject(
|
|
138
|
+
JFabricUIManager::javaobject, ReadableMap::javaobject)>(
|
|
139
|
+
"create");
|
|
140
|
+
|
|
141
|
+
auto const decoratorPropsRNM =
|
|
142
|
+
ReadableNativeMap::newObjectCxxArgs(currentDecoratorProps);
|
|
143
|
+
auto const decoratorPropsRM =
|
|
144
|
+
jni::make_local(reinterpret_cast<ReadableMap::javaobject>(
|
|
145
|
+
decoratorPropsRNM.get()));
|
|
146
|
+
|
|
147
|
+
const auto customUIManager = jni::make_global(createCustomUIManager(
|
|
148
|
+
customUIManagerClass, fabricUIManager_.get(),
|
|
149
|
+
decoratorPropsRM.get()));
|
|
150
|
+
const ContextContainer::Shared contextContainer =
|
|
151
|
+
std::make_shared<ContextContainer const>();
|
|
152
|
+
contextContainer->insert("FabricUIManager", customUIManager);
|
|
153
|
+
textLayoutManagers_[nodes.textInput->getTag()] =
|
|
154
|
+
std::make_shared<TextLayoutManager>(contextContainer);
|
|
155
|
+
previousDecoratorProps_[nodes.textInput->getTag()] =
|
|
156
|
+
currentDecoratorProps;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// we need to replace the TextLayoutManager every time to make sure
|
|
160
|
+
// the correct measurement code is run
|
|
161
|
+
auto newTextInputShadowNode =
|
|
162
|
+
std::static_pointer_cast<AndroidTextInputShadowNode>(newNode);
|
|
163
|
+
newTextInputShadowNode->setTextLayoutManager(
|
|
164
|
+
textLayoutManagers_[nodes.textInput->getTag()]);
|
|
165
|
+
|
|
166
|
+
return newNode;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return std::static_pointer_cast<RootShadowNode>(rootNode);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
} // namespace livemarkdown
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
#include <react/fabric/JFabricUIManager.h>
|
|
5
|
+
|
|
6
|
+
#include <react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h>
|
|
7
|
+
#include <react/renderer/uimanager/UIManager.h>
|
|
8
|
+
#include <react/renderer/uimanager/UIManagerCommitHook.h>
|
|
9
|
+
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <unordered_map>
|
|
12
|
+
|
|
13
|
+
#include "react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h"
|
|
14
|
+
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
using namespace react;
|
|
17
|
+
|
|
18
|
+
namespace livemarkdown {
|
|
19
|
+
|
|
20
|
+
struct MarkdownTextInputDecoratorPair {
|
|
21
|
+
const std::shared_ptr<const AndroidTextInputShadowNode> textInput;
|
|
22
|
+
const std::shared_ptr<const MarkdownTextInputDecoratorShadowNode> decorator;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
class MarkdownCommitHook : public UIManagerCommitHook {
|
|
26
|
+
public:
|
|
27
|
+
MarkdownCommitHook(
|
|
28
|
+
jni::global_ref<facebook::react::JFabricUIManager::javaobject>
|
|
29
|
+
fabricUIManager);
|
|
30
|
+
|
|
31
|
+
~MarkdownCommitHook() noexcept override;
|
|
32
|
+
|
|
33
|
+
void commitHookWasRegistered(UIManager const &) noexcept override {}
|
|
34
|
+
|
|
35
|
+
void commitHookWasUnregistered(UIManager const &) noexcept override {}
|
|
36
|
+
|
|
37
|
+
RootShadowNode::Unshared shadowTreeWillCommit(
|
|
38
|
+
ShadowTree const &shadowTree,
|
|
39
|
+
RootShadowNode::Shared const &oldRootShadowNode,
|
|
40
|
+
RootShadowNode::Unshared const &newRootShadowNode) noexcept override;
|
|
41
|
+
|
|
42
|
+
private:
|
|
43
|
+
const jni::global_ref<facebook::react::JFabricUIManager::javaobject>
|
|
44
|
+
fabricUIManager_;
|
|
45
|
+
const std::shared_ptr<UIManager> uiManager_;
|
|
46
|
+
std::unordered_map<facebook::react::Tag, SharedTextLayoutManager>
|
|
47
|
+
textLayoutManagers_;
|
|
48
|
+
std::unordered_map<facebook::react::Tag, folly::dynamic>
|
|
49
|
+
previousDecoratorProps_;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
} // namespace livemarkdown
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#include <fbjni/fbjni.h>
|
|
2
|
+
#include <react/fabric/Binding.h>
|
|
3
|
+
#include <react/renderer/scheduler/Scheduler.h>
|
|
4
|
+
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
#include "NativeProxy.h"
|
|
8
|
+
|
|
9
|
+
namespace livemarkdown {
|
|
10
|
+
|
|
11
|
+
using namespace facebook;
|
|
12
|
+
using namespace react;
|
|
13
|
+
|
|
14
|
+
NativeProxy::NativeProxy(jni::alias_ref<NativeProxy::javaobject> jThis)
|
|
15
|
+
: javaPart_(jni::make_global(jThis)) {}
|
|
16
|
+
|
|
17
|
+
NativeProxy::~NativeProxy() {}
|
|
18
|
+
|
|
19
|
+
void NativeProxy::registerNatives() {
|
|
20
|
+
registerHybrid(
|
|
21
|
+
{makeNativeMethod("initHybrid", NativeProxy::initHybrid),
|
|
22
|
+
makeNativeMethod("createCommitHook", NativeProxy::createCommitHook)});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void NativeProxy::createCommitHook(
|
|
26
|
+
jni::alias_ref<facebook::react::JFabricUIManager::javaobject>
|
|
27
|
+
fabricUIManager) {
|
|
28
|
+
const auto &globalUIManager = jni::make_global(fabricUIManager);
|
|
29
|
+
|
|
30
|
+
this->commitHook_ = std::make_shared<MarkdownCommitHook>(globalUIManager);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
jni::local_ref<NativeProxy::jhybriddata>
|
|
34
|
+
NativeProxy::initHybrid(jni::alias_ref<jhybridobject> jThis) {
|
|
35
|
+
return makeCxxInstance(jThis);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
} // namespace livemarkdown
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
#include <react/fabric/JFabricUIManager.h>
|
|
5
|
+
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
#include "MarkdownCommitHook.h"
|
|
9
|
+
|
|
10
|
+
namespace livemarkdown {
|
|
11
|
+
|
|
12
|
+
using namespace facebook;
|
|
13
|
+
using namespace facebook::jni;
|
|
14
|
+
|
|
15
|
+
class NativeProxy : public jni::HybridClass<NativeProxy> {
|
|
16
|
+
public:
|
|
17
|
+
static auto constexpr kJavaDescriptor =
|
|
18
|
+
"Lcom/expensify/livemarkdown/NativeProxy;";
|
|
19
|
+
static jni::local_ref<jhybriddata>
|
|
20
|
+
initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
21
|
+
static void registerNatives();
|
|
22
|
+
|
|
23
|
+
~NativeProxy();
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
friend HybridBase;
|
|
27
|
+
jni::global_ref<NativeProxy::javaobject> javaPart_;
|
|
28
|
+
std::shared_ptr<MarkdownCommitHook> commitHook_;
|
|
29
|
+
|
|
30
|
+
explicit NativeProxy(jni::alias_ref<NativeProxy::javaobject> jThis);
|
|
31
|
+
|
|
32
|
+
void
|
|
33
|
+
createCommitHook(jni::alias_ref<facebook::react::JFabricUIManager::javaobject>
|
|
34
|
+
fabricUIManager);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
} // namespace livemarkdown
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by
|
|
4
|
+
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
5
|
+
*
|
|
6
|
+
* Do not edit this file as changes may cause incorrect behavior and will be
|
|
7
|
+
* lost once the code is regenerated.
|
|
8
|
+
*
|
|
9
|
+
* @generated by codegen project: GenerateModuleJniCpp.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "RNLiveMarkdownSpec.h"
|
|
13
|
+
|
|
14
|
+
namespace facebook {
|
|
15
|
+
namespace react {
|
|
16
|
+
|
|
17
|
+
static facebook::jsi::Value
|
|
18
|
+
__hostFunction_NativeLiveMarkdownModuleSpecJSI_install(
|
|
19
|
+
facebook::jsi::Runtime &rt, TurboModule &turboModule,
|
|
20
|
+
const facebook::jsi::Value *args, size_t count) {
|
|
21
|
+
static jmethodID cachedMethodId = nullptr;
|
|
22
|
+
return static_cast<JavaTurboModule &>(turboModule)
|
|
23
|
+
.invokeJavaMethod(rt, BooleanKind, "install", "()Z", args, count,
|
|
24
|
+
cachedMethodId);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
NativeLiveMarkdownModuleSpecJSI::NativeLiveMarkdownModuleSpecJSI(
|
|
28
|
+
const JavaTurboModule::InitParams ¶ms)
|
|
29
|
+
: JavaTurboModule(params) {
|
|
30
|
+
methodMap_["install"] =
|
|
31
|
+
MethodMetadata{0, __hostFunction_NativeLiveMarkdownModuleSpecJSI_install};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
std::shared_ptr<TurboModule>
|
|
35
|
+
RNLiveMarkdownSpec_ModuleProvider(const std::string &moduleName,
|
|
36
|
+
const JavaTurboModule::InitParams ¶ms) {
|
|
37
|
+
if (moduleName == "LiveMarkdownModule") {
|
|
38
|
+
return std::make_shared<NativeLiveMarkdownModuleSpecJSI>(params);
|
|
39
|
+
}
|
|
40
|
+
return nullptr;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
} // namespace react
|
|
44
|
+
} // namespace facebook
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by
|
|
4
|
+
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
5
|
+
*
|
|
6
|
+
* Do not edit this file as changes may cause incorrect behavior and will be
|
|
7
|
+
* lost once the code is regenerated.
|
|
8
|
+
*
|
|
9
|
+
* @generated by codegen project: GenerateModuleJniH.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#pragma once
|
|
13
|
+
|
|
14
|
+
#include <ReactCommon/JavaTurboModule.h>
|
|
15
|
+
#include <ReactCommon/TurboModule.h>
|
|
16
|
+
#include <jsi/jsi.h>
|
|
17
|
+
|
|
18
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorViewComponentDescriptor.h>
|
|
19
|
+
|
|
20
|
+
namespace facebook {
|
|
21
|
+
namespace react {
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* JNI C++ class for module 'NativeLiveMarkdownModule'
|
|
25
|
+
*/
|
|
26
|
+
class JSI_EXPORT NativeLiveMarkdownModuleSpecJSI : public JavaTurboModule {
|
|
27
|
+
public:
|
|
28
|
+
NativeLiveMarkdownModuleSpecJSI(const JavaTurboModule::InitParams ¶ms);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
JSI_EXPORT
|
|
32
|
+
std::shared_ptr<TurboModule>
|
|
33
|
+
RNLiveMarkdownSpec_ModuleProvider(const std::string &moduleName,
|
|
34
|
+
const JavaTurboModule::InitParams ¶ms);
|
|
35
|
+
|
|
36
|
+
} // namespace react
|
|
37
|
+
} // namespace facebook
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package com.expensify.livemarkdown;
|
|
2
|
+
|
|
3
|
+
import com.facebook.jni.HybridData;
|
|
4
|
+
import com.facebook.proguard.annotations.DoNotStrip;
|
|
5
|
+
import com.facebook.react.fabric.FabricUIManager;
|
|
6
|
+
import com.facebook.soloader.SoLoader;
|
|
7
|
+
|
|
8
|
+
public class NativeProxy {
|
|
9
|
+
static {
|
|
10
|
+
SoLoader.loadLibrary("react_codegen_RNLiveMarkdownSpec");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@DoNotStrip
|
|
14
|
+
@SuppressWarnings("unused")
|
|
15
|
+
private final HybridData mHybridData;
|
|
16
|
+
|
|
17
|
+
public NativeProxy() {
|
|
18
|
+
mHybridData = initHybrid();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private native HybridData initHybrid();
|
|
22
|
+
|
|
23
|
+
public native void createCommitHook(FabricUIManager fabricUIManager);
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateModuleJavaSpec.js
|
|
9
|
+
*
|
|
10
|
+
* @nolint
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.expensify.livemarkdown;
|
|
14
|
+
|
|
15
|
+
import com.facebook.proguard.annotations.DoNotStrip;
|
|
16
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
17
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
18
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
19
|
+
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
|
20
|
+
import javax.annotation.Nonnull;
|
|
21
|
+
|
|
22
|
+
public abstract class NativeLiveMarkdownModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
|
23
|
+
public static final String NAME = "LiveMarkdownModule";
|
|
24
|
+
|
|
25
|
+
public NativeLiveMarkdownModuleSpec(ReactApplicationContext reactContext) {
|
|
26
|
+
super(reactContext);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Override
|
|
30
|
+
public @Nonnull String getName() {
|
|
31
|
+
return NAME;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
35
|
+
@DoNotStrip
|
|
36
|
+
public abstract boolean install();
|
|
37
|
+
}
|
package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownShadowFamilyRegistry.h
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
-
#
|
|
2
|
+
#if defined(RCT_NEW_ARCH_ENABLED) || defined(ANDROID)
|
|
3
3
|
|
|
4
|
-
#include <react/renderer/
|
|
4
|
+
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
5
5
|
|
|
6
6
|
#include <mutex>
|
|
7
7
|
#include <set>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
-
#
|
|
2
|
+
#if defined(RCT_NEW_ARCH_ENABLED) || defined(ANDROID)
|
|
3
3
|
|
|
4
4
|
#include "MarkdownShadowFamilyRegistry.h"
|
|
5
5
|
#include "MarkdownTextInputDecoratorState.h"
|
|
6
6
|
#include <jsi/jsi.h>
|
|
7
7
|
#include <react/renderer/components/RNLiveMarkdownSpec/EventEmitters.h>
|
|
8
8
|
#include <react/renderer/components/RNLiveMarkdownSpec/Props.h>
|
|
9
|
-
#include <react/renderer/components/iostextinput/TextInputShadowNode.h>
|
|
10
9
|
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
11
10
|
|
|
12
11
|
namespace facebook {
|
package/{ios → cpp/react/renderer/components/RNLiveMarkdownSpec}/MarkdownTextInputDecoratorState.h
RENAMED
|
@@ -14,7 +14,20 @@ public:
|
|
|
14
14
|
const ShadowNodeFamily::Shared textInputFamily_)
|
|
15
15
|
: decoratorFamily(textInputFamily_){};
|
|
16
16
|
|
|
17
|
+
#ifdef ANDROID
|
|
18
|
+
MarkdownTextInputDecoratorState(
|
|
19
|
+
MarkdownTextInputDecoratorState const &previousState, folly::dynamic data)
|
|
20
|
+
: decoratorFamily(previousState.decoratorFamily){};
|
|
21
|
+
#endif
|
|
22
|
+
|
|
17
23
|
const ShadowNodeFamily::Shared decoratorFamily;
|
|
24
|
+
|
|
25
|
+
#ifdef ANDROID
|
|
26
|
+
folly::dynamic getDynamic() const {
|
|
27
|
+
return folly::dynamic::object("decoratorFamily", "pointer should be here?");
|
|
28
|
+
}
|
|
29
|
+
MapBuffer getMapBuffer() const { return MapBufferBuilder::EMPTY(); };
|
|
30
|
+
#endif
|
|
18
31
|
};
|
|
19
32
|
|
|
20
33
|
} // namespace react
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
namespace facebook {
|
|
8
8
|
namespace react {
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class MarkdownTextInputDecoratorViewComponentDescriptor final
|
|
11
11
|
: public ConcreteComponentDescriptor<MarkdownTextInputDecoratorShadowNode> {
|
|
12
12
|
public:
|
|
13
13
|
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
#import <RNLiveMarkdown/MarkdownTextInputDecoratorView.h>
|
|
7
7
|
#import <RNLiveMarkdown/RCTMarkdownStyle.h>
|
|
8
8
|
|
|
9
|
-
#import
|
|
9
|
+
#import <RNLiveMarkdown/MarkdownTextInputDecoratorViewComponentDescriptor.h>
|
|
10
10
|
#import "MarkdownShadowFamilyRegistry.h"
|
|
11
11
|
#import "RCTFabricComponentsPlugins.h"
|
|
12
12
|
|
|
@@ -19,7 +19,7 @@ using namespace facebook::react;
|
|
|
19
19
|
|
|
20
20
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
21
21
|
{
|
|
22
|
-
return concreteComponentDescriptorProvider<
|
|
22
|
+
return concreteComponentDescriptorProvider<MarkdownTextInputDecoratorViewComponentDescriptor>();
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
@@ -6,5 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _codegenNativeComponent = _interopRequireDefault(require("react-native/Libraries/Utilities/codegenNativeComponent"));
|
|
8
8
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
|
-
var _default = exports.default = (0, _codegenNativeComponent.default)('MarkdownTextInputDecoratorView'
|
|
9
|
+
var _default = exports.default = (0, _codegenNativeComponent.default)('MarkdownTextInputDecoratorView', {
|
|
10
|
+
interfaceOnly: true
|
|
11
|
+
});
|
|
10
12
|
//# sourceMappingURL=MarkdownTextInputDecoratorViewNativeComponent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_codegenNativeComponent","_interopRequireDefault","require","obj","__esModule","default","_default","exports","codegenNativeComponent"],"sourceRoot":"../../src","sources":["MarkdownTextInputDecoratorViewNativeComponent.ts"],"mappings":";;;;;;AAGA,IAAAA,uBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA6F,SAAAD,uBAAAE,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GA+C9E,IAAAG,+BAAsB,EAAc,gCAAgC,CAAC"}
|
|
1
|
+
{"version":3,"names":["_codegenNativeComponent","_interopRequireDefault","require","obj","__esModule","default","_default","exports","codegenNativeComponent","interfaceOnly"],"sourceRoot":"../../src","sources":["MarkdownTextInputDecoratorViewNativeComponent.ts"],"mappings":";;;;;;AAGA,IAAAA,uBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA6F,SAAAD,uBAAAE,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GA+C9E,IAAAG,+BAAsB,EAAc,gCAAgC,EAAE;EACnFC,aAAa,EAAE;AACjB,CAAC,CAAC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
2
|
-
export default codegenNativeComponent('MarkdownTextInputDecoratorView'
|
|
2
|
+
export default codegenNativeComponent('MarkdownTextInputDecoratorView', {
|
|
3
|
+
interfaceOnly: true
|
|
4
|
+
});
|
|
3
5
|
//# sourceMappingURL=MarkdownTextInputDecoratorViewNativeComponent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["codegenNativeComponent"],"sourceRoot":"../../src","sources":["MarkdownTextInputDecoratorViewNativeComponent.ts"],"mappings":"AAGA,OAAOA,sBAAsB,MAAM,yDAAyD;AA+C5F,eAAeA,sBAAsB,CAAc,gCAAgC,CAAC"}
|
|
1
|
+
{"version":3,"names":["codegenNativeComponent","interfaceOnly"],"sourceRoot":"../../src","sources":["MarkdownTextInputDecoratorViewNativeComponent.ts"],"mappings":"AAGA,OAAOA,sBAAsB,MAAM,yDAAyD;AA+C5F,eAAeA,sBAAsB,CAAc,gCAAgC,EAAE;EACnFC,aAAa,EAAE;AACjB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownTextInputDecoratorViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/MarkdownTextInputDecoratorViewNativeComponent.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,2CAA2C,CAAC;AAGrE,UAAU,aAAa;IACrB,MAAM,EAAE;QACN,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC;IACF,KAAK,EAAE;QACL,QAAQ,EAAE,KAAK,CAAC;KACjB,CAAC;IACF,IAAI,EAAE;QACJ,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC;IACF,EAAE,EAAE;QACF,QAAQ,EAAE,KAAK,CAAC;KACjB,CAAC;IACF,UAAU,EAAE;QACV,WAAW,EAAE,UAAU,CAAC;QACxB,WAAW,EAAE,KAAK,CAAC;QACnB,UAAU,EAAE,KAAK,CAAC;QAClB,WAAW,EAAE,KAAK,CAAC;KACpB,CAAC;IACF,IAAI,EAAE;QACJ,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,KAAK,CAAC;QAChB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;IACF,GAAG,EAAE;QACH,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,KAAK,CAAC;QAChB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;CACH;AAED,UAAU,WAAY,SAAQ,SAAS;IACrC,aAAa,EAAE,aAAa,CAAC;CAC9B;;AAED,
|
|
1
|
+
{"version":3,"file":"MarkdownTextInputDecoratorViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/MarkdownTextInputDecoratorViewNativeComponent.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,2CAA2C,CAAC;AAGrE,UAAU,aAAa;IACrB,MAAM,EAAE;QACN,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC;IACF,KAAK,EAAE;QACL,QAAQ,EAAE,KAAK,CAAC;KACjB,CAAC;IACF,IAAI,EAAE;QACJ,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC;IACF,EAAE,EAAE;QACF,QAAQ,EAAE,KAAK,CAAC;KACjB,CAAC;IACF,UAAU,EAAE;QACV,WAAW,EAAE,UAAU,CAAC;QACxB,WAAW,EAAE,KAAK,CAAC;QACnB,UAAU,EAAE,KAAK,CAAC;QAClB,WAAW,EAAE,KAAK,CAAC;KACpB,CAAC;IACF,IAAI,EAAE;QACJ,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,KAAK,CAAC;QAChB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;IACF,GAAG,EAAE;QACH,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,KAAK,CAAC;QAChB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,UAAU,CAAC;KAC7B,CAAC;CACH;AAED,UAAU,WAAY,SAAQ,SAAS;IACrC,aAAa,EAAE,aAAa,CAAC;CAC9B;;AAED,wBAEG;AAEH,YAAY,EAAC,aAAa,EAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expensify/react-native-live-markdown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"ios",
|
|
16
16
|
"cpp",
|
|
17
17
|
"*.podspec",
|
|
18
|
+
"react-native.config.js",
|
|
18
19
|
"!ios/build",
|
|
19
20
|
"!android/build",
|
|
20
21
|
"!android/gradle",
|
|
@@ -48,6 +48,8 @@ interface NativeProps extends ViewProps {
|
|
|
48
48
|
markdownStyle: MarkdownStyle;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export default codegenNativeComponent<NativeProps>('MarkdownTextInputDecoratorView'
|
|
51
|
+
export default codegenNativeComponent<NativeProps>('MarkdownTextInputDecoratorView', {
|
|
52
|
+
interfaceOnly: true,
|
|
53
|
+
});
|
|
52
54
|
|
|
53
55
|
export type {MarkdownStyle};
|