AsyncTreeMenu#

@palmyralabs/rt-forms · src/palmyra/menu/AsyncTreeMenu.tsx

Overview#

Sidebar tree menu. On mount it calls store.getRoot() once against a Wire TreeQueryStore to fetch the whole tree, converts the flat list into react-accessible-treeview nodes, and renders a checkbox-less expandable sidebar.

Selecting a leaf calls useNavigate() from react-router-dom with the leaf’s metadata.code (or metadata.target for non-leaf shortcut links). Expand state and the last selection are persisted to localStorage under the keys palmyra.rui.sidemenu.expanded and palmyra.rui.sidemenu.expanded.selected, so the menu restores itself on page refresh.

Icons are pluggable — pass an IconProvider to map per-leaf metadata.code values to React icons; omit it to get the SimpleIconProvider default.

Props — IAsyncTreeMenuInput#

interface IAsyncTreeMenuInput {
  store:         TreeQueryStore<IChildTreeRequest, any>;
  iconProvider?: IconProvider;
}

Node shape#

getRoot() is expected to return { result: MenuRow[] } where each row has:

{
  id:       number,
  parent:   number | null,       // null / -1 for roots
  name:     string,              // label displayed in the sidebar
  children: string,              // comma-separated child ids, e.g. "12,13,15"
  code:     string,              // navigation target for leaves (router path)
  action?:  string,
  target?:  string,              // navigation target for non-leaf shortcuts
}

children is a comma-separated string of ids — the component parses it into the tree on its own. isBranch is derived from whether children is non-empty.

Example#

import AsyncTreeMenu from '@palmyralabs/rt-forms/menu/AsyncTreeMenu';
import AppStoreFactory from '@/wire/StoreFactory';

export function Sidebar() {
  const store = AppStoreFactory.getTreeStore({}, '/menu');

  return (
    <aside className="sidebar">
      <AsyncTreeMenu store={store} />
    </aside>
  );
}

With a custom icon provider:

import type { IconProvider } from '@palmyralabs/rt-forms';
import { Users, Package, BarChart3 } from 'lucide-react';

const iconProvider: IconProvider = {
  getIcon(code: string) {
    switch (code) {
      case '/app/users':    return Users;
      case '/app/products': return Package;
      case '/app/reports':  return BarChart3;
      default:              return null;
    }
  }
};

<AsyncTreeMenu store={store} iconProvider={iconProvider} />

State persistence#

Clear the persisted state — e.g. after logout — by removing both keys:

localStorage.removeItem('palmyra.rui.sidemenu.expanded');
localStorage.removeItem('palmyra.rui.sidemenu.expanded.selected');