diff --git a/README.md b/README.md index 327e7a7f..a0acc260 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - ⌨️ Live synchronous formatting on every keystroke - ⚡ Fully native experience (selection, spellcheck, autocomplete) - 🎨 Customizable styles -- 🌐 Universal support (Android, iOS, web) +- 🌐 Universal support (Android, iOS, macOS, web) - 🏗️ Supports New Architecture ## Installation diff --git a/RNLiveMarkdown.podspec b/RNLiveMarkdown.podspec index c70a0608..90eea736 100644 --- a/RNLiveMarkdown.podspec +++ b/RNLiveMarkdown.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.license = package["license"] s.authors = package["author"] - s.platforms = { :ios => "11.0", :visionos => "1.0" } + s.platforms = { :ios => "11.0", :visionos => "1.0", :osx => "10.15" } s.source = { :git => "https://github.com/expensify/react-native-live-markdown.git", :tag => "#{s.version}" } s.source_files = "apple/**/*.{h,m,mm}" diff --git a/apple/MarkdownLayoutManager.h b/apple/MarkdownLayoutManager.h index 29be3508..fed54edc 100644 --- a/apple/MarkdownLayoutManager.h +++ b/apple/MarkdownLayoutManager.h @@ -1,4 +1,3 @@ -#import #import NS_ASSUME_NONNULL_BEGIN diff --git a/apple/MarkdownLayoutManager.mm b/apple/MarkdownLayoutManager.mm index 3974ba98..c60be01a 100644 --- a/apple/MarkdownLayoutManager.mm +++ b/apple/MarkdownLayoutManager.mm @@ -1,4 +1,5 @@ #import +#import @implementation MarkdownLayoutManager @@ -8,7 +9,7 @@ - (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origi [self enumerateLineFragmentsForGlyphRange:glyphsToShow usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) { __block BOOL isBlockquote = NO; __block int currentDepth = 0; - RCTMarkdownUtils *markdownUtils = [self valueForKey:@"markdownUtils"]; + RCTMarkdownUtils *markdownUtils = objc_getAssociatedObject(self, @selector(markdownUtils)); [markdownUtils.blockquoteRangesAndLevels enumerateObjectsUsingBlock:^(NSDictionary *item, NSUInteger idx, BOOL * _Nonnull stop) { NSRange range = [[item valueForKey:@"range"] rangeValue]; currentDepth = [[item valueForKey:@"depth"] unsignedIntegerValue]; @@ -31,7 +32,7 @@ - (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origi CGFloat x = paddingLeft + (level * shift) + markdownUtils.markdownStyle.blockquoteMarginLeft; CGRect lineRect = CGRectMake(x, y, width, height); [markdownUtils.markdownStyle.blockquoteBorderColor setFill]; - UIRectFill(lineRect); + NSRectFill(lineRect); } } }]; diff --git a/apple/MarkdownTextInputDecoratorView.h b/apple/MarkdownTextInputDecoratorView.h index 198780f2..2b298b07 100644 --- a/apple/MarkdownTextInputDecoratorView.h +++ b/apple/MarkdownTextInputDecoratorView.h @@ -1,9 +1,8 @@ -#import #import NS_ASSUME_NONNULL_BEGIN -@interface MarkdownTextInputDecoratorView : UIView +@interface MarkdownTextInputDecoratorView : RCTUIView - (void)setMarkdownStyle:(RCTMarkdownStyle *)markdownStyle; diff --git a/apple/MarkdownTextInputDecoratorView.mm b/apple/MarkdownTextInputDecoratorView.mm index c6ae82fb..a45a487c 100644 --- a/apple/MarkdownTextInputDecoratorView.mm +++ b/apple/MarkdownTextInputDecoratorView.mm @@ -22,7 +22,7 @@ @implementation MarkdownTextInputDecoratorView { #else __weak RCTBaseTextInputView *_textInput; #endif /* RCT_NEW_ARCH_ENABLED */ - __weak UIView *_backedTextInputView; + __weak RCTUIView *_backedTextInputView; __weak RCTBackedTextFieldDelegateAdapter *_adapter; __weak RCTUITextView *_textView; } @@ -47,7 +47,7 @@ - (void)didMoveToWindow { #endif /* RCT_NEW_ARCH_ENABLED */ react_native_assert(currentIndex != 0 && currentIndex != NSNotFound && "Error while finding current component."); - UIView *view = [viewsArray objectAtIndex:currentIndex - 1]; + RCTUIView *view = [viewsArray objectAtIndex:currentIndex - 1]; #ifdef RCT_NEW_ARCH_ENABLED react_native_assert([view isKindOfClass:[RCTTextInputComponentView class]] && "Previous sibling component is not an instance of RCTTextInputComponentView."); @@ -78,17 +78,17 @@ - (void)didMoveToWindow { CGSize contentSize = _textView.contentSize; CGRect textBounds = [layoutManager usedRectForTextContainer:_textView.textContainer]; contentSize.height = textBounds.size.height + _textView.textContainerInset.top + _textView.textContainerInset.bottom; - [_textView setContentSize:contentSize]; + // [_textView setContentSize:contentSize]; // TODO layoutManager.allowsNonContiguousLayout = NO; // workaround for onScroll issue object_setClass(layoutManager, [MarkdownLayoutManager class]); - [layoutManager setValue:_markdownUtils forKey:@"markdownUtils"]; + objc_setAssociatedObject(layoutManager, @selector(markdownUtils), _markdownUtils, OBJC_ASSOCIATION_RETAIN); } else { react_native_assert(false && "Cannot enable Markdown for this type of TextInput."); } } -- (void)willMoveToWindow:(UIWindow *)newWindow +- (void)willMoveToWindow:(RCTUIWindow *)newWindow { if (_textInput != nil) { [_textInput setMarkdownUtils:nil]; @@ -98,9 +98,10 @@ - (void)willMoveToWindow:(UIWindow *)newWindow } if (_textView != nil) { [_textView setMarkdownUtils:nil]; - if (_textView.layoutManager != nil && [object_getClass(_textView.layoutManager) isEqual:[MarkdownLayoutManager class]]) { - [_textView.layoutManager setValue:nil forKey:@"markdownUtils"]; - object_setClass(_textView.layoutManager, [NSLayoutManager class]); + NSLayoutManager *layoutManager = _textView.layoutManager; + if (layoutManager != nil && [object_getClass(layoutManager) isEqual:[MarkdownLayoutManager class]]) { + objc_setAssociatedObject(layoutManager, @selector(markdownUtils), nil, OBJC_ASSOCIATION_RETAIN); + object_setClass(layoutManager, [NSLayoutManager class]); } } } diff --git a/apple/MarkdownTextInputDecoratorViewManager.mm b/apple/MarkdownTextInputDecoratorViewManager.mm index d59f27b6..2753f3fc 100644 --- a/apple/MarkdownTextInputDecoratorViewManager.mm +++ b/apple/MarkdownTextInputDecoratorViewManager.mm @@ -5,7 +5,7 @@ @implementation MarkdownTextInputDecoratorViewManager RCT_EXPORT_MODULE(MarkdownTextInputDecoratorView) -- (UIView *)view +- (RCTUIView *)view { return [[MarkdownTextInputDecoratorView alloc] init]; } diff --git a/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm b/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm index 11c3baf8..a0337b56 100644 --- a/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm +++ b/apple/RCTBackedTextFieldDelegateAdapter+Markdown.mm @@ -18,7 +18,7 @@ - (void)markdown_textFieldDidChange RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; if (markdownUtils != nil) { RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"]; - UITextRange *range = backedTextInputView.selectedTextRange; + NSRange range = [backedTextInputView selectedTextRange]; backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText withAttributes:backedTextInputView.defaultTextAttributes]; [backedTextInputView setSelectedTextRange:range notifyDelegate:YES]; } diff --git a/apple/RCTBaseTextInputView+Markdown.mm b/apple/RCTBaseTextInputView+Markdown.mm index 7662d545..1358d50f 100644 --- a/apple/RCTBaseTextInputView+Markdown.mm +++ b/apple/RCTBaseTextInputView+Markdown.mm @@ -47,7 +47,7 @@ - (void)markdown_updateLocalData id backedTextInputView = self.backedTextInputView; NSAttributedString *oldAttributedText = backedTextInputView.attributedText; NSAttributedString *newAttributedText = [markdownUtils parseMarkdown:oldAttributedText withAttributes:backedTextInputView.defaultTextAttributes]; - UITextRange *range = backedTextInputView.selectedTextRange; + NSRange range = backedTextInputView.selectedTextRange; // update attributed text without emitting onSelectionChange event id delegate = backedTextInputView.textInputDelegate; @@ -84,7 +84,7 @@ + (void)load SEL swizzledSelector = @selector(markdown_updateLocalData); Method originalMethod = class_getInstanceMethod(cls, originalSelector); Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); + // method_exchangeImplementations(originalMethod, swizzledMethod); } { diff --git a/apple/RCTMarkdownStyle.h b/apple/RCTMarkdownStyle.h index 75a27f16..98496cd0 100644 --- a/apple/RCTMarkdownStyle.h +++ b/apple/RCTMarkdownStyle.h @@ -1,4 +1,4 @@ -#import +#import #ifdef RCT_NEW_ARCH_ENABLED #import @@ -8,28 +8,28 @@ NS_ASSUME_NONNULL_BEGIN @interface RCTMarkdownStyle : NSObject -@property (nonatomic) UIColor *syntaxColor; -@property (nonatomic) UIColor *linkColor; +@property (nonatomic) RCTUIColor *syntaxColor; +@property (nonatomic) RCTUIColor *linkColor; @property (nonatomic) CGFloat h1FontSize; @property (nonatomic) CGFloat emojiFontSize; -@property (nonatomic) UIColor *blockquoteBorderColor; +@property (nonatomic) RCTUIColor *blockquoteBorderColor; @property (nonatomic) CGFloat blockquoteBorderWidth; @property (nonatomic) CGFloat blockquoteMarginLeft; @property (nonatomic) CGFloat blockquotePaddingLeft; @property (nonatomic) NSString *codeFontFamily; @property (nonatomic) CGFloat codeFontSize; -@property (nonatomic) UIColor *codeColor; -@property (nonatomic) UIColor *codeBackgroundColor; +@property (nonatomic) RCTUIColor *codeColor; +@property (nonatomic) RCTUIColor *codeBackgroundColor; @property (nonatomic) NSString *preFontFamily; @property (nonatomic) CGFloat preFontSize; -@property (nonatomic) UIColor *preColor; -@property (nonatomic) UIColor *preBackgroundColor; -@property (nonatomic) UIColor *mentionHereColor; -@property (nonatomic) UIColor *mentionHereBackgroundColor; -@property (nonatomic) UIColor *mentionUserColor; -@property (nonatomic) UIColor *mentionUserBackgroundColor; -@property (nonatomic) UIColor *mentionReportColor; -@property (nonatomic) UIColor *mentionReportBackgroundColor; +@property (nonatomic) RCTUIColor *preColor; +@property (nonatomic) RCTUIColor *preBackgroundColor; +@property (nonatomic) RCTUIColor *mentionHereColor; +@property (nonatomic) RCTUIColor *mentionHereBackgroundColor; +@property (nonatomic) RCTUIColor *mentionUserColor; +@property (nonatomic) RCTUIColor *mentionUserBackgroundColor; +@property (nonatomic) RCTUIColor *mentionReportColor; +@property (nonatomic) RCTUIColor *mentionReportBackgroundColor; #ifdef RCT_NEW_ARCH_ENABLED - (instancetype)initWithStruct:(const facebook::react::MarkdownTextInputDecoratorViewMarkdownStyleStruct &)style; diff --git a/apple/RCTMarkdownUtils.mm b/apple/RCTMarkdownUtils.mm index c22a8784..43d027d2 100644 --- a/apple/RCTMarkdownUtils.mm +++ b/apple/RCTMarkdownUtils.mm @@ -197,7 +197,7 @@ static void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText) return; } - maximumFontLineHeight = MAX(font.lineHeight, maximumFontLineHeight); + maximumFontLineHeight = MAX(UIFontLineHeight(font), maximumFontLineHeight); // [macOS] }]; if (maximumLineHeight < maximumFontLineHeight) { diff --git a/apple/RCTUITextView+Markdown.h b/apple/RCTUITextView+Markdown.h index 40deedad..9369d9d6 100644 --- a/apple/RCTUITextView+Markdown.h +++ b/apple/RCTUITextView+Markdown.h @@ -1,4 +1,3 @@ -#import #import #import diff --git a/apple/RCTUITextView+Markdown.mm b/apple/RCTUITextView+Markdown.mm index 70f2d882..8b3f0f34 100644 --- a/apple/RCTUITextView+Markdown.mm +++ b/apple/RCTUITextView+Markdown.mm @@ -16,9 +16,10 @@ - (void)markdown_textDidChange { RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils]; if (markdownUtils != nil) { - UITextRange *range = self.selectedTextRange; - super.attributedText = [markdownUtils parseMarkdown:self.attributedText withAttributes:self.defaultTextAttributes]; - [super setSelectedTextRange:range]; // prevents cursor from jumping at the end when typing in the middle of the text + NSRange range = [self selectedTextRange]; + NSAttributedString *attributedString = [markdownUtils parseMarkdown:self.attributedText withAttributes:self.defaultTextAttributes]; + [self.textStorage setAttributedString:attributedString ?: [NSAttributedString new]]; + [super setSelectedRange:range]; // prevents cursor from jumping at the end when typing in the middle of the text self.typingAttributes = self.defaultTextAttributes; // removes indent in new line when typing after blockquote } @@ -35,7 +36,7 @@ + (void)load SEL swizzledSelector = @selector(markdown_textDidChange); Method originalMethod = class_getInstanceMethod(cls, originalSelector); Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); - method_exchangeImplementations(originalMethod, swizzledMethod); + // method_exchangeImplementations(originalMethod, swizzledMethod); }); } diff --git a/src/styleUtils.ts b/src/styleUtils.ts index 47cfa529..5fdaa383 100644 --- a/src/styleUtils.ts +++ b/src/styleUtils.ts @@ -7,6 +7,7 @@ type PartialMarkdownStyle = Partial<{ const FONT_FAMILY_MONOSPACE = Platform.select({ ios: 'Courier', + macos: 'Menlo', default: 'monospace', });