Skip to content

feat: on link press for input#547

Open
eszlamczyk wants to merge 6 commits into
mainfrom
feat/381/input-on-link-press
Open

feat: on link press for input#547
eszlamczyk wants to merge 6 commits into
mainfrom
feat/381/input-on-link-press

Conversation

@eszlamczyk

@eszlamczyk eszlamczyk commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

What/Why?

Closes #381

Adds an onLinkPress callback to EnrichedMarkdownTextInput, making links pressable while the input is not focused - the notes-app model: reading opens links, editing edits them.

Providing the callback gates the whole behavior - no extra prop needed:

  • Not focused — tapping a link fires onLinkPress({ url }) and consumes the tap: the input does not focus and the keyboard does not open. Tapping anywhere else focuses as usual.
  • Focused — taps behave natively (cursor placement, selection); links are inert.
  • No callback — behavior identical to today: every tap focuses the input.

Fires for manual links, mentions, and auto-detected links (store + transient detector ranges).

Always-pressable (Apple Notes-style) was considered and rejected: while editing, a tap means "place the cursor here", and hijacking some taps to open URLs mid-edit fights the platform text-editing gestures. Unfocused-only gives each mode one unambiguous tap meaning.

Implementation notes:

  • JS - internal isOnLinkPressSet prop (same pattern as isOnChangeMarkdownSet): under Fabric, event handlers never reach native as props, so native needs the flag to know it should consume taps rather than just emit.
  • iOS - UITextInteraction's internal recognizers can't be reliably beaten with gesture failure requirements, and they delay raw touch delivery (touchesBegan never fires for a focusing tap). Instead the touch-down is tracked in hitTest:withEvent: (the one synchronous step UIKit can't skip) and the press is resolved by vetoing textViewShouldBeginEditing: - guaranteed to keep the keyboard closed. Long-press/drag
    guards (0.5 s timeout) keep selection gestures working. macOS: handled in mouseDown: before first-responder assignment.
  • Android - tap candidate tracked in onTouchEvent (slop + long-press timeout); on UP over a link, super gets ACTION_CANCEL instead of the UP, so pressed state and pending focus/click callbacks are cleared. Hit-testing reuses the bounds-checked offset logic from fix(android): prevent false link hit detection for taps outside text bounds #523, extracted into a shared charOffsetAt (TextTapUtils.kt).
  • iOS also gains ENRMCharacterIndexAtPointStrict — a bounds-checked hit test mirroring the Android fix(android): prevent false link hit detection for taps outside text bounds #523 fix, so taps past a line's end don't false-positive on the nearest link.

Testing

In the example app Playground, set content with a link (e.g. via Set Raw Markdown: [link text](https://reactnative.dev)), keep the input unfocused and tap the link → alert with the URL, input stays unfocused. Tap plain text → input focuses. While focused, tap the link -> caret placement, no event. Blur and tap the link again -> event fires.

Also added a new story to the storybook

Screenshots

iOS Android

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android
  • Updated documentation/README if applicable
  • Ran example app to verify changes
  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an onLinkPress callback to EnrichedMarkdownTextInput so links are pressable when the input is not focused (notes-app style), while preserving native editing behavior when focused. This introduces a small cross-platform touch/hit-testing layer to detect link taps and (when applicable) consume them without focusing the input.

Changes:

  • JS/TS: add onLinkPress API + internal isOnLinkPressSet gating prop for Fabric.
  • iOS/macOS: track touch-down in hitTest and veto textViewShouldBeginEditing: (macOS via mouseDown:) to emit onLinkPress without focusing.
  • Android: add shared charOffsetAt utility and consume link taps on ACTION_UP (send ACTION_CANCEL to super) to prevent focus/keyboard.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react-native-enriched-markdown/src/index.tsx Re-exports OnLinkPressEvent from the input API.
packages/react-native-enriched-markdown/src/EnrichedMarkdownTextInputNativeComponent.ts Adds codegen event type + isOnLinkPressSet native prop + onLinkPress event.
packages/react-native-enriched-markdown/src/EnrichedMarkdownTextInput.tsx Adds onLinkPress prop wiring and isOnLinkPressSet flag propagation.
packages/react-native-enriched-markdown/ios/utils/ENRMTextHitTest.m Adds strict hit-testing to avoid false positives past line bounds.
packages/react-native-enriched-markdown/ios/utils/ENRMTextHitTest.h Exposes ENRMCharacterIndexAtPointStrict declaration.
packages/react-native-enriched-markdown/ios/input/ENRMInputTextView.mm Tracks touch-down in hitTest for unfocused link press resolution.
packages/react-native-enriched-markdown/ios/input/EnrichedMarkdownTextInput+Internal.h Adds internal hooks for touch/mouse link handling.
packages/react-native-enriched-markdown/ios/input/EnrichedMarkdownTextInput.mm Implements unfocused link press detection and event emission.
packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/TextTapUtils.kt New shared charOffsetAt helper with bounds checks.
packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt Uses shared charOffsetAt instead of local duplicate logic.
packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt Adds emitter method to dispatch onLinkPress.
packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnLinkPressEvent.kt New RN event class for link presses.
packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt Implements unfocused link-tap candidate tracking and consumption.
packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt Exports onLinkPress event name and isOnLinkPressSet prop.
docs/INPUT.md Documents unfocused link pressing behavior and usage example.
docs/API_REFERENCE.md Adds onLinkPress API reference entry.
apps/react-native-example/src/screens/playground/PlaygroundScreen.tsx Adds example onLinkPress usage (Alert).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/react-native-enriched-markdown/ios/input/ENRMInputTextView.mm Outdated
@eszlamczyk
eszlamczyk marked this pull request as ready for review July 17, 2026 10:26
@eszlamczyk
eszlamczyk requested a review from hryhoriiK97 July 17, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Support onLinkPress in input component

2 participants