feat(tree): replace sidebar tree (react-aborist) with custom tree implementation (#2199)

* feat(tree): replace react-arborist with custom tree implementation

* feat(tree): keyboard arrow navigation between rows

* feat(emoji-picker): focus search input on open

* refactor(emoji): switch to @slidoapp/emoji-mart fork for accessibility

* feat(tree): Home/End and typeahead keyboard navigation

* feat(tree): roving tabindex and * to expand sibling subtrees

* feat(tree): Space activation and ARIA refinements

* fix(tree): move treeitem role to focusable row + aria-current
This commit is contained in:
Philip Okugbe
2026-05-13 23:01:04 +01:00
committed by GitHub
parent a689cca7a0
commit 31ed0df3f7
32 changed files with 3816 additions and 1429 deletions
@@ -37,7 +37,7 @@ import { validate as isValidUuid } from "uuid";
import { useTranslation } from "react-i18next";
import { useAtom } from "jotai";
import { treeDataAtom } from "@/features/page/tree/atoms/tree-data-atom";
import { SimpleTree } from "react-arborist";
import { treeModel } from "@/features/page/tree/model/tree-model";
import { SpaceTreeNode } from "@/features/page/tree/types";
import { useQueryEmit } from "@/features/websocket/use-query-emit";
@@ -170,11 +170,8 @@ export function useRestorePageMutation() {
onSuccess: async (restoredPage) => {
notifications.show({ message: "Page restored successfully" });
// Add the restored page back to the tree
const treeApi = new SimpleTree<SpaceTreeNode>(treeData);
// Check if the page already exists in the tree (it shouldn't)
if (!treeApi.find(restoredPage.id)) {
if (!treeModel.find(treeData, restoredPage.id)) {
// Create the tree node data with hasChildren from backend
const nodeData: SpaceTreeNode = {
id: restoredPage.id,
@@ -193,24 +190,17 @@ export function useRestorePageMutation() {
let index = 0;
if (parentId) {
const parentNode = treeApi.find(parentId);
const parentNode = treeModel.find(treeData, parentId);
if (parentNode) {
index = parentNode.children?.length || 0;
}
} else {
// Root level page
index = treeApi.data.length;
index = treeData.length;
}
// Add the node to the tree
treeApi.create({
parentId,
index,
data: nodeData,
});
// Update the tree data
setTreeData(treeApi.data);
setTreeData(treeModel.insert(treeData, parentId, nodeData, index));
// Emit websocket event to sync with other users
setTimeout(() => {