-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: add support for tree sections #9013
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
base: main
Are you sure you want to change the base?
Conversation
Build successful! 🎉 |
Build successful! 🎉 |
Build successful! 🎉 |
Build successful! 🎉 |
if (parent) { | ||
// siblings must exist because our original node exists | ||
let siblings = state.collection.getChildren?.(parent.key)!; | ||
let siblings = getDirectChildren(parent as CollectionNode<T>, state.collection as Collection<CollectionNode<T>>); |
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.
we used to use the getChildren
method from TreeCollection
but now that we've changed it to get ALL the descendants of a parent node instead of its immediate descendants, we can't use that anymore. i decided to make a new function called getDirectChildren
which is essentially the same code as what getChildren
was before
let {flattenedRows, keyMap, itemCount} = flattenTree<T>(collection, {expandedKeys}); | ||
this.flattenedRows = flattenedRows; | ||
let {collection, lastExpandedKeys, expandedKeys} = opts; | ||
let {keyMap, itemCount} = flattenTree<T>(collection, {expandedKeys}); |
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.
i've removed flattenedRows
from TreeCollection since we've moved away from the flattened structure but i didn't feel entirely comfortable removing flattenTree
entirely since we use it to build the keyMap and modify the nodes (like the levels and index). but if we do wanna rework it, im happy to do it in a follow-up unless we'd like the changes in this PR
return this.keyMap.get(key) || null; | ||
} | ||
|
||
at(idx: 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.
obviously, this was a lot easier when we had flattenedRows because we could just index it but now we don't have a nice array to index. getKeyAfter
will get the next visible row from the top and we just iterate however many times idx
is
if you're wondering why we don't just use getKeyAfter
that's already built into TreeCollection...continue reading...
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.
Do we actually use the at
method anywhere? In BaseCollection we throw an error that it isn't supported. Maybe we don't need this?
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.
no it doesn't look like we use it anywhere and it's not defined in any other collection either so we can probably just remove it
let [expandedKeys, setExpandedKeys] = useControlledState( | ||
propExpandedKeys ? convertExpanded(propExpandedKeys) : undefined, | ||
propDefaultExpandedKeys ? convertExpanded(propDefaultExpandedKeys) : new Set(), | ||
propExpandedKeys ? new Set(propExpandedKeys) : undefined, |
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.
ended up getting rid of convertExpanded
because i was running into a type error where expandedKeys could either be a Set
or string
but since propExpandedKeys/propDefaultExpandedKeys are type Iterable<Key>
and cannot be set to all
, there wasn't any way expandedKeys would ever be a string
} | ||
|
||
if ((this.expandedKeys.has(node.key) || node.type !== 'item') && node.firstChildKey != null) { | ||
return node.firstChildKey; |
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.
should getKeyAfter
include content nodes or should it skip them?
in this case, if the node is included in expandedKeys, we'll look at its firstChildKey which will be a TreeItemContent node. however, this caused issues in DropTargetKeyboardNavigation so we check if the node is a content node and if it is, we check its nextKey. however, we could deal with that here but depends on what we want to do
Build successful! 🎉 |
## API Changes
react-aria-components/react-aria-components:GridListSectionProps-GridListSectionProps <T> {
- aria-label?: string
- children?: ReactNode | (T) => ReactElement
- className?: string = 'react-aria-GridListSection'
- dependencies?: ReadonlyArray<any>
- id?: Key
- items?: Iterable<T>
- style?: CSSProperties
- value?: T
-} /react-aria-components:TreeHeader+TreeHeader {
+ UNTYPED
+} /react-aria-components:TreeSection+TreeSection <T extends {}> {
+ aria-label?: string
+ children?: ReactNode | ({}) => ReactElement
+ className?: string
+ dependencies?: ReadonlyArray<any>
+ id?: Key
+ items?: Iterable<{}>
+ style?: CSSProperties
+ value?: {}
+} |
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.
Let's discuss what we want the division of responsibilties to be between collections/keyboard delegates/drop delegates/etc.
} | ||
|
||
function getDirectChildren<T>(parent: CollectionNode<T>, collection: Collection<CollectionNode<T>>) { | ||
let node = parent?.firstChildKey != null ? collection.getItem(parent.firstChildKey) : null; |
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.
I think this only works with new collections (that's why it required a type cast above)? Maybe ok because we currently only implement Tree in RAC, not directly via hooks, but if someone did that this wouldn't work.
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.
oh true. i guess i could maybe do some type guarding and have some different logic if it's Node and not a CollectionNode? kinda unfortunate that we can't use getChildren here anymore tho
return this.keyMap.get(key) || null; | ||
} | ||
|
||
at(idx: 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.
Do we actually use the at
method anywhere? In BaseCollection we throw an error that it isn't supported. Maybe we don't need this?
return null; | ||
} | ||
|
||
// Skip over any nodes that aren't an item node (e.g. section or header node) |
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.
I wonder if this should be the job of the collection or the keyboard delegate? In BaseCollection we don't skip any nodes here. I kinda think collections should traverse everything and everything downstream (keyboard delegate, dnd, etc.) should skip over things it doesn't care about.
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.
I agree with this sentiment
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.
yeah i think that would make sense. daniel and i kinda talked about that regarding getKeyAfter
. either we could skip the content node inside of getKeyAfter
or handle it inside of dnd. we ended up doing the latter but i can see applying that logic elsewhere
let expandedKeys = this.expandedKeys; | ||
return { | ||
*[Symbol.iterator]() { | ||
function* traverseDepthFirst(node: CollectionNode<T> | null, expandedKeys: Set<Key>) { |
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.
In theory, this should be equivalent to starting at key
and traversing by iterating through getKeyAfter
until it returns null. Wonder if we could implement it that way and avoid duplicating the logic here? Same could probably be done for the iterator over the whole collection above (but starting at getFirstKey()
).
Closes
✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project: