feat(input): cross-platform contentInset prop (iOS textContainerInset + Android scroll container) - #442
feat(input): cross-platform contentInset prop (iOS textContainerInset + Android scroll container)#442andreavrr wants to merge 4 commits into
Conversation
Before / after
|
hryhoriiK97
left a comment
There was a problem hiding this comment.
@andreavrr thank you for this PR! It will require more consideration. I will look into this next week and let you know how we want to handle it.
|
Thanks a lot @hryhoriiK97! I figured, there's probably still quite a bit to sort out. Either way it feels like an important feature (well, more of a fix) on the UI side. Thanks especially for giving me a sense of the timeline, happy to make any changes whenever you get to it. |
|
Hi @hryhoriiK97, any updates on this? |
@andreavrr sorry for the late reply! After looking into this, we have a few concerns. The contentInset name is an iOS-only ScrollView concept - Android has no equivalent. And on iOS, RN's TextInput already maps padding style to textContainerInset under the hood, so this essentially duplicates existing behavior under a new name. The actual issue here is that Android's EditText clips padding during scroll (react/react-native#27623). This is a known platform limitation - even an attempt to bring contentInset to RN's Android ScrollView (react/react-native#49145) was closed due to bugs. Wrapping the EditText in a NestedScrollView works, but it changes the view hierarchy in ways that can cause edge cases with caret reveal, selection handles, Fabric layout, etc. We won't be merging this as-is, but we'll keep the issue open. If we tackle it later, we'd rather fix padding to just work on Android than add a new prop. That said, if you have any other ideas or a different approach in mind, we're definitely open to hearing them! |
Closes #426
Summary
Adds a cross-platform
contentInsetprop toEnrichedMarkdownTextInput: a content cushion that is part of the scrollable content and scrolls away with the text, instead of clipping the way style padding does today.toppushes the first line down (e.g. below a translucent header);bottomkeeps room past the last line. It exposes the iOSUITextView.textContainerInsetbehaviour and brings the same to Android.Problem
There is no prop for a content cushion that scrolls away with the text. The only workaround is style padding, and on both iOS and Android
paddingVerticalon the editor is clipped while the field scrolls (the cushion eats the text), and a long autofocused value does not reveal the end. iOS hasUITextView.textContainerInsetfor exactly this, but it is not exposed; Android has no equivalent.Minimal reproducible example (Expo SDK 56, RN 0.85.3, New Architecture):
https://github.com/andreavrr/enriched-markdown-content-inset-mre
API
Fix
contentInsettoUITextView.textContainerInset, and grow the caret-scroll target rect by the bottom inset so the caret stays revealed while typing.NestedScrollViewcontainer whose child is the editor (wrap_content, non-scrolling). The inset is the editor's own padding — part of the scrolled content — so it scrolls away without clipping (AndroidTextViewcouples its draw clip to the padding while the field scrolls internally). The container scrolls and reveals the caret. Border/background/padding go on the container (the visible frame). A manual measure/layout pass andclipToOutlinecover what RN does not here (it swallows the non-Fabric child'srequestLayout, and RN parents default tooverflow: visible). The shadow node stays an auto-grow measuring leaf, so consumers without an explicit height still grow to content.Test plan
contentInsetwith a long autofocused value: the cushion scrolls away (no clip) andautoFocusreveals the end.maxHeightconsumers still size correctly.scrollEnabled, selection menus, etc.).