refactor: Reduce Sidebar Rerenders (#7699)
* menubar and hook optimizations
* sidebar optimizations
* [autofix.ci] apply automated fixes
* cleanup
* [autofix.ci] apply automated fixes
* sidebare button optimization
* 🐛 (typescript_test.yml): increase the maximum shard count to 40 to improve test distribution and performance
* Get state on focus instead of on keypress
* revert test shard change
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
parent
a04e5f56b8
commit
710b6f99b5
12 changed files with 208 additions and 186 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||||
import useAddFlow from "@/hooks/flows/use-add-flow";
|
import useAddFlow from "@/hooks/flows/use-add-flow";
|
||||||
|
|
@ -35,7 +35,7 @@ import { cn, getNumberFromString } from "@/utils/utils";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { useShallow } from "zustand/react/shallow";
|
import { useShallow } from "zustand/react/shallow";
|
||||||
|
|
||||||
export const MenuBar = ({}: {}): JSX.Element => {
|
export const MenuBar = memo((): JSX.Element => {
|
||||||
const shortcuts = useShortcutsStore((state) => state.shortcuts);
|
const shortcuts = useShortcutsStore((state) => state.shortcuts);
|
||||||
const addFlow = useAddFlow();
|
const addFlow = useAddFlow();
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
|
|
@ -81,20 +81,10 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const [inputWidth, setInputWidth] = useState<number>(0);
|
const [inputWidth, setInputWidth] = useState<number>(0);
|
||||||
const measureRef = useRef<HTMLSpanElement>(null);
|
const measureRef = useRef<HTMLSpanElement>(null);
|
||||||
const changesNotSaved = useUnsavedChanges();
|
const changesNotSaved = useUnsavedChanges();
|
||||||
|
const [flowNames, setFlowNames] = useState<string[]>([]);
|
||||||
|
|
||||||
const { data: folders, isFetched: isFoldersFetched } = useGetFoldersQuery();
|
const { data: folders, isFetched: isFoldersFetched } = useGetFoldersQuery();
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
const flows = useFlowsManagerStore((state) => state.flows);
|
||||||
const [nameLists, setNameList] = useState<string[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (flows) {
|
|
||||||
const tempNameList: string[] = [];
|
|
||||||
flows.forEach((flow) => {
|
|
||||||
tempNameList.push(flow.name);
|
|
||||||
});
|
|
||||||
setNameList(tempNameList.filter((name) => name !== currentFlowName));
|
|
||||||
}
|
|
||||||
}, [flows, currentFlowName]);
|
|
||||||
|
|
||||||
useGetRefreshFlowsQuery(
|
useGetRefreshFlowsQuery(
|
||||||
{
|
{
|
||||||
|
|
@ -162,17 +152,11 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const handleEditName = useCallback(
|
const handleEditName = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
let invalid = false;
|
const invalid = flowNames.includes(value);
|
||||||
for (let i = 0; i < nameLists.length; i++) {
|
|
||||||
if (value === nameLists[i]) {
|
|
||||||
invalid = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setIsInvalidName(invalid);
|
setIsInvalidName(invalid);
|
||||||
setFlowName(value);
|
setFlowName(value);
|
||||||
},
|
},
|
||||||
[nameLists],
|
[flowNames],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
|
|
@ -195,7 +179,6 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
flowName !== currentFlowName &&
|
flowName !== currentFlowName &&
|
||||||
!isInvalidName
|
!isInvalidName
|
||||||
) {
|
) {
|
||||||
// Get a one-time snapshot of currentFlow using get()
|
|
||||||
const currentFlowSnapshot = useFlowStore.getState().currentFlow;
|
const currentFlowSnapshot = useFlowStore.getState().currentFlow;
|
||||||
|
|
||||||
const newFlow = {
|
const newFlow = {
|
||||||
|
|
@ -327,6 +310,12 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
setEditingName(true);
|
setEditingName(true);
|
||||||
setFlowName(currentFlowName);
|
setFlowName(currentFlowName);
|
||||||
|
const flows = useFlowsManagerStore.getState().flows;
|
||||||
|
setFlowNames(
|
||||||
|
flows
|
||||||
|
?.map((flow) => flow.name)
|
||||||
|
.filter((name) => name !== currentFlowName) ?? [],
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
onBlur={handleNameSubmit}
|
onBlur={handleNameSubmit}
|
||||||
value={flowName}
|
value={flowName}
|
||||||
|
|
@ -578,6 +567,6 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default MenuBar;
|
export default MenuBar;
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ const SidebarProvider = React.forwardRef<
|
||||||
|
|
||||||
// Helper to toggle the sidebar.
|
// Helper to toggle the sidebar.
|
||||||
const toggleSidebar = React.useCallback(() => {
|
const toggleSidebar = React.useCallback(() => {
|
||||||
return setOpen((open) => !open);
|
return setOpen((prev) => !prev);
|
||||||
}, [setOpen, open]);
|
}, [setOpen]);
|
||||||
|
|
||||||
// We add a state so that we can do data-state="expanded" or "collapsed".
|
// We add a state so that we can do data-state="expanded" or "collapsed".
|
||||||
// This makes it easier to style the sidebar with Tailwind classes.
|
// This makes it easier to style the sidebar with Tailwind classes.
|
||||||
|
|
@ -275,6 +275,14 @@ const SidebarTrigger = React.forwardRef<
|
||||||
>(({ className, onClick, ...props }, ref) => {
|
>(({ className, onClick, ...props }, ref) => {
|
||||||
const { toggleSidebar } = useSidebar();
|
const { toggleSidebar } = useSidebar();
|
||||||
|
|
||||||
|
const handleClick = React.useCallback(
|
||||||
|
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
onClick?.(event);
|
||||||
|
toggleSidebar();
|
||||||
|
},
|
||||||
|
[onClick, toggleSidebar],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|
@ -282,10 +290,7 @@ const SidebarTrigger = React.forwardRef<
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className={cn("h-7 w-7 text-muted-foreground", className)}
|
className={cn("h-7 w-7 text-muted-foreground", className)}
|
||||||
onClick={(event) => {
|
onClick={handleClick}
|
||||||
onClick?.(event);
|
|
||||||
toggleSidebar();
|
|
||||||
}}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{props.children ? (
|
{props.children ? (
|
||||||
|
|
@ -444,7 +449,8 @@ const SidebarGroup = React.forwardRef<
|
||||||
});
|
});
|
||||||
SidebarGroup.displayName = "SidebarGroup";
|
SidebarGroup.displayName = "SidebarGroup";
|
||||||
|
|
||||||
const SidebarGroupLabel = React.forwardRef<
|
const SidebarGroupLabel = React.memo(
|
||||||
|
React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<"div"> & { asChild?: boolean }
|
React.ComponentProps<"div"> & { asChild?: boolean }
|
||||||
>(({ className, asChild = false, ...props }, ref) => {
|
>(({ className, asChild = false, ...props }, ref) => {
|
||||||
|
|
@ -462,7 +468,8 @@ const SidebarGroupLabel = React.forwardRef<
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
}),
|
||||||
|
);
|
||||||
SidebarGroupLabel.displayName = "SidebarGroupLabel";
|
SidebarGroupLabel.displayName = "SidebarGroupLabel";
|
||||||
|
|
||||||
const SidebarGroupAction = React.forwardRef<
|
const SidebarGroupAction = React.forwardRef<
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {
|
||||||
} from "@/utils/reactflowUtils";
|
} from "@/utils/reactflowUtils";
|
||||||
|
|
||||||
const useDeleteFlow = () => {
|
const useDeleteFlow = () => {
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
|
||||||
const setFlows = useFlowsManagerStore((state) => state.setFlows);
|
const setFlows = useFlowsManagerStore((state) => state.setFlows);
|
||||||
|
|
||||||
const { mutate, isPending } = useDeleteDeleteFlows();
|
const { mutate, isPending } = useDeleteDeleteFlows();
|
||||||
|
|
@ -17,6 +16,7 @@ const useDeleteFlow = () => {
|
||||||
}: {
|
}: {
|
||||||
id: string | string[];
|
id: string | string[];
|
||||||
}): Promise<void> => {
|
}): Promise<void> => {
|
||||||
|
const flows = useFlowsManagerStore.getState().flows;
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
if (!Array.isArray(id)) {
|
if (!Array.isArray(id)) {
|
||||||
id = [id];
|
id = [id];
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,28 @@ import useFlowStore from "@/stores/flowStore";
|
||||||
import { AllNodeType, EdgeType, FlowType } from "@/types/flow";
|
import { AllNodeType, EdgeType, FlowType } from "@/types/flow";
|
||||||
import { customStringify } from "@/utils/reactflowUtils";
|
import { customStringify } from "@/utils/reactflowUtils";
|
||||||
import { ReactFlowJsonObject } from "@xyflow/react";
|
import { ReactFlowJsonObject } from "@xyflow/react";
|
||||||
|
|
||||||
const useSaveFlow = () => {
|
const useSaveFlow = () => {
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
|
||||||
const setFlows = useFlowsManagerStore((state) => state.setFlows);
|
const setFlows = useFlowsManagerStore((state) => state.setFlows);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const edges = useFlowStore((state) => state.edges);
|
|
||||||
const setSaveLoading = useFlowsManagerStore((state) => state.setSaveLoading);
|
const setSaveLoading = useFlowsManagerStore((state) => state.setSaveLoading);
|
||||||
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
|
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
|
||||||
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
|
||||||
|
|
||||||
const { mutate: getFlow } = useGetFlow();
|
const { mutate: getFlow } = useGetFlow();
|
||||||
const { mutate } = usePatchUpdateFlow();
|
const { mutate } = usePatchUpdateFlow();
|
||||||
|
|
||||||
const currentFlow = useFlowStore((state) => state.currentFlow);
|
|
||||||
const flowData = currentFlow?.data;
|
|
||||||
|
|
||||||
const saveFlow = async (flow?: FlowType): Promise<void> => {
|
const saveFlow = async (flow?: FlowType): Promise<void> => {
|
||||||
|
const currentFlow = useFlowStore.getState().currentFlow;
|
||||||
|
const currentSavedFlow = useFlowsManagerStore.getState().currentFlow;
|
||||||
if (
|
if (
|
||||||
customStringify(flow || currentFlow) !== customStringify(currentSavedFlow)
|
customStringify(flow || currentFlow) !== customStringify(currentSavedFlow)
|
||||||
) {
|
) {
|
||||||
setSaveLoading(true);
|
setSaveLoading(true);
|
||||||
|
|
||||||
|
const flowData = currentFlow?.data;
|
||||||
|
const nodes = useFlowStore.getState().nodes;
|
||||||
|
const edges = useFlowStore.getState().edges;
|
||||||
|
const reactFlowInstance = useFlowStore.getState().reactFlowInstance;
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
if (currentFlow) {
|
if (currentFlow) {
|
||||||
flow = flow || {
|
flow = flow || {
|
||||||
|
|
@ -83,6 +82,7 @@ const useSaveFlow = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (updatedFlow) => {
|
onSuccess: (updatedFlow) => {
|
||||||
|
const flows = useFlowsManagerStore.getState().flows;
|
||||||
setSaveLoading(false);
|
setSaveLoading(false);
|
||||||
if (flows) {
|
if (flows) {
|
||||||
// updates flow in state
|
// updates flow in state
|
||||||
|
|
|
||||||
|
|
@ -542,6 +542,13 @@ export default function Page({
|
||||||
|
|
||||||
const componentsToUpdate = useFlowStore((state) => state.componentsToUpdate);
|
const componentsToUpdate = useFlowStore((state) => state.componentsToUpdate);
|
||||||
|
|
||||||
|
const MIN_ZOOM = 0.2;
|
||||||
|
const MAX_ZOOM = 8;
|
||||||
|
const fitViewOptions = {
|
||||||
|
minZoom: MIN_ZOOM,
|
||||||
|
maxZoom: MAX_ZOOM,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full bg-canvas" ref={reactFlowWrapper}>
|
<div className="h-full w-full bg-canvas" ref={reactFlowWrapper}>
|
||||||
{showCanvas ? (
|
{showCanvas ? (
|
||||||
|
|
@ -572,13 +579,10 @@ export default function Page({
|
||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
deleteKeyCode={[]}
|
deleteKeyCode={[]}
|
||||||
fitView={isEmptyFlow.current ? false : true}
|
fitView={isEmptyFlow.current ? false : true}
|
||||||
fitViewOptions={{
|
fitViewOptions={fitViewOptions}
|
||||||
minZoom: 0.2,
|
|
||||||
maxZoom: 8,
|
|
||||||
}}
|
|
||||||
className="theme-attribution"
|
className="theme-attribution"
|
||||||
minZoom={0.2}
|
minZoom={MIN_ZOOM}
|
||||||
maxZoom={3}
|
maxZoom={MAX_ZOOM}
|
||||||
zoomOnScroll={!view}
|
zoomOnScroll={!view}
|
||||||
zoomOnPinch={!view}
|
zoomOnPinch={!view}
|
||||||
panOnDrag={!view}
|
panOnDrag={!view}
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,17 @@ import {
|
||||||
DisclosureTrigger,
|
DisclosureTrigger,
|
||||||
} from "@/components/ui/disclosure";
|
} from "@/components/ui/disclosure";
|
||||||
import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
||||||
import { memo } from "react";
|
import { memo, useCallback } from "react";
|
||||||
import { BundleItemProps } from "../../types";
|
import { BundleItemProps } from "../../types";
|
||||||
import SidebarItemsList from "../sidebarItemsList";
|
import SidebarItemsList from "../sidebarItemsList";
|
||||||
|
|
||||||
export const BundleItem = memo(
|
export const BundleItem = memo(
|
||||||
({
|
({
|
||||||
item,
|
item,
|
||||||
isOpen,
|
openCategories,
|
||||||
onOpenChange,
|
setOpenCategories,
|
||||||
dataFilter,
|
dataFilter,
|
||||||
nodeColors,
|
nodeColors,
|
||||||
uniqueInputsComponents,
|
|
||||||
onDragStart,
|
onDragStart,
|
||||||
sensitiveSort,
|
sensitiveSort,
|
||||||
handleKeyDownInput,
|
handleKeyDownInput,
|
||||||
|
|
@ -28,9 +27,21 @@ export const BundleItem = memo(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isOpen = openCategories.includes(item.name);
|
||||||
|
|
||||||
|
const handleOpenChange = useCallback(
|
||||||
|
(isOpen: boolean) => {
|
||||||
|
setOpenCategories((prev: string[]) =>
|
||||||
|
isOpen
|
||||||
|
? [...prev, item.name]
|
||||||
|
: prev.filter((cat) => cat !== item.name),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[item.name, setOpenCategories],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Disclosure key={item.name} open={isOpen} onOpenChange={handleOpenChange}>
|
||||||
<Disclosure key={item.name} open={isOpen} onOpenChange={onOpenChange}>
|
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<DisclosureTrigger className="group/collapsible">
|
<DisclosureTrigger className="group/collapsible">
|
||||||
<SidebarMenuButton asChild>
|
<SidebarMenuButton asChild>
|
||||||
|
|
@ -59,14 +70,12 @@ export const BundleItem = memo(
|
||||||
item={item}
|
item={item}
|
||||||
dataFilter={dataFilter}
|
dataFilter={dataFilter}
|
||||||
nodeColors={nodeColors}
|
nodeColors={nodeColors}
|
||||||
uniqueInputsComponents={uniqueInputsComponents}
|
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
sensitiveSort={sensitiveSort}
|
sensitiveSort={sensitiveSort}
|
||||||
/>
|
/>
|
||||||
</DisclosureContent>
|
</DisclosureContent>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</Disclosure>
|
</Disclosure>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ export const CategoryDisclosure = memo(function CategoryDisclosure({
|
||||||
setOpenCategories,
|
setOpenCategories,
|
||||||
dataFilter,
|
dataFilter,
|
||||||
nodeColors,
|
nodeColors,
|
||||||
uniqueInputsComponents,
|
|
||||||
onDragStart,
|
onDragStart,
|
||||||
sensitiveSort,
|
sensitiveSort,
|
||||||
}: {
|
}: {
|
||||||
|
|
@ -24,10 +23,6 @@ export const CategoryDisclosure = memo(function CategoryDisclosure({
|
||||||
setOpenCategories;
|
setOpenCategories;
|
||||||
dataFilter: any;
|
dataFilter: any;
|
||||||
nodeColors: any;
|
nodeColors: any;
|
||||||
uniqueInputsComponents: {
|
|
||||||
chatInput: boolean;
|
|
||||||
webhookInput: boolean;
|
|
||||||
};
|
|
||||||
onDragStart: (
|
onDragStart: (
|
||||||
event: React.DragEvent<any>,
|
event: React.DragEvent<any>,
|
||||||
data: { type: string; node?: APIClassType },
|
data: { type: string; node?: APIClassType },
|
||||||
|
|
@ -48,17 +43,17 @@ export const CategoryDisclosure = memo(function CategoryDisclosure({
|
||||||
[item.name, setOpenCategories],
|
[item.name, setOpenCategories],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const isOpen = openCategories.includes(item.name);
|
||||||
<Disclosure
|
const handleOpenChange = useCallback(
|
||||||
open={openCategories.includes(item.name)}
|
(isOpen: boolean) => {
|
||||||
onOpenChange={(isOpen) => {
|
|
||||||
setOpenCategories((prev) =>
|
setOpenCategories((prev) =>
|
||||||
isOpen
|
isOpen ? [...prev, item.name] : prev.filter((cat) => cat !== item.name),
|
||||||
? [...prev, item.name]
|
|
||||||
: prev.filter((cat) => cat !== item.name),
|
|
||||||
);
|
);
|
||||||
}}
|
},
|
||||||
>
|
[item.name, setOpenCategories],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Disclosure open={isOpen} onOpenChange={handleOpenChange}>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<DisclosureTrigger className="group/collapsible">
|
<DisclosureTrigger className="group/collapsible">
|
||||||
<SidebarMenuButton asChild>
|
<SidebarMenuButton asChild>
|
||||||
|
|
@ -87,7 +82,6 @@ export const CategoryDisclosure = memo(function CategoryDisclosure({
|
||||||
item={item}
|
item={item}
|
||||||
dataFilter={dataFilter}
|
dataFilter={dataFilter}
|
||||||
nodeColors={nodeColors}
|
nodeColors={nodeColors}
|
||||||
uniqueInputsComponents={uniqueInputsComponents}
|
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
sensitiveSort={sensitiveSort}
|
sensitiveSort={sensitiveSort}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { SIDEBAR_BUNDLES } from "@/utils/styleUtils";
|
import { SIDEBAR_BUNDLES } from "@/utils/styleUtils";
|
||||||
import { memo } from "react";
|
import { memo, useState } from "react";
|
||||||
import { CategoryGroupProps } from "../../types";
|
import { CategoryGroupProps } from "../../types";
|
||||||
import { CategoryDisclosure } from "../categoryDisclouse";
|
import { CategoryDisclosure } from "../categoryDisclouse";
|
||||||
|
|
||||||
|
|
@ -16,7 +16,6 @@ export const CategoryGroup = memo(function CategoryGroup({
|
||||||
setOpenCategories,
|
setOpenCategories,
|
||||||
search,
|
search,
|
||||||
nodeColors,
|
nodeColors,
|
||||||
uniqueInputsComponents,
|
|
||||||
onDragStart,
|
onDragStart,
|
||||||
sensitiveSort,
|
sensitiveSort,
|
||||||
}: CategoryGroupProps) {
|
}: CategoryGroupProps) {
|
||||||
|
|
@ -67,7 +66,6 @@ export const CategoryGroup = memo(function CategoryGroup({
|
||||||
nodeColors={nodeColors}
|
nodeColors={nodeColors}
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
sensitiveSort={sensitiveSort}
|
sensitiveSort={sensitiveSort}
|
||||||
uniqueInputsComponents={uniqueInputsComponents}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
SidebarGroupLabel,
|
SidebarGroupLabel,
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { memo, useMemo } from "react";
|
import { memo, useCallback, useMemo, useState } from "react";
|
||||||
import { SidebarGroupProps } from "../../types";
|
import { SidebarGroupProps } from "../../types";
|
||||||
import { BundleItem } from "../bundleItems";
|
import { BundleItem } from "../bundleItems";
|
||||||
|
|
||||||
|
|
@ -17,10 +17,9 @@ export const MemoizedSidebarGroup = memo(
|
||||||
nodeColors,
|
nodeColors,
|
||||||
onDragStart,
|
onDragStart,
|
||||||
sensitiveSort,
|
sensitiveSort,
|
||||||
|
handleKeyDownInput,
|
||||||
openCategories,
|
openCategories,
|
||||||
setOpenCategories,
|
setOpenCategories,
|
||||||
handleKeyDownInput,
|
|
||||||
uniqueInputsComponents,
|
|
||||||
}: SidebarGroupProps) => {
|
}: SidebarGroupProps) => {
|
||||||
const sortedBundles = useMemo(() => {
|
const sortedBundles = useMemo(() => {
|
||||||
return BUNDLES.toSorted((a, b) => {
|
return BUNDLES.toSorted((a, b) => {
|
||||||
|
|
@ -41,17 +40,10 @@ export const MemoizedSidebarGroup = memo(
|
||||||
<BundleItem
|
<BundleItem
|
||||||
key={item.name}
|
key={item.name}
|
||||||
item={item}
|
item={item}
|
||||||
isOpen={openCategories.includes(item.name)}
|
openCategories={openCategories}
|
||||||
onOpenChange={(isOpen) => {
|
setOpenCategories={setOpenCategories}
|
||||||
setOpenCategories((prev) =>
|
|
||||||
isOpen
|
|
||||||
? [...prev, item.name]
|
|
||||||
: prev.filter((cat) => cat !== item.name),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
dataFilter={dataFilter}
|
dataFilter={dataFilter}
|
||||||
nodeColors={nodeColors}
|
nodeColors={nodeColors}
|
||||||
uniqueInputsComponents={uniqueInputsComponents}
|
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
sensitiveSort={sensitiveSort}
|
sensitiveSort={sensitiveSort}
|
||||||
handleKeyDownInput={handleKeyDownInput}
|
handleKeyDownInput={handleKeyDownInput}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||||
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
import { checkChatInput, checkWebhookInput } from "@/utils/reactflowUtils";
|
||||||
import { removeCountFromString } from "@/utils/utils";
|
import { removeCountFromString } from "@/utils/utils";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { disableItem } from "../../helpers/disable-item";
|
import { disableItem } from "../../helpers/disable-item";
|
||||||
import { getDisabledTooltip } from "../../helpers/get-disabled-tooltip";
|
import { getDisabledTooltip } from "../../helpers/get-disabled-tooltip";
|
||||||
|
import { UniqueInputsComponents } from "../../types";
|
||||||
import SidebarDraggableComponent from "../sidebarDraggableComponent";
|
import SidebarDraggableComponent from "../sidebarDraggableComponent";
|
||||||
|
|
||||||
const SidebarItemsList = ({
|
const SidebarItemsList = ({
|
||||||
item,
|
item,
|
||||||
dataFilter,
|
dataFilter,
|
||||||
nodeColors,
|
nodeColors,
|
||||||
uniqueInputsComponents,
|
|
||||||
onDragStart,
|
onDragStart,
|
||||||
sensitiveSort,
|
sensitiveSort,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -36,6 +39,18 @@ const SidebarItemsList = ({
|
||||||
.map((SBItemName, idx) => {
|
.map((SBItemName, idx) => {
|
||||||
const currentItem = dataFilter[item.name][SBItemName];
|
const currentItem = dataFilter[item.name][SBItemName];
|
||||||
|
|
||||||
|
if (SBItemName === "ChatInput" || SBItemName === "Webhook") {
|
||||||
|
return (
|
||||||
|
<UniqueInputsDraggableComponent
|
||||||
|
item={item}
|
||||||
|
currentItem={currentItem}
|
||||||
|
SBItemName={SBItemName}
|
||||||
|
idx={idx}
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
nodeColors={nodeColors}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={currentItem.display_name}
|
content={currentItem.display_name}
|
||||||
|
|
@ -59,11 +74,8 @@ const SidebarItemsList = ({
|
||||||
official={currentItem.official === false ? false : true}
|
official={currentItem.official === false ? false : true}
|
||||||
beta={currentItem.beta ?? false}
|
beta={currentItem.beta ?? false}
|
||||||
legacy={currentItem.legacy ?? false}
|
legacy={currentItem.legacy ?? false}
|
||||||
disabled={disableItem(SBItemName, uniqueInputsComponents)}
|
disabled={false}
|
||||||
disabledTooltip={getDisabledTooltip(
|
disabledTooltip={""}
|
||||||
SBItemName,
|
|
||||||
uniqueInputsComponents,
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
);
|
);
|
||||||
|
|
@ -73,3 +85,51 @@ const SidebarItemsList = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SidebarItemsList;
|
export default SidebarItemsList;
|
||||||
|
|
||||||
|
const UniqueInputsDraggableComponent = ({
|
||||||
|
item,
|
||||||
|
currentItem,
|
||||||
|
SBItemName,
|
||||||
|
idx,
|
||||||
|
onDragStart,
|
||||||
|
nodeColors,
|
||||||
|
}) => {
|
||||||
|
const nodes = useFlowStore((state) => state.nodes);
|
||||||
|
const chatInputAdded = useMemo(() => checkChatInput(nodes), [nodes]);
|
||||||
|
const webhookInputAdded = useMemo(() => checkWebhookInput(nodes), [nodes]);
|
||||||
|
const uniqueInputsComponents: UniqueInputsComponents = useMemo(() => {
|
||||||
|
console.log("uniqueInputsComponents", {
|
||||||
|
chatInputAdded,
|
||||||
|
webhookInputAdded,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
chatInput: chatInputAdded,
|
||||||
|
webhookInput: webhookInputAdded,
|
||||||
|
};
|
||||||
|
}, [chatInputAdded, webhookInputAdded]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ShadTooltip content={currentItem.display_name} side="right" key={idx}>
|
||||||
|
<SidebarDraggableComponent
|
||||||
|
sectionName={item.name}
|
||||||
|
apiClass={currentItem}
|
||||||
|
icon={currentItem.icon ?? item.icon ?? "Unknown"}
|
||||||
|
onDragStart={(event) =>
|
||||||
|
onDragStart(event, {
|
||||||
|
type: removeCountFromString(SBItemName),
|
||||||
|
node: currentItem,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
color={nodeColors[item.name]}
|
||||||
|
itemName={SBItemName}
|
||||||
|
error={!!currentItem.error}
|
||||||
|
display_name={currentItem.display_name}
|
||||||
|
official={currentItem.official === false ? false : true}
|
||||||
|
beta={currentItem.beta ?? false}
|
||||||
|
legacy={currentItem.legacy ?? false}
|
||||||
|
disabled={disableItem(SBItemName, uniqueInputsComponents)}
|
||||||
|
disabledTooltip={getDisabledTooltip(SBItemName, uniqueInputsComponents)}
|
||||||
|
/>
|
||||||
|
</ShadTooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import Fuse from "fuse.js";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useHotkeys } from "react-hotkeys-hook";
|
import { useHotkeys } from "react-hotkeys-hook";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import useAlertStore from "../../../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
import useFlowStore from "../../../../stores/flowStore";
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
import { useTypesStore } from "../../../../stores/typesStore";
|
import { useTypesStore } from "../../../../stores/typesStore";
|
||||||
|
|
@ -48,30 +49,17 @@ interface FlowSidebarComponentProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) {
|
export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) {
|
||||||
const { data, templates } = useTypesStore(
|
const data = useTypesStore((state) => state.data);
|
||||||
useCallback(
|
|
||||||
(state) => ({
|
|
||||||
data: state.data,
|
|
||||||
templates: state.templates,
|
|
||||||
}),
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const { getFilterEdge, setFilterEdge, filterType, nodes } = useFlowStore(
|
const { getFilterEdge, setFilterEdge, filterType } = useFlowStore(
|
||||||
useCallback(
|
useShallow((state) => ({
|
||||||
(state) => ({
|
|
||||||
getFilterEdge: state.getFilterEdge,
|
getFilterEdge: state.getFilterEdge,
|
||||||
setFilterEdge: state.setFilterEdge,
|
setFilterEdge: state.setFilterEdge,
|
||||||
filterType: state.filterType,
|
filterType: state.filterType,
|
||||||
nodes: state.nodes,
|
})),
|
||||||
}),
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasStore = useStoreStore((state) => state.hasStore);
|
const hasStore = useStoreStore((state) => state.hasStore);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
|
||||||
const { setOpen } = useSidebar();
|
const { setOpen } = useSidebar();
|
||||||
const addComponent = useAddComponent();
|
const addComponent = useAddComponent();
|
||||||
|
|
||||||
|
|
@ -87,15 +75,6 @@ export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) {
|
||||||
|
|
||||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const chatInputAdded = useMemo(() => checkChatInput(nodes), [nodes]);
|
|
||||||
const webhookInputAdded = useMemo(() => checkWebhookInput(nodes), [nodes]);
|
|
||||||
const uniqueInputsComponents: UniqueInputsComponents = useMemo(() => {
|
|
||||||
return {
|
|
||||||
chatInput: chatInputAdded,
|
|
||||||
webhookInput: webhookInputAdded,
|
|
||||||
};
|
|
||||||
}, [chatInputAdded, webhookInputAdded]);
|
|
||||||
|
|
||||||
const customComponent = useMemo(() => {
|
const customComponent = useMemo(() => {
|
||||||
return data?.["custom_component"]?.["CustomComponent"] ?? null;
|
return data?.["custom_component"]?.["CustomComponent"] ?? null;
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
@ -358,7 +337,6 @@ export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) {
|
||||||
nodeColors={nodeColors}
|
nodeColors={nodeColors}
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
sensitiveSort={sensitiveSort}
|
sensitiveSort={sensitiveSort}
|
||||||
uniqueInputsComponents={uniqueInputsComponents}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hasBundleItems && (
|
{hasBundleItems && (
|
||||||
|
|
@ -373,7 +351,6 @@ export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) {
|
||||||
openCategories={openCategories}
|
openCategories={openCategories}
|
||||||
setOpenCategories={setOpenCategories}
|
setOpenCategories={setOpenCategories}
|
||||||
handleKeyDownInput={handleKeyDownInput}
|
handleKeyDownInput={handleKeyDownInput}
|
||||||
uniqueInputsComponents={uniqueInputsComponents}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export interface CategoryGroupProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
}[];
|
}[];
|
||||||
openCategories: string[];
|
openCategories: string[];
|
||||||
setOpenCategories: (categories: string[]) => void;
|
setOpenCategories: Dispatch<SetStateAction<string[]>>;
|
||||||
search: string;
|
search: string;
|
||||||
nodeColors: NodeColors;
|
nodeColors: NodeColors;
|
||||||
onDragStart: (
|
onDragStart: (
|
||||||
|
|
@ -22,10 +22,6 @@ export interface CategoryGroupProps {
|
||||||
data: { type: string; node?: APIClassType },
|
data: { type: string; node?: APIClassType },
|
||||||
) => void;
|
) => void;
|
||||||
sensitiveSort: (a: string, b: string) => number;
|
sensitiveSort: (a: string, b: string) => number;
|
||||||
uniqueInputsComponents: {
|
|
||||||
chatInput: boolean;
|
|
||||||
webhookInput: boolean;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SidebarGroupProps {
|
export interface SidebarGroupProps {
|
||||||
|
|
@ -39,15 +35,12 @@ export interface SidebarGroupProps {
|
||||||
data: { type: string; node?: APIClassType },
|
data: { type: string; node?: APIClassType },
|
||||||
) => void;
|
) => void;
|
||||||
sensitiveSort: (a: string, b: string) => number;
|
sensitiveSort: (a: string, b: string) => number;
|
||||||
openCategories: string[];
|
|
||||||
setOpenCategories: (
|
|
||||||
categories: string[] | ((prev: string[]) => string[]),
|
|
||||||
) => void;
|
|
||||||
handleKeyDownInput: (
|
handleKeyDownInput: (
|
||||||
event: React.KeyboardEvent<HTMLDivElement>,
|
event: React.KeyboardEvent<HTMLDivElement>,
|
||||||
name: string,
|
name: string,
|
||||||
) => void;
|
) => void;
|
||||||
uniqueInputsComponents: UniqueInputsComponents;
|
openCategories: string[];
|
||||||
|
setOpenCategories: Dispatch<SetStateAction<string[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BundleItemProps {
|
export interface BundleItemProps {
|
||||||
|
|
@ -56,11 +49,10 @@ export interface BundleItemProps {
|
||||||
display_name: string;
|
display_name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
};
|
};
|
||||||
isOpen: boolean;
|
openCategories: string[];
|
||||||
onOpenChange: (isOpen: boolean) => void;
|
setOpenCategories: Dispatch<SetStateAction<string[]>>;
|
||||||
dataFilter: APIDataType;
|
dataFilter: APIDataType;
|
||||||
nodeColors: NodeColors;
|
nodeColors: NodeColors;
|
||||||
uniqueInputsComponents: UniqueInputsComponents;
|
|
||||||
onDragStart: (
|
onDragStart: (
|
||||||
event: React.DragEvent<any>,
|
event: React.DragEvent<any>,
|
||||||
data: { type: string; node?: APIClassType },
|
data: { type: string; node?: APIClassType },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue