mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 12:43:58 +00:00
tab management
This commit is contained in:
@@ -16,7 +16,8 @@
|
|||||||
"react-modal": "^3.11.1",
|
"react-modal": "^3.11.1",
|
||||||
"react-scripts": "3.3.0",
|
"react-scripts": "3.3.0",
|
||||||
"socket.io-client": "^2.3.0",
|
"socket.io-client": "^2.3.0",
|
||||||
"styled-components": "^4.4.1"
|
"styled-components": "^4.4.1",
|
||||||
|
"uuid": "^3.4.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env PORT=5000 react-scripts start",
|
"start": "cross-env PORT=5000 react-scripts start",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import Screen from './Screen';
|
import Screen from './Screen';
|
||||||
import { CurrentWidgetProvider, CurrentDatabaseProvider } from './utility/globalState';
|
import { CurrentWidgetProvider, CurrentDatabaseProvider, OpenedFilesProvider } from './utility/globalState';
|
||||||
import { SocketProvider } from './utility/SocketProvider';
|
import { SocketProvider } from './utility/SocketProvider';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -9,7 +9,9 @@ function App() {
|
|||||||
<CurrentWidgetProvider>
|
<CurrentWidgetProvider>
|
||||||
<CurrentDatabaseProvider>
|
<CurrentDatabaseProvider>
|
||||||
<SocketProvider>
|
<SocketProvider>
|
||||||
|
<OpenedFilesProvider>
|
||||||
<Screen />
|
<Screen />
|
||||||
|
</OpenedFilesProvider>
|
||||||
</SocketProvider>
|
</SocketProvider>
|
||||||
</CurrentDatabaseProvider>
|
</CurrentDatabaseProvider>
|
||||||
</CurrentWidgetProvider>
|
</CurrentWidgetProvider>
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ import styled from 'styled-components';
|
|||||||
import theme from './theme';
|
import theme from './theme';
|
||||||
|
|
||||||
import { TableIcon } from './icons';
|
import { TableIcon } from './icons';
|
||||||
|
import { useOpenedFiles, useSetOpenedFiles } from './utility/globalState';
|
||||||
|
|
||||||
const files = [
|
// const files = [
|
||||||
{ name: 'app.js' },
|
// { name: 'app.js' },
|
||||||
{ name: 'BranchCategory', type: 'table', selected: true },
|
// { name: 'BranchCategory', type: 'table', selected: true },
|
||||||
{ name: 'ApplicationList' },
|
// { name: 'ApplicationList' },
|
||||||
];
|
// ];
|
||||||
|
|
||||||
const FileTabItem = styled.div`
|
const FileTabItem = styled.div`
|
||||||
border-right: 1px solid white;
|
border-right: 1px solid white;
|
||||||
@@ -30,10 +31,31 @@ const FileNameWrapper = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default function FilesTabsPanel() {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{files.map(file => (
|
{files.map(file => (
|
||||||
<FileTabItem {...file} key={file.name}>
|
<FileTabItem
|
||||||
|
{...file}
|
||||||
|
key={file.id}
|
||||||
|
onClick={() => handleTabClick(file.id)}
|
||||||
|
onMouseUp={e => handleMouseUp(e, file.id)}
|
||||||
|
>
|
||||||
<TableIcon />
|
<TableIcon />
|
||||||
<FileNameWrapper>{file.name}</FileNameWrapper>
|
<FileNameWrapper>{file.name}</FileNameWrapper>
|
||||||
</FileTabItem>
|
</FileTabItem>
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import React from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { showMenu } from '../modals/DropDownMenu';
|
import { showMenu } from '../modals/DropDownMenu';
|
||||||
import { AppObjectCore } from './AppObjects';
|
import { AppObjectCore } from './AppObjects';
|
||||||
|
import { useSetOpenedFiles } from '../utility/globalState';
|
||||||
|
|
||||||
export function AppObjectList({ list, makeAppObj, SubItems = undefined, onObjectClick = undefined }) {
|
export function AppObjectList({ list, makeAppObj, SubItems = undefined, onObjectClick = undefined }) {
|
||||||
|
const setOpenedFiles = useSetOpenedFiles();
|
||||||
return (list || []).map(x => {
|
return (list || []).map(x => {
|
||||||
const appobj = makeAppObj(x);
|
const appobj = makeAppObj(x, { setOpenedFiles });
|
||||||
let res = <AppObjectCore key={appobj.key} {...appobj} data={x} makeAppObj={makeAppObj} onClick={onObjectClick} />;
|
if (onObjectClick) appobj.onClick = onObjectClick;
|
||||||
|
let res = <AppObjectCore key={appobj.key} data={x} makeAppObj={makeAppObj} {...appobj} />;
|
||||||
if (SubItems) {
|
if (SubItems) {
|
||||||
res = (
|
res = (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { showMenu } from '../modals/DropDownMenu';
|
import { showMenu } from '../modals/DropDownMenu';
|
||||||
|
import { useSetOpenedFiles } from '../utility/globalState';
|
||||||
|
|
||||||
const AppObjectDiv = styled.div`
|
const AppObjectDiv = styled.div`
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -34,6 +35,7 @@ export function AppObjectCore({ title, Icon, Menu, data, makeAppObj, onClick })
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function AppObjectControl({ data, makeAppObj }) {
|
export function AppObjectControl({ data, makeAppObj }) {
|
||||||
const appobj = makeAppObj(data);
|
const setOpenedFiles = useSetOpenedFiles();
|
||||||
|
const appobj = makeAppObj(data, { setOpenedFiles });
|
||||||
return <AppObjectCore {...appobj} data={data} makeAppObj={makeAppObj} />;
|
return <AppObjectCore {...appobj} data={data} makeAppObj={makeAppObj} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import uuidv1 from 'uuid/v1';
|
||||||
import { TableIcon } from '../icons';
|
import { TableIcon } from '../icons';
|
||||||
import { DropDownMenuItem } from '../modals/DropDownMenu';
|
import { DropDownMenuItem } from '../modals/DropDownMenu';
|
||||||
import showModal from '../modals/showModal';
|
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 title = schemaName ? `${schemaName}.${pureName}` : pureName;
|
||||||
const key = title;
|
const key = title;
|
||||||
const Icon = TableIcon;
|
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 };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import useStorage from './useStorage';
|
||||||
|
|
||||||
function createGlobalState(defaultValue) {
|
function createGlobalState(defaultValue) {
|
||||||
const Context = React.createContext(null);
|
const Context = React.createContext(null);
|
||||||
@@ -19,8 +20,30 @@ function createGlobalState(defaultValue) {
|
|||||||
return [Provider, useValue, useSetValue];
|
return [Provider, useValue, useSetValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createStorageState(storageKey, defaultValue) {
|
||||||
|
const Context = React.createContext(null);
|
||||||
|
|
||||||
|
function Provider({ children }) {
|
||||||
|
const [currentvalue, setCurrentValue] = useStorage(storageKey, localStorage, defaultValue);
|
||||||
|
return <Context.Provider value={[currentvalue, setCurrentValue]}>{children}</Context.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useValue() {
|
||||||
|
return React.useContext(Context)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function useSetValue() {
|
||||||
|
return React.useContext(Context)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [Provider, useValue, useSetValue];
|
||||||
|
}
|
||||||
|
|
||||||
const [CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget] = createGlobalState('database');
|
const [CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget] = createGlobalState('database');
|
||||||
export { CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget };
|
export { CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget };
|
||||||
|
|
||||||
const [CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase] = createGlobalState(null);
|
const [CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase] = createGlobalState(null);
|
||||||
export { CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase };
|
export { CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase };
|
||||||
|
|
||||||
|
const [OpenedFilesProvider, useOpenedFiles, useSetOpenedFiles] = createStorageState('openedFiles', []);
|
||||||
|
export { OpenedFilesProvider, useOpenedFiles, useSetOpenedFiles };
|
||||||
|
|||||||
36
web/src/utility/useStorage.js
Normal file
36
web/src/utility/useStorage.js
Normal file
@@ -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];
|
||||||
|
}
|
||||||
@@ -10412,6 +10412,11 @@ uuid@^3.0.1, uuid@^3.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
||||||
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
|
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:
|
v8-compile-cache@^2.0.3:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
|
||||||
|
|||||||
Reference in New Issue
Block a user