aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/frontend/src/PaneButtons.tsx8
-rw-r--r--subprojects/frontend/src/TopBar.tsx13
-rw-r--r--subprojects/frontend/src/editor/EditorButtons.tsx2
-rw-r--r--subprojects/frontend/src/graph/GraphStore.ts16
-rw-r--r--subprojects/frontend/src/index.tsx5
-rw-r--r--subprojects/frontend/src/table/RelationGrid.tsx108
-rw-r--r--subprojects/frontend/src/table/SymbolSelector.tsx65
-rw-r--r--subprojects/frontend/src/table/TableArea.tsx19
-rw-r--r--subprojects/frontend/src/table/TablePane.tsx8
-rw-r--r--subprojects/frontend/src/table/TableToolbar.tsx43
-rw-r--r--subprojects/frontend/src/table/ValueRenderer.tsx62
-rw-r--r--subprojects/frontend/src/theme/ThemeProvider.tsx4
12 files changed, 330 insertions, 23 deletions
diff --git a/subprojects/frontend/src/PaneButtons.tsx b/subprojects/frontend/src/PaneButtons.tsx
index 7b83171d..9cb02793 100644
--- a/subprojects/frontend/src/PaneButtons.tsx
+++ b/subprojects/frontend/src/PaneButtons.tsx
@@ -24,7 +24,7 @@ function PaneButtons({
24 <ToggleButtonGroup 24 <ToggleButtonGroup
25 size={hideLabel ? 'small' : 'medium'} 25 size={hideLabel ? 'small' : 'medium'}
26 className="rounded" 26 className="rounded"
27 sx={{ 27 sx={(theme) => ({
28 '.MuiToggleButton-root': { 28 '.MuiToggleButton-root': {
29 ...(hideLabel 29 ...(hideLabel
30 ? {} 30 ? {}
@@ -33,6 +33,10 @@ function PaneButtons({
33 }), 33 }),
34 fontSize: '1rem', 34 fontSize: '1rem',
35 lineHeight: '1.5', 35 lineHeight: '1.5',
36 fontWeight: theme.typography.fontWeightMedium ?? 500,
37 '&:not(.Mui-selected)': {
38 color: theme.palette.text.secondary,
39 },
36 }, 40 },
37 ...(hideLabel 41 ...(hideLabel
38 ? {} 42 ? {}
@@ -41,7 +45,7 @@ function PaneButtons({
41 margin: '0 6px 0 -4px', 45 margin: '0 6px 0 -4px',
42 }, 46 },
43 }), 47 }),
44 }} 48 })}
45 > 49 >
46 <ToggleButton 50 <ToggleButton
47 value="code" 51 value="code"
diff --git a/subprojects/frontend/src/TopBar.tsx b/subprojects/frontend/src/TopBar.tsx
index e6350080..c22ca8b1 100644
--- a/subprojects/frontend/src/TopBar.tsx
+++ b/subprojects/frontend/src/TopBar.tsx
@@ -19,7 +19,6 @@ import { useEffect, useMemo, useState } from 'react';
19import PaneButtons from './PaneButtons'; 19import PaneButtons from './PaneButtons';
20import { useRootStore } from './RootStoreProvider'; 20import { useRootStore } from './RootStoreProvider';
21import ToggleDarkModeButton from './ToggleDarkModeButton'; 21import ToggleDarkModeButton from './ToggleDarkModeButton';
22import ConnectButton from './editor/ConnectButton';
23import GenerateButton from './editor/GenerateButton'; 22import GenerateButton from './editor/GenerateButton';
24 23
25function useWindowControlsOverlayVisible(): boolean { 24function useWindowControlsOverlayVisible(): boolean {
@@ -102,16 +101,10 @@ export default observer(function TopBar(): JSX.Element {
102 py: 0.5, 101 py: 0.5,
103 }} 102 }}
104 > 103 >
105 <Typography variant="h6" component="h1"> 104 <Typography variant="h6" component="h1" flexGrow={1}>
106 Refinery {import.meta.env.DEV && <DevModeBadge>Dev</DevModeBadge>} 105 Refinery {import.meta.env.DEV && <DevModeBadge>Dev</DevModeBadge>}
107 </Typography> 106 </Typography>
108 <Stack direction="row" flexGrow={1} marginLeft={1} gap={1}> 107 {medium && (
109 <ConnectButton editorStore={editorStore} />
110 {medium && !large && (
111 <PaneButtons themeStore={themeStore} hideLabel />
112 )}
113 </Stack>
114 {large && (
115 <Stack 108 <Stack
116 direction="row" 109 direction="row"
117 alignItems="center" 110 alignItems="center"
@@ -124,7 +117,7 @@ export default observer(function TopBar(): JSX.Element {
124 transform: 'translateX(-50%)', 117 transform: 'translateX(-50%)',
125 }} 118 }}
126 > 119 >
127 <PaneButtons themeStore={themeStore} /> 120 <PaneButtons themeStore={themeStore} hideLabel={!large} />
128 </Stack> 121 </Stack>
129 )} 122 )}
130 <Stack direction="row" marginLeft={1} marginRight={1} gap={1}> 123 <Stack direction="row" marginLeft={1} marginRight={1} gap={1}>
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx
index a7ac2f85..ca51f975 100644
--- a/subprojects/frontend/src/editor/EditorButtons.tsx
+++ b/subprojects/frontend/src/editor/EditorButtons.tsx
@@ -20,6 +20,7 @@ import ToggleButton from '@mui/material/ToggleButton';
20import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; 20import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
21import { observer } from 'mobx-react-lite'; 21import { observer } from 'mobx-react-lite';
22 22
23import ConnectButton from './ConnectButton';
23import type EditorStore from './EditorStore'; 24import type EditorStore from './EditorStore';
24 25
25// Exhastive switch as proven by TypeScript. 26// Exhastive switch as proven by TypeScript.
@@ -105,6 +106,7 @@ export default observer(function EditorButtons({
105 > 106 >
106 <FormatPaint fontSize="small" /> 107 <FormatPaint fontSize="small" />
107 </IconButton> 108 </IconButton>
109 <ConnectButton editorStore={editorStore} />
108 </Stack> 110 </Stack>
109 ); 111 );
110}); 112});
diff --git a/subprojects/frontend/src/graph/GraphStore.ts b/subprojects/frontend/src/graph/GraphStore.ts
index f81b4db4..50cb8c19 100644
--- a/subprojects/frontend/src/graph/GraphStore.ts
+++ b/subprojects/frontend/src/graph/GraphStore.ts
@@ -61,6 +61,8 @@ export default class GraphStore {
61 61
62 abbreviate = true; 62 abbreviate = true;
63 63
64 selectedSymbol: RelationMetadata | undefined;
65
64 constructor() { 66 constructor() {
65 makeAutoObservable(this, { 67 makeAutoObservable(this, {
66 semantics: observable.ref, 68 semantics: observable.ref,
@@ -143,6 +145,19 @@ export default class GraphStore {
143 this.abbreviate = !this.abbreviate; 145 this.abbreviate = !this.abbreviate;
144 } 146 }
145 147
148 setSelectedSymbol(option: RelationMetadata | undefined): void {
149 if (option === undefined) {
150 this.selectedSymbol = undefined;
151 return;
152 }
153 const metadata = this.relationMetadata.get(option.name);
154 if (metadata !== undefined) {
155 this.selectedSymbol = metadata;
156 } else {
157 this.selectedSymbol = undefined;
158 }
159 }
160
146 setSemantics(semantics: SemanticsSuccessResult) { 161 setSemantics(semantics: SemanticsSuccessResult) {
147 this.semantics = semantics; 162 this.semantics = semantics;
148 this.relationMetadata.clear(); 163 this.relationMetadata.clear();
@@ -161,5 +176,6 @@ export default class GraphStore {
161 toRemove.forEach((key) => { 176 toRemove.forEach((key) => {
162 this.visibility.delete(key); 177 this.visibility.delete(key);
163 }); 178 });
179 this.setSelectedSymbol(this.selectedSymbol);
164 } 180 }
165} 181}
diff --git a/subprojects/frontend/src/index.tsx b/subprojects/frontend/src/index.tsx
index 8cbb8fbc..e14486d6 100644
--- a/subprojects/frontend/src/index.tsx
+++ b/subprojects/frontend/src/index.tsx
@@ -4,12 +4,17 @@
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6 6
7import { styled } from '@mui/material/styles';
7import { configure } from 'mobx'; 8import { configure } from 'mobx';
8import { type Root, createRoot } from 'react-dom/client'; 9import { type Root, createRoot } from 'react-dom/client';
9 10
10import App from './App'; 11import App from './App';
11import RootStore from './RootStore'; 12import RootStore from './RootStore';
12 13
14// Make sure `styled` ends up in the entry chunk.
15// https://github.com/mui/material-ui/issues/32727#issuecomment-1659945548
16(window as unknown as { fixViteIssue: unknown }).fixViteIssue = styled;
17
13const initialValue = `// Metamodel 18const initialValue = `// Metamodel
14class Person { 19class Person {
15 contains Post[] posts opposite author 20 contains Post[] posts opposite author
diff --git a/subprojects/frontend/src/table/RelationGrid.tsx b/subprojects/frontend/src/table/RelationGrid.tsx
new file mode 100644
index 00000000..4009a8b4
--- /dev/null
+++ b/subprojects/frontend/src/table/RelationGrid.tsx
@@ -0,0 +1,108 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import Box from '@mui/material/Box';
8import {
9 DataGrid,
10 type GridRenderCellParams,
11 type GridColDef,
12} from '@mui/x-data-grid';
13import { observer } from 'mobx-react-lite';
14import { useMemo } from 'react';
15
16import type GraphStore from '../graph/GraphStore';
17
18import TableToolbar from './TableToolbar';
19import ValueRenderer from './ValueRenderer';
20
21interface Row {
22 nodes: string[];
23 value: string;
24}
25
26function RelationGrid({ graph }: { graph: GraphStore }): JSX.Element {
27 const {
28 selectedSymbol,
29 semantics: { nodes, partialInterpretation },
30 } = graph;
31 const symbolName = selectedSymbol?.name;
32 const arity = selectedSymbol?.arity ?? 0;
33
34 const columns = useMemo<GridColDef<Row>[]>(() => {
35 const defs: GridColDef<Row>[] = [];
36 for (let i = 0; i < arity; i += 1) {
37 defs.push({
38 field: `n${i}`,
39 headerName: String(i + 1),
40 valueGetter: (row) => row.row.nodes[i] ?? '',
41 flex: 1,
42 });
43 }
44 defs.push({
45 field: 'value',
46 headerName: 'Value',
47 flex: 1,
48 renderCell: ({ value }: GridRenderCellParams<Row, string>) => (
49 <ValueRenderer value={value} />
50 ),
51 });
52 return defs;
53 }, [arity]);
54
55 const rows = useMemo<Row[]>(() => {
56 if (symbolName === undefined) {
57 return [];
58 }
59 const interpretation = partialInterpretation[symbolName] ?? [];
60 return interpretation.map((tuple) => {
61 const nodeNames: string[] = [];
62 for (let i = 0; i < arity; i += 1) {
63 const index = tuple[i];
64 if (typeof index === 'number') {
65 const node = nodes[index];
66 if (node !== undefined) {
67 nodeNames.push(node.name);
68 }
69 }
70 }
71 return {
72 nodes: nodeNames,
73 value: String(tuple[arity]),
74 };
75 });
76 }, [arity, nodes, partialInterpretation, symbolName]);
77
78 return (
79 <Box
80 width="100%"
81 height="100%"
82 p={1}
83 sx={(theme) => ({
84 '.MuiDataGrid-withBorderColor': {
85 borderColor:
86 theme.palette.mode === 'dark'
87 ? theme.palette.divider
88 : theme.palette.outer.border,
89 },
90 })}
91 >
92 <DataGrid
93 slots={{ toolbar: TableToolbar }}
94 slotProps={{
95 toolbar: {
96 graph,
97 },
98 }}
99 rowSelection={false}
100 columns={columns}
101 rows={rows}
102 getRowId={(row) => row.nodes.join(',')}
103 />
104 </Box>
105 );
106}
107
108export default observer(RelationGrid);
diff --git a/subprojects/frontend/src/table/SymbolSelector.tsx b/subprojects/frontend/src/table/SymbolSelector.tsx
new file mode 100644
index 00000000..5272f8ed
--- /dev/null
+++ b/subprojects/frontend/src/table/SymbolSelector.tsx
@@ -0,0 +1,65 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import Autocomplete from '@mui/material/Autocomplete';
8import Box from '@mui/material/Box';
9import TextField from '@mui/material/TextField';
10import { observer } from 'mobx-react-lite';
11
12import type GraphStore from '../graph/GraphStore';
13import RelationName from '../graph/RelationName';
14
15function SymbolSelector({ graph }: { graph: GraphStore }): JSX.Element {
16 const {
17 selectedSymbol,
18 semantics: { relations },
19 } = graph;
20
21 return (
22 <Autocomplete
23 renderInput={(params) => (
24 <TextField
25 {...{
26 ...params,
27 InputLabelProps: {
28 ...params.InputLabelProps,
29 // Workaround for type errors.
30 className: params.InputLabelProps.className ?? '',
31 style: params.InputLabelProps.style ?? {},
32 },
33 }}
34 variant="standard"
35 size="medium"
36 placeholder="Symbol"
37 />
38 )}
39 options={relations}
40 getOptionLabel={(option) => option.name}
41 renderOption={(props, option) => (
42 <Box component="li" {...props}>
43 <RelationName metadata={option} />
44 </Box>
45 )}
46 value={selectedSymbol ?? null}
47 isOptionEqualToValue={(option, value) => option.name === value.name}
48 onChange={(_event, value) => graph.setSelectedSymbol(value ?? undefined)}
49 sx={(theme) => ({
50 flexBasis: 200,
51 maxWidth: 600,
52 flexGrow: 1,
53 flexShrink: 1,
54 '.MuiInput-underline::before': {
55 borderColor:
56 theme.palette.mode === 'dark'
57 ? theme.palette.divider
58 : theme.palette.outer.border,
59 },
60 })}
61 />
62 );
63}
64
65export default observer(SymbolSelector);
diff --git a/subprojects/frontend/src/table/TableArea.tsx b/subprojects/frontend/src/table/TableArea.tsx
index b4c6d4ce..cf37b96a 100644
--- a/subprojects/frontend/src/table/TableArea.tsx
+++ b/subprojects/frontend/src/table/TableArea.tsx
@@ -4,6 +4,21 @@
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6 6
7export default function TablePane(): JSX.Element { 7import { observer } from 'mobx-react-lite';
8 return <div>Table</div>; 8
9import Loading from '../Loading';
10import { useRootStore } from '../RootStoreProvider';
11
12import RelationGrid from './RelationGrid';
13
14function TablePane(): JSX.Element {
15 const { editorStore } = useRootStore();
16
17 if (editorStore === undefined) {
18 return <Loading />;
19 }
20
21 return <RelationGrid graph={editorStore.graph} />;
9} 22}
23
24export default observer(TablePane);
diff --git a/subprojects/frontend/src/table/TablePane.tsx b/subprojects/frontend/src/table/TablePane.tsx
index 01f6ac53..01442c3a 100644
--- a/subprojects/frontend/src/table/TablePane.tsx
+++ b/subprojects/frontend/src/table/TablePane.tsx
@@ -13,13 +13,7 @@ const TableArea = lazy(() => import('./TableArea'));
13 13
14export default function TablePane(): JSX.Element { 14export default function TablePane(): JSX.Element {
15 return ( 15 return (
16 <Stack 16 <Stack direction="column" height="100%" overflow="auto" alignItems="center">
17 direction="column"
18 height="100%"
19 overflow="auto"
20 alignItems="center"
21 justifyContent="center"
22 >
23 <Suspense fallback={<Loading />}> 17 <Suspense fallback={<Loading />}>
24 <TableArea /> 18 <TableArea />
25 </Suspense> 19 </Suspense>
diff --git a/subprojects/frontend/src/table/TableToolbar.tsx b/subprojects/frontend/src/table/TableToolbar.tsx
new file mode 100644
index 00000000..7645deae
--- /dev/null
+++ b/subprojects/frontend/src/table/TableToolbar.tsx
@@ -0,0 +1,43 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import Stack from '@mui/material/Stack';
8import {
9 GridToolbarColumnsButton,
10 GridToolbarContainer,
11 GridToolbarDensitySelector,
12 GridToolbarExport,
13 GridToolbarFilterButton,
14} from '@mui/x-data-grid';
15
16import type GraphStore from '../graph/GraphStore';
17
18import SymbolSelector from './SymbolSelector';
19
20export default function TableToolbar({
21 graph,
22}: {
23 graph: GraphStore;
24}): JSX.Element {
25 return (
26 <GridToolbarContainer
27 sx={{
28 display: 'flex',
29 flexDirection: 'row',
30 flexWrap: 'wrap-reverse',
31 justifyContent: 'space-between',
32 }}
33 >
34 <Stack direction="row" flexWrap="wrap">
35 <GridToolbarColumnsButton />
36 <GridToolbarFilterButton />
37 <GridToolbarDensitySelector />
38 <GridToolbarExport />
39 </Stack>
40 <SymbolSelector graph={graph} />
41 </GridToolbarContainer>
42 );
43}
diff --git a/subprojects/frontend/src/table/ValueRenderer.tsx b/subprojects/frontend/src/table/ValueRenderer.tsx
new file mode 100644
index 00000000..ac5700e4
--- /dev/null
+++ b/subprojects/frontend/src/table/ValueRenderer.tsx
@@ -0,0 +1,62 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import CancelIcon from '@mui/icons-material/Cancel';
8import LabelIcon from '@mui/icons-material/Label';
9import LabelOutlinedIcon from '@mui/icons-material/LabelOutlined';
10import { styled } from '@mui/material/styles';
11
12const Label = styled('div', {
13 name: 'ValueRenderer-Label',
14 shouldForwardProp: (prop) => prop !== 'value',
15})<{
16 value: 'TRUE' | 'UNKNOWN' | 'ERROR';
17}>(({ theme, value }) => ({
18 display: 'flex',
19 alignItems: 'center',
20 ...(value === 'UNKNOWN'
21 ? {
22 color: theme.palette.text.secondary,
23 }
24 : {}),
25 ...(value === 'ERROR'
26 ? {
27 color: theme.palette.error.main,
28 }
29 : {}),
30 '& svg': {
31 marginRight: theme.spacing(0.5),
32 },
33}));
34
35export default function ValueRenderer({
36 value,
37}: {
38 value: string | undefined;
39}): React.ReactNode {
40 switch (value) {
41 case 'TRUE':
42 return (
43 <Label value={value}>
44 <LabelIcon fontSize="small" /> true
45 </Label>
46 );
47 case 'UNKNOWN':
48 return (
49 <Label value={value}>
50 <LabelOutlinedIcon fontSize="small" /> unknown
51 </Label>
52 );
53 case 'ERROR':
54 return (
55 <Label value={value}>
56 <CancelIcon fontSize="small" /> error
57 </Label>
58 );
59 default:
60 return value;
61 }
62}
diff --git a/subprojects/frontend/src/theme/ThemeProvider.tsx b/subprojects/frontend/src/theme/ThemeProvider.tsx
index 90fea897..18310147 100644
--- a/subprojects/frontend/src/theme/ThemeProvider.tsx
+++ b/subprojects/frontend/src/theme/ThemeProvider.tsx
@@ -153,7 +153,7 @@ function createResponsiveTheme(
153 }, {}), 153 }, {}),
154 }, 154 },
155 }, 155 },
156 sizeSmall: { fontSize: '0.75rem' }, 156 sizeSmall: { fontSize: '0.875rem', lineHeight: '1.75' },
157 sizeLarge: { fontSize: '1rem' }, 157 sizeLarge: { fontSize: '1rem' },
158 text: { '&.rounded': { padding: '6px 14px' } }, 158 text: { '&.rounded': { padding: '6px 14px' } },
159 textSizeSmall: { '&.rounded': { padding: '4px 8px' } }, 159 textSizeSmall: { '&.rounded': { padding: '4px 8px' } },
@@ -289,7 +289,7 @@ const darkTheme = (() => {
289 secondary: secondaryText, 289 secondary: secondaryText,
290 disabled: '#5c6370', 290 disabled: '#5c6370',
291 }, 291 },
292 divider: alpha(secondaryText, 0.24), 292 divider: alpha(primaryText, 0.24),
293 outer: { 293 outer: {
294 background: darkBackground, 294 background: darkBackground,
295 border: '#181a1f', 295 border: '#181a1f',