diff --git a/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java b/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java index 5b746240d..7db53863a 100644 --- a/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java +++ b/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java @@ -19,8 +19,10 @@ public class MarkdownStyle { private final float mQuoteBorderWidth; private final float mQuoteMarginLeft; private final float mQuotePaddingLeft; + private final String mCodeFontFamily; private final int mCodeColor; private final int mCodeBackgroundColor; + private final String mPreFontFamily; private final int mPreColor; private final int mPreBackgroundColor; private final int mMentionHereBackgroundColor; @@ -34,8 +36,10 @@ public MarkdownStyle(@NonNull ReadableMap map, @NonNull Context context) { mQuoteBorderWidth = parseFloat(map, "quote", "borderWidth"); mQuoteMarginLeft = parseFloat(map, "quote", "marginLeft"); mQuotePaddingLeft = parseFloat(map, "quote", "paddingLeft"); + mCodeFontFamily = parseString(map, "code", "fontFamily"); mCodeColor = parseColor(map, "code", "color", context); mCodeBackgroundColor = parseColor(map, "code", "backgroundColor", context); + mPreFontFamily = parseString(map, "pre", "fontFamily"); mPreColor = parseColor(map, "pre", "color", context); mPreBackgroundColor = parseColor(map, "pre", "backgroundColor", context); mMentionHereBackgroundColor = parseColor(map, "mentionHere", "backgroundColor", context); @@ -63,6 +67,12 @@ private static float parseFloat(@NonNull ReadableMap map, @NonNull String key, @ return (float) value; } + private static String parseString(@NonNull ReadableMap map, @NonNull String key, @NonNull String prop) { + ReadableMap style = map.getMap(key); + Objects.requireNonNull(style); + return style.getString(prop); + } + public int getSyntaxColor() { return mSyntaxColor; } @@ -91,6 +101,10 @@ public float getQuotePaddingLeft() { return mQuotePaddingLeft; } + public String getCodeFontFamily() { + return mCodeFontFamily; + } + public int getCodeColor() { return mCodeColor; } @@ -99,6 +113,10 @@ public int getCodeBackgroundColor() { return mCodeBackgroundColor; } + public String getPreFontFamily() { + return mPreFontFamily; + } + public int getPreColor() { return mPreColor; } diff --git a/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java b/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java index eff0a9aaa..5bf0cbd26 100644 --- a/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +++ b/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java @@ -108,10 +108,18 @@ private Object makeLinkColorSpan() { return new ForegroundColorSpan(mMarkdownStyle.getLinkColor()); } + private Object makeCodeFontFamilySpan() { + return new TypefaceSpan(mMarkdownStyle.getCodeFontFamily()); + } + private Object makeCodeColorSpan() { return new ForegroundColorSpan(mMarkdownStyle.getCodeColor()); } + private Object makePreFontFamilySpan() { + return new TypefaceSpan(mMarkdownStyle.getPreFontFamily()); + } + private Object makePreColorSpan() { return new ForegroundColorSpan(mMarkdownStyle.getPreColor()); } @@ -209,12 +217,12 @@ private void applyRange(SpannableStringBuilder ssb, String type, int start, int setSpan(ssb, makeLinkColorSpan(), start, end); break; case "code": - setSpan(ssb, makeMonospaceSpan(), start, end); + setSpan(ssb, makeCodeFontFamilySpan(), start, end); setSpan(ssb, makeCodeColorSpan(), start, end); setSpan(ssb, makeCodeBackgroundSpan(), start, end); break; case "pre": - setSpan(ssb, makeMonospaceSpan(), start, end); + setSpan(ssb, makePreFontFamilySpan(), start, end); setSpan(ssb, makePreColorSpan(), start, end); setSpan(ssb, makePreBackgroundSpan(), start, end); break; diff --git a/ios/RCTMarkdownStyle.h b/ios/RCTMarkdownStyle.h index 83df35f00..dfd276864 100644 --- a/ios/RCTMarkdownStyle.h +++ b/ios/RCTMarkdownStyle.h @@ -11,8 +11,10 @@ @property (nonatomic) CGFloat quoteBorderWidth; @property (nonatomic) CGFloat quoteMarginLeft; @property (nonatomic) CGFloat quotePaddingLeft; +@property (nonatomic, nonnull) NSString *codeFontFamily; @property (nonatomic, nonnull) UIColor *codeColor; @property (nonatomic, nonnull) UIColor *codeBackgroundColor; +@property (nonatomic, nonnull) NSString *preFontFamily; @property (nonatomic, nonnull) UIColor *preColor; @property (nonatomic, nonnull) UIColor *preBackgroundColor; @property (nonatomic, nonnull) UIColor *mentionHereBackgroundColor; diff --git a/ios/RCTMarkdownStyle.mm b/ios/RCTMarkdownStyle.mm index 98aa2fc43..1cbe7f0f6 100644 --- a/ios/RCTMarkdownStyle.mm +++ b/ios/RCTMarkdownStyle.mm @@ -24,9 +24,11 @@ - (instancetype)initWithStruct:(const facebook::react::MarkdownTextInputDecorato _quoteMarginLeft = style.quote.marginLeft; _quotePaddingLeft = style.quote.paddingLeft; + _codeFontFamily = RCTNSStringFromString(style.code.fontFamily); _codeColor = RCTUIColorFromSharedColor(style.code.color); _codeBackgroundColor = RCTUIColorFromSharedColor(style.code.backgroundColor); + _preFontFamily = RCTNSStringFromString(style.pre.fontFamily); _preColor = RCTUIColorFromSharedColor(style.pre.color); _preBackgroundColor = RCTUIColorFromSharedColor(style.pre.backgroundColor); @@ -54,9 +56,11 @@ - (instancetype)initWithDictionary:(nonnull NSDictionary *)json _quoteMarginLeft = [json[@"quote"][@"marginLeft"] floatValue]; _quotePaddingLeft = [json[@"quote"][@"paddingLeft"] floatValue]; + _codeFontFamily = [RCTConvert NSString:json[@"code"][@"fontFamily"]]; _codeColor = [RCTConvert UIColor:json[@"code"][@"color"]]; _codeBackgroundColor = [RCTConvert UIColor:json[@"code"][@"backgroundColor"]]; + _preFontFamily = [RCTConvert NSString:json[@"pre"][@"fontFamily"]]; _preColor = [RCTConvert UIColor:json[@"pre"][@"color"]]; _preBackgroundColor = [RCTConvert UIColor:json[@"pre"][@"backgroundColor"]]; diff --git a/ios/RCTMarkdownUtils.mm b/ios/RCTMarkdownUtils.mm index f17eb9990..9bcd08153 100644 --- a/ios/RCTMarkdownUtils.mm +++ b/ios/RCTMarkdownUtils.mm @@ -1,6 +1,7 @@ #import #import #import +#import #import @implementation RCTMarkdownUtils { @@ -63,24 +64,25 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input NSUInteger length = [item[2] unsignedIntegerValue]; NSRange range = NSMakeRange(location, length); - if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"h1"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"] || [type isEqualToString:@"italic"] || [type isEqualToString:@"code"] || [type isEqualToString:@"pre"] || [type isEqualToString:@"h1"]) { + if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"] || [type isEqualToString:@"italic"] || [type isEqualToString:@"code"] || [type isEqualToString:@"pre"] || [type isEqualToString:@"h1"]) { UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:location effectiveRange:NULL]; - UIFontDescriptor *fontDescriptor = [font fontDescriptor]; - UIFontDescriptorSymbolicTraits traits = fontDescriptor.symbolicTraits; - if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"h1"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"]) { - traits |= UIFontDescriptorTraitBold; + if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"]) { + font = [RCTFont updateFont:font withWeight:@"bold"]; } else if ([type isEqualToString:@"italic"]) { - traits |= UIFontDescriptorTraitItalic; - } else if ([type isEqualToString:@"code"] || [type isEqualToString:@"pre"]) { - traits |= UIFontDescriptorTraitMonoSpace; + font = [RCTFont updateFont:font withStyle:@"italic"]; + } else if ([type isEqualToString:@"code"]) { + font = [RCTFont updateFont:font withFamily:_markdownStyle.codeFontFamily]; + } else if ([type isEqualToString:@"pre"]) { + font = [RCTFont updateFont:font withFamily:_markdownStyle.preFontFamily]; + } else if ([type isEqualToString:@"h1"]) { + font = [RCTFont updateFont:font withFamily:nil + size:[NSNumber numberWithFloat:_markdownStyle.h1FontSize] + weight:@"bold" + style:nil + variant:nil + scaleMultiplier:0]; } - UIFontDescriptor *newFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:traits]; - CGFloat size = 0; // Passing 0 to size keeps the existing size - if ([type isEqualToString:@"h1"]) { - size = _markdownStyle.h1FontSize; - } - UIFont *newFont = [UIFont fontWithDescriptor:newFontDescriptor size:size]; - [attributedString addAttribute:NSFontAttributeName value:newFont range:range]; + [attributedString addAttribute:NSFontAttributeName value:font range:range]; } if ([type isEqualToString:@"syntax"]) { @@ -107,6 +109,8 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input [_quoteRanges addObject:[NSValue valueWithRange:range]]; } else if ([type isEqualToString:@"pre"]) { [attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range]; + NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range; + [attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground]; // TODO: pass background color and ranges to layout manager } else if ([type isEqualToString:@"h1"]) { NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; diff --git a/src/MarkdownTextInput.tsx b/src/MarkdownTextInput.tsx index b5fdbaf8d..f70749baf 100644 --- a/src/MarkdownTextInput.tsx +++ b/src/MarkdownTextInput.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {StyleSheet, TextInput, processColor} from 'react-native'; +import { Platform, StyleSheet, TextInput, processColor } from 'react-native'; import type {TextInputProps} from 'react-native'; import type * as MarkdownTextInputDecoractorView from './MarkdownTextInputDecoratorViewNativeComponent'; @@ -7,6 +7,11 @@ import MarkdownTextInputDecoratorViewNativeComponent from './MarkdownTextInputDe type MarkdownStyle = MarkdownTextInputDecoractorView.MarkdownStyle; +const FONT_FAMILY_MONOSPACE = Platform.select({ + ios: 'Courier', + default: 'monospace', +}); + function makeDefaultMarkdownStyle(): MarkdownStyle { return { syntax: { @@ -25,10 +30,12 @@ function makeDefaultMarkdownStyle(): MarkdownStyle { paddingLeft: 6, }, code: { + fontFamily: FONT_FAMILY_MONOSPACE, color: 'black', backgroundColor: 'lightgray', }, pre: { + fontFamily: FONT_FAMILY_MONOSPACE, color: 'black', backgroundColor: 'lightgray', }, diff --git a/src/MarkdownTextInputDecoratorViewNativeComponent.ts b/src/MarkdownTextInputDecoratorViewNativeComponent.ts index 209a24e6b..738f69a32 100644 --- a/src/MarkdownTextInputDecoratorViewNativeComponent.ts +++ b/src/MarkdownTextInputDecoratorViewNativeComponent.ts @@ -20,10 +20,12 @@ interface MarkdownStyle { paddingLeft: Float; }; code: { + fontFamily: string; color: ColorValue; backgroundColor: ColorValue; }; pre: { + fontFamily: string; color: ColorValue; backgroundColor: ColorValue; };