import { DemoBox } from "../../src/components/demo/demo-box";

Headless Tree provides its own implementation of keybindings that can be customized and extended.

Further down on this page, you can find a list of all available hotkeys. Note that the availability of hotkeys
depends on the features that are included. For any hotkey to work, the `hotkeysCoreFeature` must be included.
This enables some basic tree-related hotkeys like moving focus with arrow keys. Other feature-specific hotkeys,
like toggling selection or renaming an item, need both the `hotkeysCoreFeature` and the feature that implements
the functionality, such as the `selectionFeature` or the `renamingFeature`.

The type of a hotkey implementation is shown below. Read on to see how you can customize existing hotkeys with this
type or add your own.

```ts
export interface HotkeyConfig<T> {
  hotkey: string;
  canRepeat?: boolean;
  allowWhenInputFocused?: boolean;
  isEnabled?: (tree: TreeInstance<T>) => boolean;
  preventDefault?: boolean;
  handler: (e: KeyboardEvent, tree: TreeInstance<T>) => void;
}
```

## Overwriting Hotkeys

You can pass an object of partial hotkey implementations in the `hotkeys` config property to your tree.
Default hotkeys and your custom hotkeys will be merged together during runtime, with your custom hotkeys
overwriting the default hotkeys. This allows you to overwrite the keybindings, parts of the behavior (like
being able to repeat pressing the key) certain hotkeys or overwriting their implementation.

Use the tables at the bottom of the page for reference of the hotkey names.

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

const meta = {
  title: "React/Hotkeys/Overwriting Hotkeys",
  tags: ["feature/hotkeys", "homepage"],
} satisfies Meta;

export default meta;

// story-start
export const OverwritingHotkeys = () => {
  const tree = useTree<string>({
    rootItemId: "folder",
    getItemName: (item) => item.getItemData(),
    isItemFolder: (item) => !item.getItemData().endsWith("item"),
    indent: 20,
    dataLoader: {
      getItem: (itemId) => itemId,
      getChildren: (itemId) => [
        `${itemId}-1`,
        `${itemId}-2`,
        `${itemId}-3`,
        `${itemId}-1item`,
        `${itemId}-2item`,
      ],
    },
    hotkeys: {
      focusNextItem: {
        hotkey: "ArrowRight",
      },
      focusPreviousItem: {
        hotkey: "ArrowLeft",
      },
    },
    features: [syncDataLoaderFeature, selectionFeature, hotkeysCoreFeature],
  });

  return (
    <>
      <p className="description">
        In this example, the hotkeys for moving the focus up and down are bound
        to &quot;ArrowLeft&quot; and &quot;ArrowRight&quot;, overwriting the
        hotkeys for expanding and collapsing (by default, the hotkeys are
        ArrowUp and ArrowDown).
      </p>
      <div {...tree.getContainerProps()} className="tree">
        {tree.getItems().map((item) => (
          <button
            {...item.getProps()}
            key={item.getId()}
            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>
        ))}
      </div>
    </>
  );
};
```

## Adding custom Hotkeys

You can also very easily add custom hotkeys, with arbitrary keybindings and implementations. While you can
technically use any names for the hotkeys, the TypeScript type is implemented to only support the official
hotkey names and any names that are prefixed with `custom` to avoid mistakes.

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

const meta = {
  title: "React/Hotkeys/Custom Hotkeys",
  tags: ["feature/hotkeys", "homepage"],
} satisfies Meta;

export default meta;

// TODO stopped verifying stories after big revampt at this story

// story-start
export const CustomHotkeys = () => {
  const tree = useTree<string>({
    rootItemId: "folder",
    getItemName: (item) => item.getItemData(),
    isItemFolder: (item) => !item.getItemData().endsWith("item"),
    indent: 20,
    dataLoader: {
      getItem: (itemId) => itemId,
      getChildren: (itemId) =>
        itemId.length < 15
          ? [
              `${itemId}-1`,
              `${itemId}-2`,
              `${itemId}-3`,
              `${itemId}-1item`,
              `${itemId}-2item`,
            ]
          : [],
    },
    hotkeys: {
      // Begin the hotkey name with "custom" to satisfy the type checker
      customExpandAll: {
        hotkey: "KeyQ",
        handler: (e, tree) => {
          tree.expandAll();
        },
      },
      customCollapseAll: {
        hotkey: "KeyW",
        handler: (e, tree) => {
          tree.collapseAll();
        },
      },
    },
    features: [
      syncDataLoaderFeature,
      selectionFeature,
      hotkeysCoreFeature,
      expandAllFeature,
    ],
  });

  return (
    <>
      <p className="description">
        In this example, two additional custom hotkeys are defined: Press
        &quot;q&quot; while the tree is focused to expand all items, and press
        &quot;w&quot; to collapse all items.
      </p>
      <div {...tree.getContainerProps()} className="tree">
        {tree.getItems().map((item) => (
          <button
            {...item.getProps()}
            key={item.getId()}
            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>
        ))}
      </div>
    </>
  );
};
```

The names of keys are based on the [HTML Key value](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).
The following additional key names are supported:

- `LetterOrNumber`: Any letter or number key, `/^[a-z0-9]$/`
- `Letter`: Any letter key, `/^[a-z]$/`
- `Plus`: The symbol plus, `/^\+$/`. Useful since the "+" symbol is used in keybinding strings to concatenate individual keys.

## Available Hotkeys

The [Accessibility Guide](https://headless-tree.lukasbach.com/llm/guides/accessibility.md) shows some demos of which hotkeys are available and how they can be used.

### General Hotkeys

The following hotkeys need the `hotkeysCoreFeature` feature added.

| Key                 | Default Keybinding | Description                                                                                                                    |
|---------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------|
| `focusNextItem`     | `ArrowDown`        | Focuses the next item in the list.                                                                                             |
| `focusPreviousItem` | `ArrowUp`          | Focuses the previous item in the list.                                                                                         |
| `expandOrDown`      | `ArrowRight`       | Expands the focused item if it is collapsed. If the focused item is already expanded, focuses the next item in the list.       |
| `collapseOrUp`      | `ArrowLeft`        | Collapses the focused item if it is expanded. If the focused item is already collapsed, focuses the previous item in the list. |
| `focusFirstItem`    | `Home`             | Focuses the first item in the list.                                                                                            |
| `focusLastItem`     | `End`              | Focuses the last item in the list.                                                                                             |

### Selections

The following hotkeys need the `hotkeysCoreFeature` and `selectionFeature` features added.

| Key                  | Default Keybinding | Description                                                                                                                            |
|----------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| `toggleSelectedItem` | `Ctrl+Space`       | Toggles the selection of the focused item.                                                                                             |
| `selectUpwards`      | `Shift+ArrowUp`    | Expands the current selection by the item prior to the focused item, and moves focus upwards. Discards non-connected selection blocks. |
| `selectDownwards`    | `Shift+ArrowDown`  | Expands the current selection by the item after the focused item, and moves focus downwards. Discards non-connected selection blocks.  |
| `selectAll`          | `Ctrl+KeyA`        | Selects all items in the list.                                                                                                         |

### Drag And Drop

The following hotkeys need the `hotkeysCoreFeature`, `dragAndDropFeature` and `keyboardDragAndDrop` features added.

| Key            | Default Keybinding   | Description                              |
|----------------|----------------------|------------------------------------------|
| `startDrag`    | `Control+Shift+KeyD` | Starts dragging the selected items.      |
| `dragUp`       | `ArrowUp`            | Moves the drag target one item upwards   |
| `dragDown`     | `ArrowDown`          | Moves the drag target one item downwards |
| `cancelDrag`   | `Escape`             | Cancels the drag operation.              |
| `completeDrag` | `Enter`              | Completes the drag operation.            |


### Search

The following hotkeys need the `hotkeysCoreFeature` and `searchFeature` features added.

| Key                  | Default Keybinding | Description                                                                             |
|----------------------|--------------------|-----------------------------------------------------------------------------------------|
| `openSearch`         | `LetterOrNumber`   | Opens the search input and focuses it.                                                  |
| `closeSearch`        | `Escape`           | Closes the search input.                                                                |
| `submitSearch`       | `Enter`            | Submits the search input, and focuses and selects the last selected search result item. |
| `nextSearchItem`     | `ArrowDown`        | Focuses the next search result item.                                                    |
| `previousSearchItem` | `ArrowUp`          | Focuses the previous search result item.                                                |

### Renaming

The following hotkeys need the `hotkeysCoreFeature` and `renamingFeature` features added.

| Key                | Default Keybinding | Description                                                                                 |
|--------------------|--------------------|---------------------------------------------------------------------------------------------|
| `renameItem`       | `F2`               | Starts renaming the focused item.                                                           |
| `abortRename`      | `Escape`           | Close the rename input.                                                                     |
| `completeRenaming` | `Enter`            | Close the rename input, and call the `config.onRename(item, newNameValue)` config property. |

### Expand All

The following hotkeys need the `hotkeysCoreFeature` and `expandAllFeature` features added.

| Key                | Default Keybinding    | Description                                                                                                                     |
|--------------------|-----------------------|---------------------------------------------------------------------------------------------------------------------------------|
| `expandSelected`   | `Control+Shift+Plus`  | Expand all selected items and their descendants. When running async, pressing Escape afterwards will cancel the expand process. |
| `collapseSelected` | `Control+Shift+Minus` | Collapse all selected items and their descendants.                                                                              |
