diff --git a/web/package.json b/web/package.json
index f4e60b674..b3d553d48 100644
--- a/web/package.json
+++ b/web/package.json
@@ -16,7 +16,8 @@
"react-modal": "^3.11.1",
"react-scripts": "3.3.0",
"socket.io-client": "^2.3.0",
- "styled-components": "^4.4.1"
+ "styled-components": "^4.4.1",
+ "uuid": "^3.4.0"
},
"scripts": {
"start": "cross-env PORT=5000 react-scripts start",
diff --git a/web/src/App.js b/web/src/App.js
index b540b3eec..88ba266a3 100644
--- a/web/src/App.js
+++ b/web/src/App.js
@@ -1,7 +1,7 @@
import React from 'react';
import './index.css';
import Screen from './Screen';
-import { CurrentWidgetProvider, CurrentDatabaseProvider } from './utility/globalState';
+import { CurrentWidgetProvider, CurrentDatabaseProvider, OpenedFilesProvider } from './utility/globalState';
import { SocketProvider } from './utility/SocketProvider';
function App() {
@@ -9,7 +9,9 @@ function App() {
-
+
+
+
diff --git a/web/src/FilesTabsPanel.js b/web/src/FilesTabsPanel.js
index 33c76f72b..c0a678942 100644
--- a/web/src/FilesTabsPanel.js
+++ b/web/src/FilesTabsPanel.js
@@ -3,12 +3,13 @@ import styled from 'styled-components';
import theme from './theme';
import { TableIcon } from './icons';
+import { useOpenedFiles, useSetOpenedFiles } from './utility/globalState';
-const files = [
- { name: 'app.js' },
- { name: 'BranchCategory', type: 'table', selected: true },
- { name: 'ApplicationList' },
-];
+// const files = [
+// { name: 'app.js' },
+// { name: 'BranchCategory', type: 'table', selected: true },
+// { name: 'ApplicationList' },
+// ];
const FileTabItem = styled.div`
border-right: 1px solid white;
@@ -30,10 +31,31 @@ const FileNameWrapper = styled.span`
`;
export default function FilesTabsPanel() {
+ const files = useOpenedFiles();
+ const setOpenedFiles = useSetOpenedFiles();
+
+ const handleTabClick = id => {
+ setOpenedFiles(files =>
+ files.map(x => ({
+ ...x,
+ selected: x.id == id,
+ }))
+ );
+ };
+ const handleMouseUp = (e, id) => {
+ if (e.button == 1) {
+ setOpenedFiles(files => files.filter(x => x.id != id));
+ }
+ };
return (
<>
{files.map(file => (
-
+ handleTabClick(file.id)}
+ onMouseUp={e => handleMouseUp(e, file.id)}
+ >
{file.name}
diff --git a/web/src/appobj/AppObjectList.js b/web/src/appobj/AppObjectList.js
index d6532c988..45fc88e55 100644
--- a/web/src/appobj/AppObjectList.js
+++ b/web/src/appobj/AppObjectList.js
@@ -2,11 +2,14 @@ import React from 'react';
import styled from 'styled-components';
import { showMenu } from '../modals/DropDownMenu';
import { AppObjectCore } from './AppObjects';
+import { useSetOpenedFiles } from '../utility/globalState';
export function AppObjectList({ list, makeAppObj, SubItems = undefined, onObjectClick = undefined }) {
+ const setOpenedFiles = useSetOpenedFiles();
return (list || []).map(x => {
- const appobj = makeAppObj(x);
- let res = ;
+ const appobj = makeAppObj(x, { setOpenedFiles });
+ if (onObjectClick) appobj.onClick = onObjectClick;
+ let res = ;
if (SubItems) {
res = (
<>
diff --git a/web/src/appobj/AppObjects.js b/web/src/appobj/AppObjects.js
index a0b1b0ab3..08906e89d 100644
--- a/web/src/appobj/AppObjects.js
+++ b/web/src/appobj/AppObjects.js
@@ -1,6 +1,7 @@
import React from 'react';
import styled from 'styled-components';
import { showMenu } from '../modals/DropDownMenu';
+import { useSetOpenedFiles } from '../utility/globalState';
const AppObjectDiv = styled.div`
padding: 5px;
@@ -34,6 +35,7 @@ export function AppObjectCore({ title, Icon, Menu, data, makeAppObj, onClick })
}
export function AppObjectControl({ data, makeAppObj }) {
- const appobj = makeAppObj(data);
+ const setOpenedFiles = useSetOpenedFiles();
+ const appobj = makeAppObj(data, { setOpenedFiles });
return ;
}
diff --git a/web/src/appobj/tableAppObject.js b/web/src/appobj/tableAppObject.js
index d4a482ed9..351189947 100644
--- a/web/src/appobj/tableAppObject.js
+++ b/web/src/appobj/tableAppObject.js
@@ -1,4 +1,5 @@
import React from 'react';
+import uuidv1 from 'uuid/v1';
import { TableIcon } from '../icons';
import { DropDownMenuItem } from '../modals/DropDownMenu';
import showModal from '../modals/showModal';
@@ -20,10 +21,14 @@ function Menu({ data, makeAppObj }) {
);
}
-export default function tableAppObject({ pureName, schemaName }) {
+export default function tableAppObject({ pureName, schemaName }, { setOpenedFiles }) {
const title = schemaName ? `${schemaName}.${pureName}` : pureName;
const key = title;
const Icon = TableIcon;
+ const onClick = ({ schemaName, pureName }) => {
+ const id = uuidv1();
+ setOpenedFiles(files => [...files, { id, name: pureName }]);
+ };
- return { title, key, Icon, Menu };
+ return { title, key, Icon, Menu, onClick };
}
diff --git a/web/src/utility/globalState.js b/web/src/utility/globalState.js
index 9cbe870c8..06ce73f9d 100644
--- a/web/src/utility/globalState.js
+++ b/web/src/utility/globalState.js
@@ -1,4 +1,5 @@
import React from 'react';
+import useStorage from './useStorage';
function createGlobalState(defaultValue) {
const Context = React.createContext(null);
@@ -19,8 +20,30 @@ function createGlobalState(defaultValue) {
return [Provider, useValue, useSetValue];
}
+function createStorageState(storageKey, defaultValue) {
+ const Context = React.createContext(null);
+
+ function Provider({ children }) {
+ const [currentvalue, setCurrentValue] = useStorage(storageKey, localStorage, defaultValue);
+ return {children};
+ }
+
+ function useValue() {
+ return React.useContext(Context)[0];
+ }
+
+ function useSetValue() {
+ return React.useContext(Context)[1];
+ }
+
+ return [Provider, useValue, useSetValue];
+}
+
const [CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget] = createGlobalState('database');
export { CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget };
const [CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase] = createGlobalState(null);
export { CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase };
+
+const [OpenedFilesProvider, useOpenedFiles, useSetOpenedFiles] = createStorageState('openedFiles', []);
+export { OpenedFilesProvider, useOpenedFiles, useSetOpenedFiles };
diff --git a/web/src/utility/useStorage.js b/web/src/utility/useStorage.js
new file mode 100644
index 000000000..c81e2dbea
--- /dev/null
+++ b/web/src/utility/useStorage.js
@@ -0,0 +1,36 @@
+import React from 'react';
+
+export default function useStorage(key, storageObject, initialValue) {
+ // State to store our value
+ // Pass initial state function to useState so logic is only executed once
+ const [storedValue, setStoredValue] = React.useState(() => {
+ try {
+ // Get from local storage by key
+ const item = storageObject.getItem(key);
+ // Parse stored json or if none return initialValue
+ return item ? JSON.parse(item) : initialValue;
+ } catch (error) {
+ // If error also return initialValue
+ console.log(error);
+ return initialValue;
+ }
+ });
+
+ // Return a wrapped version of useState's setter function that ...
+ // ... persists the new value to localStorage.
+ const setValue = value => {
+ try {
+ // Allow value to be a function so we have same API as useState
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
+ // Save state
+ setStoredValue(valueToStore);
+ // Save to local storage
+ storageObject.setItem(key, JSON.stringify(valueToStore));
+ } catch (error) {
+ // A more advanced implementation would handle the error case
+ console.log(error);
+ }
+ };
+
+ return [storedValue, setValue];
+}
diff --git a/web/yarn.lock b/web/yarn.lock
index 09785eab6..92a16f566 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -10412,6 +10412,11 @@ uuid@^3.0.1, uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
+uuid@^3.4.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+ integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
v8-compile-cache@^2.0.3:
version "2.1.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"