Skip to content

Commit

Permalink
feat: shouldRenderChildren prop (#333)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasbach committed Mar 2, 2024
1 parent e83d48c commit ccfa7aa
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 4 deletions.
4 changes: 3 additions & 1 deletion next-release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

- Add `setDomFocus` argument to focus-item methods to provide an escape hatch to set the focus state of an item in RCT
without updating the DOM focus. This defaults to true in all existing methods to maintain the current behavior if
it is absent.
it is absent. (#336)
- Allow customizing when a subtree is rendered or not with the new `shouldRenderChildren` prop. This can be used to
create opening and closing animations on subtrees. (#333)

### Bug Fixes

Expand Down
64 changes: 64 additions & 0 deletions packages/core/src/stories/BasicExamples.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Tree } from '../tree/Tree';
import { StaticTreeDataProvider } from '../uncontrolledEnvironment/StaticTreeDataProvider';
import { UncontrolledTreeEnvironment } from '../uncontrolledEnvironment/UncontrolledTreeEnvironment';
import { buildTestTree } from '../../test/helpers';
import { TreeItemIndex } from '../types';

export default {
title: 'Core/Basic Examples',
Expand Down Expand Up @@ -556,3 +557,66 @@ export const NavigateAway = () => {
</UncontrolledTreeEnvironment>
);
};

export const AnimatedExpandingAndCollapsing = () => {
const [openingItem, setOpeningItem] = useState<TreeItemIndex | undefined>();
const [closingItem, setClosingItem] = useState<TreeItemIndex | undefined>();
return (
<UncontrolledTreeEnvironment<string>
dataProvider={
new StaticTreeDataProvider(longTree.items, (item, data) => ({
...item,
data,
}))
}
getItemTitle={item => item.data}
shouldRenderChildren={(item, context) =>
context.isExpanded ||
closingItem === item.index ||
openingItem === item.index
}
onExpandItem={item => {
setOpeningItem(item.index);
setTimeout(() => {
setOpeningItem(undefined);
});
}}
onCollapseItem={item => {
setClosingItem(item.index);
setTimeout(() => {
setClosingItem(undefined);
}, 500);
}}
viewState={{
'tree-1': {},
}}
renderItemsContainer={({ children, containerProps, parentId }) => (
<div
style={{
overflow: 'hidden',
}}
>
<ul
{...containerProps}
className="rct-tree-items-container"
style={{
transition: 'all 500ms',
maxHeight:
parentId === openingItem || parentId === closingItem
? 0
: '999999px',
marginTop:
parentId === closingItem || parentId === openingItem
? '-100%'
: '0',
}}
>
{children}
</ul>
</div>
)}
>
<Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
</UncontrolledTreeEnvironment>
);
};
6 changes: 5 additions & 1 deletion packages/core/src/treeItem/TreeItemElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ export const TreeItemElement = (props: {
return null as any;
}

const children = item.isFolder && isExpanded && item.children && (
const shouldRenderChildren =
environment.shouldRenderChildren?.(item, renderContext) ??
(item.isFolder && isExpanded);

const children = item.children && shouldRenderChildren && (
<TreeItemChildren depth={props.depth + 1} parentId={props.itemIndex}>
{item.children}
</TreeItemChildren>
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/treeItem/useTreeItemRenderContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { HTMLProps, useMemo } from 'react';
import { TreeItem, TreeItemActions, TreeItemRenderFlags } from '../types';
import {
TreeItem,
TreeItemActions,
TreeItemRenderContext,
TreeItemRenderFlags,
} from '../types';
import { defaultMatcher } from '../search/defaultMatcher';
import { useTree } from '../tree/Tree';
import { useTreeEnvironment } from '../controlledEnvironment/ControlledTreeEnvironment';
Expand Down Expand Up @@ -37,7 +42,7 @@ export const useTreeItemRenderContext = (item?: TreeItem) => {
item && environment.viewState[treeId]?.expandedItems?.includes(item.index);
const isRenaming = item && renamingItem === item.index;

return useMemo(() => {
return useMemo<TreeItemRenderContext | undefined>(() => {
if (!item) {
return undefined;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ export interface TreeCapabilities<T = any, C extends string = never> {
itemTitle: string
) => boolean;
showLiveDescription?: boolean;
shouldRenderChildren?: (
item: TreeItem<T>,
context: TreeItemRenderContext<C>
) => boolean;

/**
* See Issue #148 or the sample at
Expand Down

0 comments on commit ccfa7aa

Please sign in to comment.