-
-
Notifications
You must be signed in to change notification settings - Fork 1.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
[TreeView] Allow to define indentation at the item level #13126
[TreeView] Allow to define indentation at the item level #13126
Conversation
Deploy preview: https://deploy-preview-13126--material-ui-x.netlify.app/ |
52da560
to
3f108c8
Compare
3f108c8
to
8a444e4
Compare
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
@@ -194,6 +194,14 @@ RichTreeView.propTypes = { | |||
* Used when the item's expansion is controlled. | |||
*/ | |||
expandedItems: PropTypes.arrayOf(PropTypes.string), | |||
/** |
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.
Description copy pasted from the grid
import { TreeViewItemId } from '../../models'; | ||
|
||
export const TreeViewItemDepthContext = React.createContext< | ||
number | ((itemId: TreeViewItemId) => number) |
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.
SimpleTreeView
uses a number because the depth of an item is always the depth of its parent + 1
RichTreeView
uses a function to retrieve the depth from the state
Not sure how to improve this...
.filter((wrapRoot): wrapRoot is TreeRootWrapper => !!wrapRoot); | ||
.filter((wrapRoot): wrapRoot is TreeRootWrapper<any> => !!wrapRoot) | ||
// The wrappers are reversed to ensure that the first wrapper is the outermost one. | ||
.reverse(); |
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.
Otherwise the provider of useTreeViewItems
takes precedence over useTreeVIewJSXItems
which messes the behavior on SimpleTreeView
IMHO it make sense that if two plugins wrap the root (or the items), then it's the last executed plugin that has its wrappers deeper in the component tree and thus executed last.
@@ -148,21 +151,36 @@ export const useTreeItem2 = <TPlugins extends DefaultTreeViewPlugins = DefaultTr | |||
onBlur: createRootHandleBlur(externalEventHandlers), | |||
onKeyDown: createRootHandleKeyDown(externalEventHandlers), | |||
}; | |||
|
|||
if (indentationAtItemLevel) { |
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.
To avoid any breaking change, I'm only adding those props when the experimental feature is enabled.
When not using the experimental feature, the props passed to each slot remain unchanged.
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.
The solution seems to make sense. 👍
I'm not sure if we can make it better without a BC.
A couple of things to note:
- Do we want the items to always have the style defined regardless of the flag state?
style="--TreeView-itemDepth: 2;"
- It does not seem to work (be reflected) on the headless API.
packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.types.ts
Show resolved
Hide resolved
I only added it when the flag is enabled for |
This demo sets a custom Not sure how to handle that one... This is super specific to our doc though, in a real application, people are either using the flag or not using it, they will never try to create a Tree View that supports both. |
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.
LGTM with the following addressed:
I only added it when the flag is enabled for TreeItem2 but forgot the check on TreeItem, I'll fix it
P.S. It would be nice to have this feature/prop documented somehwere, but I take it that you plan to do it together with the drag&drop feature? 🤔
However, that feature is only for the pro package, whereas this prop impacts every TreeView component. 🤷
I was planning on doing a follow up with the |
Part of #9686
Usage
(same on
SimpleTreeView
andRichTreeViewPro
)Problem
When using the drag & drop, we need to have an element that goes from the far left of the Tree View (at the left border of an element of depth 0) to the far right.
But right now this is not easily doable because of how the padding are handled on the Tree View (it's the
groupTransition
slot which sets the padding to visually indent its children).Solution
One solution would be to switch to a strategy where each it content sets its
padding-left
depending on its depth.This PR proposes one potential implementation, opt-in via an
experimentalFeature
flag (similar to what the grid is doing for breaking changes during minor).This flag would be required in order for drag and drop to work (we can add a runtime warning in dev if not enabled, a warning on the JSDoc on the
itemsReordering
prop and a warning on the doc).In v8 we would drop this flag.
Limitation
Vertical border
It becomes harder to do vertical borders like on this example:
For me the best solution here would be to not use the flag for now, and in v8 to override the padding on the
content
slot and add a padding on thegroupTransition
slot (going back to the v7 default behavior).This would not be compatible with the drag & drop but I don't think this UI is meant to be used with drag & drop anyway.
Fragile with padding customization
The fact that the drag & drop relies on the position of the
content
slot can make it quite fragile, whatever padding position we have.We can lower the risk by creating a prop
indentPerLevel
(implemented in #12213) that allows people to change the indentation offset per level without touching the CSS and without doing things that would not be compatible with the drag&drop.