-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for display: contents
#46584
Add support for display: contents
#46584
Conversation
...onents/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for all of the perseverance here 😅. Still trying to wrap my head around all the changes here.
The bookkeeping here around mutation and adding/removing the prop seems to add more a complexity than I had hoped. Do you think this would be any simpler if we went the route of properly teaching Yoga about display: contents
? RN would still need to have knowledge of it, but we get to avoid some of the tricky scenarios here (but need to teach Yoga during layout traversal).
...t/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp
Outdated
Show resolved
Hide resolved
I only have a high-level idea of how Yoga works but I imagine the approach would be relatively similar - instead of removing the nodes from the Yoga tree like in this PR, we would need to skip them during layout. Though, it's hard to say where it fits on the |
Seems like the approach with setting
|
That was indeed a local issue.
That was caused by a few checks still using |
Hey @NickGerleman, has there been any progress on this by chance? |
1bdc2a6
to
13de3ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the bits building on the Yoga change look good to me! I will pull a couple bits of this into that change, because we will get exhaustive switch/case errors without (and some toolchain doesn't classify the iterator as an input_iterator? Need to check what's going on for that one...).
Should be able to merge the Yoga change soon, now that facebook/yoga@5687182 landed.
@NickGerleman has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
if (yogaNode_.style().display() == yoga::Display::Contents) { | ||
ShadowNode::traits_.set(ShadowNodeTraits::ForceFlattenView); | ||
} else { | ||
ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we fold this into updateYogaProps
instead to make lifecycle clearer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in cf58940
Summary: This PR adds support for `display: contents` style by effectively skipping nodes with `display: contents` set during layout. This required changes in the logic related to children traversal - before this PR a node would be always laid out in the context of its direct parent. After this PR that assumption is no longer true - `display: contents` allows nodes to be skipped, i.e.: ```html <div id="node1"> <div id="node2" style="display: contents;"> <div id="node3" /> </div> </div> ``` `node3` will be laid out as if it were a child of `node1`. Because of this, iterating over direct children of a node is no longer correct to achieve the correct layout. This PR introduces `LayoutableChildren::Iterator` which can traverse the subtree of a given node in a way that nodes with `display: contents` are replaced with their concrete children. A tree like this: ```mermaid flowchart TD A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) A --> B A --> C B --> D B --> E C --> F D --> G F --> H G --> I H --> J style B fill:facebook/yoga#50 style C fill:facebook/yoga#50 style D fill:facebook/yoga#50 style H fill:facebook/yoga#50 style I fill:facebook/yoga#50 ``` would be laid out as if the green nodes (ones with `display: contents`) did not exist. It also changes the logic where children were accessed by index to use the iterator instead as random access would be non-trivial to implement and it's not really necessary - the iteration was always sequential and indices were only used as boundaries. There's one place where knowledge of layoutable children is required to calculate the gap. An optimization for this is for a node to keep a counter of how many `display: contents` nodes are its children. If there are none, a short path of just returning the size of the children vector can be taken, otherwise it needs to iterate over layoutable children and count them, since the structure may be complex. One more major change this PR introduces is `cleanupContentsNodesRecursively`. Since nodes with `display: contents` would be entirely skipped during the layout pass, they would keep previous metrics, would be kept as dirty, and, in the case of nested `contents` nodes, would not be cloned, breaking `doesOwn` relation. All of this is handled in the new method which clones `contents` nodes recursively, sets empty layout, and marks them as clean and having a new layout so that it can be used on the React Native side. Relies on facebook/yoga#1725 Changelog: [Internal] X-link: facebook/yoga#1726 Test Plan: Added tests for `display: contents` based on existing tests for `display: none` and ensured that all the tests were passing. Differential Revision: D64404340 Pulled By: NickGerleman
Would you mind rebasing on #47035 again? That change now has:
|
13de3ae
to
cf58940
Compare
Error ReferenceError
Dangerfile
|
@NickGerleman has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@NickGerleman merged this pull request in e7a3f47. |
Summary
This PR adds support for
display: contents
style by effectively skipping nodes withdisplay: contents
set during layout.This required changes in the logic related to children traversal - before this PR a node would be always laid out in the context of its direct parent. After this PR that assumption is no longer true -
display: contents
allows nodes to be skipped, i.e.:node3
will be laid out as if it were a child ofnode1
.Because of this, iterating over direct children of a node is no longer correct to achieve the correct layout. This PR introduces
LayoutableChildren::Iterator
which can traverse the subtree of a given node in a way that nodes withdisplay: contents
are replaced with their concrete children.A tree like this:
would be laid out as if the green nodes (ones with
display: contents
) did not exist. It also changes the logic where children were accessed by index to use the iterator instead as random access would be non-trivial to implement and it's not really necessary - the iteration was always sequential and indices were only used as boundaries.There's one place where knowledge of layoutable children is required to calculate the gap. An optimization for this is for a node to keep a counter of how many
display: contents
nodes are its children. If there are none, a short path of just returning the size of the children vector can be taken, otherwise it needs to iterate over layoutable children and count them, since the structure may be complex.One more major change this PR introduces is
cleanupContentsNodesRecursively
. Since nodes withdisplay: contents
would be entirely skipped during the layout pass, they would keep previous metrics, would be kept as dirty, and, in the case of nestedcontents
nodes, would not be cloned, breakingdoesOwn
relation. All of this is handled in the new method which clonescontents
nodes recursively, sets empty layout, and marks them as clean and having a new layout so that it can be used on the React Native side.Relies on facebook/yoga#1725
X-link: facebook/yoga#1726
This PR adds a few things over the corresponding one in Yoga:
DisplayType
enum -Contents
ShadowNodeTrait
-ForceFlattenView
, that forces the node not to form a host viewdisplay: contents
display: contents
todisplay: none
on the old architectureChangelog:
[GENERAL] [ADDED] - Added support for
display: contents
Test Plan:
So far I've been testing on relatively simple snippets like this one and on entirety of RNTester by inserting views with `display: contents` in random places and seeing if anything breaks.