Tabs can be added and removed.

pull/20/head
Sander Vocke 5 years ago
parent 16654795d2
commit b6e937675a
  1. 102
      client/src/components/MainWindow.tsx
  2. 57
      client/src/components/appbar/AppBar.tsx

@ -1,8 +1,8 @@
import React, { useReducer } from 'react'; import React, { useReducer, useState, Reducer } from 'react';
import { ThemeProvider, CssBaseline, createMuiTheme } from '@material-ui/core'; import { ThemeProvider, CssBaseline, createMuiTheme } from '@material-ui/core';
import { grey } from '@material-ui/core/colors'; import { grey } from '@material-ui/core/colors';
import AppBar from './appbar/AppBar'; import AppBar from './appbar/AppBar';
import QueryWindow, { QueryWindowReducer } from './windows/QueryWindow'; import QueryWindow, { QueryWindowReducer, QueryWindowState } from './windows/QueryWindow';
var _ = require('lodash'); var _ = require('lodash');
const darkTheme = createMuiTheme({ const darkTheme = createMuiTheme({
@ -14,19 +14,101 @@ const darkTheme = createMuiTheme({
}, },
}); });
export interface MainWindowState {
tabLabels: string[],
tabStates: any[],
tabReducers: Reducer<any, any>[],
activeTab: number,
}
export enum MainWindowStateActions {
SetActiveTab = "setActiveTab",
DispatchToTab = "dispatchToTab",
CloseTab = "closeTab",
AddTab = "addTab",
}
export function MainWindowReducer(state: MainWindowState, action: any) {
switch (action.type) {
case MainWindowStateActions.SetActiveTab:
return { ...state, activeTab: action.value }
case MainWindowStateActions.CloseTab:
const newSize = state.tabStates.length - 1;
return {
...state,
tabStates: state.tabStates.filter((i: any, idx: number) => idx != action.idx),
tabLabels: state.tabLabels.filter((i: any, idx: number) => idx != action.idx),
tabReducers: state.tabReducers.filter((i: any, idx: number) => idx != action.idx),
activeTab: state.activeTab >= (newSize-1) ? (newSize-1) : state.activeTab,
}
case MainWindowStateActions.AddTab:
return {
...state,
tabStates: [...state.tabStates, action.tabState],
tabLabels: [...state.tabLabels, action.tabLabel],
tabReducers: [...state.tabReducers, action.tabReducer],
}
case MainWindowStateActions.DispatchToTab:
return {
...state,
tabStates: state.tabStates.map((item: any, i: number) => {
return i === action.idx ?
state.tabReducers[i](item, action.tabAction) :
item;
})
}
default:
throw new Error("Unimplemented QueryWindow state update.")
}
}
export default function MainWindow(props: any) { export default function MainWindow(props: any) {
const [state, dispatch] = useReducer(QueryWindowReducer, { const [state, dispatch] = useReducer(MainWindowReducer, {
query: null, tabLabels: ["Query"],
resultsForQuery: null, tabStates: [
editingQuery: false, {
editingQuery: false,
query: null,
resultsForQuery: null,
},
],
tabReducers: [QueryWindowReducer, QueryWindowReducer],
activeTab: 0
})
const queryWindows = state.tabStates.map((state: QueryWindowState, i: number) => {
return <QueryWindow
state={state}
dispatch={(action: any) => {
dispatch({
type: MainWindowStateActions.DispatchToTab,
tabAction: action,
idx: i
});
}}
/>
}); });
return <ThemeProvider theme={darkTheme}> return <ThemeProvider theme={darkTheme}>
<CssBaseline /> <CssBaseline />
<AppBar /> <AppBar
<QueryWindow tabLabels={state.tabLabels}
state={state} selectedTab={state.activeTab}
dispatch={dispatch} setSelectedTab={(t: number) => dispatch({ type: MainWindowStateActions.SetActiveTab, value: t })}
onCloseTab={(t: number) => dispatch({ type: MainWindowStateActions.CloseTab, idx: t })}
onAddTab={() => {
dispatch({
type: MainWindowStateActions.AddTab,
tabState: {
editingQuery: false,
query: null,
resultsForQuery: null,
},
tabLabel: "Query",
tabReducer: QueryWindowReducer,
})
}}
/> />
{queryWindows[state.activeTab]}
</ThemeProvider> </ThemeProvider>
} }

@ -1,14 +1,63 @@
import React from 'react'; import React, { useState } from 'react';
import { AppBar as MuiAppBar, Box } from '@material-ui/core'; import { AppBar as MuiAppBar, Box, Tab as MuiTab, Tabs, IconButton } from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import AddIcon from '@material-ui/icons/Add';
export interface IProps { export interface IProps {
tabLabels: string[],
selectedTab: number,
setSelectedTab: (n: number) => void,
onCloseTab: (idx: number) => void,
onAddTab: () => void,
}
export interface TabProps {
onClose: () => void,
}
export function Tab(props: any) {
const { onClose, label, ...restProps } = props;
const [hover, setHover] = useState<boolean>(false);
const labelElem = <Box
display="flex"
alignItems="center"
justifyContent="center"
>
{label}
{hover && <Box ml={1}>
<IconButton
size="small"
color="inherit"
onClick={onClose}
>
<CloseIcon />
</IconButton>
</Box>}
</Box>;
return <MuiTab
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
label={labelElem}
{...restProps}
/>
} }
export default function AppBar(props: IProps) { export default function AppBar(props: IProps) {
return <MuiAppBar position="static" style={{ background: 'grey' }}> return <MuiAppBar position="static" style={{ background: 'grey' }}>
<Box m={0.5} display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<img height="30px" src={process.env.PUBLIC_URL + "/logo.svg"} alt="error"></img> <Box m={0.5} display="flex" alignItems="center">
<img height="30px" src={process.env.PUBLIC_URL + "/logo.svg"} alt="error"></img>
</Box>
<Tabs value={props.selectedTab} onChange={(e: any, v: number) => props.setSelectedTab(v)}>
{props.tabLabels.map((l: string, idx: number) => <Tab
label={l}
value={idx}
onClose={() => props.onCloseTab(idx)}
/>)}
</Tabs>
<IconButton color="inherit" onClick={props.onAddTab}><AddIcon/></IconButton>
</Box> </Box>
</MuiAppBar> </MuiAppBar>
} }
Loading…
Cancel
Save