feat: Add dropdown component (#10358)
This PR adds dropdown primitives to help compose custom dropdowns across the app. The following the sample usage --------- Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
@@ -4,11 +4,16 @@ import Auth from 'dashboard/api/auth';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import Avatar from 'next/avatar/Avatar.vue';
|
||||
import Icon from 'next/icon/Icon.vue';
|
||||
import SidebarProfileMenuStatus from './SidebarProfileMenuStatus.vue';
|
||||
|
||||
import {
|
||||
DropdownContainer,
|
||||
DropdownBody,
|
||||
DropdownSeparator,
|
||||
DropdownItem,
|
||||
} from 'next/dropdown-menu/base';
|
||||
|
||||
const emit = defineEmits(['close', 'openKeyShortcutModal']);
|
||||
|
||||
defineOptions({
|
||||
@@ -21,14 +26,6 @@ const router = useRouter();
|
||||
const globalConfig = useMapGetter('globalConfig/get');
|
||||
const currentUser = useMapGetter('getCurrentUser');
|
||||
const currentUserAvailability = useMapGetter('getCurrentUserAvailability');
|
||||
const [showProfileMenu, toggleProfileMenu] = useToggle(false);
|
||||
|
||||
const closeMenu = () => {
|
||||
if (showProfileMenu.value) {
|
||||
emit('close');
|
||||
toggleProfileMenu(false);
|
||||
}
|
||||
};
|
||||
|
||||
const menuItems = computed(() => {
|
||||
return [
|
||||
@@ -37,7 +34,6 @@ const menuItems = computed(() => {
|
||||
label: t('SIDEBAR_ITEMS.CONTACT_SUPPORT'),
|
||||
icon: 'i-lucide-life-buoy',
|
||||
click: () => {
|
||||
closeMenu();
|
||||
window.$chatwoot.toggle();
|
||||
},
|
||||
},
|
||||
@@ -46,7 +42,6 @@ const menuItems = computed(() => {
|
||||
label: t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS'),
|
||||
icon: 'i-lucide-keyboard',
|
||||
click: () => {
|
||||
closeMenu();
|
||||
emit('openKeyShortcutModal');
|
||||
},
|
||||
},
|
||||
@@ -55,20 +50,26 @@ const menuItems = computed(() => {
|
||||
label: t('SIDEBAR_ITEMS.PROFILE_SETTINGS'),
|
||||
icon: 'i-lucide-user-pen',
|
||||
click: () => {
|
||||
closeMenu();
|
||||
router.push({ name: 'profile_settings_index' });
|
||||
},
|
||||
},
|
||||
{
|
||||
show: true,
|
||||
label: t('SIDEBAR_ITEMS.APPEARANCE'),
|
||||
icon: 'i-lucide-swatch-book',
|
||||
icon: 'i-lucide-palette',
|
||||
click: () => {
|
||||
closeMenu();
|
||||
const ninja = document.querySelector('ninja-keys');
|
||||
ninja.open({ parent: 'appearance_settings' });
|
||||
},
|
||||
},
|
||||
{
|
||||
show: true,
|
||||
label: t('SIDEBAR_ITEMS.DOCS'),
|
||||
icon: 'i-lucide-book',
|
||||
click: () => {
|
||||
window.open('https://www.chatwoot.com/hc/user-guide/en', '_blank');
|
||||
},
|
||||
},
|
||||
{
|
||||
show: currentUser.value.type === 'SuperAdmin',
|
||||
label: t('SIDEBAR_ITEMS.SUPER_ADMIN_CONSOLE'),
|
||||
@@ -79,7 +80,7 @@ const menuItems = computed(() => {
|
||||
{
|
||||
show: true,
|
||||
label: t('SIDEBAR_ITEMS.LOGOUT'),
|
||||
icon: 'i-lucide-log-out',
|
||||
icon: 'i-lucide-power',
|
||||
click: Auth.logout,
|
||||
},
|
||||
];
|
||||
@@ -91,56 +92,40 @@ const allowedMenuItems = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative z-20 w-full min-w-0">
|
||||
<button
|
||||
class="flex gap-2 items-center rounded-lg cursor-pointer text-left w-full hover:bg-n-alpha-1 p-1"
|
||||
v-bind="$attrs"
|
||||
:class="{
|
||||
'bg-n-alpha-1': showProfileMenu,
|
||||
}"
|
||||
@click="toggleProfileMenu"
|
||||
>
|
||||
<Avatar
|
||||
:size="32"
|
||||
:name="currentUser.available_name"
|
||||
:src="currentUser.avatar_url"
|
||||
:status="currentUserAvailability"
|
||||
class="flex-shrink-0"
|
||||
rounded-full
|
||||
/>
|
||||
<div class="min-w-0">
|
||||
<div class="text-n-slate-12 text-sm leading-4 font-medium truncate">
|
||||
{{ currentUser.available_name }}
|
||||
</div>
|
||||
<div class="text-n-slate-11 text-xs truncate">
|
||||
{{ currentUser.email }}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
v-if="showProfileMenu"
|
||||
v-on-clickaway="closeMenu"
|
||||
class="absolute left-0 bottom-12 z-50"
|
||||
>
|
||||
<div
|
||||
class="w-72 min-h-32 bg-n-solid-1 border border-n-weak rounded-xl shadow-sm"
|
||||
<DropdownContainer
|
||||
class="relative z-20 w-full min-w-0"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<template #trigger="{ toggle, isOpen }">
|
||||
<button
|
||||
class="flex gap-2 items-center rounded-lg cursor-pointer text-left w-full hover:bg-n-alpha-1 p-1"
|
||||
:class="{ 'bg-n-alpha-1': isOpen }"
|
||||
@click="toggle"
|
||||
>
|
||||
<SidebarProfileMenuStatus />
|
||||
<div class="border-t border-n-strong mx-2 my-0" />
|
||||
<ul class="list-none m-0 grid gap-1 p-1 text-n-slate-12">
|
||||
<li v-for="item in allowedMenuItems" :key="item.label" class="m-0">
|
||||
<component
|
||||
:is="item.link ? 'a' : 'button'"
|
||||
v-bind="item.link ? { target: item.target, href: item.link } : {}"
|
||||
class="text-left hover:bg-n-alpha-1 px-2 py-1.5 w-full flex items-center gap-2"
|
||||
@click="item.click"
|
||||
>
|
||||
<Icon :icon="item.icon" class="size-4" />
|
||||
{{ item.label }}
|
||||
</component>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Avatar
|
||||
:size="32"
|
||||
:name="currentUser.available_name"
|
||||
:src="currentUser.avatar_url"
|
||||
:status="currentUserAvailability"
|
||||
class="flex-shrink-0"
|
||||
rounded-full
|
||||
/>
|
||||
<div class="min-w-0">
|
||||
<div class="text-n-slate-12 text-sm leading-4 font-medium truncate">
|
||||
{{ currentUser.available_name }}
|
||||
</div>
|
||||
<div class="text-n-slate-11 text-xs truncate">
|
||||
{{ currentUser.email }}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
<DropdownBody class="left-0 bottom-12 z-50 w-80 mb-1">
|
||||
<SidebarProfileMenuStatus />
|
||||
<DropdownSeparator />
|
||||
<template v-for="item in allowedMenuItems" :key="item.label">
|
||||
<DropdownItem v-if="item.show" v-bind="item" />
|
||||
</template>
|
||||
</DropdownBody>
|
||||
</DropdownContainer>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user