Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,13 @@ class EnrichedTextInputViewManager :
view?.requestHTML(requestId)
}

override fun setTextAlignment(
view: EnrichedTextInputView?,
alignment: String,
) {
TODO("Not yet implemented")
}

override fun measure(
context: Context,
localData: ReadableMap?,
Expand Down
27 changes: 27 additions & 0 deletions apps/example/src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ const STYLE_ITEMS = [
name: 'checkbox-list',
icon: 'check-square-o',
},
{
name: 'align-left',
icon: 'align-left',
},
{
name: 'align-center',
icon: 'align-center',
},
{
name: 'align-right',
icon: 'align-right',
},
] as const;

type Item = (typeof STYLE_ITEMS)[number];
Expand Down Expand Up @@ -168,6 +180,15 @@ export const Toolbar: FC<ToolbarProps> = ({
case 'mention':
editorRef.current?.startMention('@');
break;
case 'align-left':
editorRef.current?.setTextAlignment('left');
break;
case 'align-center':
editorRef.current?.setTextAlignment('center');
break;
case 'align-right':
editorRef.current?.setTextAlignment('right');
break;
}
};

Expand Down Expand Up @@ -256,6 +277,12 @@ export const Toolbar: FC<ToolbarProps> = ({
return stylesState.mention.isActive;
case 'checkbox-list':
return stylesState.checkboxList.isActive;
case 'align-left':
return stylesState.alignment === 'left';
case 'align-center':
return stylesState.alignment === 'center';
case 'align-right':
return stylesState.alignment === 'right';
default:
return false;
}
Expand Down
1 change: 1 addition & 0 deletions apps/example/src/constants/editorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const DEFAULT_STYLES: StylesState = {
image: DEFAULT_STYLE_STATE,
mention: DEFAULT_STYLE_STATE,
checkboxList: DEFAULT_STYLE_STATE,
alignment: 'left',
};

export const DEFAULT_LINK_STATE = {
Expand Down
10 changes: 10 additions & 0 deletions apps/example/src/screens/DevScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,14 @@ const styles = StyleSheet.create({
height: 1000,
backgroundColor: 'rgb(0, 26, 114)',
},
alignmentLabel: {
marginTop: 20,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
color: 'rgb(0, 26, 114)',
},
alignmentButton: {
width: '25%',
},
});
12 changes: 12 additions & 0 deletions docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,14 @@ interface OnChangeStateEvent {
isConflicting: boolean;
isBlocking: boolean;
};
alignment: string;
}
```

- `isActive` indicates if the style is active within current selection.
- `isBlocking` indicates if the style is blocked by other currently active, meaning it can't be toggled.
- `isConflicting` indicates if the style is in conflict with other currently active styles, meaning toggling it will remove conflicting style.
- `alignment` indicates the current text alignment of the paragraph at the cursor position. Possible values: `'left'`, `'center'`, `'right'`, `'justify'`.

| Type | Platform |
|-------------------------------------------------------------|----------|
Expand Down Expand Up @@ -614,6 +616,16 @@ Sets the selection at the given indexes.
- `start: number` - starting index of the selection.
- `end: number` - first index after the selection's ending index. For just a cursor in place (no selection), `start` equals `end`.

### `.setTextAlignment()`

```ts
setTextAlignment: (alignment: 'left' | 'center' | 'right' | 'justify' | 'default') => void;
```

Sets text alignment for the paragraph(s) at the current selection. When inside a list, the alignment is applied to all contiguous list items.

- `alignment` - the desired text alignment. Use `'default'` to reset to the system natural alignment.

### `.startMention()`

```ts
Expand Down
18 changes: 17 additions & 1 deletion ios/EnrichedTextInputView.mm
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#import "EnrichedTextInputView.h"
#import "AlignmentUtils.h"
#import "CoreText/CoreText.h"
#import "ImageAttachment.h"
#import "LayoutManagerExtension.h"
#import "ParagraphAttributesUtils.h"
#import "ParagraphsUtils.h"
#import "RCTFabricComponentsPlugins.h"
#import "StringExtension.h"
#import "StyleHeaders.h"
Expand Down Expand Up @@ -51,6 +53,7 @@ @implementation EnrichedTextInputView {
BOOL _emitTextChange;
NSMutableDictionary<NSValue *, UIImageView *> *_attachmentViews;
NSArray<NSDictionary *> *_contextMenuItems;
NSString *_recentlyEmittedAlignment;
}

// MARK: - Component utils
Expand Down Expand Up @@ -89,6 +92,7 @@ - (void)setDefaults {
_blockedStyles = [[NSMutableSet alloc] init];
_recentlyActiveLinkRange = NSMakeRange(0, 0);
_recentlyActiveMentionRange = NSMakeRange(0, 0);
_recentlyEmittedAlignment = @"left";
recentlyChangedRange = NSMakeRange(0, 0);
_recentInputString = @"";
_recentlyEmittedHtml = @"<html>\n<p></p>\n</html>";
Expand Down Expand Up @@ -1121,12 +1125,20 @@ - (void)tryUpdatingActiveStyles {
}
}

// detect alignment change
NSString *currentAlignment =
[AlignmentUtils currentAlignmentStringForInput:self];
if (![currentAlignment isEqualToString:_recentlyEmittedAlignment]) {
updateNeeded = YES;
}

if (updateNeeded) {
auto emitter = [self getEventEmitter];
if (emitter != nullptr) {
// update activeStyles and blockedStyles only if emitter is available
_activeStyles = newActiveStyles;
_blockedStyles = newBlockedStyles;
_recentlyEmittedAlignment = currentAlignment;

emitter->onChangeState(
{.bold = GET_STYLE_STATE([BoldStyle getStyleType]),
Expand All @@ -1147,7 +1159,8 @@ - (void)tryUpdatingActiveStyles {
.blockQuote = GET_STYLE_STATE([BlockQuoteStyle getStyleType]),
.codeBlock = GET_STYLE_STATE([CodeBlockStyle getStyleType]),
.image = GET_STYLE_STATE([ImageStyle getStyleType]),
.checkboxList = GET_STYLE_STATE([CheckboxListStyle getStyleType])});
.checkboxList = GET_STYLE_STATE([CheckboxListStyle getStyleType]),
.alignment = [currentAlignment UTF8String]});
}
}

Expand Down Expand Up @@ -1282,6 +1295,9 @@ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
} else if ([commandName isEqualToString:@"requestHTML"]) {
NSInteger requestId = [((NSNumber *)args[0]) integerValue];
[self requestHTML:requestId];
} else if ([commandName isEqualToString:@"setTextAlignment"]) {
NSString *alignmentString = (NSString *)args[0];
[AlignmentUtils applyAlignmentFromString:alignmentString toInput:self];
}
}

Expand Down
25 changes: 16 additions & 9 deletions ios/extensions/LayoutManagerExtension.mm
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ - (void)drawLists:(EnrichedTextInputView *)typedInput
NSForegroundColorAttributeName :
[typedInput->config orderedListMarkerColor]
};
CGFloat indent = pStyle.firstLineHeadIndent;

NSArray *paragraphs = [ParagraphsUtils
getSeparateParagraphsRangesIn:typedInput->textView
Expand Down Expand Up @@ -308,18 +309,21 @@ - (void)drawLists:(EnrichedTextInputView *)typedInput
marker:marker
markerAttributes:markerAttributes
origin:origin
usedRect:textUsedRect];
usedRect:textUsedRect
indent:indent];
} else if (markerFormat ==
NSTextListMarkerDisc) {
[self drawBullet:typedInput
origin:origin
usedRect:textUsedRect];
usedRect:textUsedRect
indent:indent];
} else if ([markerFormat
hasPrefix:@"{checkbox"]) {
[self drawCheckbox:typedInput
markerFormat:markerFormat
origin:origin
usedRect:textUsedRect];
usedRect:textUsedRect
indent:indent];
}
// only first line of a list gets its
// marker drawn
Expand Down Expand Up @@ -387,7 +391,8 @@ - (CGRect)getTextAlignedUsedRect:(CGRect)usedRect font:(UIFont *)font {
- (void)drawCheckbox:(EnrichedTextInputView *)typedInput
markerFormat:(NSString *)markerFormat
origin:(CGPoint)origin
usedRect:(CGRect)usedRect {
usedRect:(CGRect)usedRect
indent:(CGFloat)indent {
BOOL isChecked = [markerFormat isEqualToString:@"{checkbox:1}"];

UIImage *image = isChecked ? typedInput->config.checkboxCheckedImage
Expand All @@ -396,18 +401,19 @@ - (void)drawCheckbox:(EnrichedTextInputView *)typedInput
CGFloat boxSize = [typedInput->config checkboxListBoxSize];

CGFloat centerY = CGRectGetMidY(usedRect) + origin.y;
CGFloat boxX = origin.x + usedRect.origin.x - gapWidth - boxSize;
CGFloat boxX = origin.x + indent - gapWidth - boxSize;
CGFloat boxY = centerY - boxSize / 2.0;

[image drawAtPoint:CGPointMake(boxX, boxY)];
}

- (void)drawBullet:(EnrichedTextInputView *)typedInput
origin:(CGPoint)origin
usedRect:(CGRect)usedRect {
usedRect:(CGRect)usedRect
indent:(CGFloat)indent {
CGFloat gapWidth = [typedInput->config unorderedListGapWidth];
CGFloat bulletSize = [typedInput->config unorderedListBulletSize];
CGFloat bulletX = origin.x + usedRect.origin.x - gapWidth - bulletSize / 2;
CGFloat bulletX = origin.x + indent - gapWidth - bulletSize / 2;
CGFloat centerY = CGRectGetMidY(usedRect) + origin.y;

CGContextRef context = UIGraphicsGetCurrentContext();
Expand All @@ -425,10 +431,11 @@ - (void)drawDecimal:(EnrichedTextInputView *)typedInput
marker:(NSString *)marker
markerAttributes:(NSDictionary *)markerAttributes
origin:(CGPoint)origin
usedRect:(CGRect)usedRect {
usedRect:(CGRect)usedRect
indent:(CGFloat)indent {
CGFloat gapWidth = [typedInput->config orderedListGapWidth];
CGSize markerSize = [marker sizeWithAttributes:markerAttributes];
CGFloat markerX = usedRect.origin.x - gapWidth - markerSize.width / 2;
CGFloat markerX = origin.x + indent - gapWidth - markerSize.width / 2;
CGFloat centerY = CGRectGetMidY(usedRect) + origin.y;
CGFloat markerY = centerY - markerSize.height / 2.0;

Expand Down
Loading
Loading