react-native-enriched-markdown 0.1.0 → 0.1.1
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 +479 -0
- package/ReactNativeEnrichedMarkdown.podspec +27 -0
- package/android/build.gradle +101 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +39 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +21 -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 +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +25 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +57 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1164 -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 +203 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +153 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +119 -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 +165 -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 +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +48 -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 +89 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +105 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +52 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +28 -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 +58 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +99 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +66 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +95 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +85 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +44 -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 +320 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +37 -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/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 +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +48 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +51 -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 +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +21 -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 +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +29 -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 +180 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +75 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ThematicBreakStyle.kt +23 -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 +809 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +365 -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 +181 -0
- package/android/src/main/jni/CMakeLists.txt +82 -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 +33 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +49 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +25 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +19 -0
- package/cpp/md4c/md4c.c +6492 -0
- package/cpp/md4c/md4c.h +402 -0
- package/cpp/parser/MD4CParser.cpp +314 -0
- package/cpp/parser/MD4CParser.hpp +23 -0
- package/cpp/parser/MarkdownASTNode.hpp +49 -0
- package/ios/EnrichedMarkdownText.h +18 -0
- package/ios/EnrichedMarkdownText.mm +1074 -0
- package/ios/attachments/ImageAttachment.h +23 -0
- package/ios/attachments/ImageAttachment.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 +24 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +25 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +57 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.h +1164 -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 +33 -0
- package/ios/parser/MarkdownASTNode.m +32 -0
- package/ios/parser/MarkdownParser.h +8 -0
- package/ios/parser/MarkdownParser.mm +13 -0
- package/ios/parser/MarkdownParserBridge.mm +110 -0
- package/ios/renderer/AttributedRenderer.h +9 -0
- package/ios/renderer/AttributedRenderer.m +119 -0
- package/ios/renderer/BlockquoteRenderer.h +7 -0
- package/ios/renderer/BlockquoteRenderer.m +159 -0
- package/ios/renderer/CodeBlockRenderer.h +10 -0
- package/ios/renderer/CodeBlockRenderer.m +89 -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 +98 -0
- package/ios/renderer/ImageRenderer.h +12 -0
- package/ios/renderer/ImageRenderer.m +62 -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 +91 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +67 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +7 -0
- package/ios/renderer/ParagraphRenderer.m +69 -0
- package/ios/renderer/RenderContext.h +88 -0
- package/ios/renderer/RenderContext.m +248 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +110 -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/styles/StyleConfig.h +228 -0
- package/ios/styles/StyleConfig.mm +1467 -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 +87 -0
- package/ios/utils/EditMenuUtils.h +22 -0
- package/ios/utils/EditMenuUtils.m +118 -0
- package/ios/utils/FontUtils.h +20 -0
- package/ios/utils/FontUtils.m +13 -0
- package/ios/utils/HTMLGenerator.h +20 -0
- package/ios/utils/HTMLGenerator.m +779 -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/MarkdownExtractor.h +17 -0
- package/ios/utils/MarkdownExtractor.m +295 -0
- package/ios/utils/ParagraphStyleUtils.h +13 -0
- package/ios/utils/ParagraphStyleUtils.m +56 -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 +34 -0
- package/lib/module/EnrichedMarkdownText.js.map +1 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +130 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/normalizeMarkdownStyle.js +340 -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 +101 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +111 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -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 +152 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +130 -0
- package/src/index.tsx +7 -0
- package/src/normalizeMarkdownStyle.ts +377 -0
|
@@ -0,0 +1,1074 @@
|
|
|
1
|
+
#import "EnrichedMarkdownText.h"
|
|
2
|
+
#import "AttributedRenderer.h"
|
|
3
|
+
#import "CodeBlockBackground.h"
|
|
4
|
+
#import "EditMenuUtils.h"
|
|
5
|
+
#import "FontUtils.h"
|
|
6
|
+
#import "ImageAttachment.h"
|
|
7
|
+
#import "LastElementUtils.h"
|
|
8
|
+
#import "MarkdownASTNode.h"
|
|
9
|
+
#import "MarkdownExtractor.h"
|
|
10
|
+
#import "MarkdownParser.h"
|
|
11
|
+
#import "RenderContext.h"
|
|
12
|
+
#import "RuntimeKeys.h"
|
|
13
|
+
#import "StyleConfig.h"
|
|
14
|
+
#import "TextViewLayoutManager.h"
|
|
15
|
+
#import <objc/runtime.h>
|
|
16
|
+
|
|
17
|
+
#import <ReactNativeEnrichedMarkdown/EnrichedMarkdownTextComponentDescriptor.h>
|
|
18
|
+
#import <ReactNativeEnrichedMarkdown/EventEmitters.h>
|
|
19
|
+
#import <ReactNativeEnrichedMarkdown/Props.h>
|
|
20
|
+
#import <ReactNativeEnrichedMarkdown/RCTComponentViewHelpers.h>
|
|
21
|
+
|
|
22
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
23
|
+
#import <React/RCTConversions.h>
|
|
24
|
+
#import <React/RCTFont.h>
|
|
25
|
+
#import <react/utils/ManagedObjectWrapper.h>
|
|
26
|
+
|
|
27
|
+
using namespace facebook::react;
|
|
28
|
+
|
|
29
|
+
@interface EnrichedMarkdownText () <RCTEnrichedMarkdownTextViewProtocol, UITextViewDelegate>
|
|
30
|
+
- (void)setupTextView;
|
|
31
|
+
- (void)renderMarkdownContent:(NSString *)markdownString;
|
|
32
|
+
- (void)applyRenderedText:(NSMutableAttributedString *)attributedText;
|
|
33
|
+
- (void)textTapped:(UITapGestureRecognizer *)recognizer;
|
|
34
|
+
- (void)setupLayoutManager;
|
|
35
|
+
@end
|
|
36
|
+
|
|
37
|
+
@implementation EnrichedMarkdownText {
|
|
38
|
+
UITextView *_textView;
|
|
39
|
+
MarkdownParser *_parser;
|
|
40
|
+
NSString *_cachedMarkdown;
|
|
41
|
+
StyleConfig *_config;
|
|
42
|
+
|
|
43
|
+
// Background rendering support
|
|
44
|
+
dispatch_queue_t _renderQueue;
|
|
45
|
+
NSUInteger _currentRenderId;
|
|
46
|
+
BOOL _blockAsyncRender;
|
|
47
|
+
|
|
48
|
+
EnrichedMarkdownTextShadowNode::ConcreteState::Shared _state;
|
|
49
|
+
int _heightUpdateCounter;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
53
|
+
{
|
|
54
|
+
return concreteComponentDescriptorProvider<EnrichedMarkdownTextComponentDescriptor>();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#pragma mark - Measuring and State
|
|
58
|
+
|
|
59
|
+
- (CGSize)measureSize:(CGFloat)maxWidth
|
|
60
|
+
{
|
|
61
|
+
NSAttributedString *text = _textView.attributedText;
|
|
62
|
+
CGFloat defaultHeight = [UIFont systemFontOfSize:16.0].lineHeight;
|
|
63
|
+
|
|
64
|
+
if (!text || text.length == 0) {
|
|
65
|
+
return CGSizeMake(maxWidth, defaultHeight);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Find last content character (exclude trailing newlines from measurement)
|
|
69
|
+
NSRange lastContent = [text.string rangeOfCharacterFromSet:[[NSCharacterSet newlineCharacterSet] invertedSet]
|
|
70
|
+
options:NSBackwardsSearch];
|
|
71
|
+
if (lastContent.location == NSNotFound) {
|
|
72
|
+
return CGSizeMake(maxWidth, defaultHeight);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
NSAttributedString *contentToMeasure = [text attributedSubstringFromRange:NSMakeRange(0, NSMaxRange(lastContent))];
|
|
76
|
+
|
|
77
|
+
// Use NSStringDrawingUsesDeviceMetrics for tighter bounds (especially for images)
|
|
78
|
+
NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading;
|
|
79
|
+
if (isLastElementImage(text)) {
|
|
80
|
+
options |= NSStringDrawingUsesDeviceMetrics;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
CGRect boundingRect = [contentToMeasure boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
|
|
84
|
+
options:options
|
|
85
|
+
context:nil];
|
|
86
|
+
|
|
87
|
+
CGFloat measuredHeight = boundingRect.size.height;
|
|
88
|
+
|
|
89
|
+
// Compensate for iOS not measuring trailing newlines (code block bottom padding)
|
|
90
|
+
if (isLastElementCodeBlock(text)) {
|
|
91
|
+
measuredHeight += [_config codeBlockPadding];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return CGSizeMake(maxWidth, ceil(measuredHeight));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
- (void)updateState:(const facebook::react::State::Shared &)state
|
|
98
|
+
oldState:(const facebook::react::State::Shared &)oldState
|
|
99
|
+
{
|
|
100
|
+
_state = std::static_pointer_cast<const EnrichedMarkdownTextShadowNode::ConcreteState>(state);
|
|
101
|
+
|
|
102
|
+
// Trigger initial height calculation when state is first set
|
|
103
|
+
if (oldState == nullptr) {
|
|
104
|
+
[self requestHeightUpdate];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (void)requestHeightUpdate
|
|
109
|
+
{
|
|
110
|
+
if (_state == nullptr) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_heightUpdateCounter++;
|
|
115
|
+
auto selfRef = wrapManagedObjectWeakly(self);
|
|
116
|
+
_state->updateState(EnrichedMarkdownTextState(_heightUpdateCounter, selfRef));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
120
|
+
{
|
|
121
|
+
if (self = [super initWithFrame:frame]) {
|
|
122
|
+
static const auto defaultProps = std::make_shared<const EnrichedMarkdownTextProps>();
|
|
123
|
+
_props = defaultProps;
|
|
124
|
+
|
|
125
|
+
self.backgroundColor = [UIColor clearColor];
|
|
126
|
+
_parser = [[MarkdownParser alloc] init];
|
|
127
|
+
|
|
128
|
+
// Serial queue for background rendering
|
|
129
|
+
_renderQueue = dispatch_queue_create("com.swmansion.enriched.markdown.render", DISPATCH_QUEUE_SERIAL);
|
|
130
|
+
_currentRenderId = 0;
|
|
131
|
+
|
|
132
|
+
[self setupTextView];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return self;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
- (void)setupTextView
|
|
139
|
+
{
|
|
140
|
+
_textView = [[UITextView alloc] init];
|
|
141
|
+
_textView.text = @"";
|
|
142
|
+
_textView.font = [UIFont systemFontOfSize:16.0];
|
|
143
|
+
_textView.backgroundColor = [UIColor clearColor];
|
|
144
|
+
_textView.textColor = [UIColor blackColor];
|
|
145
|
+
_textView.editable = NO;
|
|
146
|
+
_textView.delegate = self;
|
|
147
|
+
_textView.scrollEnabled = NO;
|
|
148
|
+
_textView.textContainerInset = UIEdgeInsetsZero;
|
|
149
|
+
_textView.textContainer.lineFragmentPadding = 0;
|
|
150
|
+
// Disable UITextView's default link styling - we handle it directly in attributed strings
|
|
151
|
+
_textView.linkTextAttributes = @{};
|
|
152
|
+
// isSelectable controls text selection and link previews
|
|
153
|
+
// Default to YES to match the prop default
|
|
154
|
+
_textView.selectable = YES;
|
|
155
|
+
// Hide initially to prevent flash before content is rendered
|
|
156
|
+
_textView.hidden = YES;
|
|
157
|
+
|
|
158
|
+
// Add tap gesture recognizer
|
|
159
|
+
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
|
|
160
|
+
action:@selector(textTapped:)];
|
|
161
|
+
[_textView addGestureRecognizer:tapRecognizer];
|
|
162
|
+
|
|
163
|
+
// Use RCTViewComponentView's contentView for automatic sizing
|
|
164
|
+
self.contentView = _textView;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
- (void)didAddSubview:(UIView *)subview
|
|
168
|
+
{
|
|
169
|
+
[super didAddSubview:subview];
|
|
170
|
+
|
|
171
|
+
// Set up layout manager when text view is added
|
|
172
|
+
if (subview == _textView) {
|
|
173
|
+
[self setupLayoutManager];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
- (void)willRemoveSubview:(UIView *)subview
|
|
178
|
+
{
|
|
179
|
+
// Clean up layout manager when text view is removed
|
|
180
|
+
if (subview == _textView && _textView.layoutManager != nil) {
|
|
181
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
182
|
+
if ([object_getClass(layoutManager) isEqual:[TextViewLayoutManager class]]) {
|
|
183
|
+
[layoutManager setValue:nil forKey:@"config"];
|
|
184
|
+
object_setClass(layoutManager, [NSLayoutManager class]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
[super willRemoveSubview:subview];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
- (void)setupLayoutManager
|
|
191
|
+
{
|
|
192
|
+
// Set up custom layout manager for rich text custom drawing (code, blockquotes, etc.)
|
|
193
|
+
// This single manager can handle multiple element types
|
|
194
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
195
|
+
if (layoutManager != nil) {
|
|
196
|
+
layoutManager.allowsNonContiguousLayout = NO; // workaround for onScroll issue (like react-native-live-markdown)
|
|
197
|
+
object_setClass(layoutManager, [TextViewLayoutManager class]);
|
|
198
|
+
|
|
199
|
+
// Set config on layout manager (like react-native-live-markdown sets markdownUtils)
|
|
200
|
+
if (_config != nil) {
|
|
201
|
+
[layoutManager setValue:_config forKey:@"config"];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
- (void)renderMarkdownContent:(NSString *)markdownString
|
|
207
|
+
{
|
|
208
|
+
// Skip async render for mock views (they use renderMarkdownSynchronously)
|
|
209
|
+
if (_blockAsyncRender) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
_cachedMarkdown = [markdownString copy];
|
|
214
|
+
|
|
215
|
+
// Increment render ID to invalidate any in-flight renders
|
|
216
|
+
NSUInteger renderId = ++_currentRenderId;
|
|
217
|
+
|
|
218
|
+
// Capture state needed for background rendering
|
|
219
|
+
StyleConfig *config = [_config copy];
|
|
220
|
+
MarkdownParser *parser = _parser;
|
|
221
|
+
NSUInteger inputLength = markdownString.length;
|
|
222
|
+
NSDate *scheduleStart = [NSDate date];
|
|
223
|
+
|
|
224
|
+
// Dispatch heavy work to background queue
|
|
225
|
+
dispatch_async(_renderQueue, ^{
|
|
226
|
+
// 1. Parse Markdown → AST (C++ md4c parser)
|
|
227
|
+
NSDate *parseStart = [NSDate date];
|
|
228
|
+
MarkdownASTNode *ast = [parser parseMarkdown:markdownString];
|
|
229
|
+
if (!ast) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
NSTimeInterval parseTime = [[NSDate date] timeIntervalSinceDate:parseStart] * 1000;
|
|
233
|
+
NSUInteger nodeCount = ast.children.count;
|
|
234
|
+
|
|
235
|
+
// 2. Render AST → NSAttributedString
|
|
236
|
+
NSDate *renderStart = [NSDate date];
|
|
237
|
+
AttributedRenderer *renderer = [[AttributedRenderer alloc] initWithConfig:config];
|
|
238
|
+
RenderContext *context = [RenderContext new];
|
|
239
|
+
NSMutableAttributedString *attributedText = [renderer renderRoot:ast context:context];
|
|
240
|
+
|
|
241
|
+
// Add link attributes
|
|
242
|
+
for (NSUInteger i = 0; i < context.linkRanges.count; i++) {
|
|
243
|
+
NSValue *rangeValue = context.linkRanges[i];
|
|
244
|
+
NSRange range = [rangeValue rangeValue];
|
|
245
|
+
NSString *url = context.linkURLs[i];
|
|
246
|
+
[attributedText addAttribute:@"linkURL" value:url range:range];
|
|
247
|
+
}
|
|
248
|
+
NSTimeInterval renderTime = [[NSDate date] timeIntervalSinceDate:renderStart] * 1000;
|
|
249
|
+
NSUInteger styledLength = attributedText.length;
|
|
250
|
+
|
|
251
|
+
// Apply result on main thread
|
|
252
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
253
|
+
// Check if this render is still current
|
|
254
|
+
if (renderId != self->_currentRenderId) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
[self applyRenderedText:attributedText];
|
|
259
|
+
|
|
260
|
+
NSTimeInterval totalTime = [[NSDate date] timeIntervalSinceDate:scheduleStart] * 1000;
|
|
261
|
+
NSLog(@"┌──────────────────────────────────────────────");
|
|
262
|
+
NSLog(@"│ 📝 Input: %lu chars of Markdown", (unsigned long)inputLength);
|
|
263
|
+
NSLog(@"│ ⚡ md4c (C++ native): %.0fms → %lu AST nodes", parseTime, (unsigned long)nodeCount);
|
|
264
|
+
NSLog(@"│ 🎨 NSAttributedString render: %.0fms → %lu styled chars", renderTime, (unsigned long)styledLength);
|
|
265
|
+
NSLog(@"│ ✅ Total time to display: %.0fms", totalTime);
|
|
266
|
+
NSLog(@"└──────────────────────────────────────────────");
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Synchronous rendering for mock view measurement (no UI updates needed)
|
|
272
|
+
- (void)renderMarkdownSynchronously:(NSString *)markdownString
|
|
273
|
+
{
|
|
274
|
+
if (!markdownString || markdownString.length == 0) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Block any async renders triggered by updateProps
|
|
279
|
+
_blockAsyncRender = YES;
|
|
280
|
+
_cachedMarkdown = [markdownString copy];
|
|
281
|
+
|
|
282
|
+
MarkdownASTNode *ast = [_parser parseMarkdown:markdownString];
|
|
283
|
+
if (!ast) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
AttributedRenderer *renderer = [[AttributedRenderer alloc] initWithConfig:_config];
|
|
288
|
+
RenderContext *context = [RenderContext new];
|
|
289
|
+
NSMutableAttributedString *attributedText = [renderer renderRoot:ast context:context];
|
|
290
|
+
|
|
291
|
+
for (NSUInteger i = 0; i < context.linkRanges.count; i++) {
|
|
292
|
+
NSValue *rangeValue = context.linkRanges[i];
|
|
293
|
+
NSRange range = [rangeValue rangeValue];
|
|
294
|
+
NSString *url = context.linkURLs[i];
|
|
295
|
+
[attributedText addAttribute:@"linkURL" value:url range:range];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
_textView.attributedText = attributedText;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
- (void)applyRenderedText:(NSMutableAttributedString *)attributedText
|
|
302
|
+
{
|
|
303
|
+
// Set config on the layout manager
|
|
304
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
305
|
+
if ([layoutManager isKindOfClass:[TextViewLayoutManager class]]) {
|
|
306
|
+
[layoutManager setValue:_config forKey:@"config"];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Store text view on text container so attachments can access it
|
|
310
|
+
objc_setAssociatedObject(_textView.textContainer, kTextViewKey, _textView, OBJC_ASSOCIATION_ASSIGN);
|
|
311
|
+
|
|
312
|
+
_textView.attributedText = attributedText;
|
|
313
|
+
|
|
314
|
+
// Ensure layout is updated
|
|
315
|
+
[_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer];
|
|
316
|
+
[_textView.layoutManager invalidateLayoutForCharacterRange:NSMakeRange(0, attributedText.length)
|
|
317
|
+
actualCharacterRange:NULL];
|
|
318
|
+
|
|
319
|
+
[_textView setNeedsLayout];
|
|
320
|
+
[_textView setNeedsDisplay];
|
|
321
|
+
[self setNeedsLayout];
|
|
322
|
+
|
|
323
|
+
// Request height recalculation from shadow node FIRST
|
|
324
|
+
[self requestHeightUpdate];
|
|
325
|
+
|
|
326
|
+
// Show text view on next run loop, after layout has settled
|
|
327
|
+
if (_textView.hidden) {
|
|
328
|
+
dispatch_async(dispatch_get_main_queue(), ^{ self->_textView.hidden = NO; });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
333
|
+
{
|
|
334
|
+
const auto &oldViewProps = *std::static_pointer_cast<EnrichedMarkdownTextProps const>(_props);
|
|
335
|
+
const auto &newViewProps = *std::static_pointer_cast<EnrichedMarkdownTextProps const>(props);
|
|
336
|
+
|
|
337
|
+
BOOL stylePropChanged = NO;
|
|
338
|
+
|
|
339
|
+
if (_config == nil) {
|
|
340
|
+
_config = [[StyleConfig alloc] init];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Paragraph style
|
|
344
|
+
if (newViewProps.markdownStyle.paragraph.fontSize != oldViewProps.markdownStyle.paragraph.fontSize) {
|
|
345
|
+
[_config setParagraphFontSize:newViewProps.markdownStyle.paragraph.fontSize];
|
|
346
|
+
stylePropChanged = YES;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (newViewProps.markdownStyle.paragraph.fontFamily != oldViewProps.markdownStyle.paragraph.fontFamily) {
|
|
350
|
+
if (!newViewProps.markdownStyle.paragraph.fontFamily.empty()) {
|
|
351
|
+
NSString *fontFamily =
|
|
352
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.paragraph.fontFamily.c_str()];
|
|
353
|
+
[_config setParagraphFontFamily:fontFamily];
|
|
354
|
+
} else {
|
|
355
|
+
[_config setParagraphFontFamily:nullptr];
|
|
356
|
+
}
|
|
357
|
+
stylePropChanged = YES;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (newViewProps.markdownStyle.paragraph.fontWeight != oldViewProps.markdownStyle.paragraph.fontWeight) {
|
|
361
|
+
if (!newViewProps.markdownStyle.paragraph.fontWeight.empty()) {
|
|
362
|
+
NSString *fontWeight =
|
|
363
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.paragraph.fontWeight.c_str()];
|
|
364
|
+
[_config setParagraphFontWeight:fontWeight];
|
|
365
|
+
} else {
|
|
366
|
+
[_config setParagraphFontWeight:nullptr];
|
|
367
|
+
}
|
|
368
|
+
stylePropChanged = YES;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (newViewProps.markdownStyle.paragraph.color != oldViewProps.markdownStyle.paragraph.color) {
|
|
372
|
+
if (newViewProps.markdownStyle.paragraph.color) {
|
|
373
|
+
UIColor *paragraphColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.paragraph.color);
|
|
374
|
+
[_config setParagraphColor:paragraphColor];
|
|
375
|
+
} else {
|
|
376
|
+
[_config setParagraphColor:nullptr];
|
|
377
|
+
}
|
|
378
|
+
stylePropChanged = YES;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (newViewProps.markdownStyle.paragraph.marginBottom != oldViewProps.markdownStyle.paragraph.marginBottom) {
|
|
382
|
+
[_config setParagraphMarginBottom:newViewProps.markdownStyle.paragraph.marginBottom];
|
|
383
|
+
stylePropChanged = YES;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (newViewProps.markdownStyle.paragraph.lineHeight != oldViewProps.markdownStyle.paragraph.lineHeight) {
|
|
387
|
+
[_config setParagraphLineHeight:newViewProps.markdownStyle.paragraph.lineHeight];
|
|
388
|
+
stylePropChanged = YES;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// H1 style
|
|
392
|
+
if (newViewProps.markdownStyle.h1.fontSize != oldViewProps.markdownStyle.h1.fontSize) {
|
|
393
|
+
[_config setH1FontSize:newViewProps.markdownStyle.h1.fontSize];
|
|
394
|
+
stylePropChanged = YES;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (newViewProps.markdownStyle.h1.fontFamily != oldViewProps.markdownStyle.h1.fontFamily) {
|
|
398
|
+
if (!newViewProps.markdownStyle.h1.fontFamily.empty()) {
|
|
399
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h1.fontFamily.c_str()];
|
|
400
|
+
[_config setH1FontFamily:fontFamily];
|
|
401
|
+
} else {
|
|
402
|
+
[_config setH1FontFamily:nullptr];
|
|
403
|
+
}
|
|
404
|
+
stylePropChanged = YES;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (newViewProps.markdownStyle.h1.fontWeight != oldViewProps.markdownStyle.h1.fontWeight) {
|
|
408
|
+
if (!newViewProps.markdownStyle.h1.fontWeight.empty()) {
|
|
409
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h1.fontWeight.c_str()];
|
|
410
|
+
[_config setH1FontWeight:fontWeight];
|
|
411
|
+
} else {
|
|
412
|
+
[_config setH1FontWeight:nullptr];
|
|
413
|
+
}
|
|
414
|
+
stylePropChanged = YES;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (newViewProps.markdownStyle.h1.color != oldViewProps.markdownStyle.h1.color) {
|
|
418
|
+
if (newViewProps.markdownStyle.h1.color) {
|
|
419
|
+
UIColor *h1Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h1.color);
|
|
420
|
+
[_config setH1Color:h1Color];
|
|
421
|
+
} else {
|
|
422
|
+
[_config setH1Color:nullptr];
|
|
423
|
+
}
|
|
424
|
+
stylePropChanged = YES;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (newViewProps.markdownStyle.h1.marginBottom != oldViewProps.markdownStyle.h1.marginBottom) {
|
|
428
|
+
[_config setH1MarginBottom:newViewProps.markdownStyle.h1.marginBottom];
|
|
429
|
+
stylePropChanged = YES;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (newViewProps.markdownStyle.h1.lineHeight != oldViewProps.markdownStyle.h1.lineHeight) {
|
|
433
|
+
[_config setH1LineHeight:newViewProps.markdownStyle.h1.lineHeight];
|
|
434
|
+
stylePropChanged = YES;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// H2 style
|
|
438
|
+
if (newViewProps.markdownStyle.h2.fontSize != oldViewProps.markdownStyle.h2.fontSize) {
|
|
439
|
+
[_config setH2FontSize:newViewProps.markdownStyle.h2.fontSize];
|
|
440
|
+
stylePropChanged = YES;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (newViewProps.markdownStyle.h2.fontFamily != oldViewProps.markdownStyle.h2.fontFamily) {
|
|
444
|
+
if (!newViewProps.markdownStyle.h2.fontFamily.empty()) {
|
|
445
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h2.fontFamily.c_str()];
|
|
446
|
+
[_config setH2FontFamily:fontFamily];
|
|
447
|
+
} else {
|
|
448
|
+
[_config setH2FontFamily:nullptr];
|
|
449
|
+
}
|
|
450
|
+
stylePropChanged = YES;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (newViewProps.markdownStyle.h2.fontWeight != oldViewProps.markdownStyle.h2.fontWeight) {
|
|
454
|
+
if (!newViewProps.markdownStyle.h2.fontWeight.empty()) {
|
|
455
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h2.fontWeight.c_str()];
|
|
456
|
+
[_config setH2FontWeight:fontWeight];
|
|
457
|
+
} else {
|
|
458
|
+
[_config setH2FontWeight:nullptr];
|
|
459
|
+
}
|
|
460
|
+
stylePropChanged = YES;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (newViewProps.markdownStyle.h2.color != oldViewProps.markdownStyle.h2.color) {
|
|
464
|
+
if (newViewProps.markdownStyle.h2.color) {
|
|
465
|
+
UIColor *h2Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h2.color);
|
|
466
|
+
[_config setH2Color:h2Color];
|
|
467
|
+
} else {
|
|
468
|
+
[_config setH2Color:nullptr];
|
|
469
|
+
}
|
|
470
|
+
stylePropChanged = YES;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (newViewProps.markdownStyle.h2.marginBottom != oldViewProps.markdownStyle.h2.marginBottom) {
|
|
474
|
+
[_config setH2MarginBottom:newViewProps.markdownStyle.h2.marginBottom];
|
|
475
|
+
stylePropChanged = YES;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (newViewProps.markdownStyle.h2.lineHeight != oldViewProps.markdownStyle.h2.lineHeight) {
|
|
479
|
+
[_config setH2LineHeight:newViewProps.markdownStyle.h2.lineHeight];
|
|
480
|
+
stylePropChanged = YES;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// H3 style
|
|
484
|
+
if (newViewProps.markdownStyle.h3.fontSize != oldViewProps.markdownStyle.h3.fontSize) {
|
|
485
|
+
[_config setH3FontSize:newViewProps.markdownStyle.h3.fontSize];
|
|
486
|
+
stylePropChanged = YES;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (newViewProps.markdownStyle.h3.fontFamily != oldViewProps.markdownStyle.h3.fontFamily) {
|
|
490
|
+
if (!newViewProps.markdownStyle.h3.fontFamily.empty()) {
|
|
491
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h3.fontFamily.c_str()];
|
|
492
|
+
[_config setH3FontFamily:fontFamily];
|
|
493
|
+
} else {
|
|
494
|
+
[_config setH3FontFamily:nullptr];
|
|
495
|
+
}
|
|
496
|
+
stylePropChanged = YES;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (newViewProps.markdownStyle.h3.fontWeight != oldViewProps.markdownStyle.h3.fontWeight) {
|
|
500
|
+
if (!newViewProps.markdownStyle.h3.fontWeight.empty()) {
|
|
501
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h3.fontWeight.c_str()];
|
|
502
|
+
[_config setH3FontWeight:fontWeight];
|
|
503
|
+
} else {
|
|
504
|
+
[_config setH3FontWeight:nullptr];
|
|
505
|
+
}
|
|
506
|
+
stylePropChanged = YES;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (newViewProps.markdownStyle.h3.color != oldViewProps.markdownStyle.h3.color) {
|
|
510
|
+
if (newViewProps.markdownStyle.h3.color) {
|
|
511
|
+
UIColor *h3Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h3.color);
|
|
512
|
+
[_config setH3Color:h3Color];
|
|
513
|
+
} else {
|
|
514
|
+
[_config setH3Color:nullptr];
|
|
515
|
+
}
|
|
516
|
+
stylePropChanged = YES;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (newViewProps.markdownStyle.h3.marginBottom != oldViewProps.markdownStyle.h3.marginBottom) {
|
|
520
|
+
[_config setH3MarginBottom:newViewProps.markdownStyle.h3.marginBottom];
|
|
521
|
+
stylePropChanged = YES;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (newViewProps.markdownStyle.h3.lineHeight != oldViewProps.markdownStyle.h3.lineHeight) {
|
|
525
|
+
[_config setH3LineHeight:newViewProps.markdownStyle.h3.lineHeight];
|
|
526
|
+
stylePropChanged = YES;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// H4 style
|
|
530
|
+
if (newViewProps.markdownStyle.h4.fontSize != oldViewProps.markdownStyle.h4.fontSize) {
|
|
531
|
+
[_config setH4FontSize:newViewProps.markdownStyle.h4.fontSize];
|
|
532
|
+
stylePropChanged = YES;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (newViewProps.markdownStyle.h4.fontFamily != oldViewProps.markdownStyle.h4.fontFamily) {
|
|
536
|
+
if (!newViewProps.markdownStyle.h4.fontFamily.empty()) {
|
|
537
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h4.fontFamily.c_str()];
|
|
538
|
+
[_config setH4FontFamily:fontFamily];
|
|
539
|
+
} else {
|
|
540
|
+
[_config setH4FontFamily:nullptr];
|
|
541
|
+
}
|
|
542
|
+
stylePropChanged = YES;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (newViewProps.markdownStyle.h4.fontWeight != oldViewProps.markdownStyle.h4.fontWeight) {
|
|
546
|
+
if (!newViewProps.markdownStyle.h4.fontWeight.empty()) {
|
|
547
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h4.fontWeight.c_str()];
|
|
548
|
+
[_config setH4FontWeight:fontWeight];
|
|
549
|
+
} else {
|
|
550
|
+
[_config setH4FontWeight:nullptr];
|
|
551
|
+
}
|
|
552
|
+
stylePropChanged = YES;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (newViewProps.markdownStyle.h4.color != oldViewProps.markdownStyle.h4.color) {
|
|
556
|
+
if (newViewProps.markdownStyle.h4.color) {
|
|
557
|
+
UIColor *h4Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h4.color);
|
|
558
|
+
[_config setH4Color:h4Color];
|
|
559
|
+
} else {
|
|
560
|
+
[_config setH4Color:nullptr];
|
|
561
|
+
}
|
|
562
|
+
stylePropChanged = YES;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (newViewProps.markdownStyle.h4.marginBottom != oldViewProps.markdownStyle.h4.marginBottom) {
|
|
566
|
+
[_config setH4MarginBottom:newViewProps.markdownStyle.h4.marginBottom];
|
|
567
|
+
stylePropChanged = YES;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (newViewProps.markdownStyle.h4.lineHeight != oldViewProps.markdownStyle.h4.lineHeight) {
|
|
571
|
+
[_config setH4LineHeight:newViewProps.markdownStyle.h4.lineHeight];
|
|
572
|
+
stylePropChanged = YES;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// H5 style
|
|
576
|
+
if (newViewProps.markdownStyle.h5.fontSize != oldViewProps.markdownStyle.h5.fontSize) {
|
|
577
|
+
[_config setH5FontSize:newViewProps.markdownStyle.h5.fontSize];
|
|
578
|
+
stylePropChanged = YES;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (newViewProps.markdownStyle.h5.fontFamily != oldViewProps.markdownStyle.h5.fontFamily) {
|
|
582
|
+
if (!newViewProps.markdownStyle.h5.fontFamily.empty()) {
|
|
583
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h5.fontFamily.c_str()];
|
|
584
|
+
[_config setH5FontFamily:fontFamily];
|
|
585
|
+
} else {
|
|
586
|
+
[_config setH5FontFamily:nullptr];
|
|
587
|
+
}
|
|
588
|
+
stylePropChanged = YES;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (newViewProps.markdownStyle.h5.fontWeight != oldViewProps.markdownStyle.h5.fontWeight) {
|
|
592
|
+
if (!newViewProps.markdownStyle.h5.fontWeight.empty()) {
|
|
593
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h5.fontWeight.c_str()];
|
|
594
|
+
[_config setH5FontWeight:fontWeight];
|
|
595
|
+
} else {
|
|
596
|
+
[_config setH5FontWeight:nullptr];
|
|
597
|
+
}
|
|
598
|
+
stylePropChanged = YES;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (newViewProps.markdownStyle.h5.color != oldViewProps.markdownStyle.h5.color) {
|
|
602
|
+
if (newViewProps.markdownStyle.h5.color) {
|
|
603
|
+
UIColor *h5Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h5.color);
|
|
604
|
+
[_config setH5Color:h5Color];
|
|
605
|
+
} else {
|
|
606
|
+
[_config setH5Color:nullptr];
|
|
607
|
+
}
|
|
608
|
+
stylePropChanged = YES;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (newViewProps.markdownStyle.h5.marginBottom != oldViewProps.markdownStyle.h5.marginBottom) {
|
|
612
|
+
[_config setH5MarginBottom:newViewProps.markdownStyle.h5.marginBottom];
|
|
613
|
+
stylePropChanged = YES;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (newViewProps.markdownStyle.h5.lineHeight != oldViewProps.markdownStyle.h5.lineHeight) {
|
|
617
|
+
[_config setH5LineHeight:newViewProps.markdownStyle.h5.lineHeight];
|
|
618
|
+
stylePropChanged = YES;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// H6 style
|
|
622
|
+
if (newViewProps.markdownStyle.h6.fontSize != oldViewProps.markdownStyle.h6.fontSize) {
|
|
623
|
+
[_config setH6FontSize:newViewProps.markdownStyle.h6.fontSize];
|
|
624
|
+
stylePropChanged = YES;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (newViewProps.markdownStyle.h6.fontFamily != oldViewProps.markdownStyle.h6.fontFamily) {
|
|
628
|
+
if (!newViewProps.markdownStyle.h6.fontFamily.empty()) {
|
|
629
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h6.fontFamily.c_str()];
|
|
630
|
+
[_config setH6FontFamily:fontFamily];
|
|
631
|
+
} else {
|
|
632
|
+
[_config setH6FontFamily:nullptr];
|
|
633
|
+
}
|
|
634
|
+
stylePropChanged = YES;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (newViewProps.markdownStyle.h6.fontWeight != oldViewProps.markdownStyle.h6.fontWeight) {
|
|
638
|
+
if (!newViewProps.markdownStyle.h6.fontWeight.empty()) {
|
|
639
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.h6.fontWeight.c_str()];
|
|
640
|
+
[_config setH6FontWeight:fontWeight];
|
|
641
|
+
} else {
|
|
642
|
+
[_config setH6FontWeight:nullptr];
|
|
643
|
+
}
|
|
644
|
+
stylePropChanged = YES;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (newViewProps.markdownStyle.h6.color != oldViewProps.markdownStyle.h6.color) {
|
|
648
|
+
if (newViewProps.markdownStyle.h6.color) {
|
|
649
|
+
UIColor *h6Color = RCTUIColorFromSharedColor(newViewProps.markdownStyle.h6.color);
|
|
650
|
+
[_config setH6Color:h6Color];
|
|
651
|
+
} else {
|
|
652
|
+
[_config setH6Color:nullptr];
|
|
653
|
+
}
|
|
654
|
+
stylePropChanged = YES;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (newViewProps.markdownStyle.h6.marginBottom != oldViewProps.markdownStyle.h6.marginBottom) {
|
|
658
|
+
[_config setH6MarginBottom:newViewProps.markdownStyle.h6.marginBottom];
|
|
659
|
+
stylePropChanged = YES;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (newViewProps.markdownStyle.h6.lineHeight != oldViewProps.markdownStyle.h6.lineHeight) {
|
|
663
|
+
[_config setH6LineHeight:newViewProps.markdownStyle.h6.lineHeight];
|
|
664
|
+
stylePropChanged = YES;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Blockquote style
|
|
668
|
+
if (newViewProps.markdownStyle.blockquote.fontSize != oldViewProps.markdownStyle.blockquote.fontSize) {
|
|
669
|
+
[_config setBlockquoteFontSize:newViewProps.markdownStyle.blockquote.fontSize];
|
|
670
|
+
stylePropChanged = YES;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (newViewProps.markdownStyle.blockquote.fontFamily != oldViewProps.markdownStyle.blockquote.fontFamily) {
|
|
674
|
+
NSString *fontFamily =
|
|
675
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.blockquote.fontFamily.c_str()];
|
|
676
|
+
[_config setBlockquoteFontFamily:fontFamily];
|
|
677
|
+
stylePropChanged = YES;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (newViewProps.markdownStyle.blockquote.fontWeight != oldViewProps.markdownStyle.blockquote.fontWeight) {
|
|
681
|
+
NSString *fontWeight =
|
|
682
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.blockquote.fontWeight.c_str()];
|
|
683
|
+
[_config setBlockquoteFontWeight:fontWeight];
|
|
684
|
+
stylePropChanged = YES;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (newViewProps.markdownStyle.blockquote.color != oldViewProps.markdownStyle.blockquote.color) {
|
|
688
|
+
UIColor *blockquoteColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.blockquote.color);
|
|
689
|
+
[_config setBlockquoteColor:blockquoteColor];
|
|
690
|
+
stylePropChanged = YES;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (newViewProps.markdownStyle.blockquote.marginBottom != oldViewProps.markdownStyle.blockquote.marginBottom) {
|
|
694
|
+
[_config setBlockquoteMarginBottom:newViewProps.markdownStyle.blockquote.marginBottom];
|
|
695
|
+
stylePropChanged = YES;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (newViewProps.markdownStyle.blockquote.lineHeight != oldViewProps.markdownStyle.blockquote.lineHeight) {
|
|
699
|
+
[_config setBlockquoteLineHeight:newViewProps.markdownStyle.blockquote.lineHeight];
|
|
700
|
+
stylePropChanged = YES;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (newViewProps.markdownStyle.blockquote.borderColor != oldViewProps.markdownStyle.blockquote.borderColor) {
|
|
704
|
+
UIColor *blockquoteBorderColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.blockquote.borderColor);
|
|
705
|
+
[_config setBlockquoteBorderColor:blockquoteBorderColor];
|
|
706
|
+
stylePropChanged = YES;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (newViewProps.markdownStyle.blockquote.borderWidth != oldViewProps.markdownStyle.blockquote.borderWidth) {
|
|
710
|
+
[_config setBlockquoteBorderWidth:newViewProps.markdownStyle.blockquote.borderWidth];
|
|
711
|
+
stylePropChanged = YES;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (newViewProps.markdownStyle.blockquote.gapWidth != oldViewProps.markdownStyle.blockquote.gapWidth) {
|
|
715
|
+
[_config setBlockquoteGapWidth:newViewProps.markdownStyle.blockquote.gapWidth];
|
|
716
|
+
stylePropChanged = YES;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (newViewProps.markdownStyle.blockquote.backgroundColor != oldViewProps.markdownStyle.blockquote.backgroundColor) {
|
|
720
|
+
UIColor *blockquoteBackgroundColor =
|
|
721
|
+
RCTUIColorFromSharedColor(newViewProps.markdownStyle.blockquote.backgroundColor);
|
|
722
|
+
[_config setBlockquoteBackgroundColor:blockquoteBackgroundColor];
|
|
723
|
+
stylePropChanged = YES;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (newViewProps.markdownStyle.link.color != oldViewProps.markdownStyle.link.color) {
|
|
727
|
+
UIColor *linkColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.link.color);
|
|
728
|
+
[_config setLinkColor:linkColor];
|
|
729
|
+
stylePropChanged = YES;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (newViewProps.markdownStyle.link.underline != oldViewProps.markdownStyle.link.underline) {
|
|
733
|
+
[_config setLinkUnderline:newViewProps.markdownStyle.link.underline];
|
|
734
|
+
stylePropChanged = YES;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (newViewProps.markdownStyle.strong.color != oldViewProps.markdownStyle.strong.color) {
|
|
738
|
+
if (newViewProps.markdownStyle.strong.color) {
|
|
739
|
+
UIColor *strongColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.strong.color);
|
|
740
|
+
[_config setStrongColor:strongColor];
|
|
741
|
+
} else {
|
|
742
|
+
[_config setStrongColor:nullptr];
|
|
743
|
+
}
|
|
744
|
+
stylePropChanged = YES;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (newViewProps.markdownStyle.em.color != oldViewProps.markdownStyle.em.color) {
|
|
748
|
+
if (newViewProps.markdownStyle.em.color) {
|
|
749
|
+
UIColor *emphasisColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.em.color);
|
|
750
|
+
[_config setEmphasisColor:emphasisColor];
|
|
751
|
+
} else {
|
|
752
|
+
[_config setEmphasisColor:nullptr];
|
|
753
|
+
}
|
|
754
|
+
stylePropChanged = YES;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (newViewProps.markdownStyle.code.color != oldViewProps.markdownStyle.code.color) {
|
|
758
|
+
if (newViewProps.markdownStyle.code.color) {
|
|
759
|
+
UIColor *codeColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.code.color);
|
|
760
|
+
[_config setCodeColor:codeColor];
|
|
761
|
+
} else {
|
|
762
|
+
[_config setCodeColor:nullptr];
|
|
763
|
+
}
|
|
764
|
+
stylePropChanged = YES;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (newViewProps.markdownStyle.code.backgroundColor != oldViewProps.markdownStyle.code.backgroundColor) {
|
|
768
|
+
if (newViewProps.markdownStyle.code.backgroundColor) {
|
|
769
|
+
UIColor *codeBackgroundColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.code.backgroundColor);
|
|
770
|
+
[_config setCodeBackgroundColor:codeBackgroundColor];
|
|
771
|
+
} else {
|
|
772
|
+
[_config setCodeBackgroundColor:nullptr];
|
|
773
|
+
}
|
|
774
|
+
stylePropChanged = YES;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (newViewProps.markdownStyle.code.borderColor != oldViewProps.markdownStyle.code.borderColor) {
|
|
778
|
+
if (newViewProps.markdownStyle.code.borderColor) {
|
|
779
|
+
UIColor *codeBorderColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.code.borderColor);
|
|
780
|
+
[_config setCodeBorderColor:codeBorderColor];
|
|
781
|
+
} else {
|
|
782
|
+
[_config setCodeBorderColor:nullptr];
|
|
783
|
+
}
|
|
784
|
+
stylePropChanged = YES;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (newViewProps.markdownStyle.image.height != oldViewProps.markdownStyle.image.height) {
|
|
788
|
+
[_config setImageHeight:newViewProps.markdownStyle.image.height];
|
|
789
|
+
stylePropChanged = YES;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (newViewProps.markdownStyle.image.borderRadius != oldViewProps.markdownStyle.image.borderRadius) {
|
|
793
|
+
[_config setImageBorderRadius:newViewProps.markdownStyle.image.borderRadius];
|
|
794
|
+
stylePropChanged = YES;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (newViewProps.markdownStyle.image.marginBottom != oldViewProps.markdownStyle.image.marginBottom) {
|
|
798
|
+
[_config setImageMarginBottom:newViewProps.markdownStyle.image.marginBottom];
|
|
799
|
+
stylePropChanged = YES;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (newViewProps.markdownStyle.inlineImage.size != oldViewProps.markdownStyle.inlineImage.size) {
|
|
803
|
+
[_config setInlineImageSize:newViewProps.markdownStyle.inlineImage.size];
|
|
804
|
+
stylePropChanged = YES;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// List style
|
|
808
|
+
if (newViewProps.markdownStyle.list.fontSize != oldViewProps.markdownStyle.list.fontSize) {
|
|
809
|
+
[_config setListStyleFontSize:newViewProps.markdownStyle.list.fontSize];
|
|
810
|
+
stylePropChanged = YES;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (newViewProps.markdownStyle.list.fontFamily != oldViewProps.markdownStyle.list.fontFamily) {
|
|
814
|
+
NSString *fontFamily = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.list.fontFamily.c_str()];
|
|
815
|
+
[_config setListStyleFontFamily:fontFamily];
|
|
816
|
+
stylePropChanged = YES;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (newViewProps.markdownStyle.list.fontWeight != oldViewProps.markdownStyle.list.fontWeight) {
|
|
820
|
+
NSString *fontWeight = [[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.list.fontWeight.c_str()];
|
|
821
|
+
[_config setListStyleFontWeight:fontWeight];
|
|
822
|
+
stylePropChanged = YES;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (newViewProps.markdownStyle.list.color != oldViewProps.markdownStyle.list.color) {
|
|
826
|
+
UIColor *listColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.list.color);
|
|
827
|
+
[_config setListStyleColor:listColor];
|
|
828
|
+
stylePropChanged = YES;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (newViewProps.markdownStyle.list.marginBottom != oldViewProps.markdownStyle.list.marginBottom) {
|
|
832
|
+
[_config setListStyleMarginBottom:newViewProps.markdownStyle.list.marginBottom];
|
|
833
|
+
stylePropChanged = YES;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (newViewProps.markdownStyle.list.lineHeight != oldViewProps.markdownStyle.list.lineHeight) {
|
|
837
|
+
[_config setListStyleLineHeight:newViewProps.markdownStyle.list.lineHeight];
|
|
838
|
+
stylePropChanged = YES;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (newViewProps.markdownStyle.list.bulletColor != oldViewProps.markdownStyle.list.bulletColor) {
|
|
842
|
+
UIColor *bulletColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.list.bulletColor);
|
|
843
|
+
[_config setListStyleBulletColor:bulletColor];
|
|
844
|
+
stylePropChanged = YES;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (newViewProps.markdownStyle.list.bulletSize != oldViewProps.markdownStyle.list.bulletSize) {
|
|
848
|
+
[_config setListStyleBulletSize:newViewProps.markdownStyle.list.bulletSize];
|
|
849
|
+
stylePropChanged = YES;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (newViewProps.markdownStyle.list.markerColor != oldViewProps.markdownStyle.list.markerColor) {
|
|
853
|
+
UIColor *markerColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.list.markerColor);
|
|
854
|
+
[_config setListStyleMarkerColor:markerColor];
|
|
855
|
+
stylePropChanged = YES;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (newViewProps.markdownStyle.list.markerFontWeight != oldViewProps.markdownStyle.list.markerFontWeight) {
|
|
859
|
+
NSString *markerFontWeight =
|
|
860
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.list.markerFontWeight.c_str()];
|
|
861
|
+
[_config setListStyleMarkerFontWeight:markerFontWeight];
|
|
862
|
+
stylePropChanged = YES;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (newViewProps.markdownStyle.list.gapWidth != oldViewProps.markdownStyle.list.gapWidth) {
|
|
866
|
+
[_config setListStyleGapWidth:newViewProps.markdownStyle.list.gapWidth];
|
|
867
|
+
stylePropChanged = YES;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (newViewProps.markdownStyle.list.marginLeft != oldViewProps.markdownStyle.list.marginLeft) {
|
|
871
|
+
[_config setListStyleMarginLeft:newViewProps.markdownStyle.list.marginLeft];
|
|
872
|
+
stylePropChanged = YES;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Code block style
|
|
876
|
+
if (newViewProps.markdownStyle.codeBlock.fontSize != oldViewProps.markdownStyle.codeBlock.fontSize) {
|
|
877
|
+
[_config setCodeBlockFontSize:newViewProps.markdownStyle.codeBlock.fontSize];
|
|
878
|
+
stylePropChanged = YES;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (newViewProps.markdownStyle.codeBlock.fontFamily != oldViewProps.markdownStyle.codeBlock.fontFamily) {
|
|
882
|
+
NSString *fontFamily =
|
|
883
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.codeBlock.fontFamily.c_str()];
|
|
884
|
+
[_config setCodeBlockFontFamily:fontFamily];
|
|
885
|
+
stylePropChanged = YES;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
if (newViewProps.markdownStyle.codeBlock.fontWeight != oldViewProps.markdownStyle.codeBlock.fontWeight) {
|
|
889
|
+
NSString *fontWeight =
|
|
890
|
+
[[NSString alloc] initWithUTF8String:newViewProps.markdownStyle.codeBlock.fontWeight.c_str()];
|
|
891
|
+
[_config setCodeBlockFontWeight:fontWeight];
|
|
892
|
+
stylePropChanged = YES;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (newViewProps.markdownStyle.codeBlock.color != oldViewProps.markdownStyle.codeBlock.color) {
|
|
896
|
+
UIColor *codeBlockColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.codeBlock.color);
|
|
897
|
+
[_config setCodeBlockColor:codeBlockColor];
|
|
898
|
+
stylePropChanged = YES;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (newViewProps.markdownStyle.codeBlock.marginBottom != oldViewProps.markdownStyle.codeBlock.marginBottom) {
|
|
902
|
+
[_config setCodeBlockMarginBottom:newViewProps.markdownStyle.codeBlock.marginBottom];
|
|
903
|
+
stylePropChanged = YES;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (newViewProps.markdownStyle.codeBlock.lineHeight != oldViewProps.markdownStyle.codeBlock.lineHeight) {
|
|
907
|
+
[_config setCodeBlockLineHeight:newViewProps.markdownStyle.codeBlock.lineHeight];
|
|
908
|
+
stylePropChanged = YES;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (newViewProps.markdownStyle.codeBlock.backgroundColor != oldViewProps.markdownStyle.codeBlock.backgroundColor) {
|
|
912
|
+
UIColor *codeBlockBackgroundColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.codeBlock.backgroundColor);
|
|
913
|
+
[_config setCodeBlockBackgroundColor:codeBlockBackgroundColor];
|
|
914
|
+
stylePropChanged = YES;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (newViewProps.markdownStyle.codeBlock.borderColor != oldViewProps.markdownStyle.codeBlock.borderColor) {
|
|
918
|
+
UIColor *codeBlockBorderColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.codeBlock.borderColor);
|
|
919
|
+
[_config setCodeBlockBorderColor:codeBlockBorderColor];
|
|
920
|
+
stylePropChanged = YES;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
if (newViewProps.markdownStyle.codeBlock.borderRadius != oldViewProps.markdownStyle.codeBlock.borderRadius) {
|
|
924
|
+
[_config setCodeBlockBorderRadius:newViewProps.markdownStyle.codeBlock.borderRadius];
|
|
925
|
+
stylePropChanged = YES;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
if (newViewProps.markdownStyle.codeBlock.borderWidth != oldViewProps.markdownStyle.codeBlock.borderWidth) {
|
|
929
|
+
[_config setCodeBlockBorderWidth:newViewProps.markdownStyle.codeBlock.borderWidth];
|
|
930
|
+
stylePropChanged = YES;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if (newViewProps.markdownStyle.codeBlock.padding != oldViewProps.markdownStyle.codeBlock.padding) {
|
|
934
|
+
[_config setCodeBlockPadding:newViewProps.markdownStyle.codeBlock.padding];
|
|
935
|
+
stylePropChanged = YES;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Thematic break style
|
|
939
|
+
if (newViewProps.markdownStyle.thematicBreak.color != oldViewProps.markdownStyle.thematicBreak.color) {
|
|
940
|
+
UIColor *thematicBreakColor = RCTUIColorFromSharedColor(newViewProps.markdownStyle.thematicBreak.color);
|
|
941
|
+
[_config setThematicBreakColor:thematicBreakColor];
|
|
942
|
+
stylePropChanged = YES;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (newViewProps.markdownStyle.thematicBreak.height != oldViewProps.markdownStyle.thematicBreak.height) {
|
|
946
|
+
[_config setThematicBreakHeight:newViewProps.markdownStyle.thematicBreak.height];
|
|
947
|
+
stylePropChanged = YES;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if (newViewProps.markdownStyle.thematicBreak.marginTop != oldViewProps.markdownStyle.thematicBreak.marginTop) {
|
|
951
|
+
[_config setThematicBreakMarginTop:newViewProps.markdownStyle.thematicBreak.marginTop];
|
|
952
|
+
stylePropChanged = YES;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (newViewProps.markdownStyle.thematicBreak.marginBottom != oldViewProps.markdownStyle.thematicBreak.marginBottom) {
|
|
956
|
+
[_config setThematicBreakMarginBottom:newViewProps.markdownStyle.thematicBreak.marginBottom];
|
|
957
|
+
stylePropChanged = YES;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// Update config reference on layout manager if it's not already set
|
|
961
|
+
NSLayoutManager *layoutManager = _textView.layoutManager;
|
|
962
|
+
if ([layoutManager isKindOfClass:[TextViewLayoutManager class]]) {
|
|
963
|
+
StyleConfig *currentConfig = [layoutManager valueForKey:@"config"];
|
|
964
|
+
if (currentConfig != _config) {
|
|
965
|
+
// Only update reference if it's different (first time setup)
|
|
966
|
+
[layoutManager setValue:_config forKey:@"config"];
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// Control text selection and link previews via isSelectable property
|
|
971
|
+
// According to Apple docs, isSelectable controls whether text selection and link previews work
|
|
972
|
+
// https://developer.apple.com/documentation/uikit/uitextview/isselectable
|
|
973
|
+
if (_textView.selectable != newViewProps.isSelectable) {
|
|
974
|
+
_textView.selectable = newViewProps.isSelectable;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
BOOL markdownChanged = oldViewProps.markdown != newViewProps.markdown;
|
|
978
|
+
|
|
979
|
+
if (markdownChanged || stylePropChanged) {
|
|
980
|
+
NSString *markdownString = [[NSString alloc] initWithUTF8String:newViewProps.markdown.c_str()];
|
|
981
|
+
[self renderMarkdownContent:markdownString];
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
[super updateProps:props oldProps:oldProps];
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
988
|
+
{
|
|
989
|
+
return EnrichedMarkdownText.class;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
- (void)textTapped:(UITapGestureRecognizer *)recognizer
|
|
993
|
+
{
|
|
994
|
+
/*
|
|
995
|
+
* HOW LINK TAPPING WORKS:
|
|
996
|
+
*
|
|
997
|
+
* 1. SETUP PHASE (During Rendering):
|
|
998
|
+
* - Each link gets a custom @"linkURL" attribute attached to its text range
|
|
999
|
+
* - The URL is stored as the attribute's value
|
|
1000
|
+
* - This creates an "invisible map" of where links are in the text
|
|
1001
|
+
*
|
|
1002
|
+
* Example:
|
|
1003
|
+
* Text: "Check out this [link to React Native](https://reactnative.dev)"
|
|
1004
|
+
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1005
|
+
* | | | | | | | | | | |
|
|
1006
|
+
* 0 5 10 15 20 25 30 35 40 45 50
|
|
1007
|
+
*
|
|
1008
|
+
* Attributes:
|
|
1009
|
+
* - Characters 15-29: @"linkURL" = "https://reactnative.dev"
|
|
1010
|
+
* - Characters 0-14: no special attributes
|
|
1011
|
+
* - Characters 30-50: no special attributes
|
|
1012
|
+
*
|
|
1013
|
+
* 2. TOUCH DETECTION PHASE (When User Taps):
|
|
1014
|
+
* - UITapGestureRecognizer detects the tap
|
|
1015
|
+
* - We get the tap coordinates relative to the text view
|
|
1016
|
+
* - We adjust for text container insets to get precise text coordinates
|
|
1017
|
+
*/
|
|
1018
|
+
|
|
1019
|
+
UITextView *textView = (UITextView *)recognizer.view;
|
|
1020
|
+
|
|
1021
|
+
// Location of the tap in text-container coordinates
|
|
1022
|
+
NSLayoutManager *layoutManager = textView.layoutManager;
|
|
1023
|
+
CGPoint location = [recognizer locationInView:textView];
|
|
1024
|
+
location.x -= textView.textContainerInset.left;
|
|
1025
|
+
location.y -= textView.textContainerInset.top;
|
|
1026
|
+
|
|
1027
|
+
/*
|
|
1028
|
+
* 3. CHARACTER INDEX LOOKUP:
|
|
1029
|
+
* - NSLayoutManager converts the tap coordinates to a character index
|
|
1030
|
+
* - This tells us exactly which character in the text was tapped
|
|
1031
|
+
* - Uses UIKit's built-in text layout system (very accurate)
|
|
1032
|
+
*/
|
|
1033
|
+
NSUInteger characterIndex;
|
|
1034
|
+
characterIndex = [layoutManager characterIndexForPoint:location
|
|
1035
|
+
inTextContainer:textView.textContainer
|
|
1036
|
+
fractionOfDistanceBetweenInsertionPoints:NULL];
|
|
1037
|
+
|
|
1038
|
+
/*
|
|
1039
|
+
* 4. LINK DETECTION:
|
|
1040
|
+
* - We check if there's a @"linkURL" attribute at the tapped character
|
|
1041
|
+
* - If found, we get the URL value and the effective range
|
|
1042
|
+
* - If it's a link, we emit the onLinkPress event to React Native
|
|
1043
|
+
*
|
|
1044
|
+
* COMPLETE FLOW:
|
|
1045
|
+
* 1. User taps → UITapGestureRecognizer fires
|
|
1046
|
+
* 2. Get coordinates → Convert to text container coordinates
|
|
1047
|
+
* 3. Find character → NSLayoutManager.characterIndexForPoint
|
|
1048
|
+
* 4. Check attributes → Look for @"linkURL" at that character
|
|
1049
|
+
* 5. If link found → Emit onLinkPress event with URL
|
|
1050
|
+
* 6. React Native → Receives event and shows alert
|
|
1051
|
+
*/
|
|
1052
|
+
if (characterIndex < textView.textStorage.length) {
|
|
1053
|
+
NSRange range;
|
|
1054
|
+
NSString *url = [textView.attributedText attribute:@"linkURL" atIndex:characterIndex effectiveRange:&range];
|
|
1055
|
+
|
|
1056
|
+
if (url) {
|
|
1057
|
+
// Emit onLinkPress event to React Native
|
|
1058
|
+
const auto &eventEmitter = *std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
1059
|
+
eventEmitter.onLinkPress({.url = std::string([url UTF8String])});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
#pragma mark - UITextViewDelegate (Edit Menu)
|
|
1065
|
+
|
|
1066
|
+
// Customizes the edit menu
|
|
1067
|
+
- (UIMenu *)textView:(UITextView *)textView
|
|
1068
|
+
editMenuForTextInRange:(NSRange)range
|
|
1069
|
+
suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
|
|
1070
|
+
{
|
|
1071
|
+
return buildEditMenuForSelection(_textView.attributedText, range, _cachedMarkdown, _config, suggestedActions);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
@end
|