Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 40 additions & 13 deletions src/components/shared/ReactFlow/FlowSidebar/FlowSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { useState } from "react";

import { VerticalResizeHandle } from "@/components/ui/resize-handle";
import {
Sidebar,
SidebarContent,
Expand All @@ -11,37 +14,61 @@ import FileActions from "./sections/FileActions";
import GraphComponents from "./sections/GraphComponents";
import RunsAndSubmission from "./sections/RunsAndSubmission";

const MIN_WIDTH = 200;
const MAX_WIDTH = 400;
const DEFAULT_WIDTH = 256;
const COLLAPSED_WIDTH = 48;

const FlowSidebar = () => {
const { open, setOpen } = useSidebar();
const { currentSubgraphPath } = useComponentSpec();
const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_WIDTH);

const isViewingSubgraph = currentSubgraphPath.length > 1;

const triggerLeft = open ? sidebarWidth - 1 : COLLAPSED_WIDTH - 1;

const sidebarTriggerClasses = cn(
"absolute z-1 transition-all duration-300 bg-white mt-8 rounded-r-md shadow-md p-0.5 pr-1",
open ? "left-[255px]" : "left-[47px]",
"absolute z-1 bg-white mt-8 rounded-r-md shadow-md p-0.5 pr-1",
isViewingSubgraph ? "top-[65px]" : "top-6",
);

return (
<>
<div className={sidebarTriggerClasses}>
<div
className={sidebarTriggerClasses}
style={{ left: `${triggerLeft}px` }}
>
<SidebarTrigger
className="text-gray-600 hover:bg-gray-50 hover:text-gray-900"
onClick={() => setOpen(!open)}
/>
</div>
<Sidebar
side="left"
className="mt-14 h-[calc(100vh-56px)]"
collapsible="icon"
<div
style={
{ "--sidebar-width": `${sidebarWidth}px` } as React.CSSProperties
}
>
<SidebarContent className="gap-0! m-0! p-0!">
<FileActions isOpen={open} />
<RunsAndSubmission isOpen={open} />
<GraphComponents isOpen={open} />
</SidebarContent>
</Sidebar>
<Sidebar
side="left"
className="mt-14 h-[calc(100vh-56px)]"
collapsible="icon"
>
<SidebarContent className="gap-0! m-0! p-0!">
<FileActions isOpen={open} />
<RunsAndSubmission isOpen={open} />
<GraphComponents isOpen={open} />
</SidebarContent>
{open && (
<VerticalResizeHandle
side="right"
minWidth={MIN_WIDTH}
maxWidth={MAX_WIDTH}
onResize={setSidebarWidth}
/>
)}
</Sidebar>
</div>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,15 @@ const ComponentMarkup = ({
return (
<SidebarMenuItem
className={cn(
"pl-2 py-1.5",
"pl-2 py-1.5 w-full",
error
? "cursor-not-allowed opacity-60"
: "cursor-grab hover:bg-gray-100 active:bg-gray-200",
)}
draggable={!error && !isLoading}
onDragStart={onDragStart}
>
<InlineStack gap="2">
<InlineStack gap="2" wrap="nowrap" className="w-full min-w-0">
{isLoading ? (
<span className="text-gray-400 truncate text-sm">Loading...</span>
) : error ? (
Expand All @@ -189,10 +189,11 @@ const ComponentMarkup = ({
) : (
<InlineStack
wrap="nowrap"
className="w-full"
data-testid="component-item"
data-component-name={displayName}
>
<InlineStack gap="2" className="w-full" wrap="nowrap">
<InlineStack gap="2" className="flex-1 min-w-0" wrap="nowrap">
{isRemoteComponentLibrarySearchEnabled ? (
<ComponentIcon
name={iconName}
Expand All @@ -204,7 +205,7 @@ const ComponentMarkup = ({
)}

<div
className="flex flex-col w-32"
className="flex flex-col flex-1 min-w-0"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={onMouseClick}
Expand All @@ -216,11 +217,11 @@ const ComponentMarkup = ({
{displayName}
</span>
{author && author.length > 0 && (
<span className="truncate text-[10px] text-gray-500 max-w-25 font-mono">
<span className="truncate text-[10px] text-gray-500 font-mono">
{author}
</span>
)}
<span className="truncate text-[10px] text-gray-500 max-w-25 font-mono">
<span className="truncate text-[10px] text-gray-500 font-mono">
Ver: {digest}
</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radiogroup";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { SidebarMenu } from "@/components/ui/sidebar";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import {
Expand Down Expand Up @@ -265,31 +265,33 @@ const SearchResults = ({ searchResult }: SearchResultsProps) => {
data-testid="search-results-header"
>{`Search Results (${totalResults})`}</Text>
<Separator />
<ScrollArea className="h-[calc(100vh-400px)]" type="always">
{totalResults > 0 ? (
searchResult.map((folder) => (
<BlockStack key={folder.name}>
{folder.components && folder.components.length > 0 ? (
<>
{searchResult.length > 1 ? (
<Text tone="subdued" size="xs">
{folder.name}
</Text>
) : null}
{folder.components.map((component) => (
<ComponentMarkup
key={`${folder.name}-${component.digest}-${component.name}`}
component={component}
/>
))}
</>
) : null}
</BlockStack>
))
) : (
<Text tone="subdued">No results found</Text>
)}
</ScrollArea>
<div className="h-[calc(100vh-400px)] w-full overflow-y-auto overflow-x-hidden scrollbar-thin">
<SidebarMenu>
{totalResults > 0 ? (
searchResult.map((folder) => (
<BlockStack key={folder.name}>
{folder.components && folder.components.length > 0 ? (
<>
{searchResult.length > 1 ? (
<Text tone="subdued" size="xs">
{folder.name}
</Text>
) : null}
{folder.components.map((component) => (
<ComponentMarkup
key={`${folder.name}-${component.digest}-${component.name}`}
component={component}
/>
))}
</>
) : null}
</BlockStack>
))
) : (
<Text tone="subdued">No results found</Text>
)}
</SidebarMenu>
</div>
</BlockStack>
);
};
Expand Down
12 changes: 9 additions & 3 deletions src/components/ui/resize-handle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ interface ResizeHandleProps {
minWidth?: number;
maxWidth?: number;
side?: "left" | "right";
/** Optional callback for controlled resize. When provided, the component won't modify parent's style directly. */
onResize?: (width: number) => void;
}

export const VerticalResizeHandle = ({
minWidth = 200,
maxWidth = 600,
side = "left",
onResize,
}: ResizeHandleProps) => {
const parentElementRef = useRef<HTMLElement | null>(null);
const resizingRef = useRef<{ startX: number; startWidth: number } | null>(
Expand All @@ -38,9 +41,13 @@ export const VerticalResizeHandle = ({

const constrainedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));

parentElementRef.current.style.width = `${constrainedWidth}px`;
if (onResize) {
onResize(constrainedWidth);
} else {
parentElementRef.current.style.width = `${constrainedWidth}px`;
}
},
[minWidth, maxWidth, side],
[minWidth, maxWidth, side, onResize],
);

const handleMouseUp = useCallback(() => {
Expand All @@ -61,7 +68,6 @@ export const VerticalResizeHandle = ({
startX: e.clientX,
startWidth: parentElementRef.current.offsetWidth,
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
},
Expand Down
14 changes: 7 additions & 7 deletions src/components/ui/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function Sidebar({
<div
data-slot="sidebar-gap"
className={cn(
"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
"relative w-(--sidebar-width) bg-transparent",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset"
Expand All @@ -226,7 +226,7 @@ function Sidebar({
<div
data-slot="sidebar-container"
className={cn(
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) md:flex",
side === "left"
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
Expand Down Expand Up @@ -288,7 +288,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
onClick={toggleSidebar}
title="Toggle Sidebar"
className={cn(
"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-0.5 sm:flex",
"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-0.5 sm:flex",
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
Expand Down Expand Up @@ -402,7 +402,7 @@ function SidebarGroupLabel({
data-slot="sidebar-group-label"
data-sidebar="group-label"
className={cn(
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-sm font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-sm font-medium outline-hidden focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
className,
)}
Expand All @@ -423,7 +423,7 @@ function SidebarGroupAction({
data-slot="sidebar-group-action"
data-sidebar="group-action"
className={cn(
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 md:after:hidden",
"group-data-[collapsible=icon]:hidden",
Expand Down Expand Up @@ -471,7 +471,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
}

const sidebarMenuButtonVariants = cva(
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden cursor-pointer ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden cursor-pointer ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
{
variants: {
variant: {
Expand Down Expand Up @@ -562,7 +562,7 @@ function SidebarMenuAction({
data-slot="sidebar-menu-action"
data-sidebar="menu-action"
className={cn(
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 md:after:hidden",
"peer-data-[size=sm]/menu-button:top-1",
Expand Down
Loading