diff --git a/client/src/components/MainWindow.tsx b/client/src/components/MainWindow.tsx index 68c7142..6cbdc67 100644 --- a/client/src/components/MainWindow.tsx +++ b/client/src/components/MainWindow.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 { grey } from '@material-ui/core/colors'; import AppBar from './appbar/AppBar'; -import QueryWindow, { QueryWindowReducer } from './windows/QueryWindow'; +import QueryWindow, { QueryWindowReducer, QueryWindowState } from './windows/QueryWindow'; var _ = require('lodash'); const darkTheme = createMuiTheme({ @@ -14,19 +14,101 @@ const darkTheme = createMuiTheme({ }, }); +export interface MainWindowState { + tabLabels: string[], + tabStates: any[], + tabReducers: Reducer[], + 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) { - const [state, dispatch] = useReducer(QueryWindowReducer, { - query: null, - resultsForQuery: null, - editingQuery: false, + const [state, dispatch] = useReducer(MainWindowReducer, { + tabLabels: ["Query"], + tabStates: [ + { + editingQuery: false, + query: null, + resultsForQuery: null, + }, + ], + tabReducers: [QueryWindowReducer, QueryWindowReducer], + activeTab: 0 + }) + + const queryWindows = state.tabStates.map((state: QueryWindowState, i: number) => { + return { + dispatch({ + type: MainWindowStateActions.DispatchToTab, + tabAction: action, + idx: i + }); + }} + /> }); return - - 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]} } \ No newline at end of file diff --git a/client/src/components/appbar/AppBar.tsx b/client/src/components/appbar/AppBar.tsx index 1db35fe..0679f92 100644 --- a/client/src/components/appbar/AppBar.tsx +++ b/client/src/components/appbar/AppBar.tsx @@ -1,14 +1,63 @@ -import React from 'react'; -import { AppBar as MuiAppBar, Box } from '@material-ui/core'; +import React, { useState } from 'react'; +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 { + 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(false); + + const labelElem = + {label} + {hover && + + + + } + ; + return setHover(true)} + onMouseLeave={() => setHover(false)} + label={labelElem} + {...restProps} + /> } export default function AppBar(props: IProps) { return - - error + + + error + + props.setSelectedTab(v)}> + {props.tabLabels.map((l: string, idx: number) => props.onCloseTab(idx)} + />)} + + } \ No newline at end of file