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
20 changes: 17 additions & 3 deletions ios/EnrichedTextInputView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,14 @@ - (void)updateProps:(Props::Shared const &)props
}
}

if (newViewProps.htmlStyle.image.verticalAlign !=
oldViewProps.htmlStyle.image.verticalAlign) {
[newConfig setImageVerticalAlign:
[NSString fromCppString:newViewProps.htmlStyle.image
.verticalAlign]];
stylePropChanged = YES;
}

if (newViewProps.htmlStyle.a.textDecorationLine !=
oldViewProps.htmlStyle.a.textDecorationLine) {
NSString *objcString =
Expand Down Expand Up @@ -2320,9 +2328,15 @@ - (CGRect)frameForAttachment:(ImageAttachment *)attachment
font = [config primaryFont];
}

// Calculate (Baseline Alignment)
CGFloat targetY =
CGRectGetMaxY(lineRect) + font.descender - attachmentSize.height;
// Calculate vertical position based on config
CGFloat targetY;
if ([[config imageVerticalAlign] isEqualToString:@"bottom"]) {
targetY = CGRectGetMaxY(lineRect) - attachmentSize.height;
} else {
// baseline (default): align image bottom with the descender
targetY =
CGRectGetMaxY(lineRect) + font.descender - attachmentSize.height;
}
CGRect rect =
CGRectMake(glyphRect.origin.x + textView.textContainerInset.left,
targetY + textView.textContainerInset.top,
Expand Down
2 changes: 2 additions & 0 deletions ios/config/InputConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,6 @@
- (void)setCheckboxListBoxColor:(UIColor *)newValue;
- (UIImage *)checkboxCheckedImage;
- (UIImage *)checkboxUncheckedImage;
- (NSString *)imageVerticalAlign;
- (void)setImageVerticalAlign:(NSString *)newValue;
@end
10 changes: 10 additions & 0 deletions ios/config/InputConfig.mm
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ @implementation InputConfig {
UIColor *_checkboxListBoxColor;
UIImage *_checkboxCheckedImage;
UIImage *_checkboxUncheckedImage;
NSString *_imageVerticalAlign;
}

- (instancetype)init {
Expand Down Expand Up @@ -115,6 +116,7 @@ - (id)copyWithZone:(NSZone *)zone {
copy->_checkboxListBoxColor = [_checkboxListBoxColor copy];
copy->_checkboxCheckedImage = _checkboxCheckedImage;
copy->_checkboxUncheckedImage = _checkboxUncheckedImage;
copy->_imageVerticalAlign = [_imageVerticalAlign copy];
return copy;
}

Expand Down Expand Up @@ -661,4 +663,12 @@ - (UIImage *)generateCheckboxImage:(BOOL)isChecked {
return result;
}

- (NSString *)imageVerticalAlign {
return _imageVerticalAlign ?: @"baseline";
}

- (void)setImageVerticalAlign:(NSString *)newValue {
_imageVerticalAlign = newValue;
}

@end
23 changes: 18 additions & 5 deletions ios/interfaces/ImageAttachment.mm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "ImageAttachment.h"
#import "EnrichedTextInputView.h"
#import "ImageExtension.h"

// NSTextStorage frequently recreates NSTextAttachment objects during attribute
Expand Down Expand Up @@ -62,12 +63,24 @@ - (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer
return baseBounds;
}

// Extend the layout bounds below the baseline by the font's descender.
// Without this, a line containing only the attachment has no descender space
// below the baseline, but adding a text character introduces it — causing
// the line height to jump. By reserving descender space upfront the line
// height stays consistent regardless of whether text is present.
CGFloat descender = font.descender;

// Check vertical alignment config
NSString *verticalAlign = @"baseline";
if ([self.delegate isKindOfClass:[EnrichedTextInputView class]]) {
EnrichedTextInputView *inputView = (EnrichedTextInputView *)self.delegate;
verticalAlign = [inputView->config imageVerticalAlign];
}

if ([verticalAlign isEqualToString:@"bottom"]) {
// Align glyph to line fragment bottom — no descender offset on y,
// but include descender in height so the line fragment is tall enough.
return CGRectMake(baseBounds.origin.x, 0, baseBounds.size.width,
baseBounds.size.height);
}

// Default baseline: extend layout bounds below the baseline by the font's
// descender so the line height stays consistent.
return CGRectMake(baseBounds.origin.x, descender, baseBounds.size.width,
baseBounds.size.height - descender);
}
Expand Down
3 changes: 3 additions & 0 deletions src/spec/EnrichedTextInputNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ export interface HtmlStyleInternal {
marginLeft?: Float;
boxColor?: ColorValue;
};
image?: {
verticalAlign?: string;
};
}

export interface NativeProps extends ViewProps {
Expand Down
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ export interface HtmlStyle {
marginLeft?: number;
boxColor?: ColorValue;
};
image?: {
verticalAlign?: 'baseline' | 'bottom';
};
}

// Event types
Expand Down
3 changes: 3 additions & 0 deletions src/utils/normalizeHtmlStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ const defaultStyle: Required<HtmlStyle> = {
marginLeft: 16,
boxColor: 'blue',
},
image: {
verticalAlign: 'baseline',
},
};

const isMentionStyleRecord = (
Expand Down