Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macOS support #149

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ parser/react-native-live-markdown-parser.js

# any js file inside android and ios folders
**/android/**/*.js
**/ios/**/*.js
**/apple/**/*.js

# Output of the build process & scripts
lib/**/*
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions ios/MarkdownLayoutManager.h → apple/MarkdownLayoutManager.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
#import <UIKit/UIKit.h>
#import <react-native-live-markdown/RCTMarkdownUtils.h>

NS_ASSUME_NONNULL_BEGIN

@interface MarkdownLayoutManager : NSLayoutManager

@property(nonatomic) RCTMarkdownUtils *markdownUtils;

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#import <objc/runtime.h>
#import <react-native-live-markdown/MarkdownLayoutManager.h>

@implementation MarkdownLayoutManager
Expand All @@ -7,7 +8,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;
RCTMarkdownUtils *markdownUtils = [self valueForKey:@"markdownUtils"];
RCTMarkdownUtils *markdownUtils = objc_getAssociatedObject(self, @selector(markdownUtils));
[markdownUtils.blockquoteRanges enumerateObjectsUsingBlock:^(NSValue *item, NSUInteger idx, BOOL * _Nonnull stop) {
NSRange range = [item rangeValue];
NSUInteger start = range.location;
Expand All @@ -27,7 +28,7 @@ - (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origi
CGFloat height = rect.size.height;
CGRect lineRect = CGRectMake(x, y, width, height);
[markdownUtils.markdownStyle.blockquoteBorderColor setFill];
UIRectFill(lineRect);
NSRectFill(lineRect);
}
}];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#import <UIKit/UIKit.h>
#import <react-native-live-markdown/RCTMarkdownStyle.h>

NS_ASSUME_NONNULL_BEGIN

@interface MarkdownTextInputDecoratorView : UIView
@interface MarkdownTextInputDecoratorView : RCTUIView

- (void)setMarkdownStyle:(RCTMarkdownStyle *)markdownStyle;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,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.");
Expand All @@ -55,7 +55,7 @@ - (void)didMoveToWindow {
#else
react_native_assert([view isKindOfClass:[RCTBaseTextInputView class]] && "Previous sibling component is not an instance of RCTBaseTextInputView.");
_textInput = (RCTBaseTextInputView *)view;
UIView<RCTBackedTextInputViewProtocol> *backedTextInputView = _textInput.backedTextInputView;
RCTUIView<RCTBackedTextInputViewProtocol> *backedTextInputView = _textInput.backedTextInputView;
#endif /* RCT_NEW_ARCH_ENABLED */

_markdownUtils = [[RCTMarkdownUtils alloc] initWithBackedTextInputView:backedTextInputView];
Expand All @@ -73,13 +73,13 @@ - (void)didMoveToWindow {
NSLayoutManager *layoutManager = _textView.layoutManager; // switching to TextKit 1 compatibility mode
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:(NSWindow *)newWindow
{
if (_textInput != nil) {
[_textInput setMarkdownUtils:nil];
Expand All @@ -89,9 +89,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]);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ @implementation MarkdownTextInputDecoratorViewManager

RCT_EXPORT_MODULE(MarkdownTextInputDecoratorView)

- (UIView *)view
- (RCTUIView *)view
{
return [[MarkdownTextInputDecoratorView alloc] init];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.selectedRange;
backedTextInputView.attributedText = [markdownUtils parseMarkdown:backedTextInputView.attributedText];
[backedTextInputView setSelectedTextRange:range notifyDelegate:YES];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ - (void)markdown_updateLocalData
{
RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils];
if (markdownUtils != nil) {
UITextRange *range = self.backedTextInputView.selectedTextRange;
NSRange range = self.backedTextInputView.selectedRange;
NSAttributedString *attributedText = [markdownUtils parseMarkdown:self.backedTextInputView.attributedText];
[self.backedTextInputView setAttributedText:attributedText];
[self.backedTextInputView setSelectedTextRange:range notifyDelegate:YES];
Expand Down Expand Up @@ -58,7 +58,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);
}
});
}
Expand Down
24 changes: 13 additions & 11 deletions ios/RCTMarkdownStyle.h → apple/RCTMarkdownStyle.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import <React/RCTUIKit.h> // [macOS]

#ifdef RCT_NEW_ARCH_ENABLED
#import <react/renderer/components/RNLiveMarkdownSpec/Props.h>
#endif /* RCT_NEW_ARCH_ENABLED */
Expand All @@ -6,23 +8,23 @@ 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) UIColor *blockquoteBorderColor;
@property (nonatomic) RCTUIColor *blockquoteBorderColor;
@property (nonatomic) CGFloat blockquoteBorderWidth;
@property (nonatomic) CGFloat blockquoteMarginLeft;
@property (nonatomic) CGFloat blockquotePaddingLeft;
@property (nonatomic) NSString *codeFontFamily;
@property (nonatomic) UIColor *codeColor;
@property (nonatomic) UIColor *codeBackgroundColor;
@property (nonatomic) RCTUIColor *codeColor;
@property (nonatomic) RCTUIColor *codeBackgroundColor;
@property (nonatomic) NSString *preFontFamily;
@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) RCTUIColor *preColor;
@property (nonatomic) RCTUIColor *preBackgroundColor;
@property (nonatomic) RCTUIColor *mentionHereColor;
@property (nonatomic) RCTUIColor *mentionHereBackgroundColor;
@property (nonatomic) RCTUIColor *mentionUserColor;
@property (nonatomic) RCTUIColor *mentionUserBackgroundColor;

#ifdef RCT_NEW_ARCH_ENABLED
- (instancetype)initWithStruct:(const facebook::react::MarkdownTextInputDecoratorViewMarkdownStyleStruct &)style;
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions ios/RCTMarkdownUtils.h → apple/RCTMarkdownUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic) RCTMarkdownStyle *markdownStyle;
@property (nonatomic) NSMutableArray<NSValue *> *blockquoteRanges;
@property (weak, nonatomic) UIView<RCTBackedTextInputViewProtocol> *backedTextInputView;
@property (weak, nonatomic) RCTUIView<RCTBackedTextInputViewProtocol> *backedTextInputView;

- (instancetype)initWithBackedTextInputView:(UIView<RCTBackedTextInputViewProtocol> *)backedTextInputView;
- (instancetype)initWithBackedTextInputView:(RCTUIView<RCTBackedTextInputViewProtocol> *)backedTextInputView;

- (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input;

Expand Down
2 changes: 1 addition & 1 deletion ios/RCTMarkdownUtils.mm → apple/RCTMarkdownUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ @implementation RCTMarkdownUtils {
__weak RCTMarkdownStyle *_prevMarkdownStyle;
}

- (instancetype)initWithBackedTextInputView:(UIView<RCTBackedTextInputViewProtocol> *)backedTextInputView
- (instancetype)initWithBackedTextInputView:(RCTUIView<RCTBackedTextInputViewProtocol> *)backedTextInputView
{
if (self = [super init]) {
_backedTextInputView = backedTextInputView;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#import <UIKit/UIKit.h>
#import <React/RCTUITextView.h>
#import <react-native-live-markdown/RCTMarkdownUtils.h>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ - (void)markdown_textDidChange
{
RCTMarkdownUtils *markdownUtils = [self getMarkdownUtils];
if (markdownUtils != nil) {
UITextRange *range = self.selectedTextRange;
super.attributedText = [markdownUtils parseMarkdown:self.attributedText];
[super setSelectedTextRange:range]; // prevents cursor from jumping at the end when typing in the middle of the text
NSRange range = self.selectedRange;
NSAttributedString *attributedText = [markdownUtils parseMarkdown:self.attributedText];
[self breakUndoCoalescing];
[self.textStorage setAttributedString:attributedText ?: [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
}

Expand All @@ -35,7 +37,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);
});
}

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
"lib",
"parser/react-native-live-markdown-parser.js",
"android",
"ios",
"apple",
"cpp",
"*.podspec",
"!ios/build",
"!apple/build",
"!android/build",
"!android/gradle",
"!android/gradlew",
Expand Down
4 changes: 2 additions & 2 deletions react-native-live-markdown.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ Pod::Spec.new do |s|
s.license = package["license"]
s.authors = package["author"]

s.platforms = { :ios => "11.0" }
s.platforms = { :ios => "11.0", osx: "10.15" }
s.source = { :git => "https://github.com/expensify/react-native-live-markdown.git", :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,m,mm}"
s.source_files = "apple/**/*.{h,m,mm}"

s.resources = "parser/react-native-live-markdown-parser.js"

Expand Down
1 change: 1 addition & 0 deletions src/MarkdownTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type MarkdownStyle = MarkdownTextInputDecoractorView.MarkdownStyle;

const FONT_FAMILY_MONOSPACE = Platform.select({
ios: 'Courier',
macos: 'Courier',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
macos: 'Courier',
macos: 'SF Mono',

Can we use SF Mono as default for macOS?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, thanks for this suggestion! Do we want to stick with Courier on iOS though?

Copy link

@Simek Simek Feb 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done a bit of checking, and looks like SF fonts are not included by default for all systems to this day on both iOS and macOS.

In this case Menlo or Monaco could be a viable alternative for both platforms:

default: 'monospace',
});

Expand Down
2 changes: 1 addition & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"outputs": []
},
"build:ios": {
"inputs": ["package.json", "*.podspec", "ios", "src/*.ts", "src/*.tsx", "example/package.json", "example/ios", "!example/ios/build", "!example/ios/Pods"],
"inputs": ["package.json", "*.podspec", "apple", "src/*.ts", "src/*.tsx", "example/package.json", "example/ios", "!example/ios/build", "!example/ios/Pods"],
"outputs": []
}
}
Expand Down
Loading