-
Couldn't load subscription status.
- Fork 24.9k
Add support for display: contents
#46584
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
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 ReferenceErrorDangerfile |
|
@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: contentsstyle by effectively skipping nodes withdisplay: contentsset 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: contentsallows nodes to be skipped, i.e.:node3will 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::Iteratorwhich can traverse the subtree of a given node in a way that nodes withdisplay: contentsare replaced with their concrete children.A tree like this:
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:#050 style C fill:#050 style D fill:#050 style H fill:#050 style I fill:#050would 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: contentsnodes 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: contentswould be entirely skipped during the layout pass, they would keep previous metrics, would be kept as dirty, and, in the case of nestedcontentsnodes, would not be cloned, breakingdoesOwnrelation. All of this is handled in the new method which clonescontentsnodes 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:
DisplayTypeenum -ContentsShadowNodeTrait-ForceFlattenView, that forces the node not to form a host viewdisplay: contentsdisplay: contentstodisplay: noneon the old architectureChangelog:
[GENERAL] [ADDED] - Added support for
display: contentsTest 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.