-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
iOS works (using method swizzling, for all TextInputs)
- Loading branch information
Showing
12 changed files
with
296 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#import <React/RCTBackedTextInputDelegateAdapter.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface RCTBackedTextFieldDelegateAdapter (Markdown) | ||
|
||
- (void)markdown_textFieldDidChange; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#import <react-native-markdown-text-input/RCTBackedTextFieldDelegateAdapter+Markdown.h> | ||
#import <react-native-markdown-text-input/RCTMarkdownUtils.h> | ||
#import <React/RCTUITextField.h> | ||
#import <objc/message.h> | ||
|
||
@implementation RCTBackedTextFieldDelegateAdapter (Markdown) | ||
|
||
- (void)markdown_textFieldDidChange | ||
{ | ||
RCTUITextField *backedTextInputView = [self valueForKey:@"_backedTextInputView"]; | ||
UITextRange *range = backedTextInputView.selectedTextRange; | ||
backedTextInputView.attributedText = [RCTMarkdownUtils parseMarkdown:backedTextInputView.attributedText.string]; | ||
[backedTextInputView setSelectedTextRange:range notifyDelegate:YES]; | ||
|
||
// Call the original method | ||
[self markdown_textFieldDidChange]; | ||
} | ||
|
||
+ (void)load | ||
{ | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
Class cls = [self class]; | ||
SEL originalSelector = @selector(textFieldDidChange); | ||
SEL swizzledSelector = @selector(markdown_textFieldDidChange); | ||
Method originalMethod = class_getInstanceMethod(cls, originalSelector); | ||
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); | ||
method_exchangeImplementations(originalMethod, swizzledMethod); | ||
}); | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#import <React/RCTBaseTextInputView.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface RCTBaseTextInputView (Markdown) | ||
|
||
- (void)markdown_setAttributedText:(NSAttributedString *)attributedText; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#import <react-native-markdown-text-input/RCTBaseTextInputView+Markdown.h> | ||
#import <react-native-markdown-text-input/RCTMarkdownUtils.h> | ||
#import <objc/message.h> | ||
|
||
@implementation RCTBaseTextInputView (Markdown) | ||
|
||
- (void)markdown_setAttributedText:(NSAttributedString *)attributedText | ||
{ | ||
if (attributedText != nil) { | ||
attributedText = [RCTMarkdownUtils parseMarkdown:attributedText.string]; | ||
} | ||
|
||
// Call the original method | ||
[self markdown_setAttributedText:attributedText]; | ||
} | ||
|
||
+ (void)load | ||
{ | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
Class cls = [self class]; | ||
SEL originalSelector = @selector(setAttributedText:); | ||
SEL swizzledSelector = @selector(markdown_setAttributedText:); | ||
Method originalMethod = class_getInstanceMethod(cls, originalSelector); | ||
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); | ||
method_exchangeImplementations(originalMethod, swizzledMethod); | ||
}); | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@interface RCTMarkdownUtils : NSObject | ||
|
||
+ (nonnull NSAttributedString *)parseMarkdown:(nonnull NSString *)input; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#import <react-native-markdown-text-input/RCTMarkdownUtils.h> | ||
#import <JavaScriptCore/JavaScriptCore.h> | ||
|
||
@implementation RCTMarkdownUtils | ||
|
||
+ (NSAttributedString *)parseMarkdown:(nonnull NSString *)input { | ||
static JSContext *ctx = nil; | ||
static JSValue *function = nil; | ||
if (ctx == nil) { | ||
NSString *path = [[NSBundle mainBundle] pathForResource:@"out" ofType:@"js"]; | ||
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; | ||
ctx = [[JSContext alloc] init]; | ||
[ctx evaluateScript:content]; | ||
function = ctx[@"parseMarkdownToTextAndRanges"]; | ||
} | ||
|
||
JSValue *result = [function callWithArguments:@[input]]; | ||
NSString *text = [result[0] toString]; | ||
NSArray *ranges = [result[1] toArray]; | ||
|
||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text]; | ||
[attributedString beginEditing]; | ||
|
||
[attributedString addAttribute:NSFontAttributeName | ||
value:[UIFont systemFontOfSize:20] | ||
range:NSMakeRange(0, [text length])]; | ||
|
||
NSMutableArray *quoteRanges = [NSMutableArray new]; | ||
|
||
[ranges enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | ||
NSArray *item = obj; | ||
NSString *type = item[0]; | ||
NSUInteger location = [item[1] unsignedIntegerValue]; | ||
NSUInteger length = [item[2] unsignedIntegerValue]; | ||
NSRange range = NSMakeRange(location, length); | ||
|
||
UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:location effectiveRange:NULL]; | ||
UIFontDescriptor *fontDescriptor = [font fontDescriptor]; | ||
UIFontDescriptorSymbolicTraits existingTraits = fontDescriptor.symbolicTraits; | ||
UIFontDescriptorSymbolicTraits desiredTraits = UIFontDescriptorClassMask; | ||
|
||
if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"h1"]) { | ||
desiredTraits = existingTraits | UIFontDescriptorTraitBold; | ||
} else if ([type isEqualToString:@"italic"]) { | ||
desiredTraits = existingTraits | UIFontDescriptorTraitItalic; | ||
} else if ([type isEqualToString:@"code"] || [type isEqualToString:@"pre"]) { | ||
desiredTraits = existingTraits | UIFontDescriptorTraitMonoSpace; | ||
} else if ([type isEqualToString:@"syntax"]) { | ||
desiredTraits = UIFontDescriptorTraitBold; // TODO: remove italic in nested bold+italic | ||
} | ||
|
||
UIFontDescriptor *newFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:desiredTraits]; | ||
CGFloat size = 0; // Passing 0 to size keeps the existing size | ||
if ([type isEqualToString:@"h1"]) { | ||
size = 25; | ||
} | ||
UIFont *newFont = [UIFont fontWithDescriptor:newFontDescriptor size:size]; | ||
[attributedString addAttribute:NSFontAttributeName value:newFont range:range]; | ||
|
||
if ([type isEqualToString:@"syntax"]) { | ||
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range:range]; | ||
} else if ([type isEqualToString:@"strikethrough"]) { | ||
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range]; | ||
} else if ([type isEqualToString:@"code"]) { | ||
[attributedString addAttribute:NSForegroundColorAttributeName value:[[UIColor alloc] initWithRed:6/255.0 green:25/255.0 blue:109/255.0 alpha:1.0] range:range]; | ||
[attributedString addAttribute:NSBackgroundColorAttributeName value:[[UIColor alloc] initWithRed:0.95 green:0.95 blue:0.95 alpha:1.0] range:range]; | ||
} else if ([type isEqualToString:@"mention"]) { | ||
[attributedString addAttribute:NSBackgroundColorAttributeName value:[[UIColor alloc] initWithRed:252/255.0 green:232/255.0 blue:142/255.0 alpha:1.0] range:range]; | ||
} else if ([type isEqualToString:@"link"]) { | ||
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range]; | ||
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:range]; | ||
} else if ([type isEqualToString:@"blockquote"]) { | ||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; | ||
paragraphStyle.firstLineHeadIndent = 11; | ||
paragraphStyle.headIndent = 11; | ||
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; | ||
[quoteRanges addObject:[NSValue valueWithRange:range]]; | ||
} else if ([type isEqualToString:@"pre"]) { | ||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; | ||
paragraphStyle.firstLineHeadIndent = 5; | ||
paragraphStyle.headIndent = 5; | ||
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; | ||
} | ||
}]; | ||
|
||
[attributedString endEditing]; | ||
|
||
return attributedString; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#import <UIKit/UIKit.h> | ||
|
||
#import <React/RCTUITextView.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface RCTUITextView (Markdown) | ||
|
||
- (void)markdown_textDidChange; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#import <react-native-markdown-text-input/RCTUITextView+Markdown.h> | ||
#import <react-native-markdown-text-input/RCTMarkdownUtils.h> | ||
#import <objc/message.h> | ||
|
||
@implementation RCTUITextView (Markdown) | ||
|
||
- (void)markdown_textDidChange | ||
{ | ||
UITextRange *range = self.selectedTextRange; | ||
super.attributedText = [RCTMarkdownUtils parseMarkdown:self.attributedText.string]; | ||
[super setSelectedTextRange:range]; // prevents cursor from jumping at the end when typing in the middle of the text | ||
self.typingAttributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:20]}; // removes indent in new line when typing after quote | ||
|
||
// Call the original method | ||
[self markdown_textDidChange]; | ||
} | ||
|
||
+ (void)load | ||
{ | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
Class cls = [self class]; | ||
SEL originalSelector = @selector(textDidChange); | ||
SEL swizzledSelector = @selector(markdown_textDidChange); | ||
Method originalMethod = class_getInstanceMethod(cls, originalSelector); | ||
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector); | ||
method_exchangeImplementations(originalMethod, swizzledMethod); | ||
}); | ||
} | ||
|
||
@end |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters