@expensify/react-native-live-markdown 0.1.254 → 0.1.256
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 +2 -0
- package/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java +0 -10
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorView.java +5 -28
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorViewManagerSpec.java +3 -3
- package/android/src/main/new_arch/CMakeLists.txt +2 -1
- package/android/src/main/new_arch/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp +135 -0
- package/android/src/main/new_arch/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h +75 -0
- package/apple/LiveMarkdownModule.mm +1 -53
- package/apple/MarkdownTextInputDecoratorComponentView.mm +6 -36
- package/apple/MarkdownTextInputDecoratorShadowNode.h +67 -0
- package/apple/MarkdownTextInputDecoratorShadowNode.mm +271 -0
- package/apple/RCTMarkdownUtils.mm +6 -0
- package/apple/RCTTextInputComponentView+Markdown.mm +0 -16
- package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorState.h +3 -16
- package/lib/commonjs/MarkdownTextInput.js +7 -9
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/module/MarkdownTextInput.js +7 -9
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/typescript/src/MarkdownTextInput.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/MarkdownTextInput.tsx +8 -11
- package/android/src/main/java/com/expensify/livemarkdown/NativeProxy.java +0 -24
- package/android/src/main/new_arch/MarkdownCommitHook.cpp +0 -192
- package/android/src/main/new_arch/MarkdownCommitHook.h +0 -58
- package/android/src/main/new_arch/NativeProxy.cpp +0 -40
- package/android/src/main/new_arch/NativeProxy.h +0 -39
- package/android/src/main/new_arch/OnLoad.cpp +0 -8
- package/apple/MarkdownCommitHook.h +0 -46
- package/apple/MarkdownCommitHook.mm +0 -303
- package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownShadowFamilyRegistry.cpp +0 -60
- package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownShadowFamilyRegistry.h +0 -38
- package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp +0 -33
- package/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h +0 -46
- package/cpp/react/renderer/components/RNLiveMarkdownSpec/OwningShadowNodeFragment.h +0 -28
package/RNLiveMarkdown.podspec
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
package com.expensify.livemarkdown;
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
-
import com.facebook.react.fabric.FabricUIManager;
|
|
5
|
-
import com.facebook.react.uimanager.UIManagerHelper;
|
|
6
|
-
import com.facebook.react.uimanager.common.UIManagerType;
|
|
7
4
|
import com.facebook.soloader.SoLoader;
|
|
8
5
|
|
|
9
6
|
import java.util.Objects;
|
|
@@ -13,19 +10,12 @@ public class LiveMarkdownModule extends NativeLiveMarkdownModuleSpec {
|
|
|
13
10
|
SoLoader.loadLibrary("livemarkdown");
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
private NativeProxy mNativeProxy;
|
|
17
13
|
public LiveMarkdownModule(ReactApplicationContext reactContext) {
|
|
18
14
|
super(reactContext);
|
|
19
|
-
|
|
20
|
-
this.mNativeProxy = new NativeProxy();
|
|
21
15
|
}
|
|
22
16
|
|
|
23
17
|
@Override
|
|
24
18
|
public boolean install() {
|
|
25
|
-
FabricUIManager uiManager =
|
|
26
|
-
(FabricUIManager) UIManagerHelper.getUIManager(getReactApplicationContext(), UIManagerType.FABRIC);
|
|
27
|
-
mNativeProxy.createCommitHook(uiManager);
|
|
28
|
-
|
|
29
19
|
long jsiRuntime = Objects.requireNonNull(getReactApplicationContext().getJavaScriptContextHolder(), "[react-native-live-markdown] JavaScriptContextHolder is null").get();
|
|
30
20
|
injectJSIBindings(jsiRuntime);
|
|
31
21
|
|
package/android/src/main/java/com/expensify/livemarkdown/MarkdownTextInputDecoratorView.java
CHANGED
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
package com.expensify.livemarkdown;
|
|
2
2
|
|
|
3
|
-
import androidx.annotation.Nullable;
|
|
4
|
-
|
|
5
3
|
import android.content.Context;
|
|
6
4
|
import android.text.Editable;
|
|
7
5
|
import android.text.SpannableStringBuilder;
|
|
8
6
|
import android.text.TextWatcher;
|
|
9
|
-
import android.util.AttributeSet;
|
|
10
7
|
|
|
11
8
|
import android.view.View;
|
|
12
|
-
import android.view.ViewGroup;
|
|
13
|
-
import android.view.ViewParent;
|
|
14
9
|
|
|
15
10
|
import com.facebook.react.bridge.ReactContext;
|
|
16
11
|
import com.facebook.react.views.textinput.ReactEditText;
|
|
12
|
+
import com.facebook.react.views.view.ReactViewGroup;
|
|
17
13
|
|
|
18
|
-
public class MarkdownTextInputDecoratorView extends
|
|
14
|
+
public class MarkdownTextInputDecoratorView extends ReactViewGroup {
|
|
19
15
|
|
|
20
16
|
public MarkdownTextInputDecoratorView(Context context) {
|
|
21
17
|
super(context);
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
public MarkdownTextInputDecoratorView(Context context, @Nullable AttributeSet attrs) {
|
|
25
|
-
super(context, attrs);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public MarkdownTextInputDecoratorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
|
29
|
-
super(context, attrs, defStyleAttr);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
20
|
private MarkdownStyle mMarkdownStyle;
|
|
33
21
|
|
|
34
22
|
private int mParserId;
|
|
@@ -43,23 +31,12 @@ public class MarkdownTextInputDecoratorView extends View {
|
|
|
43
31
|
protected void onAttachedToWindow() {
|
|
44
32
|
super.onAttachedToWindow();
|
|
45
33
|
|
|
46
|
-
View
|
|
47
|
-
|
|
48
|
-
if (parent instanceof ViewGroup) {
|
|
49
|
-
final ViewGroup viewGroup = (ViewGroup) parent;
|
|
50
|
-
for (int i = 1; i < viewGroup.getChildCount(); i++) {
|
|
51
|
-
if (viewGroup.getChildAt(i) == this) {
|
|
52
|
-
previousSibling = viewGroup.getChildAt(i - 1);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (previousSibling instanceof ReactEditText) {
|
|
34
|
+
View child = getChildAt(0);
|
|
35
|
+
if (child instanceof ReactEditText) {
|
|
59
36
|
mMarkdownUtils = new MarkdownUtils((ReactContext) getContext());
|
|
60
37
|
mMarkdownUtils.setMarkdownStyle(mMarkdownStyle);
|
|
61
38
|
mMarkdownUtils.setParserId(mParserId);
|
|
62
|
-
mReactEditText = (ReactEditText)
|
|
39
|
+
mReactEditText = (ReactEditText) child;
|
|
63
40
|
mTextWatcher = new MarkdownTextWatcher(mMarkdownUtils);
|
|
64
41
|
mReactEditText.addTextChangedListener(mTextWatcher);
|
|
65
42
|
applyNewStyles();
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
package com.expensify.livemarkdown;
|
|
2
2
|
|
|
3
|
-
import android.view.
|
|
3
|
+
import android.view.ViewGroup;
|
|
4
4
|
|
|
5
5
|
import androidx.annotation.Nullable;
|
|
6
6
|
|
|
7
|
-
import com.facebook.react.uimanager.
|
|
7
|
+
import com.facebook.react.uimanager.ViewGroupManager;
|
|
8
8
|
import com.facebook.react.uimanager.ViewManagerDelegate;
|
|
9
9
|
import com.facebook.react.viewmanagers.MarkdownTextInputDecoratorViewManagerDelegate;
|
|
10
10
|
import com.facebook.react.viewmanagers.MarkdownTextInputDecoratorViewManagerInterface;
|
|
11
11
|
|
|
12
|
-
public abstract class MarkdownTextInputDecoratorViewManagerSpec<T extends
|
|
12
|
+
public abstract class MarkdownTextInputDecoratorViewManagerSpec<T extends ViewGroup> extends ViewGroupManager<T> implements MarkdownTextInputDecoratorViewManagerInterface<T> {
|
|
13
13
|
private final ViewManagerDelegate<T> mDelegate;
|
|
14
14
|
|
|
15
15
|
public MarkdownTextInputDecoratorViewManagerSpec() {
|
|
@@ -11,7 +11,7 @@ set(LIB_CUSTOM_SOURCES_DIR ${LIB_CPP_DIR}/react/renderer/components/${LIB_LITERA
|
|
|
11
11
|
set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/build/generated/source/codegen/jni)
|
|
12
12
|
set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})
|
|
13
13
|
|
|
14
|
-
file(GLOB LIB_MODULE_SRCS CONFIGURE_DEPENDS *.cpp)
|
|
14
|
+
file(GLOB LIB_MODULE_SRCS CONFIGURE_DEPENDS *.cpp react/renderer/components/RNLiveMarkdownSpec/*.cpp)
|
|
15
15
|
file(GLOB LIB_CUSTOM_SRCS CONFIGURE_DEPENDS ${LIB_CUSTOM_SOURCES_DIR}/*.cpp)
|
|
16
16
|
file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp)
|
|
17
17
|
|
|
@@ -27,6 +27,7 @@ target_include_directories(
|
|
|
27
27
|
${LIB_TARGET_NAME}
|
|
28
28
|
PUBLIC
|
|
29
29
|
.
|
|
30
|
+
./react/renderer/components/RNLiveMarkdownSpec
|
|
30
31
|
${LIB_ANDROID_GENERATED_JNI_DIR}
|
|
31
32
|
${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
|
|
32
33
|
${LIB_CPP_DIR}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#include "MarkdownTextInputDecoratorShadowNode.h"
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
#include <react/fabric/JFabricUIManager.h>
|
|
5
|
+
#include <react/jni/ReadableNativeMap.h>
|
|
6
|
+
#include <react/renderer/components/androidtextinput/AndroidTextInputState.h>
|
|
7
|
+
#include <react/renderer/components/view/conversions.h>
|
|
8
|
+
#include <react/renderer/core/ComponentDescriptor.h>
|
|
9
|
+
#include <yoga/Yoga.h>
|
|
10
|
+
|
|
11
|
+
namespace facebook {
|
|
12
|
+
namespace react {
|
|
13
|
+
|
|
14
|
+
extern const char MarkdownTextInputDecoratorViewComponentName[] =
|
|
15
|
+
"MarkdownTextInputDecoratorView";
|
|
16
|
+
|
|
17
|
+
void MarkdownTextInputDecoratorShadowNode::initialize() {
|
|
18
|
+
// Setting display: contents style results in ForceFlattenView trait being set
|
|
19
|
+
// on the shadow node. This trait causes the node not to have a host view. By
|
|
20
|
+
// removing the trait, it's possible to force RN to create a host view, layout
|
|
21
|
+
// of which can then be customized.
|
|
22
|
+
ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void MarkdownTextInputDecoratorShadowNode::createCustomContextContainer() {
|
|
26
|
+
static const auto customFabricUIManagerClass = jni::findClassStatic(
|
|
27
|
+
"com/expensify/livemarkdown/CustomFabricUIManager");
|
|
28
|
+
static const auto createMethod =
|
|
29
|
+
customFabricUIManagerClass
|
|
30
|
+
->getStaticMethod<JFabricUIManager::javaobject(
|
|
31
|
+
JFabricUIManager::javaobject, ReadableMap::javaobject, int)>(
|
|
32
|
+
"create");
|
|
33
|
+
|
|
34
|
+
const auto &rawProps = this->getProps()->rawProps;
|
|
35
|
+
const auto &markdownStyle = rawProps["markdownStyle"];
|
|
36
|
+
const auto parserId = rawProps["parserId"].asInt();
|
|
37
|
+
|
|
38
|
+
const auto decoratorPropsRNM =
|
|
39
|
+
ReadableNativeMap::newObjectCxxArgs(markdownStyle);
|
|
40
|
+
const auto decoratorPropsRM =
|
|
41
|
+
jni::make_local(reinterpret_cast<ReadableMap::javaobject>(
|
|
42
|
+
decoratorPropsRNM.get()));
|
|
43
|
+
|
|
44
|
+
const auto &fabricUIManager =
|
|
45
|
+
this->getContextContainer()->at<JFabricUIManager::javaobject>("FabricUIManager");
|
|
46
|
+
|
|
47
|
+
const auto customFabricUIManager = jni::make_global(createMethod(
|
|
48
|
+
customFabricUIManagerClass, fabricUIManager,
|
|
49
|
+
decoratorPropsRM.get(), parserId));
|
|
50
|
+
const auto contextContainer =
|
|
51
|
+
std::make_shared<ContextContainer const>();
|
|
52
|
+
contextContainer->insert("FabricUIManager", customFabricUIManager);
|
|
53
|
+
|
|
54
|
+
customContextContainer_ = contextContainer;
|
|
55
|
+
previousMarkdownStyle_ = markdownStyle;
|
|
56
|
+
previousParserId_ = parserId;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void MarkdownTextInputDecoratorShadowNode::updateCustomContextContainerIfNeeded() {
|
|
60
|
+
const auto markdownStyle =
|
|
61
|
+
this->getProps()->rawProps["markdownStyle"];
|
|
62
|
+
const auto parserId =
|
|
63
|
+
this->getProps()->rawProps["parserId"].asInt();
|
|
64
|
+
|
|
65
|
+
if (parserId != previousParserId_ || markdownStyle != previousMarkdownStyle_) {
|
|
66
|
+
createCustomContextContainer();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void MarkdownTextInputDecoratorShadowNode::overwriteTextLayoutManager() {
|
|
71
|
+
const auto &children = getChildren();
|
|
72
|
+
if (children.empty()) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
react_native_assert(
|
|
76
|
+
children.size() == 1 &&
|
|
77
|
+
"MarkdownTextInputDecoratorView received more than one child");
|
|
78
|
+
|
|
79
|
+
const auto child = std::dynamic_pointer_cast<const AndroidTextInputShadowNode>(
|
|
80
|
+
children[0]);
|
|
81
|
+
|
|
82
|
+
react_native_assert(
|
|
83
|
+
child != nullptr &&
|
|
84
|
+
"MarkdownTextInputDecoratorView received child other than a TextInput");
|
|
85
|
+
child->ensureUnsealed();
|
|
86
|
+
|
|
87
|
+
const auto mutableChild = std::const_pointer_cast<AndroidTextInputShadowNode>(child);
|
|
88
|
+
mutableChild->setTextLayoutManager(
|
|
89
|
+
std::make_shared<TextLayoutManager>(customContextContainer_));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void MarkdownTextInputDecoratorShadowNode::appendChild(
|
|
93
|
+
const ShadowNode::Shared &child) {
|
|
94
|
+
YogaLayoutableShadowNode::appendChild(child);
|
|
95
|
+
|
|
96
|
+
overwriteTextLayoutManager();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void MarkdownTextInputDecoratorShadowNode::replaceChild(
|
|
100
|
+
const ShadowNode &oldChild, const ShadowNode::Shared &newChild,
|
|
101
|
+
size_t suggestedIndex) {
|
|
102
|
+
YogaLayoutableShadowNode::replaceChild(oldChild, newChild, suggestedIndex);
|
|
103
|
+
|
|
104
|
+
overwriteTextLayoutManager();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void MarkdownTextInputDecoratorShadowNode::layout(LayoutContext layoutContext) {
|
|
108
|
+
YogaLayoutableShadowNode::layout(layoutContext);
|
|
109
|
+
|
|
110
|
+
const auto &children = getChildren();
|
|
111
|
+
react_native_assert(
|
|
112
|
+
children.size() == 1 &&
|
|
113
|
+
"MarkdownTextInputDecoratorView didn't receive exactly one child");
|
|
114
|
+
|
|
115
|
+
auto child = std::static_pointer_cast<const YogaLayoutableShadowNode>(getChildren()[0]);
|
|
116
|
+
child->ensureUnsealed();
|
|
117
|
+
auto mutableChild = std::const_pointer_cast<YogaLayoutableShadowNode>(child);
|
|
118
|
+
|
|
119
|
+
// TODO: this may not be the correct way to do this
|
|
120
|
+
// Since nodes with display: contents are skipped during layout, they have
|
|
121
|
+
// zero-layout. To properly display the view, assign the layout metrics from
|
|
122
|
+
// the child (text input, which was calculated by Yoga) to the decorator view.
|
|
123
|
+
auto childMetrics = child->getLayoutMetrics();
|
|
124
|
+
setLayoutMetrics(childMetrics); // makes a copy
|
|
125
|
+
|
|
126
|
+
// Then, it's also needed to update the metrics on the child as the position
|
|
127
|
+
// is relative to the parent, which was just moved above. By zeroing the
|
|
128
|
+
// origin, the child is effectively moved to the same position it was before
|
|
129
|
+
// the manipulation here.
|
|
130
|
+
childMetrics.frame.origin = Point{};
|
|
131
|
+
mutableChild->setLayoutMetrics(childMetrics);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
} // namespace react
|
|
135
|
+
} // namespace facebook
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorState.h>
|
|
4
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/EventEmitters.h>
|
|
5
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/Props.h>
|
|
6
|
+
#include <react/renderer/components/androidtextinput/AndroidTextInputShadowNode.h>
|
|
7
|
+
|
|
8
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
9
|
+
#include <react/renderer/core/LayoutContext.h>
|
|
10
|
+
|
|
11
|
+
namespace facebook {
|
|
12
|
+
namespace react {
|
|
13
|
+
|
|
14
|
+
JSI_EXPORT extern const char MarkdownTextInputDecoratorViewComponentName[];
|
|
15
|
+
|
|
16
|
+
class JSI_EXPORT MarkdownTextInputDecoratorShadowNode final
|
|
17
|
+
: public ConcreteViewShadowNode<MarkdownTextInputDecoratorViewComponentName,
|
|
18
|
+
MarkdownTextInputDecoratorViewProps,
|
|
19
|
+
MarkdownTextInputDecoratorViewEventEmitter,
|
|
20
|
+
MarkdownTextInputDecoratorState> {
|
|
21
|
+
public:
|
|
22
|
+
MarkdownTextInputDecoratorShadowNode(ShadowNodeFragment const &fragment,
|
|
23
|
+
ShadowNodeFamily::Shared const &family,
|
|
24
|
+
ShadowNodeTraits traits)
|
|
25
|
+
: ConcreteViewShadowNode(fragment, family, traits) {
|
|
26
|
+
initialize();
|
|
27
|
+
createCustomContextContainer();
|
|
28
|
+
|
|
29
|
+
if (fragment.children) {
|
|
30
|
+
overwriteTextLayoutManager();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
MarkdownTextInputDecoratorShadowNode(ShadowNode const &sourceShadowNode,
|
|
35
|
+
ShadowNodeFragment const &fragment)
|
|
36
|
+
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
|
|
37
|
+
initialize();
|
|
38
|
+
|
|
39
|
+
const auto &sourceDecorator = static_cast<const MarkdownTextInputDecoratorShadowNode &>(sourceShadowNode);
|
|
40
|
+
|
|
41
|
+
customContextContainer_ = sourceDecorator.customContextContainer_;
|
|
42
|
+
previousMarkdownStyle_ = sourceDecorator.previousMarkdownStyle_;
|
|
43
|
+
previousParserId_ = sourceDecorator.previousParserId_;
|
|
44
|
+
|
|
45
|
+
updateCustomContextContainerIfNeeded();
|
|
46
|
+
|
|
47
|
+
if (fragment.children) {
|
|
48
|
+
overwriteTextLayoutManager();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void appendChild(const ShadowNode::Shared &child) override;
|
|
53
|
+
|
|
54
|
+
void replaceChild(const ShadowNode &oldChild,
|
|
55
|
+
const ShadowNode::Shared &newChild,
|
|
56
|
+
size_t suggestedIndex = SIZE_MAX) override;
|
|
57
|
+
|
|
58
|
+
void layout(LayoutContext layoutContext) override;
|
|
59
|
+
|
|
60
|
+
private:
|
|
61
|
+
ContextContainer::Shared customContextContainer_;
|
|
62
|
+
folly::dynamic previousMarkdownStyle_;
|
|
63
|
+
int previousParserId_;
|
|
64
|
+
|
|
65
|
+
void initialize();
|
|
66
|
+
|
|
67
|
+
void overwriteTextLayoutManager();
|
|
68
|
+
|
|
69
|
+
void createCustomContextContainer();
|
|
70
|
+
|
|
71
|
+
void updateCustomContextContainerIfNeeded();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
} // namespace react
|
|
75
|
+
} // namespace facebook
|
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
#import "LiveMarkdownModule.h"
|
|
2
2
|
|
|
3
3
|
#import <RNLiveMarkdown/RuntimeDecorator.h>
|
|
4
|
-
#import <RNLiveMarkdown/MarkdownCommitHook.h>
|
|
5
|
-
|
|
6
4
|
#import <React/RCTBridge+Private.h>
|
|
7
|
-
#import <React/RCTScheduler.h>
|
|
8
|
-
#import <React/RCTSurfacePresenter.h>
|
|
9
|
-
|
|
10
5
|
#import <jsi/jsi.h>
|
|
11
6
|
|
|
12
7
|
using namespace facebook;
|
|
13
8
|
using namespace expensify::livemarkdown;
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
// I think this is the easiest way to access the UIManager, which we need to
|
|
17
|
-
// actually register the hook
|
|
18
|
-
|
|
19
|
-
@implementation LiveMarkdownModule {
|
|
20
|
-
std::shared_ptr<MarkdownCommitHook> commitHook_;
|
|
21
|
-
__weak RCTSurfacePresenter *surfacePresenter_;
|
|
22
|
-
}
|
|
10
|
+
@implementation LiveMarkdownModule
|
|
23
11
|
|
|
24
12
|
RCT_EXPORT_MODULE()
|
|
25
13
|
|
|
@@ -28,53 +16,13 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
|
|
|
28
16
|
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
|
|
29
17
|
jsi::Runtime &rt = *(jsi::Runtime *)cxxBridge.runtime;
|
|
30
18
|
expensify::livemarkdown::injectJSIBindings(rt);
|
|
31
|
-
|
|
32
|
-
RCTScheduler *scheduler = [surfacePresenter_ scheduler];
|
|
33
|
-
commitHook_ = std::make_shared<MarkdownCommitHook>(scheduler.uiManager);
|
|
34
|
-
|
|
35
19
|
return @(1);
|
|
36
20
|
}
|
|
37
21
|
|
|
38
|
-
- (void)handleJavaScriptDidLoadNotification:(NSNotification *)notification
|
|
39
|
-
{
|
|
40
|
-
surfacePresenter_ = self.bridge.surfacePresenter;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
- (void)setBridge:(RCTBridge *)bridge
|
|
44
|
-
{
|
|
45
|
-
[super setBridge:bridge];
|
|
46
|
-
|
|
47
|
-
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
48
|
-
selector:@selector(handleJavaScriptDidLoadNotification:)
|
|
49
|
-
name:RCTJavaScriptDidLoadNotification
|
|
50
|
-
object:nil];
|
|
51
|
-
|
|
52
|
-
// only within the first loading `self.bridge.surfacePresenter` exists
|
|
53
|
-
// during the reload `self.bridge.surfacePresenter` is null
|
|
54
|
-
if (self.bridge.surfacePresenter) {
|
|
55
|
-
surfacePresenter_ = self.bridge.surfacePresenter;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/*
|
|
60
|
-
* Taken from RCTNativeAnimatedTurboModule:
|
|
61
|
-
* This selector is invoked via BridgelessTurboModuleSetup.
|
|
62
|
-
*/
|
|
63
|
-
- (void)setSurfacePresenter:(id<RCTSurfacePresenterStub>)surfacePresenter
|
|
64
|
-
{
|
|
65
|
-
surfacePresenter_ = surfacePresenter;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
22
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
69
23
|
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
70
24
|
return std::make_shared<facebook::react::NativeLiveMarkdownModuleSpecJSI>(
|
|
71
25
|
params);
|
|
72
26
|
}
|
|
73
27
|
|
|
74
|
-
- (void)invalidate {
|
|
75
|
-
MarkdownShadowFamilyRegistry::reset();
|
|
76
|
-
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
77
|
-
[super invalidate];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
28
|
@end
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
#import <RNLiveMarkdown/MarkdownBackedTextInputDelegate.h>
|
|
7
7
|
#import <RNLiveMarkdown/MarkdownLayoutManager.h>
|
|
8
|
-
#import <RNLiveMarkdown/MarkdownShadowFamilyRegistry.h>
|
|
9
8
|
#import <RNLiveMarkdown/MarkdownTextInputDecoratorComponentView.h>
|
|
10
9
|
#import <RNLiveMarkdown/MarkdownTextInputDecoratorViewComponentDescriptor.h>
|
|
11
10
|
#import <RNLiveMarkdown/RCTBackedTextFieldDelegateAdapter+Markdown.h>
|
|
@@ -26,7 +25,6 @@ using namespace facebook::react;
|
|
|
26
25
|
__weak UIView<RCTBackedTextInputViewProtocol> *_backedTextInputView;
|
|
27
26
|
__weak RCTBackedTextFieldDelegateAdapter *_adapter;
|
|
28
27
|
__weak RCTUITextView *_textView;
|
|
29
|
-
ShadowNodeFamily::Shared _decoratorFamily;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
@@ -50,44 +48,13 @@ using namespace facebook::react;
|
|
|
50
48
|
return self;
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
- (void)
|
|
51
|
+
- (void)didAddSubview:(UIView *)subview
|
|
54
52
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (_decoratorFamily != nullptr) {
|
|
58
|
-
MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
_decoratorFamily = data.decoratorFamily;
|
|
62
|
-
MarkdownShadowFamilyRegistry::registerFamilyForUpdates(_decoratorFamily);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
|
66
|
-
if (newSuperview == nil) {
|
|
67
|
-
MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
|
|
68
|
-
_decoratorFamily = nullptr;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
[super willMoveToSuperview:newSuperview];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
- (void)didMoveToWindow {
|
|
75
|
-
if (self.superview == nil) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
NSArray *viewsArray = self.superview.subviews;
|
|
80
|
-
NSUInteger currentIndex = [viewsArray indexOfObject:self];
|
|
81
|
-
|
|
82
|
-
react_native_assert(currentIndex != 0 && currentIndex != NSNotFound && "Error while finding current component.");
|
|
83
|
-
UIView *view = [viewsArray objectAtIndex:currentIndex - 1];
|
|
84
|
-
|
|
85
|
-
react_native_assert([view isKindOfClass:[RCTTextInputComponentView class]] && "Previous sibling component is not an instance of RCTTextInputComponentView.");
|
|
86
|
-
_textInput = (RCTTextInputComponentView *)view;
|
|
53
|
+
react_native_assert([subview isKindOfClass:[RCTTextInputComponentView class]] && "Child component of MarkdownTextInputDecoratorComponentView is not an instance of RCTTextInputComponentView.");
|
|
54
|
+
_textInput = (RCTTextInputComponentView *)subview;
|
|
87
55
|
_backedTextInputView = [_textInput valueForKey:@"_backedTextInputView"];
|
|
88
56
|
|
|
89
57
|
_markdownUtils = [[RCTMarkdownUtils alloc] init];
|
|
90
|
-
react_native_assert(_markdownStyle != nil);
|
|
91
58
|
[_markdownUtils setMarkdownStyle:_markdownStyle];
|
|
92
59
|
[_markdownUtils setParserId:_parserId];
|
|
93
60
|
|
|
@@ -121,6 +88,9 @@ using namespace facebook::react;
|
|
|
121
88
|
|
|
122
89
|
- (void)willMoveToWindow:(UIWindow *)newWindow
|
|
123
90
|
{
|
|
91
|
+
if (newWindow != nil) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
124
94
|
if (_textInput != nil) {
|
|
125
95
|
[_textInput setMarkdownUtils:nil];
|
|
126
96
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorState.h>
|
|
4
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/EventEmitters.h>
|
|
5
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/Props.h>
|
|
6
|
+
#include <react/renderer/components/iostextinput/TextInputShadowNode.h>
|
|
7
|
+
|
|
8
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
9
|
+
#include <react/renderer/core/LayoutContext.h>
|
|
10
|
+
|
|
11
|
+
namespace facebook {
|
|
12
|
+
namespace react {
|
|
13
|
+
|
|
14
|
+
JSI_EXPORT extern const char MarkdownTextInputDecoratorViewComponentName[];
|
|
15
|
+
|
|
16
|
+
class JSI_EXPORT MarkdownTextInputDecoratorShadowNode final
|
|
17
|
+
: public ConcreteViewShadowNode<MarkdownTextInputDecoratorViewComponentName,
|
|
18
|
+
MarkdownTextInputDecoratorViewProps,
|
|
19
|
+
MarkdownTextInputDecoratorViewEventEmitter,
|
|
20
|
+
MarkdownTextInputDecoratorState> {
|
|
21
|
+
public:
|
|
22
|
+
MarkdownTextInputDecoratorShadowNode(ShadowNodeFragment const &fragment,
|
|
23
|
+
ShadowNodeFamily::Shared const &family,
|
|
24
|
+
ShadowNodeTraits traits)
|
|
25
|
+
: ConcreteViewShadowNode(fragment, family, traits) {
|
|
26
|
+
initialize();
|
|
27
|
+
|
|
28
|
+
if (fragment.children) {
|
|
29
|
+
overwriteMeasureCallbackConnector();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
MarkdownTextInputDecoratorShadowNode(ShadowNode const &sourceShadowNode,
|
|
34
|
+
ShadowNodeFragment const &fragment)
|
|
35
|
+
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
|
|
36
|
+
initialize();
|
|
37
|
+
|
|
38
|
+
if (fragment.children) {
|
|
39
|
+
overwriteMeasureCallbackConnector();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void appendChild(const ShadowNode::Shared &child) override;
|
|
44
|
+
void replaceChild(const ShadowNode &oldChild,
|
|
45
|
+
const ShadowNode::Shared &newChild,
|
|
46
|
+
size_t suggestedIndex = SIZE_MAX) override;
|
|
47
|
+
void layout(LayoutContext layoutContext) override;
|
|
48
|
+
Size
|
|
49
|
+
measureContent(const LayoutContext &layoutContext,
|
|
50
|
+
const LayoutConstraints &layoutConstraints) const override;
|
|
51
|
+
|
|
52
|
+
private:
|
|
53
|
+
void initialize();
|
|
54
|
+
void overwriteMeasureCallbackConnector();
|
|
55
|
+
void applyMarkdownFormattingToTextInputState(std::shared_ptr<TextInputShadowNode> node,
|
|
56
|
+
const LayoutContext &layoutContext) const;
|
|
57
|
+
static YGSize yogaNodeMeasureCallbackConnector(YGNodeConstRef yogaNode,
|
|
58
|
+
float width,
|
|
59
|
+
YGMeasureMode widthMode,
|
|
60
|
+
float height,
|
|
61
|
+
YGMeasureMode heightMode);
|
|
62
|
+
static YogaLayoutableShadowNode &
|
|
63
|
+
shadowNodeFromContext(YGNodeConstRef yogaNode);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
} // namespace react
|
|
67
|
+
} // namespace facebook
|