react-native-enriched 0.1.6 → 0.2.0
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/README.md +3 -9
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +1 -1
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +1 -1
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +0 -45
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +19 -2
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +3 -3
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +36 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +132 -11
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +13 -0
- package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +2 -9
- package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +110 -3
- package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +57 -30
- package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +16 -13
- package/android/src/main/java/com/swmansion/enriched/utils/ResourceManager.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +3 -0
- package/android/src/main/res/drawable/broken_image.xml +10 -0
- package/ios/EnrichedTextInputView.h +3 -0
- package/ios/EnrichedTextInputView.mm +97 -29
- package/ios/config/InputConfig.h +6 -0
- package/ios/config/InputConfig.mm +32 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +0 -45
- package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +20 -4
- package/ios/inputParser/InputParser.mm +147 -24
- package/ios/internals/EnrichedTextInputViewShadowNode.h +1 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +29 -17
- package/ios/styles/BlockQuoteStyle.mm +5 -26
- package/ios/styles/BoldStyle.mm +2 -0
- package/ios/styles/CodeBlockStyle.mm +228 -0
- package/ios/styles/H1Style.mm +1 -0
- package/ios/styles/H2Style.mm +1 -0
- package/ios/styles/H3Style.mm +1 -0
- package/ios/styles/ImageStyle.mm +158 -0
- package/ios/styles/InlineCodeStyle.mm +2 -0
- package/ios/styles/ItalicStyle.mm +2 -0
- package/ios/styles/LinkStyle.mm +8 -0
- package/ios/styles/MentionStyle.mm +133 -36
- package/ios/styles/OrderedListStyle.mm +2 -0
- package/ios/styles/StrikethroughStyle.mm +2 -0
- package/ios/styles/UnderlineStyle.mm +2 -0
- package/ios/styles/UnorderedListStyle.mm +2 -0
- package/ios/utils/BaseStyleProtocol.h +1 -0
- package/ios/utils/ImageData.h +10 -0
- package/ios/utils/ImageData.mm +4 -0
- package/ios/utils/LayoutManagerExtension.mm +118 -3
- package/ios/utils/OccurenceUtils.h +4 -0
- package/ios/utils/OccurenceUtils.mm +47 -0
- package/ios/utils/ParagraphAttributesUtils.h +1 -0
- package/ios/utils/ParagraphAttributesUtils.mm +87 -20
- package/ios/utils/StyleHeaders.h +12 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +22 -10
- package/lib/module/EnrichedTextInput.js +2 -2
- package/lib/module/EnrichedTextInput.js.map +1 -1
- package/lib/module/EnrichedTextInputNativeComponent.ts +6 -5
- package/lib/module/normalizeHtmlStyle.js +0 -4
- package/lib/module/normalizeHtmlStyle.js.map +1 -1
- package/lib/typescript/src/EnrichedTextInput.d.ts +1 -5
- package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +1 -5
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedTextInput.tsx +3 -7
- package/src/EnrichedTextInputNativeComponent.ts +6 -5
- package/src/normalizeHtmlStyle.ts +0 -4
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
UIColor *_linkColor;
|
|
37
37
|
TextDecorationLineEnum _linkDecorationLine;
|
|
38
38
|
NSDictionary *_mentionProperties;
|
|
39
|
+
UIColor *_codeBlockFgColor;
|
|
40
|
+
CGFloat _codeBlockBorderRadius;
|
|
41
|
+
UIColor *_codeBlockBgColor;
|
|
42
|
+
CGFloat _imageWidth;
|
|
43
|
+
CGFloat _imageHeight;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
- (instancetype) init {
|
|
@@ -79,6 +84,9 @@
|
|
|
79
84
|
copy->_linkColor = [_linkColor copy];
|
|
80
85
|
copy->_linkDecorationLine = [_linkDecorationLine copy];
|
|
81
86
|
copy->_mentionProperties = [_mentionProperties mutableCopy];
|
|
87
|
+
copy->_codeBlockFgColor = [_codeBlockFgColor copy];
|
|
88
|
+
copy->_codeBlockBgColor = [_codeBlockBgColor copy];
|
|
89
|
+
copy->_codeBlockBorderRadius = _codeBlockBorderRadius;
|
|
82
90
|
return copy;
|
|
83
91
|
}
|
|
84
92
|
|
|
@@ -379,4 +387,28 @@
|
|
|
379
387
|
return fallbackProps;
|
|
380
388
|
}
|
|
381
389
|
|
|
390
|
+
- (UIColor *)codeBlockFgColor {
|
|
391
|
+
return _codeBlockFgColor;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
- (void)setCodeBlockFgColor:(UIColor *)newValue {
|
|
395
|
+
_codeBlockFgColor = newValue;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
- (UIColor *)codeBlockBgColor {
|
|
399
|
+
return _codeBlockBgColor;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
- (void)setCodeBlockBgColor:(UIColor *)newValue {
|
|
403
|
+
_codeBlockBgColor = newValue;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
- (CGFloat)codeBlockBorderRadius {
|
|
407
|
+
return _codeBlockBorderRadius;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
- (void)setCodeBlockBorderRadius:(CGFloat)newValue {
|
|
411
|
+
_codeBlockBorderRadius = newValue;
|
|
412
|
+
}
|
|
413
|
+
|
|
382
414
|
@end
|
|
@@ -309,45 +309,6 @@ static inline folly::dynamic toDynamic(const EnrichedTextInputViewHtmlStyleAStru
|
|
|
309
309
|
}
|
|
310
310
|
#endif
|
|
311
311
|
|
|
312
|
-
struct EnrichedTextInputViewHtmlStyleImgStruct {
|
|
313
|
-
Float width{0.0};
|
|
314
|
-
Float height{0.0};
|
|
315
|
-
|
|
316
|
-
#ifdef RN_SERIALIZABLE_STATE
|
|
317
|
-
bool operator==(const EnrichedTextInputViewHtmlStyleImgStruct&) const = default;
|
|
318
|
-
|
|
319
|
-
folly::dynamic toDynamic() const {
|
|
320
|
-
folly::dynamic result = folly::dynamic::object();
|
|
321
|
-
result["width"] = width;
|
|
322
|
-
result["height"] = height;
|
|
323
|
-
return result;
|
|
324
|
-
}
|
|
325
|
-
#endif
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, EnrichedTextInputViewHtmlStyleImgStruct &result) {
|
|
329
|
-
auto map = (std::unordered_map<std::string, RawValue>)value;
|
|
330
|
-
|
|
331
|
-
auto tmp_width = map.find("width");
|
|
332
|
-
if (tmp_width != map.end()) {
|
|
333
|
-
fromRawValue(context, tmp_width->second, result.width);
|
|
334
|
-
}
|
|
335
|
-
auto tmp_height = map.find("height");
|
|
336
|
-
if (tmp_height != map.end()) {
|
|
337
|
-
fromRawValue(context, tmp_height->second, result.height);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
static inline std::string toString(const EnrichedTextInputViewHtmlStyleImgStruct &value) {
|
|
342
|
-
return "[Object EnrichedTextInputViewHtmlStyleImgStruct]";
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
#ifdef RN_SERIALIZABLE_STATE
|
|
346
|
-
static inline folly::dynamic toDynamic(const EnrichedTextInputViewHtmlStyleImgStruct &value) {
|
|
347
|
-
return value.toDynamic();
|
|
348
|
-
}
|
|
349
|
-
#endif
|
|
350
|
-
|
|
351
312
|
struct EnrichedTextInputViewHtmlStyleOlStruct {
|
|
352
313
|
Float gapWidth{0.0};
|
|
353
314
|
Float marginLeft{0.0};
|
|
@@ -459,7 +420,6 @@ struct EnrichedTextInputViewHtmlStyleStruct {
|
|
|
459
420
|
EnrichedTextInputViewHtmlStyleCodeStruct code{};
|
|
460
421
|
EnrichedTextInputViewHtmlStyleAStruct a{};
|
|
461
422
|
folly::dynamic mention{};
|
|
462
|
-
EnrichedTextInputViewHtmlStyleImgStruct img{};
|
|
463
423
|
EnrichedTextInputViewHtmlStyleOlStruct ol{};
|
|
464
424
|
EnrichedTextInputViewHtmlStyleUlStruct ul{};
|
|
465
425
|
|
|
@@ -476,7 +436,6 @@ struct EnrichedTextInputViewHtmlStyleStruct {
|
|
|
476
436
|
result["code"] = ::facebook::react::toDynamic(code);
|
|
477
437
|
result["a"] = ::facebook::react::toDynamic(a);
|
|
478
438
|
result["mention"] = mention;
|
|
479
|
-
result["img"] = ::facebook::react::toDynamic(img);
|
|
480
439
|
result["ol"] = ::facebook::react::toDynamic(ol);
|
|
481
440
|
result["ul"] = ::facebook::react::toDynamic(ul);
|
|
482
441
|
return result;
|
|
@@ -519,10 +478,6 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
519
478
|
if (tmp_mention != map.end()) {
|
|
520
479
|
fromRawValue(context, tmp_mention->second, result.mention);
|
|
521
480
|
}
|
|
522
|
-
auto tmp_img = map.find("img");
|
|
523
|
-
if (tmp_img != map.end()) {
|
|
524
|
-
fromRawValue(context, tmp_img->second, result.img);
|
|
525
|
-
}
|
|
526
481
|
auto tmp_ol = map.find("ol");
|
|
527
482
|
if (tmp_ol != map.end()) {
|
|
528
483
|
fromRawValue(context, tmp_ol->second, result.ol);
|
|
@@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
30
30
|
- (void)toggleOrderedList;
|
|
31
31
|
- (void)toggleUnorderedList;
|
|
32
32
|
- (void)addLink:(NSInteger)start end:(NSInteger)end text:(NSString *)text url:(NSString *)url;
|
|
33
|
-
- (void)addImage:(NSString *)uri;
|
|
33
|
+
- (void)addImage:(NSString *)uri width:(float)width height:(float)height;
|
|
34
34
|
- (void)startMention:(NSString *)indicator;
|
|
35
35
|
- (void)addMention:(NSString *)indicator text:(NSString *)text payload:(NSString *)payload;
|
|
36
36
|
@end
|
|
@@ -302,8 +302,8 @@ NSObject *arg3 = args[3];
|
|
|
302
302
|
|
|
303
303
|
if ([commandName isEqualToString:@"addImage"]) {
|
|
304
304
|
#if RCT_DEBUG
|
|
305
|
-
if ([args count] !=
|
|
306
|
-
RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"EnrichedTextInputView", commandName, (int)[args count],
|
|
305
|
+
if ([args count] != 3) {
|
|
306
|
+
RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"EnrichedTextInputView", commandName, (int)[args count], 3);
|
|
307
307
|
return;
|
|
308
308
|
}
|
|
309
309
|
#endif
|
|
@@ -316,7 +316,23 @@ if ([commandName isEqualToString:@"addImage"]) {
|
|
|
316
316
|
#endif
|
|
317
317
|
NSString * uri = (NSString *)arg0;
|
|
318
318
|
|
|
319
|
-
|
|
319
|
+
NSObject *arg1 = args[1];
|
|
320
|
+
#if RCT_DEBUG
|
|
321
|
+
if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @"float", @"EnrichedTextInputView", commandName, @"2nd")) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
#endif
|
|
325
|
+
float width = [(NSNumber *)arg1 floatValue];
|
|
326
|
+
|
|
327
|
+
NSObject *arg2 = args[2];
|
|
328
|
+
#if RCT_DEBUG
|
|
329
|
+
if (!RCTValidateTypeOfViewCommandArgument(arg2, [NSNumber class], @"float", @"EnrichedTextInputView", commandName, @"3rd")) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
#endif
|
|
333
|
+
float height = [(NSNumber *)arg2 floatValue];
|
|
334
|
+
|
|
335
|
+
[componentView addImage:uri width:width height:height];
|
|
320
336
|
return;
|
|
321
337
|
}
|
|
322
338
|
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
BOOL inUnorderedList = NO;
|
|
30
30
|
BOOL inOrderedList = NO;
|
|
31
31
|
BOOL inBlockQuote = NO;
|
|
32
|
+
BOOL inCodeBlock = NO;
|
|
32
33
|
unichar lastCharacter = 0;
|
|
33
34
|
|
|
34
35
|
for(int i = 0; i < text.length; i++) {
|
|
@@ -85,6 +86,9 @@
|
|
|
85
86
|
|
|
86
87
|
// append closing tags
|
|
87
88
|
for(NSNumber *style in sortedEndedStyles) {
|
|
89
|
+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
88
92
|
NSString *tagContent = [self tagContentForStyle:style openingTag:NO location:currentRange.location];
|
|
89
93
|
[result appendString: [NSString stringWithFormat:@"</%@>", tagContent]];
|
|
90
94
|
}
|
|
@@ -95,7 +99,8 @@
|
|
|
95
99
|
[previousActiveStyles containsObject:@([H1Style getStyleType])] ||
|
|
96
100
|
[previousActiveStyles containsObject:@([H2Style getStyleType])] ||
|
|
97
101
|
[previousActiveStyles containsObject:@([H3Style getStyleType])] ||
|
|
98
|
-
[previousActiveStyles containsObject:@([BlockQuoteStyle getStyleType])]
|
|
102
|
+
[previousActiveStyles containsObject:@([BlockQuoteStyle getStyleType])] ||
|
|
103
|
+
[previousActiveStyles containsObject:@([CodeBlockStyle getStyleType])]
|
|
99
104
|
) {
|
|
100
105
|
// do nothing, proper closing paragraph tags have been already appended
|
|
101
106
|
} else {
|
|
@@ -128,6 +133,11 @@
|
|
|
128
133
|
inBlockQuote = NO;
|
|
129
134
|
[result appendString:@"\n</blockquote>"];
|
|
130
135
|
}
|
|
136
|
+
// handle ending codeblock
|
|
137
|
+
if(inCodeBlock && ![currentActiveStyles containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
138
|
+
inCodeBlock = NO;
|
|
139
|
+
[result appendString:@"\n</codeblock>"];
|
|
140
|
+
}
|
|
131
141
|
|
|
132
142
|
// handle starting unordered list
|
|
133
143
|
if(!inUnorderedList && [currentActiveStyles containsObject:@([UnorderedListStyle getStyleType])]) {
|
|
@@ -144,6 +154,11 @@
|
|
|
144
154
|
inBlockQuote = YES;
|
|
145
155
|
[result appendString:@"\n<blockquote>"];
|
|
146
156
|
}
|
|
157
|
+
// handle starting codeblock
|
|
158
|
+
if(!inCodeBlock && [currentActiveStyles containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
159
|
+
inCodeBlock = YES;
|
|
160
|
+
[result appendString:@"\n<codeblock>"];
|
|
161
|
+
}
|
|
147
162
|
|
|
148
163
|
// don't add the <p> tag if some paragraph styles are present
|
|
149
164
|
if([currentActiveStyles containsObject:@([UnorderedListStyle getStyleType])] ||
|
|
@@ -151,7 +166,8 @@
|
|
|
151
166
|
[currentActiveStyles containsObject:@([H1Style getStyleType])] ||
|
|
152
167
|
[currentActiveStyles containsObject:@([H2Style getStyleType])] ||
|
|
153
168
|
[currentActiveStyles containsObject:@([H3Style getStyleType])] ||
|
|
154
|
-
[currentActiveStyles containsObject:@([BlockQuoteStyle getStyleType])]
|
|
169
|
+
[currentActiveStyles containsObject:@([BlockQuoteStyle getStyleType])] ||
|
|
170
|
+
[currentActiveStyles containsObject:@([CodeBlockStyle getStyleType])]
|
|
155
171
|
) {
|
|
156
172
|
[result appendString:@"\n"];
|
|
157
173
|
} else {
|
|
@@ -184,25 +200,51 @@
|
|
|
184
200
|
}
|
|
185
201
|
}
|
|
186
202
|
|
|
203
|
+
// if a style begins but there is a style inner to it that is (and was previously) active, it also should be closed and readded
|
|
204
|
+
|
|
205
|
+
// newly added styles
|
|
206
|
+
NSMutableSet *newStyles = [currentActiveStyles mutableCopy];
|
|
207
|
+
[newStyles minusSet: previousActiveStyles];
|
|
208
|
+
// styles that were and still are active
|
|
209
|
+
NSMutableSet *stillActiveStyles = [previousActiveStyles mutableCopy];
|
|
210
|
+
[stillActiveStyles intersectSet:currentActiveStyles];
|
|
211
|
+
|
|
212
|
+
for(NSNumber *style in newStyles) {
|
|
213
|
+
for(NSNumber *ongoingStyle in stillActiveStyles) {
|
|
214
|
+
if([ongoingStyle integerValue] > [style integerValue]) {
|
|
215
|
+
// the prev style is inner; needs to be closed and re-added later
|
|
216
|
+
[fixedEndedStyles addObject:ongoingStyle];
|
|
217
|
+
[stylesToBeReAdded addObject:ongoingStyle];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
187
222
|
// they are sorted in a descending order
|
|
188
223
|
NSArray<NSNumber*> *sortedEndedStyles = [fixedEndedStyles sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"intValue" ascending:NO]]];
|
|
189
224
|
|
|
190
225
|
// append closing tags
|
|
191
226
|
for(NSNumber *style in sortedEndedStyles) {
|
|
227
|
+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
192
230
|
NSString *tagContent = [self tagContentForStyle:style openingTag:NO location:currentRange.location];
|
|
193
231
|
[result appendString: [NSString stringWithFormat:@"</%@>", tagContent]];
|
|
194
232
|
}
|
|
195
233
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
[newStyles minusSet: previousActiveStyles];
|
|
234
|
+
// all styles that have begun: new styles + the ones that need to be re-added
|
|
235
|
+
// they are sorted in a ascending manner to properly keep tags' FILO order
|
|
199
236
|
[newStyles unionSet: stylesToBeReAdded];
|
|
200
237
|
NSArray<NSNumber*> *sortedNewStyles = [newStyles sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"intValue" ascending:YES]]];
|
|
201
238
|
|
|
202
239
|
// append opening tags
|
|
203
240
|
for(NSNumber *style in sortedNewStyles) {
|
|
204
241
|
NSString *tagContent = [self tagContentForStyle:style openingTag:YES location:currentRange.location];
|
|
205
|
-
[
|
|
242
|
+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
|
|
243
|
+
[result appendString: [NSString stringWithFormat:@"<%@/>", tagContent]];
|
|
244
|
+
[currentActiveStyles removeObject:@([ImageStyle getStyleType])];
|
|
245
|
+
} else {
|
|
246
|
+
[result appendString: [NSString stringWithFormat:@"<%@>", tagContent]];
|
|
247
|
+
}
|
|
206
248
|
}
|
|
207
249
|
|
|
208
250
|
// append the letter and escape it if needed
|
|
@@ -223,6 +265,9 @@
|
|
|
223
265
|
|
|
224
266
|
// append closing tags
|
|
225
267
|
for(NSNumber *style in sortedEndedStyles) {
|
|
268
|
+
if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
226
271
|
NSString *tagContent = [self tagContentForStyle:style openingTag:NO location:_input->textView.textStorage.string.length - 1];
|
|
227
272
|
[result appendString: [NSString stringWithFormat:@"</%@>", tagContent]];
|
|
228
273
|
}
|
|
@@ -235,6 +280,8 @@
|
|
|
235
280
|
[result appendString:@"\n</ol>"];
|
|
236
281
|
} else if([previousActiveStyles containsObject:@([BlockQuoteStyle getStyleType])]) {
|
|
237
282
|
[result appendString:@"\n</blockquote>"];
|
|
283
|
+
} else if([previousActiveStyles containsObject:@([CodeBlockStyle getStyleType])]) {
|
|
284
|
+
[result appendString:@"\n</codeblock>"];
|
|
238
285
|
} else if(
|
|
239
286
|
[previousActiveStyles containsObject:@([H1Style getStyleType])] ||
|
|
240
287
|
[previousActiveStyles containsObject:@([H2Style getStyleType])] ||
|
|
@@ -258,6 +305,10 @@
|
|
|
258
305
|
inBlockQuote = NO;
|
|
259
306
|
[result appendString:@"\n</blockquote>"];
|
|
260
307
|
}
|
|
308
|
+
if(inCodeBlock) {
|
|
309
|
+
inCodeBlock = NO;
|
|
310
|
+
[result appendString:@"\n</codeblock>"];
|
|
311
|
+
}
|
|
261
312
|
}
|
|
262
313
|
|
|
263
314
|
[result appendString: @"\n</html>"];
|
|
@@ -273,6 +324,19 @@
|
|
|
273
324
|
return @"b";
|
|
274
325
|
} else if([style isEqualToNumber: @([ItalicStyle getStyleType])]) {
|
|
275
326
|
return @"i";
|
|
327
|
+
} else if ([style isEqualToNumber: @([ImageStyle getStyleType])]) {
|
|
328
|
+
if(openingTag) {
|
|
329
|
+
ImageStyle *imageStyle = (ImageStyle *)_input->stylesDict[@([ImageStyle getStyleType])];
|
|
330
|
+
if(imageStyle != nullptr) {
|
|
331
|
+
ImageData *data = [imageStyle getImageDataAt:location];
|
|
332
|
+
if(data != nullptr && data.uri != nullptr) {
|
|
333
|
+
return [NSString stringWithFormat:@"img src=\"%@\" width=\"%f\" height=\"%f\"", data.uri, data.width, data.height];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return @"img";
|
|
337
|
+
} else {
|
|
338
|
+
return @"";
|
|
339
|
+
}
|
|
276
340
|
} else if([style isEqualToNumber: @([UnderlineStyle getStyleType])]) {
|
|
277
341
|
return @"u";
|
|
278
342
|
} else if([style isEqualToNumber: @([StrikethroughStyle getStyleType])]) {
|
|
@@ -328,8 +392,8 @@
|
|
|
328
392
|
return @"h3";
|
|
329
393
|
} else if([style isEqualToNumber:@([UnorderedListStyle getStyleType])] || [style isEqualToNumber:@([OrderedListStyle getStyleType])]) {
|
|
330
394
|
return @"li";
|
|
331
|
-
} else if([style isEqualToNumber:@([BlockQuoteStyle getStyleType])]) {
|
|
332
|
-
// blockquotes use <p> tags the same way lists use <li>
|
|
395
|
+
} else if([style isEqualToNumber:@([BlockQuoteStyle getStyleType])] || [style isEqualToNumber:@([CodeBlockStyle getStyleType])]) {
|
|
396
|
+
// blockquotes and codeblock use <p> tags the same way lists use <li>
|
|
333
397
|
return @"p";
|
|
334
398
|
}
|
|
335
399
|
return @"";
|
|
@@ -393,6 +457,9 @@
|
|
|
393
457
|
} else if([styleType isEqualToNumber: @([MentionStyle getStyleType])]) {
|
|
394
458
|
MentionParams *params = (MentionParams *)stylePair.styleValue;
|
|
395
459
|
[((MentionStyle *)baseStyle) addMentionAtRange:styleRange params:params];
|
|
460
|
+
} else if([styleType isEqualToNumber: @([ImageStyle getStyleType])]) {
|
|
461
|
+
ImageData *imgData = (ImageData *)stylePair.styleValue;
|
|
462
|
+
[((ImageStyle *)baseStyle) addImageAtRange:styleRange imageData:imgData withSelection:NO];
|
|
396
463
|
} else {
|
|
397
464
|
[baseStyle addAttributes:styleRange];
|
|
398
465
|
}
|
|
@@ -447,6 +514,8 @@
|
|
|
447
514
|
fixedHtml = [self stringByAddingNewlinesToTag:@"</ol>" inString:fixedHtml leading:YES trailing:YES];
|
|
448
515
|
fixedHtml = [self stringByAddingNewlinesToTag:@"<blockquote>" inString:fixedHtml leading:YES trailing:YES];
|
|
449
516
|
fixedHtml = [self stringByAddingNewlinesToTag:@"</blockquote>" inString:fixedHtml leading:YES trailing:YES];
|
|
517
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"<codeblock>" inString:fixedHtml leading:YES trailing:YES];
|
|
518
|
+
fixedHtml = [self stringByAddingNewlinesToTag:@"</codeblock>" inString:fixedHtml leading:YES trailing:YES];
|
|
450
519
|
|
|
451
520
|
// line opening tags
|
|
452
521
|
fixedHtml = [self stringByAddingNewlinesToTag:@"<p>" inString:fixedHtml leading:YES trailing:NO];
|
|
@@ -481,6 +550,24 @@
|
|
|
481
550
|
return str;
|
|
482
551
|
}
|
|
483
552
|
|
|
553
|
+
- (void)finalizeTagEntry:(NSMutableString *)tagName ongoingTags:(NSMutableDictionary *)ongoingTags initiallyProcessedTags:(NSMutableArray *)processedTags plainText:(NSMutableString *)plainText
|
|
554
|
+
{
|
|
555
|
+
NSMutableArray *tagEntry = [[NSMutableArray alloc] init];
|
|
556
|
+
|
|
557
|
+
NSArray *tagData = ongoingTags[tagName];
|
|
558
|
+
NSInteger tagLocation = [((NSNumber *)tagData[0]) intValue];
|
|
559
|
+
NSRange tagRange = NSMakeRange(tagLocation, plainText.length - tagLocation);
|
|
560
|
+
|
|
561
|
+
[tagEntry addObject:[tagName copy]];
|
|
562
|
+
[tagEntry addObject:[NSValue valueWithRange:tagRange]];
|
|
563
|
+
if(tagData.count > 1) {
|
|
564
|
+
[tagEntry addObject:[(NSString *)tagData[1] copy]];
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
[processedTags addObject:tagEntry];
|
|
568
|
+
[ongoingTags removeObjectForKey:tagName];
|
|
569
|
+
}
|
|
570
|
+
|
|
484
571
|
- (NSArray *)getTextAndStylesFromHtml:(NSString *)fixedHtml {
|
|
485
572
|
NSMutableString *plainText = [[NSMutableString alloc] initWithString: @""];
|
|
486
573
|
NSMutableDictionary *ongoingTags = [[NSMutableDictionary alloc] init];
|
|
@@ -508,6 +595,14 @@
|
|
|
508
595
|
gettingTagName = NO;
|
|
509
596
|
gettingTagParams = NO;
|
|
510
597
|
|
|
598
|
+
BOOL isSelfClosing = NO;
|
|
599
|
+
|
|
600
|
+
// Check if params ended with '/' (e.g. <img src="" />)
|
|
601
|
+
if ([currentTagParams hasSuffix:@"/"]) {
|
|
602
|
+
[currentTagParams deleteCharactersInRange:NSMakeRange(currentTagParams.length - 1, 1)];
|
|
603
|
+
isSelfClosing = YES;
|
|
604
|
+
}
|
|
605
|
+
|
|
511
606
|
if([currentTagName isEqualToString:@"p"] || [currentTagName isEqualToString:@"br"] || [currentTagName isEqualToString:@"li"]) {
|
|
512
607
|
// do nothing, we don't include these tags in styles
|
|
513
608
|
} else if(!closingTag) {
|
|
@@ -520,31 +615,22 @@
|
|
|
520
615
|
ongoingTags[currentTagName] = tagArr;
|
|
521
616
|
|
|
522
617
|
// skip one newline after opening tags that are in separate lines intentionally
|
|
523
|
-
if([currentTagName isEqualToString:@"ul"] || [currentTagName isEqualToString:@"ol"] || [currentTagName isEqualToString:@"blockquote"]) {
|
|
618
|
+
if([currentTagName isEqualToString:@"ul"] || [currentTagName isEqualToString:@"ol"] || [currentTagName isEqualToString:@"blockquote"] || [currentTagName isEqualToString:@"codeblock"]) {
|
|
524
619
|
i += 1;
|
|
525
620
|
}
|
|
621
|
+
|
|
622
|
+
if (isSelfClosing) {
|
|
623
|
+
[self finalizeTagEntry:currentTagName ongoingTags:ongoingTags initiallyProcessedTags:initiallyProcessedTags plainText:plainText];
|
|
624
|
+
}
|
|
526
625
|
} else {
|
|
527
626
|
// we finish closing tags - pack tag name, tag range and optionally tag params into an entry that goes inside initiallyProcessedTags
|
|
528
627
|
|
|
529
628
|
// skip one newline that was added before some closing tags that are in separate lines
|
|
530
|
-
if([currentTagName isEqualToString:@"ul"] || [currentTagName isEqualToString:@"ol"] || [currentTagName isEqualToString:@"blockquote"]) {
|
|
629
|
+
if([currentTagName isEqualToString:@"ul"] || [currentTagName isEqualToString:@"ol"] || [currentTagName isEqualToString:@"blockquote"] || [currentTagName isEqualToString:@"codeblock"]) {
|
|
531
630
|
plainText = [[plainText substringWithRange: NSMakeRange(0, plainText.length - 1)] mutableCopy];
|
|
532
631
|
}
|
|
533
632
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
NSArray *tagData = ongoingTags[currentTagName];
|
|
537
|
-
NSInteger tagLocation = [((NSNumber *)tagData[0]) intValue];
|
|
538
|
-
NSRange tagRange = NSMakeRange(tagLocation, plainText.length - tagLocation);
|
|
539
|
-
|
|
540
|
-
[tagEntry addObject:[currentTagName copy]];
|
|
541
|
-
[tagEntry addObject:[NSValue valueWithRange:tagRange]];
|
|
542
|
-
if(tagData.count > 1) {
|
|
543
|
-
[tagEntry addObject:[(NSString *)tagData[1] copy]];
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
[initiallyProcessedTags addObject:tagEntry];
|
|
547
|
-
[ongoingTags removeObjectForKey:currentTagName];
|
|
633
|
+
[self finalizeTagEntry:currentTagName ongoingTags:ongoingTags initiallyProcessedTags:initiallyProcessedTags plainText:plainText];
|
|
548
634
|
}
|
|
549
635
|
// post-tag cleanup
|
|
550
636
|
closingTag = NO;
|
|
@@ -603,6 +689,41 @@
|
|
|
603
689
|
[styleArr addObject:@([BoldStyle getStyleType])];
|
|
604
690
|
} else if([tagName isEqualToString:@"i"]) {
|
|
605
691
|
[styleArr addObject:@([ItalicStyle getStyleType])];
|
|
692
|
+
} else if([tagName isEqualToString:@"img"]) {
|
|
693
|
+
NSRegularExpression *srcRegex = [NSRegularExpression regularExpressionWithPattern:@"src=\"([^\"]+)\""
|
|
694
|
+
options:0
|
|
695
|
+
error:nullptr
|
|
696
|
+
];
|
|
697
|
+
NSTextCheckingResult* match = [srcRegex firstMatchInString:params options:0 range: NSMakeRange(0, params.length)];
|
|
698
|
+
|
|
699
|
+
if(match == nullptr) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
NSRange srcRange = match.range;
|
|
704
|
+
[styleArr addObject:@([ImageStyle getStyleType])];
|
|
705
|
+
// cut only the uri from the src="..." string
|
|
706
|
+
NSString *uri = [params substringWithRange:NSMakeRange(srcRange.location + 5, srcRange.length - 6)];
|
|
707
|
+
ImageData *imageData = [[ImageData alloc] init];
|
|
708
|
+
imageData.uri = uri;
|
|
709
|
+
|
|
710
|
+
NSRegularExpression *widthRegex = [NSRegularExpression regularExpressionWithPattern:@"width=\"([0-9.]+)\"" options:0 error:nil];
|
|
711
|
+
NSTextCheckingResult *widthMatch = [widthRegex firstMatchInString:params options:0 range:NSMakeRange(0, params.length)];
|
|
712
|
+
|
|
713
|
+
if (widthMatch) {
|
|
714
|
+
NSString *widthString = [params substringWithRange:[widthMatch rangeAtIndex:1]];
|
|
715
|
+
imageData.width = [widthString floatValue];
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
NSRegularExpression *heightRegex = [NSRegularExpression regularExpressionWithPattern:@"height=\"([0-9.]+)\"" options:0 error:nil];
|
|
719
|
+
NSTextCheckingResult *heightMatch = [heightRegex firstMatchInString:params options:0 range:NSMakeRange(0, params.length)];
|
|
720
|
+
|
|
721
|
+
if (heightMatch) {
|
|
722
|
+
NSString *heightString = [params substringWithRange:[heightMatch rangeAtIndex:1]];
|
|
723
|
+
imageData.height = [heightString floatValue];
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
stylePair.styleValue = imageData;
|
|
606
727
|
} else if([tagName isEqualToString:@"u"]) {
|
|
607
728
|
[styleArr addObject:@([UnderlineStyle getStyleType])];
|
|
608
729
|
} else if([tagName isEqualToString:@"s"]) {
|
|
@@ -668,6 +789,8 @@
|
|
|
668
789
|
[styleArr addObject:@([OrderedListStyle getStyleType])];
|
|
669
790
|
} else if([tagName isEqualToString:@"blockquote"]) {
|
|
670
791
|
[styleArr addObject:@([BlockQuoteStyle getStyleType])];
|
|
792
|
+
} else if([tagName isEqualToString:@"codeblock"]) {
|
|
793
|
+
[styleArr addObject:@([CodeBlockStyle getStyleType])];
|
|
671
794
|
} else {
|
|
672
795
|
// some other external tags like span just don't get put into the processed styles
|
|
673
796
|
continue;
|
|
@@ -17,6 +17,18 @@ EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode(
|
|
|
17
17
|
localForceHeightRecalculationCounter_ = 0;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// mock input is used for the first measure calls that need to be done when the real input isn't defined yet
|
|
21
|
+
id EnrichedTextInputViewShadowNode::setupMockTextInputView_() const {
|
|
22
|
+
// it's rendered far away from the viewport
|
|
23
|
+
const int veryFarAway = 20000;
|
|
24
|
+
const int mockSize = 1000;
|
|
25
|
+
EnrichedTextInputView *mockTextInputView_ = [[EnrichedTextInputView alloc] initWithFrame:(CGRectMake(veryFarAway, veryFarAway, mockSize, mockSize))];
|
|
26
|
+
const auto props = this->getProps();
|
|
27
|
+
mockTextInputView_->blockEmitting = YES;
|
|
28
|
+
[mockTextInputView_ updateProps:props oldProps:nullptr];
|
|
29
|
+
return mockTextInputView_;
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode(
|
|
21
33
|
const ShadowNode& sourceShadowNode,
|
|
22
34
|
const ShadowNodeFragment& fragment
|
|
@@ -61,23 +73,23 @@ Size EnrichedTextInputViewShadowNode::measureContent(const LayoutContext& layout
|
|
|
61
73
|
};
|
|
62
74
|
}
|
|
63
75
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
__block CGSize estimatedSize;
|
|
77
|
+
|
|
78
|
+
// synchronously dispatch to main thread if needed
|
|
79
|
+
if([NSThread isMainThread]) {
|
|
80
|
+
EnrichedTextInputView *mockTextInputView = setupMockTextInputView_();
|
|
81
|
+
estimatedSize = [mockTextInputView measureSize:layoutConstraints.maximumSize.width];
|
|
82
|
+
} else {
|
|
83
|
+
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
84
|
+
EnrichedTextInputView *mockTextInputView = setupMockTextInputView_();
|
|
85
|
+
estimatedSize = [mockTextInputView measureSize:layoutConstraints.maximumSize.width];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
estimatedSize.width,
|
|
91
|
+
MIN(estimatedSize.height, layoutConstraints.maximumSize.height)
|
|
92
|
+
};
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
return Size();
|
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
|
|
8
8
|
@implementation BlockQuoteStyle {
|
|
9
9
|
EnrichedTextInputView *_input;
|
|
10
|
+
NSArray *_stylesToExclude;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
+ (StyleType)getStyleType { return BlockQuote; }
|
|
13
14
|
|
|
15
|
+
+ (BOOL)isParagraphStyle { return YES; }
|
|
16
|
+
|
|
14
17
|
- (instancetype)initWithInput:(id)input {
|
|
15
18
|
self = [super init];
|
|
16
19
|
_input = (EnrichedTextInputView *)input;
|
|
20
|
+
_stylesToExclude = @[ @(InlineCode), @(Mention), @(Link) ];
|
|
17
21
|
return self;
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -175,31 +179,6 @@
|
|
|
175
179
|
];
|
|
176
180
|
}
|
|
177
181
|
|
|
178
|
-
// gets ranges that aren't link, mention or inline code
|
|
179
|
-
- (NSArray *)getProperColorRangesIn:(NSRange)range {
|
|
180
|
-
LinkStyle *linkStyle = _input->stylesDict[@([LinkStyle getStyleType])];
|
|
181
|
-
MentionStyle *mentionStyle = _input->stylesDict[@([MentionStyle getStyleType])];
|
|
182
|
-
InlineCodeStyle *codeStyle = _input->stylesDict[@([InlineCodeStyle getStyleType])];
|
|
183
|
-
|
|
184
|
-
NSMutableArray *newRanges = [[NSMutableArray alloc] init];
|
|
185
|
-
int lastRangeLocation = range.location;
|
|
186
|
-
|
|
187
|
-
for(int i = range.location; i < range.location + range.length; i++) {
|
|
188
|
-
NSRange currentRange = NSMakeRange(i, 1);
|
|
189
|
-
if([linkStyle detectStyle:currentRange] || [mentionStyle detectStyle:currentRange] || [codeStyle detectStyle:currentRange]) {
|
|
190
|
-
if(i - lastRangeLocation > 0) {
|
|
191
|
-
[newRanges addObject:[NSValue valueWithRange:NSMakeRange(lastRangeLocation, i - lastRangeLocation)]];
|
|
192
|
-
}
|
|
193
|
-
lastRangeLocation = i+1;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if(lastRangeLocation < range.location + range.length) {
|
|
197
|
-
[newRanges addObject:[NSValue valueWithRange:NSMakeRange(lastRangeLocation, range.location + range.length - lastRangeLocation)]];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return newRanges;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
182
|
// general checkup correcting blockquote color
|
|
204
183
|
// since links, mentions and inline code affects coloring, the checkup gets done only outside of them
|
|
205
184
|
- (void)manageBlockquoteColor {
|
|
@@ -212,7 +191,7 @@
|
|
|
212
191
|
NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:_input->textView range:wholeRange];
|
|
213
192
|
for(NSValue *pValue in paragraphs) {
|
|
214
193
|
NSRange paragraphRange = [pValue rangeValue];
|
|
215
|
-
NSArray *properRanges = [
|
|
194
|
+
NSArray *properRanges = [OccurenceUtils getRangesWithout:_stylesToExclude withInput:_input inRange:paragraphRange];
|
|
216
195
|
|
|
217
196
|
for(NSValue *value in properRanges) {
|
|
218
197
|
NSRange currRange = [value rangeValue];
|