feat: Add loading state and skeleton UI for FlowPage sidebar (#6738)

* feat: Add loading state and skeleton UI for FlowPage sidebar

* fix: Improve UI components with minor styling and z-index adjustments

* refactor: Simplify SkeletonGroup component and update FlowPage sidebar loading state

* refactor: Adjust SkeletonGroup rendering and FlowPage sidebar styling

* refactor: Remove z-index from PageComponent loading state

* refactor: Update FlowPage sidebar skeleton height class
This commit is contained in:
Deon Sanchez 2025-02-21 11:43:00 -07:00 • committed by GitHub
commit feff8b681e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 85 additions and 34 deletions

View file

@ -0,0 +1,20 @@
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/utils/utils";
const SkeletonGroup = ({
count = 2,
className = "",
}: {
count?: number;
className?: string;
}) => {
return (
<>
{Array.from({ length: count }, (_, i) => (
<Skeleton key={i} className={cn("w-full", className)} />
))}
</>
);
};
export default SkeletonGroup;

View file

@ -79,7 +79,13 @@ const edgeTypes = {
default: DefaultEdge, default: DefaultEdge,
}; };
export default function Page({ view }: { view?: boolean }): JSX.Element { export default function Page({
view,
setIsLoading,
}: {
view?: boolean;
setIsLoading: (isLoading: boolean) => void;
}): JSX.Element {
const uploadFlow = useUploadFlow(); const uploadFlow = useUploadFlow();
const autoSaveFlow = useAutoSaveFlow(); const autoSaveFlow = useAutoSaveFlow();
const types = useTypesStore((state) => state.types); const types = useTypesStore((state) => state.types);
@ -184,6 +190,10 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
Object.keys(types).length > 0 && Object.keys(types).length > 0 &&
!isFetching; !isFetching;
useEffect(() => {
setIsLoading(!showCanvas);
}, [showCanvas]);
useEffect(() => { useEffect(() => {
useFlowStore.setState({ autoSaveFlow }); useFlowStore.setState({ autoSaveFlow });
}, [autoSaveFlow]); }, [autoSaveFlow]);

View file

@ -7,6 +7,7 @@ const SidebarMenuButtons = ({
hasStore = false, hasStore = false,
customComponent, customComponent,
addComponent, addComponent,
isLoading = false,
}) => { }) => {
return ( return (
<> <>
@ -37,6 +38,7 @@ const SidebarMenuButtons = ({
<SidebarMenuButton asChild> <SidebarMenuButton asChild>
<Button <Button
unstyled unstyled
disabled={isLoading}
onClick={() => { onClick={() => {
if (customComponent) { if (customComponent) {
addComponent(customComponent, "CustomComponent"); addComponent(customComponent, "CustomComponent");

View file

@ -4,6 +4,7 @@ import {
SidebarFooter, SidebarFooter,
useSidebar, useSidebar,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import SkeletonGroup from "@/components/ui/skeletonGroup";
import { useAddComponent } from "@/hooks/useAddComponent"; import { useAddComponent } from "@/hooks/useAddComponent";
import { useShortcutsStore } from "@/stores/shortcuts"; import { useShortcutsStore } from "@/stores/shortcuts";
import { useStoreStore } from "@/stores/storeStore"; import { useStoreStore } from "@/stores/storeStore";
@ -44,7 +45,7 @@ interface FlowSidebarComponentProps {
setShowLegacy: (value: boolean) => void; setShowLegacy: (value: boolean) => void;
} }
export function FlowSidebarComponent() { export function FlowSidebarComponent({ isLoading }: { isLoading?: boolean }) {
const { data, templates } = useTypesStore( const { data, templates } = useTypesStore(
useCallback( useCallback(
(state) => ({ (state) => ({
@ -322,39 +323,55 @@ export function FlowSidebarComponent() {
setFilterData={setFilterData} setFilterData={setFilterData}
data={data} data={data}
/> />
<SidebarContent> <SidebarContent>
{hasResults ? ( {isLoading ? (
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-1 p-3">
<SkeletonGroup count={13} className="my-0.5 h-7" />
</div>
<div className="h-8" />
<div className="flex flex-col gap-1 px-3 pt-2">
<SkeletonGroup count={21} className="my-0.5 h-7" />
</div>
</div>
) : (
<> <>
<CategoryGroup {hasResults ? (
dataFilter={dataFilter} <>
sortedCategories={sortedCategories} <CategoryGroup
CATEGORIES={CATEGORIES} dataFilter={dataFilter}
openCategories={openCategories} sortedCategories={sortedCategories}
setOpenCategories={setOpenCategories} CATEGORIES={CATEGORIES}
search={search} openCategories={openCategories}
nodeColors={nodeColors} setOpenCategories={setOpenCategories}
chatInputAdded={chatInputAdded} search={search}
onDragStart={onDragStart} nodeColors={nodeColors}
sensitiveSort={sensitiveSort} chatInputAdded={chatInputAdded}
/> onDragStart={onDragStart}
{hasBundleItems && ( sensitiveSort={sensitiveSort}
<MemoizedSidebarGroup />
BUNDLES={BUNDLES}
search={search} {hasBundleItems && (
sortedCategories={sortedCategories} <MemoizedSidebarGroup
dataFilter={dataFilter} BUNDLES={BUNDLES}
nodeColors={nodeColors} search={search}
chatInputAdded={chatInputAdded} sortedCategories={sortedCategories}
onDragStart={onDragStart} dataFilter={dataFilter}
sensitiveSort={sensitiveSort} nodeColors={nodeColors}
openCategories={openCategories} chatInputAdded={chatInputAdded}
setOpenCategories={setOpenCategories} onDragStart={onDragStart}
handleKeyDownInput={handleKeyDownInput} sensitiveSort={sensitiveSort}
/> openCategories={openCategories}
setOpenCategories={setOpenCategories}
handleKeyDownInput={handleKeyDownInput}
/>
)}
</>
) : (
<NoResultsMessage onClearSearch={handleClearSearch} />
)} )}
</> </>
) : (
<NoResultsMessage onClearSearch={handleClearSearch} />
)} )}
</SidebarContent> </SidebarContent>
<SidebarFooter className="border-t p-4 py-3"> <SidebarFooter className="border-t p-4 py-3">
@ -362,6 +379,7 @@ export function FlowSidebarComponent() {
hasStore={hasStore} hasStore={hasStore}
customComponent={customComponent} customComponent={customComponent}
addComponent={addComponent} addComponent={addComponent}
isLoading={isLoading}
/> />
</SidebarFooter> </SidebarFooter>
</Sidebar> </Sidebar>

View file

@ -6,7 +6,7 @@ import { useIsMobile } from "@/hooks/use-mobile";
import { SaveChangesModal } from "@/modals/saveChangesModal"; import { SaveChangesModal } from "@/modals/saveChangesModal";
import useAlertStore from "@/stores/alertStore"; import useAlertStore from "@/stores/alertStore";
import { customStringify } from "@/utils/reactflowUtils"; import { customStringify } from "@/utils/reactflowUtils";
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { useBlocker, useParams } from "react-router-dom"; import { useBlocker, useParams } from "react-router-dom";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -18,6 +18,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const currentFlow = useFlowStore((state) => state.currentFlow); const currentFlow = useFlowStore((state) => state.currentFlow);
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow); const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const [isLoading, setIsLoading] = useState(false);
const changesNotSaved = const changesNotSaved =
customStringify(currentFlow) !== customStringify(currentSavedFlow) && customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
@ -154,10 +155,10 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
{currentFlow && ( {currentFlow && (
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
<SidebarProvider width="17.5rem" defaultOpen={!isMobile}> <SidebarProvider width="17.5rem" defaultOpen={!isMobile}>
{!view && <FlowSidebarComponent />} {!view && <FlowSidebarComponent isLoading={isLoading} />}
<main className="flex w-full overflow-hidden"> <main className="flex w-full overflow-hidden">
<div className="h-full w-full"> <div className="h-full w-full">
<Page /> <Page setIsLoading={setIsLoading} />
</div> </div>
</main> </main>
</SidebarProvider> </SidebarProvider>