Skip to content

Commit 722f35f

Browse files
committed
refactor: popovers; improvement: notifications, diff controls, action bar
1 parent cb084a1 commit 722f35f

File tree

14 files changed

+305
-263
lines changed

14 files changed

+305
-263
lines changed

apps/sim/app/_styles/globals.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,20 @@ input[type="search"]::-ms-clear {
676676
}
677677
}
678678

679+
/**
680+
* Notification toast enter animation
681+
*/
682+
@keyframes notification-enter {
683+
from {
684+
opacity: 0;
685+
transform: translateX(-16px);
686+
}
687+
to {
688+
opacity: 1;
689+
transform: translateX(var(--stack-offset, 0px));
690+
}
691+
}
692+
679693
/**
680694
* @depricated
681695
* Legacy globals (light/dark) kept for backward-compat with old classes.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import {
1717
Redo,
1818
Tooltip,
1919
Undo,
20-
ZoomIn,
21-
ZoomOut,
2220
} from '@/components/emcn'
2321
import { useSession } from '@/lib/auth/auth-client'
2422
import { useUpdateGeneralSetting } from '@/hooks/queries/general-settings'
@@ -33,7 +31,6 @@ const logger = createLogger('ActionBar')
3331

3432
export function ActionBar() {
3533
const reactFlowInstance = useReactFlow()
36-
const { zoomIn, zoomOut } = reactFlowInstance
3734
const { fitViewToBounds } = useCanvasViewport(reactFlowInstance)
3835
const { mode, setMode } = useCanvasModeStore()
3936
const { undo, redo } = useCollaborativeWorkflow()
@@ -157,32 +154,6 @@ export function ActionBar() {
157154

158155
<div className='mx-[4px] h-[20px] w-[1px] bg-[var(--border)]' />
159156

160-
<Tooltip.Root>
161-
<Tooltip.Trigger asChild>
162-
<Button
163-
variant='ghost'
164-
className='h-[28px] w-[28px] rounded-[6px] p-0 hover:bg-[var(--surface-5)]'
165-
onClick={() => zoomOut()}
166-
>
167-
<ZoomOut className='h-[16px] w-[16px]' />
168-
</Button>
169-
</Tooltip.Trigger>
170-
<Tooltip.Content side='top'>Zoom out</Tooltip.Content>
171-
</Tooltip.Root>
172-
173-
<Tooltip.Root>
174-
<Tooltip.Trigger asChild>
175-
<Button
176-
variant='ghost'
177-
className='h-[28px] w-[28px] rounded-[6px] p-0 hover:bg-[var(--surface-5)]'
178-
onClick={() => zoomIn()}
179-
>
180-
<ZoomIn className='h-[16px] w-[16px]' />
181-
</Button>
182-
</Tooltip.Trigger>
183-
<Tooltip.Content side='top'>Zoom in</Tooltip.Content>
184-
</Tooltip.Root>
185-
186157
<Tooltip.Root>
187158
<Tooltip.Trigger asChild>
188159
<Button

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/block-context-menu.tsx renamed to apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/block-menu.tsx

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,55 @@
11
'use client'
22

3+
import type { RefObject } from 'react'
34
import {
45
Popover,
56
PopoverAnchor,
67
PopoverContent,
78
PopoverDivider,
89
PopoverItem,
910
} from '@/components/emcn'
10-
import type { BlockContextMenuProps } from './types'
11+
12+
/**
13+
* Block information for context menu actions
14+
*/
15+
export interface BlockInfo {
16+
id: string
17+
type: string
18+
enabled: boolean
19+
horizontalHandles: boolean
20+
parentId?: string
21+
parentType?: string
22+
}
23+
24+
/**
25+
* Props for BlockMenu component
26+
*/
27+
export interface BlockMenuProps {
28+
isOpen: boolean
29+
position: { x: number; y: number }
30+
menuRef: RefObject<HTMLDivElement | null>
31+
onClose: () => void
32+
selectedBlocks: BlockInfo[]
33+
onCopy: () => void
34+
onPaste: () => void
35+
onDuplicate: () => void
36+
onDelete: () => void
37+
onToggleEnabled: () => void
38+
onToggleHandles: () => void
39+
onRemoveFromSubflow: () => void
40+
onOpenEditor: () => void
41+
onRename: () => void
42+
hasClipboard?: boolean
43+
showRemoveFromSubflow?: boolean
44+
disableEdit?: boolean
45+
}
1146

1247
/**
1348
* Context menu for workflow block(s).
1449
* Displays block-specific actions in a popover at right-click position.
1550
* Supports multi-selection - actions apply to all selected blocks.
1651
*/
17-
export function BlockContextMenu({
52+
export function BlockMenu({
1853
isOpen,
1954
position,
2055
menuRef,
@@ -32,7 +67,7 @@ export function BlockContextMenu({
3267
hasClipboard = false,
3368
showRemoveFromSubflow = false,
3469
disableEdit = false,
35-
}: BlockContextMenuProps) {
70+
}: BlockMenuProps) {
3671
const isSingleBlock = selectedBlocks.length === 1
3772

3873
const allEnabled = selectedBlocks.every((b) => b.enabled)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { BlockInfo, BlockMenuProps } from './block-menu'
2+
export { BlockMenu } from './block-menu'
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
'use client'
2+
3+
import type { RefObject } from 'react'
4+
import {
5+
Popover,
6+
PopoverAnchor,
7+
PopoverContent,
8+
PopoverDivider,
9+
PopoverItem,
10+
} from '@/components/emcn'
11+
12+
/**
13+
* Props for CanvasMenu component
14+
*/
15+
export interface CanvasMenuProps {
16+
isOpen: boolean
17+
position: { x: number; y: number }
18+
menuRef: RefObject<HTMLDivElement | null>
19+
onClose: () => void
20+
onUndo: () => void
21+
onRedo: () => void
22+
onPaste: () => void
23+
onAddBlock: () => void
24+
onAutoLayout: () => void
25+
onFitToView: () => void
26+
onOpenLogs: () => void
27+
onToggleVariables: () => void
28+
onToggleChat: () => void
29+
onInvite: () => void
30+
isVariablesOpen?: boolean
31+
isChatOpen?: boolean
32+
hasClipboard?: boolean
33+
disableEdit?: boolean
34+
disableAdmin?: boolean
35+
canUndo?: boolean
36+
canRedo?: boolean
37+
isInvitationsDisabled?: boolean
38+
}
39+
40+
/**
41+
* Context menu for workflow canvas.
42+
* Displays canvas-level actions when right-clicking empty space.
43+
*/
44+
export function CanvasMenu({
45+
isOpen,
46+
position,
47+
menuRef,
48+
onClose,
49+
onUndo,
50+
onRedo,
51+
onPaste,
52+
onAddBlock,
53+
onAutoLayout,
54+
onFitToView,
55+
onOpenLogs,
56+
onToggleVariables,
57+
onToggleChat,
58+
onInvite,
59+
isVariablesOpen = false,
60+
isChatOpen = false,
61+
hasClipboard = false,
62+
disableEdit = false,
63+
disableAdmin = false,
64+
canUndo = false,
65+
canRedo = false,
66+
isInvitationsDisabled = false,
67+
}: CanvasMenuProps) {
68+
return (
69+
<Popover
70+
open={isOpen}
71+
onOpenChange={(open) => !open && onClose()}
72+
variant='secondary'
73+
size='sm'
74+
colorScheme='inverted'
75+
>
76+
<PopoverAnchor
77+
style={{
78+
position: 'fixed',
79+
left: `${position.x}px`,
80+
top: `${position.y}px`,
81+
width: '1px',
82+
height: '1px',
83+
}}
84+
/>
85+
<PopoverContent ref={menuRef} align='start' side='bottom' sideOffset={4}>
86+
{/* History actions */}
87+
<PopoverItem
88+
className='group'
89+
disabled={disableEdit || !canUndo}
90+
onClick={() => {
91+
onUndo()
92+
onClose()
93+
}}
94+
>
95+
<span>Undo</span>
96+
<span className='ml-auto opacity-70 group-hover:opacity-100'>⌘Z</span>
97+
</PopoverItem>
98+
<PopoverItem
99+
className='group'
100+
disabled={disableEdit || !canRedo}
101+
onClick={() => {
102+
onRedo()
103+
onClose()
104+
}}
105+
>
106+
<span>Redo</span>
107+
<span className='ml-auto opacity-70 group-hover:opacity-100'>⌘⇧Z</span>
108+
</PopoverItem>
109+
110+
{/* Edit and creation actions */}
111+
<PopoverDivider />
112+
<PopoverItem
113+
className='group'
114+
disabled={disableEdit || !hasClipboard}
115+
onClick={() => {
116+
onPaste()
117+
onClose()
118+
}}
119+
>
120+
<span>Paste</span>
121+
<span className='ml-auto opacity-70 group-hover:opacity-100'>⌘V</span>
122+
</PopoverItem>
123+
<PopoverItem
124+
className='group'
125+
disabled={disableEdit}
126+
onClick={() => {
127+
onAddBlock()
128+
onClose()
129+
}}
130+
>
131+
<span>Add Block</span>
132+
<span className='ml-auto opacity-70 group-hover:opacity-100'>⌘K</span>
133+
</PopoverItem>
134+
<PopoverItem
135+
className='group'
136+
disabled={disableEdit}
137+
onClick={() => {
138+
onAutoLayout()
139+
onClose()
140+
}}
141+
>
142+
<span>Auto-layout</span>
143+
<span className='ml-auto opacity-70 group-hover:opacity-100'>⇧L</span>
144+
</PopoverItem>
145+
<PopoverItem
146+
onClick={() => {
147+
onFitToView()
148+
onClose()
149+
}}
150+
>
151+
Fit to View
152+
</PopoverItem>
153+
154+
{/* Navigation actions */}
155+
<PopoverDivider />
156+
<PopoverItem
157+
className='group'
158+
onClick={() => {
159+
onOpenLogs()
160+
onClose()
161+
}}
162+
>
163+
<span>Open Logs</span>
164+
<span className='ml-auto opacity-70 group-hover:opacity-100'>⌘L</span>
165+
</PopoverItem>
166+
<PopoverItem
167+
onClick={() => {
168+
onToggleVariables()
169+
onClose()
170+
}}
171+
>
172+
{isVariablesOpen ? 'Close Variables' : 'Open Variables'}
173+
</PopoverItem>
174+
<PopoverItem
175+
onClick={() => {
176+
onToggleChat()
177+
onClose()
178+
}}
179+
>
180+
{isChatOpen ? 'Close Chat' : 'Open Chat'}
181+
</PopoverItem>
182+
183+
{/* Admin action - hidden when invitations are disabled */}
184+
{!isInvitationsDisabled && (
185+
<>
186+
<PopoverDivider />
187+
<PopoverItem
188+
disabled={disableAdmin}
189+
onClick={() => {
190+
onInvite()
191+
onClose()
192+
}}
193+
>
194+
Invite to Workspace
195+
</PopoverItem>
196+
</>
197+
)}
198+
</PopoverContent>
199+
</Popover>
200+
)
201+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { CanvasMenuProps } from './canvas-menu'
2+
export { CanvasMenu } from './canvas-menu'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/index.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)