mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-25 01:45:59 +00:00
introduced yarn workspace
This commit is contained in:
50
packages/web/src/utility/ObjectListControl.js
Normal file
50
packages/web/src/utility/ObjectListControl.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import useFetch from '../utility/useFetch';
|
||||
import styled from 'styled-components';
|
||||
import theme from '../theme';
|
||||
import TableControl, { TableColumn } from './TableControl';
|
||||
import { AppObjectControl } from '../appobj/AppObjects';
|
||||
import columnAppObject from '../appobj/columnAppObject';
|
||||
|
||||
const ObjectListWrapper = styled.div`
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const ObjectListHeader = styled.div`
|
||||
background-color: #ebedef;
|
||||
padding: 5px;
|
||||
`;
|
||||
|
||||
const ObjectListHeaderTitle = styled.span`
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
`;
|
||||
|
||||
const ObjectListBody = styled.div`
|
||||
margin: 20px;
|
||||
// margin-left: 20px;
|
||||
// margin-right: 20px;
|
||||
// margin-top: 3px;
|
||||
`;
|
||||
|
||||
export default function ObjectListControl({ collection = [], title, showIfEmpty = false, makeAppObj, children }) {
|
||||
if (collection.length == 0 && !showIfEmpty) return null;
|
||||
|
||||
return (
|
||||
<ObjectListWrapper>
|
||||
<ObjectListHeader>
|
||||
<ObjectListHeaderTitle>{title}</ObjectListHeaderTitle>
|
||||
</ObjectListHeader>
|
||||
<ObjectListBody>
|
||||
<TableControl rows={collection}>
|
||||
<TableColumn
|
||||
fieldName="displayName"
|
||||
header="Name"
|
||||
formatter={col => <AppObjectControl data={col} makeAppObj={makeAppObj} component="span" />}
|
||||
/>
|
||||
{children}
|
||||
</TableControl>
|
||||
</ObjectListBody>
|
||||
</ObjectListWrapper>
|
||||
);
|
||||
}
|
||||
18
packages/web/src/utility/SocketProvider.js
Normal file
18
packages/web/src/utility/SocketProvider.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import io from 'socket.io-client';
|
||||
import React from 'react';
|
||||
|
||||
const SocketContext = React.createContext(null);
|
||||
|
||||
export function SocketProvider({ children }) {
|
||||
const [socket, setSocket] = React.useState();
|
||||
React.useEffect(() => {
|
||||
// const newSocket = io('http://localhost:3000', { transports: ['websocket'] });
|
||||
const newSocket = io('http://localhost:3000');
|
||||
setSocket(newSocket);
|
||||
}, []);
|
||||
return <SocketContext.Provider value={socket}>{children}</SocketContext.Provider>;
|
||||
}
|
||||
|
||||
export default function useSocket() {
|
||||
return React.useContext(SocketContext);
|
||||
}
|
||||
62
packages/web/src/utility/TableControl.js
Normal file
62
packages/web/src/utility/TableControl.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import useFetch from '../utility/useFetch';
|
||||
import styled from 'styled-components';
|
||||
import theme from '../theme';
|
||||
|
||||
const Table = styled.table`
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
`;
|
||||
const TableHead = styled.thead``;
|
||||
const TableBody = styled.tbody``;
|
||||
const TableHeaderRow = styled.tr``;
|
||||
const TableBodyRow = styled.tr``;
|
||||
const TableHeaderCell = styled.td`
|
||||
border: 1px solid #e8eef4;
|
||||
background-color: #e8eef4;
|
||||
padding: 5px;
|
||||
`;
|
||||
const TableBodyCell = styled.td`
|
||||
border: 1px solid #e8eef4;
|
||||
padding: 5px;
|
||||
`;
|
||||
|
||||
export function TableColumn({ fieldName, header, sortable = false, formatter = undefined }) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
function format(row, col) {
|
||||
const { formatter, fieldName } = col;
|
||||
if (formatter) return formatter(row);
|
||||
return row[fieldName];
|
||||
}
|
||||
|
||||
export default function TableControl({ rows = [], children }) {
|
||||
console.log('children', children);
|
||||
|
||||
const columns = (children instanceof Array ? _.flatten(children) : [children])
|
||||
.filter(child => child && child.props && child.props.fieldName)
|
||||
.map(child => child.props);
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableHeaderRow>
|
||||
{columns.map(x => (
|
||||
<TableHeaderCell key={x.fieldName}>{x.header}</TableHeaderCell>
|
||||
))}
|
||||
</TableHeaderRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row, index) => (
|
||||
<TableBodyRow key={index}>
|
||||
{columns.map(col => (
|
||||
<TableBodyCell key={col.fieldName}>{format(row, col)}</TableBodyCell>
|
||||
))}
|
||||
</TableBodyRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
5
packages/web/src/utility/axios.js
Normal file
5
packages/web/src/utility/axios.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export default axios.create({
|
||||
baseURL: 'http://localhost:3000',
|
||||
});
|
||||
27
packages/web/src/utility/common.js
Normal file
27
packages/web/src/utility/common.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import uuidv1 from 'uuid/v1';
|
||||
|
||||
export class LoadingToken {
|
||||
constructor() {
|
||||
this.isCanceled = false;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.isCanceled = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function sleep(milliseconds) {
|
||||
return new Promise(resolve => window.setTimeout(() => resolve(null), milliseconds));
|
||||
}
|
||||
|
||||
export function openNewTab(setOpenedTabs, newTab) {
|
||||
const tabid = uuidv1();
|
||||
setOpenedTabs(files => [
|
||||
...(files || []).map(x => ({ ...x, selected: false })),
|
||||
{
|
||||
tabid,
|
||||
selected: true,
|
||||
...newTab,
|
||||
},
|
||||
]);
|
||||
}
|
||||
49
packages/web/src/utility/forms.js
Normal file
49
packages/web/src/utility/forms.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { TextField, SelectField } from './inputs';
|
||||
import { Field, useFormikContext } from 'formik';
|
||||
|
||||
export const FormRow = styled.div`
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
`;
|
||||
|
||||
export const FormLabel = styled.div`
|
||||
width: 10vw;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
export const FormValue = styled.div``;
|
||||
|
||||
export function FormTextField({ label, ...other }) {
|
||||
return (
|
||||
<FormRow>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<FormValue>
|
||||
<Field {...other} as={TextField} />
|
||||
</FormValue>
|
||||
</FormRow>
|
||||
);
|
||||
}
|
||||
|
||||
export function FormSelectField({ label, children, ...other }) {
|
||||
return (
|
||||
<FormRow>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<FormValue>
|
||||
<Field {...other} as={SelectField}>
|
||||
{children}
|
||||
</Field>
|
||||
</FormValue>
|
||||
</FormRow>
|
||||
);
|
||||
}
|
||||
|
||||
export function FormSubmit({ text }) {
|
||||
return <input type="submit" value={text} />;
|
||||
}
|
||||
|
||||
export function FormButton({ text, onClick, ...other }) {
|
||||
const { values } = useFormikContext();
|
||||
return <input type="button" value={text} onClick={() => onClick(values)} {...other} />;
|
||||
}
|
||||
49
packages/web/src/utility/globalState.js
Normal file
49
packages/web/src/utility/globalState.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import useStorage from './useStorage';
|
||||
|
||||
function createGlobalState(defaultValue) {
|
||||
const Context = React.createContext(null);
|
||||
|
||||
function Provider({ children }) {
|
||||
const [currentvalue, setCurrentValue] = React.useState(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];
|
||||
}
|
||||
|
||||
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');
|
||||
export { CurrentWidgetProvider, useCurrentWidget, useSetCurrentWidget };
|
||||
|
||||
const [CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase] = createGlobalState(null);
|
||||
export { CurrentDatabaseProvider, useCurrentDatabase, useSetCurrentDatabase };
|
||||
|
||||
const [OpenedTabsProvider, useOpenedTabs, useSetOpenedTabs] = createStorageState('openedTabs', []);
|
||||
export { OpenedTabsProvider, useOpenedTabs, useSetOpenedTabs };
|
||||
13
packages/web/src/utility/inputs.js
Normal file
13
packages/web/src/utility/inputs.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
export function TextField({ ...other }) {
|
||||
return <input type="text" {...other}></input>;
|
||||
}
|
||||
|
||||
export function SelectField({ children, ...other }) {
|
||||
return (
|
||||
<select {...other}>
|
||||
{children}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
7
packages/web/src/utility/layout.js
Normal file
7
packages/web/src/utility/layout.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Grid = styled.div``;
|
||||
|
||||
export const Row = styled.div``;
|
||||
|
||||
export const Col = styled.div``;
|
||||
107
packages/web/src/utility/useDimensions.js
Normal file
107
packages/web/src/utility/useDimensions.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// import { useState, useCallback, useLayoutEffect } from 'react';
|
||||
|
||||
// function getDimensionObject(node) {
|
||||
// const rect = node.getBoundingClientRect();
|
||||
|
||||
// return {
|
||||
// width: rect.width,
|
||||
// height: rect.height,
|
||||
// top: 'x' in rect ? rect.x : rect.top,
|
||||
// left: 'y' in rect ? rect.y : rect.left,
|
||||
// x: 'x' in rect ? rect.x : rect.left,
|
||||
// y: 'y' in rect ? rect.y : rect.top,
|
||||
// right: rect.right,
|
||||
// bottom: rect.bottom,
|
||||
// };
|
||||
// }
|
||||
|
||||
// function useDimensions({ liveMeasure = true } = {}) {
|
||||
// const [dimensions, setDimensions] = useState({});
|
||||
// const [node, setNode] = useState(null);
|
||||
|
||||
// const ref = useCallback(node => {
|
||||
// setNode(node);
|
||||
// }, []);
|
||||
|
||||
// useLayoutEffect(() => {
|
||||
// if (node) {
|
||||
// const measure = () => window.requestAnimationFrame(() => setDimensions(getDimensionObject(node)));
|
||||
// measure();
|
||||
|
||||
// if (liveMeasure) {
|
||||
// window.addEventListener('resize', measure);
|
||||
// window.addEventListener('scroll', measure);
|
||||
|
||||
// return () => {
|
||||
// window.removeEventListener('resize', measure);
|
||||
// window.removeEventListener('scroll', measure);
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// }, [node]);
|
||||
|
||||
// return [ref, dimensions, node];
|
||||
// }
|
||||
|
||||
// export default useDimensions;
|
||||
|
||||
import { useLayoutEffect, useState, useCallback } from 'react';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
// Export hook
|
||||
export default function useDimensions(dependencies = []) {
|
||||
const [node, setNode] = useState(null);
|
||||
const ref = useCallback(newNode => {
|
||||
setNode(newNode);
|
||||
}, []);
|
||||
|
||||
// Keep track of measurements
|
||||
const [dimensions, setDimensions] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
// Define measure function
|
||||
const measure = useCallback(innerNode => {
|
||||
const rect = innerNode.getBoundingClientRect();
|
||||
setDimensions({
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set initial measurements
|
||||
measure(node);
|
||||
|
||||
// Observe resizing of element
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
measure(node);
|
||||
});
|
||||
|
||||
resizeObserver.observe(node);
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [node, measure, ...dependencies]);
|
||||
|
||||
return [ref, dimensions, node];
|
||||
}
|
||||
41
packages/web/src/utility/useFetch.js
Normal file
41
packages/web/src/utility/useFetch.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import axios from './axios';
|
||||
import useSocket from './SocketProvider';
|
||||
import stableStringify from 'json-stable-stringify';
|
||||
|
||||
export default function useFetch({
|
||||
url,
|
||||
params = undefined,
|
||||
defaultValue = undefined,
|
||||
reloadTrigger = undefined,
|
||||
...config
|
||||
}) {
|
||||
const [value, setValue] = React.useState(defaultValue);
|
||||
const [loadCounter, setLoadCounter] = React.useState(0);
|
||||
const socket = useSocket();
|
||||
|
||||
const handleReload = () => {
|
||||
setLoadCounter(loadCounter + 1);
|
||||
};
|
||||
|
||||
async function loadValue() {
|
||||
const resp = await axios.request({
|
||||
method: 'get',
|
||||
params,
|
||||
url,
|
||||
...config,
|
||||
});
|
||||
setValue(resp.data);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
loadValue();
|
||||
if (reloadTrigger && socket) {
|
||||
socket.on(reloadTrigger, handleReload);
|
||||
return () => {
|
||||
socket.off(reloadTrigger, handleReload);
|
||||
};
|
||||
}
|
||||
}, [url, stableStringify(params), socket, loadCounter]);
|
||||
|
||||
return value;
|
||||
}
|
||||
36
packages/web/src/utility/useStorage.js
Normal file
36
packages/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];
|
||||
}
|
||||
Reference in New Issue
Block a user