lukasbach / react-complex-tree

Unopinionated Accessible Tree Component with Multi-Select and Drag-And-Drop
https://rct.lukasbach.com
MIT License
915 stars 72 forks source link

How to update the browser history / url in the address bar when triggering onPrimaryAction={onSelect} in order to be able to reload the page selecting the same tree item? #340

Closed sufius closed 5 months ago

sufius commented 5 months ago

Im searching in your list of examples a solution to update the browser history and/or the url in the address bar when f.e. triggering the onPrimaryAction event in order to be able to load and select the previously selected tree item. is there already something like this implemented or another solution? i wasnt able to find it.

lukasbach commented 5 months ago

This isn't directly related to the library, but you can use the DOM History API to push custom states into history and read from history when the page loads, and then use the data from history to manually set the initial state of the tree based on history data.

sufius commented 5 months ago

Thank you for this very fast response. I will try to implement it and maybe post it here. anyway dont hesitate to close this issue so far.

sufius commented 5 months ago

TLDR;

is there a smart / existing way to get all parents of target element?

Here a first solution:

sandbox

import {
  UncontrolledTreeEnvironment,
  Tree,
  StaticTreeDataProvider,
  TreeItem,
} from "react-complex-tree";
import { longTree } from "./data";
import "react-complex-tree/lib/style-modern.css";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { useState } from "react";

export default function () {
  const [selectedItem, setSelectedItem] = useState<string>("");
  const navigate = useNavigate();

  // get hash, but use it only if selectedItem is not set
  const { hash } = useLocation();

  // set hash tag and save into local state
  const onSelect = (item: TreeItem<any>, treeId: string): void => {
    navigate("#" + item.index);
    setSelectedItem(item.index);
  };

  return (
    <>
      <UncontrolledTreeEnvironment
        dataProvider={
          new StaticTreeDataProvider(longTree.items, (item, data) => ({
            ...item,
            data,
          }))
        }
        getItemTitle={(item) => item.data}
        viewState={{
          "tree-1": {
            expandedItems: [selectedItem || hash.split("#").pop()], // this is not working with deep tree items, just at root level
            focusedItem: selectedItem || hash.split("#").pop(),
            selectedItems: [selectedItem || hash.split("#").pop()],
          },
        }}
        onPrimaryAction={onSelect}
      >
        <Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
      </UncontrolledTreeEnvironment>
      <Link to="/test">Test</Link>
    </>
  );
}

and here the sourrounding RouterProvider

import { RouterProvider } from "react-router-dom";
import { router } from "./router";

export default function App() {
  return <RouterProvider router={router} />;
}

and here the routes

import React from "react";
import type { RouteObject } from "react-router-dom";
import { Outlet } from "react-router-dom";
import { createBrowserRouter } from "react-router-dom";
import Layout from "./Layout";
import Test from "./Test";
import Home from "./Home";

export const routes = [
  {
    path: "/",
    element: <Outlet />,
    children: [
      {
        index: true,
        path: "/",
        element: <Home />,
      },
      {
        path: "/test",
        element: <Test />,
      },
    ],
  },
] satisfies [RouteObject, ...RouteObject[]];

export const router = createBrowserRouter(routes, {
  future: {
    v7_normalizeFormMethod: true,
  },
});

it currently just working if the hash tag is pointing to an item in the root level:

image

but unfortunately it is not working if the hash is pointing to an element deeper in the tree. it is probably selected correctly but the parents are not expaned.

image

is there a smart / existing way to get all parents of target element?