app-react: keep nav rest results in sync

Make nav rest results stay live when additional items are taken later in the same render, which lets app nav layouts place specific items after collecting the remaining sidebar entries.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-17 16:20:22 +01:00
parent f7777a6ec8
commit 5f3f5d298b
5 changed files with 162 additions and 28 deletions
+35 -8
View File
@@ -41,6 +41,7 @@ class NavItemBag implements NavContentNavItems {
readonly #items: NavContentNavItem[];
readonly #index: Map<string, NavContentNavItem>;
readonly #taken: Set<string>;
#restItems: NavContentNavItem[] | undefined;
constructor(items: NavContentNavItem[], taken?: Iterable<string>) {
this.#items = items;
@@ -50,14 +51,27 @@ class NavItemBag implements NavContentNavItems {
take(id: string): NavContentNavItem | undefined {
const item = this.#index.get(id);
if (item) {
if (item && !this.#taken.has(id)) {
this.#taken.add(id);
if (this.#restItems) {
const index = this.#restItems.findIndex(
restItem => restItem.node.spec.id === id,
);
if (index !== -1) {
this.#restItems.splice(index, 1);
}
}
}
return item;
}
rest(): NavContentNavItem[] {
return this.#items.filter(item => !this.#taken.has(item.node.spec.id));
if (!this.#restItems) {
this.#restItems = this.#items.filter(
item => !this.#taken.has(item.node.spec.id),
);
}
return this.#restItems;
}
clone(): NavContentNavItems {
@@ -65,19 +79,32 @@ class NavItemBag implements NavContentNavItems {
}
withComponent(Component: (props: NavContentNavItem) => JSX.Element) {
let renderedItems: JSX.Element[] | undefined;
return {
take: (id: string) => {
const item = this.take(id);
return item ? <Component {...item} /> : null;
if (item && renderedItems) {
const index = renderedItems.findIndex(
renderedItem => renderedItem.key === item.node.spec.id,
);
if (index !== -1) {
renderedItems.splice(index, 1);
}
}
return item ? <Component key={item.node.spec.id} {...item} /> : null;
},
rest: (options?: { sortBy?: 'title' }) => {
const items = this.rest();
if (options?.sortBy === 'title') {
items.sort((a, b) => a.title.localeCompare(b.title));
if (!renderedItems) {
if (options?.sortBy === 'title') {
items.sort((a, b) => a.title.localeCompare(b.title));
}
renderedItems = items.map(item => (
<Component key={item.node.spec.id} {...item} />
));
}
return items.map(item => (
<Component key={item.node.spec.id} {...item} />
));
return renderedItems;
},
};
}