IconProvider#

@palmyralabs/rt-forms · src/palmyra/menu/IconProvider.ts

Overview#

Pluggable icon resolver consumed by AsyncTreeMenu. Given a menu node’s metadata.code (the same string that also acts as the router path), getIcon returns a React icon component — or undefined when nothing is mapped.

The file ships two symbols:

  • IconProvider — the interface (type-only export).
  • SimpleIconProvider — a default singleton backed by an empty map. Returns undefined for every lookup unless you patch the map, so nodes render without icons.

In practice you implement your own IconProvider and pass it to AsyncTreeMenu rather than extending the default.

Source#

interface IconProvider {
  getIcon: (name: string) => any;
}

const iconMap = {
  // add entries here
};

class IconProviderImpl implements IconProvider {
  getIcon = (name: string): any => iconMap[name];
}

const SimpleIconProvider = new IconProviderImpl();

export type { IconProvider };
export { SimpleIconProvider };

Interface#

interface IconProvider {
  getIcon(name: string): React.ComponentType | undefined;
}

name is the node’s metadata.code. The return type is declared any in the source but is expected to be a React component (class or function) so AsyncTreeMenu can render <Icon /> inline. Returning undefined / null is fine — the menu just skips the icon slot.

Example — app-specific provider#

Map your router paths to lucide-react icons (or any other icon library):

// src/menu/iconProvider.ts
import type { ComponentType } from 'react';
import { Users, Package, BarChart3, Settings, Home } from 'lucide-react';
import type { IconProvider } from '@palmyralabs/rt-forms';

const map: Record<string, ComponentType> = {
  '/app':           Home,
  '/app/users':     Users,
  '/app/products':  Package,
  '/app/reports':   BarChart3,
  '/app/settings':  Settings,
};

export const AppIconProvider: IconProvider = {
  getIcon: (name) => map[name],
};
import AsyncTreeMenu from '@palmyralabs/rt-forms/menu/AsyncTreeMenu';
import { AppIconProvider } from '@/menu/iconProvider';

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

Dynamic / data-driven icons#

For servers that return an icon hint per menu row (e.g. a Font Awesome name stored in the mst_menu table), resolve at lookup time instead of keying the map on code:

// given rows that carry metadata.iconName ...
import { FaUsers, FaBox, FaChartLine } from 'react-icons/fa';

const byName: Record<string, ComponentType> = {
  'users':    FaUsers,
  'box':      FaBox,
  'chart':    FaChartLine,
};

export const DynamicIconProvider: IconProvider = {
  // AsyncTreeMenu calls getIcon(metadata.code), so stash the icon name in metadata.code
  // — or swap out AsyncTreeMenu for a custom renderer if you need a different key.
  getIcon: (code) => byName[code],
};

If the lookup key you need isn’t metadata.code, fork AsyncTreeMenu’s nodeRenderer and call iconProvider.getIcon(element.metadata.iconName) directly.