Appearance
useCollection
useCollection creates a collection manager for keyboard navigation. Use it when your floating surface (menu, submenu, combobox, or dropdown) needs to manage active focus across a list of items.
Type
ts
function useCollection(options: UseCollectionOptions): UseCollectionReturn;
interface UseCollectionOptions {
values: MaybeRefOrGetter<readonly string[]>;
isValueDisabled?: (value: string) => boolean;
}
interface CollectionNavigationOptions {
loop?: boolean;
}
interface UseCollectionReturn {
activeValue: Ref<string | null>;
setActiveValue: (value: string | null) => void;
setNext: (options?: CollectionNavigationOptions) => void;
setPrevious: (options?: CollectionNavigationOptions) => void;
setFirst: () => void;
setLast: () => void;
isItemDisabled: (value: string) => boolean;
}Details
useCollection satisfies the NavigableCollection shape expected by useListNavigation.
valuesis the ordered set of item values.- Values must be stable strings.
isValueDisabledmarks values that navigation should skip.setActiveValue()ignores unknown and disabled values.setNext()andsetPrevious()move through enabled values only.- Pass
{ loop: true }to wrap at the collection boundaries. - If the active value disappears from
valuesor becomes disabled, it is cleared.
For nested multi-level menus, each menu and submenu level creates its own useCollection and coordinates via useFloatingContext (parentContext).
Example
This menu uses useCollection for flat keyboard navigation.
vue
<script setup lang="ts">
import { computed, ref } from "vue";
import {
useClick,
useCollection,
useFloatingContext,
useListNavigation,
usePosition,
} from "v-float";
const anchorEl = ref<HTMLElement | null>(null);
const floatingEl = ref<HTMLElement | null>(null);
const items = [
{ value: "edit", label: "Edit" },
{ value: "duplicate", label: "Duplicate" },
{ value: "archive", label: "Archive", disabled: true },
];
const values = computed(() => items.map((item) => item.value));
const collection = useCollection({
values,
isValueDisabled: (value) => value === "archive",
});
const context = useFloatingContext({ anchorEl, floatingEl });
const { styles } = usePosition(context);
useClick(context);
useListNavigation(context, {
collection,
loop: true,
});
</script>
<template>
<button ref="anchorEl" type="button">Actions</button>
<div v-if="context.state.open.value" ref="floatingEl" role="menu" :style="styles">
<button
v-for="item in items"
:key="item.value"
type="button"
role="menuitem"
:disabled="collection.isItemDisabled(item.value)"
:class="{ active: collection.activeValue.value === item.value }"
>
{{ item.label }}
</button>
</div>
</template>