Skip to content

Commit 5ffc39f

Browse files
Saadnajmikessenmaclaude
authored
feat(fabric, text): support native text selection when selectable={true} (#2864)
Followup from #2845 ## Summary - Implements native text selection support for `<Text selectable={true}>` in the Fabric (new architecture) renderer - On **macOS**, swaps the content view to an `NSTextView` subclass that handles click-drag, double-click (word), and triple-click (line) selection, plus right-click context menus - On **iOS**, swaps to a `UITextView` subclass that leverages built-in gesture recognizers for long-press-to-select - The selectable text view is created lazily — only when `selectable={true}` — so there is zero overhead for non-selectable text (the common case) - Ungates `RCTTextLayoutManager.getTextStorageForAttributedString:` so both platforms can sync Fabric's attributed string into the native text view | iOS | macOS | | ----------- | ----------- | | <img width="506" height="960" alt="Screenshot 2026-03-20 at 11 30 06 PM" src="https://github.com/user-attachments/assets/ac7ce231-0969-4003-a3ca-55fae4e35515" /> | <img width="805" height="868" alt="Screenshot 2026-03-20 at 11 36 36 PM" src="https://github.com/user-attachments/assets/9cc0e951-8bff-41a5-92ff-1f3589ab7279" /> | ## Approach Rather than adding selection logic to the existing `RCTParagraphTextView`, this introduces a separate `RCTParagraphSelectableTextView` (platform-native text view) that is swapped in as the content view when the `selectable` prop is set. This keeps the non-selectable path untouched and avoids runtime cost for the default case. On macOS, mouse events are intercepted at the `RCTParagraphComponentView` level to distinguish single clicks (forwarded to JS for `onPress`) from drag/double-click/triple-click gestures (forwarded to the `NSTextView` for native selection). Touch cancellation walks the view hierarchy to toggle `RCTSurfaceTouchHandler` without modifying that class. On iOS, `UITextView` handles selection natively through its built-in gesture recognizers — no custom hit-testing needed. ## Test plan Add the following to RNTesterPlayground and verify on both macOS and iOS: ```jsx import {Alert, StyleSheet, Text, View} from 'react-native'; function Playground() { return ( <View style={styles.container}> <Text style={styles.heading}>Text Selection Test</Text> <Text style={styles.label}>Selectable text (try click-drag, double-click, right-click):</Text> <Text selectable={true} style={styles.selectableText}> This text should be selectable. Try clicking and dragging to select a range of text. Double-click to select a word. Triple-click to select a line. Right-click to see the context menu. </Text> <Text style={styles.label}>Non-selectable text (default):</Text> <Text style={styles.nonSelectableText}> This text should NOT be selectable. Clicking and dragging should not create a text selection. This is the default behavior. </Text> <Text style={styles.label}>Selectable with nested styles:</Text> <Text selectable={true} style={styles.selectableText}> This has <Text style={styles.bold}>bold text</Text> and{' '} <Text style={styles.italic}>italic text</Text> and{' '} <Text style={styles.colored}>colored text</Text> inside it. Selection should work across all styled ranges. </Text> <Text style={styles.label}>Selectable with onPress (should not conflict):</Text> <Text selectable={true} onPress={() => Alert.alert('Text pressed!')} style={styles.pressableText}> This text is both selectable and pressable. A single click should trigger onPress. Click-drag should start a selection instead. </Text> </View> ); } ``` - [ ] Verify `<Text selectable={true}>` enables click-drag selection on macOS - [ ] Verify double-click selects a word, triple-click selects a line on macOS - [ ] Verify right-click shows native context menu with Copy on macOS - [ ] Verify `<Text selectable={true}>` enables long-press selection on iOS - [ ] Verify non-selectable text (default) is unchanged on both platforms - [ ] Verify `onPress` still fires for single clicks on selectable text - [ ] Verify nested styled text renders correctly when selectable - [ ] Verify selection is cleared when text loses focus 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Kyle Essenmacher <15271436+kessenma@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 13835e9 commit 5ffc39f

File tree

3 files changed

+311
-13
lines changed

3 files changed

+311
-13
lines changed

0 commit comments

Comments
 (0)