Skip to content

Menu pane

A container that lets you drop a full interactive component (a Color picker, a slider or a small form) into a Menu, a Menu flyout submenu or a Context menu.

A plain Menu runs a roving focus zone: arrow keys move between its items and only one item sits in the tab order at a time. Wrapping custom content in a Menu pane opts that subtree out of the focus zone, so the embedded component keeps its own keyboard behavior. Arrow keys drive its sliders and steppers and Tab reaches every control. Arrow navigation across the surrounding menu items simply skips over the pane; reach it with Tab.

Clicking inside a pane never closes the menu, so the embedded component stays usable while the menu is open.

Slots

default
The custom content to embed in the menu, such as a color picker, slider or small form.

Examples

Color picker

Embed a full color picker in a menu flyout submenu. Drag the saturation field, use the sliders and type into the inputs without the menu interfering.

<template>
    <FluxPane style="width: 240px">
        <FluxMenu>
            <FluxMenuGroup>
                <FluxMenuItem
                    icon-leading="bold"
                    label="Bold"/>

                <FluxMenuItem
                    icon-leading="italic"
                    label="Italic"/>

                <FluxMenuFlyout
                    icon="palette"
                    label="Text color">
                    <FluxMenu>
                        <FluxMenuPane>
                            <FluxColorPicker v-model="color"/>
                        </FluxMenuPane>
                    </FluxMenu>
                </FluxMenuFlyout>
            </FluxMenuGroup>
        </FluxMenu>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxColorPicker, FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxMenuPane, FluxPane } from '@flux-ui/components';
    import { ref } from 'vue';

    const color = ref('#22c55e');
</script>

Slider

A single control such as a slider. Focus it with Tab, then the arrow keys adjust the value instead of moving between menu items.

<template>
    <FluxPane style="width: 240px">
        <FluxMenu>
            <FluxMenuGroup>
                <FluxMenuItem
                    icon-leading="moon"
                    label="Night shift"/>

                <FluxMenuFlyout
                    icon="sun"
                    label="Brightness">
                    <FluxMenu>
                        <FluxMenuPane>
                            <FluxFormSlider
                                v-model="brightness"
                                aria-label="Brightness"
                                is-ticks-visible/>
                        </FluxMenuPane>
                    </FluxMenu>
                </FluxMenuFlyout>
            </FluxMenuGroup>
        </FluxMenu>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxFormSlider, FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxMenuPane, FluxPane } from '@flux-ui/components';
    import { ref } from 'vue';

    const brightness = ref(60);
</script>

Range slider

A two-thumb range. Each thumb is its own focus stop and the arrow keys move it without disturbing the menu.

<template>
    <FluxPane style="width: 240px">
        <FluxMenu>
            <FluxMenuGroup>
                <FluxMenuItem
                    icon-leading="arrow-down-a-z"
                    label="Sort"/>

                <FluxMenuFlyout
                    icon="money-bill"
                    label="Price range">
                    <FluxMenu>
                        <FluxMenuPane>
                            <FluxFormRangeSlider
                                v-model="price"
                                is-ticks-visible
                                :max="1000"
                                :min="0"
                                :step="50"/>
                        </FluxMenuPane>
                    </FluxMenu>
                </FluxMenuFlyout>
            </FluxMenuGroup>
        </FluxMenu>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxFormRangeSlider, FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxMenuPane, FluxPane } from '@flux-ui/components';
    import { ref } from 'vue';

    const price = ref<[number, number]>([200, 800]);
</script>

Text area

A multi-line input. Typing, Enter and the arrow keys for the caret all stay inside the field.

<template>
    <FluxPane style="width: 240px">
        <FluxMenu>
            <FluxMenuGroup>
                <FluxMenuItem
                    icon-leading="star"
                    label="Add to favorites"/>

                <FluxMenuFlyout
                    icon="message"
                    label="Add note">
                    <FluxMenu>
                        <FluxMenuPane>
                            <FluxFormTextArea
                                v-model="note"
                                placeholder="Write a note..."
                                :rows="3"/>
                        </FluxMenuPane>
                    </FluxMenu>
                </FluxMenuFlyout>
            </FluxMenuGroup>
        </FluxMenu>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxFormTextArea, FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxMenuPane, FluxPane } from '@flux-ui/components';
    import { ref } from 'vue';

    const note = ref('');
</script>

Filter

A search field that filters the items below it. Typing (including the arrow keys for the caret) stays inside the input.

<template>
    <FluxPane style="width: 240px">
        <FluxMenu>
            <FluxMenuGroup>
                <FluxMenuItem
                    icon-leading="plus"
                    label="New file"/>

                <FluxMenuFlyout
                    icon="filter"
                    label="Actions">
                    <FluxMenu>
                        <FluxMenuPane>
                            <FluxFormInput
                                v-model="query"
                                type="search"
                                icon-leading="magnifying-glass"
                                placeholder="Filter actions..."/>
                        </FluxMenuPane>

                        <FluxMenuGroup>
                            <FluxMenuItem
                                v-for="action of filtered"
                                :key="action.label"
                                :icon-leading="action.icon"
                                :label="action.label"/>

                            <FluxMenuSubHeader
                                v-if="filtered.length === 0"
                                label="No matches"/>
                        </FluxMenuGroup>
                    </FluxMenu>
                </FluxMenuFlyout>
            </FluxMenuGroup>
        </FluxMenu>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import type { FluxIconName } from '@flux-ui/types';
    import { FluxFormInput, FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxMenuPane, FluxMenuSubHeader, FluxPane } from '@flux-ui/components';
    import { computed, ref } from 'vue';

    const actions: { readonly icon: FluxIconName; readonly label: string; }[] = [
        {icon: 'scissors', label: 'Cut'},
        {icon: 'copy', label: 'Copy'},
        {icon: 'paste', label: 'Paste'},
        {icon: 'clone', label: 'Duplicate'},
        {icon: 'pen', label: 'Rename'},
        {icon: 'trash', label: 'Delete'}
    ];

    const query = ref('');
    const filtered = computed(() => actions.filter(action => action.label.toLowerCase().includes(query.value.toLowerCase())));
</script>

Settings

Several controls in one pane. Tab reaches every toggle, and toggling one never closes the menu.

<template>
    <FluxPane style="width: 240px">
        <FluxMenu>
            <FluxMenuGroup>
                <FluxMenuItem
                    icon-leading="user"
                    label="Account"/>

                <FluxMenuFlyout
                    icon="bell"
                    label="Notifications">
                    <FluxMenu>
                        <FluxMenuPane>
                            <label style="display: flex; align-items: center; justify-content: space-between; gap: 12px">
                                Email
                                <FluxToggle v-model="email"/>
                            </label>

                            <label style="display: flex; align-items: center; justify-content: space-between; gap: 12px">
                                Push
                                <FluxToggle v-model="push"/>
                            </label>
                        </FluxMenuPane>
                    </FluxMenu>
                </FluxMenuFlyout>
            </FluxMenuGroup>
        </FluxMenu>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxMenu, FluxMenuFlyout, FluxMenuGroup, FluxMenuItem, FluxMenuPane, FluxPane, FluxToggle } from '@flux-ui/components';
    import { ref } from 'vue';

    const email = ref(true);
    const push = ref(false);
</script>