react-native-enriched-markdown 0.1.0 → 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/LICENSE +20 -0
- package/README.md +551 -0
- package/ReactNativeEnrichedMarkdown.podspec +27 -0
- package/android/build.gradle +101 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +54 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +26 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +33 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +31 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +82 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1388 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/baseline-prof.txt +65 -0
- package/android/src/main/cpp/jni-adapter.cpp +220 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +270 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +173 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextPackage.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +385 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +279 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +31 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +166 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +84 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +104 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +70 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +68 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +105 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +59 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +103 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +80 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +109 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +86 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrikethroughRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +45 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/UnderlineRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +136 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +135 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +180 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +196 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +321 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +87 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeStyle.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/EmphasisStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrongStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +211 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +92 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +827 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/LinkLongPressMovementMethod.kt +121 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +375 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/SelectionActionMode.kt +139 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +183 -0
- package/android/src/main/jni/CMakeLists.txt +70 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.cpp +21 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.h +25 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +20 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +37 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +22 -0
- package/cpp/md4c/md4c.c +6492 -0
- package/cpp/md4c/md4c.h +402 -0
- package/cpp/parser/MD4CParser.cpp +327 -0
- package/cpp/parser/MD4CParser.hpp +27 -0
- package/cpp/parser/MarkdownASTNode.hpp +51 -0
- package/ios/EnrichedMarkdownText.h +18 -0
- package/ios/EnrichedMarkdownText.mm +1401 -0
- package/ios/attachments/EnrichedMarkdownImageAttachment.h +23 -0
- package/ios/attachments/EnrichedMarkdownImageAttachment.m +185 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +31 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +82 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.h +1388 -0
- package/ios/generated/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +20 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.h +20 -0
- package/ios/internals/EnrichedMarkdownTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.h +43 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.mm +85 -0
- package/ios/internals/EnrichedMarkdownTextState.h +24 -0
- package/ios/parser/MarkdownASTNode.h +35 -0
- package/ios/parser/MarkdownASTNode.m +32 -0
- package/ios/parser/MarkdownParser.h +17 -0
- package/ios/parser/MarkdownParser.mm +42 -0
- package/ios/parser/MarkdownParserBridge.mm +120 -0
- package/ios/renderer/AttributedRenderer.h +11 -0
- package/ios/renderer/AttributedRenderer.m +152 -0
- package/ios/renderer/BlockquoteRenderer.h +7 -0
- package/ios/renderer/BlockquoteRenderer.m +160 -0
- package/ios/renderer/CodeBlockRenderer.h +10 -0
- package/ios/renderer/CodeBlockRenderer.m +90 -0
- package/ios/renderer/CodeRenderer.h +10 -0
- package/ios/renderer/CodeRenderer.m +60 -0
- package/ios/renderer/EmphasisRenderer.h +6 -0
- package/ios/renderer/EmphasisRenderer.m +96 -0
- package/ios/renderer/HeadingRenderer.h +7 -0
- package/ios/renderer/HeadingRenderer.m +105 -0
- package/ios/renderer/ImageRenderer.h +12 -0
- package/ios/renderer/ImageRenderer.m +83 -0
- package/ios/renderer/LinkRenderer.h +7 -0
- package/ios/renderer/LinkRenderer.m +69 -0
- package/ios/renderer/ListItemRenderer.h +16 -0
- package/ios/renderer/ListItemRenderer.m +103 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +70 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +7 -0
- package/ios/renderer/ParagraphRenderer.m +80 -0
- package/ios/renderer/RenderContext.h +105 -0
- package/ios/renderer/RenderContext.m +312 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +116 -0
- package/ios/renderer/StrikethroughRenderer.h +6 -0
- package/ios/renderer/StrikethroughRenderer.m +40 -0
- package/ios/renderer/StrongRenderer.h +6 -0
- package/ios/renderer/StrongRenderer.m +83 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +16 -0
- package/ios/renderer/ThematicBreakRenderer.h +5 -0
- package/ios/renderer/ThematicBreakRenderer.m +53 -0
- package/ios/renderer/UnderlineRenderer.h +6 -0
- package/ios/renderer/UnderlineRenderer.m +39 -0
- package/ios/styles/StyleConfig.h +274 -0
- package/ios/styles/StyleConfig.mm +1806 -0
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/BlockquoteBorder.h +20 -0
- package/ios/utils/BlockquoteBorder.m +92 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +191 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +82 -0
- package/ios/utils/EditMenuUtils.h +22 -0
- package/ios/utils/EditMenuUtils.m +118 -0
- package/ios/utils/FontUtils.h +25 -0
- package/ios/utils/FontUtils.m +27 -0
- package/ios/utils/HTMLGenerator.h +20 -0
- package/ios/utils/HTMLGenerator.m +793 -0
- package/ios/utils/LastElementUtils.h +53 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +127 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.h +45 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +323 -0
- package/ios/utils/MarkdownExtractor.h +17 -0
- package/ios/utils/MarkdownExtractor.m +308 -0
- package/ios/utils/ParagraphStyleUtils.h +21 -0
- package/ios/utils/ParagraphStyleUtils.m +111 -0
- package/ios/utils/PasteboardUtils.h +36 -0
- package/ios/utils/PasteboardUtils.m +134 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +297 -0
- package/ios/utils/RuntimeKeys.h +38 -0
- package/ios/utils/RuntimeKeys.m +11 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +113 -0
- package/lib/module/EnrichedMarkdownText.js +65 -0
- package/lib/module/EnrichedMarkdownText.js.map +1 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +210 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/normalizeMarkdownStyle.js +384 -0
- package/lib/module/normalizeMarkdownStyle.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts +183 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +185 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts +6 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -0
- package/package.json +186 -1
- package/react-native.config.js +13 -0
- package/src/EnrichedMarkdownText.tsx +280 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +210 -0
- package/src/index.tsx +10 -0
- package/src/normalizeMarkdownStyle.ts +423 -0
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
#import "EnrichedMarkdownText.h"
|
|
2
|
+
#import "AccessibilityInfo.h"
|
|
3
|
+
#import "AttributedRenderer.h"
|
|
4
|
+
#import "CodeBlockBackground.h"
|
|
5
|
+
#import "EditMenuUtils.h"
|
|
6
|
+
#import "EnrichedMarkdownImageAttachment.h"
|
|
7
|
+
#import "FontUtils.h"
|
|
8
|
+
#import "LastElementUtils.h"
|
|
9
|
+
#import "MarkdownASTNode.h"
|
|
10
|
+
#import "MarkdownAccessibilityElementBuilder.h"
|
|
11
|
+
#import "MarkdownExtractor.h"
|
|
12
|
+
#import "MarkdownParser.h"
|
|
13
|
+
#import "ParagraphStyleUtils.h"
|
|
14
|
+
#import "RenderContext.h"
|
|
15
|
+
#import "RuntimeKeys.h"
|
|
16
|
+
#import "StyleConfig.h"
|
|
17
|
+
#import "TextViewLayoutManager.h"
|
|
18
|
+
#import <React/RCTUtils.h>
|
|
19
|
+
#import <objc/runtime.h>
|
|
20
|
+
|
|
21
|
+
#import <ReactNativeEnrichedMarkdown/EnrichedMarkdownTextComponentDescriptor.h>
|
|
22
|
+
#import <ReactNativeEnrichedMarkdown/EventEmitters.h>
|
|
23
|
+
#import <ReactNativeEnrichedMarkdown/Props.h>
|
|
24
|
+
#import <ReactNativeEnrichedMarkdown/RCTComponentViewHelpers.h>
|
|
25
|
+
|
|
26
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
27
|
+
#import <React/RCTConversions.h>
|
|
28
|
+
#import <React/RCTFont.h>
|
|
29
|
+
#import <react/utils/ManagedObjectWrapper.h>
|
|
30
|
+
|
|
31
|
+
using namespace facebook::react;
|
|
32
|
+
|
|
33
|
+
@interface EnrichedMarkdownText () <RCTEnrichedMarkdownTextViewProtocol, UITextViewDelegate>
|
|
34
|
+
- (void)setupTextView;
|
|
35
|
+
- (void)renderMarkdownContent:(NSString *)markdownString;
|
|
36
|
+
- (void)applyRenderedText:(NSMutableAttributedString *)attributedText;
|
|
37
|
+
- (void)textTapped:(UITapGestureRecognizer *)recognizer;
|
|
38
|
+
- (void)setupLayoutManager;
|
|
39
|
+
@end
|
|
40
|
+
|
|
41
|
+
@implementation EnrichedMarkdownText {
|
|
42
|
+
UITextView *_textView;
|
|
43
|
+
MarkdownParser *_parser;
|
|
44
|
+
NSString *_cachedMarkdown;
|
|
45
|
+
StyleConfig *_config;
|
|
46
|
+
Md4cFlags *_md4cFlags;
|
|
47
|
+
|
|
48
|
+
// Background rendering support
|
|
49
|
+
dispatch_queue_t _renderQueue;
|
|
50
|
+
NSUInteger _currentRenderId;
|
|
51
|
+
BOOL _blockAsyncRender;
|
|
52
|
+
|
|
53
|
+
EnrichedMarkdownTextShadowNode::ConcreteState::Shared _state;
|
|
54
|
+
int _heightUpdateCounter;
|
|
55
|
+
|
|
56
|
+
// Font scale tracking
|
|
57
|
+
CGFloat _currentFontScale;
|
|
58
|
+
BOOL _allowFontScaling;
|
|
59
|
+
CGFloat _maxFontSizeMultiplier;
|
|
60
|
+
|
|
61
|
+
// Last element marginBottom tracking
|
|
62
|
+
CGFloat _lastElementMarginBottom;
|
|
63
|
+
BOOL _allowTrailingMargin;
|
|
64
|
+
|
|
65
|
+
// iOS link preview control
|
|
66
|
+
BOOL _enableLinkPreview;
|
|
67
|
+
|
|
68
|
+
// Accessibility data for VoiceOver
|
|
69
|
+
AccessibilityInfo *_accessibilityInfo;
|
|
70
|
+
NSMutableArray<UIAccessibilityElement *> *_accessibilityElements;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
74
|
+
{
|
|
75
|
+
return concreteComponentDescriptorProvider<EnrichedMarkdownTextComponentDescriptor>();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#pragma mark - Measuring and State
|
|
79
|
+
|
|
80
|
+
- (CGSize)measureSize:(CGFloat)maxWidth
|
|
81
|
+
{
|
|
82
|
+
NSAttributedString *text = _textView.attributedText;
|
|
83
|
+
CGFloat defaultHeight = [UIFont systemFontOfSize:16.0].lineHeight;
|
|
84
|
+
|
|
85
|
+
if (text.length == 0) {
|
|
86
|
+
return CGSizeMake(maxWidth, defaultHeight);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Use UITextView's layout manager for measurement to avoid
|
|
90
|
+
// boundingRectWithSize: height discrepancies with NSTextAttachment objects.
|
|
91
|
+
_textView.textContainer.size = CGSizeMake(maxWidth, CGFLOAT_MAX);
|
|
92
|
+
[_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer];
|
|
93
|
+
CGRect usedRect = [_textView.layoutManager usedRectForTextContainer:_textView.textContainer];
|
|
94
|
+
|
|
95
|
+
CGFloat measuredWidth = ceil(usedRect.size.width);
|
|
96
|
+
CGFloat measuredHeight = usedRect.size.height;
|
|
97
|
+
|
|
98
|
+
// When text ends with \n (e.g. code block's bottom padding spacer),
|
|
99
|
+
// TextKit creates an "extra line fragment" after it that adds unwanted height.
|
|
100
|
+
CGRect extraFragment = _textView.layoutManager.extraLineFragmentRect;
|
|
101
|
+
if (!CGRectIsEmpty(extraFragment)) {
|
|
102
|
+
measuredHeight -= extraFragment.size.height;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Code block's bottom padding is a spacer \n with minimumLineHeight = codeBlockPadding.
|
|
106
|
+
// The layout manager may not size it accurately, so add the padding explicitly.
|
|
107
|
+
if (isLastElementCodeBlock(text)) {
|
|
108
|
+
measuredHeight += [_config codeBlockPadding];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (_allowTrailingMargin && _lastElementMarginBottom > 0) {
|
|
112
|
+
measuredHeight += _lastElementMarginBottom;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return CGSizeMake(measuredWidth, ceil(measuredHeight));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
- (void)updateState:(const facebook::react::State::Shared &)state
|
|
119
|
+
oldState:(const facebook::react::State::Shared &)oldState
|
|
120
|
+
{
|
|
121
|
+
_state = std::static_pointer_cast<const EnrichedMarkdownTextShadowNode::ConcreteState>(state);
|
|
122
|
+
|
|
123
|
+
// Trigger initial height calculation when state is first set
|
|
124
|
+
if (oldState == nullptr) {
|
|
125
|
+
[self requestHeightUpdate];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
- (void)requestHeightUpdate
|
|
130
|
+
{
|
|
131
|
+
if (_state == nullptr) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_heightUpdateCounter++;
|
|
136
|
+
auto selfRef = wrapManagedObjectWeakly(self);
|
|
137
|
+
_state->updateState(EnrichedMarkdownTextState(_heightUpdateCounter, selfRef));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
141
|
+
{
|
|
142
|
+
if (self = [super initWithFrame:frame]) {
|
|
143
|
+
static const auto defaultProps = std::make_shared<const EnrichedMarkdownTextProps>();
|
|
144
|
+
_props = defaultProps;
|
|
145
|
+
|
|
146
|
+
self.backgroundColor = [UIColor clearColor];
|
|
147
|
+
_parser = [[MarkdownParser alloc] init];
|
|
148
|
+
_md4cFlags = [Md4cFlags defaultFlags];
|
|
149
|
+
|
|
150
|
+
// Serial queue for background rendering
|
|
151
|
+
_renderQueue = dispatch_queue_create("com.swmansion.enriched.markdown.render", DISPATCH_QUEUE_SERIAL);
|
|
152
|
+
_currentRenderId = 0;
|
|
153
|
+
|
|
154
|
+
// Initialize font scale from current content size category
|
|
155
|
+
_allowFontScaling = YES;
|
|
156
|
+
_maxFontSizeMultiplier = 0;
|
|
157
|
+
_allowTrailingMargin = NO;
|
|
158
|
+
_currentFontScale = RCTFontSizeMultiplier();
|
|
159
|
+
_enableLinkPreview = YES;
|
|
160
|
+
|
|
161
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
162
|
+
selector:@selector(contentSizeCategoryDidChange:)
|
|
163
|
+
name:UIContentSizeCategoryDidChangeNotification
|
|
164
|
+
object:nil];
|
|
165
|
+
|
|
166
|
+
[self setupTextView];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return self;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
- (void)dealloc
|
|
173
|
+
{
|
|
174
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
- (CGFloat)effectiveFontScale
|
|
178
|
+
{
|
|
179
|
+
// If font scaling is disabled, always return 1.0 (no scaling)
|
|
180
|
+
return _allowFontScaling ? _currentFontScale : 1.0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
- (void)contentSizeCategoryDidChange:(NSNotification *)notification
|
|
184
|
+
{
|
|
185
|
+
if (!_allowFontScaling) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
CGFloat newFontScale = RCTFontSizeMultiplier();
|
|
190
|
+
if (_currentFontScale != newFontScale) {
|
|
191
|
+
_currentFontScale = newFontScale;
|
|
192
|
+
|
|
193
|
+
if (_config != nil) {
|
|
194
|
+
[_config setFontScaleMultiplier:[self effectiveFontScale]];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (_cachedMarkdown != nil && _cachedMarkdown.length > 0) {
|
|
198
|
+
[self renderMarkdownContent:_cachedMarkdown];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
- (void)setupTextView
|
|
204
|
+
{
|
|
205
|
+
_textView = [[UITextView alloc] init];
|
|
206
|
+
_textView.text = @"";
|
|
207
|
+
_textView.font = [UIFont systemFontOfSize:16.0];
|
|
208
|
+
_textView.backgroundColor = [UIColor clearColor];
|
|
209
|
+
_textView.textColor = [UIColor blackColor];
|
|
210
|
+
_textView.editable = NO;
|
|
211
|
+
_textView.delegate = self;
|
|
212
|
+
_textView.scrollEnabled = NO;
|
|
213
|
+
_textView.showsVerticalScrollIndicator = NO;
|
|
214
|
+
_textView.showsHorizontalScrollIndicator = NO;
|
|
215
|
+
_textView.textContainerInset = UIEdgeInsetsZero;
|
|
216
|
+
_textView.textContainer.lineFragmentPadding = 0;
|
|
217
|
+
// Disable UITextView's default link styling - we handle it directly in attributed strings
|
|
218
|
+
_textView.linkTextAttributes = @{};
|
|
219
|
+
// selectable controls text selection and link previews
|
|
220
|
+
// Default to YES to match the prop default
|
|
221
|
+
_textView.selectable = YES;
|
|
222
|
+
// Hide initially to prevent flash before content is rendered
|
|
223
|
+
_textView.hidden = YES;
|
|
224
|
+
// Disable textView's built-in accessibility - we provide custom elements with proper traits
|
|
225
|
+
_textView.accessibilityElementsHidden = YES;
|
|
226
|
+
|
|
227
|
+
// Add tap gesture recognizer
|
|
228
|
+
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
|
|
229
|
+
action:@selector(textTapped:)];
|
|
230
|
+
[_textView addGestureRecognizer:tapRecognizer];
|
|
231
|
+
|
|
232
|
+
// Use RCTViewComponentView's contentView for automatic sizing
|
|
233
|
+
self.contentView = _textView;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
- (void)didAddSubview:(UIView *)subview
|
|
237
|
+
{
|
|
238
|
+
[super didAddSubview:subview];
|
|
239
|
+
|
|
240
|
+
// Set up layout manager when text view is added
|
|
241
|
+
if (subview == _textView) {
|
|
242
|
+
[self setupLayoutManager];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
- (void)willRemoveSubview:(UIView *)subview
|
|
247
|
+
{
|
|
248
|
+
// Clean up layout manager when text view is removed
|
|
249
|
+
if (subview == _textView && _textView.layoutManager != nil) {
|
|
250
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
251
|
+
if ([object_getClass(layoutManager) isEqual:[TextViewLayoutManager class]]) {
|
|
252
|
+
[layoutManager setValue:nil forKey:@"config"];
|
|
253
|
+
object_setClass(layoutManager, [NSLayoutManager class]);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
[super willRemoveSubview:subview];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
- (void)setupLayoutManager
|
|
260
|
+
{
|
|
261
|
+
// Set up custom layout manager for rich text custom drawing (code, blockquotes, etc.)
|
|
262
|
+
// This single manager can handle multiple element types
|
|
263
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
264
|
+
if (layoutManager != nil) {
|
|
265
|
+
layoutManager.allowsNonContiguousLayout = NO; // workaround for onScroll issue (like react-native-live-markdown)
|
|
266
|
+
object_setClass(layoutManager, [TextViewLayoutManager class]);
|
|
267
|
+
|
|
268
|
+
// Set config on layout manager (like react-native-live-markdown sets markdownUtils)
|
|
269
|
+
if (_config != nil) {
|
|
270
|
+
[layoutManager setValue:_config forKey:@"config"];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
- (void)renderMarkdownContent:(NSString *)markdownString
|
|
276
|
+
{
|
|
277
|
+
// Skip async render for mock views (they use renderMarkdownSynchronously)
|
|
278
|
+
if (_blockAsyncRender) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
_cachedMarkdown = [markdownString copy];
|
|
283
|
+
|
|
284
|
+
// Increment render ID to invalidate any in-flight renders
|
|
285
|
+
NSUInteger renderId = ++_currentRenderId;
|
|
286
|
+
|
|
287
|
+
// Capture state needed for background rendering
|
|
288
|
+
StyleConfig *config = [_config copy];
|
|
289
|
+
MarkdownParser *parser = _parser;
|
|
290
|
+
Md4cFlags *md4cFlags = [_md4cFlags copy];
|
|
291
|
+
NSUInteger inputLength = markdownString.length;
|
|
292
|
+
NSDate *scheduleStart = [NSDate date];
|
|
293
|
+
|
|
294
|
+
// Capture font scaling settings
|
|
295
|
+
BOOL allowFontScaling = _allowFontScaling;
|
|
296
|
+
CGFloat maxFontSizeMultiplier = _maxFontSizeMultiplier;
|
|
297
|
+
|
|
298
|
+
// Dispatch heavy work to background queue
|
|
299
|
+
dispatch_async(_renderQueue, ^{
|
|
300
|
+
// 1. Parse Markdown → AST (C++ md4c parser)
|
|
301
|
+
NSDate *parseStart = [NSDate date];
|
|
302
|
+
MarkdownASTNode *ast = [parser parseMarkdown:markdownString flags:md4cFlags];
|
|
303
|
+
if (!ast) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
NSTimeInterval parseTime = [[NSDate date] timeIntervalSinceDate:parseStart] * 1000;
|
|
307
|
+
NSUInteger nodeCount = ast.children.count;
|
|
308
|
+
|
|
309
|
+
// 2. Render AST → NSAttributedString
|
|
310
|
+
NSDate *renderStart = [NSDate date];
|
|
311
|
+
AttributedRenderer *renderer = [[AttributedRenderer alloc] initWithConfig:config];
|
|
312
|
+
[renderer setAllowTrailingMargin:self->_allowTrailingMargin];
|
|
313
|
+
RenderContext *context = [RenderContext new];
|
|
314
|
+
context.allowFontScaling = allowFontScaling;
|
|
315
|
+
context.maxFontSizeMultiplier = maxFontSizeMultiplier;
|
|
316
|
+
NSMutableAttributedString *attributedText = [renderer renderRoot:ast context:context];
|
|
317
|
+
|
|
318
|
+
self->_lastElementMarginBottom = [renderer getLastElementMarginBottom];
|
|
319
|
+
|
|
320
|
+
// Add link attributes
|
|
321
|
+
for (NSUInteger i = 0; i < context.linkRanges.count; i++) {
|
|
322
|
+
NSValue *rangeValue = context.linkRanges[i];
|
|
323
|
+
NSRange range = [rangeValue rangeValue];
|
|
324
|
+
NSString *url = context.linkURLs[i];
|
|
325
|
+
[attributedText addAttribute:@"linkURL" value:url range:range];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Capture accessibility info
|
|
329
|
+
AccessibilityInfo *accessibilityInfo = [AccessibilityInfo infoFromContext:context];
|
|
330
|
+
|
|
331
|
+
NSTimeInterval renderTime = [[NSDate date] timeIntervalSinceDate:renderStart] * 1000;
|
|
332
|
+
NSUInteger styledLength = attributedText.length;
|
|
333
|
+
|
|
334
|
+
// Apply result on main thread
|
|
335
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
336
|
+
// Check if this render is still current
|
|
337
|
+
if (renderId != self->_currentRenderId) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Store accessibility info
|
|
342
|
+
self->_accessibilityInfo = accessibilityInfo;
|
|
343
|
+
|
|
344
|
+
[self applyRenderedText:attributedText];
|
|
345
|
+
|
|
346
|
+
NSTimeInterval totalTime = [[NSDate date] timeIntervalSinceDate:scheduleStart] * 1000;
|
|
347
|
+
NSLog(@"┌──────────────────────────────────────────────");
|
|
348
|
+
NSLog(@"│ 📝 Input: %lu chars of Markdown", (unsigned long)inputLength);
|
|
349
|
+
NSLog(@"│ ⚡ md4c (C++ native): %.0fms → %lu AST nodes", parseTime, (unsigned long)nodeCount);
|
|
350
|
+
NSLog(@"│ 🎨 NSAttributedString render: %.0fms → %lu styled chars", renderTime, (unsigned long)styledLength);
|
|
351
|
+
NSLog(@"│ ✅ Total time to display: %.0fms", totalTime);
|
|
352
|
+
NSLog(@"└──────────────────────────────────────────────");
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Synchronous rendering for mock view measurement (no UI updates needed)
|
|
358
|
+
- (void)renderMarkdownSynchronously:(NSString *)markdownString
|
|
359
|
+
{
|
|
360
|
+
if (!markdownString || markdownString.length == 0) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Block any async renders triggered by updateProps
|
|
365
|
+
_blockAsyncRender = YES;
|
|
366
|
+
_cachedMarkdown = [markdownString copy];
|
|
367
|
+
|
|
368
|
+
MarkdownASTNode *ast = [_parser parseMarkdown:markdownString flags:_md4cFlags];
|
|
369
|
+
if (!ast) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
AttributedRenderer *renderer = [[AttributedRenderer alloc] initWithConfig:_config];
|
|
374
|
+
[renderer setAllowTrailingMargin:_allowTrailingMargin];
|
|
375
|
+
RenderContext *context = [RenderContext new];
|
|
376
|
+
context.allowFontScaling = _allowFontScaling;
|
|
377
|
+
context.maxFontSizeMultiplier = _maxFontSizeMultiplier;
|
|
378
|
+
NSMutableAttributedString *attributedText = [renderer renderRoot:ast context:context];
|
|
379
|
+
|
|
380
|
+
_lastElementMarginBottom = [renderer getLastElementMarginBottom];
|
|
381
|
+
|
|
382
|
+
for (NSUInteger i = 0; i < context.linkRanges.count; i++) {
|
|
383
|
+
NSValue *rangeValue = context.linkRanges[i];
|
|
384
|
+
NSRange range = [rangeValue rangeValue];
|
|
385
|
+
NSString *url = context.linkURLs[i];
|
|
386
|
+
[attributedText addAttribute:@"linkURL" value:url range:range];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Store accessibility info
|
|
390
|
+
_accessibilityInfo = [AccessibilityInfo infoFromContext:context];
|
|
391
|
+
|
|
392
|
+
_textView.attributedText = attributedText;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
- (void)applyRenderedText:(NSMutableAttributedString *)attributedText
|
|
396
|
+
{
|
|
397
|
+
// Set config on the layout manager
|
|
398
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
399
|
+
if ([layoutManager isKindOfClass:[TextViewLayoutManager class]]) {
|
|
400
|
+
[layoutManager setValue:_config forKey:@"config"];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Store text view on text container so attachments can access it
|
|
404
|
+
objc_setAssociatedObject(_textView.textContainer, kTextViewKey, _textView, OBJC_ASSOCIATION_ASSIGN);
|
|
405
|
+
|
|
406
|
+
_textView.attributedText = attributedText;
|
|
407
|
+
|
|
408
|
+
// Ensure layout is updated
|
|
409
|
+
[_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer];
|
|
410
|
+
[_textView.layoutManager invalidateLayoutForCharacterRange:NSMakeRange(0, attributedText.length)
|
|
411
|
+
actualCharacterRange:NULL];
|
|
412
|
+
|
|
413
|
+
[_textView setNeedsLayout];
|
|
414
|
+
[_textView setNeedsDisplay];
|
|
415
|
+
[self setNeedsLayout];
|
|
416
|
+
|
|
417
|
+
// Request height recalculation from shadow node FIRST
|
|
418
|
+
[self requestHeightUpdate];
|
|
419
|
+
|
|
420
|
+
// Build accessibility elements after layout is complete
|
|
421
|
+
[self buildAccessibilityElements];
|
|
422
|
+
|
|
423
|
+
// Show text view on next run loop, after layout has settled
|
|
424
|
+
if (_textView.hidden) {
|
|
425
|
+
dispatch_async(dispatch_get_main_queue(), ^{ self->_textView.hidden = NO; });
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
430
|
+
{
|
|
431
|
+
const auto &oldViewProps = *std::static_pointer_cast<EnrichedMarkdownTextProps const>(_props);
|
|
432
|
+
const auto &newViewProps = *std::static_pointer_cast<EnrichedMarkdownTextProps const>(props);
|
|
433
|
+
|
|
434
|
+
BOOL stylePropChanged = NO;
|
|
435
|
+
|
|
436
|
+
if (_config == nil) {
|
|
437
|
+
_config = [[StyleConfig alloc] init];
|
|
438
|
+
[_config setFontScaleMultiplier:[self effectiveFontScale]];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Paragraph style
|
|
442
|
+
if (newViewProps.markdownStyle.paragraph.fontSize != oldViewProps.markdownStyle.paragraph.fontSize) {
|
|
443
|
+
[_config setParagraphFontSize:newViewProps.markdownStyle.paragraph.fontSize];
|
|
444
|
+
stylePropChanged = YES;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (newViewProps.markdownStyle.paragraph.fontFamily != oldViewProps.markdownStyle.paragraph.fontFamily) {
|
|
448
|
+
if (!newViewProps.markdownStyle.paragraph.fontFamily.empty()) {
|
|
449
|
+
NSString *fontFamily =
|
|
450
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.paragraph.fontFamily.c_str()];
|
|
451
|
+
[_config setParagraphFontFamily:fontFamily];
|
|
452
|
+
} else {
|
|
453
|
+
[_config setParagraphFontFamily:nullptr];
|
|
454
|
+
}
|
|
455
|
+
stylePropChanged = YES;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (newViewProps.markdownStyle.paragraph.fontWeight != oldViewProps.markdownStyle.paragraph.fontWeight) {
|
|
459
|
+
if (!newViewProps.markdownStyle.paragraph.fontWeight.empty()) {
|
|
460
|
+
NSString *fontWeight =
|
|
461
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.paragraph.fontWeight.c_str()];
|
|
462
|
+
[_config setParagraphFontWeight:fontWeight];
|
|
463
|
+
} else {
|
|
464
|
+
[_config setParagraphFontWeight:nullptr];
|
|
465
|
+
}
|
|
466
|
+
stylePropChanged = YES;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (newViewProps.markdownStyle.paragraph.color != oldViewProps.markdownStyle.paragraph.color) {
|
|
470
|
+
if (newViewProps.markdownStyle.paragraph.color) {
|
|
471
|
+
UIColor *paragraphColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.paragraph.color);
|
|
472
|
+
[_config setParagraphColor:paragraphColor];
|
|
473
|
+
} else {
|
|
474
|
+
[_config setParagraphColor:nullptr];
|
|
475
|
+
}
|
|
476
|
+
stylePropChanged = YES;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (newViewProps.markdownStyle.paragraph.marginTop != oldViewProps.markdownStyle.paragraph.marginTop) {
|
|
480
|
+
[_config setParagraphMarginTop:newViewProps.markdownStyle.paragraph.marginTop];
|
|
481
|
+
stylePropChanged = YES;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (newViewProps.markdownStyle.paragraph.marginBottom != oldViewProps.markdownStyle.paragraph.marginBottom) {
|
|
485
|
+
[_config setParagraphMarginBottom:newViewProps.markdownStyle.paragraph.marginBottom];
|
|
486
|
+
stylePropChanged = YES;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (newViewProps.markdownStyle.paragraph.lineHeight != oldViewProps.markdownStyle.paragraph.lineHeight) {
|
|
490
|
+
[_config setParagraphLineHeight:newViewProps.markdownStyle.paragraph.lineHeight];
|
|
491
|
+
stylePropChanged = YES;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (newViewProps.markdownStyle.paragraph.textAlign != oldViewProps.markdownStyle.paragraph.textAlign) {
|
|
495
|
+
[_config setParagraphTextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.paragraph.textAlign.c_str()))];
|
|
496
|
+
stylePropChanged = YES;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// H1 style
|
|
500
|
+
if (newViewProps.markdownStyle.h1.fontSize != oldViewProps.markdownStyle.h1.fontSize) {
|
|
501
|
+
[_config setH1FontSize:newViewProps.markdownStyle.h1.fontSize];
|
|
502
|
+
stylePropChanged = YES;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (newViewProps.markdownStyle.h1.fontFamily != oldViewProps.markdownStyle.h1.fontFamily) {
|
|
506
|
+
if (!newViewProps.markdownStyle.h1.fontFamily.empty()) {
|
|
507
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h1.fontFamily.c_str()];
|
|
508
|
+
[_config setH1FontFamily:fontFamily];
|
|
509
|
+
} else {
|
|
510
|
+
[_config setH1FontFamily:nullptr];
|
|
511
|
+
}
|
|
512
|
+
stylePropChanged = YES;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (newViewProps.markdownStyle.h1.fontWeight != oldViewProps.markdownStyle.h1.fontWeight) {
|
|
516
|
+
if (!newViewProps.markdownStyle.h1.fontWeight.empty()) {
|
|
517
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h1.fontWeight.c_str()];
|
|
518
|
+
[_config setH1FontWeight:fontWeight];
|
|
519
|
+
} else {
|
|
520
|
+
[_config setH1FontWeight:nullptr];
|
|
521
|
+
}
|
|
522
|
+
stylePropChanged = YES;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (newViewProps.markdownStyle.h1.color != oldViewProps.markdownStyle.h1.color) {
|
|
526
|
+
if (newViewProps.markdownStyle.h1.color) {
|
|
527
|
+
UIColor *h1Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h1.color);
|
|
528
|
+
[_config setH1Color:h1Color];
|
|
529
|
+
} else {
|
|
530
|
+
[_config setH1Color:nullptr];
|
|
531
|
+
}
|
|
532
|
+
stylePropChanged = YES;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (newViewProps.markdownStyle.h1.marginTop != oldViewProps.markdownStyle.h1.marginTop) {
|
|
536
|
+
[_config setH1MarginTop:newViewProps.markdownStyle.h1.marginTop];
|
|
537
|
+
stylePropChanged = YES;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (newViewProps.markdownStyle.h1.marginBottom != oldViewProps.markdownStyle.h1.marginBottom) {
|
|
541
|
+
[_config setH1MarginBottom:newViewProps.markdownStyle.h1.marginBottom];
|
|
542
|
+
stylePropChanged = YES;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (newViewProps.markdownStyle.h1.lineHeight != oldViewProps.markdownStyle.h1.lineHeight) {
|
|
546
|
+
[_config setH1LineHeight:newViewProps.markdownStyle.h1.lineHeight];
|
|
547
|
+
stylePropChanged = YES;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (newViewProps.markdownStyle.h1.textAlign != oldViewProps.markdownStyle.h1.textAlign) {
|
|
551
|
+
[_config setH1TextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.h1.textAlign.c_str()))];
|
|
552
|
+
stylePropChanged = YES;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// H2 style
|
|
556
|
+
if (newViewProps.markdownStyle.h2.fontSize != oldViewProps.markdownStyle.h2.fontSize) {
|
|
557
|
+
[_config setH2FontSize:newViewProps.markdownStyle.h2.fontSize];
|
|
558
|
+
stylePropChanged = YES;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (newViewProps.markdownStyle.h2.fontFamily != oldViewProps.markdownStyle.h2.fontFamily) {
|
|
562
|
+
if (!newViewProps.markdownStyle.h2.fontFamily.empty()) {
|
|
563
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h2.fontFamily.c_str()];
|
|
564
|
+
[_config setH2FontFamily:fontFamily];
|
|
565
|
+
} else {
|
|
566
|
+
[_config setH2FontFamily:nullptr];
|
|
567
|
+
}
|
|
568
|
+
stylePropChanged = YES;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (newViewProps.markdownStyle.h2.fontWeight != oldViewProps.markdownStyle.h2.fontWeight) {
|
|
572
|
+
if (!newViewProps.markdownStyle.h2.fontWeight.empty()) {
|
|
573
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h2.fontWeight.c_str()];
|
|
574
|
+
[_config setH2FontWeight:fontWeight];
|
|
575
|
+
} else {
|
|
576
|
+
[_config setH2FontWeight:nullptr];
|
|
577
|
+
}
|
|
578
|
+
stylePropChanged = YES;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (newViewProps.markdownStyle.h2.color != oldViewProps.markdownStyle.h2.color) {
|
|
582
|
+
if (newViewProps.markdownStyle.h2.color) {
|
|
583
|
+
UIColor *h2Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h2.color);
|
|
584
|
+
[_config setH2Color:h2Color];
|
|
585
|
+
} else {
|
|
586
|
+
[_config setH2Color:nullptr];
|
|
587
|
+
}
|
|
588
|
+
stylePropChanged = YES;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (newViewProps.markdownStyle.h2.marginTop != oldViewProps.markdownStyle.h2.marginTop) {
|
|
592
|
+
[_config setH2MarginTop:newViewProps.markdownStyle.h2.marginTop];
|
|
593
|
+
stylePropChanged = YES;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (newViewProps.markdownStyle.h2.marginBottom != oldViewProps.markdownStyle.h2.marginBottom) {
|
|
597
|
+
[_config setH2MarginBottom:newViewProps.markdownStyle.h2.marginBottom];
|
|
598
|
+
stylePropChanged = YES;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (newViewProps.markdownStyle.h2.lineHeight != oldViewProps.markdownStyle.h2.lineHeight) {
|
|
602
|
+
[_config setH2LineHeight:newViewProps.markdownStyle.h2.lineHeight];
|
|
603
|
+
stylePropChanged = YES;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (newViewProps.markdownStyle.h2.textAlign != oldViewProps.markdownStyle.h2.textAlign) {
|
|
607
|
+
[_config setH2TextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.h2.textAlign.c_str()))];
|
|
608
|
+
stylePropChanged = YES;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// H3 style
|
|
612
|
+
if (newViewProps.markdownStyle.h3.fontSize != oldViewProps.markdownStyle.h3.fontSize) {
|
|
613
|
+
[_config setH3FontSize:newViewProps.markdownStyle.h3.fontSize];
|
|
614
|
+
stylePropChanged = YES;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (newViewProps.markdownStyle.h3.fontFamily != oldViewProps.markdownStyle.h3.fontFamily) {
|
|
618
|
+
if (!newViewProps.markdownStyle.h3.fontFamily.empty()) {
|
|
619
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h3.fontFamily.c_str()];
|
|
620
|
+
[_config setH3FontFamily:fontFamily];
|
|
621
|
+
} else {
|
|
622
|
+
[_config setH3FontFamily:nullptr];
|
|
623
|
+
}
|
|
624
|
+
stylePropChanged = YES;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (newViewProps.markdownStyle.h3.fontWeight != oldViewProps.markdownStyle.h3.fontWeight) {
|
|
628
|
+
if (!newViewProps.markdownStyle.h3.fontWeight.empty()) {
|
|
629
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h3.fontWeight.c_str()];
|
|
630
|
+
[_config setH3FontWeight:fontWeight];
|
|
631
|
+
} else {
|
|
632
|
+
[_config setH3FontWeight:nullptr];
|
|
633
|
+
}
|
|
634
|
+
stylePropChanged = YES;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (newViewProps.markdownStyle.h3.color != oldViewProps.markdownStyle.h3.color) {
|
|
638
|
+
if (newViewProps.markdownStyle.h3.color) {
|
|
639
|
+
UIColor *h3Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h3.color);
|
|
640
|
+
[_config setH3Color:h3Color];
|
|
641
|
+
} else {
|
|
642
|
+
[_config setH3Color:nullptr];
|
|
643
|
+
}
|
|
644
|
+
stylePropChanged = YES;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (newViewProps.markdownStyle.h3.marginTop != oldViewProps.markdownStyle.h3.marginTop) {
|
|
648
|
+
[_config setH3MarginTop:newViewProps.markdownStyle.h3.marginTop];
|
|
649
|
+
stylePropChanged = YES;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (newViewProps.markdownStyle.h3.marginBottom != oldViewProps.markdownStyle.h3.marginBottom) {
|
|
653
|
+
[_config setH3MarginBottom:newViewProps.markdownStyle.h3.marginBottom];
|
|
654
|
+
stylePropChanged = YES;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (newViewProps.markdownStyle.h3.lineHeight != oldViewProps.markdownStyle.h3.lineHeight) {
|
|
658
|
+
[_config setH3LineHeight:newViewProps.markdownStyle.h3.lineHeight];
|
|
659
|
+
stylePropChanged = YES;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (newViewProps.markdownStyle.h3.textAlign != oldViewProps.markdownStyle.h3.textAlign) {
|
|
663
|
+
[_config setH3TextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.h3.textAlign.c_str()))];
|
|
664
|
+
stylePropChanged = YES;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// H4 style
|
|
668
|
+
if (newViewProps.markdownStyle.h4.fontSize != oldViewProps.markdownStyle.h4.fontSize) {
|
|
669
|
+
[_config setH4FontSize:newViewProps.markdownStyle.h4.fontSize];
|
|
670
|
+
stylePropChanged = YES;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (newViewProps.markdownStyle.h4.fontFamily != oldViewProps.markdownStyle.h4.fontFamily) {
|
|
674
|
+
if (!newViewProps.markdownStyle.h4.fontFamily.empty()) {
|
|
675
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h4.fontFamily.c_str()];
|
|
676
|
+
[_config setH4FontFamily:fontFamily];
|
|
677
|
+
} else {
|
|
678
|
+
[_config setH4FontFamily:nullptr];
|
|
679
|
+
}
|
|
680
|
+
stylePropChanged = YES;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (newViewProps.markdownStyle.h4.fontWeight != oldViewProps.markdownStyle.h4.fontWeight) {
|
|
684
|
+
if (!newViewProps.markdownStyle.h4.fontWeight.empty()) {
|
|
685
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h4.fontWeight.c_str()];
|
|
686
|
+
[_config setH4FontWeight:fontWeight];
|
|
687
|
+
} else {
|
|
688
|
+
[_config setH4FontWeight:nullptr];
|
|
689
|
+
}
|
|
690
|
+
stylePropChanged = YES;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (newViewProps.markdownStyle.h4.color != oldViewProps.markdownStyle.h4.color) {
|
|
694
|
+
if (newViewProps.markdownStyle.h4.color) {
|
|
695
|
+
UIColor *h4Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h4.color);
|
|
696
|
+
[_config setH4Color:h4Color];
|
|
697
|
+
} else {
|
|
698
|
+
[_config setH4Color:nullptr];
|
|
699
|
+
}
|
|
700
|
+
stylePropChanged = YES;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (newViewProps.markdownStyle.h4.marginTop != oldViewProps.markdownStyle.h4.marginTop) {
|
|
704
|
+
[_config setH4MarginTop:newViewProps.markdownStyle.h4.marginTop];
|
|
705
|
+
stylePropChanged = YES;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (newViewProps.markdownStyle.h4.marginBottom != oldViewProps.markdownStyle.h4.marginBottom) {
|
|
709
|
+
[_config setH4MarginBottom:newViewProps.markdownStyle.h4.marginBottom];
|
|
710
|
+
stylePropChanged = YES;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (newViewProps.markdownStyle.h4.lineHeight != oldViewProps.markdownStyle.h4.lineHeight) {
|
|
714
|
+
[_config setH4LineHeight:newViewProps.markdownStyle.h4.lineHeight];
|
|
715
|
+
stylePropChanged = YES;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (newViewProps.markdownStyle.h4.textAlign != oldViewProps.markdownStyle.h4.textAlign) {
|
|
719
|
+
[_config setH4TextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.h4.textAlign.c_str()))];
|
|
720
|
+
stylePropChanged = YES;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// H5 style
|
|
724
|
+
if (newViewProps.markdownStyle.h5.fontSize != oldViewProps.markdownStyle.h5.fontSize) {
|
|
725
|
+
[_config setH5FontSize:newViewProps.markdownStyle.h5.fontSize];
|
|
726
|
+
stylePropChanged = YES;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (newViewProps.markdownStyle.h5.fontFamily != oldViewProps.markdownStyle.h5.fontFamily) {
|
|
730
|
+
if (!newViewProps.markdownStyle.h5.fontFamily.empty()) {
|
|
731
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h5.fontFamily.c_str()];
|
|
732
|
+
[_config setH5FontFamily:fontFamily];
|
|
733
|
+
} else {
|
|
734
|
+
[_config setH5FontFamily:nullptr];
|
|
735
|
+
}
|
|
736
|
+
stylePropChanged = YES;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (newViewProps.markdownStyle.h5.fontWeight != oldViewProps.markdownStyle.h5.fontWeight) {
|
|
740
|
+
if (!newViewProps.markdownStyle.h5.fontWeight.empty()) {
|
|
741
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h5.fontWeight.c_str()];
|
|
742
|
+
[_config setH5FontWeight:fontWeight];
|
|
743
|
+
} else {
|
|
744
|
+
[_config setH5FontWeight:nullptr];
|
|
745
|
+
}
|
|
746
|
+
stylePropChanged = YES;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (newViewProps.markdownStyle.h5.color != oldViewProps.markdownStyle.h5.color) {
|
|
750
|
+
if (newViewProps.markdownStyle.h5.color) {
|
|
751
|
+
UIColor *h5Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h5.color);
|
|
752
|
+
[_config setH5Color:h5Color];
|
|
753
|
+
} else {
|
|
754
|
+
[_config setH5Color:nullptr];
|
|
755
|
+
}
|
|
756
|
+
stylePropChanged = YES;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (newViewProps.markdownStyle.h5.marginTop != oldViewProps.markdownStyle.h5.marginTop) {
|
|
760
|
+
[_config setH5MarginTop:newViewProps.markdownStyle.h5.marginTop];
|
|
761
|
+
stylePropChanged = YES;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (newViewProps.markdownStyle.h5.marginBottom != oldViewProps.markdownStyle.h5.marginBottom) {
|
|
765
|
+
[_config setH5MarginBottom:newViewProps.markdownStyle.h5.marginBottom];
|
|
766
|
+
stylePropChanged = YES;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (newViewProps.markdownStyle.h5.lineHeight != oldViewProps.markdownStyle.h5.lineHeight) {
|
|
770
|
+
[_config setH5LineHeight:newViewProps.markdownStyle.h5.lineHeight];
|
|
771
|
+
stylePropChanged = YES;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (newViewProps.markdownStyle.h5.textAlign != oldViewProps.markdownStyle.h5.textAlign) {
|
|
775
|
+
[_config setH5TextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.h5.textAlign.c_str()))];
|
|
776
|
+
stylePropChanged = YES;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// H6 style
|
|
780
|
+
if (newViewProps.markdownStyle.h6.fontSize != oldViewProps.markdownStyle.h6.fontSize) {
|
|
781
|
+
[_config setH6FontSize:newViewProps.markdownStyle.h6.fontSize];
|
|
782
|
+
stylePropChanged = YES;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (newViewProps.markdownStyle.h6.fontFamily != oldViewProps.markdownStyle.h6.fontFamily) {
|
|
786
|
+
if (!newViewProps.markdownStyle.h6.fontFamily.empty()) {
|
|
787
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h6.fontFamily.c_str()];
|
|
788
|
+
[_config setH6FontFamily:fontFamily];
|
|
789
|
+
} else {
|
|
790
|
+
[_config setH6FontFamily:nullptr];
|
|
791
|
+
}
|
|
792
|
+
stylePropChanged = YES;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (newViewProps.markdownStyle.h6.fontWeight != oldViewProps.markdownStyle.h6.fontWeight) {
|
|
796
|
+
if (!newViewProps.markdownStyle.h6.fontWeight.empty()) {
|
|
797
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h6.fontWeight.c_str()];
|
|
798
|
+
[_config setH6FontWeight:fontWeight];
|
|
799
|
+
} else {
|
|
800
|
+
[_config setH6FontWeight:nullptr];
|
|
801
|
+
}
|
|
802
|
+
stylePropChanged = YES;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (newViewProps.markdownStyle.h6.color != oldViewProps.markdownStyle.h6.color) {
|
|
806
|
+
if (newViewProps.markdownStyle.h6.color) {
|
|
807
|
+
UIColor *h6Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h6.color);
|
|
808
|
+
[_config setH6Color:h6Color];
|
|
809
|
+
} else {
|
|
810
|
+
[_config setH6Color:nullptr];
|
|
811
|
+
}
|
|
812
|
+
stylePropChanged = YES;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (newViewProps.markdownStyle.h6.marginTop != oldViewProps.markdownStyle.h6.marginTop) {
|
|
816
|
+
[_config setH6MarginTop:newViewProps.markdownStyle.h6.marginTop];
|
|
817
|
+
stylePropChanged = YES;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (newViewProps.markdownStyle.h6.marginBottom != oldViewProps.markdownStyle.h6.marginBottom) {
|
|
821
|
+
[_config setH6MarginBottom:newViewProps.markdownStyle.h6.marginBottom];
|
|
822
|
+
stylePropChanged = YES;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (newViewProps.markdownStyle.h6.lineHeight != oldViewProps.markdownStyle.h6.lineHeight) {
|
|
826
|
+
[_config setH6LineHeight:newViewProps.markdownStyle.h6.lineHeight];
|
|
827
|
+
stylePropChanged = YES;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if (newViewProps.markdownStyle.h6.textAlign != oldViewProps.markdownStyle.h6.textAlign) {
|
|
831
|
+
[_config setH6TextAlign:textAlignmentFromString(@(newViewProps.markdownStyle.h6.textAlign.c_str()))];
|
|
832
|
+
stylePropChanged = YES;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Blockquote style
|
|
836
|
+
if (newViewProps.markdownStyle.blockquote.fontSize != oldViewProps.markdownStyle.blockquote.fontSize) {
|
|
837
|
+
[_config setBlockquoteFontSize:newViewProps.markdownStyle.blockquote.fontSize];
|
|
838
|
+
stylePropChanged = YES;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (newViewProps.markdownStyle.blockquote.fontFamily != oldViewProps.markdownStyle.blockquote.fontFamily) {
|
|
842
|
+
NSString *fontFamily =
|
|
843
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.blockquote.fontFamily.c_str()];
|
|
844
|
+
[_config setBlockquoteFontFamily:fontFamily];
|
|
845
|
+
stylePropChanged = YES;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (newViewProps.markdownStyle.blockquote.fontWeight != oldViewProps.markdownStyle.blockquote.fontWeight) {
|
|
849
|
+
NSString *fontWeight =
|
|
850
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.blockquote.fontWeight.c_str()];
|
|
851
|
+
[_config setBlockquoteFontWeight:fontWeight];
|
|
852
|
+
stylePropChanged = YES;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (newViewProps.markdownStyle.blockquote.color != oldViewProps.markdownStyle.blockquote.color) {
|
|
856
|
+
UIColor *blockquoteColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.blockquote.color);
|
|
857
|
+
[_config setBlockquoteColor:blockquoteColor];
|
|
858
|
+
stylePropChanged = YES;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
if (newViewProps.markdownStyle.blockquote.marginTop != oldViewProps.markdownStyle.blockquote.marginTop) {
|
|
862
|
+
[_config setBlockquoteMarginTop:newViewProps.markdownStyle.blockquote.marginTop];
|
|
863
|
+
stylePropChanged = YES;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (newViewProps.markdownStyle.blockquote.marginBottom != oldViewProps.markdownStyle.blockquote.marginBottom) {
|
|
867
|
+
[_config setBlockquoteMarginBottom:newViewProps.markdownStyle.blockquote.marginBottom];
|
|
868
|
+
stylePropChanged = YES;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (newViewProps.markdownStyle.blockquote.lineHeight != oldViewProps.markdownStyle.blockquote.lineHeight) {
|
|
872
|
+
[_config setBlockquoteLineHeight:newViewProps.markdownStyle.blockquote.lineHeight];
|
|
873
|
+
stylePropChanged = YES;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (newViewProps.markdownStyle.blockquote.borderColor != oldViewProps.markdownStyle.blockquote.borderColor) {
|
|
877
|
+
UIColor *blockquoteBorderColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.blockquote.borderColor);
|
|
878
|
+
[_config setBlockquoteBorderColor:blockquoteBorderColor];
|
|
879
|
+
stylePropChanged = YES;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (newViewProps.markdownStyle.blockquote.borderWidth != oldViewProps.markdownStyle.blockquote.borderWidth) {
|
|
883
|
+
[_config setBlockquoteBorderWidth:newViewProps.markdownStyle.blockquote.borderWidth];
|
|
884
|
+
stylePropChanged = YES;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (newViewProps.markdownStyle.blockquote.gapWidth != oldViewProps.markdownStyle.blockquote.gapWidth) {
|
|
888
|
+
[_config setBlockquoteGapWidth:newViewProps.markdownStyle.blockquote.gapWidth];
|
|
889
|
+
stylePropChanged = YES;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (newViewProps.markdownStyle.blockquote.backgroundColor != oldViewProps.markdownStyle.blockquote.backgroundColor) {
|
|
893
|
+
UIColor *blockquoteBackgroundColor =
|
|
894
|
+
RCTUIColorFromSharedColor(newViewProps.markdownStyle.blockquote.backgroundColor);
|
|
895
|
+
[_config setBlockquoteBackgroundColor:blockquoteBackgroundColor];
|
|
896
|
+
stylePropChanged = YES;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (newViewProps.markdownStyle.link.color != oldViewProps.markdownStyle.link.color) {
|
|
900
|
+
UIColor *linkColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.link.color);
|
|
901
|
+
[_config setLinkColor:linkColor];
|
|
902
|
+
stylePropChanged = YES;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (newViewProps.markdownStyle.link.underline != oldViewProps.markdownStyle.link.underline) {
|
|
906
|
+
[_config setLinkUnderline:newViewProps.markdownStyle.link.underline];
|
|
907
|
+
stylePropChanged = YES;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (newViewProps.markdownStyle.strong.color != oldViewProps.markdownStyle.strong.color) {
|
|
911
|
+
if (newViewProps.markdownStyle.strong.color) {
|
|
912
|
+
UIColor *strongColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.strong.color);
|
|
913
|
+
[_config setStrongColor:strongColor];
|
|
914
|
+
} else {
|
|
915
|
+
[_config setStrongColor:nullptr];
|
|
916
|
+
}
|
|
917
|
+
stylePropChanged = YES;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (newViewProps.markdownStyle.em.color != oldViewProps.markdownStyle.em.color) {
|
|
921
|
+
if (newViewProps.markdownStyle.em.color) {
|
|
922
|
+
UIColor *emphasisColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.em.color);
|
|
923
|
+
[_config setEmphasisColor:emphasisColor];
|
|
924
|
+
} else {
|
|
925
|
+
[_config setEmphasisColor:nullptr];
|
|
926
|
+
}
|
|
927
|
+
stylePropChanged = YES;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (newViewProps.markdownStyle.strikethrough.color != oldViewProps.markdownStyle.strikethrough.color) {
|
|
931
|
+
UIColor *strikethroughColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.strikethrough.color);
|
|
932
|
+
[_config setStrikethroughColor:strikethroughColor];
|
|
933
|
+
stylePropChanged = YES;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if (newViewProps.markdownStyle.underline.color != oldViewProps.markdownStyle.underline.color) {
|
|
937
|
+
UIColor *underlineColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.underline.color);
|
|
938
|
+
[_config setUnderlineColor:underlineColor];
|
|
939
|
+
stylePropChanged = YES;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (newViewProps.markdownStyle.code.color != oldViewProps.markdownStyle.code.color) {
|
|
943
|
+
if (newViewProps.markdownStyle.code.color) {
|
|
944
|
+
UIColor *codeColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.code.color);
|
|
945
|
+
[_config setCodeColor:codeColor];
|
|
946
|
+
} else {
|
|
947
|
+
[_config setCodeColor:nullptr];
|
|
948
|
+
}
|
|
949
|
+
stylePropChanged = YES;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (newViewProps.markdownStyle.code.backgroundColor != oldViewProps.markdownStyle.code.backgroundColor) {
|
|
953
|
+
if (newViewProps.markdownStyle.code.backgroundColor) {
|
|
954
|
+
UIColor *codeBackgroundColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.code.backgroundColor);
|
|
955
|
+
[_config setCodeBackgroundColor:codeBackgroundColor];
|
|
956
|
+
} else {
|
|
957
|
+
[_config setCodeBackgroundColor:nullptr];
|
|
958
|
+
}
|
|
959
|
+
stylePropChanged = YES;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (newViewProps.markdownStyle.code.borderColor != oldViewProps.markdownStyle.code.borderColor) {
|
|
963
|
+
if (newViewProps.markdownStyle.code.borderColor) {
|
|
964
|
+
UIColor *codeBorderColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.code.borderColor);
|
|
965
|
+
[_config setCodeBorderColor:codeBorderColor];
|
|
966
|
+
} else {
|
|
967
|
+
[_config setCodeBorderColor:nullptr];
|
|
968
|
+
}
|
|
969
|
+
stylePropChanged = YES;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (newViewProps.markdownStyle.image.height != oldViewProps.markdownStyle.image.height) {
|
|
973
|
+
[_config setImageHeight:newViewProps.markdownStyle.image.height];
|
|
974
|
+
stylePropChanged = YES;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
if (newViewProps.markdownStyle.image.borderRadius != oldViewProps.markdownStyle.image.borderRadius) {
|
|
978
|
+
[_config setImageBorderRadius:newViewProps.markdownStyle.image.borderRadius];
|
|
979
|
+
stylePropChanged = YES;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (newViewProps.markdownStyle.image.marginTop != oldViewProps.markdownStyle.image.marginTop) {
|
|
983
|
+
[_config setImageMarginTop:newViewProps.markdownStyle.image.marginTop];
|
|
984
|
+
stylePropChanged = YES;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
if (newViewProps.markdownStyle.image.marginBottom != oldViewProps.markdownStyle.image.marginBottom) {
|
|
988
|
+
[_config setImageMarginBottom:newViewProps.markdownStyle.image.marginBottom];
|
|
989
|
+
stylePropChanged = YES;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (newViewProps.markdownStyle.inlineImage.size != oldViewProps.markdownStyle.inlineImage.size) {
|
|
993
|
+
[_config setInlineImageSize:newViewProps.markdownStyle.inlineImage.size];
|
|
994
|
+
stylePropChanged = YES;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// List style
|
|
998
|
+
if (newViewProps.markdownStyle.list.fontSize != oldViewProps.markdownStyle.list.fontSize) {
|
|
999
|
+
[_config setListStyleFontSize:newViewProps.markdownStyle.list.fontSize];
|
|
1000
|
+
stylePropChanged = YES;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (newViewProps.markdownStyle.list.fontFamily != oldViewProps.markdownStyle.list.fontFamily) {
|
|
1004
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.list.fontFamily.c_str()];
|
|
1005
|
+
[_config setListStyleFontFamily:fontFamily];
|
|
1006
|
+
stylePropChanged = YES;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if (newViewProps.markdownStyle.list.fontWeight != oldViewProps.markdownStyle.list.fontWeight) {
|
|
1010
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.list.fontWeight.c_str()];
|
|
1011
|
+
[_config setListStyleFontWeight:fontWeight];
|
|
1012
|
+
stylePropChanged = YES;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (newViewProps.markdownStyle.list.color != oldViewProps.markdownStyle.list.color) {
|
|
1016
|
+
UIColor *listColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.list.color);
|
|
1017
|
+
[_config setListStyleColor:listColor];
|
|
1018
|
+
stylePropChanged = YES;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (newViewProps.markdownStyle.list.marginTop != oldViewProps.markdownStyle.list.marginTop) {
|
|
1022
|
+
[_config setListStyleMarginTop:newViewProps.markdownStyle.list.marginTop];
|
|
1023
|
+
stylePropChanged = YES;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
if (newViewProps.markdownStyle.list.marginBottom != oldViewProps.markdownStyle.list.marginBottom) {
|
|
1027
|
+
[_config setListStyleMarginBottom:newViewProps.markdownStyle.list.marginBottom];
|
|
1028
|
+
stylePropChanged = YES;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (newViewProps.markdownStyle.list.lineHeight != oldViewProps.markdownStyle.list.lineHeight) {
|
|
1032
|
+
[_config setListStyleLineHeight:newViewProps.markdownStyle.list.lineHeight];
|
|
1033
|
+
stylePropChanged = YES;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (newViewProps.markdownStyle.list.bulletColor != oldViewProps.markdownStyle.list.bulletColor) {
|
|
1037
|
+
UIColor *bulletColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.list.bulletColor);
|
|
1038
|
+
[_config setListStyleBulletColor:bulletColor];
|
|
1039
|
+
stylePropChanged = YES;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (newViewProps.markdownStyle.list.bulletSize != oldViewProps.markdownStyle.list.bulletSize) {
|
|
1043
|
+
[_config setListStyleBulletSize:newViewProps.markdownStyle.list.bulletSize];
|
|
1044
|
+
stylePropChanged = YES;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if (newViewProps.markdownStyle.list.markerColor != oldViewProps.markdownStyle.list.markerColor) {
|
|
1048
|
+
UIColor *markerColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.list.markerColor);
|
|
1049
|
+
[_config setListStyleMarkerColor:markerColor];
|
|
1050
|
+
stylePropChanged = YES;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
if (newViewProps.markdownStyle.list.markerFontWeight != oldViewProps.markdownStyle.list.markerFontWeight) {
|
|
1054
|
+
NSString *markerFontWeight =
|
|
1055
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.list.markerFontWeight.c_str()];
|
|
1056
|
+
[_config setListStyleMarkerFontWeight:markerFontWeight];
|
|
1057
|
+
stylePropChanged = YES;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (newViewProps.markdownStyle.list.gapWidth != oldViewProps.markdownStyle.list.gapWidth) {
|
|
1061
|
+
[_config setListStyleGapWidth:newViewProps.markdownStyle.list.gapWidth];
|
|
1062
|
+
stylePropChanged = YES;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (newViewProps.markdownStyle.list.marginLeft != oldViewProps.markdownStyle.list.marginLeft) {
|
|
1066
|
+
[_config setListStyleMarginLeft:newViewProps.markdownStyle.list.marginLeft];
|
|
1067
|
+
stylePropChanged = YES;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// Code block style
|
|
1071
|
+
if (newViewProps.markdownStyle.codeBlock.fontSize != oldViewProps.markdownStyle.codeBlock.fontSize) {
|
|
1072
|
+
[_config setCodeBlockFontSize:newViewProps.markdownStyle.codeBlock.fontSize];
|
|
1073
|
+
stylePropChanged = YES;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
if (newViewProps.markdownStyle.codeBlock.fontFamily != oldViewProps.markdownStyle.codeBlock.fontFamily) {
|
|
1077
|
+
NSString *fontFamily =
|
|
1078
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.codeBlock.fontFamily.c_str()];
|
|
1079
|
+
[_config setCodeBlockFontFamily:fontFamily];
|
|
1080
|
+
stylePropChanged = YES;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (newViewProps.markdownStyle.codeBlock.fontWeight != oldViewProps.markdownStyle.codeBlock.fontWeight) {
|
|
1084
|
+
NSString *fontWeight =
|
|
1085
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.codeBlock.fontWeight.c_str()];
|
|
1086
|
+
[_config setCodeBlockFontWeight:fontWeight];
|
|
1087
|
+
stylePropChanged = YES;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
if (newViewProps.markdownStyle.codeBlock.color != oldViewProps.markdownStyle.codeBlock.color) {
|
|
1091
|
+
UIColor *codeBlockColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.codeBlock.color);
|
|
1092
|
+
[_config setCodeBlockColor:codeBlockColor];
|
|
1093
|
+
stylePropChanged = YES;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
if (newViewProps.markdownStyle.codeBlock.marginTop != oldViewProps.markdownStyle.codeBlock.marginTop) {
|
|
1097
|
+
[_config setCodeBlockMarginTop:newViewProps.markdownStyle.codeBlock.marginTop];
|
|
1098
|
+
stylePropChanged = YES;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (newViewProps.markdownStyle.codeBlock.marginBottom != oldViewProps.markdownStyle.codeBlock.marginBottom) {
|
|
1102
|
+
[_config setCodeBlockMarginBottom:newViewProps.markdownStyle.codeBlock.marginBottom];
|
|
1103
|
+
stylePropChanged = YES;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (newViewProps.markdownStyle.codeBlock.lineHeight != oldViewProps.markdownStyle.codeBlock.lineHeight) {
|
|
1107
|
+
[_config setCodeBlockLineHeight:newViewProps.markdownStyle.codeBlock.lineHeight];
|
|
1108
|
+
stylePropChanged = YES;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
if (newViewProps.markdownStyle.codeBlock.backgroundColor != oldViewProps.markdownStyle.codeBlock.backgroundColor) {
|
|
1112
|
+
UIColor *codeBlockBackgroundColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.codeBlock.backgroundColor);
|
|
1113
|
+
[_config setCodeBlockBackgroundColor:codeBlockBackgroundColor];
|
|
1114
|
+
stylePropChanged = YES;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
if (newViewProps.markdownStyle.codeBlock.borderColor != oldViewProps.markdownStyle.codeBlock.borderColor) {
|
|
1118
|
+
UIColor *codeBlockBorderColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.codeBlock.borderColor);
|
|
1119
|
+
[_config setCodeBlockBorderColor:codeBlockBorderColor];
|
|
1120
|
+
stylePropChanged = YES;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
if (newViewProps.markdownStyle.codeBlock.borderRadius != oldViewProps.markdownStyle.codeBlock.borderRadius) {
|
|
1124
|
+
[_config setCodeBlockBorderRadius:newViewProps.markdownStyle.codeBlock.borderRadius];
|
|
1125
|
+
stylePropChanged = YES;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (newViewProps.markdownStyle.codeBlock.borderWidth != oldViewProps.markdownStyle.codeBlock.borderWidth) {
|
|
1129
|
+
[_config setCodeBlockBorderWidth:newViewProps.markdownStyle.codeBlock.borderWidth];
|
|
1130
|
+
stylePropChanged = YES;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (newViewProps.markdownStyle.codeBlock.padding != oldViewProps.markdownStyle.codeBlock.padding) {
|
|
1134
|
+
[_config setCodeBlockPadding:newViewProps.markdownStyle.codeBlock.padding];
|
|
1135
|
+
stylePropChanged = YES;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// Thematic break style
|
|
1139
|
+
if (newViewProps.markdownStyle.thematicBreak.color != oldViewProps.markdownStyle.thematicBreak.color) {
|
|
1140
|
+
UIColor *thematicBreakColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.thematicBreak.color);
|
|
1141
|
+
[_config setThematicBreakColor:thematicBreakColor];
|
|
1142
|
+
stylePropChanged = YES;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (newViewProps.markdownStyle.thematicBreak.height != oldViewProps.markdownStyle.thematicBreak.height) {
|
|
1146
|
+
[_config setThematicBreakHeight:newViewProps.markdownStyle.thematicBreak.height];
|
|
1147
|
+
stylePropChanged = YES;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (newViewProps.markdownStyle.thematicBreak.marginTop != oldViewProps.markdownStyle.thematicBreak.marginTop) {
|
|
1151
|
+
[_config setThematicBreakMarginTop:newViewProps.markdownStyle.thematicBreak.marginTop];
|
|
1152
|
+
stylePropChanged = YES;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
if (newViewProps.markdownStyle.thematicBreak.marginBottom != oldViewProps.markdownStyle.thematicBreak.marginBottom) {
|
|
1156
|
+
[_config setThematicBreakMarginBottom:newViewProps.markdownStyle.thematicBreak.marginBottom];
|
|
1157
|
+
stylePropChanged = YES;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Update config reference on layout manager if it's not already set
|
|
1161
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
1162
|
+
if ([layoutManager isKindOfClass:[TextViewLayoutManager class]]) {
|
|
1163
|
+
StyleConfig *currentConfig = [layoutManager valueForKey:@"config"];
|
|
1164
|
+
if (currentConfig != _config) {
|
|
1165
|
+
// Only update reference if it's different (first time setup)
|
|
1166
|
+
[layoutManager setValue:_config forKey:@"config"];
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Control text selection and link previews via selectable property
|
|
1171
|
+
// According to Apple docs, selectable controls whether text selection and link previews work
|
|
1172
|
+
// https://developer.apple.com/documentation/uikit/uitextview/isselectable
|
|
1173
|
+
if (_textView.selectable != newViewProps.selectable) {
|
|
1174
|
+
_textView.selectable = newViewProps.selectable;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
if (newViewProps.allowFontScaling != oldViewProps.allowFontScaling) {
|
|
1178
|
+
_allowFontScaling = newViewProps.allowFontScaling;
|
|
1179
|
+
|
|
1180
|
+
if (_config != nil) {
|
|
1181
|
+
[_config setFontScaleMultiplier:[self effectiveFontScale]];
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
stylePropChanged = YES;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if (newViewProps.maxFontSizeMultiplier != oldViewProps.maxFontSizeMultiplier) {
|
|
1188
|
+
_maxFontSizeMultiplier = newViewProps.maxFontSizeMultiplier;
|
|
1189
|
+
|
|
1190
|
+
if (_config != nil) {
|
|
1191
|
+
[_config setMaxFontSizeMultiplier:_maxFontSizeMultiplier];
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
stylePropChanged = YES;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// Update allowTrailingMargin
|
|
1198
|
+
if (newViewProps.allowTrailingMargin != oldViewProps.allowTrailingMargin) {
|
|
1199
|
+
_allowTrailingMargin = newViewProps.allowTrailingMargin;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// Update md4cFlags
|
|
1203
|
+
BOOL md4cFlagsChanged = NO;
|
|
1204
|
+
if (newViewProps.md4cFlags.underline != oldViewProps.md4cFlags.underline) {
|
|
1205
|
+
_md4cFlags.underline = newViewProps.md4cFlags.underline;
|
|
1206
|
+
md4cFlagsChanged = YES;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
BOOL markdownChanged = oldViewProps.markdown != newViewProps.markdown;
|
|
1210
|
+
BOOL allowTrailingMarginChanged = newViewProps.allowTrailingMargin != oldViewProps.allowTrailingMargin;
|
|
1211
|
+
|
|
1212
|
+
_enableLinkPreview = newViewProps.enableLinkPreview;
|
|
1213
|
+
|
|
1214
|
+
if (markdownChanged || stylePropChanged || md4cFlagsChanged || allowTrailingMarginChanged) {
|
|
1215
|
+
NSString *markdownString = [[NSString alloc] initWithUTF8String:newViewProps.markdown.c_str()];
|
|
1216
|
+
[self renderMarkdownContent:markdownString];
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
[super updateProps:props oldProps:oldProps];
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
1223
|
+
{
|
|
1224
|
+
return EnrichedMarkdownText.class;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
- (void)textTapped:(UITapGestureRecognizer *)recognizer
|
|
1228
|
+
{
|
|
1229
|
+
/*
|
|
1230
|
+
* HOW LINK TAPPING WORKS:
|
|
1231
|
+
*
|
|
1232
|
+
* 1. SETUP PHASE (During Rendering):
|
|
1233
|
+
* - Each link gets a custom @"linkURL" attribute attached to its text range
|
|
1234
|
+
* - The URL is stored as the attribute's value
|
|
1235
|
+
* - This creates an "invisible map" of where links are in the text
|
|
1236
|
+
*
|
|
1237
|
+
* Example:
|
|
1238
|
+
* Text: "Check out this [link to React Native](https://reactnative.dev)"
|
|
1239
|
+
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1240
|
+
* | | | | | | | | | | |
|
|
1241
|
+
* 0 5 10 15 20 25 30 35 40 45 50
|
|
1242
|
+
*
|
|
1243
|
+
* Attributes:
|
|
1244
|
+
* - Characters 15-29: @"linkURL" = "https://reactnative.dev"
|
|
1245
|
+
* - Characters 0-14: no special attributes
|
|
1246
|
+
* - Characters 30-50: no special attributes
|
|
1247
|
+
*
|
|
1248
|
+
* 2. TOUCH DETECTION PHASE (When User Taps):
|
|
1249
|
+
* - UITapGestureRecognizer detects the tap
|
|
1250
|
+
* - We get the tap coordinates relative to the text view
|
|
1251
|
+
* - We adjust for text container insets to get precise text coordinates
|
|
1252
|
+
*/
|
|
1253
|
+
|
|
1254
|
+
UITextView *textView = (UITextView *)recognizer.view;
|
|
1255
|
+
|
|
1256
|
+
// Location of the tap in text-container coordinates
|
|
1257
|
+
NSLayoutManager *layoutManager = textView.layoutManager;
|
|
1258
|
+
CGPoint location = [recognizer locationInView:textView];
|
|
1259
|
+
location.x -= textView.textContainerInset.left;
|
|
1260
|
+
location.y -= textView.textContainerInset.top;
|
|
1261
|
+
|
|
1262
|
+
/*
|
|
1263
|
+
* 3. CHARACTER INDEX LOOKUP:
|
|
1264
|
+
* - NSLayoutManager converts the tap coordinates to a character index
|
|
1265
|
+
* - This tells us exactly which character in the text was tapped
|
|
1266
|
+
* - Uses UIKit's built-in text layout system (very accurate)
|
|
1267
|
+
*/
|
|
1268
|
+
NSUInteger characterIndex;
|
|
1269
|
+
characterIndex = [layoutManager characterIndexForPoint:location
|
|
1270
|
+
inTextContainer:textView.textContainer
|
|
1271
|
+
fractionOfDistanceBetweenInsertionPoints:NULL];
|
|
1272
|
+
|
|
1273
|
+
/*
|
|
1274
|
+
* 4. LINK DETECTION:
|
|
1275
|
+
* - We check if there's a @"linkURL" attribute at the tapped character
|
|
1276
|
+
* - If found, we get the URL value and the effective range
|
|
1277
|
+
* - If it's a link, we emit the onLinkPress event to React Native
|
|
1278
|
+
*
|
|
1279
|
+
* COMPLETE FLOW:
|
|
1280
|
+
* 1. User taps → UITapGestureRecognizer fires
|
|
1281
|
+
* 2. Get coordinates → Convert to text container coordinates
|
|
1282
|
+
* 3. Find character → NSLayoutManager.characterIndexForPoint
|
|
1283
|
+
* 4. Check attributes → Look for @"linkURL" at that character
|
|
1284
|
+
* 5. If link found → Emit onLinkPress event with URL
|
|
1285
|
+
* 6. React Native → Receives event and shows alert
|
|
1286
|
+
*/
|
|
1287
|
+
if (characterIndex < textView.textStorage.length) {
|
|
1288
|
+
NSRange range;
|
|
1289
|
+
NSString *url = [textView.attributedText attribute:@"linkURL" atIndex:characterIndex effectiveRange:&range];
|
|
1290
|
+
|
|
1291
|
+
if (url) {
|
|
1292
|
+
// Emit onLinkPress event to React Native
|
|
1293
|
+
const auto &eventEmitter = *std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
1294
|
+
eventEmitter.onLinkPress({.url = std::string([url UTF8String])});
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
#pragma mark - UITextViewDelegate (Link Interaction)
|
|
1300
|
+
|
|
1301
|
+
- (BOOL)textView:(UITextView *)textView
|
|
1302
|
+
shouldInteractWithURL:(NSURL *)URL
|
|
1303
|
+
inRange:(NSRange)characterRange
|
|
1304
|
+
interaction:(UITextItemInteraction)interaction
|
|
1305
|
+
{
|
|
1306
|
+
// Only intercept long-press interactions
|
|
1307
|
+
if (interaction != UITextItemInteractionPresentActions) {
|
|
1308
|
+
return YES;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// Safely extract the custom URL attribute
|
|
1312
|
+
NSString *urlString = [textView.attributedText attribute:@"linkURL"
|
|
1313
|
+
atIndex:characterRange.location
|
|
1314
|
+
effectiveRange:NULL];
|
|
1315
|
+
|
|
1316
|
+
// If link preview is enabled or no URL found, allow default system behavior
|
|
1317
|
+
if (!urlString || _enableLinkPreview) {
|
|
1318
|
+
return YES;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
// System preview disabled — emit onLinkLongPress event to React Native
|
|
1322
|
+
auto eventEmitter = std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
1323
|
+
if (eventEmitter) {
|
|
1324
|
+
eventEmitter->onLinkLongPress({.url = std::string([urlString UTF8String])});
|
|
1325
|
+
}
|
|
1326
|
+
return NO;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
#pragma mark - UITextViewDelegate (Edit Menu)
|
|
1330
|
+
|
|
1331
|
+
// Customizes the edit menu
|
|
1332
|
+
- (UIMenu *)textView:(UITextView *)textView
|
|
1333
|
+
editMenuForTextInRange:(NSRange)range
|
|
1334
|
+
suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
|
|
1335
|
+
{
|
|
1336
|
+
return buildEditMenuForSelection(_textView.attributedText, range, _cachedMarkdown, _config, suggestedActions);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
#pragma mark - Accessibility (VoiceOver Navigation)
|
|
1340
|
+
|
|
1341
|
+
- (void)buildAccessibilityElements
|
|
1342
|
+
{
|
|
1343
|
+
_accessibilityElements = [MarkdownAccessibilityElementBuilder buildElementsForTextView:_textView
|
|
1344
|
+
info:_accessibilityInfo
|
|
1345
|
+
container:self];
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
- (BOOL)isAccessibilityElement
|
|
1349
|
+
{
|
|
1350
|
+
return NO; // This is a container, not a single element
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
- (NSInteger)accessibilityElementCount
|
|
1354
|
+
{
|
|
1355
|
+
return _accessibilityElements.count;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
- (id)accessibilityElementAtIndex:(NSInteger)index
|
|
1359
|
+
{
|
|
1360
|
+
if (index < 0 || index >= (NSInteger)_accessibilityElements.count) {
|
|
1361
|
+
return nil;
|
|
1362
|
+
}
|
|
1363
|
+
return _accessibilityElements[index];
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
- (NSInteger)indexOfAccessibilityElement:(id)element
|
|
1367
|
+
{
|
|
1368
|
+
return [_accessibilityElements indexOfObject:element];
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
- (NSArray *)accessibilityElements
|
|
1372
|
+
{
|
|
1373
|
+
return _accessibilityElements;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
- (NSArray<UIAccessibilityCustomRotor *> *)accessibilityCustomRotors
|
|
1377
|
+
{
|
|
1378
|
+
NSMutableArray<UIAccessibilityCustomRotor *> *rotors = [NSMutableArray array];
|
|
1379
|
+
|
|
1380
|
+
NSArray<UIAccessibilityElement *> *headingElements =
|
|
1381
|
+
[MarkdownAccessibilityElementBuilder filterHeadingElements:_accessibilityElements];
|
|
1382
|
+
if (headingElements.count > 0) {
|
|
1383
|
+
[rotors addObject:[MarkdownAccessibilityElementBuilder createHeadingRotorWithElements:headingElements]];
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
NSArray<UIAccessibilityElement *> *linkElements =
|
|
1387
|
+
[MarkdownAccessibilityElementBuilder filterLinkElements:_accessibilityElements];
|
|
1388
|
+
if (linkElements.count > 0) {
|
|
1389
|
+
[rotors addObject:[MarkdownAccessibilityElementBuilder createLinkRotorWithElements:linkElements]];
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
NSArray<UIAccessibilityElement *> *imageElements =
|
|
1393
|
+
[MarkdownAccessibilityElementBuilder filterImageElements:_accessibilityElements];
|
|
1394
|
+
if (imageElements.count > 0) {
|
|
1395
|
+
[rotors addObject:[MarkdownAccessibilityElementBuilder createImageRotorWithElements:imageElements]];
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
return rotors;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
@end
|