@expensify/react-native-live-markdown 0.1.168 → 0.1.170
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 +1 -1
- package/apple/RCTMarkdownUtils.mm +213 -0
- package/package.json +3 -3
- package/ios/RCTMarkdownUtils.mm +0 -210
- /package/{ios → apple}/MarkdownCommitHook.h +0 -0
- /package/{ios → apple}/MarkdownCommitHook.mm +0 -0
- /package/{ios → apple}/MarkdownLayoutManager.h +0 -0
- /package/{ios → apple}/MarkdownLayoutManager.mm +0 -0
- /package/{ios → apple}/MarkdownTextInputDecoratorComponentView.h +0 -0
- /package/{ios → apple}/MarkdownTextInputDecoratorComponentView.mm +0 -0
- /package/{ios → apple}/MarkdownTextInputDecoratorView.h +0 -0
- /package/{ios → apple}/MarkdownTextInputDecoratorView.mm +0 -0
- /package/{ios → apple}/MarkdownTextInputDecoratorViewManager.h +0 -0
- /package/{ios → apple}/MarkdownTextInputDecoratorViewManager.mm +0 -0
- /package/{ios → apple}/RCTBackedTextFieldDelegateAdapter+Markdown.h +0 -0
- /package/{ios → apple}/RCTBackedTextFieldDelegateAdapter+Markdown.mm +0 -0
- /package/{ios → apple}/RCTBaseTextInputView+Markdown.h +0 -0
- /package/{ios → apple}/RCTBaseTextInputView+Markdown.mm +0 -0
- /package/{ios → apple}/RCTLiveMarkdownModule.h +0 -0
- /package/{ios → apple}/RCTLiveMarkdownModule.mm +0 -0
- /package/{ios → apple}/RCTMarkdownStyle.h +0 -0
- /package/{ios → apple}/RCTMarkdownStyle.mm +0 -0
- /package/{ios → apple}/RCTMarkdownUtils.h +0 -0
- /package/{ios → apple}/RCTTextInputComponentView+Markdown.h +0 -0
- /package/{ios → apple}/RCTTextInputComponentView+Markdown.mm +0 -0
- /package/{ios → apple}/RCTUITextView+Markdown.h +0 -0
- /package/{ios → apple}/RCTUITextView+Markdown.mm +0 -0
package/RNLiveMarkdown.podspec
CHANGED
|
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
|
|
|
18
18
|
s.platforms = { :ios => "11.0", :visionos => "1.0" }
|
|
19
19
|
s.source = { :git => "https://github.com/expensify/react-native-live-markdown.git", :tag => "#{s.version}" }
|
|
20
20
|
|
|
21
|
-
s.source_files = "
|
|
21
|
+
s.source_files = "apple/**/*.{h,m,mm}"
|
|
22
22
|
|
|
23
23
|
s.resources = "parser/react-native-live-markdown-parser.js"
|
|
24
24
|
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#import <RNLiveMarkdown/RCTMarkdownUtils.h>
|
|
2
|
+
#import "react_native_assert.h"
|
|
3
|
+
#import <React/RCTAssert.h>
|
|
4
|
+
#import <React/RCTFont.h>
|
|
5
|
+
|
|
6
|
+
#include <jsi/jsi.h>
|
|
7
|
+
#include <hermes/hermes.h>
|
|
8
|
+
|
|
9
|
+
using namespace facebook;
|
|
10
|
+
|
|
11
|
+
@implementation RCTMarkdownUtils {
|
|
12
|
+
NSString *_prevInputString;
|
|
13
|
+
NSAttributedString *_prevAttributedString;
|
|
14
|
+
NSDictionary<NSAttributedStringKey, id> *_prevTextAttributes;
|
|
15
|
+
__weak RCTMarkdownStyle *_prevMarkdownStyle;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes
|
|
19
|
+
{
|
|
20
|
+
@synchronized (self) {
|
|
21
|
+
if (input == nil) {
|
|
22
|
+
return nil;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
NSString *inputString = [input string];
|
|
26
|
+
if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle]) {
|
|
27
|
+
return _prevAttributedString;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static std::shared_ptr<jsi::Runtime> runtime;
|
|
31
|
+
static std::mutex runtimeMutex;
|
|
32
|
+
auto lock = std::lock_guard<std::mutex>(runtimeMutex);
|
|
33
|
+
|
|
34
|
+
if (runtime == nullptr) {
|
|
35
|
+
NSString *path = [[NSBundle mainBundle] pathForResource:@"react-native-live-markdown-parser" ofType:@"js"];
|
|
36
|
+
assert(path != nil && "[react-native-live-markdown] Markdown parser bundle not found");
|
|
37
|
+
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
|
38
|
+
assert(content != nil && "[react-native-live-markdown] Markdown parser bundle is empty");
|
|
39
|
+
runtime = facebook::hermes::makeHermesRuntime();
|
|
40
|
+
auto codeBuffer = std::make_shared<const jsi::StringBuffer>([content UTF8String]);
|
|
41
|
+
runtime->evaluateJavaScript(codeBuffer, "evaluateJavaScript");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
jsi::Runtime &rt = *runtime;
|
|
45
|
+
auto text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);
|
|
46
|
+
|
|
47
|
+
auto func = rt.global().getPropertyAsFunction(rt, "parseExpensiMarkToRanges");
|
|
48
|
+
auto output = func.call(rt, text);
|
|
49
|
+
if (output.isUndefined()) {
|
|
50
|
+
return input;
|
|
51
|
+
}
|
|
52
|
+
const auto &ranges = output.asObject(rt).asArray(rt);
|
|
53
|
+
|
|
54
|
+
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
|
|
55
|
+
[attributedString beginEditing];
|
|
56
|
+
|
|
57
|
+
// If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string.
|
|
58
|
+
// It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting.
|
|
59
|
+
// This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem.
|
|
60
|
+
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)];
|
|
61
|
+
|
|
62
|
+
_blockquoteRangesAndLevels = [NSMutableArray new];
|
|
63
|
+
|
|
64
|
+
for (size_t i = 0, n = ranges.size(rt); i < n; ++i) {
|
|
65
|
+
const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt);
|
|
66
|
+
const auto &type = item.getProperty(rt, "type").asString(rt).utf8(rt);
|
|
67
|
+
const auto &start = static_cast<int>(item.getProperty(rt, "start").asNumber());
|
|
68
|
+
const auto &length = static_cast<int>(item.getProperty(rt, "length").asNumber());
|
|
69
|
+
const auto &depth = item.hasProperty(rt, "depth") ? static_cast<int>(item.getProperty(rt, "depth").asNumber()) : 1;
|
|
70
|
+
|
|
71
|
+
[self applyRangeToAttributedString:attributedString type:type start:start length:length depth:depth];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
RCTApplyBaselineOffset(attributedString);
|
|
75
|
+
|
|
76
|
+
[attributedString endEditing];
|
|
77
|
+
|
|
78
|
+
_prevInputString = inputString;
|
|
79
|
+
_prevAttributedString = attributedString;
|
|
80
|
+
_prevTextAttributes = attributes;
|
|
81
|
+
_prevMarkdownStyle = _markdownStyle;
|
|
82
|
+
|
|
83
|
+
return attributedString;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
- (void)applyRangeToAttributedString:(NSMutableAttributedString *)attributedString type:(const std::string)type start:(const int)start length:(const int)length depth:(const int)depth {
|
|
88
|
+
if (length == 0 || start + length > attributedString.length) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
NSRange range = NSMakeRange(start, length);
|
|
93
|
+
|
|
94
|
+
if (type == "bold" || type == "italic" || type == "code" || type == "pre" || type == "h1" || type == "emoji") {
|
|
95
|
+
UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:start effectiveRange:NULL];
|
|
96
|
+
if (type == "bold") {
|
|
97
|
+
font = [RCTFont updateFont:font withWeight:@"bold"];
|
|
98
|
+
} else if (type == "italic") {
|
|
99
|
+
font = [RCTFont updateFont:font withStyle:@"italic"];
|
|
100
|
+
} else if (type == "code") {
|
|
101
|
+
font = [RCTFont updateFont:font withFamily:_markdownStyle.codeFontFamily
|
|
102
|
+
size:[NSNumber numberWithFloat:_markdownStyle.codeFontSize]
|
|
103
|
+
weight:nil
|
|
104
|
+
style:nil
|
|
105
|
+
variant:nil
|
|
106
|
+
scaleMultiplier:0];
|
|
107
|
+
} else if (type == "pre") {
|
|
108
|
+
font = [RCTFont updateFont:font withFamily:_markdownStyle.preFontFamily
|
|
109
|
+
size:[NSNumber numberWithFloat:_markdownStyle.preFontSize]
|
|
110
|
+
weight:nil
|
|
111
|
+
style:nil
|
|
112
|
+
variant:nil
|
|
113
|
+
scaleMultiplier:0];
|
|
114
|
+
} else if (type == "h1") {
|
|
115
|
+
font = [RCTFont updateFont:font withFamily:nil
|
|
116
|
+
size:[NSNumber numberWithFloat:_markdownStyle.h1FontSize]
|
|
117
|
+
weight:@"bold"
|
|
118
|
+
style:nil
|
|
119
|
+
variant:nil
|
|
120
|
+
scaleMultiplier:0];
|
|
121
|
+
} else if (type == "emoji") {
|
|
122
|
+
font = [RCTFont updateFont:font withFamily:nil
|
|
123
|
+
size:[NSNumber numberWithFloat:_markdownStyle.emojiFontSize]
|
|
124
|
+
weight:nil
|
|
125
|
+
style:nil
|
|
126
|
+
variant:nil
|
|
127
|
+
scaleMultiplier:0];
|
|
128
|
+
}
|
|
129
|
+
[attributedString addAttribute:NSFontAttributeName value:font range:range];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (type == "syntax") {
|
|
133
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.syntaxColor range:range];
|
|
134
|
+
} else if (type == "strikethrough") {
|
|
135
|
+
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
136
|
+
} else if (type == "code") {
|
|
137
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range];
|
|
138
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range];
|
|
139
|
+
} else if (type == "mention-here") {
|
|
140
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionHereColor range:range];
|
|
141
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range];
|
|
142
|
+
} else if (type == "mention-user") {
|
|
143
|
+
// TODO: change mention color when it mentions current user
|
|
144
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionUserColor range:range];
|
|
145
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionUserBackgroundColor range:range];
|
|
146
|
+
} else if (type == "mention-report") {
|
|
147
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionReportColor range:range];
|
|
148
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionReportBackgroundColor range:range];
|
|
149
|
+
} else if (type == "link") {
|
|
150
|
+
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
151
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range];
|
|
152
|
+
} else if (type == "blockquote") {
|
|
153
|
+
CGFloat indent = (_markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft) * depth;
|
|
154
|
+
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
155
|
+
paragraphStyle.firstLineHeadIndent = indent;
|
|
156
|
+
paragraphStyle.headIndent = indent;
|
|
157
|
+
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
|
158
|
+
[_blockquoteRangesAndLevels addObject:@{
|
|
159
|
+
@"range": [NSValue valueWithRange:range],
|
|
160
|
+
@"depth": @(depth)
|
|
161
|
+
}];
|
|
162
|
+
} else if (type == "pre") {
|
|
163
|
+
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range];
|
|
164
|
+
NSRange rangeForBackground = [[attributedString string] characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range;
|
|
165
|
+
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground];
|
|
166
|
+
// TODO: pass background color and ranges to layout manager
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
|
|
171
|
+
{
|
|
172
|
+
__block CGFloat maximumLineHeight = 0;
|
|
173
|
+
|
|
174
|
+
[attributedText enumerateAttribute:NSParagraphStyleAttributeName
|
|
175
|
+
inRange:NSMakeRange(0, attributedText.length)
|
|
176
|
+
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
|
177
|
+
usingBlock:^(NSParagraphStyle *paragraphStyle, __unused NSRange range, __unused BOOL *stop) {
|
|
178
|
+
if (!paragraphStyle) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
maximumLineHeight = MAX(paragraphStyle.maximumLineHeight, maximumLineHeight);
|
|
183
|
+
}];
|
|
184
|
+
|
|
185
|
+
if (maximumLineHeight == 0) {
|
|
186
|
+
// `lineHeight` was not specified, nothing to do.
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
__block CGFloat maximumFontLineHeight = 0;
|
|
191
|
+
|
|
192
|
+
[attributedText enumerateAttribute:NSFontAttributeName
|
|
193
|
+
inRange:NSMakeRange(0, attributedText.length)
|
|
194
|
+
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
|
195
|
+
usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) {
|
|
196
|
+
if (!font) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
maximumFontLineHeight = MAX(font.lineHeight, maximumFontLineHeight);
|
|
201
|
+
}];
|
|
202
|
+
|
|
203
|
+
if (maximumLineHeight < maximumFontLineHeight) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
CGFloat baseLineOffset = (maximumLineHeight - maximumFontLineHeight) / 2.0;
|
|
208
|
+
[attributedText addAttribute:NSBaselineOffsetAttributeName
|
|
209
|
+
value:@(baseLineOffset)
|
|
210
|
+
range:NSMakeRange(0, attributedText.length)];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@end
|
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.170",
|
|
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",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"lib",
|
|
13
13
|
"parser/react-native-live-markdown-parser.js",
|
|
14
14
|
"android",
|
|
15
|
-
"
|
|
15
|
+
"apple",
|
|
16
16
|
"cpp",
|
|
17
17
|
"*.podspec",
|
|
18
18
|
"react-native.config.js",
|
|
19
|
-
"!
|
|
19
|
+
"!apple/build",
|
|
20
20
|
"!android/build",
|
|
21
21
|
"!android/gradle",
|
|
22
22
|
"!android/gradlew",
|
package/ios/RCTMarkdownUtils.mm
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
#import <RNLiveMarkdown/RCTMarkdownUtils.h>
|
|
2
|
-
#import "react_native_assert.h"
|
|
3
|
-
#import <React/RCTAssert.h>
|
|
4
|
-
#import <React/RCTFont.h>
|
|
5
|
-
|
|
6
|
-
#include <jsi/jsi.h>
|
|
7
|
-
#include <hermes/hermes.h>
|
|
8
|
-
|
|
9
|
-
using namespace facebook;
|
|
10
|
-
|
|
11
|
-
@implementation RCTMarkdownUtils {
|
|
12
|
-
NSString *_prevInputString;
|
|
13
|
-
NSAttributedString *_prevAttributedString;
|
|
14
|
-
NSDictionary<NSAttributedStringKey, id> *_prevTextAttributes;
|
|
15
|
-
__weak RCTMarkdownStyle *_prevMarkdownStyle;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes
|
|
19
|
-
{
|
|
20
|
-
@synchronized (self) {
|
|
21
|
-
if (input == nil) {
|
|
22
|
-
return nil;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
NSString *inputString = [input string];
|
|
26
|
-
if ([inputString isEqualToString:_prevInputString] && [attributes isEqualToDictionary:_prevTextAttributes] && [_markdownStyle isEqual:_prevMarkdownStyle]) {
|
|
27
|
-
return _prevAttributedString;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static std::shared_ptr<jsi::Runtime> runtime;
|
|
31
|
-
static std::mutex runtimeMutex;
|
|
32
|
-
auto lock = std::lock_guard<std::mutex>(runtimeMutex);
|
|
33
|
-
|
|
34
|
-
if (runtime == nullptr) {
|
|
35
|
-
NSString *path = [[NSBundle mainBundle] pathForResource:@"react-native-live-markdown-parser" ofType:@"js"];
|
|
36
|
-
assert(path != nil && "[react-native-live-markdown] Markdown parser bundle not found");
|
|
37
|
-
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
|
38
|
-
assert(content != nil && "[react-native-live-markdown] Markdown parser bundle is empty");
|
|
39
|
-
runtime = facebook::hermes::makeHermesRuntime();
|
|
40
|
-
auto codeBuffer = std::make_shared<const jsi::StringBuffer>([content UTF8String]);
|
|
41
|
-
runtime->evaluateJavaScript(codeBuffer, "evaluateJavaScript");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
jsi::Runtime &rt = *runtime;
|
|
45
|
-
auto text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);
|
|
46
|
-
|
|
47
|
-
auto func = rt.global().getPropertyAsFunction(rt, "parseExpensiMarkToRanges");
|
|
48
|
-
auto output = func.call(rt, text);
|
|
49
|
-
if (output.isUndefined()) {
|
|
50
|
-
return input;
|
|
51
|
-
}
|
|
52
|
-
const auto &ranges = output.asObject(rt).asArray(rt);
|
|
53
|
-
|
|
54
|
-
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
|
|
55
|
-
[attributedString beginEditing];
|
|
56
|
-
|
|
57
|
-
// If the attributed string ends with underlined text, blurring the single-line input imprints the underline style across the whole string.
|
|
58
|
-
// It looks like a bug in iOS, as there is no underline style to be found in the attributed string, especially after formatting.
|
|
59
|
-
// This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem.
|
|
60
|
-
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)];
|
|
61
|
-
|
|
62
|
-
_blockquoteRangesAndLevels = [NSMutableArray new];
|
|
63
|
-
|
|
64
|
-
for (size_t i = 0, n = ranges.size(rt); i < n; ++i) {
|
|
65
|
-
const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt);
|
|
66
|
-
const auto &type = item.getProperty(rt, "type").asString(rt).utf8(rt);
|
|
67
|
-
const auto &location = static_cast<int>(item.getProperty(rt, "start").asNumber());
|
|
68
|
-
const auto &length = static_cast<int>(item.getProperty(rt, "length").asNumber());
|
|
69
|
-
const auto &depth = item.hasProperty(rt, "depth") ? static_cast<int>(item.getProperty(rt, "depth").asNumber()) : 1;
|
|
70
|
-
|
|
71
|
-
if (length == 0 || location + length > attributedString.length) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
NSRange range = NSMakeRange(location, length);
|
|
76
|
-
|
|
77
|
-
if (type == "bold" || type == "italic" || type == "code" || type == "pre" || type == "h1" || type == "emoji") {
|
|
78
|
-
UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:location effectiveRange:NULL];
|
|
79
|
-
if (type == "bold") {
|
|
80
|
-
font = [RCTFont updateFont:font withWeight:@"bold"];
|
|
81
|
-
} else if (type == "italic") {
|
|
82
|
-
font = [RCTFont updateFont:font withStyle:@"italic"];
|
|
83
|
-
} else if (type == "code") {
|
|
84
|
-
font = [RCTFont updateFont:font withFamily:_markdownStyle.codeFontFamily
|
|
85
|
-
size:[NSNumber numberWithFloat:_markdownStyle.codeFontSize]
|
|
86
|
-
weight:nil
|
|
87
|
-
style:nil
|
|
88
|
-
variant:nil
|
|
89
|
-
scaleMultiplier:0];
|
|
90
|
-
} else if (type == "pre") {
|
|
91
|
-
font = [RCTFont updateFont:font withFamily:_markdownStyle.preFontFamily
|
|
92
|
-
size:[NSNumber numberWithFloat:_markdownStyle.preFontSize]
|
|
93
|
-
weight:nil
|
|
94
|
-
style:nil
|
|
95
|
-
variant:nil
|
|
96
|
-
scaleMultiplier:0];
|
|
97
|
-
} else if (type == "h1") {
|
|
98
|
-
font = [RCTFont updateFont:font withFamily:nil
|
|
99
|
-
size:[NSNumber numberWithFloat:_markdownStyle.h1FontSize]
|
|
100
|
-
weight:@"bold"
|
|
101
|
-
style:nil
|
|
102
|
-
variant:nil
|
|
103
|
-
scaleMultiplier:0];
|
|
104
|
-
} else if (type == "emoji") {
|
|
105
|
-
font = [RCTFont updateFont:font withFamily:nil
|
|
106
|
-
size:[NSNumber numberWithFloat:_markdownStyle.emojiFontSize]
|
|
107
|
-
weight:nil
|
|
108
|
-
style:nil
|
|
109
|
-
variant:nil
|
|
110
|
-
scaleMultiplier:0];
|
|
111
|
-
}
|
|
112
|
-
[attributedString addAttribute:NSFontAttributeName value:font range:range];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (type == "syntax") {
|
|
116
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.syntaxColor range:range];
|
|
117
|
-
} else if (type == "strikethrough") {
|
|
118
|
-
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
119
|
-
} else if (type == "code") {
|
|
120
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range];
|
|
121
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range];
|
|
122
|
-
} else if (type == "mention-here") {
|
|
123
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionHereColor range:range];
|
|
124
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range];
|
|
125
|
-
} else if (type == "mention-user") {
|
|
126
|
-
// TODO: change mention color when it mentions current user
|
|
127
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionUserColor range:range];
|
|
128
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionUserBackgroundColor range:range];
|
|
129
|
-
} else if (type == "mention-report") {
|
|
130
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionReportColor range:range];
|
|
131
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionReportBackgroundColor range:range];
|
|
132
|
-
} else if (type == "link") {
|
|
133
|
-
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
|
|
134
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range];
|
|
135
|
-
} else if (type == "blockquote") {
|
|
136
|
-
CGFloat indent = (_markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft) * depth;
|
|
137
|
-
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
138
|
-
paragraphStyle.firstLineHeadIndent = indent;
|
|
139
|
-
paragraphStyle.headIndent = indent;
|
|
140
|
-
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
|
|
141
|
-
[_blockquoteRangesAndLevels addObject:@{
|
|
142
|
-
@"range": [NSValue valueWithRange:range],
|
|
143
|
-
@"depth": @(depth)
|
|
144
|
-
}];
|
|
145
|
-
} else if (type == "pre") {
|
|
146
|
-
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range];
|
|
147
|
-
NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range;
|
|
148
|
-
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground];
|
|
149
|
-
// TODO: pass background color and ranges to layout manager
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
RCTApplyBaselineOffset(attributedString);
|
|
154
|
-
|
|
155
|
-
[attributedString endEditing];
|
|
156
|
-
|
|
157
|
-
_prevInputString = inputString;
|
|
158
|
-
_prevAttributedString = attributedString;
|
|
159
|
-
_prevTextAttributes = attributes;
|
|
160
|
-
_prevMarkdownStyle = _markdownStyle;
|
|
161
|
-
|
|
162
|
-
return attributedString;
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
static void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
|
|
168
|
-
{
|
|
169
|
-
__block CGFloat maximumLineHeight = 0;
|
|
170
|
-
|
|
171
|
-
[attributedText enumerateAttribute:NSParagraphStyleAttributeName
|
|
172
|
-
inRange:NSMakeRange(0, attributedText.length)
|
|
173
|
-
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
|
174
|
-
usingBlock:^(NSParagraphStyle *paragraphStyle, __unused NSRange range, __unused BOOL *stop) {
|
|
175
|
-
if (!paragraphStyle) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
maximumLineHeight = MAX(paragraphStyle.maximumLineHeight, maximumLineHeight);
|
|
180
|
-
}];
|
|
181
|
-
|
|
182
|
-
if (maximumLineHeight == 0) {
|
|
183
|
-
// `lineHeight` was not specified, nothing to do.
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
__block CGFloat maximumFontLineHeight = 0;
|
|
188
|
-
|
|
189
|
-
[attributedText enumerateAttribute:NSFontAttributeName
|
|
190
|
-
inRange:NSMakeRange(0, attributedText.length)
|
|
191
|
-
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
|
192
|
-
usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) {
|
|
193
|
-
if (!font) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
maximumFontLineHeight = MAX(font.lineHeight, maximumFontLineHeight);
|
|
198
|
-
}];
|
|
199
|
-
|
|
200
|
-
if (maximumLineHeight < maximumFontLineHeight) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
CGFloat baseLineOffset = (maximumLineHeight - maximumFontLineHeight) / 2.0;
|
|
205
|
-
[attributedText addAttribute:NSBaselineOffsetAttributeName
|
|
206
|
-
value:@(baseLineOffset)
|
|
207
|
-
range:NSMakeRange(0, attributedText.length)];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
@end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|