mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-03 07:03:59 +00:00
Merge branch 'feature/search'
This commit is contained in:
@@ -2,9 +2,9 @@ import _compact from 'lodash/compact';
|
|||||||
import _isString from 'lodash/isString';
|
import _isString from 'lodash/isString';
|
||||||
import _startCase from 'lodash/startCase';
|
import _startCase from 'lodash/startCase';
|
||||||
|
|
||||||
export interface FilterNameDefinition {
|
// export interface FilterNameDefinition {
|
||||||
childName: string;
|
// childName: string;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function camelMatch(filter: string, text: string): boolean {
|
function camelMatch(filter: string, text: string): boolean {
|
||||||
if (!text) return false;
|
if (!text) return false;
|
||||||
@@ -20,7 +20,7 @@ function camelMatch(filter: string, text: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterName(filter: string, ...names: (string | FilterNameDefinition)[]) {
|
export function filterName(filter: string, ...names: string[]) {
|
||||||
if (!filter) return true;
|
if (!filter) return true;
|
||||||
|
|
||||||
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
|
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
|
||||||
@@ -28,22 +28,124 @@ export function filterName(filter: string, ...names: (string | FilterNameDefinit
|
|||||||
|
|
||||||
const namesCompacted = _compact(names);
|
const namesCompacted = _compact(names);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const namesOwn: string[] = namesCompacted.filter(x => _isString(x));
|
|
||||||
// @ts-ignore
|
|
||||||
const namesChild: string[] = namesCompacted.filter(x => x.childName).map(x => x.childName);
|
|
||||||
|
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
// const tokenUpper = token.toUpperCase();
|
const found = namesCompacted.find(name => camelMatch(token, name));
|
||||||
if (token.startsWith('#')) {
|
|
||||||
// const tokenUpperSub = tokenUpper.substring(1);
|
|
||||||
const found = namesChild.find(name => camelMatch(token.substring(1), name));
|
|
||||||
if (!found) return false;
|
if (!found) return false;
|
||||||
} else {
|
|
||||||
const found = namesOwn.find(name => camelMatch(token, name));
|
|
||||||
if (!found) return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function filterNameCompoud(
|
||||||
|
filter: string,
|
||||||
|
namesMain: string[],
|
||||||
|
namesChild: string[]
|
||||||
|
): 'main' | 'child' | 'both' | 'none' {
|
||||||
|
if (!filter) return 'both';
|
||||||
|
|
||||||
|
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
|
||||||
|
const tokens = filter.split(' ').map(x => x.trim());
|
||||||
|
|
||||||
|
const namesCompactedMain = _compact(namesMain);
|
||||||
|
const namesCompactedChild = _compact(namesChild);
|
||||||
|
|
||||||
|
let isMainOnly = true;
|
||||||
|
let isChildOnly = true;
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
const foundMain = namesCompactedMain.find(name => camelMatch(token, name));
|
||||||
|
const foundChild = namesCompactedChild.find(name => camelMatch(token, name));
|
||||||
|
if (!foundMain && !foundChild) return 'none';
|
||||||
|
|
||||||
|
if (!foundMain) isMainOnly = false;
|
||||||
|
if (!foundChild) isChildOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMainOnly && isChildOnly) return 'both';
|
||||||
|
if (isMainOnly) return 'main';
|
||||||
|
if (isChildOnly) return 'child';
|
||||||
|
return 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tokenizeBySearchFilter(text: string, filter: string): { text: string; isMatch: boolean }[] {
|
||||||
|
const camelTokens = [];
|
||||||
|
const stdTokens = [];
|
||||||
|
for (const token of filter.split(' ').map(x => x.trim())) {
|
||||||
|
if (token.replace(/[A-Z]/g, '').length == 0) {
|
||||||
|
camelTokens.push(token);
|
||||||
|
} else {
|
||||||
|
stdTokens.push(token.toUpperCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = [
|
||||||
|
{
|
||||||
|
text,
|
||||||
|
isMatch: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const token of camelTokens) {
|
||||||
|
const nextres = [];
|
||||||
|
for (const item of res) {
|
||||||
|
const indexes = [];
|
||||||
|
for (const char of token) {
|
||||||
|
const index = item.text.indexOf(char, indexes.length > 0 ? indexes[indexes.length - 1] + 1 : 0);
|
||||||
|
if (index < 0) {
|
||||||
|
indexes.push(-1);
|
||||||
|
} else {
|
||||||
|
indexes.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (indexes.some(x => x < 0)) {
|
||||||
|
nextres.push(item);
|
||||||
|
} else {
|
||||||
|
let lastIndex = 0;
|
||||||
|
for (let i = 0; i < indexes.length; i++) {
|
||||||
|
if (indexes[i] > lastIndex) {
|
||||||
|
nextres.push({ text: item.text.substring(lastIndex, indexes[i]), isMatch: false });
|
||||||
|
}
|
||||||
|
nextres.push({ text: item.text.substring(indexes[i], indexes[i] + 1), isMatch: true });
|
||||||
|
lastIndex = indexes[i] + 1;
|
||||||
|
}
|
||||||
|
nextres.push({ text: item.text.substring(lastIndex), isMatch: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = nextres;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const token of stdTokens) {
|
||||||
|
const nextres = [];
|
||||||
|
for (const item of res) {
|
||||||
|
const index = item.text?.toUpperCase().indexOf(token);
|
||||||
|
if (index < 0) {
|
||||||
|
nextres.push(item);
|
||||||
|
} else {
|
||||||
|
nextres.push({ text: item.text.substring(0, index), isMatch: false });
|
||||||
|
nextres.push({ text: item.text.substring(index, index + token.length), isMatch: true });
|
||||||
|
nextres.push({ text: item.text.substring(index + token.length), isMatch: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = nextres;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.filter(x => x.text.length > 0);
|
||||||
|
|
||||||
|
// const result = [];
|
||||||
|
// let lastMatch = 0;
|
||||||
|
// for (const token of tokens) {
|
||||||
|
// const index = text.indexOf(token, lastMatch);
|
||||||
|
// if (index < 0) {
|
||||||
|
// result.push({ token, isMatch: false });
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// result.push({ token: text.substring(lastMatch, index), isMatch: false });
|
||||||
|
// result.push({ token: text.substring(index, index + token.length), isMatch: true });
|
||||||
|
// lastMatch = index + token.length;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// result.push({ token: text.substring(lastMatch), isMatch: false });
|
||||||
|
|
||||||
|
// return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
|
|
||||||
export const extractKey = data => data.fileName;
|
export const extractKey = data => data.fileName;
|
||||||
export const createMatcher =
|
export const createMatcher =
|
||||||
({ fileName }) =>
|
|
||||||
filter =>
|
filter =>
|
||||||
|
({ fileName }) =>
|
||||||
filterName(filter, fileName);
|
filterName(filter, fileName);
|
||||||
const APP_ICONS = {
|
const APP_ICONS = {
|
||||||
'config.json': 'img json',
|
'config.json': 'img json',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export const extractKey = data => data.name;
|
export const extractKey = data => data.name;
|
||||||
export const createMatcher = data => filter => filterName(filter, data.name);
|
export const createMatcher = filter => data => filterName(filter, data.name);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import CheckboxField from '../forms/CheckboxField.svelte';
|
import CheckboxField from '../forms/CheckboxField.svelte';
|
||||||
import { copyTextToClipboard } from '../utility/clipboard';
|
import { copyTextToClipboard } from '../utility/clipboard';
|
||||||
import { showSnackbarSuccess } from '../utility/snackbar';
|
import { showSnackbarSuccess } from '../utility/snackbar';
|
||||||
|
import TokenizedFilteredText from '../widgets/TokenizedFilteredText.svelte';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
@@ -31,6 +32,8 @@
|
|||||||
export let showPinnedInsteadOfUnpin = false;
|
export let showPinnedInsteadOfUnpin = false;
|
||||||
export let indentLevel = 0;
|
export let indentLevel = 0;
|
||||||
export let disableBoldScroll = false;
|
export let disableBoldScroll = false;
|
||||||
|
export let filter = null;
|
||||||
|
export let disableHover = false;
|
||||||
|
|
||||||
$: isChecked =
|
$: isChecked =
|
||||||
checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x));
|
checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x));
|
||||||
@@ -90,6 +93,7 @@
|
|||||||
class="main"
|
class="main"
|
||||||
class:isBold
|
class:isBold
|
||||||
class:isChoosed
|
class:isChoosed
|
||||||
|
class:disableHover
|
||||||
draggable={true}
|
draggable={true}
|
||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
on:mouseup={handleMouseUp}
|
on:mouseup={handleMouseUp}
|
||||||
@@ -131,7 +135,7 @@
|
|||||||
{#if colorMark}
|
{#if colorMark}
|
||||||
<FontIcon style={`color:${colorMark}`} icon="icon square" />
|
<FontIcon style={`color:${colorMark}`} icon="icon square" />
|
||||||
{/if}
|
{/if}
|
||||||
{title}
|
<TokenizedFilteredText text={title} {filter} />
|
||||||
{#if statusIconBefore}
|
{#if statusIconBefore}
|
||||||
<span class="status">
|
<span class="status">
|
||||||
<FontIcon icon={statusIconBefore} />
|
<FontIcon icon={statusIconBefore} />
|
||||||
@@ -153,7 +157,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if extInfo}
|
{#if extInfo}
|
||||||
<span class="ext-info">
|
<span class="ext-info">
|
||||||
{extInfo}
|
<TokenizedFilteredText text={extInfo} {filter} />
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if onPin}
|
{#if onPin}
|
||||||
@@ -196,7 +200,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
.main:hover {
|
.main:hover:not(.disableHover) {
|
||||||
background-color: var(--theme-bg-hover);
|
background-color: var(--theme-bg-hover);
|
||||||
}
|
}
|
||||||
.isBold {
|
.isBold {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
export let onDropOnGroup = undefined;
|
export let onDropOnGroup = undefined;
|
||||||
export let groupContextMenu = null;
|
export let groupContextMenu = null;
|
||||||
export let collapsedGroupNames;
|
export let collapsedGroupNames;
|
||||||
|
export let filter = undefined;
|
||||||
|
|
||||||
$: isExpanded = !$collapsedGroupNames.includes(group);
|
$: isExpanded = !$collapsedGroupNames.includes(group);
|
||||||
|
|
||||||
@@ -86,6 +87,8 @@
|
|||||||
on:objectClick
|
on:objectClick
|
||||||
{disableContextMenu}
|
{disableContextMenu}
|
||||||
{passProps}
|
{passProps}
|
||||||
|
isExpandedBySearch={filter && item.isChildMatched}
|
||||||
|
{filter}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import AppObjectListItem from './AppObjectListItem.svelte';
|
import AppObjectListItem from './AppObjectListItem.svelte';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
import Link from '../elements/Link.svelte';
|
||||||
|
|
||||||
export let list;
|
export let list;
|
||||||
export let module;
|
export let module;
|
||||||
@@ -15,7 +16,7 @@
|
|||||||
export let expandIconFunc = undefined;
|
export let expandIconFunc = undefined;
|
||||||
export let checkedObjectsStore = null;
|
export let checkedObjectsStore = null;
|
||||||
export let disableContextMenu = false;
|
export let disableContextMenu = false;
|
||||||
export let passProps;
|
export let passProps = {};
|
||||||
export let getIsExpanded = null;
|
export let getIsExpanded = null;
|
||||||
export let setIsExpanded = null;
|
export let setIsExpanded = null;
|
||||||
export let sortGroups = false;
|
export let sortGroups = false;
|
||||||
@@ -25,25 +26,47 @@
|
|||||||
export let groupFunc = undefined;
|
export let groupFunc = undefined;
|
||||||
export let onDropOnGroup = undefined;
|
export let onDropOnGroup = undefined;
|
||||||
export let emptyGroupNames = [];
|
export let emptyGroupNames = [];
|
||||||
|
export let isExpandedBySearch = false;
|
||||||
|
|
||||||
export let collapsedGroupNames = writable([]);
|
export let collapsedGroupNames = writable([]);
|
||||||
export let onChangeFilteredList;
|
export let onChangeFilteredList = undefined;
|
||||||
|
|
||||||
$: filtered = !groupFunc
|
let expandLimited = false;
|
||||||
? list.filter(data => {
|
|
||||||
const matcher = module.createMatcher && module.createMatcher(data);
|
|
||||||
if (matcher && !matcher(filter)) return false;
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
: null;
|
|
||||||
|
|
||||||
$: childrenMatched = !groupFunc
|
$: matcher = module.createMatcher && module.createMatcher(filter, passProps?.searchSettings);
|
||||||
? list.filter(data => {
|
|
||||||
const matcher = module.createChildMatcher && module.createChildMatcher(data);
|
$: dataLabeled = _.compact(
|
||||||
if (matcher && !matcher(filter)) return false;
|
(list || []).map(data => {
|
||||||
return true;
|
const matchResult = matcher ? matcher(data) : true;
|
||||||
|
|
||||||
|
let isMatched = true;
|
||||||
|
let isChildMatched = true;
|
||||||
|
|
||||||
|
if (matchResult == false) {
|
||||||
|
isMatched = false;
|
||||||
|
isChildMatched = false;
|
||||||
|
} else if (matchResult == 'child') {
|
||||||
|
isMatched = true;
|
||||||
|
isChildMatched = true;
|
||||||
|
} else if (matchResult == 'main') {
|
||||||
|
isMatched = true;
|
||||||
|
isChildMatched = false;
|
||||||
|
} else if (matchResult == 'none') {
|
||||||
|
isMatched = false;
|
||||||
|
isChildMatched = false;
|
||||||
|
} else if (matchResult == 'both') {
|
||||||
|
isMatched = true;
|
||||||
|
isChildMatched = !module.disableShowChildrenWithParentMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = groupFunc ? groupFunc(data) : undefined;
|
||||||
|
return { group, data, isMatched, isChildMatched };
|
||||||
})
|
})
|
||||||
: null;
|
);
|
||||||
|
|
||||||
|
$: filtered = dataLabeled.filter(x => x.isMatched).map(x => x.data);
|
||||||
|
|
||||||
|
$: childrenMatched = dataLabeled.filter(x => x.isChildMatched).map(x => x.data);
|
||||||
|
|
||||||
// let filtered = [];
|
// let filtered = [];
|
||||||
|
|
||||||
@@ -59,17 +82,6 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
$: listGrouped = groupFunc
|
|
||||||
? _.compact(
|
|
||||||
(list || []).map(data => {
|
|
||||||
const matcher = module.createMatcher && module.createMatcher(data);
|
|
||||||
const isMatched = matcher && !matcher(filter) ? false : true;
|
|
||||||
const group = groupFunc(data);
|
|
||||||
return { group, data, isMatched };
|
|
||||||
})
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
function extendGroups(base, emptyList) {
|
function extendGroups(base, emptyList) {
|
||||||
const res = {
|
const res = {
|
||||||
...base,
|
...base,
|
||||||
@@ -81,7 +93,10 @@
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: groups = groupFunc ? extendGroups(_.groupBy(listGrouped, 'group'), emptyGroupNames) : null;
|
$: groups = groupFunc ? extendGroups(_.groupBy(dataLabeled, 'group'), emptyGroupNames) : null;
|
||||||
|
|
||||||
|
$: listLimited = isExpandedBySearch && !expandLimited ? filtered.slice(0, Math.min(filter.trim().length, 3)) : list;
|
||||||
|
$: isListLimited = isExpandedBySearch && listLimited.length < filtered.length;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if groupFunc}
|
{#if groupFunc}
|
||||||
@@ -107,7 +122,7 @@
|
|||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
{#each list as data}
|
{#each listLimited as data}
|
||||||
<AppObjectListItem
|
<AppObjectListItem
|
||||||
isHidden={!filtered.includes(data)}
|
isHidden={!filtered.includes(data)}
|
||||||
{module}
|
{module}
|
||||||
@@ -120,10 +135,19 @@
|
|||||||
{checkedObjectsStore}
|
{checkedObjectsStore}
|
||||||
{disableContextMenu}
|
{disableContextMenu}
|
||||||
{filter}
|
{filter}
|
||||||
isExpandedBySearch={childrenMatched.includes(data)}
|
isExpandedBySearch={filter && childrenMatched.includes(data)}
|
||||||
{passProps}
|
{passProps}
|
||||||
{getIsExpanded}
|
{getIsExpanded}
|
||||||
{setIsExpanded}
|
{setIsExpanded}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
{#if isListLimited}
|
||||||
|
<div class="ml-2">
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
expandLimited = true;
|
||||||
|
}}>Show next {filtered.length - listLimited.length}</Link
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -57,15 +57,19 @@
|
|||||||
{module}
|
{module}
|
||||||
{disableContextMenu}
|
{disableContextMenu}
|
||||||
{passProps}
|
{passProps}
|
||||||
|
{filter}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if (isExpanded || isExpandedBySearch) && subItemsComponent}
|
{#if (isExpanded || isExpandedBySearch) && subItemsComponent}
|
||||||
<div class="subitems">
|
<div class="subitems">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={subItemsComponent(data)}
|
this={subItemsComponent(data, {
|
||||||
|
isExpandedBySearch,
|
||||||
|
})}
|
||||||
{data}
|
{data}
|
||||||
{filter}
|
{filter}
|
||||||
{passProps}
|
{passProps}
|
||||||
|
{isExpandedBySearch}
|
||||||
isExpandedOnlyBySearch={isExpandedBySearch && !isExpanded}
|
isExpandedOnlyBySearch={isExpandedBySearch && !isExpanded}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,8 +42,8 @@
|
|||||||
|
|
||||||
export const extractKey = data => data.fileName;
|
export const extractKey = data => data.fileName;
|
||||||
export const createMatcher =
|
export const createMatcher =
|
||||||
({ fileName }) =>
|
|
||||||
filter =>
|
filter =>
|
||||||
|
({ fileName }) =>
|
||||||
filterName(filter, fileName);
|
filterName(filter, fileName);
|
||||||
const ARCHIVE_ICONS = {
|
const ARCHIVE_ICONS = {
|
||||||
'table.yaml': 'img table',
|
'table.yaml': 'img table',
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
import { getExtensions } from '../stores';
|
import { getExtensions } from '../stores';
|
||||||
|
|
||||||
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
||||||
import { exportQuickExportFile, } from '../utility/exportFileTools';
|
import { exportQuickExportFile } from '../utility/exportFileTools';
|
||||||
import openNewTab from '../utility/openNewTab';
|
import openNewTab from '../utility/openNewTab';
|
||||||
import AppObjectCore from './AppObjectCore.svelte';
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export const extractKey = data => data.name;
|
export const extractKey = data => data.name;
|
||||||
export const createMatcher = data => filter => filterName(filter, data.name);
|
export const createMatcher = filter => data => filterName(filter, data.name);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export const extractKey = ({ columnName }) => columnName;
|
export const extractKey = ({ columnName }) => columnName;
|
||||||
|
|
||||||
|
export const createMatcher =
|
||||||
|
(filter, cfg = DEFAULT_SEARCH_SETTINGS) =>
|
||||||
|
data => {
|
||||||
|
const filterArgs = [];
|
||||||
|
if (cfg.columnName) filterArgs.push(data.columnName);
|
||||||
|
if (cfg.columnComment) filterArgs.push(data.columnComment);
|
||||||
|
if (cfg.columnDataType) filterArgs.push(data.dataType);
|
||||||
|
|
||||||
|
const res = filterName(filter, ...filterArgs);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -9,6 +21,8 @@
|
|||||||
import { renameDatabaseObjectDialog, alterDatabaseDialog } from '../utility/alterDatabaseTools';
|
import { renameDatabaseObjectDialog, alterDatabaseDialog } from '../utility/alterDatabaseTools';
|
||||||
|
|
||||||
import AppObjectCore from './AppObjectCore.svelte';
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
|
import { DEFAULT_SEARCH_SETTINGS } from '../stores';
|
||||||
|
import { filterName } from 'dbgate-tools';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
@@ -59,5 +73,5 @@
|
|||||||
{extInfo}
|
{extInfo}
|
||||||
icon={getColumnIcon(data, true)}
|
icon={getColumnIcon(data, true)}
|
||||||
menu={createMenu}
|
menu={createMenu}
|
||||||
disableHover
|
\
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
<script context="module">
|
<script context="module">
|
||||||
export const extractKey = data => data._id;
|
export const extractKey = data => data._id;
|
||||||
export const createMatcher = props => filter => {
|
export const createMatcher = filter => props => {
|
||||||
const { _id, displayName, server } = props;
|
const { _id, displayName, server } = props;
|
||||||
const databases = getLocalStorage(`database_list_${_id}`) || [];
|
const databases = getLocalStorage(`database_list_${_id}`) || [];
|
||||||
return filterName(filter, displayName, server, ...databases.map(x => x.name));
|
return filterNameCompoud(
|
||||||
};
|
filter,
|
||||||
export const createChildMatcher = props => filter => {
|
[displayName, server],
|
||||||
if (!filter) return false;
|
databases.map(x => x.name)
|
||||||
const { _id } = props;
|
);
|
||||||
const databases = getLocalStorage(`database_list_${_id}`) || [];
|
|
||||||
return filterName(filter, ...databases.map(x => x.name));
|
|
||||||
};
|
};
|
||||||
export function openConnection(connection, disableExpand = false) {
|
export function openConnection(connection, disableExpand = false) {
|
||||||
if (connection.singleDatabase) {
|
if (connection.singleDatabase) {
|
||||||
@@ -106,7 +104,7 @@
|
|||||||
openedConnections,
|
openedConnections,
|
||||||
openedSingleDatabaseConnections,
|
openedSingleDatabaseConnections,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
import { filterName } from 'dbgate-tools';
|
import { filterName, filterNameCompoud } from 'dbgate-tools';
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
export const extractKey = props => props.name;
|
export const extractKey = props => props.name;
|
||||||
|
|
||||||
|
export const createMatcher = filter => props => {
|
||||||
|
const { name, displayName, server } = props;
|
||||||
|
return filterName(filter, name, displayName, server);
|
||||||
|
};
|
||||||
|
|
||||||
export function disconnectDatabaseConnection(conid, database, showConfirmation = true) {
|
export function disconnectDatabaseConnection(conid, database, showConfirmation = true) {
|
||||||
const closeCondition = x =>
|
const closeCondition = x =>
|
||||||
x.props?.conid == conid &&
|
x.props?.conid == conid &&
|
||||||
@@ -465,7 +470,13 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
|||||||
import openNewTab from '../utility/openNewTab';
|
import openNewTab from '../utility/openNewTab';
|
||||||
import AppObjectCore from './AppObjectCore.svelte';
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
|
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
|
||||||
import { extractDbNameFromComposite, extractPackageName, findEngineDriver, getConnectionLabel } from 'dbgate-tools';
|
import {
|
||||||
|
extractDbNameFromComposite,
|
||||||
|
extractPackageName,
|
||||||
|
filterName,
|
||||||
|
findEngineDriver,
|
||||||
|
getConnectionLabel,
|
||||||
|
} from 'dbgate-tools';
|
||||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||||
import { getDatabaseInfo, useUsedApps } from '../utility/metadataLoaders';
|
import { getDatabaseInfo, useUsedApps } from '../utility/metadataLoaders';
|
||||||
import { openJsonDocument } from '../tabs/JsonTab.svelte';
|
import { openJsonDocument } from '../tabs/JsonTab.svelte';
|
||||||
|
|||||||
@@ -3,14 +3,32 @@
|
|||||||
|
|
||||||
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
||||||
export const createMatcher =
|
export const createMatcher =
|
||||||
({ schemaName, pureName, columns }) =>
|
(filter, cfg = DEFAULT_SEARCH_SETTINGS) =>
|
||||||
filter =>
|
({ schemaName, pureName, objectComment, tableEngine, columns, objectTypeField, createSql }) => {
|
||||||
filterName(
|
const mainArgs = [];
|
||||||
filter,
|
const childArgs = [];
|
||||||
pureName,
|
if (cfg.schemaName) mainArgs.push(schemaName);
|
||||||
schemaName,
|
if (objectTypeField == 'tables') {
|
||||||
...(columns?.map(({ columnName }) => ({ childName: columnName })) || [])
|
if (cfg.tableName) mainArgs.push(pureName);
|
||||||
);
|
if (cfg.tableComment) mainArgs.push(objectComment);
|
||||||
|
if (cfg.tableEngine) mainArgs.push(tableEngine);
|
||||||
|
|
||||||
|
for (const column of columns || []) {
|
||||||
|
if (cfg.columnName) childArgs.push(column.columnName);
|
||||||
|
if (cfg.columnComment) childArgs.push(column.columnComment);
|
||||||
|
if (cfg.columnDataType) childArgs.push(column.dataType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cfg.sqlObjectName) mainArgs.push(pureName);
|
||||||
|
if (cfg.sqlObjectText) childArgs.push(createSql);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = filterNameCompoud(filter, mainArgs, childArgs);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const disableShowChildrenWithParentMatch = true;
|
||||||
|
|
||||||
export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
||||||
|
|
||||||
export const databaseObjectIcons = {
|
export const databaseObjectIcons = {
|
||||||
@@ -877,9 +895,11 @@
|
|||||||
import AppObjectCore from './AppObjectCore.svelte';
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
import {
|
import {
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
|
DEFAULT_SEARCH_SETTINGS,
|
||||||
extensions,
|
extensions,
|
||||||
getActiveTab,
|
getActiveTab,
|
||||||
getCurrentSettings,
|
getCurrentSettings,
|
||||||
|
getDatabaseObjectAppObjectSearchSettings,
|
||||||
getExtensions,
|
getExtensions,
|
||||||
getLastUsedDefaultActions,
|
getLastUsedDefaultActions,
|
||||||
lastUsedDefaultActions,
|
lastUsedDefaultActions,
|
||||||
@@ -892,6 +912,7 @@
|
|||||||
import {
|
import {
|
||||||
extractDbNameFromComposite,
|
extractDbNameFromComposite,
|
||||||
filterName,
|
filterName,
|
||||||
|
filterNameCompoud,
|
||||||
generateDbPairingId,
|
generateDbPairingId,
|
||||||
getAlterDatabaseScript,
|
getAlterDatabaseScript,
|
||||||
getConnectionLabel,
|
getConnectionLabel,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export const extractKey = data => data.name;
|
export const extractKey = data => data.name;
|
||||||
export const createMatcher = ({ name, title }) => filter => filterName(filter, name, title);
|
export const createMatcher =
|
||||||
|
filter =>
|
||||||
|
({ name, title }) =>
|
||||||
|
filterName(filter, name, title);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
23
packages/web/src/appobj/ProcedureLineAppObject.svelte
Normal file
23
packages/web/src/appobj/ProcedureLineAppObject.svelte
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script lang="ts" context="module">
|
||||||
|
export const extractKey = ({ columnName }) => columnName;
|
||||||
|
|
||||||
|
export const createMatcher =
|
||||||
|
(filter, cfg = DEFAULT_SEARCH_SETTINGS) =>
|
||||||
|
data => {
|
||||||
|
const filterArgs = [];
|
||||||
|
if (cfg.sqlObjectText) filterArgs.push(data.lineData);
|
||||||
|
|
||||||
|
const res = filterName(filter, ...filterArgs);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
|
import { filterName } from 'dbgate-tools';
|
||||||
|
import { DEFAULT_SEARCH_SETTINGS } from '../stores';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AppObjectCore {...$$restProps} {data} icon="icon text" title={data.lineData?.substring(0, 100)} disableHover />
|
||||||
@@ -104,8 +104,8 @@
|
|||||||
|
|
||||||
export const extractKey = data => data.file;
|
export const extractKey = data => data.file;
|
||||||
export const createMatcher =
|
export const createMatcher =
|
||||||
({ file }) =>
|
|
||||||
filter =>
|
filter =>
|
||||||
|
({ file }) =>
|
||||||
filterName(filter, file);
|
filterName(filter, file);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -12,16 +12,18 @@
|
|||||||
export let passProps;
|
export let passProps;
|
||||||
|
|
||||||
export let isExpandedOnlyBySearch;
|
export let isExpandedOnlyBySearch;
|
||||||
|
export let isExpandedBySearch;
|
||||||
|
|
||||||
$: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id });
|
$: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id });
|
||||||
$: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || [];
|
$: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || [];
|
||||||
|
|
||||||
|
// .filter(x => filterName(filter, x.name, data.displayName, data.server))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppObjectList
|
<AppObjectList
|
||||||
list={_.sortBy(
|
list={_.sortBy(dbList, x => x.sortOrder ?? x.name).map(db => ({ ...db, connection: data }))}
|
||||||
dbList.filter(x => filterName(filter, x.name, data.displayName, data.server)),
|
|
||||||
x => x.sortOrder ?? x.name
|
|
||||||
).map(db => ({ ...db, connection: data }))}
|
|
||||||
module={databaseAppObject}
|
module={databaseAppObject}
|
||||||
{passProps}
|
{passProps}
|
||||||
|
{filter}
|
||||||
|
{isExpandedBySearch}
|
||||||
/>
|
/>
|
||||||
|
|||||||
21
packages/web/src/appobj/SubProcedureLineList.svelte
Normal file
21
packages/web/src/appobj/SubProcedureLineList.svelte
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts" context="module">
|
||||||
|
export const extractKey = ({ lineData }) => lineData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import AppObjectList from './AppObjectList.svelte';
|
||||||
|
import * as procedureLineAppObject from './ProcedureLineAppObject.svelte';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
export let filter;
|
||||||
|
export let isExpandedBySearch;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AppObjectList
|
||||||
|
list={(data.createSql?.split('\n') || []).map(lineData => ({
|
||||||
|
lineData,
|
||||||
|
}))}
|
||||||
|
module={procedureLineAppObject}
|
||||||
|
{filter}
|
||||||
|
{isExpandedBySearch}
|
||||||
|
/>
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
import * as columnAppObject from './ColumnAppObject.svelte';
|
import * as columnAppObject from './ColumnAppObject.svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
export let filter;
|
||||||
|
export let isExpandedBySearch;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppObjectList
|
<AppObjectList
|
||||||
@@ -14,4 +16,6 @@
|
|||||||
foreignKey: findForeignKeyForColumn(data, col),
|
foreignKey: findForeignKeyForColumn(data, col),
|
||||||
}))}
|
}))}
|
||||||
module={columnAppObject}
|
module={columnAppObject}
|
||||||
|
{filter}
|
||||||
|
{isExpandedBySearch}
|
||||||
/>
|
/>
|
||||||
@@ -235,6 +235,7 @@
|
|||||||
{columnIndex}
|
{columnIndex}
|
||||||
{allowChangeChangeSetStructure}
|
{allowChangeChangeSetStructure}
|
||||||
isSelected={selectedColumns.includes(column.uniqueName) || currentColumnUniqueName == column.uniqueName}
|
isSelected={selectedColumns.includes(column.uniqueName) || currentColumnUniqueName == column.uniqueName}
|
||||||
|
{filter}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (domFocusField) domFocusField.focus();
|
if (domFocusField) domFocusField.focus();
|
||||||
selectedColumns = [column.uniqueName];
|
selectedColumns = [column.uniqueName];
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
export let isDynamicStructure;
|
export let isDynamicStructure;
|
||||||
|
export let filter = undefined;
|
||||||
|
|
||||||
export let tableInfo;
|
export let tableInfo;
|
||||||
export let setTableInfo;
|
export let setTableInfo;
|
||||||
@@ -83,7 +84,7 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<ColumnLabel {...column} showDataType {conid} {database} />
|
<ColumnLabel {...column} showDataType {conid} {database} {filter} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if allowChangeChangeSetStructure && !isDynamicStructure}
|
{#if allowChangeChangeSetStructure && !isDynamicStructure}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
import { openDatabaseObjectDetail } from '../appobj/DatabaseObjectAppObject.svelte';
|
import { openDatabaseObjectDetail } from '../appobj/DatabaseObjectAppObject.svelte';
|
||||||
|
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import TokenizedFilteredText from '../widgets/TokenizedFilteredText.svelte';
|
||||||
import Link from './Link.svelte';
|
import Link from './Link.svelte';
|
||||||
|
|
||||||
export let notNull = false;
|
export let notNull = false;
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
export let conid = undefined;
|
export let conid = undefined;
|
||||||
export let database = undefined;
|
export let database = undefined;
|
||||||
export let iconOverride = undefined;
|
export let iconOverride = undefined;
|
||||||
|
export let filter = undefined;
|
||||||
|
|
||||||
$: icon = iconOverride || getColumnIcon($$props, forceIcon);
|
$: icon = iconOverride || getColumnIcon($$props, forceIcon);
|
||||||
</script>
|
</script>
|
||||||
@@ -33,7 +35,7 @@
|
|||||||
{#if icon}
|
{#if icon}
|
||||||
<FontIcon {icon} />
|
<FontIcon {icon} />
|
||||||
{/if}
|
{/if}
|
||||||
{headerText || columnName}
|
<TokenizedFilteredText text={headerText || columnName} {filter} />
|
||||||
{#if extInfo}
|
{#if extInfo}
|
||||||
<span class="extinfo">{extInfo}</span>
|
<span class="extinfo">{extInfo}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -143,6 +143,7 @@
|
|||||||
'icon parent-filter': 'mdi mdi-home-alert',
|
'icon parent-filter': 'mdi mdi-home-alert',
|
||||||
'icon parent-filter-outline': 'mdi mdi-home-alert-outline',
|
'icon parent-filter-outline': 'mdi mdi-home-alert-outline',
|
||||||
'icon download': 'mdi mdi-download',
|
'icon download': 'mdi mdi-download',
|
||||||
|
'icon text': 'mdi mdi-text',
|
||||||
|
|
||||||
'icon run': 'mdi mdi-play',
|
'icon run': 'mdi mdi-play',
|
||||||
'icon chevron-down': 'mdi mdi-chevron-down',
|
'icon chevron-down': 'mdi mdi-chevron-down',
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
let submenuItem;
|
let submenuItem;
|
||||||
let submenuOffset;
|
let submenuOffset;
|
||||||
|
|
||||||
|
let switchIndex = 0;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
let closeHandlers = [];
|
let closeHandlers = [];
|
||||||
|
|
||||||
@@ -80,6 +82,14 @@
|
|||||||
submenuOffset = hoverOffset;
|
submenuOffset = hoverOffset;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (item.switchStore) {
|
||||||
|
item.switchStore.update(x => ({
|
||||||
|
...x,
|
||||||
|
[item.switchValue]: !x[item.switchValue],
|
||||||
|
}));
|
||||||
|
switchIndex++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
dispatchClose();
|
dispatchClose();
|
||||||
if (onCloseParent) onCloseParent();
|
if (onCloseParent) onCloseParent();
|
||||||
if (item.onClick) item.onClick();
|
if (item.onClick) item.onClick();
|
||||||
@@ -131,7 +141,18 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a on:click={e => handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}>
|
<a on:click={e => handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}>
|
||||||
|
<span>
|
||||||
|
{#if item.switchStoreGetter}
|
||||||
|
{#key switchIndex}
|
||||||
|
{#if item.switchStoreGetter()[item.switchValue]}
|
||||||
|
<FontIcon icon="icon checkbox-marked" padRight />
|
||||||
|
{:else}
|
||||||
|
<FontIcon icon="icon checkbox-blank" padRight />
|
||||||
|
{/if}
|
||||||
|
{/key}
|
||||||
|
{/if}
|
||||||
{item.text || item.label}
|
{item.text || item.label}
|
||||||
|
</span>
|
||||||
{#if item.keyText}
|
{#if item.keyText}
|
||||||
<span class="keyText">{formatKeyText(item.keyText)}</span>
|
<span class="keyText">{formatKeyText(item.keyText)}</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -179,6 +200,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: calc(100% - 20px);
|
max-height: calc(100% - 20px);
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyText {
|
.keyText {
|
||||||
|
|||||||
@@ -161,6 +161,25 @@ export const lastUsedDefaultActions = writableWithStorage({}, 'lastUsedDefaultAc
|
|||||||
export const selectedDatabaseObjectAppObject = writable(null);
|
export const selectedDatabaseObjectAppObject = writable(null);
|
||||||
export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string; connection: any }>(null);
|
export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string; connection: any }>(null);
|
||||||
|
|
||||||
|
export const DEFAULT_SEARCH_SETTINGS = {
|
||||||
|
collectionName: true,
|
||||||
|
schemaName: false,
|
||||||
|
tableName: true,
|
||||||
|
viewName: true,
|
||||||
|
columnName: true,
|
||||||
|
columnDataType: false,
|
||||||
|
tableComment: true,
|
||||||
|
columnComment: true,
|
||||||
|
sqlObjectName: true,
|
||||||
|
sqlObjectText: true,
|
||||||
|
tableEngine: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const databaseObjectAppObjectSearchSettings = writableWithStorage(
|
||||||
|
DEFAULT_SEARCH_SETTINGS,
|
||||||
|
'databaseObjectAppObjectSearchSettings'
|
||||||
|
);
|
||||||
|
|
||||||
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
||||||
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
||||||
);
|
);
|
||||||
@@ -359,3 +378,11 @@ lastUsedDefaultActions.subscribe(value => {
|
|||||||
lastUsedDefaultActionsValue = value;
|
lastUsedDefaultActionsValue = value;
|
||||||
});
|
});
|
||||||
export const getLastUsedDefaultActions = () => lastUsedDefaultActionsValue;
|
export const getLastUsedDefaultActions = () => lastUsedDefaultActionsValue;
|
||||||
|
|
||||||
|
let databaseObjectAppObjectSearchSettingsValue: typeof DEFAULT_SEARCH_SETTINGS = {
|
||||||
|
...DEFAULT_SEARCH_SETTINGS,
|
||||||
|
};
|
||||||
|
databaseObjectAppObjectSearchSettings.subscribe(value => {
|
||||||
|
databaseObjectAppObjectSearchSettingsValue = value;
|
||||||
|
});
|
||||||
|
export const getDatabaseObjectAppObjectSearchSettings = () => databaseObjectAppObjectSearchSettingsValue;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
import AppObjectList from '../appobj/AppObjectList.svelte';
|
import AppObjectList from '../appobj/AppObjectList.svelte';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import * as databaseObjectAppObject from '../appobj/DatabaseObjectAppObject.svelte';
|
import * as databaseObjectAppObject from '../appobj/DatabaseObjectAppObject.svelte';
|
||||||
import SubColumnParamList from '../appobj/SubColumnParamList.svelte';
|
import SubTableColumnList from '../appobj/SubTableColumnList.svelte';
|
||||||
import { chevronExpandIcon } from '../icons/expandIcons';
|
import { chevronExpandIcon } from '../icons/expandIcons';
|
||||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||||
@@ -38,8 +38,10 @@
|
|||||||
import { extractDbNameFromComposite, findEngineDriver } from 'dbgate-tools';
|
import { extractDbNameFromComposite, findEngineDriver } from 'dbgate-tools';
|
||||||
import {
|
import {
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
|
databaseObjectAppObjectSearchSettings,
|
||||||
extensions,
|
extensions,
|
||||||
focusedConnectionOrDatabase,
|
focusedConnectionOrDatabase,
|
||||||
|
getDatabaseObjectAppObjectSearchSettings,
|
||||||
getSelectedDatabaseObjectAppObject,
|
getSelectedDatabaseObjectAppObject,
|
||||||
selectedDatabaseObjectAppObject,
|
selectedDatabaseObjectAppObject,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
@@ -53,6 +55,7 @@
|
|||||||
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
|
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
|
||||||
import FocusedConnectionInfoWidget from './FocusedConnectionInfoWidget.svelte';
|
import FocusedConnectionInfoWidget from './FocusedConnectionInfoWidget.svelte';
|
||||||
import SubProcedureParamList from '../appobj/SubProcedureParamList.svelte';
|
import SubProcedureParamList from '../appobj/SubProcedureParamList.svelte';
|
||||||
|
import SubProcedureLineList from '../appobj/SubProcedureLineList.svelte';
|
||||||
|
|
||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
@@ -124,11 +127,32 @@
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: flatFilteredList = objectList.filter(data => {
|
function createSearchMenu() {
|
||||||
const matcher = databaseObjectAppObject.createMatcher(data);
|
const res = [];
|
||||||
if (matcher && !matcher(filter)) return false;
|
if (driver?.databaseEngineTypes?.includes('document')) {
|
||||||
return true;
|
res.push({ label: 'Collection names' });
|
||||||
});
|
}
|
||||||
|
if (driver?.databaseEngineTypes?.includes('sql')) {
|
||||||
|
res.push({ label: 'Schema name', switchValue: 'schemaName' });
|
||||||
|
res.push({ label: 'Table name', switchValue: 'tableName' });
|
||||||
|
res.push({ label: 'View name', switchValue: 'viewName' });
|
||||||
|
res.push({ label: 'Column name', switchValue: 'columnName' });
|
||||||
|
res.push({ label: 'Column data type', switchValue: 'columnType' });
|
||||||
|
res.push({ label: 'Table comment', switchValue: 'tableComment' });
|
||||||
|
res.push({ label: 'Column comment', switchValue: 'columnComment' });
|
||||||
|
res.push({ label: 'Procedure/function/trigger name', switchValue: 'sqlObjectName' });
|
||||||
|
res.push({ label: 'Procedure/function/trigger text', switchValue: 'sqlObjectText' });
|
||||||
|
res.push({ label: 'Table engine', switchValue: 'tableEngine' });
|
||||||
|
}
|
||||||
|
return res.map(item => ({
|
||||||
|
...item,
|
||||||
|
switchStore: databaseObjectAppObjectSearchSettings,
|
||||||
|
switchStoreGetter: getDatabaseObjectAppObjectSearchSettings,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
$: matcher = databaseObjectAppObject.createMatcher(filter, $databaseObjectAppObjectSearchSettings);
|
||||||
|
$: flatFilteredList = objectList.filter(data => !matcher || matcher(data));
|
||||||
|
|
||||||
export function focus() {
|
export function focus() {
|
||||||
domListHandler?.focusFirst();
|
domListHandler?.focusFirst();
|
||||||
@@ -184,7 +208,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<SearchBoxWrapper>
|
<SearchBoxWrapper>
|
||||||
<SearchInput
|
<SearchInput
|
||||||
placeholder="Search in tables, objects, # prefix in columns"
|
placeholder="Search in tables, views, procedures"
|
||||||
bind:value={filter}
|
bind:value={filter}
|
||||||
bind:this={domFilter}
|
bind:this={domFilter}
|
||||||
onFocusFilteredList={() => {
|
onFocusFilteredList={() => {
|
||||||
@@ -192,7 +216,12 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CloseSearchButton bind:filter />
|
<CloseSearchButton bind:filter />
|
||||||
|
{#if filter}
|
||||||
|
<DropDownButton icon="icon filter" menu={createSearchMenu} />
|
||||||
|
{/if}
|
||||||
|
{#if !filter}
|
||||||
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
||||||
|
{/if}
|
||||||
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list" square>
|
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list" square>
|
||||||
<FontIcon icon="icon refresh" />
|
<FontIcon icon="icon refresh" />
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
@@ -240,10 +269,14 @@
|
|||||||
.map(x => ({ ...x, conid, database }))}
|
.map(x => ({ ...x, conid, database }))}
|
||||||
module={databaseObjectAppObject}
|
module={databaseObjectAppObject}
|
||||||
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
|
groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)}
|
||||||
subItemsComponent={data =>
|
subItemsComponent={(data, { isExpandedBySearch }) =>
|
||||||
data.objectTypeField == 'procedures' || data.objectTypeField == 'functions'
|
data.objectTypeField == 'procedures' || data.objectTypeField == 'functions'
|
||||||
? SubProcedureParamList
|
? isExpandedBySearch
|
||||||
: SubColumnParamList}
|
? SubProcedureLineList
|
||||||
|
: SubProcedureParamList
|
||||||
|
: isExpandedBySearch && (data.objectTypeField == 'views' || data.objectTypeField == 'matviews')
|
||||||
|
? SubProcedureLineList
|
||||||
|
: SubTableColumnList}
|
||||||
isExpandable={data =>
|
isExpandable={data =>
|
||||||
data.objectTypeField == 'tables' ||
|
data.objectTypeField == 'tables' ||
|
||||||
data.objectTypeField == 'views' ||
|
data.objectTypeField == 'views' ||
|
||||||
@@ -256,6 +289,7 @@
|
|||||||
showPinnedInsteadOfUnpin: true,
|
showPinnedInsteadOfUnpin: true,
|
||||||
connection: $connection,
|
connection: $connection,
|
||||||
hideSchemaName: !!$appliedCurrentSchema,
|
hideSchemaName: !!$appliedCurrentSchema,
|
||||||
|
searchSettings: $databaseObjectAppObjectSearchSettings,
|
||||||
}}
|
}}
|
||||||
getIsExpanded={data =>
|
getIsExpanded={data =>
|
||||||
expandedObjects.includes(`${data.objectTypeField}||${data.schemaName}||${data.pureName}`)}
|
expandedObjects.includes(`${data.objectTypeField}||${data.schemaName}||${data.pureName}`)}
|
||||||
|
|||||||
26
packages/web/src/widgets/TokenizedFilteredText.svelte
Normal file
26
packages/web/src/widgets/TokenizedFilteredText.svelte
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { tokenizeBySearchFilter } from 'dbgate-tools';
|
||||||
|
|
||||||
|
export let text = '';
|
||||||
|
export let filter = '';
|
||||||
|
|
||||||
|
$: tokenized = filter ? tokenizeBySearchFilter(text, filter) : null;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if tokenized}
|
||||||
|
{#each tokenized as token}
|
||||||
|
{#if token.isMatch}
|
||||||
|
<span class="highlight">{token.text}</span>
|
||||||
|
{:else}
|
||||||
|
{token.text}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{text}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.highlight {
|
||||||
|
background-color: var(--theme-bg-orange);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user