From e68b31312643c13f89e792f08c5cb6c009cee05b Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:11:25 +0000 Subject: [PATCH 1/4] feat(nc-gui): open base/table/view in new tab on pressing cltr & cliking on then --- .../dashboard/TreeView/ProjectNode.vue | 23 +++++- .../dashboard/TreeView/TableNode.vue | 9 ++- packages/nc-gui/composables/useTableNew.ts | 31 ++++++-- packages/nc-gui/store/views.ts | 79 ++++++++++++++----- packages/nc-gui/utils/urlUtils.ts | 8 ++ 5 files changed, 123 insertions(+), 27 deletions(-) diff --git a/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue b/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue index 2acb07fa85..6d2331e002 100644 --- a/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue @@ -32,6 +32,8 @@ import { useTablesStore, useTabs, useToggle, + useMagicKeys, + navigateToBlankTargetOpenOption, } from '#imports' import type { NcProject } from '#imports' @@ -71,6 +73,8 @@ const { orgRoles, isUIAllowed } = useRoles() useTabs() +const { meta: metaKey, ctrlKey } = useMagicKeys() + const editMode = ref(false) const tempTitle = ref('') @@ -253,12 +257,29 @@ const onProjectClick = async (base: NcProject, ignoreNavigation?: boolean, toggl if (!base) { return } + const cmdOrCtrl = isMac() ? metaKey.value : ctrlKey.value - if (!toggleIsExpanded) $e('c:base:open') + if (!toggleIsExpanded && !cmdOrCtrl) $e('c:base:open') ignoreNavigation = isMobileMode.value || ignoreNavigation toggleIsExpanded = isMobileMode.value || toggleIsExpanded + if (cmdOrCtrl && !ignoreNavigation) { + await navigateTo( + `${cmdOrCtrl ? '#' : ''}${baseUrl({ + id: base.id!, + type: 'database', + isSharedBase: isSharedBase.value, + })}`, + cmdOrCtrl + ? { + open: navigateToBlankTargetOpenOption, + } + : undefined, + ) + return + } + if (toggleIsExpanded) { base.isExpanded = !base.isExpanded } else { diff --git a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue index 309446907d..e108628e1e 100644 --- a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue @@ -4,7 +4,7 @@ import { toRef } from '@vue/reactivity' import { message } from 'ant-design-vue' import { storeToRefs } from 'pinia' -import { ProjectRoleInj, TreeViewInj, useNuxtApp, useRoles, useTabs } from '#imports' +import { ProjectRoleInj, TreeViewInj, useNuxtApp, useRoles, useTabs, useMagicKeys } from '#imports' import type { SidebarTableNode } from '~/lib' const props = withDefaults( @@ -39,6 +39,8 @@ useTableNew({ baseId: base.value.id!, }) +const { meta: metaKey, ctrlKey } = useMagicKeys() + const baseRole = inject(ProjectRoleInj) provide(SidebarTableInj, table) @@ -106,6 +108,11 @@ const onExpand = async () => { } const onOpenTable = async () => { + if (isMac() ? metaKey.value : ctrlKey.value) { + await _openTable(table.value, true) + return + } + isLoading.value = true try { await _openTable(table.value) diff --git a/packages/nc-gui/composables/useTableNew.ts b/packages/nc-gui/composables/useTableNew.ts index b20494e43b..61ec1ff01b 100644 --- a/packages/nc-gui/composables/useTableNew.ts +++ b/packages/nc-gui/composables/useTableNew.ts @@ -19,6 +19,8 @@ import { useNuxtApp, useTabs, watch, + useMagicKeys, + navigateToBlankTargetOpenOption, } from '#imports' export function useTableNew(param: { onTableCreate?: (tableMeta: TableType) => void; baseId: string; sourceId?: string }) { @@ -60,7 +62,7 @@ export function useTableNew(param: { onTableCreate?: (tableMeta: TableType) => v const tables = computed(() => baseTables.value.get(param.baseId) || []) const base = computed(() => bases.value.get(param.baseId)) - const openTable = async (table: SidebarTableNode) => { + const openTable = async (table: SidebarTableNode, cmdOrCtrl: boolean = false) => { if (!table.base_id) return let base = bases.value.get(table.base_id) @@ -86,7 +88,14 @@ export function useTableNew(param: { onTableCreate?: (tableMeta: TableType) => v const navigateToTable = async () => { if (openedViewsTab.value === 'view') { - await navigateTo(`/${workspaceIdOrType}/${baseIdOrBaseId}/${table?.id}`) + await navigateTo( + `${cmdOrCtrl ? '#' : ''}/${workspaceIdOrType}/${baseIdOrBaseId}/${table?.id}`, + cmdOrCtrl + ? { + open: navigateToBlankTargetOpenOption, + } + : undefined, + ) } table.isViewsLoading = true @@ -99,7 +108,16 @@ export function useTableNew(param: { onTableCreate?: (tableMeta: TableType) => v // find the default view and navigate to it, if not found navigate to the first one const defaultView = views.find((v) => v.is_default) || views[0] - await navigateTo(`/${workspaceIdOrType}/${baseIdOrBaseId}/${table?.id}/${defaultView.id}/${openedViewsTab.value}`) + await navigateTo( + `${cmdOrCtrl ? '#' : ''}/${workspaceIdOrType}/${baseIdOrBaseId}/${table?.id}/${defaultView.id}/${ + openedViewsTab.value + }`, + cmdOrCtrl + ? { + open: navigateToBlankTargetOpenOption, + } + : undefined, + ) } } catch (e) { console.error(e) @@ -119,8 +137,11 @@ export function useTableNew(param: { onTableCreate?: (tableMeta: TableType) => v table.isMetaLoading = false } } - - await Promise.all([navigateToTable(), loadTableMeta()]) + if (cmdOrCtrl) { + await navigateToTable() + } else { + await Promise.all([navigateToTable(), loadTableMeta()]) + } } const createTable = async () => { diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts index 64e426f48f..5c2a1c9e10 100644 --- a/packages/nc-gui/store/views.ts +++ b/packages/nc-gui/store/views.ts @@ -1,6 +1,7 @@ import type { FilterType, SortType, ViewType, ViewTypes } from 'nocodb-sdk' import { acceptHMRUpdate, defineStore } from 'pinia' import type { ViewPageType } from '~/lib' +import { useMagicKeys, navigateToBlankTargetOpenOption } from '#imports' export const useViewsStore = defineStore('viewsStore', () => { const { $api } = useNuxtApp() @@ -27,6 +28,8 @@ export const useViewsStore = defineStore('viewsStore', () => { const { activeWorkspaceId } = storeToRefs(useWorkspace()) + const { meta: metaKey, ctrlKey } = useMagicKeys() + const recentViews = computed(() => allRecentViews.value.filter((f) => f.workspaceId === activeWorkspaceId.value).splice(0, 10), ) @@ -216,6 +219,8 @@ export const useViewsStore = defineStore('viewsStore', () => { hardReload?: boolean doNotSwitchTab?: boolean }) => { + const cmdOrCtrl = isMac() ? metaKey.value : ctrlKey.value + const routeName = 'index-typeOrId-baseId-index-index-viewId-viewTitle-slugs' let baseIdOrBaseId = baseId @@ -231,29 +236,63 @@ export const useViewsStore = defineStore('viewsStore', () => { router.currentRoute.value.query.page && router.currentRoute.value.query.page === 'fields' ) { - await router.push({ - name: routeName, - params: { - viewTitle: view.id || '', - viewId: tableId, - baseId: baseIdOrBaseId, - slugs, - }, - query: router.currentRoute.value.query, - }) + if (cmdOrCtrl) { + await navigateTo( + router.resolve({ + name: routeName, + params: { + viewTitle: view.id || '', + viewId: tableId, + baseId: baseIdOrBaseId, + slugs, + }, + query: router.currentRoute.value.query, + }).href, + { + open: navigateToBlankTargetOpenOption, + }, + ) + } else { + await router.push({ + name: routeName, + params: { + viewTitle: view.id || '', + viewId: tableId, + baseId: baseIdOrBaseId, + slugs, + }, + query: router.currentRoute.value.query, + }) + } } else { - await router.push({ - name: routeName, - params: { - viewTitle: view.id || '', - viewId: tableId, - baseId: baseIdOrBaseId, - slugs, - }, - }) + if (cmdOrCtrl) { + const href = router.resolve({ + name: routeName, + params: { + viewTitle: view.id || '', + viewId: tableId, + baseId: baseIdOrBaseId, + slugs, + }, + }).href + + await navigateTo(href, { + open: navigateToBlankTargetOpenOption, + }) + } else { + await router.push({ + name: routeName, + params: { + viewTitle: view.id || '', + viewId: tableId, + baseId: baseIdOrBaseId, + slugs, + }, + }) + } } - if (hardReload) { + if (!cmdOrCtrl && hardReload) { await router .replace({ name: routeName, diff --git a/packages/nc-gui/utils/urlUtils.ts b/packages/nc-gui/utils/urlUtils.ts index d0c098b460..b1ab1b826f 100644 --- a/packages/nc-gui/utils/urlUtils.ts +++ b/packages/nc-gui/utils/urlUtils.ts @@ -35,3 +35,11 @@ export const openLink = (path: string, baseURL?: string, target = '_blank') => { const url = new URL(path, baseURL) window.open(url.href, target, 'noopener,noreferrer') } + +export const navigateToBlankTargetOpenOption = { + target: '_blank', + windowFeatures: { + noopener: true, + noreferrer: true, + }, +} From d796cef57199eb1f96298fbebb0db3f7589ccd82 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:11:25 +0000 Subject: [PATCH 2/4] fix(nc-gui): small changes --- packages/nc-gui/store/views.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts index 5c2a1c9e10..d767a94af6 100644 --- a/packages/nc-gui/store/views.ts +++ b/packages/nc-gui/store/views.ts @@ -266,19 +266,20 @@ export const useViewsStore = defineStore('viewsStore', () => { } } else { if (cmdOrCtrl) { - const href = router.resolve({ - name: routeName, - params: { - viewTitle: view.id || '', - viewId: tableId, - baseId: baseIdOrBaseId, - slugs, + await navigateTo( + router.resolve({ + name: routeName, + params: { + viewTitle: view.id || '', + viewId: tableId, + baseId: baseIdOrBaseId, + slugs, + }, + }).href, + { + open: navigateToBlankTargetOpenOption, }, - }).href - - await navigateTo(href, { - open: navigateToBlankTargetOpenOption, - }) + ) } else { await router.push({ name: routeName, From 839e93ce33f366b0b368ee4cbf7db55ddfcf74fa Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:11:25 +0000 Subject: [PATCH 3/4] fix(nc-gui): open view new tab in background on cmd+click --- .../dashboard/Sidebar/TopSection.vue | 6 +++++- .../dashboard/TreeView/ProjectNode.vue | 4 ++-- .../dashboard/TreeView/TableNode.vue | 2 +- .../dashboard/TreeView/ViewsNode.vue | 19 ++++++++++++++++--- packages/nc-gui/composables/useTableNew.ts | 4 ++-- packages/nc-gui/store/views.ts | 2 +- packages/nc-gui/store/workspace.ts | 11 +++++++++-- 7 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/nc-gui/components/dashboard/Sidebar/TopSection.vue b/packages/nc-gui/components/dashboard/Sidebar/TopSection.vue index 2b409eef86..84ecb8430f 100644 --- a/packages/nc-gui/components/dashboard/Sidebar/TopSection.vue +++ b/packages/nc-gui/components/dashboard/Sidebar/TopSection.vue @@ -6,6 +6,8 @@ const { isUIAllowed } = useRoles() const { appInfo } = useGlobal() +const { meta: metaKey, ctrlKey } = useMagicKeys() + const { isWorkspaceLoading, isWorkspaceSettingsPageOpened } = storeToRefs(workspaceStore) const { navigateToWorkspaceSettings } = workspaceStore @@ -15,8 +17,10 @@ const { isSharedBase } = storeToRefs(baseStore) const isCreateProjectOpen = ref(false) const navigateToSettings = () => { + const cmdOrCtrl = isMac() ? metaKey.value : ctrlKey.value + // TODO: Handle cloud case properly - navigateToWorkspaceSettings() + navigateToWorkspaceSettings('', cmdOrCtrl) // if (appInfo.value.baseHostName) { // window.location.href = `https://app.${appInfo.value.baseHostName}/dashboard` diff --git a/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue b/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue index 6d2331e002..0230f38925 100644 --- a/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue @@ -16,6 +16,7 @@ import { h, inject, navigateTo, + navigateToBlankTargetOpenOption, openLink, ref, resolveComponent, @@ -26,14 +27,13 @@ import { useDialog, useGlobal, useI18n, + useMagicKeys, useNuxtApp, useRoles, useRouter, useTablesStore, useTabs, useToggle, - useMagicKeys, - navigateToBlankTargetOpenOption, } from '#imports' import type { NcProject } from '#imports' diff --git a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue index e108628e1e..31c23323dd 100644 --- a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue @@ -4,7 +4,7 @@ import { toRef } from '@vue/reactivity' import { message } from 'ant-design-vue' import { storeToRefs } from 'pinia' -import { ProjectRoleInj, TreeViewInj, useNuxtApp, useRoles, useTabs, useMagicKeys } from '#imports' +import { ProjectRoleInj, TreeViewInj, useMagicKeys, useNuxtApp, useRoles, useTabs } from '#imports' import type { SidebarTableNode } from '~/lib' const props = withDefaults( diff --git a/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue b/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue index b5a89d0b7a..910352b24e 100644 --- a/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue @@ -9,6 +9,7 @@ import { message, onKeyStroke, useDebounceFn, + useMagicKeys, useNuxtApp, useRoles, useVModel, @@ -52,6 +53,8 @@ const { activeView } = storeToRefs(useViewsStore()) const { getMeta } = useMetas() +const { meta: metaKey, ctrlKey } = useMagicKeys() + const table = computed(() => props.table) const injectedTable = ref(table.value) @@ -80,11 +83,21 @@ const _title = ref() /** Debounce click handler, so we can potentially enable editing view name {@see onDblClick} */ const onClick = useDebounceFn(() => { - if (isEditing.value || isStopped.value) return - emits('changeView', vModel.value) }, 250) +const handleOnClick = () => { + if (isEditing.value || isStopped.value) return + + const cmdOrCtrl = isMac() ? metaKey.value : ctrlKey.value + + if (cmdOrCtrl) { + emits('changeView', vModel.value) + } else { + onClick() + } +} + /** Enable editing view name on dbl click */ function onDblClick() { if (isMobileMode.value) return @@ -209,7 +222,7 @@ watch(isDropdownOpen, async () => { }" :data-testid="`view-sidebar-view-${vModel.alias || vModel.title}`" @dblclick.stop="onDblClick" - @click="onClick" + @click.prevent="handleOnClick" >
void; baseId: string; sourceId?: string }) { diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts index d767a94af6..f5e60d480c 100644 --- a/packages/nc-gui/store/views.ts +++ b/packages/nc-gui/store/views.ts @@ -1,7 +1,7 @@ import type { FilterType, SortType, ViewType, ViewTypes } from 'nocodb-sdk' import { acceptHMRUpdate, defineStore } from 'pinia' import type { ViewPageType } from '~/lib' -import { useMagicKeys, navigateToBlankTargetOpenOption } from '#imports' +import { navigateToBlankTargetOpenOption, useMagicKeys } from '#imports' export const useViewsStore = defineStore('viewsStore', () => { const { $api } = useNuxtApp() diff --git a/packages/nc-gui/store/workspace.ts b/packages/nc-gui/store/workspace.ts index db79670ce3..36f6d1df16 100644 --- a/packages/nc-gui/store/workspace.ts +++ b/packages/nc-gui/store/workspace.ts @@ -199,8 +199,15 @@ export const useWorkspace = defineStore('workspaceStore', () => { }) } - const navigateToWorkspaceSettings = async () => { - navigateTo('/account/users') + const navigateToWorkspaceSettings = async (_, cmdOrCtrl) => { + await navigateTo( + `${cmdOrCtrl ? '#' : ''}/account/users`, + cmdOrCtrl + ? { + open: navigateToBlankTargetOpenOption, + } + : undefined, + ) } function setLoadingState(isLoading = false) { From 61df4953005c084a2575f0a3cd5d30429cb9e29d Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:11:25 +0000 Subject: [PATCH 4/4] chore(nc-gui): lint --- packages/nc-gui/composables/useTableNew.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nc-gui/composables/useTableNew.ts b/packages/nc-gui/composables/useTableNew.ts index 8d77f790cb..d51e3dd67a 100644 --- a/packages/nc-gui/composables/useTableNew.ts +++ b/packages/nc-gui/composables/useTableNew.ts @@ -16,7 +16,6 @@ import { useBase, useCommandPalette, useI18n, - useMagicKeys, useMetas, useNuxtApp, useTabs,