mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-22 00:36:01 +00:00
connection list searchbox
This commit is contained in:
@@ -1,5 +1,44 @@
|
|||||||
export function filterName(filter: string, name: string) {
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
// original C# variant
|
||||||
|
// public bool Match(string value)
|
||||||
|
// {
|
||||||
|
// if (String.IsNullOrEmpty(Filter)) return false;
|
||||||
|
// if (String.IsNullOrEmpty(value)) return true;
|
||||||
|
|
||||||
|
// var camelVariants = new HashSet<string>();
|
||||||
|
// camelVariants.Add(new String(value.Where(Char.IsUpper).ToArray()));
|
||||||
|
// if (value.All(x => Char.IsUpper(x) || x == '_'))
|
||||||
|
// {
|
||||||
|
// var sb = new StringBuilder();
|
||||||
|
// for (int i = 0; i < value.Length; i++)
|
||||||
|
// {
|
||||||
|
// if (Char.IsUpper(value[i]) && (i == 0 || value[i - 1] == '_')) sb.Append(value[i]);
|
||||||
|
// }
|
||||||
|
// camelVariants.Add(sb.ToString());
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// string s = value, s0;
|
||||||
|
// do
|
||||||
|
// {
|
||||||
|
// s0 = s;
|
||||||
|
// s = Regex.Replace(s, "([A-Z])([A-Z])([A-Z])", "$1$3");
|
||||||
|
// } while (s0 != s);
|
||||||
|
// camelVariants.Add(new String(s.Where(Char.IsUpper).ToArray()));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// bool camelMatch = camelVariants.Any(x => DoMatch(Filter, x));
|
||||||
|
// if (Filter.All(Char.IsUpper)) return camelMatch;
|
||||||
|
// return DoMatch(Filter, value) || camelMatch;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function filterName(filter: string, ...names: string[]) {
|
||||||
if (!filter) return true;
|
if (!filter) return true;
|
||||||
if (!name) return false;
|
|
||||||
return name.toUpperCase().includes(filter.toUpperCase());
|
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
|
||||||
|
const tokens = filter.split(' ').map(x => x.trim());
|
||||||
|
|
||||||
|
return !!_.compact(names).find(name => !tokens.find(token => !name.toUpperCase().includes(token.toUpperCase())));
|
||||||
|
// return name.toUpperCase().includes(filter.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,92 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import _ from 'lodash';
|
||||||
import { showMenu } from '../modals/DropDownMenu';
|
|
||||||
import { AppObjectCore } from './AppObjects';
|
import { AppObjectCore } from './AppObjects';
|
||||||
import { useSetOpenedTabs } from '../utility/globalState';
|
import { useSetOpenedTabs } from '../utility/globalState';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { ExpandIcon } from '../icons';
|
||||||
|
|
||||||
export function AppObjectList({ list, makeAppObj, SubItems = undefined, onObjectClick = undefined }) {
|
const SubItemsDiv = styled.div`
|
||||||
|
margin-left: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ExpandIconHolder = styled.span`
|
||||||
|
margin-right: 5px;
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// function ExpandIcon({ isBlank, isExpanded, isHover }) {
|
||||||
|
// if (column.foreignKey) {
|
||||||
|
// return (
|
||||||
|
// <FontIcon
|
||||||
|
// icon={`far ${isExpanded ? 'fa-minus-square' : 'fa-plus-square'} `}
|
||||||
|
// {...other}
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return <FontIcon icon={`fas fa-square ${isHover ? 'lightblue' : 'white'}`} {...other} />;
|
||||||
|
// }
|
||||||
|
|
||||||
|
function AppObjectListItem({ makeAppObj, data, filter, appobj, onObjectClick, SubItems }) {
|
||||||
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||||
|
const [isHover, setIsHover] = React.useState(false);
|
||||||
|
|
||||||
|
const { matcher } = appobj;
|
||||||
|
if (matcher && !matcher(filter)) return null;
|
||||||
|
if (onObjectClick) appobj.onClick = onObjectClick;
|
||||||
|
if (SubItems) {
|
||||||
|
appobj.onClick = () => setIsExpanded(!isExpanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = (
|
||||||
|
<AppObjectCore
|
||||||
|
data={data}
|
||||||
|
makeAppObj={makeAppObj}
|
||||||
|
{...appobj}
|
||||||
|
onMouseEnter={() => setIsHover(true)}
|
||||||
|
onMouseLeave={() => setIsHover(false)}
|
||||||
|
prefix={
|
||||||
|
SubItems ? (
|
||||||
|
<ExpandIconHolder>
|
||||||
|
<ExpandIcon isSelected={isHover} isExpanded={isExpanded} />
|
||||||
|
</ExpandIconHolder>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (SubItems && isExpanded) {
|
||||||
|
res = (
|
||||||
|
<>
|
||||||
|
{res}
|
||||||
|
<SubItemsDiv>
|
||||||
|
<SubItems data={data} filter={filter} />
|
||||||
|
</SubItemsDiv>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AppObjectList({
|
||||||
|
list,
|
||||||
|
makeAppObj,
|
||||||
|
SubItems = undefined,
|
||||||
|
onObjectClick = undefined,
|
||||||
|
filter = undefined,
|
||||||
|
}) {
|
||||||
const setOpenedTabs = useSetOpenedTabs();
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
return (list || []).map(x => {
|
return (list || []).map(data => {
|
||||||
const appobj = makeAppObj(x, { setOpenedTabs });
|
const appobj = makeAppObj(data, { setOpenedTabs });
|
||||||
if (onObjectClick) appobj.onClick = onObjectClick;
|
return (
|
||||||
let res = <AppObjectCore key={appobj.key} data={x} makeAppObj={makeAppObj} {...appobj} />;
|
<AppObjectListItem
|
||||||
if (SubItems) {
|
key={appobj.key}
|
||||||
res = (
|
appobj={appobj}
|
||||||
<>
|
makeAppObj={makeAppObj}
|
||||||
{res}
|
data={data}
|
||||||
<SubItems data={x} />
|
filter={filter}
|
||||||
</>
|
onObjectClick={onObjectClick}
|
||||||
);
|
SubItems={SubItems}
|
||||||
}
|
/>
|
||||||
return res;
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,18 @@ const IconWrap = styled.span`
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function AppObjectCore({ title, Icon, Menu, data, makeAppObj, onClick, isBold, component = 'div' }) {
|
export function AppObjectCore({
|
||||||
|
title,
|
||||||
|
Icon,
|
||||||
|
Menu,
|
||||||
|
data,
|
||||||
|
makeAppObj,
|
||||||
|
onClick,
|
||||||
|
isBold,
|
||||||
|
component = 'div',
|
||||||
|
prefix = null,
|
||||||
|
...other
|
||||||
|
}) {
|
||||||
const setOpenedTabs = useSetOpenedTabs();
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
|
||||||
const handleContextMenu = event => {
|
const handleContextMenu = event => {
|
||||||
@@ -36,7 +47,13 @@ export function AppObjectCore({ title, Icon, Menu, data, makeAppObj, onClick, is
|
|||||||
const Component = component == 'div' ? AppObjectDiv : AppObjectSpan;
|
const Component = component == 'div' ? AppObjectDiv : AppObjectSpan;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component onContextMenu={handleContextMenu} onClick={onClick ? () => onClick(data) : undefined} isBold={isBold}>
|
<Component
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
|
onClick={onClick ? () => onClick(data) : undefined}
|
||||||
|
isBold={isBold}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{prefix}
|
||||||
<IconWrap>
|
<IconWrap>
|
||||||
<Icon />
|
<Icon />
|
||||||
</IconWrap>
|
</IconWrap>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { DropDownMenuItem } from '../modals/DropDownMenu';
|
|||||||
import showModal from '../modals/showModal';
|
import showModal from '../modals/showModal';
|
||||||
import ConnectionModal from '../modals/ConnectionModal';
|
import ConnectionModal from '../modals/ConnectionModal';
|
||||||
import axios from '../utility/axios';
|
import axios from '../utility/axios';
|
||||||
|
import { filterName } from '@dbgate/datalib';
|
||||||
|
|
||||||
function getIcon(engine) {
|
function getIcon(engine) {
|
||||||
switch (engine) {
|
switch (engine) {
|
||||||
@@ -38,6 +39,7 @@ export default function connectionAppObject({ _id, server, displayName, engine }
|
|||||||
const title = displayName || server;
|
const title = displayName || server;
|
||||||
const key = _id;
|
const key = _id;
|
||||||
const Icon = getIcon(engine);
|
const Icon = getIcon(engine);
|
||||||
|
const matcher = filter => filterName(filter, displayName, server);
|
||||||
|
|
||||||
return { title, key, Icon, Menu };
|
return { title, key, Icon, Menu, matcher };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import ColumnLabel from './ColumnLabel';
|
import ColumnLabel from './ColumnLabel';
|
||||||
import { filterName } from '@dbgate/datalib';
|
import { filterName } from '@dbgate/datalib';
|
||||||
import { FontIcon } from '../icons';
|
import { ExpandIcon } from '../icons';
|
||||||
|
|
||||||
const Wrapper = styled.div``;
|
const Wrapper = styled.div``;
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@ const SearchBoxWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const Button = styled.button`
|
const Button = styled.button`
|
||||||
// -webkit-appearance: none;
|
// -webkit-appearance: none;
|
||||||
// -moz-appearance: none;
|
// -moz-appearance: none;
|
||||||
// appearance: none;
|
// appearance: none;
|
||||||
// width: 50px;
|
// width: 50px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -32,17 +32,17 @@ const Input = styled.input`
|
|||||||
min-width: 90px;
|
min-width: 90px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function ExpandIcon({ display, column, isHover, ...other }) {
|
// function ExpandIcon({ display, column, isHover, ...other }) {
|
||||||
if (column.foreignKey) {
|
// if (column.foreignKey) {
|
||||||
return (
|
// return (
|
||||||
<FontIcon
|
// <FontIcon
|
||||||
icon={`far ${display.isExpandedColumn(column.uniqueName) ? 'fa-minus-square' : 'fa-plus-square'} `}
|
// icon={`far ${display.isExpandedColumn(column.uniqueName) ? 'fa-minus-square' : 'fa-plus-square'} `}
|
||||||
{...other}
|
// {...other}
|
||||||
/>
|
// />
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
return <FontIcon icon={`fas fa-square ${isHover ? 'lightblue' : 'white'}`} {...other} />;
|
// return <FontIcon icon={`fas fa-square ${isHover ? 'lightblue' : 'white'}`} {...other} />;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} props
|
* @param {object} props
|
||||||
@@ -55,9 +55,9 @@ function ColumnManagerRow(props) {
|
|||||||
return (
|
return (
|
||||||
<Row onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
|
<Row onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
|
||||||
<ExpandIcon
|
<ExpandIcon
|
||||||
display={display}
|
isBlank={!column.foreignKey}
|
||||||
column={column}
|
isExpanded={column.foreignKey && display.isExpandedColumn(column.uniqueName)}
|
||||||
isHover={isHover}
|
isSelected={isHover}
|
||||||
onClick={() => display.toggleExpandedColumn(column.uniqueName)}
|
onClick={() => display.toggleExpandedColumn(column.uniqueName)}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function getIconImage(src, props) {
|
|||||||
const { size = 16, style = {}, className, title } = props || {};
|
const { size = 16, style = {}, className, title } = props || {};
|
||||||
if (!src) return null;
|
if (!src) return null;
|
||||||
if (src.endsWith('.svg')) {
|
if (src.endsWith('.svg')) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
src = `${process.env.PUBLIC_URL}/icons/${src}`;
|
src = `${process.env.PUBLIC_URL}/icons/${src}`;
|
||||||
}
|
}
|
||||||
// if (props.alignToLine) {
|
// if (props.alignToLine) {
|
||||||
@@ -41,6 +41,13 @@ export function FontIcon({ icon, ...props }) {
|
|||||||
return <i {...props} className={className} style={style} title={props.title} />;
|
return <i {...props} className={className} style={style} title={props.title} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ExpandIcon({ isBlank = false, isExpanded = false, isSelected = false, ...other }) {
|
||||||
|
if (isBlank) {
|
||||||
|
return <FontIcon icon={`fas fa-square ${isSelected ? 'lightblue' : 'white'}`} {...other} />;
|
||||||
|
}
|
||||||
|
return <FontIcon icon={`far ${isExpanded ? 'fa-minus-square' : 'fa-plus-square'} `} {...other} />;
|
||||||
|
}
|
||||||
|
|
||||||
export const TableIcon = props => getIconImage('table2.svg', props);
|
export const TableIcon = props => getIconImage('table2.svg', props);
|
||||||
export const ViewIcon = props => getIconImage('view2.svg', props);
|
export const ViewIcon = props => getIconImage('view2.svg', props);
|
||||||
export const DatabaseIcon = props => getIconImage('database.svg', props);
|
export const DatabaseIcon = props => getIconImage('database.svg', props);
|
||||||
@@ -87,11 +94,11 @@ export const LinkedServerIcon = props => getIconImage('linkedserver.svg', props)
|
|||||||
|
|
||||||
export const EmptyIcon = props => getIconImage('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=', props);
|
export const EmptyIcon = props => getIconImage('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=', props);
|
||||||
|
|
||||||
export const TimesRedIcon = props => <FontIcon name='fas fa-times red' {...props} />;
|
export const TimesRedIcon = props => <FontIcon name="fas fa-times red" {...props} />;
|
||||||
export const TimesGreenCircleIcon = props => <FontIcon icon='fas fa-times-circle green' {...props} />;
|
export const TimesGreenCircleIcon = props => <FontIcon icon="fas fa-times-circle green" {...props} />;
|
||||||
export const GrayFilterIcon = props => <FontIcon icon='fas fa-filter lightgray' {...props} />;
|
export const GrayFilterIcon = props => <FontIcon icon="fas fa-filter lightgray" {...props} />;
|
||||||
export const ExclamationTriangleIcon = props => <FontIcon icon='fas fa-exclamation-triangle' {...props} />;
|
export const ExclamationTriangleIcon = props => <FontIcon icon="fas fa-exclamation-triangle" {...props} />;
|
||||||
export const HourGlassIcon = props => <FontIcon icon='fas fa-hourglass' {...props} />;
|
export const HourGlassIcon = props => <FontIcon icon="fas fa-hourglass" {...props} />;
|
||||||
export const InfoBlueCircleIcon = props => <FontIcon icon='fas fa-info-circle blue' {...props} />;
|
export const InfoBlueCircleIcon = props => <FontIcon icon="fas fa-info-circle blue" {...props} />;
|
||||||
|
|
||||||
export const SpinnerIcon = props => <FontIcon icon='fas fa-spinner spin' {...props} />;
|
export const SpinnerIcon = props => <FontIcon icon="fas fa-spinner spin" {...props} />;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ const MainContainer = styled.div`
|
|||||||
|
|
||||||
const OuterContainer = styled.div`
|
const OuterContainer = styled.div`
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
// min-height: 10px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: ${theme.leftPanel.width}px;
|
width: ${theme.leftPanel.width}px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -71,16 +70,16 @@ function ConnectionList() {
|
|||||||
url: 'connections/list',
|
url: 'connections/list',
|
||||||
reloadTrigger: 'connection-list-changed',
|
reloadTrigger: 'connection-list-changed',
|
||||||
});
|
});
|
||||||
|
const [filter, setFilter] = React.useState('');
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchBoxWrapper>
|
<SearchBoxWrapper>
|
||||||
<Input type="text" placeholder="Search" />
|
<Input type="text" placeholder="Search connection" value={filter} onChange={e => setFilter(e.target.value)} />
|
||||||
<Button>Hide</Button>
|
<Button>Refresh</Button>
|
||||||
<Button>Show</Button>
|
|
||||||
</SearchBoxWrapper>
|
</SearchBoxWrapper>
|
||||||
|
|
||||||
<InnerContainer>
|
<InnerContainer>
|
||||||
<AppObjectList list={connections} makeAppObj={connectionAppObject} SubItems={SubDatabaseList} />
|
<AppObjectList list={connections} makeAppObj={connectionAppObject} SubItems={SubDatabaseList} filter={filter} />
|
||||||
</InnerContainer>
|
</InnerContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user