ActionMenu
a list of actions that opens from a button, with separators between groups.
usage
example
code
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
placement="right-start"
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
placement="right-start"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
placement="right-start"
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
spacious
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
spacious
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
spacious
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="none"
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
border="none"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="none"
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="single"
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
border="single"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="single"
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="rounded"
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
border="rounded"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="rounded"
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="heavy"
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
border="heavy"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="heavy"
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
import { ActionMenu, Button } from 'charcell/react';
export function ActionMenuExample() {
return (
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="double"
trigger={({ triggerProps }) => (
<Button {...triggerProps} label="turret" />
)}
/>
);
}▴
▾
<script setup lang="ts">
import { ActionMenu, Button } from 'charcell/vue';
</script>
<template>
<ActionMenu
:items="[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]"
border="double"
>
<template #trigger="{ triggerProps }">
<Button v-bind="triggerProps" label="turret" />
</template>
</ActionMenu>
</template>▴
▾
<script lang="ts">
import { ActionMenu, Button } from 'charcell/svelte';
</script>
<ActionMenu
items={[
{ value: 'aim', label: 'aim' },
{ value: 'fire', label: 'fire' },
{ value: 'break', separator: true },
{ value: 'raise', label: 'raise', disabled: true },
{ value: 'lower', label: 'lower' },
]}
border="double"
>
{#snippet trigger({ triggerProps })}
<Button {...triggerProps} label="turret" />
{/snippet}
</ActionMenu>▴
▾
<!-- component: ActionMenuDriver (behavior not included) --><button
class="CharcellButton CharcellButton--boxed CharcellButton--interactive"
type="button"
>
turret</button
><!-- /component -->▴
▾
as text
┌────────┐ │ turret │ ├────────┴╖ │❯aim ║ │ fire ║ │╶───────╴║ │ raise ║ │ lower ║ ╘═════════╝
┌────────┐ │ turret │ ├────────┴╖ │❯aim ║ │ fire ║ │╶───────╴║ │ raise ║ │ lower ║ ╘═════════╝
┌────────┐ │ turret │ ├────────┴╖ │❯aim ║ │ fire ║ │╶───────╴║ │ raise ║ │ lower ║ ╘═════════╝
┌────────┐ │❯turret │ ├────────┴╖ │❯aim ║ │ fire ║ │╶───────╴║ │ raise ║ │ lower ║ ╘═════════╝
┌────────┐┌───────╖ │ turret ││❯aim ║ └────────┘│ fire ║ │╶─────╴║ │ raise ║ │ lower ║ ╘═══════╝
┌────────┐┌───────╖ │ turret ││❯aim ║ └────────┘│ fire ║ │╶─────╴║ │ raise ║ │ lower ║ ╘═══════╝
┌────────┐┌───────╖ │ turret ││❯aim ║ └────────┘│ fire ║ │╶─────╴║ │ raise ║ │ lower ║ ╘═══════╝
┌────────┐┌───────╖ │❯turret ││❯aim ║ └────────┘│ fire ║ │╶─────╴║ │ raise ║ │ lower ║ ╘═══════╝
┌────────┐ │ turret │ ├────────┴╖ │❯aim ║ │ ║ │ fire ║ │ ║ │╶───────╴║ │ raise ║ │ ║ │ lower ║ │ ║ ╘═════════╝
┌────────┐ │ turret │ ├────────┴╖ │❯aim ║ │ ║ │ fire ║ │ ║ │╶───────╴║ │ raise ║ │ ║ │ lower ║ │ ║ ╘═════════╝
┌────────┐ │ turret │ ├────────┴╖ │❯aim ║ │ ║ │ fire ║ │ ║ │╶───────╴║ │ raise ║ │ ║ │ lower ║ │ ║ ╘═════════╝
┌────────┐ │❯turret │ ├────────┴╖ │❯aim ║ │ ║ │ fire ║ │ ║ │╶───────╴║ │ raise ║ │ ║ │ lower ║ │ ║ ╘═════════╝
┌────────┐ │ turret │ └────────┘ ❯aim fire ╶─────╴ raise lower
┌────────┐ │ turret │ └────────┘ ❯aim fire ╶─────╴ raise lower
┌────────┐ │ turret │ └────────┘ ❯aim fire ╶─────╴ raise lower
┌────────┐ │❯turret │ └────────┘ ❯aim fire ╶─────╴ raise lower
┌────────┐ │ turret │ ├────────┴┐ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ └─────────┘
┌────────┐ │ turret │ ├────────┴┐ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ └─────────┘
┌────────┐ │ turret │ ├────────┴┐ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ └─────────┘
┌────────┐ │❯turret │ ├────────┴┐ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ └─────────┘
┌────────┐ │ turret │ ├────────┴╮ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ ╰─────────╯
┌────────┐ │ turret │ ├────────┴╮ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ ╰─────────╯
┌────────┐ │ turret │ ├────────┴╮ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ ╰─────────╯
┌────────┐ │❯turret │ ├────────┴╮ │❯aim │ │ fire │ │╶───────╴│ │ raise │ │ lower │ ╰─────────╯
┌────────┐ │ turret │ └────────┘ ┏━━━━━━━┓ ┃❯aim ┃ ┃ fire ┃ ┃╶─────╴┃ ┃ raise ┃ ┃ lower ┃ ┗━━━━━━━┛
┌────────┐ │ turret │ └────────┘ ┏━━━━━━━┓ ┃❯aim ┃ ┃ fire ┃ ┃╶─────╴┃ ┃ raise ┃ ┃ lower ┃ ┗━━━━━━━┛
┌────────┐ │ turret │ └────────┘ ┏━━━━━━━┓ ┃❯aim ┃ ┃ fire ┃ ┃╶─────╴┃ ┃ raise ┃ ┃ lower ┃ ┗━━━━━━━┛
┌────────┐ │❯turret │ └────────┘ ┏━━━━━━━┓ ┃❯aim ┃ ┃ fire ┃ ┃╶─────╴┃ ┃ raise ┃ ┃ lower ┃ ┗━━━━━━━┛
┌────────┐ │ turret │ └────────┘ ╔═══════╗ ║❯aim ║ ║ fire ║ ║╶─────╴║ ║ raise ║ ║ lower ║ ╚═══════╝
┌────────┐ │ turret │ └────────┘ ╔═══════╗ ║❯aim ║ ║ fire ║ ║╶─────╴║ ║ raise ║ ║ lower ║ ╚═══════╝
┌────────┐ │ turret │ └────────┘ ╔═══════╗ ║❯aim ║ ║ fire ║ ║╶─────╴║ ║ raise ║ ║ lower ║ ╚═══════╝
┌────────┐ │❯turret │ └────────┘ ╔═══════╗ ║❯aim ║ ║ fire ║ ║╶─────╴║ ║ raise ║ ║ lower ║ ╚═══════╝
props
ActionMenu
- direction'ltr' | 'rtl'
- The reading direction its keys and placement follow (
'rtl'mirrors the arrow keys); the page's, once mounted, when unset. - itemsActionMenuItem[]
- The actions, in order:
{ value, label, disabled }, or{ value, separator: true }for a break between groups. - onSelect(value: string) => void
- Called with the chosen item's value.
- labelstring
- The list's accessible name, when its trigger does not say enough.
- widthnumber
- The menu's width in whole cells, its ring included; it fits its content when unset.
- minWidthnumber
- The menu's least width in whole cells, its ring included: it grows past it with its content.
- attachedboolean= true
- Whether the list grows out of a boxed trigger, sharing its last row of stroke; it does when it opens at a start placement (
'bottom-start','top-start'), as placed once it fits the screen, and its left side, and the side it shares (its top below, its bottom above), are single or rounded, as a boxed Button's are.falseholds it a row apart, for a trigger with no ring. - openboolean
- Controlled open state; pair it with
onOpenChange. - binds with
v-model:openin vue,bind:openin svelte - defaultOpenboolean
- Uncontrolled initial open state.
- onOpenChange(open: boolean) => void
- Called with the next open state whenever the menu opens or closes.
- spaciousboolean
- Gives each item two rows, its text in the first: a 32px target, which meets WCAG 2.5.8. One-row items miss it.
- closeOnSelectboolean= true
- Whether choosing an item closes the menu.
- loopFocusboolean
- Whether the highlight wraps from the last item to the first.
- placementPlacement
- Where the list opens against its trigger, as a Zag placement (
'bottom-start'when unset). - border'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of every side of the list's ring:
single,rounded,heavy,doubleornone. - borderX'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the list's ring's start and end sides, over
border. - borderY'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the list's ring's top and bottom sides, over
border. - borderTop'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the list's ring's top side, over
borderYandborder. - borderEnd'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the list's ring's end side, over
borderXandborder;doublewhen none of them is set, raising the list. - borderBottom'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the list's ring's bottom side, over
borderYandborder;doublewhen none of them is set, raising the list. - borderStart'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the list's ring's start side, over
borderXandborder.
ActionMenuList
- attachedboolean
- Fuses the window to a boxed trigger: it moves one row onto it and shares its stroke.
- spaciousboolean
- Each item takes two rows, its text in the first: a 32px target, which meets WCAG 2.5.8.
- aboveboolean
- Opened above the trigger, so an attached window shares the trigger's top stroke.
- widthnumber
- The window's width in whole cells, its ring included; it fits its content when unset, and never outgrows the space it has.
- minWidthnumber
- The window's least width in whole cells, its ring included: it grows past it with its content.
- border'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of every side of the ring:
single,rounded,heavy,doubleornone. - borderX'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the ring's start and end sides, over
border. - borderY'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the ring's top and bottom sides, over
border. - borderTop'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the ring's top side, over
borderYandborder. - borderEnd'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the ring's end side, over
borderXandborder. - borderBottom'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the ring's bottom side, over
borderYandborder. - borderStart'none' | 'single' | 'rounded' | 'heavy' | 'double'
- The style of the ring's start side, over
borderXandborder.
it also takes any other attribute of its <div>.
slots
in react, the default slot is children and each named slot a prop of its name, a function where the slot hands values; in vue, a slot; in svelte, a snippet.
ActionMenu
- trigger{ triggerProps: Record<string, unknown> }its values required
- What opens the menu, typically a
Button: spreadtriggerPropsonto it.
ActionMenuList
- default
▴
▾