diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts index ff531588f..068ba5074 100644 --- a/packages/web/src/stores.ts +++ b/packages/web/src/stores.ts @@ -6,7 +6,7 @@ import { GlobalCommand } from './commands/registerCommand'; import { useConfig, useSettings } from './utility/metadataLoaders'; import _ from 'lodash'; -interface TabDefinition { +export interface TabDefinition { title: string; closedTime?: number; icon: string; @@ -15,6 +15,7 @@ interface TabDefinition { busy: boolean; tabid: string; tabComponent: string; + tabOrder?: number; } export function writableWithStorage(defaultValue: T, storageName) { @@ -44,7 +45,6 @@ export const activeTab = derived([openedTabs], ([$openedTabs]) => $openedTabs.fi export const recentDatabases = writableWithStorage([], 'recentDatabases'); export const pinnedDatabases = writableWithStorage([], 'pinnedDatabases'); export const pinnedTables = writableWithStorage([], 'pinnedTables'); -export const tabDatabaseGroupOrder = writableWithStorage({}, 'tabDatabaseGroupOrder'); export const commandsSettings = writable({}); export const allResultsInOneTabDefault = writableWithStorage(false, 'allResultsInOneTabDefault'); export const archiveFilesAsDataSheets = writableWithStorage([], 'archiveFilesAsDataSheets'); diff --git a/packages/web/src/utility/openNewTab.ts b/packages/web/src/utility/openNewTab.ts index f0222bb46..ded774168 100644 --- a/packages/web/src/utility/openNewTab.ts +++ b/packages/web/src/utility/openNewTab.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import uuidv1 from 'uuid/v1'; import { get } from 'svelte/store'; -import { getOpenedTabs, openedTabs, tabDatabaseGroupOrder } from '../stores'; +import { getOpenedTabs, openedTabs, TabDefinition } from '../stores'; import tabs from '../tabs'; import { setSelectedTabFunc } from './common'; import localforage from 'localforage'; @@ -73,35 +73,34 @@ export default async function openNewTab(newTab, initialData = undefined, option } } - openedTabs.update(files => [ - ...(files || []).map(x => ({ ...x, selected: false })), - { + openedTabs.update(files => { + const dbKey = getTabDbKey(newTab); + const items = sortTabs(files.filter(x => x.closedTime == null)); + + const newItem = { ...newTab, tabid, - selected: true, - // @ts-ignore - tabOrder: (_.max(files.map(x => x.tabOrder || 0)) || 0) + 1, - }, - ]); - - const allOpenedTabs = getOpenedTabs(); - - tabDatabaseGroupOrder.update(groupOrder => { - const groupOrderFiltered = _.pickBy(groupOrder, (v, k) => - allOpenedTabs.filter(x => x.closedTime == null).find(x => getTabDbKey(x) == k) - ); - const dbKey = getTabDbKey({ - ...newTab, - tabid, - }); - const newOrder = - groupOrderFiltered[dbKey] || - // @ts-ignore - (_.max(Object.values(groupOrderFiltered)) || 0) + 1; - return { - ...groupOrderFiltered, - [dbKey]: newOrder, }; + if (dbKey != null) { + const lastIndex = _.findLastIndex(items, x => getTabDbKey(x) == dbKey); + if (lastIndex >= 0) { + items.splice(lastIndex + 1, 0, newItem); + } else { + items.push(newItem); + } + } else { + items.push(newItem); + } + + return [ + ...(files || []).map(x => ({ ...x, selected: false, tabOrder: _.findIndex(items, y => y.tabid == x.tabid) })), + { + ...newTab, + tabid, + selected: true, + tabOrder: _.findIndex(items, y => y.tabid == tabid), + }, + ]; }); // console.log('OPENING NEW TAB', newTab); @@ -158,5 +157,29 @@ export function getTabDbKey(tab) { if (tab.props && tab.props.archiveFolder) { return `archive://${tab.props.archiveFolder}`; } - return `no://${tab.tabid}`; + return null; +} + +export function sortTabs(tabs: any[]): any[] { + return _.sortBy(tabs, [x => x.tabOrder || 0, x => getTabDbKey(x), 'title', 'tabid']); +} + +export function groupTabs(tabs: any[]) { + const res = []; + + for (const tab of sortTabs(tabs)) { + const lastGroup = res[res.length - 1]; + if (lastGroup?.tabDbKey == tab.tabDbKey) { + lastGroup.tabs.push(tab); + } else { + res.push({ + tabDbKey: tab.tabDbKey, + tabDbName: tab.tabDbName, + tabs: [tab], + grpid: tab.tabid, + }); + } + } + + return res; } diff --git a/packages/web/src/widgets/TabsPanel.svelte b/packages/web/src/widgets/TabsPanel.svelte index dcce4c3a2..ddab40cab 100644 --- a/packages/web/src/widgets/TabsPanel.svelte +++ b/packages/web/src/widgets/TabsPanel.svelte @@ -22,6 +22,26 @@ }); }; + const closeMultipleTabs = closeCondition => { + openedTabs.update(files => { + const newFiles = files.map(x => ({ + ...x, + closedTime: x.closedTime || (closeCondition(x) ? new Date().getTime() : undefined), + })); + + if (newFiles.find(x => x.selected && x.closedTime == null)) { + return newFiles; + } + + const selectedIndex = _.findLastIndex(newFiles, x => x.closedTime == null); + + return newFiles.map((x, index) => ({ + ...x, + selected: index == selectedIndex, + })); + }); + }; + const closeTab = closeTabFunc((x, active) => x.tabid == active.tabid); const closeAll = () => { const closedTime = new Date().getTime(); @@ -60,16 +80,14 @@ } function getDbIcon(key) { - if (key.startsWith('database://')) return 'icon database'; - if (key.startsWith('archive://')) return 'icon archive'; - if (key.startsWith('server://')) return 'icon server'; + if (key) { + if (key.startsWith('database://')) return 'icon database'; + if (key.startsWith('archive://')) return 'icon archive'; + if (key.startsWith('server://')) return 'icon server'; + } return 'icon file'; } - function sortTabs(tabs) { - return _.sortBy(tabs, [x => x['tabOrder'] || 0, 'title', 'tabid']); - } - registerCommand({ id: 'tabs.nextTab', category: 'Tabs', @@ -124,22 +142,14 @@ import FavoriteModal from '../modals/FavoriteModal.svelte'; import { showModal } from '../modals/modalTools'; - import { - currentDatabase, - getActiveTab, - getOpenedTabs, - openedTabs, - activeTabId, - getActiveTabId, - tabDatabaseGroupOrder, - } from '../stores'; + import { currentDatabase, getActiveTab, getOpenedTabs, openedTabs, activeTabId, getActiveTabId } from '../stores'; import tabs from '../tabs'; import { setSelectedTab } from '../utility/common'; import contextMenu from '../utility/contextMenu'; import getConnectionLabel from '../utility/getConnectionLabel'; import { isElectronAvailable } from '../utility/getElectron'; import { getConnectionInfo, useConnectionList } from '../utility/metadataLoaders'; - import { duplicateTab, getTabDbKey } from '../utility/openNewTab'; + import { duplicateTab, getTabDbKey, sortTabs, groupTabs } from '../utility/openNewTab'; import { useConnectionColorFactory } from '../utility/useConnectionColor'; $: connectionList = useConnectionList(); @@ -159,15 +169,17 @@ tabDbKey: getTabDbKey(tab), })); - $: tabsByDb = _.groupBy(tabsWithDb, 'tabDbKey'); - $: dbKeys = _.sortBy(_.keys(tabsByDb), [x => $tabDatabaseGroupOrder[x] || 0, x => x]); + $: groupedTabs = groupTabs(tabsWithDb); + + // $: tabsByDb = _.groupBy(tabsWithDb, 'tabDbKey'); + // $: dbKeys = _.sortBy(_.keys(tabsByDb), [x => $tabDatabaseGroupOrder[x] || 0, x => x]); $: scrollInViewTab($activeTabId); let draggingTab = null; let draggingTabTarget = null; - let draggingDbKey = null; - let draggingDbKeyTarget = null; + let draggingDbGroup = null; + let draggingDbGroupTarget = null; const connectionColorFactory = useConnectionColorFactory(3, null, true); @@ -260,13 +272,13 @@ function dragDropTab(draggingTab, targetTab) { if (draggingTab.tabid == targetTab.tabid) return; - if (getTabDbKey(draggingTab) != getTabDbKey(targetTab)) { - dragDropDbKey(getTabDbKey(draggingTab), getTabDbKey(targetTab)); - return; - } + // if (getTabDbKey(draggingTab) != getTabDbKey(targetTab)) { + // // dragDropDbKey(getTabDbKey(draggingTab), getTabDbKey(targetTab)); + // return; + // } - const dbKey = getTabDbKey(draggingTab); - const items = sortTabs(tabsByDb[dbKey]); + // const dbKey = getTabDbKey(draggingTab); + const items = sortTabs($openedTabs.filter(x => x.closedTime == null)); const dstIndex = _.findIndex(items, x => x.tabid == targetTab.tabid); const srcIndex = _.findIndex(items, x => x.tabid == draggingTab.tabid); if (srcIndex < 0 || dstIndex < 0) { @@ -296,78 +308,83 @@ ); } - function dragDropDbKey(draggingDbKey, targetDbKey) { - if (!draggingDbKey) return; - if (targetDbKey == draggingDbKey) return; + // function dragDropDbKey(draggingDbKey, targetDbKey) { + // if (!draggingDbKey) return; + // if (targetDbKey == draggingDbKey) return; - const groupOrderFiltered = _.pickBy($tabDatabaseGroupOrder, (v, k) => - $openedTabs.filter(x => x.closedTime == null).find(x => getTabDbKey(x) == k) - ); + // const groupOrderFiltered = _.pickBy($tabDatabaseGroupOrder, (v, k) => + // $openedTabs.filter(x => x.closedTime == null).find(x => getTabDbKey(x) == k) + // ); - const items = _.sortBy(_.keys(groupOrderFiltered), x => groupOrderFiltered[x]); + // const items = _.sortBy(_.keys(groupOrderFiltered), x => groupOrderFiltered[x]); - const dstIndex = _.indexOf(items, targetDbKey); - const srcIndex = _.indexOf(items, draggingDbKey); - if (srcIndex < 0 || dstIndex < 0) { - console.warn('Drag tab group index not found'); - return; - } - const newItems = - dstIndex < srcIndex - ? [...items.slice(0, dstIndex), draggingDbKey, ...items.slice(dstIndex).filter(x => x != draggingDbKey)] - : [ - ...items.slice(0, dstIndex + 1).filter(x => x != draggingDbKey), - draggingDbKey, - ...items.slice(dstIndex + 1), - ]; + // const dstIndex = _.indexOf(items, targetDbKey); + // const srcIndex = _.indexOf(items, draggingDbKey); + // if (srcIndex < 0 || dstIndex < 0) { + // console.warn('Drag tab group index not found'); + // return; + // } + // const newItems = + // dstIndex < srcIndex + // ? [...items.slice(0, dstIndex), draggingDbKey, ...items.slice(dstIndex).filter(x => x != draggingDbKey)] + // : [ + // ...items.slice(0, dstIndex + 1).filter(x => x != draggingDbKey), + // draggingDbKey, + // ...items.slice(dstIndex + 1), + // ]; - const newGroupOrder = {}; - for (const key in groupOrderFiltered) { - const index = newItems.indexOf(key); - newGroupOrder[key] = index >= 0 ? index + 1 : groupOrderFiltered[key]; - } + // const newGroupOrder = {}; + // for (const key in groupOrderFiltered) { + // const index = newItems.indexOf(key); + // newGroupOrder[key] = index >= 0 ? index + 1 : groupOrderFiltered[key]; + // } - tabDatabaseGroupOrder.set(newGroupOrder); - } + // tabDatabaseGroupOrder.set(newGroupOrder); + // } -{#each dbKeys as dbKey} +{#each groupedTabs as tabGroup}
handleSetDb(tabsByDb[dbKey][0].props)} - use:contextMenu={getDatabaseContextMenu(tabsByDb[dbKey])} + class:selected={draggingDbGroup + ? tabGroup.grpid == draggingDbGroupTarget?.grpid + : tabGroup.tabDbKey == currentDbKey} + on:click={() => handleSetDb(tabGroup.tabs[0].props)} + use:contextMenu={getDatabaseContextMenu(tabGroup.tabs)} style={$connectionColorFactory( - tabsByDb[dbKey][0].props, - (draggingDbKey ? dbKey == draggingDbKeyTarget : tabsByDb[dbKey][0].tabDbKey == currentDbKey) ? 2 : 3 + tabGroup.tabs[0].props, + (draggingDbGroup ? tabGroup.grpid == draggingDbGroupTarget?.grpid : tabGroup.tabDbKey == currentDbKey) ? 2 : 3 )} draggable={true} on:dragstart={e => { - draggingDbKey = dbKey; + draggingDbGroup = tabGroup; }} on:dragenter={e => { - draggingDbKeyTarget = dbKey; + draggingDbGroupTarget = tabGroup; }} on:drop={e => { - dragDropDbKey(draggingDbKey, dbKey); + // dragDropDbKey(draggingDbKey, dbKey); }} on:dragend={e => { - draggingDbKey = null; - draggingDbKeyTarget = null; + draggingDbGroup = null; + draggingDbGroupTarget = null; }} > - - {tabsByDb[dbKey][0].tabDbName} + + {tabGroup.tabDbName} - {#if tabsByDb[dbKey].length > 1} - closeWithSameDb(tabsByDb[dbKey][0].tabid)}> + {#if tabGroup.tabs.length > 1} + closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid))} + > {/if}
- {#each sortTabs(tabsByDb[dbKey]) as tab} + {#each tabGroup.tabs as tab}