Skip to content
+

Rich Tree View - Headless

Create your custom Tree View.

Create a custom plugin

Basic plugin

A custom plugins contains 2 required elements:

  1. A hook that will be executed by the useTreeView hook:

    const useCustomPlugin = ({ params }) => {
      React.useEffect(() => {
        console.log(params.customParam);
      });
    
      return {};
    };
    
  2. A property containing the params used by this topic:

    useCustomPlugin.params = {
      customParam: true,
    };
    

Params default value

Use the getDefaultizedParams property to set a default value to your plugin params:

const useCustomPlugin = ({ params }) => {
  React.useEffect(() => {
    console.log(params.customParam);
  });

  return {};
};

useCustomPlugin.params = { customParam: true };

useCustomPlugin.getDefaultizedParams = ({ params }) => ({
  ...params,
  customParam: params.customParam ?? false,
});

Controllable models

A model is a value that can either be controlled or initialized using a prop. The Tree View contains several models like the expandedItems model which contains the ids of the items currently expanded.

You can create your own models using the models property of your plugin:

useCustomPlugin.params = {
  // ...other params
  defaultCustomModel: true,
  customModel: true,
};

useCustomPlugin.getDefaultizedParams = ({ params }) => ({
  ...params,
  // ... other defaultized params
  defaultCustomModel: params.defaultCustomModel ?? false,
});

useCustomPlugin.models = {
  customModel: {
    getDefaultValue: (params) => defaultCustomModel,
  },
};

You can then use this model in your plugin (or in any other plugin) using the models parameter:

const useCustomPlugin = ({ models }) => {
  React.useEffect(() => {
    console.log(models.customModel.value);
  });

  const updateCustomModel = (newValue) =>
    models.customModel.setControlledValue(newValue);

  return {};
};

Use elements from another plugin

Your plugin can access the instance methods, the params and the models of any other plugin.

const useCustomPlugin = ({ models }) => {
  const handleSomeAction = () => {
    // Log the id of the items currently expanded
    console.log(models.expandedItems.value);

    // Collapse all the items
    models.expandedItems.setValue([]);

    // Check if an item is expanded
    const isExpanded = instance.isNodeExpanded('some-item-id');
  };
};

Define a plugin state

TODO

Populate the Tree View instance

The Tree View instance is an object accessible in all the plugins and in the Tree Item. It is the main way a plugin can provide features to the rest of the component.

const useCustomPlugin = ({ models }) => {
  const toggleCustomModel = () =>
    models.customModel.setValue(!models.customModel.value);

  return {
    instance: {
      toggleCustomModel,
    },
  };
};

You can then use this instance method in any other plugin:

const useOtherCustomPlugin = ({ models, instance }) => {
  const handleSomeAction = () => {
    instance.toggleCustomModel();
  };
};

Emit and receive events

const useCustomPlugin = () => {
  const toggleCustomModel = () => {
    const newValue = !models.customModel.value;
    models.customModel.setValue(newValue);
    publishTreeViewEvent(instance, 'toggleCustomModel', { value: newValue });
  };

  return {
    instance: {
      toggleCustomModel,
    },
  };
};

You can then subscribe to this event in any other plugin:

const useOtherCustomPlugin = ({ instance }) => {
  useInstanceEventHandler(instance, 'toggleCustomModel', ({ value }) => {
    console.log('New value of customModel', value);
  });
};

Pass props to your root element

Use the getRootProps property of your returned value to pass props to your root element:

const useCustomPlugin = ({ params }) => {
  return {
    getRootProps: () => ({
      'data-customparam': params.customParam,
    }),
  };
};

Pass elements to the Tree Item

Use the contextValue property in the returned object to pass elements to the Tree Item:

const useCustomPlugin = ({ params }) => {
  return {
    contextValue: () => ({
      customPlugin: { enabled: true },
    }),
  };
};

function useTreeItemState(itemId: string) {
  const {
    customPlugin,
    // ...other elements returned by the context
  } = useTreeViewContext<DefaultTreeViewPluginSignatures>();

  // ...rest of the `useTreeItemState` hook content

  return {
    customPlugin,
    // ...other elements returned by `useTreeItemState`
  };
}

function TreeItemContent() {
  const {
    customPlugin,
    // ...other elements returned by `useTreeItemState`
  } = useTreeItemState(props.itemId);

  // Do something with customPlugin.enabled
}

Plugin typing

The typing of a plugin is defined using its signature. This type contains the following information:

type UseCustomPluginSignature = TreeViewPluginSignature<{
  // The params specific to your plugin before running `getDefaultizedParams`
  params: UseCustomPluginParams;
  // The params specific to your plugins after running `getDefaultizedParams`
  defaultizedParams: UseCustomPluginDefaultizedParams;
  // The methods added to the Tree View instance by your plugin
  instance: UseCustomPluginInstance;
  // The events emitted by your plugin
  events: UseCustomPluginEvents;
  // The states defined by your plugin
  state: UseCustomPluginState;
  // The context value defined by your plugin and passed to the items
  contextValue: UseCustomPluginContextValue;
  // The slots used by this plugin
  slots: UseCustomPluginSlots;
  // The slot props used by this plugin
  slotProps: UseCustomPluginSlotProps;
  // The name of the models defined by your plugin
  modelNames: UseCustomPluginModelNames;
  // The plugins this plugin needs to work correctly
  dependencies: UseCustomPluginDependantPlugins;
}>;

The most basic plugin would have the following signature:

type UseCustomPluginSignature = TreeViewPluginSignature<{}>;

The plugin built in the sections above would have the following signature:

type UseCustomPluginSignature = TreeViewPluginSignature<{
  params: {
    customParam?: boolean;
    customModel?: boolean;
    defaultCustomModel?: boolean;
  };
  // `customParam` and `defaultCustomModel` have a default value defined in `getDefaultizedParams`
  defaultizedParams: {
    customParam: boolean;
    customModel?: boolean;
    defaultCustomModel: boolean;
  };
  instance: { toggleCustomModel: () => void };
  events: {
    toggleCustomModel: {
      params: { value: boolean };
    };
  };
  contextValue: { customPlugin: { enabled: boolean } };
  modelNames: 'customModel';
  // We want to have access to the expansion models and methods of the expansion plugin.
  dependencies: [UseTreeViewExpansionSignature];
}>;

Examples

Log expanded items

Interact with the Tree View to see the expanded items being logged:

Press Enter to start editing