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 behaviour — 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>