feat: introduce BundleItem component and memoize sidebar groups for performance (#5312)
✨ (index.tsx): introduce MemoizedSidebarGroup component to improve performance by memoizing sorted bundles calculation and rendering 📝 (bundleItems/index.tsx): add BundleItem component to render individual bundle items in the sidebar with collapsible functionality
This commit is contained in:
parent
5aa87ee41e
commit
b3b5290598
3 changed files with 178 additions and 69 deletions
|
|
@ -0,0 +1,83 @@
|
|||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||
import {
|
||||
Disclosure,
|
||||
DisclosureContent,
|
||||
DisclosureTrigger,
|
||||
} from "@/components/ui/disclosure";
|
||||
import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
||||
import { memo } from "react";
|
||||
import SidebarItemsList from "../sidebarItemsList";
|
||||
|
||||
export const BundleItem = memo(
|
||||
({
|
||||
item,
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
dataFilter,
|
||||
nodeColors,
|
||||
chatInputAdded,
|
||||
onDragStart,
|
||||
sensitiveSort,
|
||||
handleKeyDownInput,
|
||||
}: {
|
||||
item: any;
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
dataFilter: any;
|
||||
nodeColors: any;
|
||||
chatInputAdded: any;
|
||||
onDragStart: any;
|
||||
sensitiveSort: any;
|
||||
handleKeyDownInput: any;
|
||||
}) => {
|
||||
if (
|
||||
!dataFilter[item.name] ||
|
||||
Object.keys(dataFilter[item.name]).length === 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Disclosure key={item.name} open={isOpen} onOpenChange={onOpenChange}>
|
||||
<SidebarMenuItem>
|
||||
<DisclosureTrigger className="group/collapsible">
|
||||
<SidebarMenuButton asChild>
|
||||
<div
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => handleKeyDownInput(e, item.name)}
|
||||
className="flex cursor-pointer items-center gap-2"
|
||||
data-testid={`disclosure-bundles-${item.display_name.toLowerCase()}`}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={item.icon}
|
||||
className="h-4 w-4 text-muted-foreground group-aria-expanded/collapsible:text-primary"
|
||||
/>
|
||||
<span className="flex-1 group-aria-expanded/collapsible:font-semibold">
|
||||
{item.display_name}
|
||||
</span>
|
||||
<ForwardedIconComponent
|
||||
name="ChevronRight"
|
||||
className="-mr-1 h-4 w-4 text-muted-foreground transition-all group-aria-expanded/collapsible:rotate-90"
|
||||
/>
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</DisclosureTrigger>
|
||||
<DisclosureContent>
|
||||
<SidebarItemsList
|
||||
item={item}
|
||||
dataFilter={dataFilter}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
/>
|
||||
</DisclosureContent>
|
||||
</SidebarMenuItem>
|
||||
</Disclosure>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BundleItem.displayName = "BundleItem";
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarMenu,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { memo, useMemo } from "react";
|
||||
import { BundleItem } from "../bundleItems";
|
||||
|
||||
export const MemoizedSidebarGroup = memo(
|
||||
({
|
||||
BUNDLES,
|
||||
search,
|
||||
sortedCategories,
|
||||
dataFilter,
|
||||
nodeColors,
|
||||
chatInputAdded,
|
||||
onDragStart,
|
||||
sensitiveSort,
|
||||
openCategories,
|
||||
setOpenCategories,
|
||||
handleKeyDownInput,
|
||||
}: {
|
||||
BUNDLES: any;
|
||||
search: any;
|
||||
sortedCategories: any;
|
||||
dataFilter: any;
|
||||
nodeColors: any;
|
||||
chatInputAdded: any;
|
||||
onDragStart: any;
|
||||
sensitiveSort: any;
|
||||
openCategories: any;
|
||||
setOpenCategories: any;
|
||||
handleKeyDownInput: any;
|
||||
}) => {
|
||||
// Memoize the sorted bundles calculation
|
||||
const sortedBundles = useMemo(() => {
|
||||
return BUNDLES.toSorted((a, b) => {
|
||||
const referenceArray = search !== "" ? sortedCategories : BUNDLES;
|
||||
return (
|
||||
referenceArray.findIndex((value) => value === a.name) -
|
||||
referenceArray.findIndex((value) => value === b.name)
|
||||
);
|
||||
});
|
||||
}, [BUNDLES, search, sortedCategories]);
|
||||
|
||||
return (
|
||||
<SidebarGroup className="p-3">
|
||||
<SidebarGroupLabel>Bundles</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{sortedBundles.map((item) => (
|
||||
<BundleItem
|
||||
key={item.name}
|
||||
item={item}
|
||||
isOpen={openCategories.includes(item.name)}
|
||||
onOpenChange={(isOpen) => {
|
||||
setOpenCategories((prev) =>
|
||||
isOpen
|
||||
? [...prev, item.name]
|
||||
: prev.filter((cat) => cat !== item.name),
|
||||
);
|
||||
}}
|
||||
dataFilter={dataFilter}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
handleKeyDownInput={handleKeyDownInput}
|
||||
/>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
MemoizedSidebarGroup.displayName = "MemoizedSidebarGroup";
|
||||
|
||||
export default MemoizedSidebarGroup;
|
||||
|
|
@ -36,6 +36,7 @@ import { APIClassType } from "../../../../types/api";
|
|||
import sensitiveSort from "../extraSidebarComponent/utils/sensitive-sort";
|
||||
import { CategoryGroup } from "./components/categoryGroup";
|
||||
import NoResultsMessage from "./components/emptySearchComponent";
|
||||
import MemoizedSidebarGroup from "./components/sidebarBundles";
|
||||
import SidebarMenuButtons from "./components/sidebarFooterButtons";
|
||||
import { SidebarHeaderComponent } from "./components/sidebarHeader";
|
||||
import SidebarItemsList from "./components/sidebarItemsList";
|
||||
|
|
@ -416,75 +417,19 @@ export function FlowSidebarComponent() {
|
|||
/>
|
||||
)}
|
||||
{hasBundleItems && (
|
||||
<SidebarGroup className="p-3">
|
||||
<SidebarGroupLabel>Bundles</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{BUNDLES.toSorted(
|
||||
(a, b) =>
|
||||
(search !== "" ? sortedCategories : BUNDLES).findIndex(
|
||||
(value) => value === a.name,
|
||||
) -
|
||||
(search !== "" ? sortedCategories : BUNDLES).findIndex(
|
||||
(value) => value === b.name,
|
||||
),
|
||||
).map(
|
||||
(item) =>
|
||||
dataFilter[item.name] &&
|
||||
Object.keys(dataFilter[item.name]).length > 0 && (
|
||||
<Disclosure
|
||||
key={item.name}
|
||||
open={openCategories.includes(item.name)}
|
||||
onOpenChange={(isOpen) => {
|
||||
setOpenCategories((prev) =>
|
||||
isOpen
|
||||
? [...prev, item.name]
|
||||
: prev.filter((cat) => cat !== item.name),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<SidebarMenuItem>
|
||||
<DisclosureTrigger className="group/collapsible">
|
||||
<SidebarMenuButton asChild>
|
||||
<div
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) =>
|
||||
handleKeyDownInput(e, item.name)
|
||||
}
|
||||
className="flex cursor-pointer items-center gap-2"
|
||||
data-testid={`disclosure-bundles-${item.display_name.toLocaleLowerCase()}`}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={item.icon}
|
||||
className="h-4 w-4 text-muted-foreground group-aria-expanded/collapsible:text-primary"
|
||||
/>
|
||||
<span className="flex-1 group-aria-expanded/collapsible:font-semibold">
|
||||
{item.display_name}
|
||||
</span>
|
||||
<ForwardedIconComponent
|
||||
name="ChevronRight"
|
||||
className="-mr-1 h-4 w-4 text-muted-foreground transition-all group-aria-expanded/collapsible:rotate-90"
|
||||
/>
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</DisclosureTrigger>
|
||||
<DisclosureContent>
|
||||
<SidebarItemsList
|
||||
item={item}
|
||||
dataFilter={dataFilter}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
/>
|
||||
</DisclosureContent>
|
||||
</SidebarMenuItem>
|
||||
</Disclosure>
|
||||
),
|
||||
)}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
<MemoizedSidebarGroup
|
||||
BUNDLES={BUNDLES}
|
||||
search={search}
|
||||
sortedCategories={sortedCategories}
|
||||
dataFilter={dataFilter}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
openCategories={openCategories}
|
||||
setOpenCategories={setOpenCategories}
|
||||
handleKeyDownInput={handleKeyDownInput}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue