mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 13:26:53 +00:00
409 lines
13 KiB
Vue
409 lines
13 KiB
Vue
<script lang="ts" setup>
|
|
provide(IsMiniSidebarInj, ref(true))
|
|
|
|
const router = useRouter()
|
|
|
|
const route = router.currentRoute
|
|
|
|
const { navigateToProject, isMobileMode, appInfo } = useGlobal()
|
|
|
|
const { meta: metaKey, control } = useMagicKeys()
|
|
|
|
const workspaceStore = useWorkspace()
|
|
|
|
const {
|
|
activeWorkspaceId,
|
|
activeWorkspace,
|
|
isWorkspaceSettingsPageOpened,
|
|
isIntegrationsPageOpened,
|
|
isWorkspacesLoading,
|
|
isTemplatesPageOpened,
|
|
} = storeToRefs(workspaceStore)
|
|
|
|
const {
|
|
navigateToWorkspaceSettings,
|
|
navigateToIntegrations: _navigateToIntegrations,
|
|
navigateToTemplates: _navigateToTemplates,
|
|
isTemplatesFeatureEnabled,
|
|
} = workspaceStore
|
|
|
|
const { basesList } = storeToRefs(useBases())
|
|
|
|
const { isSharedBase } = storeToRefs(useBase())
|
|
|
|
const { isUIAllowed } = useRoles()
|
|
|
|
const { setActiveCmdView } = useCommand()
|
|
|
|
const { isChatWootEnabled } = useProvideChatwoot()
|
|
|
|
const {
|
|
isPanelExpanded: isChatPanelExpanded,
|
|
hasWorkspaceContext: hasChatWorkspaceContext,
|
|
hasBaseContext: hasChatBaseContext,
|
|
toggleChatPanel,
|
|
} = useChatPanel()
|
|
|
|
const { blockAiChat } = useEeConfig()
|
|
|
|
const handleChatToggle = () => {
|
|
toggleChatPanel()
|
|
}
|
|
|
|
const navigateToProjectPage = () => {
|
|
if (route.value.name?.startsWith('index-typeOrId-baseId-')) {
|
|
return
|
|
}
|
|
|
|
const lastVisitedBase = ncLastVisitedBase().get()
|
|
|
|
const baseToNavigate = lastVisitedBase
|
|
? basesList.value?.find((b) => b.id === lastVisitedBase) ?? basesList.value[0]
|
|
: basesList.value[0]
|
|
|
|
navigateToProject({ workspaceId: isEeUI ? activeWorkspaceId.value : undefined, baseId: baseToNavigate?.id })
|
|
}
|
|
|
|
const navigateToSettings = () => {
|
|
if (isEeUI && !appInfo.value?.ee && !appInfo.value?.isOnPrem) {
|
|
navigateTo('/account/users/list')
|
|
return
|
|
}
|
|
|
|
const cmdOrCtrl = isMac() ? metaKey.value : control.value
|
|
|
|
navigateToWorkspaceSettings('', cmdOrCtrl)
|
|
}
|
|
|
|
const navigateToTemplates = () => {
|
|
const cmdOrCtrl = isMac() ? metaKey.value : control.value
|
|
|
|
_navigateToTemplates('', cmdOrCtrl)
|
|
}
|
|
|
|
const navigateToIntegrations = () => {
|
|
const cmdOrCtrl = isMac() ? metaKey.value : control.value
|
|
|
|
_navigateToIntegrations('', cmdOrCtrl)
|
|
}
|
|
|
|
useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
|
|
const isBaseSearchInput = e.target instanceof HTMLInputElement && e.target.closest('.nc-base-search-input')
|
|
|
|
if (
|
|
!e.altKey ||
|
|
(!isBaseSearchInput &&
|
|
(isActiveInputElementExist(e) ||
|
|
cmdKActive() ||
|
|
isCmdJActive() ||
|
|
isNcDropdownOpen() ||
|
|
isActiveElementInsideExtension() ||
|
|
isActiveElementInsideScriptPane() ||
|
|
isDrawerOrModalExist() ||
|
|
isExpandedFormOpenExist()))
|
|
) {
|
|
return
|
|
}
|
|
|
|
switch (e.code) {
|
|
case 'KeyB': {
|
|
e.preventDefault()
|
|
navigateToProjectPage()
|
|
break
|
|
}
|
|
}
|
|
})
|
|
|
|
// Cmd/Ctrl + Shift + A — toggle AI chat
|
|
useEventListener(document, 'keydown', (e: KeyboardEvent) => {
|
|
if (!isEeUI || blockAiChat.value) return
|
|
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey
|
|
if (
|
|
cmdOrCtrl &&
|
|
e.shiftKey &&
|
|
e.code === 'KeyA' &&
|
|
!isActiveInputElementExist(e) &&
|
|
!isNcDropdownOpen() &&
|
|
!isDrawerOrModalExist()
|
|
) {
|
|
e.preventDefault()
|
|
handleChatToggle()
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="nc-mini-sidebar" data-testid="nc-mini-sidebar">
|
|
<div class="flex flex-col items-center">
|
|
<DashboardMiniSidebarItemWrapper size="small" show-in-mobile>
|
|
<div
|
|
class="min-h-9 sticky top-0 bg-nc-bg-gray-minisidebar"
|
|
:class="{
|
|
'pt-1.5 pb-1': isMobileMode,
|
|
}"
|
|
>
|
|
<GeneralLoader v-if="isWorkspacesLoading" size="large" />
|
|
<NcTooltip v-else placement="right" hide-on-click :arrow="false" :disabled="!activeWorkspace">
|
|
<template #title>
|
|
<div class="capitalize">{{ activeWorkspace?.title ?? '' }}</div>
|
|
</template>
|
|
<WorkspaceMenu />
|
|
</NcTooltip>
|
|
</div>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip placement="right" hide-on-click :arrow="false">
|
|
<template #title>
|
|
<div class="flex items-center gap-1">{{ $t('labels.quickSearch') }} {{ renderCmdOrCtrlKey(true) }} K</div>
|
|
</template>
|
|
<div
|
|
v-e="['c:quick-actions']"
|
|
class="nc-mini-sidebar-btn-full-width"
|
|
data-testid="nc-sidebar-cmd-k-btn"
|
|
@click="setActiveCmdView('cmd-k')"
|
|
>
|
|
<div class="nc-mini-sidebar-btn">
|
|
<GeneralIcon icon="search" class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip placement="right" hide-on-click :arrow="false">
|
|
<template #title>
|
|
<div class="flex items-center gap-1">{{ $t('labels.recentViews') }} {{ renderCmdOrCtrlKey(true) }} L</div>
|
|
</template>
|
|
<div
|
|
v-e="['c:quick-actions']"
|
|
class="nc-mini-sidebar-btn-full-width"
|
|
data-testid="nc-sidebar-cmd-l-btn"
|
|
@click="setActiveCmdView('cmd-l')"
|
|
>
|
|
<div class="nc-mini-sidebar-btn">
|
|
<MdiClockOutline class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip placement="right" hide-on-click :arrow="false">
|
|
<template #title>
|
|
<div class="flex items-center gap-1">{{ $t('labels.searchDocumentation') }} {{ renderCmdOrCtrlKey(true) }} J</div>
|
|
</template>
|
|
<div
|
|
v-e="['c:quick-actions']"
|
|
class="nc-mini-sidebar-btn-full-width"
|
|
data-testid="nc-sidebar-cmd-j-btn"
|
|
@click="setActiveCmdView('cmd-j')"
|
|
>
|
|
<div class="nc-mini-sidebar-btn">
|
|
<GeneralIcon icon="ncFile" class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<div
|
|
v-if="(!isMobileMode || isEeUI) && (isUIAllowed('workspaceSettings') || isUIAllowed('workspaceCollaborators'))"
|
|
class="px-2 my-2 w-full"
|
|
>
|
|
<NcDivider class="!my-0 !border-nc-border-gray-dark" />
|
|
</div>
|
|
<DashboardMiniSidebarItemWrapper
|
|
v-if="isUIAllowed('workspaceSettings') || isUIAllowed('workspaceCollaborators')"
|
|
:show-in-mobile="isEeUI"
|
|
>
|
|
<NcTooltip
|
|
:title="isEeUI ? `${$t('objects.workspace')} ${$t('labels.settings')}` : $t('title.teamAndSettings')"
|
|
placement="right"
|
|
hide-on-click
|
|
:arrow="false"
|
|
>
|
|
<div
|
|
v-e="['c:team:settings']"
|
|
class="nc-mini-sidebar-btn-full-width"
|
|
data-testid="nc-sidebar-team-settings-btn"
|
|
@click="navigateToSettings"
|
|
>
|
|
<div
|
|
class="nc-mini-sidebar-btn"
|
|
:class="{
|
|
active: isWorkspaceSettingsPageOpened,
|
|
}"
|
|
>
|
|
<GeneralIcon icon="ncSettings" class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<DashboardMiniSidebarItemWrapper v-if="isUIAllowed('workspaceIntegrations')">
|
|
<NcTooltip
|
|
:title="isEeUI ? `${$t('objects.workspace')} ${$t('general.integrations')}` : $t('general.integrations')"
|
|
placement="right"
|
|
hide-on-click
|
|
:arrow="false"
|
|
>
|
|
<div
|
|
v-e="['c:integrations']"
|
|
class="nc-mini-sidebar-btn-full-width"
|
|
data-testid="nc-sidebar-integrations-btn"
|
|
@click="navigateToIntegrations"
|
|
>
|
|
<div
|
|
class="nc-mini-sidebar-btn"
|
|
:class="{
|
|
active: isIntegrationsPageOpened,
|
|
}"
|
|
>
|
|
<GeneralIcon icon="integration" class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<div v-if="!isMobileMode" class="px-2 w-full">
|
|
<NcDivider class="!my-0 !border-nc-border-gray-dark !my-2" />
|
|
</div>
|
|
<DashboardMiniSidebarItemWrapper v-if="isTemplatesFeatureEnabled">
|
|
<NcTooltip :title="$t('general.templates')" placement="right" hide-on-click :arrow="false">
|
|
<div
|
|
v-e="['c:templates']"
|
|
class="nc-mini-sidebar-btn-full-width"
|
|
data-testid="nc-sidebar-templates-btn"
|
|
@click="navigateToTemplates"
|
|
>
|
|
<div
|
|
class="nc-mini-sidebar-btn"
|
|
:class="{
|
|
active: isTemplatesPageOpened,
|
|
}"
|
|
>
|
|
<GeneralIcon icon="globe" class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip :title="$t('labels.myNotifications')" placement="right" hide-on-click :arrow="false">
|
|
<NotificationMenu />
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
|
|
<DashboardMiniSidebarItemWrapper v-if="isEeUI && !blockAiChat && hasChatWorkspaceContext && hasChatBaseContext">
|
|
<NcTooltip placement="right" hide-on-click :arrow="false">
|
|
<template #title>
|
|
<div class="flex items-center gap-1">{{ $t('labels.aiChat') }} {{ renderCmdOrCtrlKey(true) }} ⇧ A</div>
|
|
</template>
|
|
<div
|
|
v-e="['c:chat:toggle']"
|
|
class="nc-mini-sidebar-btn-full-width relative"
|
|
data-testid="nc-sidebar-chat-btn"
|
|
@click="handleChatToggle"
|
|
>
|
|
<div class="nc-mini-sidebar-btn" :class="{ active: isChatPanelExpanded }">
|
|
<GeneralIcon icon="ncAutoAwesome" class="h-4 w-4" />
|
|
</div>
|
|
</div>
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
</div>
|
|
<div class="flex flex-col items-center">
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<DashboardMiniSidebarTheme />
|
|
</DashboardMiniSidebarItemWrapper>
|
|
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip :title="$t('general.help')" placement="right" hide-on-click :arrow="false">
|
|
<DashboardMiniSidebarHelp />
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<template v-if="!isMobileMode">
|
|
<!-- Disabled for now since feed is not actively maintained -->
|
|
<!--
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip
|
|
v-if="appInfo.feedEnabled"
|
|
:title="`${$t('title.whatsNew')}!`"
|
|
placement="right"
|
|
hide-on-click
|
|
:arrow="false"
|
|
>
|
|
<DashboardSidebarFeed />
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper> -->
|
|
<DashboardMiniSidebarItemWrapper v-if="isChatWootEnabled">
|
|
<NcTooltip :title="`${$t('labels.chatWithNocoDBSupport')}!`" placement="right" hide-on-click :arrow="false">
|
|
<DashboardSidebarChatSupport />
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
<div class="px-2 w-full">
|
|
<NcDivider class="!my-2 !border-nc-border-gray-dark" />
|
|
</div>
|
|
<DashboardMiniSidebarItemWrapper>
|
|
<NcTooltip v-if="!isSharedBase" :title="$t('labels.createNew')" placement="right" hide-on-click :arrow="false">
|
|
<DashboardMiniSidebarCreateNewActionMenu />
|
|
</NcTooltip>
|
|
</DashboardMiniSidebarItemWrapper>
|
|
</template>
|
|
<div v-else class="px-2 w-full">
|
|
<NcDivider class="!my-2 !border-nc-border-gray-dark" />
|
|
</div>
|
|
|
|
<DashboardSidebarUserInfo />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.nc-mini-sidebar {
|
|
@apply w-[var(--mini-sidebar-width)] flex-none bg-nc-bg-gray-minisidebar flex flex-col justify-between items-center border-r-1 border-nc-border-gray-medium z-502 nc-scrollbar-thin overflow-x-hidden relative;
|
|
|
|
.nc-mini-sidebar-ws-item {
|
|
@apply cursor-pointer h-9 w-8 rounded py-1 flex items-center justify-center children:flex-none text-nc-content-gray-muted transition-all duration-200;
|
|
|
|
.nc-workspace-avatar {
|
|
img {
|
|
@apply !cursor-pointer;
|
|
}
|
|
}
|
|
|
|
&.nc-small-shadow .nc-workspace-avatar {
|
|
box-shadow: 0px 5px 0px -2px rgba(var(--rgb-base), 0.4);
|
|
}
|
|
&.nc-medium-shadow .nc-workspace-avatar {
|
|
box-shadow: 0px 4px 0px -2px rgba(var(--rgb-base), 0.4), 0px 7px 0px -3px rgba(var(--rgb-base), 0.2);
|
|
}
|
|
}
|
|
|
|
.nc-mini-sidebar-btn-full-width {
|
|
@apply w-[var(--mini-sidebar-width)] h-[var(--mini-sidebar-width)] flex-none flex justify-center items-center cursor-pointer transition-all duration-200;
|
|
|
|
&:hover {
|
|
.nc-mini-sidebar-btn:not(.active) {
|
|
@apply bg-nc-bg-gray-medium;
|
|
}
|
|
}
|
|
}
|
|
|
|
.nc-mini-sidebar-btn {
|
|
@apply cursor-pointer h-7 w-7 rounded !p-1.5 flex items-center justify-center children:flex-none !text-nc-content-gray-muted transition-all duration-200;
|
|
|
|
&:not(.active) {
|
|
@apply hover:bg-nc-bg-gray-medium;
|
|
}
|
|
|
|
&.active {
|
|
@apply !bg-nc-brand-100 dark:!bg-nc-bg-gray-medium !text-nc-content-brand;
|
|
}
|
|
|
|
&.active-base {
|
|
@apply !text-nc-content-brand;
|
|
}
|
|
|
|
&.hovered {
|
|
@apply bg-nc-bg-gray-medium;
|
|
}
|
|
}
|
|
}
|
|
</style>
|