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

The default behavior of Headless Tree resembles the UX behavior of a VSCode Filetree: Clicking on an item
will both focus it and make it the sole selection. Control-clicking will add the item to an existing selection.
If the clicked item is also a folder, it will toggle its expanded-state.

This can be customized very easily by overwriting the generation of the `onClick` (and `onDoubleClick` if you want) handlers
for generated props of tree items. This expands on the idea that was introduced in the [Guide on Plugins](https://headless-tree.lukasbach.com/llm/recipe/plugins.md)
by allowing you to write a small custom plugin that overwrites the behavior of other Headless Tree core features.

## Expand on Double Click

In the following sample, the behavior is changed so that folders are only expanded or collapsed on double-click,
and the primary action handler is also bound to double-click instead of the typical single-click.

Note that we completely overwrite the `onClick` handler here, so we need to re-implement the selection behavior
even if it remains unchanged. We do not need to re-implement other props that are generated for tree items,
since we integrate the existing functionality of other features with the `prev?.()` call.
If we were to expand the `onClick` behavior instead of overwriting it, we could also expand instead of overwrite
the `onClick` handler with `prev?.()?.onClick?.(e)`, as is being done in the
[actual implementation of the selection feature](https://github.com/lukasbach/headless-tree/blob/7f50c2e380f3455c4dfd0413e609ba96b1338fe7/packages/core/src/features/selection/feature.ts#L93).

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

const meta = {
  title: "React/Guides/Click Behavior/Expand On Double Click",
  tags: ["guide/clickbehavior", "basic", "homepage"],
} satisfies Meta;

export default meta;

// story-start

const customClickBehavior: FeatureImplementation = {
  itemInstance: {
    getProps: ({ tree, item, prev }) => ({
      ...prev?.(),
      onDoubleClick: (e: MouseEvent) => {
        item.primaryAction();

        if (!item.isFolder()) {
          return;
        }

        if (item.isExpanded()) {
          item.collapse();
        } else {
          item.expand();
        }
      },
      onClick: (e: MouseEvent) => {
        if (e.shiftKey) {
          item.selectUpTo(e.ctrlKey || e.metaKey);
        } else if (e.ctrlKey || e.metaKey) {
          item.toggleSelect();
        } else {
          tree.setSelectedItems([item.getItemMeta().itemId]);
        }

        item.setFocused();
      },
    }),
  },
};

export const ExpandOnDoubleClick = () => {
  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`,
      ],
    },
    onDrop: console.log,
    features: [
      syncDataLoaderFeature,
      selectionFeature,
      dragAndDropFeature,
      keyboardDragAndDropFeature,
      hotkeysCoreFeature,
      customClickBehavior,
    ],
  });

  return (
    <div {...tree.getContainerProps()} className="tree">
      {tree.getItems().map((item) => (
        <div className="outeritem" key={item.getId()}>
          <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(),
                drop: item.isDragTarget(),
              })}
            >
              {item.getItemName()}
            </div>
          </button>
        </div>
      ))}
      <div style={tree.getDragLineStyle()} className="dragline" />
    </div>
  );
};
```

## Expand on Arrow-Click

Similar to the previous sample, in the following code snippet we completely remove the expand/collapse behavior
from the `onClick` handler of tree items, and render dedicated arrow buttons that can be clicked to expand or collapse
the tree items instead.

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

import "./expand-on-arrow-click.css";

const meta = {
  title: "React/Guides/Click Behavior/Expand On Arrow Click",
  tags: ["guide/clickbehavior", "basic"],
} satisfies Meta;

export default meta;

// story-start

const customClickBehavior: FeatureImplementation = {
  itemInstance: {
    getProps: ({ tree, item, prev }) => ({
      ...prev?.(),
      onClick: (e: MouseEvent) => {
        if (e.shiftKey) {
          item.selectUpTo(e.ctrlKey || e.metaKey);
        } else if (e.ctrlKey || e.metaKey) {
          item.toggleSelect();
        } else {
          tree.setSelectedItems([item.getItemMeta().itemId]);
        }

        item.setFocused();
      },
    }),
  },
};

export const ExpandOnArrowClick = () => {
  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`,
      ],
    },
    onDrop: console.log,
    features: [
      syncDataLoaderFeature,
      selectionFeature,
      dragAndDropFeature,
      keyboardDragAndDropFeature,
      hotkeysCoreFeature,
      customClickBehavior,
    ],
  });

  return (
    <div {...tree.getContainerProps()} className="tree noarrow">
      {tree.getItems().map((item) => (
        <div
          className="outeritem"
          key={item.getId()}
          style={{ paddingLeft: `${item.getItemMeta().level * 20}px` }}
        >
          {item.isFolder() && (
            <button onClick={item.isExpanded() ? item.collapse : item.expand}>
              {item.isExpanded() ? "v" : ">"}
            </button>
          )}
          <button {...item.getProps()} key={item.getId()}>
            <div
              className={cn("treeitem", {
                focused: item.isFocused(),
                expanded: item.isExpanded(),
                selected: item.isSelected(),
                folder: item.isFolder(),
                drop: item.isDragTarget(),
              })}
            >
              {item.getItemName()}
            </div>
          </button>
        </div>
      ))}
      <div style={tree.getDragLineStyle()} className="dragline" />
    </div>
  );
};
```
