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,
|
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]);
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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,7 +323,20 @@ export function FlowSidebarComponent() {
|
||||||
setFilterData={setFilterData}
|
setFilterData={setFilterData}
|
||||||
data={data}
|
data={data}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
|
{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>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
{hasResults ? (
|
{hasResults ? (
|
||||||
<>
|
<>
|
||||||
<CategoryGroup
|
<CategoryGroup
|
||||||
|
|
@ -337,6 +351,7 @@ export function FlowSidebarComponent() {
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
sensitiveSort={sensitiveSort}
|
sensitiveSort={sensitiveSort}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hasBundleItems && (
|
{hasBundleItems && (
|
||||||
<MemoizedSidebarGroup
|
<MemoizedSidebarGroup
|
||||||
BUNDLES={BUNDLES}
|
BUNDLES={BUNDLES}
|
||||||
|
|
@ -356,12 +371,15 @@ export function FlowSidebarComponent() {
|
||||||
) : (
|
) : (
|
||||||
<NoResultsMessage onClearSearch={handleClearSearch} />
|
<NoResultsMessage onClearSearch={handleClearSearch} />
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter className="border-t p-4 py-3">
|
<SidebarFooter className="border-t p-4 py-3">
|
||||||
<SidebarMenuButtons
|
<SidebarMenuButtons
|
||||||
hasStore={hasStore}
|
hasStore={hasStore}
|
||||||
customComponent={customComponent}
|
customComponent={customComponent}
|
||||||
addComponent={addComponent}
|
addComponent={addComponent}
|
||||||
|
isLoading={isLoading}
|
||||||
/>
|
/>
|
||||||
</SidebarFooter>
|
</SidebarFooter>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue