useFloatingMenu
Implements the useFloating()
composable from FloatingUI for positioning Menu overlays by taking in two refs: one for the reference element and one for the Menu, plus middleware to track and update visible changes to the Menu.
Updates the value of these CSS properties for the Menu: visibility
, position
, top
, right
, left
, and transform
.
This composable will:
- Ensure the menu is always visually attached to its triggering element
- Ensure the menu is always the same width as its triggering element
Usage
useFloatingMenu()
takes in two arguments:
- The template ref of the element that the menu is visually attached to
- The ref of the menu which is an instance of CdxMenu
<template>
<div ref="handle">
<cdx-menu ref="menu" />
</div>
</template>
const handle = ref(null);
const menu = ref(null);
useFloatingMenu( handle, menu );
Full example
In this example, the component is a TextInput with a Menu. The composable absolutely positions the Menu to appear attached to the TextInput and share the same width.
When the Menu is not expanded, it's hidden using visibility: hidden;
.
When the Menu is expanded, the Menu and its children components, MenuItems, are visible. Floating UI's middleware hides the expanded Menu when the user has scrolled the TextInput out of view. This is because the Menu is relative to the TextInput.
<template>
<div class="cdx-docs-input-with-menu">
<cdx-text-input
ref="textInput"
v-model="selectedValue"
class="cdx-docs-input-with-menu__input"
role="combobox"
:aria-expanded="expanded"
@click="onClick"
@blur="expanded = false"
@keydown="onKeydown"
/>
<cdx-menu
ref="menu"
v-model:selected="selectedValue"
v-model:expanded="expanded"
:menu-items="menuItems"
:visible-item-limit="visibleItemLimit || null"
/>
<label for="cdx-menu-limit-items-input">
Number of visible items in Menu (empty or 0 for show all):
</label>
<input
id="cdx-menu-limit-items-input"
v-model="visibleItemLimit"
type="number"
>
</div>
</template>
<script lang="ts" setup>
import { ComponentPublicInstance, Ref, ref } from 'vue';
import { CdxMenu, CdxTextInput } from '@wikimedia/codex';
// import needed because useFloatingMenu is not being exported in lib yet
import useFloatingMenu from '../composables/useFloatingMenu';
const textInput = ref(null);
const menu = ref(null);
useFloatingMenu( textInput as Ref<ComponentPublicInstance>, menu );
const selectedValue = ref( '' );
const expanded = ref( false );
const menuItems = [
{ label: 'One', value: '1' },
{ label: 'Two', value: '2', disabled: true },
{ label: 'Three', value: '3' },
{ label: 'Four', value: '4' }
];
function onKeydown( event ) {
// Delegate key events to the menu
menu.value?.delegateKeyNavigation( event );
}
function onClick() {
expanded.value = true;
}
const visibleItemLimit = ref( '' );
</script>