diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-08-30 19:07:05 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2023-08-30 19:07:05 +0200 |
commit | e41e0391ba843b85a5b2890db95121fa39426a05 (patch) | |
tree | 4927c919cf3077ba9abb2cf31155de4d227b3119 /subprojects/frontend | |
parent | refactor(frontend): filter dialog formatting (diff) | |
download | refinery-e41e0391ba843b85a5b2890db95121fa39426a05.tar.gz refinery-e41e0391ba843b85a5b2890db95121fa39426a05.tar.zst refinery-e41e0391ba843b85a5b2890db95121fa39426a05.zip |
feat(frontend): window pane switcher
Diffstat (limited to 'subprojects/frontend')
-rw-r--r-- | subprojects/frontend/package.json | 2 | ||||
-rw-r--r-- | subprojects/frontend/src/DirectionalSplitPane.tsx | 159 | ||||
-rw-r--r-- | subprojects/frontend/src/PaneButtons.tsx | 96 | ||||
-rw-r--r-- | subprojects/frontend/src/Refinery.tsx | 142 | ||||
-rw-r--r-- | subprojects/frontend/src/TopBar.tsx | 91 | ||||
-rw-r--r-- | subprojects/frontend/src/WorkArea.tsx | 33 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorButtons.tsx | 2 | ||||
-rw-r--r-- | subprojects/frontend/src/table/TableArea.tsx | 9 | ||||
-rw-r--r-- | subprojects/frontend/src/table/TablePane.tsx | 28 | ||||
-rw-r--r-- | subprojects/frontend/src/theme/ThemeStore.ts | 48 |
10 files changed, 415 insertions, 195 deletions
diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json index 812099bf..647b9a31 100644 --- a/subprojects/frontend/package.json +++ b/subprojects/frontend/package.json | |||
@@ -46,6 +46,8 @@ | |||
46 | "@material-icons/svg": "^1.0.33", | 46 | "@material-icons/svg": "^1.0.33", |
47 | "@mui/icons-material": "5.14.7", | 47 | "@mui/icons-material": "5.14.7", |
48 | "@mui/material": "5.14.7", | 48 | "@mui/material": "5.14.7", |
49 | "@mui/system": "^5.14.7", | ||
50 | "@mui/x-data-grid": "^6.12.0", | ||
49 | "ansi-styles": "^6.2.1", | 51 | "ansi-styles": "^6.2.1", |
50 | "csstype": "^3.1.2", | 52 | "csstype": "^3.1.2", |
51 | "d3": "^7.8.5", | 53 | "d3": "^7.8.5", |
diff --git a/subprojects/frontend/src/DirectionalSplitPane.tsx b/subprojects/frontend/src/DirectionalSplitPane.tsx new file mode 100644 index 00000000..59c8b739 --- /dev/null +++ b/subprojects/frontend/src/DirectionalSplitPane.tsx | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; | ||
8 | import MoreVertIcon from '@mui/icons-material/MoreVert'; | ||
9 | import Box from '@mui/material/Box'; | ||
10 | import Stack from '@mui/material/Stack'; | ||
11 | import { alpha, useTheme } from '@mui/material/styles'; | ||
12 | import { useCallback, useRef, useState } from 'react'; | ||
13 | import { useResizeDetector } from 'react-resize-detector'; | ||
14 | |||
15 | export default function DirectionalSplitPane({ | ||
16 | primary: left, | ||
17 | secondary: right, | ||
18 | primaryOnly: showLeftOnly, | ||
19 | secondaryOnly: showRightOnly, | ||
20 | }: { | ||
21 | primary: React.ReactNode; | ||
22 | secondary: React.ReactNode; | ||
23 | primaryOnly?: boolean; | ||
24 | secondaryOnly?: boolean; | ||
25 | }): JSX.Element { | ||
26 | const theme = useTheme(); | ||
27 | const stackRef = useRef<HTMLDivElement | null>(null); | ||
28 | const { ref: resizeRef, width, height } = useResizeDetector(); | ||
29 | const sliderRef = useRef<HTMLDivElement>(null); | ||
30 | const [resizing, setResizing] = useState(false); | ||
31 | const [fraction, setFraction] = useState(0.5); | ||
32 | |||
33 | const horizontalSplit = | ||
34 | width !== undefined && height !== undefined && height > width; | ||
35 | const direction = horizontalSplit ? 'column' : 'row'; | ||
36 | const axis = horizontalSplit ? 'height' : 'width'; | ||
37 | const primarySize = showLeftOnly | ||
38 | ? '100%' | ||
39 | : `calc(${fraction * 100}% - 0.5px)`; | ||
40 | const secondarySize = showRightOnly | ||
41 | ? '100%' | ||
42 | : `calc(${(1 - fraction) * 100}% - 0.5px)`; | ||
43 | const ref = useCallback( | ||
44 | (element: HTMLDivElement | null) => { | ||
45 | resizeRef(element); | ||
46 | stackRef.current = element; | ||
47 | }, | ||
48 | [resizeRef], | ||
49 | ); | ||
50 | |||
51 | return ( | ||
52 | <Stack | ||
53 | direction={direction} | ||
54 | height="100%" | ||
55 | width="100%" | ||
56 | overflow="hidden" | ||
57 | ref={ref} | ||
58 | > | ||
59 | {!showRightOnly && <Box {...{ [axis]: primarySize }}>{left}</Box>} | ||
60 | <Box | ||
61 | sx={{ | ||
62 | overflow: 'visible', | ||
63 | position: 'relative', | ||
64 | [axis]: '0px', | ||
65 | display: showLeftOnly || showRightOnly ? 'none' : 'flex', | ||
66 | flexDirection: direction, | ||
67 | [horizontalSplit | ||
68 | ? 'borderBottom' | ||
69 | : 'borderRight']: `1px solid ${theme.palette.outer.border}`, | ||
70 | }} | ||
71 | > | ||
72 | <Box | ||
73 | ref={sliderRef} | ||
74 | sx={{ | ||
75 | display: 'flex', | ||
76 | position: 'absolute', | ||
77 | [axis]: theme.spacing(2), | ||
78 | ...(horizontalSplit | ||
79 | ? { | ||
80 | top: theme.spacing(-1), | ||
81 | left: 0, | ||
82 | right: 0, | ||
83 | transform: 'translateY(0.5px)', | ||
84 | } | ||
85 | : { | ||
86 | left: theme.spacing(-1), | ||
87 | top: 0, | ||
88 | bottom: 0, | ||
89 | transform: 'translateX(0.5px)', | ||
90 | }), | ||
91 | zIndex: 999, | ||
92 | alignItems: 'center', | ||
93 | justifyContent: 'center', | ||
94 | color: theme.palette.text.secondary, | ||
95 | cursor: horizontalSplit ? 'ns-resize' : 'ew-resize', | ||
96 | '.MuiSvgIcon-root': { | ||
97 | opacity: resizing ? 1 : 0, | ||
98 | }, | ||
99 | ...(resizing | ||
100 | ? { | ||
101 | background: alpha( | ||
102 | theme.palette.text.primary, | ||
103 | theme.palette.action.activatedOpacity, | ||
104 | ), | ||
105 | } | ||
106 | : { | ||
107 | '&:hover': { | ||
108 | background: alpha( | ||
109 | theme.palette.text.primary, | ||
110 | theme.palette.action.hoverOpacity, | ||
111 | ), | ||
112 | '.MuiSvgIcon-root': { | ||
113 | opacity: 1, | ||
114 | }, | ||
115 | }, | ||
116 | }), | ||
117 | }} | ||
118 | onPointerDown={(event) => { | ||
119 | if (event.button !== 0) { | ||
120 | return; | ||
121 | } | ||
122 | sliderRef.current?.setPointerCapture(event.pointerId); | ||
123 | setResizing(true); | ||
124 | }} | ||
125 | onPointerUp={(event) => { | ||
126 | if (event.button !== 0) { | ||
127 | return; | ||
128 | } | ||
129 | sliderRef.current?.releasePointerCapture(event.pointerId); | ||
130 | setResizing(false); | ||
131 | }} | ||
132 | onPointerMove={(event) => { | ||
133 | if (!resizing) { | ||
134 | return; | ||
135 | } | ||
136 | const container = stackRef.current; | ||
137 | if (container === null) { | ||
138 | return; | ||
139 | } | ||
140 | const rect = container.getBoundingClientRect(); | ||
141 | const newFraction = horizontalSplit | ||
142 | ? (event.clientY - rect.top) / rect.height | ||
143 | : (event.clientX - rect.left) / rect.width; | ||
144 | setFraction(Math.min(0.9, Math.max(0.1, newFraction))); | ||
145 | }} | ||
146 | onDoubleClick={() => setFraction(0.5)} | ||
147 | > | ||
148 | {horizontalSplit ? <MoreHorizIcon /> : <MoreVertIcon />} | ||
149 | </Box> | ||
150 | </Box> | ||
151 | {!showLeftOnly && <Box {...{ [axis]: secondarySize }}>{right}</Box>} | ||
152 | </Stack> | ||
153 | ); | ||
154 | } | ||
155 | |||
156 | DirectionalSplitPane.defaultProps = { | ||
157 | primaryOnly: false, | ||
158 | secondaryOnly: false, | ||
159 | }; | ||
diff --git a/subprojects/frontend/src/PaneButtons.tsx b/subprojects/frontend/src/PaneButtons.tsx new file mode 100644 index 00000000..7b83171d --- /dev/null +++ b/subprojects/frontend/src/PaneButtons.tsx | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import CodeIcon from '@mui/icons-material/Code'; | ||
8 | import SchemaRoundedIcon from '@mui/icons-material/SchemaRounded'; | ||
9 | import TableChartIcon from '@mui/icons-material/TableChart'; | ||
10 | import ToggleButton from '@mui/material/ToggleButton'; | ||
11 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | ||
12 | import { observer } from 'mobx-react-lite'; | ||
13 | |||
14 | import type ThemeStore from './theme/ThemeStore'; | ||
15 | |||
16 | function PaneButtons({ | ||
17 | themeStore, | ||
18 | hideLabel, | ||
19 | }: { | ||
20 | themeStore: ThemeStore; | ||
21 | hideLabel?: boolean; | ||
22 | }): JSX.Element { | ||
23 | return ( | ||
24 | <ToggleButtonGroup | ||
25 | size={hideLabel ? 'small' : 'medium'} | ||
26 | className="rounded" | ||
27 | sx={{ | ||
28 | '.MuiToggleButton-root': { | ||
29 | ...(hideLabel | ||
30 | ? {} | ||
31 | : { | ||
32 | paddingBlock: '6px', | ||
33 | }), | ||
34 | fontSize: '1rem', | ||
35 | lineHeight: '1.5', | ||
36 | }, | ||
37 | ...(hideLabel | ||
38 | ? {} | ||
39 | : { | ||
40 | '& svg': { | ||
41 | margin: '0 6px 0 -4px', | ||
42 | }, | ||
43 | }), | ||
44 | }} | ||
45 | > | ||
46 | <ToggleButton | ||
47 | value="code" | ||
48 | selected={themeStore.showCode} | ||
49 | onClick={(event) => { | ||
50 | if (event.shiftKey || event.ctrlKey) { | ||
51 | themeStore.setSelectedPane('code'); | ||
52 | } else { | ||
53 | themeStore.toggleCode(); | ||
54 | } | ||
55 | }} | ||
56 | > | ||
57 | <CodeIcon fontSize="small" /> | ||
58 | {!hideLabel && 'Code'} | ||
59 | </ToggleButton> | ||
60 | <ToggleButton | ||
61 | value="graph" | ||
62 | selected={themeStore.showGraph} | ||
63 | onClick={(event) => { | ||
64 | if (event.shiftKey || event.ctrlKey) { | ||
65 | themeStore.setSelectedPane('graph'); | ||
66 | } else { | ||
67 | themeStore.toggleGraph(); | ||
68 | } | ||
69 | }} | ||
70 | > | ||
71 | <SchemaRoundedIcon fontSize="small" /> | ||
72 | {!hideLabel && 'Graph'} | ||
73 | </ToggleButton> | ||
74 | <ToggleButton | ||
75 | value="table" | ||
76 | selected={themeStore.showTable} | ||
77 | onClick={(event) => { | ||
78 | if (event.shiftKey || event.ctrlKey) { | ||
79 | themeStore.setSelectedPane('table'); | ||
80 | } else { | ||
81 | themeStore.toggleTable(); | ||
82 | } | ||
83 | }} | ||
84 | > | ||
85 | <TableChartIcon fontSize="small" /> | ||
86 | {!hideLabel && 'Table'} | ||
87 | </ToggleButton> | ||
88 | </ToggleButtonGroup> | ||
89 | ); | ||
90 | } | ||
91 | |||
92 | PaneButtons.defaultProps = { | ||
93 | hideLabel: false, | ||
94 | }; | ||
95 | |||
96 | export default observer(PaneButtons); | ||
diff --git a/subprojects/frontend/src/Refinery.tsx b/subprojects/frontend/src/Refinery.tsx index 099646f0..5ad16000 100644 --- a/subprojects/frontend/src/Refinery.tsx +++ b/subprojects/frontend/src/Refinery.tsx | |||
@@ -4,151 +4,13 @@ | |||
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | 6 | ||
7 | import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; | ||
8 | import MoreVertIcon from '@mui/icons-material/MoreVert'; | ||
9 | import Box from '@mui/material/Box'; | ||
10 | import Grow from '@mui/material/Grow'; | 7 | import Grow from '@mui/material/Grow'; |
11 | import Stack from '@mui/material/Stack'; | 8 | import Stack from '@mui/material/Stack'; |
12 | import { alpha, useTheme } from '@mui/material/styles'; | ||
13 | import { SnackbarProvider } from 'notistack'; | 9 | import { SnackbarProvider } from 'notistack'; |
14 | import { memo, useRef, useState } from 'react'; | ||
15 | import { useResizeDetector } from 'react-resize-detector'; | ||
16 | 10 | ||
17 | import TopBar from './TopBar'; | 11 | import TopBar from './TopBar'; |
18 | import UpdateNotification from './UpdateNotification'; | 12 | import UpdateNotification from './UpdateNotification'; |
19 | import EditorPane from './editor/EditorPane'; | 13 | import WorkArea from './WorkArea'; |
20 | import GraphPane from './graph/GraphPane'; | ||
21 | |||
22 | const DirectionalSplitPane = memo(function SplitPanel({ | ||
23 | horizontalSplit, | ||
24 | }: { | ||
25 | horizontalSplit: boolean; | ||
26 | }): JSX.Element { | ||
27 | const theme = useTheme(); | ||
28 | const stackRef = useRef<HTMLDivElement>(null); | ||
29 | const sliderRef = useRef<HTMLDivElement>(null); | ||
30 | const [resizing, setResizing] = useState(false); | ||
31 | const [fraction, setFraction] = useState(0.5); | ||
32 | |||
33 | const direction = horizontalSplit ? 'column' : 'row'; | ||
34 | const axis = horizontalSplit ? 'height' : 'width'; | ||
35 | const primarySize = `calc(${fraction * 100}% - 0.5px)`; | ||
36 | const secondarySize = `calc(${(1 - fraction) * 100}% - 0.5px)`; | ||
37 | |||
38 | return ( | ||
39 | <Stack direction={direction} height="100%" overflow="hidden" ref={stackRef}> | ||
40 | <Box {...{ [axis]: primarySize }}> | ||
41 | <EditorPane /> | ||
42 | </Box> | ||
43 | <Box | ||
44 | sx={{ | ||
45 | overflow: 'visible', | ||
46 | position: 'relative', | ||
47 | [axis]: '0px', | ||
48 | display: 'flex', | ||
49 | flexDirection: direction, | ||
50 | [horizontalSplit | ||
51 | ? 'borderBottom' | ||
52 | : 'borderRight']: `1px solid ${theme.palette.outer.border}`, | ||
53 | }} | ||
54 | > | ||
55 | <Box | ||
56 | ref={sliderRef} | ||
57 | sx={{ | ||
58 | display: 'flex', | ||
59 | position: 'absolute', | ||
60 | [axis]: theme.spacing(2), | ||
61 | ...(horizontalSplit | ||
62 | ? { | ||
63 | top: theme.spacing(-1), | ||
64 | left: 0, | ||
65 | right: 0, | ||
66 | transform: 'translateY(0.5px)', | ||
67 | } | ||
68 | : { | ||
69 | left: theme.spacing(-1), | ||
70 | top: 0, | ||
71 | bottom: 0, | ||
72 | transform: 'translateX(0.5px)', | ||
73 | }), | ||
74 | zIndex: 999, | ||
75 | alignItems: 'center', | ||
76 | justifyContent: 'center', | ||
77 | color: theme.palette.text.secondary, | ||
78 | cursor: horizontalSplit ? 'ns-resize' : 'ew-resize', | ||
79 | '.MuiSvgIcon-root': { | ||
80 | opacity: resizing ? 1 : 0, | ||
81 | }, | ||
82 | ...(resizing | ||
83 | ? { | ||
84 | background: alpha( | ||
85 | theme.palette.text.primary, | ||
86 | theme.palette.action.activatedOpacity, | ||
87 | ), | ||
88 | } | ||
89 | : { | ||
90 | '&:hover': { | ||
91 | background: alpha( | ||
92 | theme.palette.text.primary, | ||
93 | theme.palette.action.hoverOpacity, | ||
94 | ), | ||
95 | '.MuiSvgIcon-root': { | ||
96 | opacity: 1, | ||
97 | }, | ||
98 | }, | ||
99 | }), | ||
100 | }} | ||
101 | onPointerDown={(event) => { | ||
102 | if (event.button !== 0) { | ||
103 | return; | ||
104 | } | ||
105 | sliderRef.current?.setPointerCapture(event.pointerId); | ||
106 | setResizing(true); | ||
107 | }} | ||
108 | onPointerUp={(event) => { | ||
109 | if (event.button !== 0) { | ||
110 | return; | ||
111 | } | ||
112 | sliderRef.current?.releasePointerCapture(event.pointerId); | ||
113 | setResizing(false); | ||
114 | }} | ||
115 | onPointerMove={(event) => { | ||
116 | if (!resizing) { | ||
117 | return; | ||
118 | } | ||
119 | const container = stackRef.current; | ||
120 | if (container === null) { | ||
121 | return; | ||
122 | } | ||
123 | const rect = container.getBoundingClientRect(); | ||
124 | const newFraction = horizontalSplit | ||
125 | ? (event.clientY - rect.top) / rect.height | ||
126 | : (event.clientX - rect.left) / rect.width; | ||
127 | setFraction(Math.min(0.9, Math.max(0.1, newFraction))); | ||
128 | }} | ||
129 | onDoubleClick={() => setFraction(0.5)} | ||
130 | > | ||
131 | {horizontalSplit ? <MoreHorizIcon /> : <MoreVertIcon />} | ||
132 | </Box> | ||
133 | </Box> | ||
134 | <Box {...{ [axis]: secondarySize }}> | ||
135 | <GraphPane /> | ||
136 | </Box> | ||
137 | </Stack> | ||
138 | ); | ||
139 | }); | ||
140 | |||
141 | function SplitPane(): JSX.Element { | ||
142 | const { ref, width, height } = useResizeDetector(); | ||
143 | const horizontalSplit = | ||
144 | width !== undefined && height !== undefined && height > width; | ||
145 | |||
146 | return ( | ||
147 | <Box height="100%" overflow="auto" ref={ref}> | ||
148 | <DirectionalSplitPane horizontalSplit={horizontalSplit} /> | ||
149 | </Box> | ||
150 | ); | ||
151 | } | ||
152 | 14 | ||
153 | export default function Refinery(): JSX.Element { | 15 | export default function Refinery(): JSX.Element { |
154 | return ( | 16 | return ( |
@@ -156,7 +18,7 @@ export default function Refinery(): JSX.Element { | |||
156 | <UpdateNotification /> | 18 | <UpdateNotification /> |
157 | <Stack direction="column" height="100%" overflow="auto"> | 19 | <Stack direction="column" height="100%" overflow="auto"> |
158 | <TopBar /> | 20 | <TopBar /> |
159 | <SplitPane /> | 21 | <WorkArea /> |
160 | </Stack> | 22 | </Stack> |
161 | </SnackbarProvider> | 23 | </SnackbarProvider> |
162 | ); | 24 | ); |
diff --git a/subprojects/frontend/src/TopBar.tsx b/subprojects/frontend/src/TopBar.tsx index c722c203..e6350080 100644 --- a/subprojects/frontend/src/TopBar.tsx +++ b/subprojects/frontend/src/TopBar.tsx | |||
@@ -6,7 +6,6 @@ | |||
6 | 6 | ||
7 | import GitHubIcon from '@mui/icons-material/GitHub'; | 7 | import GitHubIcon from '@mui/icons-material/GitHub'; |
8 | import AppBar from '@mui/material/AppBar'; | 8 | import AppBar from '@mui/material/AppBar'; |
9 | import Button from '@mui/material/Button'; | ||
10 | import IconButton from '@mui/material/IconButton'; | 9 | import IconButton from '@mui/material/IconButton'; |
11 | import Stack from '@mui/material/Stack'; | 10 | import Stack from '@mui/material/Stack'; |
12 | import Toolbar from '@mui/material/Toolbar'; | 11 | import Toolbar from '@mui/material/Toolbar'; |
@@ -17,8 +16,10 @@ import { throttle } from 'lodash-es'; | |||
17 | import { observer } from 'mobx-react-lite'; | 16 | import { observer } from 'mobx-react-lite'; |
18 | import { useEffect, useMemo, useState } from 'react'; | 17 | import { useEffect, useMemo, useState } from 'react'; |
19 | 18 | ||
19 | import PaneButtons from './PaneButtons'; | ||
20 | import { useRootStore } from './RootStoreProvider'; | 20 | import { useRootStore } from './RootStoreProvider'; |
21 | import ToggleDarkModeButton from './ToggleDarkModeButton'; | 21 | import ToggleDarkModeButton from './ToggleDarkModeButton'; |
22 | import ConnectButton from './editor/ConnectButton'; | ||
22 | import GenerateButton from './editor/GenerateButton'; | 23 | import GenerateButton from './editor/GenerateButton'; |
23 | 24 | ||
24 | function useWindowControlsOverlayVisible(): boolean { | 25 | function useWindowControlsOverlayVisible(): boolean { |
@@ -65,11 +66,12 @@ const DevModeBadge = styled('div')(({ theme }) => ({ | |||
65 | })); | 66 | })); |
66 | 67 | ||
67 | export default observer(function TopBar(): JSX.Element { | 68 | export default observer(function TopBar(): JSX.Element { |
68 | const { editorStore } = useRootStore(); | 69 | const { editorStore, themeStore } = useRootStore(); |
69 | const overlayVisible = useWindowControlsOverlayVisible(); | 70 | const overlayVisible = useWindowControlsOverlayVisible(); |
70 | const { breakpoints } = useTheme(); | 71 | const { breakpoints } = useTheme(); |
71 | const small = useMediaQuery(breakpoints.down('sm')); | 72 | const medium = useMediaQuery(breakpoints.up('sm')); |
72 | const large = useMediaQuery(breakpoints.up('md')); | 73 | const large = useMediaQuery(breakpoints.up('md')); |
74 | const veryLarge = useMediaQuery(breakpoints.up('lg')); | ||
73 | 75 | ||
74 | return ( | 76 | return ( |
75 | <AppBar | 77 | <AppBar |
@@ -100,59 +102,42 @@ export default observer(function TopBar(): JSX.Element { | |||
100 | py: 0.5, | 102 | py: 0.5, |
101 | }} | 103 | }} |
102 | > | 104 | > |
103 | <Typography variant="h6" component="h1" flexGrow={1}> | 105 | <Typography variant="h6" component="h1"> |
104 | Refinery {import.meta.env.DEV && <DevModeBadge>Dev</DevModeBadge>} | 106 | Refinery {import.meta.env.DEV && <DevModeBadge>Dev</DevModeBadge>} |
105 | </Typography> | 107 | </Typography> |
106 | <Stack direction="row" marginRight={1}> | 108 | <Stack direction="row" flexGrow={1} marginLeft={1} gap={1}> |
107 | <GenerateButton editorStore={editorStore} hideWarnings={small} /> | 109 | <ConnectButton editorStore={editorStore} /> |
110 | {medium && !large && ( | ||
111 | <PaneButtons themeStore={themeStore} hideLabel /> | ||
112 | )} | ||
113 | </Stack> | ||
114 | {large && ( | ||
115 | <Stack | ||
116 | direction="row" | ||
117 | alignItems="center" | ||
118 | sx={{ | ||
119 | py: 0.5, | ||
120 | position: 'absolute', | ||
121 | top: 0, | ||
122 | bottom: 0, | ||
123 | left: '50%', | ||
124 | transform: 'translateX(-50%)', | ||
125 | }} | ||
126 | > | ||
127 | <PaneButtons themeStore={themeStore} /> | ||
128 | </Stack> | ||
129 | )} | ||
130 | <Stack direction="row" marginLeft={1} marginRight={1} gap={1}> | ||
131 | <GenerateButton editorStore={editorStore} hideWarnings={!veryLarge} /> | ||
108 | {large && ( | 132 | {large && ( |
109 | <> | 133 | <IconButton |
110 | <Button | 134 | aria-label="GitHub" |
111 | arial-label="Budapest University of Technology and Economics, Critical Systems Research Group" | 135 | href="https://github.com/graphs4value/refinery" |
112 | className="rounded" | 136 | target="_blank" |
113 | color="inherit" | 137 | color="inherit" |
114 | href="https://ftsrg.mit.bme.hu" | 138 | > |
115 | target="_blank" | 139 | <GitHubIcon /> |
116 | sx={{ marginLeft: 1 }} | 140 | </IconButton> |
117 | > | ||
118 | BME FTSRG | ||
119 | </Button> | ||
120 | <Button | ||
121 | aria-label="McGill University, Department of Electrical and Computer Engineering" | ||
122 | className="rounded" | ||
123 | color="inherit" | ||
124 | href="https://www.mcgill.ca/ece/daniel-varro" | ||
125 | target="_blank" | ||
126 | > | ||
127 | M<span style={{ textTransform: 'none' }}>c</span>Gill ECE | ||
128 | </Button> | ||
129 | <Button | ||
130 | aria-label="Linkönping University, Department of Computer and Information Science" | ||
131 | className="rounded" | ||
132 | color="inherit" | ||
133 | href="https://liu.se/en/employee/danva91" | ||
134 | target="_blank" | ||
135 | > | ||
136 | L<span style={{ textTransform: 'none' }}>i</span>U IDA | ||
137 | </Button> | ||
138 | <Button | ||
139 | aria-label="2022 Amazon Research Awards recipent" | ||
140 | className="rounded" | ||
141 | color="inherit" | ||
142 | href="https://www.amazon.science/research-awards/recipients/daniel-varro-fall-2021" | ||
143 | target="_blank" | ||
144 | > | ||
145 | Amazon Science | ||
146 | </Button> | ||
147 | <IconButton | ||
148 | aria-label="GitHub" | ||
149 | href="https://github.com/graphs4value/refinery" | ||
150 | target="_blank" | ||
151 | color="inherit" | ||
152 | > | ||
153 | <GitHubIcon /> | ||
154 | </IconButton> | ||
155 | </> | ||
156 | )} | 141 | )} |
157 | </Stack> | 142 | </Stack> |
158 | <ToggleDarkModeButton /> | 143 | <ToggleDarkModeButton /> |
diff --git a/subprojects/frontend/src/WorkArea.tsx b/subprojects/frontend/src/WorkArea.tsx new file mode 100644 index 00000000..adb29a50 --- /dev/null +++ b/subprojects/frontend/src/WorkArea.tsx | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import { observer } from 'mobx-react-lite'; | ||
8 | |||
9 | import DirectionalSplitPane from './DirectionalSplitPane'; | ||
10 | import { useRootStore } from './RootStoreProvider'; | ||
11 | import EditorPane from './editor/EditorPane'; | ||
12 | import GraphPane from './graph/GraphPane'; | ||
13 | import TablePane from './table/TablePane'; | ||
14 | |||
15 | export default observer(function WorkArea(): JSX.Element { | ||
16 | const { themeStore } = useRootStore(); | ||
17 | |||
18 | return ( | ||
19 | <DirectionalSplitPane | ||
20 | primary={<EditorPane />} | ||
21 | secondary={ | ||
22 | <DirectionalSplitPane | ||
23 | primary={<GraphPane />} | ||
24 | secondary={<TablePane />} | ||
25 | primaryOnly={!themeStore.showTable} | ||
26 | secondaryOnly={!themeStore.showGraph} | ||
27 | /> | ||
28 | } | ||
29 | primaryOnly={!themeStore.showGraph && !themeStore.showTable} | ||
30 | secondaryOnly={!themeStore.showCode} | ||
31 | /> | ||
32 | ); | ||
33 | }); | ||
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx index ca51f975..a7ac2f85 100644 --- a/subprojects/frontend/src/editor/EditorButtons.tsx +++ b/subprojects/frontend/src/editor/EditorButtons.tsx | |||
@@ -20,7 +20,6 @@ import ToggleButton from '@mui/material/ToggleButton'; | |||
20 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | 20 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; |
21 | import { observer } from 'mobx-react-lite'; | 21 | import { observer } from 'mobx-react-lite'; |
22 | 22 | ||
23 | import ConnectButton from './ConnectButton'; | ||
24 | import type EditorStore from './EditorStore'; | 23 | import type EditorStore from './EditorStore'; |
25 | 24 | ||
26 | // Exhastive switch as proven by TypeScript. | 25 | // Exhastive switch as proven by TypeScript. |
@@ -106,7 +105,6 @@ export default observer(function EditorButtons({ | |||
106 | > | 105 | > |
107 | <FormatPaint fontSize="small" /> | 106 | <FormatPaint fontSize="small" /> |
108 | </IconButton> | 107 | </IconButton> |
109 | <ConnectButton editorStore={editorStore} /> | ||
110 | </Stack> | 108 | </Stack> |
111 | ); | 109 | ); |
112 | }); | 110 | }); |
diff --git a/subprojects/frontend/src/table/TableArea.tsx b/subprojects/frontend/src/table/TableArea.tsx new file mode 100644 index 00000000..b4c6d4ce --- /dev/null +++ b/subprojects/frontend/src/table/TableArea.tsx | |||
@@ -0,0 +1,9 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | export default function TablePane(): JSX.Element { | ||
8 | return <div>Table</div>; | ||
9 | } | ||
diff --git a/subprojects/frontend/src/table/TablePane.tsx b/subprojects/frontend/src/table/TablePane.tsx new file mode 100644 index 00000000..01f6ac53 --- /dev/null +++ b/subprojects/frontend/src/table/TablePane.tsx | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import Stack from '@mui/material/Stack'; | ||
8 | import { Suspense, lazy } from 'react'; | ||
9 | |||
10 | import Loading from '../Loading'; | ||
11 | |||
12 | const TableArea = lazy(() => import('./TableArea')); | ||
13 | |||
14 | export default function TablePane(): JSX.Element { | ||
15 | return ( | ||
16 | <Stack | ||
17 | direction="column" | ||
18 | height="100%" | ||
19 | overflow="auto" | ||
20 | alignItems="center" | ||
21 | justifyContent="center" | ||
22 | > | ||
23 | <Suspense fallback={<Loading />}> | ||
24 | <TableArea /> | ||
25 | </Suspense> | ||
26 | </Stack> | ||
27 | ); | ||
28 | } | ||
diff --git a/subprojects/frontend/src/theme/ThemeStore.ts b/subprojects/frontend/src/theme/ThemeStore.ts index 7c657449..fa47d873 100644 --- a/subprojects/frontend/src/theme/ThemeStore.ts +++ b/subprojects/frontend/src/theme/ThemeStore.ts | |||
@@ -12,11 +12,19 @@ export enum ThemePreference { | |||
12 | PreferDark, | 12 | PreferDark, |
13 | } | 13 | } |
14 | 14 | ||
15 | export type SelectedPane = 'code' | 'graph' | 'table'; | ||
16 | |||
15 | export default class ThemeStore { | 17 | export default class ThemeStore { |
16 | preference = ThemePreference.System; | 18 | preference = ThemePreference.System; |
17 | 19 | ||
18 | systemDarkMode: boolean; | 20 | systemDarkMode: boolean; |
19 | 21 | ||
22 | showCode = true; | ||
23 | |||
24 | showGraph = true; | ||
25 | |||
26 | showTable = false; | ||
27 | |||
20 | constructor() { | 28 | constructor() { |
21 | const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); | 29 | const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); |
22 | this.systemDarkMode = mediaQuery.matches; | 30 | this.systemDarkMode = mediaQuery.matches; |
@@ -48,4 +56,44 @@ export default class ThemeStore { | |||
48 | : ThemePreference.PreferDark; | 56 | : ThemePreference.PreferDark; |
49 | } | 57 | } |
50 | } | 58 | } |
59 | |||
60 | toggleCode(): void { | ||
61 | if (!this.showGraph && !this.showTable) { | ||
62 | return; | ||
63 | } | ||
64 | this.showCode = !this.showCode; | ||
65 | } | ||
66 | |||
67 | toggleGraph(): void { | ||
68 | if (!this.showCode && !this.showTable) { | ||
69 | return; | ||
70 | } | ||
71 | this.showGraph = !this.showGraph; | ||
72 | } | ||
73 | |||
74 | toggleTable(): void { | ||
75 | if (!this.showCode && !this.showGraph) { | ||
76 | return; | ||
77 | } | ||
78 | this.showTable = !this.showTable; | ||
79 | } | ||
80 | |||
81 | get selectedPane(): SelectedPane { | ||
82 | if (this.showCode) { | ||
83 | return 'code'; | ||
84 | } | ||
85 | if (this.showGraph) { | ||
86 | return 'graph'; | ||
87 | } | ||
88 | if (this.showTable) { | ||
89 | return 'table'; | ||
90 | } | ||
91 | return 'code'; | ||
92 | } | ||
93 | |||
94 | setSelectedPane(pane: SelectedPane): void { | ||
95 | this.showCode = pane === 'code'; | ||
96 | this.showGraph = pane === 'graph'; | ||
97 | this.showTable = pane === 'table'; | ||
98 | } | ||
51 | } | 99 | } |