Skip to content
18 changes: 17 additions & 1 deletion apps/sim/app/_styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,27 @@
opacity: 0;
}

html[data-sidebar-collapsed] .sidebar-container span,
html[data-sidebar-collapsed] .sidebar-container .text-small {
opacity: 0;
}

.sidebar-container .sidebar-collapse-hide {
transition: opacity 60ms ease;
}

.sidebar-container[data-collapsed] .sidebar-collapse-hide {
.sidebar-container[data-collapsed] .sidebar-collapse-hide,
html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-hide {
opacity: 0;
}

.sidebar-container[data-collapsed] .sidebar-collapse-remove,
html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-remove {
display: none;
}

html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
width: 0;
opacity: 0;
}

Expand Down
1 change: 1 addition & 0 deletions apps/sim/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })

if (isCollapsed) {
document.documentElement.style.setProperty('--sidebar-width', '51px');
document.documentElement.setAttribute('data-sidebar-collapsed', '');
} else {
var width = state && state.sidebarWidth;
var maxSidebarWidth = window.innerWidth * 0.3;
Expand Down
3 changes: 3 additions & 0 deletions apps/sim/app/workspace/[workspaceId]/home/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { persistImportedWorkflow } from '@/lib/workflows/operations/import-export'
import { useChatHistory, useMarkTaskRead } from '@/hooks/queries/tasks'
import type { ChatContext } from '@/stores/panel'
import { useSidebarStore } from '@/stores/sidebar/store'
import {
MessageContent,
MothershipView,
Expand Down Expand Up @@ -166,6 +167,8 @@ export function Home({ chatId }: HomeProps = {}) {

const handleResourceEvent = useCallback(() => {
if (isResourceCollapsedRef.current) {
const { isCollapsed, toggleCollapsed } = useSidebarStore.getState()
if (!isCollapsed) toggleCollapsed()
setIsResourceCollapsed(false)
setIsResourceAnimatingIn(true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
useSidebarDragContextValue,
useWorkflowSelection,
} from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks'
import {
compareByOrder,
groupWorkflowsByFolder,
} from '@/app/workspace/[workspaceId]/w/components/sidebar/utils'
import { useFolders } from '@/hooks/queries/folders'
import { useFolderStore } from '@/stores/folders/store'
import type { FolderTreeNode } from '@/stores/folders/types'
Expand All @@ -22,17 +26,6 @@ const TREE_SPACING = {
INDENT_PER_LEVEL: 20,
} as const

function compareByOrder<T extends { sortOrder: number; createdAt?: Date; id: string }>(
a: T,
b: T
): number {
if (a.sortOrder !== b.sortOrder) return a.sortOrder - b.sortOrder
const timeA = a.createdAt?.getTime() ?? 0
const timeB = b.createdAt?.getTime() ?? 0
if (timeA !== timeB) return timeA - timeB
return a.id.localeCompare(b.id)
}

interface WorkflowListProps {
workspaceId: string
workflowId: string | undefined
Expand Down Expand Up @@ -129,21 +122,10 @@ export const WorkflowList = memo(function WorkflowList({
return activeWorkflow?.folderId || null
}, [workflowId, regularWorkflows, isLoading, foldersLoading])

const workflowsByFolder = useMemo(() => {
const grouped = regularWorkflows.reduce(
(acc, workflow) => {
const folderId = workflow.folderId || 'root'
if (!acc[folderId]) acc[folderId] = []
acc[folderId].push(workflow)
return acc
},
{} as Record<string, WorkflowMetadata[]>
)
for (const folderId of Object.keys(grouped)) {
grouped[folderId].sort(compareByOrder)
}
return grouped
}, [regularWorkflows])
const workflowsByFolder = useMemo(
() => groupWorkflowsByFolder(regularWorkflows),
[regularWorkflows]
)

const orderedWorkflowIds = useMemo(() => {
const ids: string[] = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { type DropIndicator, useDragDrop } from './use-drag-drop'
export { useFolderExpand } from './use-folder-expand'
export { useFolderOperations } from './use-folder-operations'
export { useFolderSelection } from './use-folder-selection'
export { useHoverMenu } from './use-hover-menu'
export { useItemDrag } from './use-item-drag'
export { useItemRename } from './use-item-rename'
export {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'

const CLOSE_DELAY_MS = 150

const preventAutoFocus = (e: Event) => e.preventDefault()

/**
* Manages hover-triggered dropdown menu state.
* Provides handlers for trigger and content mouse events with a delay
* to prevent flickering when moving between trigger and content.
*/
export function useHoverMenu() {
const [isOpen, setIsOpen] = useState(false)
const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)

const cancelClose = useCallback(() => {
if (closeTimerRef.current) {
clearTimeout(closeTimerRef.current)
closeTimerRef.current = null
}
}, [])

useEffect(() => {
return () => {
if (closeTimerRef.current) {
clearTimeout(closeTimerRef.current)
}
}
}, [])

const scheduleClose = useCallback(() => {
cancelClose()
closeTimerRef.current = setTimeout(() => setIsOpen(false), CLOSE_DELAY_MS)
}, [cancelClose])

const open = useCallback(() => {
cancelClose()
setIsOpen(true)
}, [cancelClose])

const close = useCallback(() => {
cancelClose()
setIsOpen(false)
}, [cancelClose])

const triggerProps = useMemo(
() => ({ onMouseEnter: open, onMouseLeave: scheduleClose }) as const,
[open, scheduleClose]
)

const contentProps = useMemo(
() =>
({
onMouseEnter: cancelClose,
onMouseLeave: scheduleClose,
onCloseAutoFocus: preventAutoFocus,
}) as const,
[cancelClose, scheduleClose]
)

return { isOpen, close, triggerProps, contentProps }
}
Loading
Loading