From a20b4b3339fbffa23832b773a2493e8cb19d70a6 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 11:47:53 +0100 Subject: [PATCH 01/12] search settings --- packages/tools/src/filterName.ts | 46 +++++++++++-------- packages/web/src/appobj/AppObjectCore.svelte | 1 + packages/web/src/appobj/AppObjectList.svelte | 6 +-- .../web/src/appobj/AppObjectListItem.svelte | 1 + .../src/appobj/DatabaseObjectAppObject.svelte | 34 ++++++++++---- packages/web/src/modals/DropDownMenu.svelte | 24 +++++++++- packages/web/src/stores.ts | 29 +++++++++++- packages/web/src/widgets/SqlObjectList.svelte | 38 +++++++++++++-- 8 files changed, 145 insertions(+), 34 deletions(-) diff --git a/packages/tools/src/filterName.ts b/packages/tools/src/filterName.ts index 81c1d0b8e..8cdc38d43 100644 --- a/packages/tools/src/filterName.ts +++ b/packages/tools/src/filterName.ts @@ -2,9 +2,9 @@ import _compact from 'lodash/compact'; import _isString from 'lodash/isString'; import _startCase from 'lodash/startCase'; -export interface FilterNameDefinition { - childName: string; -} +// export interface FilterNameDefinition { +// childName: string; +// } function camelMatch(filter: string, text: string): boolean { 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; // const camelVariants = [name.replace(/[^A-Z]/g, '')] @@ -28,22 +28,32 @@ export function filterName(filter: string, ...names: (string | FilterNameDefinit 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) { - // const tokenUpper = token.toUpperCase(); - if (token.startsWith('#')) { - // const tokenUpperSub = tokenUpper.substring(1); - const found = namesChild.find(name => camelMatch(token.substring(1), name)); - if (!found) return false; - } else { - const found = namesOwn.find(name => camelMatch(token, name)); - if (!found) return false; - } + const found = namesCompacted.find(name => camelMatch(token, name)); + if (!found) return false; } return true; } + +export function tokenizeBySearchFilter(text: string, filter: string): { token: string; isMatch: boolean }[] { + const tokens = filter.split(' ').map(x => x.trim()); + + 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; +} diff --git a/packages/web/src/appobj/AppObjectCore.svelte b/packages/web/src/appobj/AppObjectCore.svelte index e6c099d1f..6f7f072a0 100644 --- a/packages/web/src/appobj/AppObjectCore.svelte +++ b/packages/web/src/appobj/AppObjectCore.svelte @@ -31,6 +31,7 @@ export let showPinnedInsteadOfUnpin = false; export let indentLevel = 0; export let disableBoldScroll = false; + export let filter = null; $: isChecked = checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x)); diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index 87662a659..6a7387cf8 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -31,7 +31,7 @@ $: filtered = !groupFunc ? list.filter(data => { - const matcher = module.createMatcher && module.createMatcher(data); + const matcher = module.createMatcher && module.createMatcher(data, passProps?.searchSettings); if (matcher && !matcher(filter)) return false; return true; }) @@ -39,7 +39,7 @@ $: childrenMatched = !groupFunc ? list.filter(data => { - const matcher = module.createChildMatcher && module.createChildMatcher(data); + const matcher = module.createChildMatcher && module.createChildMatcher(data, passProps?.searchSettings); if (matcher && !matcher(filter)) return false; return true; }) @@ -62,7 +62,7 @@ $: listGrouped = groupFunc ? _.compact( (list || []).map(data => { - const matcher = module.createMatcher && module.createMatcher(data); + const matcher = module.createMatcher && module.createMatcher(data, passProps?.searchSettings); const isMatched = matcher && !matcher(filter) ? false : true; const group = groupFunc(data); return { group, data, isMatched }; diff --git a/packages/web/src/appobj/AppObjectListItem.svelte b/packages/web/src/appobj/AppObjectListItem.svelte index 395127e63..518564b27 100644 --- a/packages/web/src/appobj/AppObjectListItem.svelte +++ b/packages/web/src/appobj/AppObjectListItem.svelte @@ -57,6 +57,7 @@ {module} {disableContextMenu} {passProps} + {filter} /> {#if (isExpanded || isExpandedBySearch) && subItemsComponent} diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index 5e4ffeb5f..aae68a01e 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -3,14 +3,30 @@ export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName); export const createMatcher = - ({ schemaName, pureName, columns }) => - filter => - filterName( - filter, - pureName, - schemaName, - ...(columns?.map(({ columnName }) => ({ childName: columnName })) || []) - ); + ( + { schemaName, pureName, objectComment, tableEngine, columns, objectTypeField, createSql }, + cfg = DEFAULT_SEARCH_SETTINGS + ) => + filter => { + const filterArgs = []; + if (cfg.schemaName) filterArgs.push(schemaName); + if (objectTypeField == 'tables') { + if (cfg.tableName) filterArgs.push(pureName); + if (cfg.tableComment) filterArgs.push(objectComment); + if (cfg.tableEngine) filterArgs.push(tableEngine); + + for (const column of columns || []) { + if (cfg.columnName) filterArgs.push(column.columnName); + if (cfg.columnComment) filterArgs.push(column.columnComment); + if (cfg.columnDataType) filterArgs.push(column.dataType); + } + } else { + if (cfg.sqlObjectName) filterArgs.push(pureName); + if (cfg.sqlObjectText) filterArgs.push(createSql); + } + + return filterName(filter, ...filterArgs); + }; export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName); export const databaseObjectIcons = { @@ -877,9 +893,11 @@ import AppObjectCore from './AppObjectCore.svelte'; import { currentDatabase, + DEFAULT_SEARCH_SETTINGS, extensions, getActiveTab, getCurrentSettings, + getDatabaseObjectAppObjectSearchSettings, getExtensions, getLastUsedDefaultActions, lastUsedDefaultActions, diff --git a/packages/web/src/modals/DropDownMenu.svelte b/packages/web/src/modals/DropDownMenu.svelte index e1969c725..6af2f2e90 100644 --- a/packages/web/src/modals/DropDownMenu.svelte +++ b/packages/web/src/modals/DropDownMenu.svelte @@ -55,6 +55,8 @@ let submenuItem; let submenuOffset; + let switchIndex = 0; + const dispatch = createEventDispatcher(); let closeHandlers = []; @@ -80,6 +82,14 @@ submenuOffset = hoverOffset; return; } + if (item.switchStore) { + item.switchStore.update(x => ({ + ...x, + [item.switchValue]: !x[item.switchValue], + })); + switchIndex++; + return; + } dispatchClose(); if (onCloseParent) onCloseParent(); if (item.onClick) item.onClick(); @@ -131,7 +141,18 @@ }} > handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}> - {item.text || item.label} + + {#if item.switchStoreGetter} + {#key switchIndex} + {#if item.switchStoreGetter()[item.switchValue]} + + {:else} + + {/if} + {/key} + {/if} + {item.text || item.label} + {#if item.keyText} {formatKeyText(item.keyText)} {/if} @@ -179,6 +200,7 @@ white-space: nowrap; overflow-y: auto; max-height: calc(100% - 20px); + user-select: none; } .keyText { diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts index a48d2018b..997bfcae4 100644 --- a/packages/web/src/stores.ts +++ b/packages/web/src/stores.ts @@ -161,6 +161,25 @@ export const lastUsedDefaultActions = writableWithStorage({}, 'lastUsedDefaultAc export const selectedDatabaseObjectAppObject = writable(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: true, + 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]) => $extensions.themes.find(x => x.themeClassName == $currentTheme) ); @@ -358,4 +377,12 @@ let lastUsedDefaultActionsValue = {}; lastUsedDefaultActions.subscribe(value => { lastUsedDefaultActionsValue = value; }); -export const getLastUsedDefaultActions = () => lastUsedDefaultActionsValue; \ No newline at end of file +export const getLastUsedDefaultActions = () => lastUsedDefaultActionsValue; + +let databaseObjectAppObjectSearchSettingsValue: typeof DEFAULT_SEARCH_SETTINGS = { + ...DEFAULT_SEARCH_SETTINGS, +}; +databaseObjectAppObjectSearchSettings.subscribe(value => { + databaseObjectAppObjectSearchSettingsValue = value; +}); +export const getDatabaseObjectAppObjectSearchSettings = () => databaseObjectAppObjectSearchSettingsValue; diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index 2be2c6ee1..aeb729871 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -38,8 +38,10 @@ import { extractDbNameFromComposite, findEngineDriver } from 'dbgate-tools'; import { currentDatabase, + databaseObjectAppObjectSearchSettings, extensions, focusedConnectionOrDatabase, + getDatabaseObjectAppObjectSearchSettings, getSelectedDatabaseObjectAppObject, selectedDatabaseObjectAppObject, } from '../stores'; @@ -124,8 +126,32 @@ return res; } + function createSearchMenu() { + const res = []; + if (driver?.databaseEngineTypes?.includes('document')) { + 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, + })); + } + $: flatFilteredList = objectList.filter(data => { - const matcher = databaseObjectAppObject.createMatcher(data); + const matcher = databaseObjectAppObject.createMatcher(data, $databaseObjectAppObjectSearchSettings); if (matcher && !matcher(filter)) return false; return true; }); @@ -184,7 +210,7 @@ {:else} { @@ -192,7 +218,12 @@ }} /> - + {#if filter} + + {/if} + {#if !filter} + + {/if} @@ -256,6 +287,7 @@ showPinnedInsteadOfUnpin: true, connection: $connection, hideSchemaName: !!$appliedCurrentSchema, + searchSettings: $databaseObjectAppObjectSearchSettings, }} getIsExpanded={data => expandedObjects.includes(`${data.objectTypeField}||${data.schemaName}||${data.pureName}`)} From c46c9a4e16bb008962edd8bcc1443b69bc456e8a Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 13:08:59 +0100 Subject: [PATCH 02/12] lisgt matcher refactor --- .../web/src/appobj/AppFileAppObject.svelte | 2 +- .../web/src/appobj/AppFolderAppObject.svelte | 2 +- packages/web/src/appobj/AppObjectList.svelte | 22 +++++-------------- .../src/appobj/ArchiveFileAppObject.svelte | 4 ++-- .../src/appobj/ArchiveFolderAppObject.svelte | 2 +- .../web/src/appobj/ConnectionAppObject.svelte | 4 ++-- .../src/appobj/DatabaseObjectAppObject.svelte | 10 ++++----- packages/web/src/appobj/MacroAppObject.svelte | 5 ++++- .../web/src/appobj/SavedFileAppObject.svelte | 2 +- packages/web/src/widgets/SqlObjectList.svelte | 7 ++---- 10 files changed, 24 insertions(+), 36 deletions(-) diff --git a/packages/web/src/appobj/AppFileAppObject.svelte b/packages/web/src/appobj/AppFileAppObject.svelte index c51176f6b..f3ed7e257 100644 --- a/packages/web/src/appobj/AppFileAppObject.svelte +++ b/packages/web/src/appobj/AppFileAppObject.svelte @@ -29,8 +29,8 @@ export const extractKey = data => data.fileName; export const createMatcher = - ({ fileName }) => filter => + ({ fileName }) => filterName(filter, fileName); const APP_ICONS = { 'config.json': 'img json', diff --git a/packages/web/src/appobj/AppFolderAppObject.svelte b/packages/web/src/appobj/AppFolderAppObject.svelte index 857228e85..8cbc52a8c 100644 --- a/packages/web/src/appobj/AppFolderAppObject.svelte +++ b/packages/web/src/appobj/AppFolderAppObject.svelte @@ -1,6 +1,6 @@ diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index aeb729871..3aca347a2 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -150,11 +150,8 @@ })); } - $: flatFilteredList = objectList.filter(data => { - const matcher = databaseObjectAppObject.createMatcher(data, $databaseObjectAppObjectSearchSettings); - if (matcher && !matcher(filter)) return false; - return true; - }); + $: matcher = databaseObjectAppObject.createMatcher(filter, $databaseObjectAppObjectSearchSettings); + $: flatFilteredList = objectList.filter(data => !matcher || matcher(data)); export function focus() { domListHandler?.focusFirst(); From 0932f4c537f20d5b6542f17498a59168a9d61506 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 14:08:19 +0100 Subject: [PATCH 03/12] search columns WIP --- packages/web/src/appobj/AppObjectGroup.svelte | 3 +++ packages/web/src/appobj/AppObjectList.svelte | 11 ++++++----- .../web/src/appobj/ColumnAppObject.svelte | 14 ++++++++++++++ .../web/src/appobj/DatabaseAppObject.svelte | 13 ++++++++++++- .../src/appobj/DatabaseObjectAppObject.svelte | 19 +++++++++++++++++++ .../web/src/appobj/SubColumnParamList.svelte | 2 ++ .../web/src/appobj/SubDatabaseList.svelte | 8 ++++---- 7 files changed, 60 insertions(+), 10 deletions(-) diff --git a/packages/web/src/appobj/AppObjectGroup.svelte b/packages/web/src/appobj/AppObjectGroup.svelte index bdb64e4b5..df03400aa 100644 --- a/packages/web/src/appobj/AppObjectGroup.svelte +++ b/packages/web/src/appobj/AppObjectGroup.svelte @@ -19,6 +19,7 @@ export let onDropOnGroup = undefined; export let groupContextMenu = null; export let collapsedGroupNames; + export let filter = undefined; $: isExpanded = !$collapsedGroupNames.includes(group); @@ -86,6 +87,8 @@ on:objectClick {disableContextMenu} {passProps} + isExpandedBySearch={filter && item.isChildMatched} + {filter} /> {/each} diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index 0ce2b6c76..a43cfa286 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -15,7 +15,7 @@ export let expandIconFunc = undefined; export let checkedObjectsStore = null; export let disableContextMenu = false; - export let passProps; + export let passProps = {}; export let getIsExpanded = null; export let setIsExpanded = null; export let sortGroups = false; @@ -27,7 +27,7 @@ export let emptyGroupNames = []; export let collapsedGroupNames = writable([]); - export let onChangeFilteredList; + export let onChangeFilteredList = undefined; $: matcher = module.createMatcher && module.createMatcher(filter, passProps?.searchSettings); $: childMatcher = module.createChildMatcher && module.createChildMatcher(filter, passProps?.searchSettings); @@ -53,9 +53,10 @@ $: listGrouped = groupFunc ? _.compact( (list || []).map(data => { - const isMatched = matcher && !matcher(data) ? false : true; + const isMatched = !matcher || matcher(data); + const isChildMatched = !childMatcher || childMatcher(data); const group = groupFunc(data); - return { group, data, isMatched }; + return { group, data, isMatched, isChildMatched }; }) ) : null; @@ -110,7 +111,7 @@ {checkedObjectsStore} {disableContextMenu} {filter} - isExpandedBySearch={childrenMatched.includes(data)} + isExpandedBySearch={filter && childrenMatched.includes(data)} {passProps} {getIsExpanded} {setIsExpanded} diff --git a/packages/web/src/appobj/ColumnAppObject.svelte b/packages/web/src/appobj/ColumnAppObject.svelte index 65259a9a9..80b5e1e63 100644 --- a/packages/web/src/appobj/ColumnAppObject.svelte +++ b/packages/web/src/appobj/ColumnAppObject.svelte @@ -1,5 +1,17 @@ diff --git a/packages/web/src/appobj/SubDatabaseList.svelte b/packages/web/src/appobj/SubDatabaseList.svelte index 6d8abaec0..6b265fd36 100644 --- a/packages/web/src/appobj/SubDatabaseList.svelte +++ b/packages/web/src/appobj/SubDatabaseList.svelte @@ -15,13 +15,13 @@ $: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id }); $: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || []; + + // .filter(x => filterName(filter, x.name, data.displayName, data.server)) filterName(filter, x.name, data.displayName, data.server)), - x => x.sortOrder ?? x.name - ).map(db => ({ ...db, connection: data }))} + list={_.sortBy(dbList, x => x.sortOrder ?? x.name).map(db => ({ ...db, connection: data }))} module={databaseAppObject} {passProps} + {filter} /> From 20a5a5051678548a1ec0fe37afeb9d82286898e5 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 14:10:18 +0100 Subject: [PATCH 04/12] rename file --- .../{SubColumnParamList.svelte => SubTableColumnList.svelte} | 0 packages/web/src/widgets/SqlObjectList.svelte | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/web/src/appobj/{SubColumnParamList.svelte => SubTableColumnList.svelte} (100%) diff --git a/packages/web/src/appobj/SubColumnParamList.svelte b/packages/web/src/appobj/SubTableColumnList.svelte similarity index 100% rename from packages/web/src/appobj/SubColumnParamList.svelte rename to packages/web/src/appobj/SubTableColumnList.svelte diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index 3aca347a2..9cf31bdda 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -27,7 +27,7 @@ import AppObjectList from '../appobj/AppObjectList.svelte'; import _ from 'lodash'; 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 ErrorInfo from '../elements/ErrorInfo.svelte'; import LoadingInfo from '../elements/LoadingInfo.svelte'; @@ -271,7 +271,7 @@ subItemsComponent={data => data.objectTypeField == 'procedures' || data.objectTypeField == 'functions' ? SubProcedureParamList - : SubColumnParamList} + : SubTableColumnList} isExpandable={data => data.objectTypeField == 'tables' || data.objectTypeField == 'views' || From 2fc2ac491c29d3423651154ae4b8f8cc94ef4b89 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 14:45:38 +0100 Subject: [PATCH 05/12] find in SQL text --- packages/web/src/appobj/AppObjectCore.svelte | 4 +++- .../web/src/appobj/ColumnAppObject.svelte | 2 +- .../src/appobj/ProcedureLineAppObject.svelte | 23 +++++++++++++++++++ .../src/appobj/SubProcedureLineList.svelte | 19 +++++++++++++++ packages/web/src/icons/FontIcon.svelte | 1 + packages/web/src/widgets/SqlObjectList.svelte | 5 +++- 6 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 packages/web/src/appobj/ProcedureLineAppObject.svelte create mode 100644 packages/web/src/appobj/SubProcedureLineList.svelte diff --git a/packages/web/src/appobj/AppObjectCore.svelte b/packages/web/src/appobj/AppObjectCore.svelte index 6f7f072a0..8545f5f3d 100644 --- a/packages/web/src/appobj/AppObjectCore.svelte +++ b/packages/web/src/appobj/AppObjectCore.svelte @@ -32,6 +32,7 @@ export let indentLevel = 0; export let disableBoldScroll = false; export let filter = null; + export let disableHover = false; $: isChecked = checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x)); @@ -91,6 +92,7 @@ class="main" class:isBold class:isChoosed + class:disableHover draggable={true} on:click={handleClick} on:mouseup={handleMouseUp} @@ -197,7 +199,7 @@ white-space: nowrap; font-weight: normal; } - .main:hover { + .main:hover:not(.disableHover) { background-color: var(--theme-bg-hover); } .isBold { diff --git a/packages/web/src/appobj/ColumnAppObject.svelte b/packages/web/src/appobj/ColumnAppObject.svelte index 80b5e1e63..5f2aae81c 100644 --- a/packages/web/src/appobj/ColumnAppObject.svelte +++ b/packages/web/src/appobj/ColumnAppObject.svelte @@ -73,5 +73,5 @@ {extInfo} icon={getColumnIcon(data, true)} menu={createMenu} - disableHover + \ /> diff --git a/packages/web/src/appobj/ProcedureLineAppObject.svelte b/packages/web/src/appobj/ProcedureLineAppObject.svelte new file mode 100644 index 000000000..d0baa67a3 --- /dev/null +++ b/packages/web/src/appobj/ProcedureLineAppObject.svelte @@ -0,0 +1,23 @@ + + + + + diff --git a/packages/web/src/appobj/SubProcedureLineList.svelte b/packages/web/src/appobj/SubProcedureLineList.svelte new file mode 100644 index 000000000..ef04d92e1 --- /dev/null +++ b/packages/web/src/appobj/SubProcedureLineList.svelte @@ -0,0 +1,19 @@ + + + + + ({ + lineData, + }))} + module={procedureLineAppObject} + {filter} +/> diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte index 1f8d6930f..06d4b956f 100644 --- a/packages/web/src/icons/FontIcon.svelte +++ b/packages/web/src/icons/FontIcon.svelte @@ -143,6 +143,7 @@ 'icon parent-filter': 'mdi mdi-home-alert', 'icon parent-filter-outline': 'mdi mdi-home-alert-outline', 'icon download': 'mdi mdi-download', + 'icon text': 'mdi mdi-text', 'icon run': 'mdi mdi-play', 'icon chevron-down': 'mdi mdi-chevron-down', diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index 9cf31bdda..6a0798c0e 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -55,6 +55,7 @@ import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools'; import FocusedConnectionInfoWidget from './FocusedConnectionInfoWidget.svelte'; import SubProcedureParamList from '../appobj/SubProcedureParamList.svelte'; + import SubProcedureLineList from '../appobj/SubProcedureLineList.svelte'; export let conid; export let database; @@ -270,7 +271,9 @@ groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)} subItemsComponent={data => data.objectTypeField == 'procedures' || data.objectTypeField == 'functions' - ? SubProcedureParamList + ? filter + ? SubProcedureLineList + : SubProcedureParamList : SubTableColumnList} isExpandable={data => data.objectTypeField == 'tables' || From 939f04ae62f5780a17aee1f9ddb2d1f145ecdb06 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 15:19:54 +0100 Subject: [PATCH 06/12] optimalization --- packages/web/src/appobj/AppObjectList.svelte | 19 ++++++++++++++++++- .../web/src/appobj/AppObjectListItem.svelte | 2 +- .../web/src/appobj/SubDatabaseList.svelte | 1 + .../src/appobj/SubProcedureLineList.svelte | 2 ++ .../web/src/appobj/SubTableColumnList.svelte | 2 ++ packages/web/src/widgets/SqlObjectList.svelte | 4 ++-- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index a43cfa286..18558721c 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -5,6 +5,7 @@ import AppObjectListItem from './AppObjectListItem.svelte'; import { writable } from 'svelte/store'; + import Link from '../elements/Link.svelte'; export let list; export let module; @@ -25,10 +26,13 @@ export let groupFunc = undefined; export let onDropOnGroup = undefined; export let emptyGroupNames = []; + export let isExpandedOnlyBySearch = false; export let collapsedGroupNames = writable([]); export let onChangeFilteredList = undefined; + let expandLimited = false; + $: matcher = module.createMatcher && module.createMatcher(filter, passProps?.searchSettings); $: childMatcher = module.createChildMatcher && module.createChildMatcher(filter, passProps?.searchSettings); @@ -73,6 +77,10 @@ } $: groups = groupFunc ? extendGroups(_.groupBy(listGrouped, 'group'), emptyGroupNames) : null; + + $: listLimited = + isExpandedOnlyBySearch && !expandLimited ? filtered.slice(0, Math.min(filter.trim().length, 3)) : list; + $: isListLimited = isExpandedOnlyBySearch && listLimited.length < filtered.length; {#if groupFunc} @@ -98,7 +106,7 @@ /> {/each} {:else} - {#each list as data} + {#each listLimited as data} {/each} + {#if isListLimited} +
+ { + expandLimited = true; + }}>Show next {filtered.length - listLimited.length} +
+ {/if} {/if} diff --git a/packages/web/src/appobj/AppObjectListItem.svelte b/packages/web/src/appobj/AppObjectListItem.svelte index 518564b27..687a29570 100644 --- a/packages/web/src/appobj/AppObjectListItem.svelte +++ b/packages/web/src/appobj/AppObjectListItem.svelte @@ -63,7 +63,7 @@ {#if (isExpanded || isExpandedBySearch) && subItemsComponent}
diff --git a/packages/web/src/appobj/SubProcedureLineList.svelte b/packages/web/src/appobj/SubProcedureLineList.svelte index ef04d92e1..4ba0afa53 100644 --- a/packages/web/src/appobj/SubProcedureLineList.svelte +++ b/packages/web/src/appobj/SubProcedureLineList.svelte @@ -8,6 +8,7 @@ export let data; export let filter; + export let isExpandedOnlyBySearch; diff --git a/packages/web/src/appobj/SubTableColumnList.svelte b/packages/web/src/appobj/SubTableColumnList.svelte index dfe66d865..f9af8743e 100644 --- a/packages/web/src/appobj/SubTableColumnList.svelte +++ b/packages/web/src/appobj/SubTableColumnList.svelte @@ -6,6 +6,7 @@ export let data; export let filter; + export let isExpandedOnlyBySearch; diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index 6a0798c0e..a549db596 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -269,9 +269,9 @@ .map(x => ({ ...x, conid, database }))} module={databaseObjectAppObject} groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)} - subItemsComponent={data => + subItemsComponent={(data, { isExpandedOnlyBySearch }) => data.objectTypeField == 'procedures' || data.objectTypeField == 'functions' - ? filter + ? filter && isExpandedOnlyBySearch ? SubProcedureLineList : SubProcedureParamList : SubTableColumnList} From 7e1ceb69ae8046dfe6ff418c9e9f11fc8ee9d42f Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 15:36:26 +0100 Subject: [PATCH 07/12] UX fix --- packages/web/src/appobj/AppObjectList.svelte | 6 +++--- packages/web/src/appobj/AppObjectListItem.svelte | 5 ++++- packages/web/src/appobj/SubDatabaseList.svelte | 3 ++- packages/web/src/appobj/SubProcedureLineList.svelte | 4 ++-- packages/web/src/appobj/SubTableColumnList.svelte | 4 ++-- packages/web/src/widgets/SqlObjectList.svelte | 4 ++-- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index 18558721c..91e7ae559 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -26,7 +26,7 @@ export let groupFunc = undefined; export let onDropOnGroup = undefined; export let emptyGroupNames = []; - export let isExpandedOnlyBySearch = false; + export let isExpandedBySearch = false; export let collapsedGroupNames = writable([]); export let onChangeFilteredList = undefined; @@ -79,8 +79,8 @@ $: groups = groupFunc ? extendGroups(_.groupBy(listGrouped, 'group'), emptyGroupNames) : null; $: listLimited = - isExpandedOnlyBySearch && !expandLimited ? filtered.slice(0, Math.min(filter.trim().length, 3)) : list; - $: isListLimited = isExpandedOnlyBySearch && listLimited.length < filtered.length; + isExpandedBySearch && !expandLimited ? filtered.slice(0, Math.min(filter.trim().length, 3)) : list; + $: isListLimited = isExpandedBySearch && listLimited.length < filtered.length; {#if groupFunc} diff --git a/packages/web/src/appobj/AppObjectListItem.svelte b/packages/web/src/appobj/AppObjectListItem.svelte index 687a29570..0bafecdb4 100644 --- a/packages/web/src/appobj/AppObjectListItem.svelte +++ b/packages/web/src/appobj/AppObjectListItem.svelte @@ -63,10 +63,13 @@ {#if (isExpanded || isExpandedBySearch) && subItemsComponent}
diff --git a/packages/web/src/appobj/SubDatabaseList.svelte b/packages/web/src/appobj/SubDatabaseList.svelte index 808fb7932..27a2d6aa8 100644 --- a/packages/web/src/appobj/SubDatabaseList.svelte +++ b/packages/web/src/appobj/SubDatabaseList.svelte @@ -12,6 +12,7 @@ export let passProps; export let isExpandedOnlyBySearch; + export let isExpandedBySearch; $: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id }); $: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || []; @@ -24,5 +25,5 @@ module={databaseAppObject} {passProps} {filter} - {isExpandedOnlyBySearch} + {isExpandedBySearch} /> diff --git a/packages/web/src/appobj/SubProcedureLineList.svelte b/packages/web/src/appobj/SubProcedureLineList.svelte index 4ba0afa53..2539c1929 100644 --- a/packages/web/src/appobj/SubProcedureLineList.svelte +++ b/packages/web/src/appobj/SubProcedureLineList.svelte @@ -8,7 +8,7 @@ export let data; export let filter; - export let isExpandedOnlyBySearch; + export let isExpandedBySearch; diff --git a/packages/web/src/appobj/SubTableColumnList.svelte b/packages/web/src/appobj/SubTableColumnList.svelte index f9af8743e..32d06ef0b 100644 --- a/packages/web/src/appobj/SubTableColumnList.svelte +++ b/packages/web/src/appobj/SubTableColumnList.svelte @@ -6,7 +6,7 @@ export let data; export let filter; - export let isExpandedOnlyBySearch; + export let isExpandedBySearch; diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index a549db596..17c6783a9 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -269,9 +269,9 @@ .map(x => ({ ...x, conid, database }))} module={databaseObjectAppObject} groupFunc={data => getObjectTypeFieldLabel(data.objectTypeField, driver)} - subItemsComponent={(data, { isExpandedOnlyBySearch }) => + subItemsComponent={(data, { isExpandedBySearch }) => data.objectTypeField == 'procedures' || data.objectTypeField == 'functions' - ? filter && isExpandedOnlyBySearch + ? isExpandedBySearch ? SubProcedureLineList : SubProcedureParamList : SubTableColumnList} From 358a641449331764b3c59546dae54f4a112c49cb Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 15:53:52 +0100 Subject: [PATCH 08/12] refactor - not working --- packages/web/src/appobj/AppObjectList.svelte | 30 +++++++++---------- .../src/appobj/DatabaseObjectAppObject.svelte | 2 ++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index 91e7ae559..e67f15658 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -36,9 +36,19 @@ $: matcher = module.createMatcher && module.createMatcher(filter, passProps?.searchSettings); $: childMatcher = module.createChildMatcher && module.createChildMatcher(filter, passProps?.searchSettings); - $: filtered = !groupFunc ? list.filter(data => !matcher || matcher(data)) : null; + $: dataLabeled = _.compact( + (list || []).map(data => { + const isMatched = !matcher || matcher(data); + const isChildMatched = + module.disableShowChildrenWithParentMatch && isMatched ? false : !childMatcher || childMatcher(data); + const group = groupFunc ? groupFunc(data) : undefined; + return { group, data, isMatched, isChildMatched }; + }) + ); - $: childrenMatched = !groupFunc ? list.filter(data => !childMatcher || childMatcher(data)) : null; + $: filtered = dataLabeled.filter(x => x.isMatched).map(x => x.data); + + $: childrenMatched = dataLabeled.filter(x => x.isChildMatched).map(x => x.data); // let filtered = []; @@ -54,17 +64,6 @@ // } // } - $: listGrouped = groupFunc - ? _.compact( - (list || []).map(data => { - const isMatched = !matcher || matcher(data); - const isChildMatched = !childMatcher || childMatcher(data); - const group = groupFunc(data); - return { group, data, isMatched, isChildMatched }; - }) - ) - : null; - function extendGroups(base, emptyList) { const res = { ...base, @@ -76,10 +75,9 @@ 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; + $: listLimited = isExpandedBySearch && !expandLimited ? filtered.slice(0, Math.min(filter.trim().length, 3)) : list; $: isListLimited = isExpandedBySearch && listLimited.length < filtered.length; diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index 844914dde..c2d5bbb6d 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -44,6 +44,8 @@ return res; }; + export const disableShowChildrenWithParentMatch = true; + export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName); export const databaseObjectIcons = { From bf4841bca4d5cb1d909e98a332b7fb55e75c51e7 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 16:23:55 +0100 Subject: [PATCH 09/12] filter name optimalization --- packages/tools/src/filterName.ts | 31 ++++++++++++++ packages/web/src/appobj/AppObjectList.svelte | 26 ++++++++++-- .../web/src/appobj/ConnectionAppObject.svelte | 14 +++---- .../src/appobj/DatabaseObjectAppObject.svelte | 42 ++++++------------- packages/web/src/stores.ts | 2 +- 5 files changed, 73 insertions(+), 42 deletions(-) diff --git a/packages/tools/src/filterName.ts b/packages/tools/src/filterName.ts index 8cdc38d43..72b434b1f 100644 --- a/packages/tools/src/filterName.ts +++ b/packages/tools/src/filterName.ts @@ -36,6 +36,37 @@ export function filterName(filter: string, ...names: string[]) { 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): { token: string; isMatch: boolean }[] { const tokens = filter.split(' ').map(x => x.trim()); diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index e67f15658..785c85835 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -34,13 +34,31 @@ let expandLimited = false; $: matcher = module.createMatcher && module.createMatcher(filter, passProps?.searchSettings); - $: childMatcher = module.createChildMatcher && module.createChildMatcher(filter, passProps?.searchSettings); $: dataLabeled = _.compact( (list || []).map(data => { - const isMatched = !matcher || matcher(data); - const isChildMatched = - module.disableShowChildrenWithParentMatch && isMatched ? false : !childMatcher || childMatcher(data); + 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 }; }) diff --git a/packages/web/src/appobj/ConnectionAppObject.svelte b/packages/web/src/appobj/ConnectionAppObject.svelte index 355d10930..f19616fd3 100644 --- a/packages/web/src/appobj/ConnectionAppObject.svelte +++ b/packages/web/src/appobj/ConnectionAppObject.svelte @@ -3,13 +3,11 @@ export const createMatcher = filter => props => { const { _id, displayName, server } = props; const databases = getLocalStorage(`database_list_${_id}`) || []; - return filterName(filter, displayName, server, ...databases.map(x => x.name)); - }; - export const createChildMatcher = filter => props => { - if (!filter) return false; - const { _id } = props; - const databases = getLocalStorage(`database_list_${_id}`) || []; - return filterName(filter, ...databases.map(x => x.name)); + return filterNameCompoud( + filter, + [displayName, server], + databases.map(x => x.name) + ); }; export function openConnection(connection, disableExpand = false) { if (connection.singleDatabase) { @@ -106,7 +104,7 @@ openedConnections, openedSingleDatabaseConnections, } from '../stores'; - import { filterName } from 'dbgate-tools'; + import { filterName, filterNameCompoud } from 'dbgate-tools'; import { showModal } from '../modals/modalTools'; import ConfirmModal from '../modals/ConfirmModal.svelte'; import InputTextModal from '../modals/InputTextModal.svelte'; diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index c2d5bbb6d..de1fd3f4e 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -5,42 +5,25 @@ export const createMatcher = (filter, cfg = DEFAULT_SEARCH_SETTINGS) => ({ schemaName, pureName, objectComment, tableEngine, columns, objectTypeField, createSql }) => { - const filterArgs = []; - if (cfg.schemaName) filterArgs.push(schemaName); + const mainArgs = []; + const childArgs = []; + if (cfg.schemaName) mainArgs.push(schemaName); if (objectTypeField == 'tables') { - if (cfg.tableName) filterArgs.push(pureName); - if (cfg.tableComment) filterArgs.push(objectComment); - if (cfg.tableEngine) filterArgs.push(tableEngine); + 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) filterArgs.push(column.columnName); - if (cfg.columnComment) filterArgs.push(column.columnComment); - if (cfg.columnDataType) filterArgs.push(column.dataType); + 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) filterArgs.push(pureName); - if (cfg.sqlObjectText) filterArgs.push(createSql); + if (cfg.sqlObjectName) mainArgs.push(pureName); + if (cfg.sqlObjectText) childArgs.push(createSql); } - const res = filterName(filter, ...filterArgs); - return res; - }; - - export const createChildMatcher = - (filter, cfg = DEFAULT_SEARCH_SETTINGS) => - ({ columns, objectTypeField, createSql }) => { - const filterArgs = []; - if (objectTypeField == 'tables') { - for (const column of columns || []) { - if (cfg.columnName) filterArgs.push(column.columnName); - if (cfg.columnComment) filterArgs.push(column.columnComment); - if (cfg.columnDataType) filterArgs.push(column.dataType); - } - } else { - if (cfg.sqlObjectText) filterArgs.push(createSql); - } - - const res = filterName(filter, ...filterArgs); + const res = filterNameCompoud(filter, mainArgs, childArgs); return res; }; @@ -929,6 +912,7 @@ import { extractDbNameFromComposite, filterName, + filterNameCompoud, generateDbPairingId, getAlterDatabaseScript, getConnectionLabel, diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts index 997bfcae4..dd60b6b53 100644 --- a/packages/web/src/stores.ts +++ b/packages/web/src/stores.ts @@ -167,7 +167,7 @@ export const DEFAULT_SEARCH_SETTINGS = { tableName: true, viewName: true, columnName: true, - columnDataType: true, + columnDataType: false, tableComment: true, columnComment: true, sqlObjectName: true, From 5544b6291b128faf049474018197424a5638a3bc Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 16:50:17 +0100 Subject: [PATCH 10/12] search tokenizer --- packages/tools/src/filterName.ts | 64 ++++++++++++++----- packages/web/src/appobj/AppObjectCore.svelte | 5 +- packages/web/src/widgets/SqlObjectList.svelte | 4 +- .../src/widgets/TokenizedFilteredText.svelte | 26 ++++++++ 4 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 packages/web/src/widgets/TokenizedFilteredText.svelte diff --git a/packages/tools/src/filterName.ts b/packages/tools/src/filterName.ts index 72b434b1f..0676d701a 100644 --- a/packages/tools/src/filterName.ts +++ b/packages/tools/src/filterName.ts @@ -67,24 +67,56 @@ export function filterNameCompoud( return 'none'; } -export function tokenizeBySearchFilter(text: string, filter: string): { token: string; isMatch: boolean }[] { - const tokens = filter.split(' ').map(x => x.trim()); - - 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; +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()); } - - 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 }); + let res = [ + { + text, + isMatch: false, + }, + ]; - return result; + 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; + + // 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; } diff --git a/packages/web/src/appobj/AppObjectCore.svelte b/packages/web/src/appobj/AppObjectCore.svelte index 8545f5f3d..8b26e55a3 100644 --- a/packages/web/src/appobj/AppObjectCore.svelte +++ b/packages/web/src/appobj/AppObjectCore.svelte @@ -5,6 +5,7 @@ import CheckboxField from '../forms/CheckboxField.svelte'; import { copyTextToClipboard } from '../utility/clipboard'; import { showSnackbarSuccess } from '../utility/snackbar'; + import TokenizedFilteredText from '../widgets/TokenizedFilteredText.svelte'; const dispatch = createEventDispatcher(); @@ -134,7 +135,7 @@ {#if colorMark} {/if} - {title} + {#if statusIconBefore} @@ -156,7 +157,7 @@ {/if} {#if extInfo} - {extInfo} + {/if} {#if onPin} diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte index 17c6783a9..7af64f669 100644 --- a/packages/web/src/widgets/SqlObjectList.svelte +++ b/packages/web/src/widgets/SqlObjectList.svelte @@ -274,7 +274,9 @@ ? isExpandedBySearch ? SubProcedureLineList : SubProcedureParamList - : SubTableColumnList} + : isExpandedBySearch && (data.objectTypeField == 'views' || data.objectTypeField == 'matviews') + ? SubProcedureLineList + : SubTableColumnList} isExpandable={data => data.objectTypeField == 'tables' || data.objectTypeField == 'views' || diff --git a/packages/web/src/widgets/TokenizedFilteredText.svelte b/packages/web/src/widgets/TokenizedFilteredText.svelte new file mode 100644 index 000000000..3a7575a46 --- /dev/null +++ b/packages/web/src/widgets/TokenizedFilteredText.svelte @@ -0,0 +1,26 @@ + + +{#if tokenized} + {#each tokenized as token} + {#if token.isMatch} + {token.text} + {:else} + {token.text} + {/if} + {/each} +{:else} + {text} +{/if} + + From 5d294f62365068c22147ee44c40c8126dc3ac9d3 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 16:58:10 +0100 Subject: [PATCH 11/12] camel search tokenizer --- packages/tools/src/filterName.ts | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/tools/src/filterName.ts b/packages/tools/src/filterName.ts index 0676d701a..504ebf08d 100644 --- a/packages/tools/src/filterName.ts +++ b/packages/tools/src/filterName.ts @@ -85,6 +85,35 @@ export function tokenizeBySearchFilter(text: string, filter: string): { text: st }, ]; + 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) { @@ -100,7 +129,7 @@ export function tokenizeBySearchFilter(text: string, filter: string): { text: st res = nextres; } - return res; + return res.filter(x => x.text.length > 0); // const result = []; // let lastMatch = 0; From 6ea54a5b0ac4cb2b90081521911377d37aaa8959 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Mon, 16 Dec 2024 17:01:05 +0100 Subject: [PATCH 12/12] tokenized column search --- packages/web/src/datagrid/ColumnManager.svelte | 1 + packages/web/src/datagrid/ColumnManagerRow.svelte | 3 ++- packages/web/src/elements/ColumnLabel.svelte | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/web/src/datagrid/ColumnManager.svelte b/packages/web/src/datagrid/ColumnManager.svelte index ee889e90e..76031e8ac 100644 --- a/packages/web/src/datagrid/ColumnManager.svelte +++ b/packages/web/src/datagrid/ColumnManager.svelte @@ -235,6 +235,7 @@ {columnIndex} {allowChangeChangeSetStructure} isSelected={selectedColumns.includes(column.uniqueName) || currentColumnUniqueName == column.uniqueName} + {filter} on:click={() => { if (domFocusField) domFocusField.focus(); selectedColumns = [column.uniqueName]; diff --git a/packages/web/src/datagrid/ColumnManagerRow.svelte b/packages/web/src/datagrid/ColumnManagerRow.svelte index 864f6c60d..b5340d991 100644 --- a/packages/web/src/datagrid/ColumnManagerRow.svelte +++ b/packages/web/src/datagrid/ColumnManagerRow.svelte @@ -15,6 +15,7 @@ export let conid; export let database; export let isDynamicStructure; + export let filter = undefined; export let tableInfo; export let setTableInfo; @@ -83,7 +84,7 @@ }} /> {/if} - +
{#if allowChangeChangeSetStructure && !isDynamicStructure} diff --git a/packages/web/src/elements/ColumnLabel.svelte b/packages/web/src/elements/ColumnLabel.svelte index 14d74c972..9e5443ba9 100644 --- a/packages/web/src/elements/ColumnLabel.svelte +++ b/packages/web/src/elements/ColumnLabel.svelte @@ -11,6 +11,7 @@ import { openDatabaseObjectDetail } from '../appobj/DatabaseObjectAppObject.svelte'; import FontIcon from '../icons/FontIcon.svelte'; + import TokenizedFilteredText from '../widgets/TokenizedFilteredText.svelte'; import Link from './Link.svelte'; export let notNull = false; @@ -25,6 +26,7 @@ export let conid = undefined; export let database = undefined; export let iconOverride = undefined; + export let filter = undefined; $: icon = iconOverride || getColumnIcon($$props, forceIcon); @@ -33,7 +35,7 @@ {#if icon} {/if} - {headerText || columnName} + {#if extInfo} {extInfo} {/if}