AsyncTreeMenuEditor#
@palmyralabs/rt-forms · src/palmyra/menu/AsyncTreeMenuEditor.tsx
Overview#
Admin-side counterpart to AsyncTreeMenu. Renders the same tree with checkbox selection so an administrator can edit which menu entries a given role/group can access.
Internally it builds its own TreeQueryStore from a factory (storeFactory.getTreeStore), seeded with endPointOptions: { groupId }, so the backend returns each node pre-marked with the group’s current mask value. On mount, nodes with mask == 2 are selected by default; descendants inherit via propagateSelect.
The getValue() imperative method walks the current selection and returns the tree in a normalised shape ready to POST back to the permission endpoint.
Props — IAsyncTreeEditorInput#
interface IAsyncTreeEditorInput {
storeFactory: StoreFactory<IChildTreeRequest, StoreOptions>;
endPoint: IEndPoint;
groupId: number;
readOnly?: boolean; // disables click-to-toggle (still renders the tree)
fineGrained?: boolean; // hides the placeholder "crud - under construction" per-leaf control
}Ref — IAsyncTreeEditor#
interface IAsyncTreeEditor {
getValue(): Node;
}
interface Node {
id: number;
parent: number;
name: string;
loaded: boolean;
isBranch: true;
children: (Node | number)[];
selected: 0 | 1 | 2; // 0 = none, 1 = half / partial, 2 = all
}getValue() returns the tree rooted at a synthetic node { id: -1, name: 'root', children: [...] }. Each child carries menuId, parent, name, mask (0 / 1 / 2), menuCode, and its own children array — the shape the backend’s ACL-save endpoint expects.
Example — edit and save a group’s menu access#
import { useRef } from 'react';
import { AsyncTreeMenuEditor, type IAsyncTreeEditor } from '@palmyralabs/rt-forms';
import { Button, Group } from '@mantine/core';
import AppStoreFactory from '@/wire/StoreFactory';
export function GroupMenuAccessEditor({ groupId, onSaved }: {
groupId: number;
onSaved: () => void;
}) {
const editorRef = useRef<IAsyncTreeEditor>(null);
const save = async () => {
const tree = editorRef.current?.getValue();
await fetch(`/api/palmyra/group/${groupId}/menu`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(tree),
});
onSaved();
};
return (
<>
<AsyncTreeMenuEditor
ref={editorRef}
storeFactory={AppStoreFactory}
endPoint="/menu"
groupId={groupId}
/>
<Group justify="flex-end" mt="md">
<Button onClick={save}>Save access</Button>
</Group>
</>
);
}Read-only variant#
Pass readOnly to freeze the current selection — useful for audit/review screens where an administrator looks but can’t change:
<AsyncTreeMenuEditor
storeFactory={AppStoreFactory}
endPoint="/menu"
groupId={groupId}
readOnly
/>Caveats#
- The component expects
getRoot()to return rows with amaskcolumn (0/1/2); without it every node renders unselected. - The per-leaf CRUD control is currently a placeholder (
"crud - under construction") —fineGrained={true}hides it. Plan your UX around coarse-grained selection for now.