@expensify/react-native-live-markdown 0.1.44 → 0.1.45
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 +8 -1
- package/ios/MarkdownCommitHook.h +42 -0
- package/ios/MarkdownCommitHook.mm +195 -0
- package/ios/MarkdownShadowFamilyRegistry.cpp +58 -0
- package/ios/MarkdownShadowFamilyRegistry.h +35 -0
- package/ios/MarkdownTextInputDecoratorComponentDescriptor.h +17 -0
- package/ios/MarkdownTextInputDecoratorComponentView.mm +25 -2
- package/ios/MarkdownTextInputDecoratorShadowNode.cpp +38 -0
- package/ios/MarkdownTextInputDecoratorShadowNode.h +49 -0
- package/ios/MarkdownTextInputDecoratorState.h +21 -0
- package/ios/MarkdownTextInputDecoratorView.mm +5 -1
- package/ios/RCTBackedTextFieldDelegateAdapter+Markdown.m +1 -1
- package/ios/RCTBaseTextInputView+Markdown.m +2 -2
- package/ios/RCTLiveMarkdownModule.h +12 -0
- package/ios/RCTLiveMarkdownModule.mm +48 -0
- package/ios/RCTMarkdownUtils.h +1 -3
- package/ios/RCTMarkdownUtils.mm +129 -136
- package/ios/RCTTextInputComponentView+Markdown.mm +18 -4
- package/ios/RCTUITextView+Markdown.mm +1 -1
- package/lib/commonjs/MarkdownTextInput.js +4 -0
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/commonjs/NativeLiveMarkdownModule.js +9 -0
- package/lib/commonjs/NativeLiveMarkdownModule.js.map +1 -0
- package/lib/module/MarkdownTextInput.js +4 -0
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/module/NativeLiveMarkdownModule.js +3 -0
- package/lib/module/NativeLiveMarkdownModule.js.map +1 -0
- package/lib/typescript/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/NativeLiveMarkdownModule.d.ts +7 -0
- package/lib/typescript/src/NativeLiveMarkdownModule.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/MarkdownTextInput.tsx +5 -0
- package/src/NativeLiveMarkdownModule.ts +8 -0
package/RNLiveMarkdown.podspec
CHANGED
|
@@ -14,9 +14,16 @@ 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,cpp}"
|
|
18
18
|
|
|
19
19
|
s.resources = "parser/react-native-live-markdown-parser.js"
|
|
20
20
|
|
|
21
21
|
install_modules_dependencies(s)
|
|
22
|
+
|
|
23
|
+
if ENV['USE_FRAMEWORKS'] && ENV['RCT_NEW_ARCH_ENABLED']
|
|
24
|
+
add_dependency(s, "React-Fabric", :additional_framework_paths => [
|
|
25
|
+
"react/renderer/textlayoutmanager/platform/ios",
|
|
26
|
+
"react/renderer/components/textinput/iostextinput",
|
|
27
|
+
])
|
|
28
|
+
end
|
|
22
29
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
|
+
|
|
4
|
+
#include <react/renderer/components/iostextinput/TextInputShadowNode.h>
|
|
5
|
+
#include <react/renderer/uimanager/UIManager.h>
|
|
6
|
+
#include <react/renderer/uimanager/UIManagerCommitHook.h>
|
|
7
|
+
|
|
8
|
+
#include <memory>
|
|
9
|
+
|
|
10
|
+
#include "MarkdownTextInputDecoratorShadowNode.h"
|
|
11
|
+
|
|
12
|
+
using namespace facebook::react;
|
|
13
|
+
|
|
14
|
+
namespace livemarkdown {
|
|
15
|
+
|
|
16
|
+
struct MarkdownTextInputDecoratorPair {
|
|
17
|
+
const std::shared_ptr<const TextInputShadowNode> textInput;
|
|
18
|
+
const std::shared_ptr<const MarkdownTextInputDecoratorShadowNode> decorator;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
class MarkdownCommitHook : public UIManagerCommitHook {
|
|
22
|
+
public:
|
|
23
|
+
MarkdownCommitHook(const std::shared_ptr<UIManager> &uiManager);
|
|
24
|
+
|
|
25
|
+
~MarkdownCommitHook() noexcept override;
|
|
26
|
+
|
|
27
|
+
void commitHookWasRegistered(UIManager const &) noexcept override {}
|
|
28
|
+
|
|
29
|
+
void commitHookWasUnregistered(UIManager const &) noexcept override {}
|
|
30
|
+
|
|
31
|
+
RootShadowNode::Unshared shadowTreeWillCommit(
|
|
32
|
+
ShadowTree const &shadowTree,
|
|
33
|
+
RootShadowNode::Shared const &oldRootShadowNode,
|
|
34
|
+
RootShadowNode::Unshared const &newRootShadowNode) noexcept override;
|
|
35
|
+
|
|
36
|
+
private:
|
|
37
|
+
const std::shared_ptr<UIManager> uiManager_;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace livemarkdown
|
|
41
|
+
|
|
42
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
|
|
3
|
+
#include <React/RCTUtils.h>
|
|
4
|
+
#include <react/renderer/core/ComponentDescriptor.h>
|
|
5
|
+
#include <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
|
|
6
|
+
|
|
7
|
+
#include "MarkdownCommitHook.h"
|
|
8
|
+
#include "MarkdownShadowFamilyRegistry.h"
|
|
9
|
+
#include "RCTMarkdownStyle.h"
|
|
10
|
+
#include "RCTMarkdownUtils.h"
|
|
11
|
+
|
|
12
|
+
using namespace facebook::react;
|
|
13
|
+
|
|
14
|
+
namespace livemarkdown {
|
|
15
|
+
|
|
16
|
+
MarkdownCommitHook::MarkdownCommitHook(
|
|
17
|
+
const std::shared_ptr<UIManager> &uiManager)
|
|
18
|
+
: uiManager_(uiManager) {
|
|
19
|
+
uiManager_->registerCommitHook(*this);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
MarkdownCommitHook::~MarkdownCommitHook() noexcept {
|
|
23
|
+
uiManager_->unregisterCommitHook(*this);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
RootShadowNode::Unshared MarkdownCommitHook::shadowTreeWillCommit(
|
|
27
|
+
ShadowTree const &, RootShadowNode::Shared const &,
|
|
28
|
+
RootShadowNode::Unshared const &newRootShadowNode) noexcept {
|
|
29
|
+
auto rootNode = newRootShadowNode->ShadowNode::clone(ShadowNodeFragment{});
|
|
30
|
+
|
|
31
|
+
// A preface to why we do the weird thing below:
|
|
32
|
+
// On the new architecture there are two ways of measuring text on iOS: by
|
|
33
|
+
// value and by pointer. When done by value, the attributed string to be
|
|
34
|
+
// measured is created on the c++ side. We cannot modify this process as we do
|
|
35
|
+
// not extend TextInputShadowNode. We also cannot really change the layout
|
|
36
|
+
// manager used to do this, since it's a private field (ok, we can but in a
|
|
37
|
+
// not very nice way). But also, the logic for parsing and applying markdown
|
|
38
|
+
// is written in JS/ObjC and we really wouldn't want to reimplement it in c++.
|
|
39
|
+
//
|
|
40
|
+
// Nice thing is that it can also be done by pointer to NSAttributedString,
|
|
41
|
+
// which is the platform's way to handle styled text, and is also used by Live
|
|
42
|
+
// Markdown. On this path, the measurement is done by the OS APIs. The thing
|
|
43
|
+
// we want to make sure of, is that markdown-decorated text input always uses
|
|
44
|
+
// this path and uses a pointer to a string with markdown styles applied.
|
|
45
|
+
// Thankfully, RN provides nice utility functions that allow to convert
|
|
46
|
+
// between the RN's AttributedString and iOS's NSAttributedString. The logic
|
|
47
|
+
// below does exactly that.
|
|
48
|
+
|
|
49
|
+
// In order to properly apply markdown formatting to the text input, we need
|
|
50
|
+
// to update the TextInputShadowNode's state with styled string, but we only
|
|
51
|
+
// have access to the ShadowNodeFamilies of the decorator components. We also
|
|
52
|
+
// know that a markdown decorator is always preceded with the TextInput to
|
|
53
|
+
// decorate, so we need to take the sibling.
|
|
54
|
+
std::vector<MarkdownTextInputDecoratorPair> nodesToUpdate;
|
|
55
|
+
MarkdownShadowFamilyRegistry::runForEveryFamily([&rootNode, &nodesToUpdate](
|
|
56
|
+
ShadowNodeFamily::Shared
|
|
57
|
+
family) {
|
|
58
|
+
// get the path from the root to the node from the decorator family
|
|
59
|
+
const auto ancestors = family->getAncestors(*rootNode);
|
|
60
|
+
|
|
61
|
+
if (!ancestors.empty()) {
|
|
62
|
+
auto &parentNode = ancestors.back().first.get();
|
|
63
|
+
auto index = ancestors.back().second;
|
|
64
|
+
|
|
65
|
+
// this is node represented by one of the registered families and since we
|
|
66
|
+
// only register markdown decorator shadow families, static casting should
|
|
67
|
+
// be safe here
|
|
68
|
+
const auto &decoratorNode =
|
|
69
|
+
std::static_pointer_cast<const MarkdownTextInputDecoratorShadowNode>(
|
|
70
|
+
parentNode.getChildren().at(index));
|
|
71
|
+
// text input always precedes the decorator component
|
|
72
|
+
const auto &previousSibling = parentNode.getChildren().at(index - 1);
|
|
73
|
+
|
|
74
|
+
if (const auto &textInputNode =
|
|
75
|
+
std::dynamic_pointer_cast<const TextInputShadowNode>(
|
|
76
|
+
previousSibling)) {
|
|
77
|
+
// store the pair of text input and decorator to update in the next step
|
|
78
|
+
// we need both, decorator to get markdown style and text input to
|
|
79
|
+
// update it
|
|
80
|
+
nodesToUpdate.push_back({
|
|
81
|
+
textInputNode,
|
|
82
|
+
decoratorNode,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
for (const auto &nodes : nodesToUpdate) {
|
|
89
|
+
const auto &textInputState =
|
|
90
|
+
*std::static_pointer_cast<const ConcreteState<TextInputState>>(
|
|
91
|
+
nodes.textInput->getState());
|
|
92
|
+
const auto &stateData = textInputState.getData();
|
|
93
|
+
const auto fontSizeMultiplier =
|
|
94
|
+
newRootShadowNode->getConcreteProps().layoutContext.fontSizeMultiplier;
|
|
95
|
+
|
|
96
|
+
// We only want to update the shadow node when the attributed string is
|
|
97
|
+
// stored by value If it's stored by pointer, the markdown formatting should
|
|
98
|
+
// already by applied to it, since the only source of that pointer (besides
|
|
99
|
+
// this commit hook) is RCTTextInputComponentView, which has the relevant
|
|
100
|
+
// method swizzled to make sure the markdown styles are always applied
|
|
101
|
+
// before updating state. There are two caveats:
|
|
102
|
+
// 1. On the first render the swizzled method will not apply markdown since
|
|
103
|
+
// the native component
|
|
104
|
+
// is not mounted yet. In that case we save the tag to update in the
|
|
105
|
+
// method applying markdown formatting and apply it here instead,
|
|
106
|
+
// preventing wrong layout on reloads.
|
|
107
|
+
// 2. When the markdown style prop is changed, the native state needs to be
|
|
108
|
+
// updated to reflect
|
|
109
|
+
// them. In that case the relevant tag is saved in the registry when the
|
|
110
|
+
// new shadow node is created.
|
|
111
|
+
if (stateData.attributedStringBox.getMode() ==
|
|
112
|
+
AttributedStringBox::Mode::Value ||
|
|
113
|
+
MarkdownShadowFamilyRegistry::shouldForceUpdate(
|
|
114
|
+
nodes.textInput->getTag())) {
|
|
115
|
+
rootNode = rootNode->cloneTree(
|
|
116
|
+
nodes.textInput->getFamily(),
|
|
117
|
+
[&nodes, &textInputState, &stateData,
|
|
118
|
+
fontSizeMultiplier](const ShadowNode &node) {
|
|
119
|
+
const auto &markdownProps = *std::static_pointer_cast<
|
|
120
|
+
MarkdownTextInputDecoratorViewProps const>(
|
|
121
|
+
nodes.decorator->getProps());
|
|
122
|
+
const auto &textInputProps =
|
|
123
|
+
*std::static_pointer_cast<TextInputProps const>(
|
|
124
|
+
nodes.textInput->getProps());
|
|
125
|
+
|
|
126
|
+
const auto defaultTextAttributes =
|
|
127
|
+
textInputProps.getEffectiveTextAttributes(fontSizeMultiplier);
|
|
128
|
+
const auto defaultNSTextAttributes =
|
|
129
|
+
RCTNSTextAttributesFromTextAttributes(defaultTextAttributes);
|
|
130
|
+
|
|
131
|
+
// this can possibly be optimized
|
|
132
|
+
RCTMarkdownStyle *markdownStyle = [[RCTMarkdownStyle alloc]
|
|
133
|
+
initWithStruct:markdownProps.markdownStyle];
|
|
134
|
+
RCTMarkdownUtils *utils = [[RCTMarkdownUtils alloc] init];
|
|
135
|
+
[utils setMarkdownStyle:markdownStyle];
|
|
136
|
+
|
|
137
|
+
// convert the attibuted string stored in state to
|
|
138
|
+
// NSAttributedString
|
|
139
|
+
auto nsAttributedString =
|
|
140
|
+
RCTNSAttributedStringFromAttributedStringBox(
|
|
141
|
+
stateData.attributedStringBox);
|
|
142
|
+
|
|
143
|
+
// Handles the first render, where the text stored in props is
|
|
144
|
+
// different than the one stored in state The one in state is empty,
|
|
145
|
+
// while the one in props is passed from JS If we don't update the
|
|
146
|
+
// state here, we'll end up with a one-default-line-sized text
|
|
147
|
+
// input. A better condition to do that can be probably chosen, but
|
|
148
|
+
// this seems to work
|
|
149
|
+
auto plainString =
|
|
150
|
+
std::string([[nsAttributedString string] UTF8String]);
|
|
151
|
+
if (plainString != textInputProps.text) {
|
|
152
|
+
// creates new AttributedString from props, adapted from
|
|
153
|
+
// TextInputShadowNode (ios one, text inputs are
|
|
154
|
+
// platform-specific)
|
|
155
|
+
auto attributedString = AttributedString{};
|
|
156
|
+
attributedString.appendFragment(AttributedString::Fragment{
|
|
157
|
+
textInputProps.text, defaultTextAttributes});
|
|
158
|
+
|
|
159
|
+
auto attachments = BaseTextShadowNode::Attachments{};
|
|
160
|
+
BaseTextShadowNode::buildAttributedString(
|
|
161
|
+
defaultTextAttributes, *nodes.textInput, attributedString,
|
|
162
|
+
attachments);
|
|
163
|
+
|
|
164
|
+
// convert the newly created attributed string to
|
|
165
|
+
// NSAttributedString
|
|
166
|
+
nsAttributedString = RCTNSAttributedStringFromAttributedStringBox(
|
|
167
|
+
AttributedStringBox{attributedString});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// apply markdown
|
|
171
|
+
auto newString = [utils parseMarkdown:nsAttributedString
|
|
172
|
+
withAttributes:defaultNSTextAttributes];
|
|
173
|
+
|
|
174
|
+
// create a clone of the old TextInputState and update the
|
|
175
|
+
// attributed string box to point to the string with markdown
|
|
176
|
+
// applied
|
|
177
|
+
auto newStateData = std::make_shared<TextInputState>(stateData);
|
|
178
|
+
newStateData->attributedStringBox =
|
|
179
|
+
RCTAttributedStringBoxFromNSAttributedString(newString);
|
|
180
|
+
|
|
181
|
+
// clone the text input with the new state
|
|
182
|
+
return node.clone({
|
|
183
|
+
.state = std::make_shared<const ConcreteState<TextInputState>>(
|
|
184
|
+
newStateData, textInputState),
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return std::static_pointer_cast<RootShadowNode>(rootNode);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
} // namespace livemarkdown
|
|
194
|
+
|
|
195
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
|
|
3
|
+
#include "MarkdownShadowFamilyRegistry.h"
|
|
4
|
+
|
|
5
|
+
std::set<facebook::react::ShadowNodeFamily::Shared>
|
|
6
|
+
MarkdownShadowFamilyRegistry::familiesToUpdate_;
|
|
7
|
+
std::set<facebook::react::Tag> MarkdownShadowFamilyRegistry::forcedUpdates_;
|
|
8
|
+
std::mutex MarkdownShadowFamilyRegistry::mutex_;
|
|
9
|
+
|
|
10
|
+
void MarkdownShadowFamilyRegistry::registerFamilyForUpdates(
|
|
11
|
+
facebook::react::ShadowNodeFamily::Shared family) {
|
|
12
|
+
auto lock =
|
|
13
|
+
std::unique_lock<std::mutex>(MarkdownShadowFamilyRegistry::mutex_);
|
|
14
|
+
MarkdownShadowFamilyRegistry::familiesToUpdate_.insert(family);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
void MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(
|
|
18
|
+
facebook::react::ShadowNodeFamily::Shared family) {
|
|
19
|
+
auto lock =
|
|
20
|
+
std::unique_lock<std::mutex>(MarkdownShadowFamilyRegistry::mutex_);
|
|
21
|
+
MarkdownShadowFamilyRegistry::familiesToUpdate_.erase(family);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void MarkdownShadowFamilyRegistry::reset() {
|
|
25
|
+
auto lock =
|
|
26
|
+
std::unique_lock<std::mutex>(MarkdownShadowFamilyRegistry::mutex_);
|
|
27
|
+
MarkdownShadowFamilyRegistry::familiesToUpdate_.clear();
|
|
28
|
+
MarkdownShadowFamilyRegistry::forcedUpdates_.clear();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
void MarkdownShadowFamilyRegistry::runForEveryFamily(
|
|
32
|
+
std::function<void(facebook::react::ShadowNodeFamily::Shared)> fun) {
|
|
33
|
+
auto lock =
|
|
34
|
+
std::unique_lock<std::mutex>(MarkdownShadowFamilyRegistry::mutex_);
|
|
35
|
+
for (auto &family : MarkdownShadowFamilyRegistry::familiesToUpdate_) {
|
|
36
|
+
fun(family);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void MarkdownShadowFamilyRegistry::forceNextStateUpdate(
|
|
41
|
+
facebook::react::Tag tag) {
|
|
42
|
+
auto lock =
|
|
43
|
+
std::unique_lock<std::mutex>(MarkdownShadowFamilyRegistry::mutex_);
|
|
44
|
+
forcedUpdates_.insert(tag);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
bool MarkdownShadowFamilyRegistry::shouldForceUpdate(facebook::react::Tag tag) {
|
|
48
|
+
auto lock =
|
|
49
|
+
std::unique_lock<std::mutex>(MarkdownShadowFamilyRegistry::mutex_);
|
|
50
|
+
bool force = forcedUpdates_.contains(tag);
|
|
51
|
+
if (force) {
|
|
52
|
+
forcedUpdates_.erase(tag);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#endif
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
|
+
|
|
4
|
+
#include <react/renderer/components/iostextinput/TextInputShadowNode.h>
|
|
5
|
+
|
|
6
|
+
#include <mutex>
|
|
7
|
+
#include <set>
|
|
8
|
+
|
|
9
|
+
// A registry to store pointers to the ShadowNodeFamilies of markdown
|
|
10
|
+
// decorators. The only place we can _legally_ access the family of shadow node
|
|
11
|
+
// is in the constructor and we need it inside commit hook. To achieve it, we
|
|
12
|
+
// use this simple registry where families are registered when nodes are created
|
|
13
|
+
// and cleaned up when native view is removed from window or when a turbomodule
|
|
14
|
+
// is deallocated.
|
|
15
|
+
|
|
16
|
+
class MarkdownShadowFamilyRegistry {
|
|
17
|
+
public:
|
|
18
|
+
static void
|
|
19
|
+
registerFamilyForUpdates(facebook::react::ShadowNodeFamily::Shared family);
|
|
20
|
+
static void
|
|
21
|
+
unregisterFamilyForUpdates(facebook::react::ShadowNodeFamily::Shared family);
|
|
22
|
+
static void reset();
|
|
23
|
+
static void runForEveryFamily(
|
|
24
|
+
std::function<void(facebook::react::ShadowNodeFamily::Shared family)>
|
|
25
|
+
fun);
|
|
26
|
+
static void forceNextStateUpdate(facebook::react::Tag tag);
|
|
27
|
+
static bool shouldForceUpdate(facebook::react::Tag tag);
|
|
28
|
+
|
|
29
|
+
private:
|
|
30
|
+
static std::set<facebook::react::ShadowNodeFamily::Shared> familiesToUpdate_;
|
|
31
|
+
static std::set<facebook::react::Tag> forcedUpdates_;
|
|
32
|
+
static std::mutex mutex_;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
#endif
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "MarkdownTextInputDecoratorShadowNode.h"
|
|
4
|
+
#include <react/debug/react_native_assert.h>
|
|
5
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
6
|
+
|
|
7
|
+
namespace facebook {
|
|
8
|
+
namespace react {
|
|
9
|
+
|
|
10
|
+
class MarkdownTextInputDecoratorComponentDescriptor final
|
|
11
|
+
: public ConcreteComponentDescriptor<MarkdownTextInputDecoratorShadowNode> {
|
|
12
|
+
public:
|
|
13
|
+
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
} // namespace react
|
|
17
|
+
} // namespace facebook
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
// This guard prevent this file to be compiled in the old architecture.
|
|
2
2
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
|
-
#import <react/renderer/components/RNLiveMarkdownSpec/ComponentDescriptors.h>
|
|
4
3
|
#import <react/renderer/components/RNLiveMarkdownSpec/Props.h>
|
|
5
4
|
|
|
6
5
|
#import <RNLiveMarkdown/MarkdownTextInputDecoratorComponentView.h>
|
|
7
6
|
#import <RNLiveMarkdown/MarkdownTextInputDecoratorView.h>
|
|
8
7
|
#import <RNLiveMarkdown/RCTMarkdownStyle.h>
|
|
9
8
|
|
|
9
|
+
#import "MarkdownTextInputDecoratorComponentDescriptor.h"
|
|
10
|
+
#import "MarkdownShadowFamilyRegistry.h"
|
|
10
11
|
#import "RCTFabricComponentsPlugins.h"
|
|
11
12
|
|
|
12
13
|
using namespace facebook::react;
|
|
13
14
|
|
|
14
15
|
@implementation MarkdownTextInputDecoratorComponentView {
|
|
15
16
|
MarkdownTextInputDecoratorView *_view;
|
|
17
|
+
ShadowNodeFamily::Shared _decoratorFamily;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
19
21
|
{
|
|
20
|
-
return concreteComponentDescriptorProvider<
|
|
22
|
+
return concreteComponentDescriptorProvider<MarkdownTextInputDecoratorComponentDescriptor>();
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
@@ -34,6 +36,27 @@ using namespace facebook::react;
|
|
|
34
36
|
return self;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
- (void)updateState:(const facebook::react::State::Shared &)state oldState:(const facebook::react::State::Shared &)oldState
|
|
40
|
+
{
|
|
41
|
+
auto data = std::static_pointer_cast<MarkdownTextInputDecoratorShadowNode::ConcreteState const>(state)->getData();
|
|
42
|
+
|
|
43
|
+
if (_decoratorFamily != nullptr) {
|
|
44
|
+
MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_decoratorFamily = data.decoratorFamily;
|
|
48
|
+
MarkdownShadowFamilyRegistry::registerFamilyForUpdates(_decoratorFamily);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
|
52
|
+
if (newSuperview == nil) {
|
|
53
|
+
MarkdownShadowFamilyRegistry::unregisterFamilyForUpdates(_decoratorFamily);
|
|
54
|
+
_decoratorFamily = nullptr;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
[super willMoveToSuperview:newSuperview];
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
38
61
|
{
|
|
39
62
|
const auto &oldViewProps = *std::static_pointer_cast<MarkdownTextInputDecoratorViewProps const>(_props);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/LayoutContext.h>
|
|
4
|
+
|
|
5
|
+
#include "MarkdownShadowFamilyRegistry.h"
|
|
6
|
+
#include "MarkdownTextInputDecoratorShadowNode.h"
|
|
7
|
+
|
|
8
|
+
namespace facebook {
|
|
9
|
+
namespace react {
|
|
10
|
+
|
|
11
|
+
extern const char MarkdownTextInputDecoratorViewComponentName[] =
|
|
12
|
+
"MarkdownTextInputDecoratorView";
|
|
13
|
+
|
|
14
|
+
const ShadowNodeFragment::Value
|
|
15
|
+
MarkdownTextInputDecoratorShadowNode::updateFragmentState(
|
|
16
|
+
ShadowNodeFragment const &fragment,
|
|
17
|
+
ShadowNodeFamily::Shared const &family) {
|
|
18
|
+
const auto newStateData =
|
|
19
|
+
std::make_shared<MarkdownTextInputDecoratorState>(family);
|
|
20
|
+
|
|
21
|
+
MarkdownShadowFamilyRegistry::registerFamilyForUpdates(family);
|
|
22
|
+
|
|
23
|
+
// we pass the pointer to the ShadowNodeFamily in the initial state, so it's
|
|
24
|
+
// propagated on every clone we need it to clear the reference in the registry
|
|
25
|
+
// when the view is removed from window it cannot be done in the destructor,
|
|
26
|
+
// as multiple shadow nodes for the same family may be created
|
|
27
|
+
return ShadowNodeFragment::Value({
|
|
28
|
+
.props = fragment.props,
|
|
29
|
+
.children = fragment.children,
|
|
30
|
+
.state =
|
|
31
|
+
std::make_shared<const ConcreteState>(newStateData, *fragment.state),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
} // namespace react
|
|
36
|
+
} // namespace facebook
|
|
37
|
+
|
|
38
|
+
#endif
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
|
+
|
|
4
|
+
#include "MarkdownShadowFamilyRegistry.h"
|
|
5
|
+
#include "MarkdownTextInputDecoratorState.h"
|
|
6
|
+
#include <jsi/jsi.h>
|
|
7
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/EventEmitters.h>
|
|
8
|
+
#include <react/renderer/components/RNLiveMarkdownSpec/Props.h>
|
|
9
|
+
#include <react/renderer/components/iostextinput/TextInputShadowNode.h>
|
|
10
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
11
|
+
|
|
12
|
+
namespace facebook {
|
|
13
|
+
namespace react {
|
|
14
|
+
|
|
15
|
+
JSI_EXPORT extern const char MarkdownTextInputDecoratorViewComponentName[];
|
|
16
|
+
|
|
17
|
+
class JSI_EXPORT MarkdownTextInputDecoratorShadowNode final
|
|
18
|
+
: public ConcreteViewShadowNode<MarkdownTextInputDecoratorViewComponentName,
|
|
19
|
+
MarkdownTextInputDecoratorViewProps,
|
|
20
|
+
MarkdownTextInputDecoratorViewEventEmitter,
|
|
21
|
+
MarkdownTextInputDecoratorState> {
|
|
22
|
+
public:
|
|
23
|
+
MarkdownTextInputDecoratorShadowNode(ShadowNodeFragment const &fragment,
|
|
24
|
+
ShadowNodeFamily::Shared const &family,
|
|
25
|
+
ShadowNodeTraits traits)
|
|
26
|
+
: ConcreteViewShadowNode(static_cast<ShadowNodeFragment>(
|
|
27
|
+
updateFragmentState(fragment, family)),
|
|
28
|
+
family, traits) {}
|
|
29
|
+
|
|
30
|
+
MarkdownTextInputDecoratorShadowNode(ShadowNode const &sourceShadowNode,
|
|
31
|
+
ShadowNodeFragment const &fragment)
|
|
32
|
+
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
|
|
33
|
+
// if the props changed, we need to update the shadow node state to reflect
|
|
34
|
+
// potential style changes
|
|
35
|
+
if (fragment.props != ShadowNodeFragment::propsPlaceholder()) {
|
|
36
|
+
MarkdownShadowFamilyRegistry::forceNextStateUpdate(this->getTag());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
static const ShadowNodeFragment::Value
|
|
42
|
+
updateFragmentState(ShadowNodeFragment const &fragment,
|
|
43
|
+
ShadowNodeFamily::Shared const &family);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
} // namespace react
|
|
47
|
+
} // namespace facebook
|
|
48
|
+
|
|
49
|
+
#endif
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
4
|
+
|
|
5
|
+
namespace facebook {
|
|
6
|
+
namespace react {
|
|
7
|
+
|
|
8
|
+
class JSI_EXPORT MarkdownTextInputDecoratorState final {
|
|
9
|
+
public:
|
|
10
|
+
using Shared = std::shared_ptr<const MarkdownTextInputDecoratorState>;
|
|
11
|
+
|
|
12
|
+
MarkdownTextInputDecoratorState() : decoratorFamily(nullptr){};
|
|
13
|
+
MarkdownTextInputDecoratorState(
|
|
14
|
+
const ShadowNodeFamily::Shared textInputFamily_)
|
|
15
|
+
: decoratorFamily(textInputFamily_){};
|
|
16
|
+
|
|
17
|
+
const ShadowNodeFamily::Shared decoratorFamily;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
} // namespace react
|
|
21
|
+
} // namespace facebook
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
UIView<RCTBackedTextInputViewProtocol> *backedTextInputView = _textInput.backedTextInputView;
|
|
59
59
|
#endif /* RCT_NEW_ARCH_ENABLED */
|
|
60
60
|
|
|
61
|
-
_markdownUtils = [[RCTMarkdownUtils alloc]
|
|
61
|
+
_markdownUtils = [[RCTMarkdownUtils alloc] init];
|
|
62
62
|
react_native_assert(_markdownStyle != nil);
|
|
63
63
|
[_markdownUtils setMarkdownStyle:_markdownStyle];
|
|
64
64
|
|
|
@@ -100,7 +100,11 @@
|
|
|
100
100
|
{
|
|
101
101
|
_markdownStyle = markdownStyle;
|
|
102
102
|
[_markdownUtils setMarkdownStyle:markdownStyle];
|
|
103
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
104
|
+
[_textInput textInputDidChange]; // apply new styles
|
|
105
|
+
#else
|
|
103
106
|
[_textInput setAttributedText:_textInput.attributedText]; // apply new styles
|
|
107
|
+
#endif
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
@end
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
if (markdownUtils != nil) {
|
|
20
20
|
RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"];
|
|
21
21
|
UITextRange *range = backedTextInputView.selectedTextRange;
|
|
22
|
-
backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText];
|
|
22
|
+
backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText withAttributes:backedTextInputView.defaultTextAttributes];
|
|
23
23
|
[backedTextInputView setSelectedTextRange:range notifyDelegate:YES];
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils];
|
|
18
18
|
if (markdownUtils != nil) {
|
|
19
|
-
attributedText = [markdownUtils parseMarkdown:attributedText];
|
|
19
|
+
attributedText = [markdownUtils parseMarkdown:attributedText withAttributes:self.backedTextInputView.defaultTextAttributes];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// Call the original method
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
if (markdownUtils != nil) {
|
|
30
30
|
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
|
|
31
31
|
NSAttributedString *oldAttributedText = backedTextInputView.attributedText;
|
|
32
|
-
NSAttributedString *newAttributedText = [markdownUtils parseMarkdown:oldAttributedText];
|
|
32
|
+
NSAttributedString *newAttributedText = [markdownUtils parseMarkdown:oldAttributedText withAttributes:backedTextInputView.defaultTextAttributes];
|
|
33
33
|
UITextRange *range = backedTextInputView.selectedTextRange;
|
|
34
34
|
|
|
35
35
|
// update attributed text without emitting onSelectionChange event
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
|
|
3
|
+
#import <RNLiveMarkdownSpec/RNLiveMarkdownSpec.h>
|
|
4
|
+
#import <React/RCTEventEmitter.h>
|
|
5
|
+
#import <React/RCTUIManager.h>
|
|
6
|
+
|
|
7
|
+
// Without inheriting after RCTEventEmitter we don't get access to bridge
|
|
8
|
+
@interface RCTLiveMarkdownModule
|
|
9
|
+
: RCTEventEmitter <NativeLiveMarkdownModuleSpec>
|
|
10
|
+
@end
|
|
11
|
+
|
|
12
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
|
|
3
|
+
#import <React/RCTScheduler.h>
|
|
4
|
+
#import <React/RCTSurfacePresenter.h>
|
|
5
|
+
|
|
6
|
+
#import "MarkdownCommitHook.h"
|
|
7
|
+
#import "MarkdownShadowFamilyRegistry.h"
|
|
8
|
+
#import "RCTLiveMarkdownModule.h"
|
|
9
|
+
|
|
10
|
+
// A turbomodule used to register the commit hook
|
|
11
|
+
// I think this is the easiest way to access the UIManager, which we need to
|
|
12
|
+
// actually register the hook
|
|
13
|
+
|
|
14
|
+
@implementation RCTLiveMarkdownModule {
|
|
15
|
+
BOOL installed_;
|
|
16
|
+
std::shared_ptr<livemarkdown::MarkdownCommitHook> commitHook_;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
RCT_EXPORT_MODULE(@"LiveMarkdownModule")
|
|
20
|
+
|
|
21
|
+
- (NSNumber *)install {
|
|
22
|
+
if (!installed_) {
|
|
23
|
+
installed_ = YES;
|
|
24
|
+
|
|
25
|
+
RCTBridge *bridge = self.bridge;
|
|
26
|
+
RCTSurfacePresenter *surfacePresenter = bridge.surfacePresenter;
|
|
27
|
+
RCTScheduler *scheduler = [surfacePresenter scheduler];
|
|
28
|
+
|
|
29
|
+
commitHook_ =
|
|
30
|
+
std::make_shared<livemarkdown::MarkdownCommitHook>(scheduler.uiManager);
|
|
31
|
+
}
|
|
32
|
+
return @1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
36
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
37
|
+
return std::make_shared<facebook::react::NativeLiveMarkdownModuleSpecJSI>(
|
|
38
|
+
params);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
- (void)invalidate {
|
|
42
|
+
MarkdownShadowFamilyRegistry::reset();
|
|
43
|
+
[super invalidate];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@end
|
|
47
|
+
|
|
48
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
package/ios/RCTMarkdownUtils.h
CHANGED
|
@@ -9,9 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
9
9
|
@property (nonatomic) NSMutableArray<NSDictionary *> *blockquoteRangesAndLevels;
|
|
10
10
|
@property (weak, nonatomic) UIView<RCTBackedTextInputViewProtocol> *backedTextInputView;
|
|
11
11
|
|
|
12
|
-
- (
|
|
13
|
-
|
|
14
|
-
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input;
|
|
12
|
+
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey, id>*)attributes;
|
|
15
13
|
|
|
16
14
|
@end
|
|
17
15
|
|
package/ios/RCTMarkdownUtils.mm
CHANGED
|
@@ -11,145 +11,138 @@
|
|
|
11
11
|
__weak RCTMarkdownStyle *_prevMarkdownStyle;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
- (
|
|
14
|
+
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes
|
|
15
15
|
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
16
|
+
@synchronized (self) {
|
|
17
|
+
if (input == nil) {
|
|
18
|
+
return nil;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
NSString *inputString = [input string];
|
|
22
|
+
if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle]) {
|
|
23
|
+
return _prevAttributedString;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static JSContext *ctx = nil;
|
|
27
|
+
static JSValue *function = nil;
|
|
28
|
+
if (ctx == nil) {
|
|
29
|
+
NSString *path = [[NSBundle mainBundle] pathForResource:@"react-native-live-markdown-parser" ofType:@"js"];
|
|
30
|
+
assert(path != nil && "[react-native-live-markdown] Markdown parser bundle not found");
|
|
31
|
+
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
|
32
|
+
assert(content != nil && "[react-native-live-markdown] Markdown parser bundle is empty");
|
|
33
|
+
ctx = [[JSContext alloc] init];
|
|
34
|
+
[ctx evaluateScript:content];
|
|
35
|
+
function = ctx[@"parseExpensiMarkToRanges"];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
JSValue *result = [function callWithArguments:@[inputString]];
|
|
39
|
+
NSArray *ranges = [result toArray];
|
|
40
|
+
|
|
41
|
+
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
|
|
42
|
+
[attributedString beginEditing];
|
|
43
|
+
|
|
44
|
+
// If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string.
|
|
45
|
+
// It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting.
|
|
46
|
+
// This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem.
|
|
47
|
+
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)];
|
|
48
|
+
|
|
49
|
+
_blockquoteRangesAndLevels = [NSMutableArray new];
|
|
50
|
+
|
|
51
|
+
[ranges enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
52
|
+
NSDictionary *item = obj;
|
|
53
|
+
NSString *type = [item valueForKey:@"type"];
|
|
54
|
+
NSInteger location = [[item valueForKey:@"start"] unsignedIntegerValue];
|
|
55
|
+
NSInteger length = [[item valueForKey:@"length"] unsignedIntegerValue];
|
|
56
|
+
NSInteger depth = [[item valueForKey:@"depth"] unsignedIntegerValue] ?: 1;
|
|
57
|
+
NSRange range = NSMakeRange(location, length);
|
|
58
|
+
|
|
59
|
+
if ([type isEqualToString:@"bold"] || [type isEqualToString:@"italic"] || [type isEqualToString:@"code"] || [type isEqualToString:@"pre"] || [type isEqualToString:@"h1"] || [type isEqualToString:@"emoji"]) {
|
|
60
|
+
UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:location effectiveRange:NULL];
|
|
61
|
+
if ([type isEqualToString:@"bold"]) {
|
|
62
|
+
font = [RCTFont updateFont:font withWeight:@"bold"];
|
|
63
|
+
} else if ([type isEqualToString:@"italic"]) {
|
|
64
|
+
font = [RCTFont updateFont:font withStyle:@"italic"];
|
|
65
|
+
} else if ([type isEqualToString:@"code"]) {
|
|
66
|
+
font = [RCTFont updateFont:font withFamily:_markdownStyle.codeFontFamily
|
|
67
|
+
size:[NSNumber numberWithFloat:_markdownStyle.codeFontSize]
|
|
68
|
+
weight:nil
|
|
69
|
+
style:nil
|
|
70
|
+
variant:nil
|
|
71
|
+
scaleMultiplier:0];
|
|
72
|
+
} else if ([type isEqualToString:@"pre"]) {
|
|
73
|
+
font = [RCTFont updateFont:font withFamily:_markdownStyle.preFontFamily
|
|
74
|
+
size:[NSNumber numberWithFloat:_markdownStyle.preFontSize]
|
|
75
|
+
weight:nil
|
|
76
|
+
style:nil
|
|
77
|
+
variant:nil
|
|
78
|
+
scaleMultiplier:0];
|
|
79
|
+
} else if ([type isEqualToString:@"h1"]) {
|
|
80
|
+
font = [RCTFont updateFont:font withFamily:nil
|
|
81
|
+
size:[NSNumber numberWithFloat:_markdownStyle.h1FontSize]
|
|
82
|
+
weight:@"bold"
|
|
83
|
+
style:nil
|
|
84
|
+
variant:nil
|
|
85
|
+
scaleMultiplier:0];
|
|
86
|
+
} else if ([type isEqualToString:@"emoji"]) {
|
|
87
|
+
font = [RCTFont updateFont:font withFamily:nil
|
|
88
|
+
size:[NSNumber numberWithFloat:_markdownStyle.emojiFontSize]
|
|
89
|
+
weight:nil
|
|
90
|
+
style:nil
|
|
91
|
+
variant:nil
|
|
92
|
+
scaleMultiplier:0];
|
|
93
|
+
}
|
|
94
|
+
[attributedString addAttribute:NSFontAttributeName value:font range:range];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if ([type isEqualToString:@"syntax"]) {
|
|
98
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.syntaxColor range:range];
|
|
99
|
+
} else if ([type isEqualToString:@"strikethrough"]) {
|
|
100
|
+
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
101
|
+
} else if ([type isEqualToString:@"code"]) {
|
|
102
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range];
|
|
103
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range];
|
|
104
|
+
} else if ([type isEqualToString:@"mention-here"]) {
|
|
105
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionHereColor range:range];
|
|
106
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range];
|
|
107
|
+
} else if ([type isEqualToString:@"mention-user"]) {
|
|
108
|
+
// TODO: change mention color when it mentions current user
|
|
109
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionUserColor range:range];
|
|
110
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionUserBackgroundColor range:range];
|
|
111
|
+
} else if ([type isEqualToString:@"link"]) {
|
|
112
|
+
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
113
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range];
|
|
114
|
+
} else if ([type isEqualToString:@"blockquote"]) {
|
|
115
|
+
CGFloat indent = (_markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft) * depth;
|
|
116
|
+
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
117
|
+
paragraphStyle.firstLineHeadIndent = indent;
|
|
118
|
+
paragraphStyle.headIndent = indent;
|
|
119
|
+
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
|
120
|
+
[_blockquoteRangesAndLevels addObject:@{
|
|
121
|
+
@"range": [NSValue valueWithRange:range],
|
|
122
|
+
@"depth": @(depth)
|
|
123
|
+
}];
|
|
124
|
+
} else if ([type isEqualToString:@"pre"]) {
|
|
125
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range];
|
|
126
|
+
NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range;
|
|
127
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground];
|
|
128
|
+
// TODO: pass background color and ranges to layout manager
|
|
129
|
+
} else if ([type isEqualToString:@"h1"]) {
|
|
130
|
+
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
131
|
+
NSRange rangeWithHashAndSpace = NSMakeRange(range.location - 2, range.length + 2); // we also need to include prepending "# "
|
|
132
|
+
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:rangeWithHashAndSpace];
|
|
133
|
+
}
|
|
134
|
+
}];
|
|
135
|
+
|
|
136
|
+
[attributedString endEditing];
|
|
137
|
+
|
|
138
|
+
_prevInputString = inputString;
|
|
139
|
+
_prevAttributedString = attributedString;
|
|
140
|
+
_prevTextAttributes = attributes;
|
|
141
|
+
_prevMarkdownStyle = _markdownStyle;
|
|
142
|
+
|
|
143
|
+
return attributedString;
|
|
105
144
|
|
|
106
|
-
if ([type isEqualToString:@"syntax"]) {
|
|
107
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.syntaxColor range:range];
|
|
108
|
-
} else if ([type isEqualToString:@"strikethrough"]) {
|
|
109
|
-
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
110
|
-
} else if ([type isEqualToString:@"code"]) {
|
|
111
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range];
|
|
112
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range];
|
|
113
|
-
} else if ([type isEqualToString:@"mention-here"]) {
|
|
114
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionHereColor range:range];
|
|
115
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range];
|
|
116
|
-
} else if ([type isEqualToString:@"mention-user"]) {
|
|
117
|
-
// TODO: change mention color when it mentions current user
|
|
118
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionUserColor range:range];
|
|
119
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionUserBackgroundColor range:range];
|
|
120
|
-
} else if ([type isEqualToString:@"link"]) {
|
|
121
|
-
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
122
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range];
|
|
123
|
-
} else if ([type isEqualToString:@"blockquote"]) {
|
|
124
|
-
CGFloat indent = (_markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft) * depth;
|
|
125
|
-
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
126
|
-
paragraphStyle.firstLineHeadIndent = indent;
|
|
127
|
-
paragraphStyle.headIndent = indent;
|
|
128
|
-
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
|
129
|
-
[_blockquoteRangesAndLevels addObject:@{
|
|
130
|
-
@"range": [NSValue valueWithRange:range],
|
|
131
|
-
@"depth": @(depth)
|
|
132
|
-
}];
|
|
133
|
-
} else if ([type isEqualToString:@"pre"]) {
|
|
134
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range];
|
|
135
|
-
NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range;
|
|
136
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground];
|
|
137
|
-
// TODO: pass background color and ranges to layout manager
|
|
138
|
-
} else if ([type isEqualToString:@"h1"]) {
|
|
139
|
-
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
140
|
-
NSRange rangeWithHashAndSpace = NSMakeRange(range.location - 2, range.length + 2); // we also need to include prepending "# "
|
|
141
|
-
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:rangeWithHashAndSpace];
|
|
142
145
|
}
|
|
143
|
-
}];
|
|
144
|
-
|
|
145
|
-
[attributedString endEditing];
|
|
146
|
-
|
|
147
|
-
_prevInputString = inputString;
|
|
148
|
-
_prevAttributedString = attributedString;
|
|
149
|
-
_prevTextAttributes = _backedTextInputView.defaultTextAttributes;
|
|
150
|
-
_prevMarkdownStyle = _markdownStyle;
|
|
151
|
-
|
|
152
|
-
return attributedString;
|
|
153
146
|
}
|
|
154
147
|
|
|
155
148
|
@end
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
#import <React/RCTUITextField.h>
|
|
7
7
|
#import <objc/message.h>
|
|
8
8
|
|
|
9
|
+
#import "MarkdownShadowFamilyRegistry.h"
|
|
10
|
+
|
|
9
11
|
@implementation RCTTextInputComponentView (Markdown)
|
|
10
12
|
|
|
11
13
|
- (void)setMarkdownUtils:(RCTMarkdownUtils *)markdownUtils {
|
|
@@ -13,8 +15,8 @@
|
|
|
13
15
|
|
|
14
16
|
if (markdownUtils != nil) {
|
|
15
17
|
// force Markdown formatting on first render because `_setAttributedText` is called before `setMarkdownUtils`
|
|
16
|
-
RCTUITextField *backedTextInputView = [self
|
|
17
|
-
backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText];
|
|
18
|
+
RCTUITextField *backedTextInputView = [self getBackedTextInputView];
|
|
19
|
+
backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText withAttributes:backedTextInputView.defaultTextAttributes];
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -22,11 +24,23 @@
|
|
|
22
24
|
return objc_getAssociatedObject(self, @selector(getMarkdownUtils));
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
- (RCTUITextField *)getBackedTextInputView {
|
|
28
|
+
RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"];
|
|
29
|
+
return backedTextInputView;
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
- (void)markdown__setAttributedString:(NSAttributedString *)attributedString
|
|
26
33
|
{
|
|
27
34
|
RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils];
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
RCTUITextField *backedTextInputView = [self getBackedTextInputView];
|
|
36
|
+
if (markdownUtils != nil && backedTextInputView != nil) {
|
|
37
|
+
attributedString = [markdownUtils parseMarkdown:attributedString withAttributes:backedTextInputView.defaultTextAttributes];
|
|
38
|
+
} else {
|
|
39
|
+
// If markdownUtils is undefined, the text input hasn't been mounted yet. It will
|
|
40
|
+
// update its state with the unformatted attributed string, we want to prevent displaying
|
|
41
|
+
// this state by applying markdown in the commit hook where we can read markdown styles
|
|
42
|
+
// from decorator props.
|
|
43
|
+
MarkdownShadowFamilyRegistry::forceNextStateUpdate((facebook::react::Tag)self.tag);
|
|
30
44
|
}
|
|
31
45
|
|
|
32
46
|
// Call the original method
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils];
|
|
18
18
|
if (markdownUtils != nil) {
|
|
19
19
|
UITextRange *range = self.selectedTextRange;
|
|
20
|
-
super.attributedText = [markdownUtils parseMarkdown:self.attributedText];
|
|
20
|
+
super.attributedText = [markdownUtils parseMarkdown:self.attributedText withAttributes:self.defaultTextAttributes];
|
|
21
21
|
[super setSelectedTextRange:range]; // prevents cursor from jumping at the end when typing in the middle of the text
|
|
22
22
|
self.typingAttributes = self.defaultTextAttributes; // removes indent in new line when typing after blockquote
|
|
23
23
|
}
|
|
@@ -7,11 +7,15 @@ exports.default = void 0;
|
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
8
|
var _react = _interopRequireDefault(require("react"));
|
|
9
9
|
var _MarkdownTextInputDecoratorViewNativeComponent = _interopRequireDefault(require("./MarkdownTextInputDecoratorViewNativeComponent"));
|
|
10
|
+
var _NativeLiveMarkdownModule = _interopRequireDefault(require("./NativeLiveMarkdownModule"));
|
|
10
11
|
var StyleUtils = _interopRequireWildcard(require("./styleUtils"));
|
|
11
12
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
12
13
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
13
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
15
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
16
|
+
if (_NativeLiveMarkdownModule.default) {
|
|
17
|
+
_NativeLiveMarkdownModule.default.install();
|
|
18
|
+
}
|
|
15
19
|
function processColorsInMarkdownStyle(input) {
|
|
16
20
|
const output = JSON.parse(JSON.stringify(input));
|
|
17
21
|
Object.keys(output).forEach(key => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_MarkdownTextInputDecoratorViewNativeComponent","StyleUtils","_interopRequireWildcard","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","prototype","hasOwnProperty","call","i","set","obj","_extends","assign","bind","target","arguments","length","source","key","apply","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","keys","forEach","prop","endsWith","processColor","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","React","forwardRef","props","ref","IS_FABRIC","global","markdownStyle","useMemo","createElement","Fragment","TextInput","style","styles","farAway","displayNone","StyleSheet","create","display","position","top","left","_default","exports"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAC,sBAAA,CAAAF,OAAA;AAEA,IAAAG,8CAAA,GAAAD,sBAAA,CAAAF,OAAA;
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_MarkdownTextInputDecoratorViewNativeComponent","_NativeLiveMarkdownModule","StyleUtils","_interopRequireWildcard","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","prototype","hasOwnProperty","call","i","set","obj","_extends","assign","bind","target","arguments","length","source","key","apply","NativeLiveMarkdownModule","install","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","keys","forEach","prop","endsWith","processColor","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","React","forwardRef","props","ref","IS_FABRIC","global","markdownStyle","useMemo","createElement","Fragment","TextInput","style","styles","farAway","displayNone","StyleSheet","create","display","position","top","left","_default","exports"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
var _default = exports.default = _reactNative.TurboModuleRegistry.get('LiveMarkdownModule');
|
|
9
|
+
//# sourceMappingURL=NativeLiveMarkdownModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativeLiveMarkdownModule.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAAiD,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAMlCC,gCAAmB,CAACC,GAAG,CAAO,oBAAoB,CAAC"}
|
|
@@ -2,7 +2,11 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
|
|
|
2
2
|
import { StyleSheet, TextInput, processColor } from 'react-native';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import MarkdownTextInputDecoratorViewNativeComponent from './MarkdownTextInputDecoratorViewNativeComponent';
|
|
5
|
+
import NativeLiveMarkdownModule from './NativeLiveMarkdownModule';
|
|
5
6
|
import * as StyleUtils from './styleUtils';
|
|
7
|
+
if (NativeLiveMarkdownModule) {
|
|
8
|
+
NativeLiveMarkdownModule.install();
|
|
9
|
+
}
|
|
6
10
|
function processColorsInMarkdownStyle(input) {
|
|
7
11
|
const output = JSON.parse(JSON.stringify(input));
|
|
8
12
|
Object.keys(output).forEach(key => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["StyleSheet","TextInput","processColor","React","MarkdownTextInputDecoratorViewNativeComponent","StyleUtils","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","Object","keys","forEach","key","obj","prop","endsWith","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","forwardRef","props","ref","IS_FABRIC","global","markdownStyle","useMemo","createElement","Fragment","_extends","style","styles","farAway","displayNone","create","display","position","top","left"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":";AAAA,SAAQA,UAAU,EAAEC,SAAS,EAAEC,YAAY,QAAO,cAAc;AAChE,OAAOC,KAAK,MAAM,OAAO;AAEzB,OAAOC,6CAA6C,MAAM,iDAAiD;
|
|
1
|
+
{"version":3,"names":["StyleSheet","TextInput","processColor","React","MarkdownTextInputDecoratorViewNativeComponent","NativeLiveMarkdownModule","StyleUtils","install","processColorsInMarkdownStyle","input","output","JSON","parse","stringify","Object","keys","forEach","key","obj","prop","endsWith","processMarkdownStyle","mergeMarkdownStyleWithDefault","MarkdownTextInput","forwardRef","props","ref","IS_FABRIC","global","markdownStyle","useMemo","createElement","Fragment","_extends","style","styles","farAway","displayNone","create","display","position","top","left"],"sourceRoot":"../../src","sources":["MarkdownTextInput.tsx"],"mappings":";AAAA,SAAQA,UAAU,EAAEC,SAAS,EAAEC,YAAY,QAAO,cAAc;AAChE,OAAOC,KAAK,MAAM,OAAO;AAEzB,OAAOC,6CAA6C,MAAM,iDAAiD;AAC3G,OAAOC,wBAAwB,MAAM,4BAA4B;AAEjE,OAAO,KAAKC,UAAU,MAAM,cAAc;AAG1C,IAAID,wBAAwB,EAAE;EAC5BA,wBAAwB,CAACE,OAAO,CAAC,CAAC;AACpC;AASA,SAASC,4BAA4BA,CAACC,KAAoB,EAAiB;EACzE,MAAMC,MAAM,GAAGC,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,SAAS,CAACJ,KAAK,CAAC,CAAC;EAEhDK,MAAM,CAACC,IAAI,CAACL,MAAM,CAAC,CAACM,OAAO,CAAEC,GAAG,IAAK;IACnC,MAAMC,GAAG,GAAGR,MAAM,CAACO,GAAG,CAAC;IACvBH,MAAM,CAACC,IAAI,CAACG,GAAG,CAAC,CAACF,OAAO,CAAEG,IAAI,IAAK;MACjC;MACA,IAAI,EAAEA,IAAI,KAAK,OAAO,IAAIA,IAAI,CAACC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE;QACjD;MACF;MACAF,GAAG,CAACC,IAAI,CAAC,GAAGjB,YAAY,CAACgB,GAAG,CAACC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC;EACJ,CAAC,CAAC;EAEF,OAAOT,MAAM;AACf;AAEA,SAASW,oBAAoBA,CAACZ,KAAuC,EAAiB;EACpF,OAAOD,4BAA4B,CAACF,UAAU,CAACgB,6BAA6B,CAACb,KAAK,CAAC,CAAC;AACtF;AAEA,MAAMc,iBAAiB,gBAAGpB,KAAK,CAACqB,UAAU,CAAoC,CAACC,KAAK,EAAEC,GAAG,KAAK;EAC5F,MAAMC,SAAS,IAAG,uBAAuB,IAAIC,MAAM;EAEnD,MAAMC,aAAa,GAAG1B,KAAK,CAAC2B,OAAO,CAAC,MAAMT,oBAAoB,CAACI,KAAK,CAACI,aAAa,CAAC,EAAE,CAACJ,KAAK,CAACI,aAAa,CAAC,CAAC;EAE3G,oBACE1B,KAAA,CAAA4B,aAAA,CAAA5B,KAAA,CAAA6B,QAAA,qBACE7B,KAAA,CAAA4B,aAAA,CAAC9B,SAAS,EAAAgC,QAAA,KACJR,KAAK;IACTC,GAAG,EAAEA;EAAI,EACV,CAAC,eACFvB,KAAA,CAAA4B,aAAA,CAAC3B,6CAA6C;IAC5C8B,KAAK,EAAEP,SAAS,GAAGQ,MAAM,CAACC,OAAO,GAAGD,MAAM,CAACE,WAAY;IACvDR,aAAa,EAAEA;EAAc,CAC9B,CACD,CAAC;AAEP,CAAC,CAAC;AAEF,MAAMM,MAAM,GAAGnC,UAAU,CAACsC,MAAM,CAAC;EAC/BD,WAAW,EAAE;IACXE,OAAO,EAAE;EACX,CAAC;EACDH,OAAO,EAAE;IACPI,QAAQ,EAAE,UAAU;IACpBC,GAAG,EAAE,GAAG;IACRC,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAIF,eAAenB,iBAAiB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativeLiveMarkdownModule.ts"],"mappings":"AACA,SAAQA,mBAAmB,QAAO,cAAc;AAMhD,eAAeA,mBAAmB,CAACC,GAAG,CAAO,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownTextInput.d.ts","sourceRoot":"","sources":["../../../src/MarkdownTextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAe,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"MarkdownTextInput.d.ts","sourceRoot":"","sources":["../../../src/MarkdownTextInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAe,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AAKjD,OAAO,KAAK,KAAK,eAAe,MAAM,cAAc,CAAC;AAMrD,KAAK,oBAAoB,GAAG,eAAe,CAAC,oBAAoB,CAAC;AAGjE,UAAU,sBAAuB,SAAQ,cAAc;IACrD,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC;AAuBD,QAAA,MAAM,iBAAiB,0FAiBrB,CAAC;AAaH,YAAY,EAAC,oBAAoB,IAAI,aAAa,EAAE,sBAAsB,EAAC,CAAC;AAE5E,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeLiveMarkdownModule.d.ts","sourceRoot":"","sources":["../../../src/NativeLiveMarkdownModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAG9C,UAAU,IAAK,SAAQ,WAAW;IAChC,OAAO,EAAE,MAAM,OAAO,CAAC;CACxB;;AAED,wBAAmE"}
|
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.45",
|
|
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",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
},
|
|
157
157
|
"codegenConfig": {
|
|
158
158
|
"name": "RNLiveMarkdownSpec",
|
|
159
|
-
"type": "
|
|
159
|
+
"type": "all",
|
|
160
160
|
"jsSrcsDir": "src"
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -2,10 +2,15 @@ import {StyleSheet, TextInput, processColor} from 'react-native';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import type {TextInputProps} from 'react-native';
|
|
4
4
|
import MarkdownTextInputDecoratorViewNativeComponent from './MarkdownTextInputDecoratorViewNativeComponent';
|
|
5
|
+
import NativeLiveMarkdownModule from './NativeLiveMarkdownModule';
|
|
5
6
|
import type * as MarkdownTextInputDecoratorViewNativeComponentTypes from './MarkdownTextInputDecoratorViewNativeComponent';
|
|
6
7
|
import * as StyleUtils from './styleUtils';
|
|
7
8
|
import type * as StyleUtilsTypes from './styleUtils';
|
|
8
9
|
|
|
10
|
+
if (NativeLiveMarkdownModule) {
|
|
11
|
+
NativeLiveMarkdownModule.install();
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
type PartialMarkdownStyle = StyleUtilsTypes.PartialMarkdownStyle;
|
|
10
15
|
type MarkdownStyle = MarkdownTextInputDecoratorViewNativeComponentTypes.MarkdownStyle;
|
|
11
16
|
|