Skip to content

Commit 83fe6d3

Browse files
committed
refactor(37943): merge the two toolbars
1 parent f4a60f0 commit 83fe6d3

File tree

4 files changed

+199
-246
lines changed

4 files changed

+199
-246
lines changed

hivemq-edge-frontend/src/modules/Workspace/components/ReactFlowWrapper.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import StatusListener from '@/modules/Workspace/components/controls/StatusListen
1717
import CanvasControls from '@/modules/Workspace/components/controls/CanvasControls.tsx'
1818
import SelectionListener from '@/modules/Workspace/components/controls/SelectionListener.tsx'
1919
import CanvasToolbar from '@/modules/Workspace/components/controls/CanvasToolbar.tsx'
20-
import LayoutControlsPanel from '@/modules/Workspace/components/controls/LayoutControlsPanel.tsx'
2120
import MonitoringEdge from '@/modules/Workspace/components/edges/MonitoringEdge.tsx'
2221
import {
2322
NodeAdapter,
@@ -117,7 +116,6 @@ const ReactFlowWrapper = () => {
117116
aria-label={t('workspace.canvas.aria-label')}
118117
>
119118
<Box role="group" aria-label={t('workspace.canvas.toolbar.container')} aria-controls="edge-workspace-canvas">
120-
<LayoutControlsPanel />
121119
<CanvasToolbar />
122120
<SelectionListener />
123121
<StatusListener />

hivemq-edge-frontend/src/modules/Workspace/components/controls/CanvasToolbar.tsx

Lines changed: 199 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,60 @@
11
import type { FC } from 'react'
22
import { useEffect, useState } from 'react'
33
import { useTranslation } from 'react-i18next'
4-
import { Box, HStack, Icon } from '@chakra-ui/react'
4+
import {
5+
Box,
6+
VStack,
7+
Icon,
8+
Divider,
9+
Tooltip,
10+
IconButton as ChakraIconButton,
11+
useDisclosure,
12+
useBreakpointValue,
13+
} from '@chakra-ui/react'
514
import { ChevronLeftIcon, ChevronRightIcon, SearchIcon } from '@chakra-ui/icons'
15+
import { LuNetwork, LuSettings } from 'react-icons/lu'
616

717
import IconButton from '@/components/Chakra/IconButton.tsx'
818
import Panel from '@/components/react-flow/Panel.tsx'
919
import SearchEntities from '@/modules/Workspace/components/filters/SearchEntities.tsx'
1020
import DrawerFilterToolbox from '@/modules/Workspace/components/filters/DrawerFilterToolbox.tsx'
11-
import { ANIMATION, TOOLBAR } from '@/modules/Theme/utils.ts'
21+
import LayoutSelector from '@/modules/Workspace/components/layout/LayoutSelector.tsx'
22+
import ApplyLayoutButton from '@/modules/Workspace/components/layout/ApplyLayoutButton.tsx'
23+
import LayoutPresetsManager from '@/modules/Workspace/components/layout/LayoutPresetsManager.tsx'
24+
import LayoutOptionsDrawer from '@/modules/Workspace/components/layout/LayoutOptionsDrawer.tsx'
25+
import { useLayoutEngine } from '@/modules/Workspace/hooks/useLayoutEngine'
26+
import useWorkspaceStore from '@/modules/Workspace/hooks/useWorkspaceStore'
27+
import { useKeyboardShortcut } from '@/hooks/useKeyboardShortcut'
28+
import config from '@/config'
29+
import { ANIMATION } from '@/modules/Theme/utils.ts'
1230

1331
const CanvasToolbar: FC = () => {
1432
const { t } = useTranslation()
1533
const [expanded, setExpanded] = useState(false)
1634
const [contentVisible, setContentVisible] = useState(false)
1735

36+
const dividerOrientation = useBreakpointValue<'horizontal' | 'vertical'>({
37+
base: 'horizontal',
38+
xl: 'vertical',
39+
})
40+
const tooltipPlacement = useBreakpointValue<'top' | 'bottom'>({
41+
base: 'top',
42+
xl: 'bottom',
43+
})
44+
45+
const { applyLayout } = useLayoutEngine()
46+
const { layoutConfig } = useWorkspaceStore()
47+
const { isOpen: isLayoutDrawerOpen, onOpen: onLayoutDrawerOpen, onClose: onLayoutDrawerClose } = useDisclosure()
48+
49+
useKeyboardShortcut({
50+
key: 'l',
51+
ctrl: true,
52+
callback: () => {
53+
applyLayout()
54+
},
55+
description: 'Apply current layout',
56+
})
57+
1858
useEffect(() => {
1959
let timeout: NodeJS.Timeout
2060
if (expanded) {
@@ -26,64 +66,169 @@ const CanvasToolbar: FC = () => {
2666
}, [expanded])
2767

2868
return (
29-
<Panel
30-
position="top-right"
31-
data-testid="content-toolbar"
32-
role="group"
33-
aria-label={t('workspace.canvas.toolbar.search-filter')}
34-
>
35-
<Box
36-
m="1.2px"
37-
display="flex"
38-
alignItems="center"
39-
transition="max-width 0.4s cubic-bezier(0.4,0,0.2,1)"
40-
maxWidth={expanded ? TOOLBAR.MAX_WIDTH : TOOLBAR.MIN_WIDTH}
41-
width="auto"
42-
minHeight="40px"
43-
boxShadow="md"
44-
borderRadius="md"
45-
position="relative"
46-
overflow="hidden"
47-
_focusWithin={{ boxShadow: 'outline' }}
69+
<>
70+
<Panel
71+
position="top-left"
72+
data-testid="content-toolbar"
73+
role="group"
74+
aria-label={t('workspace.canvas.toolbar.search-filter')}
4875
>
49-
<IconButton
50-
data-testid="toolbox-search-expand"
51-
aria-label={t('workspace.controls.expand')}
52-
icon={
53-
<>
54-
<Icon as={ChevronLeftIcon} boxSize="24px" />
55-
<SearchIcon />
56-
</>
57-
}
58-
onClick={() => setExpanded(true)}
59-
variant="ghost"
60-
size="sm"
61-
mx={2}
62-
display={expanded ? 'none' : 'inline-flex'}
63-
/>
64-
<HStack
65-
spacing={2}
66-
ml={2}
67-
width="100%"
68-
opacity={expanded ? 1 : 0}
69-
transition="opacity 0.4s cubic-bezier(0.4,0,0.2,1)"
70-
pointerEvents={contentVisible ? 'auto' : 'none'}
71-
style={{ display: contentVisible ? 'flex' : 'none' }}
76+
<Box
77+
m="1.2px"
78+
display="flex"
79+
flexDirection={{ base: 'column', xl: 'row' }}
80+
transition="all 0.4s cubic-bezier(0.4,0,0.2,1)"
81+
// maxWidth={{
82+
// base: expanded ? '100vw' : '56px',
83+
// md: expanded ? '90vw' : '56px',
84+
// xl: expanded ? TOOLBAR.MAX_WIDTH : TOOLBAR.MIN_WIDTH,
85+
// }}
86+
// width={{
87+
// base: expanded ? '100vw' : 'auto',
88+
// md: expanded ? 'auto' : 'auto',
89+
// }}
90+
// minHeight={{ base: '48px', md: '40px' }}
91+
boxShadow="md"
92+
borderRadius="md"
93+
position="relative"
94+
overflow="hidden"
95+
bg="white"
96+
_dark={{ bg: 'gray.800' }}
97+
_focusWithin={{ boxShadow: 'outline' }}
7298
>
73-
<SearchEntities />
74-
<DrawerFilterToolbox />
7599
<IconButton
76-
data-testid="toolbox-search-collapse"
77-
aria-label={t('workspace.controls.collapse')}
78-
icon={<Icon as={ChevronRightIcon} boxSize="24px" />}
79-
onClick={() => setExpanded(false)}
100+
data-testid="toolbox-search-expand"
101+
aria-label={t('workspace.controls.expand')}
102+
aria-expanded="false"
103+
aria-controls="workspace-toolbar-content"
104+
icon={
105+
<>
106+
<SearchIcon mr="2px" />
107+
<Icon as={LuNetwork} boxSize="18px" />
108+
<Icon
109+
as={ChevronRightIcon}
110+
boxSize="24px"
111+
// transform={{ base: 'rotate(-90deg)', xl: 'rotate(0deg)' }}
112+
/>
113+
</>
114+
}
115+
onClick={() => setExpanded(true)}
80116
variant="ghost"
81117
size="sm"
82-
mr={2}
118+
m={2}
119+
// minWidth="120px"
120+
display={expanded ? 'none' : 'inline-flex'}
83121
/>
84-
</HStack>
85-
</Box>
86-
</Panel>
122+
123+
<Box
124+
id="workspace-toolbar-content"
125+
role="region"
126+
aria-label={t('workspace.canvas.toolbar.search-filter')}
127+
display={contentVisible ? 'flex' : 'none'}
128+
flexDirection={{ base: 'column', xl: 'row' }}
129+
gap={{ base: 3, xl: 2 }}
130+
p={{ base: 3, xl: 2 }}
131+
width="100%"
132+
opacity={expanded ? 1 : 0}
133+
transition="opacity 0.4s cubic-bezier(0.4,0,0.2,1)"
134+
pointerEvents={contentVisible ? 'auto' : 'none'}
135+
>
136+
<VStack
137+
spacing={{ base: 2, xl: 0 }}
138+
align="stretch"
139+
flex={{ base: '1', xl: 'initial' }}
140+
sx={{
141+
'& > *': {
142+
width: { base: '100%', xl: 'auto' },
143+
},
144+
}}
145+
>
146+
<Box display="flex" flexDirection={{ base: 'column', md: 'row' }} gap={2}>
147+
<SearchEntities />
148+
<DrawerFilterToolbox />
149+
</Box>
150+
</VStack>
151+
{config.features.WORKSPACE_AUTO_LAYOUT && (
152+
<Divider
153+
orientation={dividerOrientation}
154+
// h={{ base: 'auto', xl: '24px' }}
155+
// w={{ base: 'auto', xl: 'auto' }}
156+
borderColor="gray.300"
157+
_dark={{ borderColor: 'gray.600' }}
158+
/>
159+
)}
160+
{config.features.WORKSPACE_AUTO_LAYOUT && (
161+
<VStack
162+
role="region"
163+
data-testid="layout-controls-panel"
164+
aria-label={t('workspace.autoLayout.controls.aria-label')}
165+
spacing={{ base: 2, xl: 0 }}
166+
align="stretch"
167+
flex={{ base: '1', xl: 'initial' }}
168+
>
169+
<Box
170+
display="flex"
171+
flexDirection={{ base: 'column', md: 'row', xl: 'row' }}
172+
gap={2}
173+
sx={{
174+
'& > *': {
175+
width: { base: '100%', md: 'auto' },
176+
},
177+
}}
178+
>
179+
<LayoutSelector />
180+
<ApplyLayoutButton />
181+
<Box display="flex" gap={2} width="fit-content">
182+
<LayoutPresetsManager />
183+
<Tooltip label={t('workspace.autoLayout.options.title')} placement={tooltipPlacement}>
184+
<ChakraIconButton
185+
data-testid="workspace-layout-options"
186+
aria-label={t('workspace.autoLayout.options.title')}
187+
icon={<Icon as={LuSettings} />}
188+
size="sm"
189+
variant="ghost"
190+
onClick={onLayoutDrawerOpen}
191+
width={{ base: '100%', md: 'auto' }}
192+
/>
193+
</Tooltip>
194+
</Box>
195+
</Box>
196+
</VStack>
197+
)}
198+
<Divider
199+
orientation={dividerOrientation}
200+
// h={{ base: 'auto', xl: '24px' }}
201+
// w={{ base: 'auto', xl: 'auto' }}
202+
borderColor="gray.300"
203+
_dark={{ borderColor: 'gray.600' }}
204+
/>
205+
<IconButton
206+
data-testid="toolbox-search-collapse"
207+
aria-label={t('workspace.controls.collapse')}
208+
aria-expanded="true"
209+
aria-controls="workspace-toolbar-content"
210+
icon={<Icon as={ChevronLeftIcon} boxSize="24px" />}
211+
onClick={() => setExpanded(false)}
212+
variant="ghost"
213+
size="sm"
214+
alignSelf={{ base: 'center', xl: 'center' }}
215+
mt={{ base: 2, xl: 0 }}
216+
mb={{ base: 0, xl: 0 }}
217+
/>
218+
</Box>
219+
</Box>
220+
</Panel>
221+
222+
{/* Layout Options Drawer */}
223+
{config.features.WORKSPACE_AUTO_LAYOUT && (
224+
<LayoutOptionsDrawer
225+
isOpen={isLayoutDrawerOpen}
226+
onClose={onLayoutDrawerClose}
227+
algorithmType={layoutConfig.currentAlgorithm}
228+
options={layoutConfig.options}
229+
/>
230+
)}
231+
</>
87232
)
88233
}
89234

0 commit comments

Comments
 (0)