Look now.
A bold here.
Plain text with an .
Before after.
Before after.
See tiny
Other item
{value}
+
+ {JSON.stringify(lastImagePress)}
+
{JSON.stringify(lastLinkPress)}
diff --git a/apps/example/src/components/TextRenderer.tsx b/apps/example/src/components/TextRenderer.tsx
index 6fe302c46..9cf55afc7 100644
--- a/apps/example/src/components/TextRenderer.tsx
+++ b/apps/example/src/components/TextRenderer.tsx
@@ -3,6 +3,7 @@ import {
EnrichedText,
type OnLinkPressEvent,
type OnMentionPressEvent,
+ type OnImagePressEvent,
} from 'react-native-enriched-html';
import { enrichedTextHtmlStyle } from '../constants/editorConfig';
@@ -22,6 +23,13 @@ export const TextRenderer = ({ nodes }: TextRendererProps) => {
);
};
+ const handleImagePress = (e: OnImagePressEvent) => {
+ Alert.alert(
+ 'Image Pressed',
+ `You pressed the image: ${JSON.stringify(e.image)}`
+ );
+ };
+
if (nodes.length === 0) {
return null;
}
@@ -35,6 +43,7 @@ export const TextRenderer = ({ nodes }: TextRendererProps) => {
htmlStyle={enrichedTextHtmlStyle}
onLinkPress={handleLinkPress}
onMentionPress={handleMentionPress}
+ onImagePress={handleImagePress}
>
{node}
diff --git a/apps/example/src/screens/EnrichedTextScreen.tsx b/apps/example/src/screens/EnrichedTextScreen.tsx
index de482f1bb..93947ba3d 100644
--- a/apps/example/src/screens/EnrichedTextScreen.tsx
+++ b/apps/example/src/screens/EnrichedTextScreen.tsx
@@ -3,6 +3,7 @@ import { View, StyleSheet, ScrollView, Text } from 'react-native';
import {
EnrichedText,
type EnrichedTextProps,
+ type OnImagePressEvent,
type OnLinkPressEvent,
type OnMentionPressEvent,
} from 'react-native-enriched-html';
@@ -37,6 +38,10 @@ export function EnrichedTextScreen({ onSwitch }: EnrichedTextScreenProps) {
);
};
+ const handleImagePress = (e: OnImagePressEvent) => {
+ setHtml(`You pressed the image: ${JSON.stringify(e.image)}`);
+ };
+
return (
<>
Asdasd
\n\nAsdasd
\n\nAsdasda
"), "Asdasd
Asdasd
Asdasda
"); - EXPECT_EQ(GumboParser::normalizeHtml( - "\nAsdasd
\nAsdasd
\nAsdasda
\n"), - "Asdasd
Asdasd
Asdasda
"); + EXPECT_EQ( + GumboParser::normalizeHtml( + "\nAsdasd
\nAsdasd
\nAsdasda
\n"), + "Asdasd
Asdasd
Asdasda
"); EXPECT_EQ(GumboParser::normalizeHtml("Asdasd
Asdasd
"), "Asdasd
Asdasd
"); diff --git a/docs/TEXT_API_REFERENCE.md b/docs/TEXT_API_REFERENCE.md index af7f375df..7a456cecd 100644 --- a/docs/TEXT_API_REFERENCE.md +++ b/docs/TEXT_API_REFERENCE.md @@ -114,6 +114,27 @@ interface OnMentionPressEvent { | -------------------------------------- | ------------- | ----------------- | | `(event: OnMentionPressEvent) => void` | - | iOS, Android, Web | +### `onImagePress` + +Called when the user presses an inline image. Receives an `OnImagePressEvent` with the image uri and dimensions. + +```ts +interface OnImagePressEvent { + image: { + uri: string; + width: number; + height: number; + }; +} +``` + +| Type | Default Value | Platform | +| ------------------------------------ | ------------- | ----------------- | +| `(event: OnImagePressEvent) => void` | - | iOS, Android, Web | + +> [!NOTE] +> No visual feedback is applied on press. + ## EnrichedTextHtmlStyle type Extends [`HtmlStyle`](API_REFERENCE.md#htmlstyle-type) with additional press-state styling for interactive elements. All properties from `HtmlStyle` are supported except `a` and `mention`, which are replaced by the extended versions below. diff --git a/docs/WEB.md b/docs/WEB.md index 382d26f48..1ef1a8177 100644 --- a/docs/WEB.md +++ b/docs/WEB.md @@ -41,7 +41,7 @@ See [Web Keyboard Shortcuts](./INPUT_API_REFERENCE.md#web-keyboard-shortcuts) fo - Customizing the styling using props: `style`, `htmlStyle`, `selectionColor`. - `selectable` prop - `useHtmlNormalizer` -- `onLinkPress` and `onMentionPress` callbacks +- `onLinkPress`, `onMentionPress` and `onImagePress` callbacks ### Unsupported diff --git a/ios/EnrichedTextView.h b/ios/EnrichedTextView.h index e0af096f7..cd5d47243 100644 --- a/ios/EnrichedTextView.h +++ b/ios/EnrichedTextView.h @@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN - (CGSize)measureSize:(CGFloat)maxWidth; - (void)emitOnLinkPressEvent:(NSString *)url; - (void)emitOnMentionPressEvent:(MentionParams *)mention; +- (void)emitOnImagePressEvent:(MediaAttachment *)attachment; @end NS_ASSUME_NONNULL_END diff --git a/ios/EnrichedTextView.mm b/ios/EnrichedTextView.mm index 16ef47083..75e7ded9a 100644 --- a/ios/EnrichedTextView.mm +++ b/ios/EnrichedTextView.mm @@ -785,6 +785,21 @@ - (void)emitOnMentionPressEvent:(MentionParams *)mention { } } +- (void)emitOnImagePressEvent:(MediaAttachment *)attachment { + if (!attachment) + return; + auto emitter = [self getEventEmitter]; + if (emitter != nullptr) { + emitter->onImagePress( + {.image = { + .uri = + attachment.uri ? [attachment.uri toCppString] : std::string{}, + .width = attachment.width, + .height = attachment.height, + }}); + } +} + // MARK: - Media attachments delegate - (void)mediaAttachmentDidUpdate:(MediaAttachment *)attachment { diff --git a/ios/utils/EnrichedTextTouchHandler.mm b/ios/utils/EnrichedTextTouchHandler.mm index 255daef6d..48665320c 100644 --- a/ios/utils/EnrichedTextTouchHandler.mm +++ b/ios/utils/EnrichedTextTouchHandler.mm @@ -1,6 +1,7 @@ #import "EnrichedTextTouchHandler.h" #import "ColorExtension.h" #import "EnrichedTextView.h" +#import "ImageAttachment.h" #import "LinkData.h" #import "MentionParams.h" #import "MentionStyleProps.h" @@ -78,8 +79,10 @@ - (id)findClickableAt:(NSUInteger)idx if (idx == NSNotFound || idx >= self.view.textView.textStorage.length) return nil; - NSArray *keys = - @[ @"EnrichedManualLink", @"EnrichedAutomaticLink", @"EnrichedMention" ]; + NSArray *keys = @[ + @"EnrichedManualLink", @"EnrichedAutomaticLink", @"EnrichedMention", + @"EnrichedImage" + ]; for (NSString *k in keys) { id val = [self.view.textView.textStorage attribute:k atIndex:idx @@ -93,6 +96,10 @@ - (id)findClickableAt:(NSUInteger)idx } - (void)updateVisualsPressed:(BOOL)pressed { + if ([_activeAttrKey isEqualToString:@"EnrichedImage"]) { + return; + } + if (pressed) { UIColor *color = nil; UIColor *bgColor = nil; @@ -141,6 +148,15 @@ - (void)dispatchEvent { [self.view emitOnLinkPressEvent:((LinkData *)_activeValue).url]; } else if ([_activeAttrKey isEqualToString:@"EnrichedMention"]) { [self.view emitOnMentionPressEvent:(MentionParams *)_activeValue]; + } else if ([_activeAttrKey isEqualToString:@"EnrichedImage"]) { + NSRange attachmentRange = _activeRange; + ImageAttachment *attachment = + [self.view.textView.textStorage attribute:NSAttachmentAttributeName + atIndex:attachmentRange.location + effectiveRange:NULL]; + if ([attachment isKindOfClass:[ImageAttachment class]]) { + [self.view emitOnImagePressEvent:attachment]; + } } } diff --git a/src/index.native.tsx b/src/index.native.tsx index 82d450b16..8f7473c7a 100644 --- a/src/index.native.tsx +++ b/src/index.native.tsx @@ -31,4 +31,5 @@ export type { EnrichedTextHtmlStyle, OnMentionPressEvent, OnLinkPressEvent, + OnImagePressEvent, } from './types'; diff --git a/src/index.tsx b/src/index.tsx index 02d34b743..6ccbe4fa1 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,4 +23,5 @@ export type { EnrichedTextHtmlStyle, OnMentionPressEvent, OnLinkPressEvent, + OnImagePressEvent, } from './types'; diff --git a/src/native/EnrichedText.tsx b/src/native/EnrichedText.tsx index f2a9f952c..2a1ba0198 100644 --- a/src/native/EnrichedText.tsx +++ b/src/native/EnrichedText.tsx @@ -16,6 +16,7 @@ import EnrichedTextNativeComponent, { type NativeProps, type OnLinkPressEvent, type OnMentionPressEventInternal, + type OnImagePressEvent, } from '../spec/EnrichedTextNativeComponent'; import { nullthrows } from '../utils/nullthrows'; import { normalizeEnrichedTextHtmlStyle } from '../utils/normalizeHtmlStyle'; @@ -37,6 +38,7 @@ export const EnrichedText = ({ allowFontScaling = true, onLinkPress: _onLinkPress, onMentionPress: _onMentionPress, + onImagePress: _onImagePress, ...rest }: EnrichedTextProps) => { const nativeRef = useRef