Menu
Navigation with submenus, search, and a drawer for small screens.
Quick start
Add Menu1 in the Designer. Use the example in a page method.
Menu1.items = [
{ id: 'home', label: 'Home', icon: 'house' },
{ id: 'records', label: 'Records', icon: 'folder', children: [
{ id: 'customers', label: 'Customers', icon: 'users' },
] },
];
Menu1.selectedItemId = 'home';
Menu1.expandItem('records');
Menu1.updateItem('customers', { badge: '3' });- Open the configurator from the Menu in the Designer. Arrange items, submenus, icons, badges, brand, and user.
- Choose vertical or horizontal orientation. The menu floats; anchors and margins define its position.
- Below mobileBreakpoint, the menu uses a drawer. Configure drawerWidth, drawerSide, and closeOnSelect.
- page navigates to a page ID; href opens a link. Items with children and no destination expand their submenu.
- OnItemClick receives item, itemId, and path. OnItemToggle receives itemId and expanded; use it for submenus without an action.
- searchable enables search. searchMode = 'filter' filters items; 'event' retains items and emits OnSearch with detail.text.
- collapsed controls the compact version; open(), close(), and toggle() control opening. themeToggle displays the theme control.
More examples
Handle an action by item ID
import type { WebMenu, WebMenuItemEvent } from '@webrad/runtime';
// Link this method to OnItemClick in Interaction.
class Example {
public async Menu1ItemClick(
sender: WebMenu,
event: CustomEvent<WebMenuItemEvent>,
): Promise<void> {
if (event.detail.itemId !== 'sign-out') return;
event.preventDefault(); // Cancel navigation before await.
const result = await RestGO.logout();
if (result.error) { Message(result.error.message); return; }
Navigate('PageLogin');
}
}Search and responsive configuration
Menu1.configuration = {
...Menu1.configuration,
searchable: true,
searchMode: 'event',
mobileBreakpoint: 768,
drawerWidth: 288,
drawerSide: 'left',
closeOnSelect: true,
};
// OnSearch receives event.detail.text; update data in your logic.
Menu1.brand = { ...Menu1.brand, caption: 'My application' };
Menu1.user = { ...Menu1.user, visible: true, name: 'Ana', status: 'online' };
Menu1.themeToggle = true;Inspector properties
Component options and defaults. Fields without runtime access are configured in the Inspector.
| Property / source | Type and options | Default | Runtime access |
|---|---|---|---|
nameName · Identity | string | — | name |
xLeft · Size & position | number | 0 | x |
yTop · Size & position | number | 0 | y |
widthWidth · Size & position | number | 288 | width |
heightHeight · Size & position | number | 640 | height |
itemWidthResponsive width · Responsive sizeFit content grows the item to its widest rendered child and preserves child intrinsic widths; minimum and maximum width remain limits. | enumfixed stretch fit-content | "fixed" | Inspector / configuration |
itemHeightResponsive height · Responsive sizeFit content grows the item to its lowest rendered child and preserves child intrinsic heights; minimum and maximum height remain limits. | enumfixed stretch fit-content | "fixed" | Inspector / configuration |
itemMinWidthMinimum width · Responsive size | number | 0 | Inspector / configuration |
itemMaxWidthMaximum width · Responsive size | number | null | Inspector / configuration |
itemMinHeightMinimum height · Responsive size | number | 0 | Inspector / configuration |
itemMaxHeightMaximum height · Responsive size | number | null | Inspector / configuration |
itemGrowGrow · Responsive size | number | 0 | Inspector / configuration |
itemShrinkShrink · Responsive sizeAllows fixed and stretch flow items to yield space. Fit-content keeps its intrinsic size so its children are not compressed. | boolean | true | Inspector / configuration |
itemAlignSelfItem alignment · Responsive sizeCross-axis alignment for this item in a row or column. Auto inherits the parent alignment; responsive cross-axis stretch retains its sizing behavior. | enumauto start center end stretch | "auto" | Inspector / configuration |
itemHorizontalAnchorHorizontal anchor · Anchors & offsets | enumstart center end stretch | "start" | Inspector / configuration |
itemVerticalAnchorVertical anchor · Anchors & offsets | enumstart center end stretch | "start" | Inspector / configuration |
itemLeftLeft inset · Anchors & offsets | number | 0 | Inspector / configuration |
itemRightRight inset · Anchors & offsets | number | 0 | Inspector / configuration |
itemTopTop inset · Anchors & offsets | number | 0 | Inspector / configuration |
itemBottomBottom inset · Anchors & offsets | number | 0 | Inspector / configuration |
marginMargin · SpacingOuter spacing on all four sides, in pixels. Row and column layouts include this spacing between components. | number | 0 | margin |
marginTopMargin top · SpacingOuter spacing on this side, in pixels. Overrides the shared Margin value. | number | 0Derived from saved properties | marginTop |
marginRightMargin right · SpacingOuter spacing on this side, in pixels. Overrides the shared Margin value. | number | 0Derived from saved properties | marginRight |
marginBottomMargin bottom · SpacingOuter spacing on this side, in pixels. Overrides the shared Margin value. | number | 0Derived from saved properties | marginBottom |
marginLeftMargin left · SpacingOuter spacing on this side, in pixels. Overrides the shared Margin value. | number | 0Derived from saved properties | marginLeft |
visibleVisible · Behavior | boolean | true | visible |
enabledEnabled · Behavior | boolean | true | enabled |
menuDefinitionIdMenu definition · MenuShared configuration and TypeScript; runtime state remains independent for each placement. | menu-definition | — | Inspector / configuration |
editMenuDefinitionEdit shared menu · Menu | designer-actionEditor: menu-definition-edit | — | Inspector / configuration |
configurationMenu configuration · MenuEdit orientation, menu items, brand, user, appearance and the optional Light/Dark theme switch. Responsive placement is automatic. | menu-configEditor: menu-configurator | {"schemaVersion":1,"orientation":"vertical","accessibleLabel":"Main navigation","collapsible":true,"collapsed":false,"accordion":false,"searchable":false,"themeToggle":true,"searchMode":"filter","searchPlaceholder":"Search menu…","emptyText":"No items found","footerText":"","selectedItemId":"","mobileBreakpoint":768,"mobileIconPosition":"left","drawerWidth":288,"drawerSide":"left","closeOnSelect":true,"brand":{"visible":true,"caption":"Workspace","subtitle":"Your application","logo":"","logoSize":36,"page":"","href":""},"user":{"visible":false,"bindToCurrentUser":false,"settings":false,"settingsLanguage":"en","emailConfirmationUrl":"","passwordResetUrl":"","name":"","description":"","avatar":"","status":"none"},"appearance":{"background":"var(--wr-theme-surface)","foreground":"var(--wr-theme-heading)","muted":"var(--wr-theme-text-muted)","border":"var(--wr-theme-border)","hoverBackground":"var(--wr-theme-disabled-surface)","activeBackground":"var(--wr-theme-surface-muted)","activeForeground":"var(--wr-theme-primary)","accent":"var(--wr-theme-primary)","badgeBackground":"var(--wr-theme-surface-muted)","badgeForeground":"var(--wr-theme-primary)","itemHeight":44,"itemRadius":10,"itemGap":4,"iconSize":20,"fontSize":14,"padding":14,"indent":18,"collapsedWidth":72},"items":[{"id":"home","label":"Home","icon":"house"},{"id":"workspace","label":"Workspace","icon":"layers","expanded":true,"children":[{"id":"projects","label":"Projects","icon":"folder"},{"id":"team","label":"Team","icon":"users"}]},{"id":"settings","label":"Settings","icon":"settings"}]} | configuration |
placementPlacement · | enumin-flow floating | "floating" | placementRead-only in the Inspector |
floatingHorizontalAnchorHorizontal side · Menu placement | enumleft center right | "left" | floatingHorizontalAnchor |
floatingVerticalAnchorVertical side · Menu placement | enumtop center bottom | "top" | floatingVerticalAnchor |
floatingZIndexLayer · | number | 1000 | floatingZIndexRead-only in the Inspector |
currentRowBindingsCurrent row · DataBind writable visual values to fields of this repeated row. | current-row-bindingsEditor: current-row-bindings | [] | currentRowBindings |
API in code
| Member | Signature | Access |
|---|---|---|
floatingZIndex | get floatingZIndex(): number; | Read-only |
margin | get margin(): number; | Read and write |
marginTop | get marginTop(): number; | Read and write |
marginRight | get marginRight(): number; | Read and write |
marginBottom | get marginBottom(): number; | Read and write |
marginLeft | get marginLeft(): number; | Read and write |
placement | get placement(): 'floating'; | Read-only |
floatingHorizontalAnchor | get floatingHorizontalAnchor(): 'left' | 'center' | 'right'; | Read and write |
floatingVerticalAnchor | get floatingVerticalAnchor(): 'top' | 'center' | 'bottom'; | Read and write |
configuration | get configuration(): WebMenuConfiguration; | Read and write |
enabled | get enabled(): boolean; | Read and write |
items | get items(): readonly WebMenuItem[]; | Read and write |
orientation | get orientation(): WebMenuConfiguration['orientation']; | Read and write |
brand | get brand(): WebMenuConfiguration['brand']; | Read and write |
user | get user(): WebMenuConfiguration['user']; | Read and write |
appearance | get appearance(): WebMenuConfiguration['appearance']; | Read and write |
selectedItemId | get selectedItemId(): string; | Read and write |
collapsed | get collapsed(): boolean; | Read and write |
isOpen | get isOpen(): boolean; | Read-only |
isMobile | get isMobile(): boolean; | Read-only |
searchMode | get searchMode(): WebMenuConfiguration['searchMode']; | Read and write |
themeToggle | get themeToggle(): boolean; | Read and write |
searchText | get searchText(): string; | Read and write |
Inherited members (19)
| Member | Signature | Access |
|---|---|---|
currentRowBindings | currentRowBindings: readonly WebCurrentRowBinding[]; | Read and write |
element | readonly element: TElement; | Read-only |
id | readonly id: string; | Read-only |
name | readonly name: string; | Read-only |
visible | visible: boolean; | Read and write |
x | x: number; | Read and write |
y | y: number; | Read and write |
width | width: number; | Read and write |
height | height: number; | Read and write |
alignSelf | alignSelf: RuntimeLayoutAlignSelf; | Read and write |
tabIndex | tabIndex: number; | Read and write |
backgroundColor | backgroundColor: string; | Read and write |
color | color: string; | Read and write |
borderColor | borderColor: string; | Read and write |
Events
Link methods in Interaction. How to use events.
OnSearch
WebMenuSearchEvent in event.detail · text
Emitted when the user presses Enter in event search mode. Typing and clearing do not emit; menu items remain unchanged.
Signature and example
import type { WebMenu, WebMenuSearchEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1Search(
sender: WebMenu,
event: CustomEvent<WebMenuSearchEvent>,
): Promise<void> {
console.log(event.detail);
const { text } = event.detail;
console.log(text);
}
}OnItemClick
WebMenuItemEvent in event.detail · item, itemId, path
Call event.preventDefault() synchronously to cancel automatic selection and navigation.
Signature and example
import type { WebMenu, WebMenuItemEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1ItemClick(
sender: WebMenu,
event: CustomEvent<WebMenuItemEvent>,
): Promise<void> {
console.log(event.detail);
const { item, itemId, path } = event.detail;
console.log(item, itemId, path);
}
}OnSelectionChange
WebMenuItemEvent in event.detail · item, itemId
Signature and example
import type { WebMenu, WebMenuItemEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1SelectionChange(
sender: WebMenu,
event: CustomEvent<WebMenuItemEvent>,
): Promise<void> {
console.log(event.detail);
const { item, itemId } = event.detail;
console.log(item, itemId);
}
}OnItemToggle
WebMenuItemToggleEvent in event.detail · itemId, expanded
Signature and example
import type { WebMenu, WebMenuItemToggleEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1ItemToggle(
sender: WebMenu,
event: CustomEvent<WebMenuItemToggleEvent>,
): Promise<void> {
console.log(event.detail);
const { itemId, expanded } = event.detail;
console.log(itemId, expanded);
}
}OnCollapseChange
WebMenuStateEvent in event.detail · collapsed
Signature and example
import type { WebMenu, WebMenuStateEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1CollapseChange(
sender: WebMenu,
event: CustomEvent<WebMenuStateEvent>,
): Promise<void> {
console.log(event.detail);
const { collapsed } = event.detail;
console.log(collapsed);
}
}OnOpen
WebMenuStateEvent in event.detail
Signature and example
import type { WebMenu, WebMenuStateEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1Open(
sender: WebMenu,
event: CustomEvent<WebMenuStateEvent>,
): Promise<void> {
console.log(event.detail);
}
}OnClose
WebMenuStateEvent in event.detail
Signature and example
import type { WebMenu, WebMenuStateEvent } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1Close(
sender: WebMenu,
event: CustomEvent<WebMenuStateEvent>,
): Promise<void> {
console.log(event.detail);
}
}OnBrandClick
WebMenuBrand in event.detail
Signature and example
import type { WebMenu, WebMenuBrand } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1BrandClick(
sender: WebMenu,
event: CustomEvent<WebMenuBrand>,
): Promise<void> {
console.log(event.detail);
}
}OnUserClick
WebMenuUser in event.detail
Signature and example
import type { WebMenu, WebMenuUser } from '@webrad/runtime';
// Copy this method into the page class and link it in Interaction.
class Example {
public async Menu1UserClick(
sender: WebMenu,
event: CustomEvent<WebMenuUser>,
): Promise<void> {
console.log(event.detail);
}
}