import { DemoBox } from "../../src/components/demo/demo-box";
import { FeaturePageHeader } from "../../src/components/docs-page/feature-page-header";

<FeaturePageHeader
    title="Renaming"
    subtitle="Rename items in the tree"
    feature="renaming"
/>

```ts jsx
import type { Meta } from "@storybook/react";
import React, { Fragment } from "react";
import {
  hotkeysCoreFeature,
  renamingFeature,
  selectionFeature,
  syncDataLoaderFeature,
} from "@headless-tree/core";
import { useTree } from "@headless-tree/react";
import cn from "classnames";

const meta = {
  title: "React/Renaming/Basic",
  tags: ["feature/renaming", "basic", "homepage"],
} satisfies Meta;

export default meta;

// story-start
export const Basic = () => {
  const tree = useTree<string>({
    rootItemId: "root",
    getItemName: (item) => item.getItemData(),
    isItemFolder: () => true,
    dataLoader: {
      getItem: (itemId) => itemId,
      getChildren: (itemId) => [`${itemId}-1`, `${itemId}-2`, `${itemId}-3`],
    },
    onRename: (item, value) => {
      alert(`Renamed ${item.getItemName()} to ${value}`);
    },
    initialState: {
      expandedItems: ["root-1", "root-1-1"],
      renamingItem: "root-1-1-2",
      renamingValue: "abc",
    },
    features: [
      syncDataLoaderFeature,
      selectionFeature,
      hotkeysCoreFeature,
      renamingFeature,
    ],
  });

  return (
    <>
      <p className="description">
        <button onClick={() => tree.getItemInstance("root-2").startRenaming()}>
          Rename root-2
        </button>{" "}
        or press F2 when an item is focused.
      </p>
      <div {...tree.getContainerProps()} className="tree">
        {tree.getItems().map((item) => (
          <Fragment key={item.getId()}>
            {item.isRenaming() ? (
              <div
                className="renaming-item"
                style={{ marginLeft: `${item.getItemMeta().level * 20}px` }}
              >
                <input {...item.getRenameInputProps()} />
              </div>
            ) : (
              <button
                {...item.getProps()}
                style={{ paddingLeft: `${item.getItemMeta().level * 20}px` }}
              >
                <div
                  className={cn("treeitem", {
                    focused: item.isFocused(),
                    expanded: item.isExpanded(),
                    selected: item.isSelected(),
                    folder: item.isFolder(),
                  })}
                >
                  {item.getItemName()}
                </div>
              </button>
            )}
          </Fragment>
        ))}
      </div>
    </>
  );
};
```

The renaming feature allows you to allow users to rename items in the tree. They can either start renaming
an item via the `renameItem` hotkey (defaulting to F2), or when the `item.startRenaming()` method is called.
You can hook this up to e.g. double-clicking an item-name or to a context menu option.

The feature exposes two state variables, `renamingItem` to keep track of the item that is currently being renamed,
and `renamingValue` to keep track of the new name that the user is typing in. When
[maintaining individual states](https://headless-tree.lukasbach.com/llm/guides/state.md), you can hook into changes to
those states via the `setRenamingItem` and `setRenamingValue` config methods.

## Rendering the Rename Input

Similar to other Headless Tree Features, the library only provides the functionality, but not the UI. For any tree
item, you can determine whether it should render a renaming behavior with `item.isRenaming()`. Make sure to
pass `item.getRenameInputProps()` to the input element to hook up the renaming behavior to the input field.

```jsx
if (item.isRenaming()) {
  return (
    <input {...item.getRenameInputProps()} />
  );
}

// otherwise, render the item as usual
```

## Customizing which items can be renamed

You can also customize which items can be renamed by providing a `canRename` function in the tree configuration.

```ts jsx
import type { Meta } from "@storybook/react";
import React, { Fragment } from "react";
import {
  hotkeysCoreFeature,
  renamingFeature,
  selectionFeature,
  syncDataLoaderFeature,
} from "@headless-tree/core";
import { useTree } from "@headless-tree/react";
import cn from "classnames";

const meta = {
  title: "React/Renaming/Can Rename Configurability",
  tags: ["feature/renaming"],
} satisfies Meta;

export default meta;

// story-start
export const CanRenameConfigurability = () => {
  const tree = useTree<string>({
    rootItemId: "root",
    getItemName: (item) => item.getItemData(),
    isItemFolder: (item) =>
      item.getId().endsWith("-1") || item.getId().endsWith("-2"),
    dataLoader: {
      getItem: (itemId) => itemId,
      getChildren: (itemId) => [
        `${itemId}-1`,
        `${itemId}-2`,
        `${itemId}-3`,
        `${itemId}-4 (can rename)`,
      ],
    },
    onRename: (item, value) => {
      alert(`Renamed ${item.getItemName()} to ${value}`);
    },
    canRename: (item) => item.getId().includes("can rename"),
    features: [
      syncDataLoaderFeature,
      selectionFeature,
      hotkeysCoreFeature,
      renamingFeature,
    ],
  });

  return (
    <>
      <p className="description">
        <button
          onClick={() =>
            tree.getItemInstance("root-4 (can rename)").startRenaming()
          }
        >
          Rename root-4
        </button>{" "}
        or press F2 when an item is focused.
      </p>
      <div {...tree.getContainerProps()} className="tree">
        {tree.getItems().map((item) => (
          <Fragment key={item.getId()}>
            {item.isRenaming() ? (
              <div
                className="renaming-item"
                style={{ marginLeft: `${item.getItemMeta().level * 20}px` }}
              >
                <input {...item.getRenameInputProps()} />
              </div>
            ) : (
              <button
                {...item.getProps()}
                style={{ paddingLeft: `${item.getItemMeta().level * 20}px` }}
              >
                <div
                  className={cn("treeitem", {
                    focused: item.isFocused(),
                    expanded: item.isExpanded(),
                    selected: item.isSelected(),
                    folder: item.isFolder(),
                  })}
                >
                  {item.getItemName()}
                </div>
              </button>
            )}
          </Fragment>
        ))}
      </div>
    </>
  );
};
```

## Updating the cache of async data loaders

Async data loaders cache item information to avoid reloading items that have already been loaded. If you are using
an async data loader, you will need to update the cached item information when an item is renamed. You can do this
by calling `item.updateCachedData(newItemData)` on the item that was renamed:

```ts
item.updateCachedData({ ...item.getItemData(), name: value });
```

Alternatively, you can also invalidate the cache for the item by calling `item.invalidateItemData()`, which will
make sure that the item data is reloaded from the data loader the next time it is rendered.