ContextMenu
Usage
Use anything you like in the default slot of the ContextMenu, and right-click on it to display the menu.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<PContextMenu :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<PContextMenu :items="items">
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template>Items
Use the items prop as an array of objects with the following properties:
label?: stringicon?: stringavatar?: AvatarPropskbds?: string[] | KbdProps[]type?: "link" | "label" | "separator" | "checkbox"color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"checked?: booleandisabled?: booleanslot?: stringonSelect?: (e: Event) => voidonUpdateChecked?: (checked: boolean) => voidchildren?: ContextMenuItem[] | ContextMenuItem[][]class?: anyui?: { item?: ClassNameValue, label?: ClassNameValue, separator?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelExternalIcon?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue }
You can pass any property from the Link component such as to, target, etc.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<PContextMenu
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[][]>([
[
{
label: 'Appearance',
children: [
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
]
}
],
[
{
label: 'Show Sidebar',
kbds: ['meta', 's']
},
{
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'd']
},
{
label: 'Collapse Pinned Tabs',
disabled: true
}
],
[
{
label: 'Refresh the Page'
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
},
{
type: 'separator'
},
{
label: 'Developer',
children: [
[
{
label: 'View Source',
kbds: ['meta', 'shift', 'u']
},
{
label: 'Developer Tools',
kbds: ['option', 'meta', 'i']
},
{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'c']
}
],
[
{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'j']
}
]
]
}
]
])
</script>
<template>
<PContextMenu
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template>items prop to create separated groups of items.children array of objects with the same properties as the items prop to create a nested menu which can be controlled using the open, defaultOpen and content properties.Size
Use the size prop to change the size of the ContextMenu.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[]>([
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
])
</script>
<template>
<PContextMenu
size="xl"
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[]>([
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
])
</script>
<template>
<PContextMenu
size="xl"
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template>Modal
Use the modal prop to control whether the ContextMenu blocks interaction with outside content. Defaults to true.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[]>([
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
])
</script>
<template>
<PContextMenu
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[]>([
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
])
</script>
<template>
<PContextMenu
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template>Disabled
Use the disabled prop to disable the ContextMenu.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[]>([
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
])
</script>
<template>
<PContextMenu
disabled
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui'
const items = ref<ContextMenuItem[]>([
{
label: 'System',
icon: 'i-lucide-monitor'
},
{
label: 'Light',
icon: 'i-lucide-sun'
},
{
label: 'Dark',
icon: 'i-lucide-moon'
}
])
</script>
<template>
<PContextMenu
disabled
:items="items"
:ui="{
content: 'w-48'
}"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-border-accented text-sm aspect-video w-72"
>
Right click here
</div>
</PContextMenu>
</template>Examples
With checkbox items
You can use the type property with checkbox and use the checked / onUpdateChecked properties to control the checked state of the item.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui';
const showSidebar = ref(true);
const showToolbar = ref(false);
const items = computed<Array<ContextMenuItem>>(() => [{
label: 'View',
type: 'label' as const,
}, {
type: 'separator' as const,
}, {
label: 'Show Sidebar',
type: 'checkbox' as const,
checked: showSidebar.value,
onUpdateChecked(checked: boolean) {
showSidebar.value = checked;
},
onSelect(e: Event) {
e.preventDefault();
},
}, {
label: 'Show Toolbar',
type: 'checkbox' as const,
checked: showToolbar.value,
onUpdateChecked(checked: boolean) {
showToolbar.value = checked;
},
}, {
label: 'Collapse Pinned Tabs',
type: 'checkbox' as const,
disabled: true,
}]);
</script>
<template>
<PContextMenu
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
</PContextMenu>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { ContextMenuItem } from 'pohon-ui';
const showSidebar = ref(true);
const showToolbar = ref(false);
const items = computed<Array<ContextMenuItem>>(() => [{
label: 'View',
type: 'label' as const,
}, {
type: 'separator' as const,
}, {
label: 'Show Sidebar',
type: 'checkbox' as const,
checked: showSidebar.value,
onUpdateChecked(checked: boolean) {
showSidebar.value = checked;
},
onSelect(e: Event) {
e.preventDefault();
},
}, {
label: 'Show Toolbar',
type: 'checkbox' as const,
checked: showToolbar.value,
onUpdateChecked(checked: boolean) {
showToolbar.value = checked;
},
}, {
label: 'Collapse Pinned Tabs',
type: 'checkbox' as const,
disabled: true,
}]);
</script>
<template>
<PContextMenu
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
</PContextMenu>
</template>
checked state of items, it's recommended to wrap your items array inside a computed.With color items
You can use the color property to highlight certain items with a color.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui';
const items: Array<Array<ContextMenuItem>> = [
[
{
label: 'View',
icon: 'i-lucide-eye',
},
{
label: 'Copy',
icon: 'i-lucide-copy',
},
{
label: 'Edit',
icon: 'i-lucide-pencil',
},
],
[
{
label: 'Delete',
color: 'error' as const,
icon: 'i-lucide-trash',
},
],
];
</script>
<template>
<PContextMenu
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
</PContextMenu>
</template>
Control open state
You can control the open state by using the v-model:open directive. When opened programmatically, the menu is anchored to the trigger instead of the cursor.
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui';
const open = ref(false);
defineShortcuts({
o: () => open.value = !open.value,
});
const items: Array<ContextMenuItem> = [
{
label: 'Profile',
icon: 'i-lucide-user',
},
{
label: 'Billing',
icon: 'i-lucide-credit-card',
},
{
label: 'Settings',
icon: 'i-lucide-cog',
},
];
</script>
<template>
<PContextMenu
v-model:open="open"
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
</PContextMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui';
const open = ref(false);
defineShortcuts({
o: () => open.value = !open.value,
});
const items: Array<ContextMenuItem> = [
{
label: 'Profile',
icon: 'i-lucide-user',
},
{
label: 'Billing',
icon: 'i-lucide-credit-card',
},
{
label: 'Settings',
icon: 'i-lucide-cog',
},
];
</script>
<template>
<PContextMenu
v-model:open="open"
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
</PContextMenu>
</template>
defineShortcuts, you can toggle the ContextMenu by pressing O.With custom slot
Use the slot property to customize a specific item.
You will have access to the following slots:
#{{ item.slot }}#{{ item.slot }}-leading#{{ item.slot }}-label#{{ item.slot }}-trailing
<script setup lang="ts">
import type { ContextMenuItem } from 'pohon-ui';
const loading = ref(true);
const items = [
{
label: 'Refresh the Page',
slot: 'refresh' as const,
},
{
label: 'Clear Cookies and Refresh',
},
{
label: 'Clear Cache and Refresh',
},
] satisfies Array<ContextMenuItem>;
</script>
<template>
<PContextMenu
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
<template #refresh-label>
{{ loading ? 'Refreshing...' : 'Refresh the Page' }}
</template>
<template #refresh-trailing>
<PIcon
v-if="loading"
name="i-lucide-loader-circle"
class="color-primary shrink-0 size-5 animate-spin"
/>
</template>
</PContextMenu>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContextMenuItem } from 'pohon-ui';
const loading = ref(true);
const items = [
{
label: 'Refresh the Page',
slot: 'refresh' as const,
},
{
label: 'Clear Cookies and Refresh',
},
{
label: 'Clear Cache and Refresh',
},
] satisfies Array<ContextMenuItem>;
</script>
<template>
<PContextMenu
:items="items"
:ui="{ content: 'w-48' }"
>
<div class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center">
Right click here
</div>
<template #refresh-label>
{{ loading ? 'Refreshing...' : 'Refresh the Page' }}
</template>
<template #refresh-trailing>
<PIcon
v-if="loading"
name="i-lucide-loader-circle"
class="color-primary shrink-0 size-5 animate-spin"
/>
</template>
</PContextMenu>
</template>
#item, #item-leading, #item-label and #item-trailing slots to customize all items.Extract shortcuts
Use the extractShortcuts utility to automatically define shortcuts from menu items with a kbds property. It recursively extracts shortcuts and returns an object compatible with defineShortcuts.
<script setup lang="ts">
const items = [
[{
label: 'Show Sidebar',
kbds: ['meta', 'S'],
onSelect() {
console.log('Show Sidebar clicked');
}
}, {
label: 'Show Toolbar',
kbds: ['shift', 'meta', 'D'],
onSelect() {
console.log('Show Toolbar clicked');
}
}, {
label: 'Collapse Pinned Tabs',
disabled: true
}],
[{
label: 'Refresh the Page'
}, {
label: 'Clear Cookies and Refresh'
}, {
label: 'Clear Cache and Refresh'
}, {
type: 'separator' as const
}, {
label: 'Developer',
children: [[{
label: 'View Source',
kbds: ['option', 'meta', 'U'],
onSelect() {
console.log('View Source clicked');
}
}, {
label: 'Developer Tools',
kbds: ['option', 'meta', 'I'],
onSelect() {
console.log('Developer Tools clicked');
}
}], [{
label: 'Inspect Elements',
kbds: ['option', 'meta', 'C'],
onSelect() {
console.log('Inspect Elements clicked');
}
}], [{
label: 'JavaScript Console',
kbds: ['option', 'meta', 'J'],
onSelect() {
console.log('JavaScript Console clicked');
}
}]]
}]
];
defineShortcuts(extractShortcuts(items));
</script>
select function of the corresponding item.API
Props
| Prop | Default | Type |
|---|
Slots
| Slot | Type |
|---|
Emits
| Event | Type |
|---|
Theme
export default defineAppConfig({
ui: {
contextMenu: {
slots: {
content: 'bg-background ring-border rounded-md flex flex-col max-h-$akar-context-menu-content-available-height min-w-32 ring shadow-lg origin-$akar-context-menu-content-transform-origin overflow-hidden data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
viewport: 'relative divide-y divide-border scroll-py-1 overflow-y-auto flex-1',
group: 'p-1 isolate',
label: 'w-full flex items-center font-600 color-text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
item: 'group outline-none flex w-full select-none items-start relative before:rounded-md before:inset-px before:absolute before:-z-1 before:content-empty data-[disabled]:opacity-75 data-[disabled]:cursor-not-allowed',
itemLeadingIcon: 'shrink-0',
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0',
itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0',
itemTrailingKbdsSize: '',
itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate color-text-muted',
itemLabelExternalIcon: 'inline-block size-3 align-top color-text-dimmed'
},
variants: {
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
active: {
true: {
item: 'color-text-highlighted before:bg-background-elevated',
itemLeadingIcon: 'color-text'
},
false: {
item: [
'color-text data-[highlighted]:color-text-highlighted data-[state=open]:color-text-highlighted data-[highlighted]:before:bg-background-elevated/50 data-[state=open]:before:bg-background-elevated/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'color-text-dimmed group-data-[highlighted]:color-text group-data-[state=open]:color-text',
'transition-colors'
]
}
},
loading: {
true: {
itemLeadingIcon: 'animate-spin'
}
},
size: {
xs: {
label: 'p-1 text-xs gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemTrailingIcon: 'size-4',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'sm'
},
sm: {
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemTrailingIcon: 'size-4',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'sm'
},
md: {
label: 'p-1.5 text-sm gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemTrailingIcon: 'size-5',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'md'
},
lg: {
label: 'p-2 text-sm gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemTrailingIcon: 'size-5',
itemTrailingKbds: 'gap-1',
itemTrailingKbdsSize: 'md'
},
xl: {
label: 'p-2 text-base gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemTrailingIcon: 'size-6',
itemTrailingKbds: 'gap-1',
itemTrailingKbdsSize: 'lg'
}
}
},
compoundVariants: [
{
color: 'primary',
active: false,
class: {
item: 'color-primary data-[highlighted]:color-primary data-[highlighted]:before:bg-primary/10 data-[state=open]:before:bg-primary/10',
itemLeadingIcon: 'color-primary/75 group-data-[highlighted]:color-primary group-data-[state=open]:color-primary'
}
},
{
color: 'primary',
active: true,
class: {
item: 'color-primary before:bg-primary/10',
itemLeadingIcon: 'color-primary'
}
}
],
defaultVariants: {
size: 'md'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
contextMenu: {
slots: {
content: 'bg-background ring-border rounded-md flex flex-col max-h-$akar-context-menu-content-available-height min-w-32 ring shadow-lg origin-$akar-context-menu-content-transform-origin overflow-hidden data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
viewport: 'relative divide-y divide-border scroll-py-1 overflow-y-auto flex-1',
group: 'p-1 isolate',
label: 'w-full flex items-center font-600 color-text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
item: 'group outline-none flex w-full select-none items-start relative before:rounded-md before:inset-px before:absolute before:-z-1 before:content-empty data-[disabled]:opacity-75 data-[disabled]:cursor-not-allowed',
itemLeadingIcon: 'shrink-0',
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
itemTrailingIcon: 'shrink-0',
itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0',
itemTrailingKbdsSize: '',
itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate color-text-muted',
itemLabelExternalIcon: 'inline-block size-3 align-top color-text-dimmed'
},
variants: {
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
active: {
true: {
item: 'color-text-highlighted before:bg-background-elevated',
itemLeadingIcon: 'color-text'
},
false: {
item: [
'color-text data-[highlighted]:color-text-highlighted data-[state=open]:color-text-highlighted data-[highlighted]:before:bg-background-elevated/50 data-[state=open]:before:bg-background-elevated/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'color-text-dimmed group-data-[highlighted]:color-text group-data-[state=open]:color-text',
'transition-colors'
]
}
},
loading: {
true: {
itemLeadingIcon: 'animate-spin'
}
},
size: {
xs: {
label: 'p-1 text-xs gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemTrailingIcon: 'size-4',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'sm'
},
sm: {
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemTrailingIcon: 'size-4',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'sm'
},
md: {
label: 'p-1.5 text-sm gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemTrailingIcon: 'size-5',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'md'
},
lg: {
label: 'p-2 text-sm gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemTrailingIcon: 'size-5',
itemTrailingKbds: 'gap-1',
itemTrailingKbdsSize: 'md'
},
xl: {
label: 'p-2 text-base gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemTrailingIcon: 'size-6',
itemTrailingKbds: 'gap-1',
itemTrailingKbdsSize: 'lg'
}
}
},
compoundVariants: [
{
color: 'primary',
active: false,
class: {
item: 'color-primary data-[highlighted]:color-primary data-[highlighted]:before:bg-primary/10 data-[state=open]:before:bg-primary/10',
itemLeadingIcon: 'color-primary/75 group-data-[highlighted]:color-primary group-data-[state=open]:color-primary'
}
},
{
color: 'primary',
active: true,
class: {
item: 'color-primary before:bg-primary/10',
itemLeadingIcon: 'color-primary'
}
}
],
defaultVariants: {
size: 'md'
}
}
}
})
]
})