import { AppMenuFavoriteButton } from "./AppMenuFavoriteButton.js"; import type { AppMenuLink, AppMenuLinkGroup } from "./types.js"; import { useRovingTabIndex } from "./useRovingTabIndex.js"; export type AppMenuListProps = { groups: AppMenuLinkGroup[]; /** Total number of links across all groups; the roving tab index size. */ itemCount: number; /** Called when an entry that doesn't opt out with `keepOpen` is activated. */ onClose: () => void; /** Without a handler no favorite toggles are rendered. */ onToggleFavorite?: (id: string, favorite: boolean) => void; }; /** * The app menu's right hand pane: the selected category's links, laid out as * titled groups of wrapping columns. * * The whole list is a single tab stop; the arrow keys move focus between the * links, following the visual grid. */ export function AppMenuList({ groups, itemCount, onClose, onToggleFavorite }: AppMenuListProps) { let roving = useRovingTabIndex(itemCount, { columns: true }); // Links are numbered across all groups, since they share one roving index. let itemIndex = -1; function renderItem(item: AppMenuLink, index: number) { let props = { className: "ob-app-menu__link", ...item.attributes, ...roving.getItemProps(index), }; function activate() { item.onClick?.(); if (!item.keepOpen) onClose(); } let link = item.href ? ( {item.label} ) : ( ); // Only app entries carry an id and a favorite state; the fixed category // links (e.g. the developer tools) can't be favorited. if (!onToggleFavorite || item.id === undefined || typeof item.isFavorite !== "boolean") return link; let id = item.id; let isFavorite = item.isFavorite; return ( <> {link} onToggleFavorite(id, !isFavorite)} {...roving.getSecondaryProps(index)} /> ); } return (
{groups.map((group, groupIdx) => (
{group.title &&

{group.title}

}
    {group.items.map((item) => { itemIndex += 1; return (
  • {renderItem(item, itemIndex)}
  • ); })}
))}
); }