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

Inline code block support #43

Closed
wants to merge 7 commits into from
Closed
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
1 change: 1 addition & 0 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const UIView = {
collapsable: true,
needsOffscreenAlphaCompositing: true,
style: ReactNativeStyleAttributes,
textCodeBlockStyle: true,
};

const RCTView = {
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/BaseText/RCTBaseTextViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ - (RCTShadowView *)shadowView
// Special
RCT_REMAP_SHADOW_PROPERTY(isHighlighted, textAttributes.isHighlighted, BOOL)
RCT_REMAP_SHADOW_PROPERTY(textTransform, textAttributes.textTransform, RCTTextTransform)
RCT_REMAP_SHADOW_PROPERTY(textCodeBlockStyle, textAttributes.textCodeBlockStyle, NSDictionary)

@end
2 changes: 2 additions & 0 deletions Libraries/Text/RCTTextAttributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN

extern NSString *const RCTTextAttributesIsHighlightedAttributeName;
extern NSString *const RCTTextAttributesTagAttributeName;
extern NSString *const RCTTextAttributesIsTextCodeBlockStyleAttributeName;

/**
* Represents knowledge about all supported *text* attributes
Expand Down Expand Up @@ -57,6 +58,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
@property (nonatomic, strong, nullable) NSNumber *tag;
@property (nonatomic, assign) UIUserInterfaceLayoutDirection layoutDirection;
@property (nonatomic, assign) RCTTextTransform textTransform;
@property (nonatomic, assign) NSDictionary *textCodeBlockStyle;

#pragma mark - Inheritance

Expand Down
14 changes: 13 additions & 1 deletion Libraries/Text/RCTTextAttributes.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

NSString *const RCTTextAttributesIsHighlightedAttributeName = @"RCTTextAttributesIsHighlightedAttributeName";
NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttributeName";
NSString *const RCTTextAttributesIsTextCodeBlockStyleAttributeName = @"RCTTextAttributesIsTextCodeBlockStyleAttributeName";

@implementation RCTTextAttributes

Expand Down Expand Up @@ -95,6 +96,12 @@ - (void)applyTextAttributes:(RCTTextAttributes *)textAttributes
: _layoutDirection;
_textTransform =
textAttributes->_textTransform != RCTTextTransformUndefined ? textAttributes->_textTransform : _textTransform;
_textCodeBlockStyle = textAttributes->_textCodeBlockStyle ?: _textCodeBlockStyle;
}

- (NSDictionary *)textCodeBlockStyle
{
return _textCodeBlockStyle;
}

- (NSParagraphStyle *)effectiveParagraphStyle
Expand Down Expand Up @@ -211,6 +218,10 @@ - (NSParagraphStyle *)effectiveParagraphStyle
attributes[RCTTextAttributesTagAttributeName] = _tag;
}

if (_textCodeBlockStyle) {
attributes[RCTTextAttributesIsTextCodeBlockStyleAttributeName] = _textCodeBlockStyle;
}

return [attributes copy];
}

Expand Down Expand Up @@ -344,7 +355,8 @@ - (BOOL)isEqual:(RCTTextAttributes *)textAttributes
RCTTextAttributesCompareObjects(_textShadowColor) &&
// Special
RCTTextAttributesCompareOthers(_isHighlighted) && RCTTextAttributesCompareObjects(_tag) &&
RCTTextAttributesCompareOthers(_layoutDirection) && RCTTextAttributesCompareOthers(_textTransform);
RCTTextAttributesCompareOthers(_layoutDirection) && RCTTextAttributesCompareOthers(_textTransform) &&
RCTTextAttributesCompareObjects(_textCodeBlockStyle);
}

@end
16 changes: 16 additions & 0 deletions Libraries/Text/Text/RCTTextCodeBlockStyle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCTTextCodeBlockStyle : NSLayoutManager

Choose a reason for hiding this comment

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

What's the reason for using custom LayoutManager?
There are already other attributes like background colour and they don't require the change.
I guess we want to merge it later upstream so I think it makes sense to avoid introducing new mechanisms if they are not needed. However, I'm not sure we need it while writing this comment :)

Choose a reason for hiding this comment

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


@end

NS_ASSUME_NONNULL_END
98 changes: 98 additions & 0 deletions Libraries/Text/Text/RCTTextCodeBlockStyle.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import "RCTTextCodeBlockStyle.h"
#import "RCTTextAttributes.h"

@implementation RCTTextCodeBlockStyle

- (UIColor*)hexStringToColor:(NSString *)stringToConvert

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Awesome, +1.

{
NSString *noHashString = [stringToConvert stringByReplacingOccurrencesOfString:@"#" withString:@""];
NSScanner *stringScanner = [NSScanner scannerWithString:noHashString];

unsigned hex;
if (![stringScanner scanHexInt:&hex]) return nil;
int r = (hex >> 16) & 0xFF;
int g = (hex >> 8) & 0xFF;
int b = (hex) & 0xFF;

return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:1.0f];
}

-(void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin {
[super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin];

if ((glyphsToShow.location + glyphsToShow.length) > [[self textStorage] length]) {
return;
}

[[self textStorage] enumerateAttribute:RCTTextAttributesIsTextCodeBlockStyleAttributeName
inRange:glyphsToShow
options:0
usingBlock:^(NSDictionary *textCodeBlockStyle, NSRange range, __unused BOOL *stop) {

NSString *backgroundColor = [textCodeBlockStyle objectForKey:@"backgroundColor"];
NSString *borderColor = [textCodeBlockStyle objectForKey:@"borderColor"];
float borderRadius = [[textCodeBlockStyle objectForKey:@"borderRadius"] floatValue];
float borderWidth = [[textCodeBlockStyle objectForKey:@"borderWidth"] floatValue];
float horizontalOffset = 5;

Choose a reason for hiding this comment

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

shouldn't it be exposed to JS?

float verticalOffset = 2;

Choose a reason for hiding this comment

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

shouldn't it be exposed to JS?


CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [self hexStringToColor:backgroundColor].CGColor);
CGContextSetStrokeColorWithColor(context, [self hexStringToColor:borderColor].CGColor);

if (!backgroundColor) {
return;
}

// Enumerates line fragments intersecting with the whole text container.
[self enumerateLineFragmentsForGlyphRange:range
usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange lineGlyphRange, BOOL * _Nonnull stop) {

__block UIBezierPath *textCodeBlockStylePath = nil;

NSRange lineRange = NSIntersectionRange(range, lineGlyphRange);

[self enumerateEnclosingRectsForGlyphRange:lineRange
withinSelectedGlyphRange:lineRange
inTextContainer:textContainer
usingBlock:^(CGRect enclosingRect, __unused BOOL *anotherStop) {

BOOL isFirstLine = range.location >= lineGlyphRange.location;
BOOL isLastLine = range.length + range.location <= lineGlyphRange.length + lineGlyphRange.location;
long corners = (
(isFirstLine ? (UIRectCornerTopLeft | UIRectCornerBottomLeft) : 0) |
(isLastLine ? (UIRectCornerTopRight | UIRectCornerBottomRight) : 0)
);

CGRect resultRect = CGRectMake(
enclosingRect.origin.x,
enclosingRect.origin.y + (borderWidth / 2) + verticalOffset,
enclosingRect.size.width + ((isFirstLine && isLastLine) || isLastLine ? 0 : horizontalOffset),
enclosingRect.size.height - (borderWidth * 2)
);

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:resultRect byRoundingCorners:corners cornerRadii:CGSizeMake(borderRadius, borderRadius)];

Choose a reason for hiding this comment

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

Should we set those values here?

context.setAllowsAntialiasing(true)
context.setShouldAntialias(true)


if (textCodeBlockStylePath) {
[textCodeBlockStylePath appendPath:path];
} else {
textCodeBlockStylePath = path;
}

textCodeBlockStylePath.lineWidth = borderWidth;
[textCodeBlockStylePath stroke];
[textCodeBlockStylePath fill];
}];
}];

}];
}

@end
3 changes: 2 additions & 1 deletion Libraries/Text/Text/RCTTextShadowView.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#import <React/RCTTextView.h>
#import "NSTextStorage+FontScaling.h"
#import "RCTTextCodeBlockStyle.h"

@implementation RCTTextShadowView {
__weak RCTBridge *_bridge;
Expand Down Expand Up @@ -219,7 +220,7 @@ - (NSTextStorage *)textStorageAndLayoutManagerThatFitsSize:(CGSize)size exclusiv
textContainer.lineBreakMode = _maximumNumberOfLines > 0 ? _lineBreakMode : NSLineBreakByClipping;
textContainer.maximumNumberOfLines = _maximumNumberOfLines;

NSLayoutManager *layoutManager = [NSLayoutManager new];
NSLayoutManager *layoutManager = [RCTTextCodeBlockStyle new];
layoutManager.usesFontLeading = NO;
[layoutManager addTextContainer:textContainer];

Expand Down
33 changes: 33 additions & 0 deletions Libraries/Text/TextCodeBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/

import type {ColorValue} from 'react-native/Libraries/StyleSheet/StyleSheet';

export type TextCodeBlockStyleProp = $ReadOnly<{|
/**
* The background color of the text code block.
*/
backgroundColor?: ?ColorValue,
Copy link
Member

Choose a reason for hiding this comment

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

I have seen ? in the property key. Are these properties nullable?

Copy link
Author

Choose a reason for hiding this comment

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


/**
* The border color of the text code block.
*/
borderColor?: ?ColorValue,

/**
* The border radius of the text code block.
*/
borderRadius?: ?number,

/**
* The border width of the text code block.
*/
borderWidth?: ?number,
|}>;
2 changes: 2 additions & 0 deletions Libraries/Text/TextNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const textViewConfig = {
dataDetectorType: true,
android_hyphenationFrequency: true,
lineBreakStrategyIOS: true,
textCodeBlockStyle: true,
},
directEventTypes: {
topTextLayout: {
Expand All @@ -64,6 +65,7 @@ const virtualTextViewConfig = {
isHighlighted: true,
isPressable: true,
maxFontSizeMultiplier: true,
textCodeBlockStyle: true,
},
uiViewClassName: 'RCTVirtualText',
};
Expand Down
8 changes: 8 additions & 0 deletions Libraries/Text/TextProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
PressEvent,
TextLayoutEvent,
} from '../Types/CoreEventTypes';
import type {TextCodeBlockStyleProp} from './TextCodeBlockStyle';
import type {Node} from 'react';

export type PressRetentionOffset = $ReadOnly<{|
Expand Down Expand Up @@ -266,4 +267,11 @@ export type TextProps = $ReadOnly<{|
* See https://reactnative.dev/docs/text.html#linebreakstrategyios
*/
lineBreakStrategyIOS?: ?('none' | 'standard' | 'hangul-word' | 'push-out'),

/*
* Draws inline border around text with background, mostly used for inline code blocks.
*
* See https://github.com/Expensify/App/issues/4733
*/
textCodeBlockStyle?: ?TextCodeBlockStyleProp,
|}>;
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public class ViewProps {
public static final String ACCESSIBILITY_VALUE = "accessibilityValue";
public static final String ACCESSIBILITY_LABELLED_BY = "accessibilityLabelledBy";
public static final String IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility";
public static final String TEXT_CODE_BLOCK = "textCodeBlockStyle";

// DEPRECATED
public static final String ROTATION = "rotation";
Expand Down
Loading