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:
parent
d545e8d302
commit
feff8b681e
5 changed files with 85 additions and 34 deletions
20
src/frontend/src/components/ui/skeletonGroup.tsx
Normal file
20
src/frontend/src/components/ui/skeletonGroup.tsx
Normal 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;
|
||||
|
|
@ -79,7 +79,13 @@ const edgeTypes = {
|
|||
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 autoSaveFlow = useAutoSaveFlow();
|
||||
const types = useTypesStore((state) => state.types);
|
||||
|
|
@ -184,6 +190,10 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
|||
Object.keys(types).length > 0 &&
|
||||
!isFetching;
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(!showCanvas);
|
||||
}, [showCanvas]);
|
||||
|
||||
useEffect(() => {
|
||||
useFlowStore.setState({ autoSaveFlow });
|
||||
}, [autoSaveFlow]);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const SidebarMenuButtons = ({
|
|||
hasStore = false,
|
||||
customComponent,
|
||||
addComponent,
|
||||
isLoading = false,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
|
|
@ -37,6 +38,7 @@ const SidebarMenuButtons = ({
|
|||
<SidebarMenuButton asChild>
|
||||
<Button
|
||||
unstyled
|
||||
disabled={isLoading}
|
||||
onClick={() => {
|
||||
if (customComponent) {
|
||||
addComponent(customComponent, "CustomComponent");
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
SidebarFooter,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
import SkeletonGroup from "@/components/ui/skeletonGroup";
|
||||
import { useAddComponent } from "@/hooks/useAddComponent";
|
||||
import { useShortcutsStore } from "@/stores/shortcuts";
|
||||
import { useStoreStore } from "@/stores/storeStore";
|
||||
|
|
@ -44,7 +45,7 @@ interface FlowSidebarComponentProps {
|
|||
setShowLegacy: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export function FlowSidebarComponent() {
|
||||
export function FlowSidebarComponent({ isLoading }: { isLoading?: boolean }) {
|
||||
const { data, templates } = useTypesStore(
|
||||
useCallback(
|
||||
(state) => ({
|
||||
|
|
@ -322,39 +323,55 @@ export function FlowSidebarComponent() {
|
|||
setFilterData={setFilterData}
|
||||
data={data}
|
||||
/>
|
||||
|
||||
<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
|
||||
dataFilter={dataFilter}
|
||||
sortedCategories={sortedCategories}
|
||||
CATEGORIES={CATEGORIES}
|
||||
openCategories={openCategories}
|
||||
setOpenCategories={setOpenCategories}
|
||||
search={search}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
/>
|
||||
{hasBundleItems && (
|
||||
<MemoizedSidebarGroup
|
||||
BUNDLES={BUNDLES}
|
||||
search={search}
|
||||
sortedCategories={sortedCategories}
|
||||
dataFilter={dataFilter}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
openCategories={openCategories}
|
||||
setOpenCategories={setOpenCategories}
|
||||
handleKeyDownInput={handleKeyDownInput}
|
||||
/>
|
||||
{hasResults ? (
|
||||
<>
|
||||
<CategoryGroup
|
||||
dataFilter={dataFilter}
|
||||
sortedCategories={sortedCategories}
|
||||
CATEGORIES={CATEGORIES}
|
||||
openCategories={openCategories}
|
||||
setOpenCategories={setOpenCategories}
|
||||
search={search}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
/>
|
||||
|
||||
{hasBundleItems && (
|
||||
<MemoizedSidebarGroup
|
||||
BUNDLES={BUNDLES}
|
||||
search={search}
|
||||
sortedCategories={sortedCategories}
|
||||
dataFilter={dataFilter}
|
||||
nodeColors={nodeColors}
|
||||
chatInputAdded={chatInputAdded}
|
||||
onDragStart={onDragStart}
|
||||
sensitiveSort={sensitiveSort}
|
||||
openCategories={openCategories}
|
||||
setOpenCategories={setOpenCategories}
|
||||
handleKeyDownInput={handleKeyDownInput}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<NoResultsMessage onClearSearch={handleClearSearch} />
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<NoResultsMessage onClearSearch={handleClearSearch} />
|
||||
)}
|
||||
</SidebarContent>
|
||||
<SidebarFooter className="border-t p-4 py-3">
|
||||
|
|
@ -362,6 +379,7 @@ export function FlowSidebarComponent() {
|
|||
hasStore={hasStore}
|
||||
customComponent={customComponent}
|
||||
addComponent={addComponent}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { useIsMobile } from "@/hooks/use-mobile";
|
|||
import { SaveChangesModal } from "@/modals/saveChangesModal";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import { customStringify } from "@/utils/reactflowUtils";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useBlocker, useParams } from "react-router-dom";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
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 currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const changesNotSaved =
|
||||
customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
|
||||
|
|
@ -154,10 +155,10 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
|||
{currentFlow && (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
<SidebarProvider width="17.5rem" defaultOpen={!isMobile}>
|
||||
{!view && <FlowSidebarComponent />}
|
||||
{!view && <FlowSidebarComponent isLoading={isLoading} />}
|
||||
<main className="flex w-full overflow-hidden">
|
||||
<div className="h-full w-full">
|
||||
<Page />
|
||||
<Page setIsLoading={setIsLoading} />
|
||||
</div>
|
||||
</main>
|
||||
</SidebarProvider>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue