Skip to content

Commit

Permalink
chore: extract data provider docs into two pages, finish new docs
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasbach committed Jan 19, 2024
1 parent c5a0af4 commit 2d0764f
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 151 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import { UncontrolledTreeEnvironment, Tree, StaticTreeDataProvider } from 'react
</UncontrolledTreeEnvironment>;
```

More details at [the Get-Started Guide](https://rct.lukasbach.com/docs/getstarted).
More details at [the Get-Started Guide](https://rct.lukasbach.com/docs/getstarted). The [guide on how to integrate
data with a static tree data provider](https://rct.lukasbach.com/docs/guides/static-data-provider) is also
a good starting point to understand how to integrate data with React Complex Tree.

## Features

Expand Down
7 changes: 6 additions & 1 deletion packages/docs/docs/getstarted.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ For the environment, there are three ways to implement your tree environment:

- Use an [UncontrolledTreeEnvironment](/docs/react/UncontrolledTreeEnvironment) and a
[StaticTreeDataProvider](/docs/api/classes/StaticTreeDataProvider) that provides the items. This is the easiest
approach and is described below.
approach and is described below, and in the [static tree data provider guide](/docs/guides/static-data-provider).
- Use an [UncontrolledTreeEnvironment](/docs/react/UncontrolledTreeEnvironment) and implement a custom
[TreeDataProvider](/docs/api/interfaces/TreeDataProvider). This is similarly easy and provides the ability to
directly react to change events and define a lazy item-loading strategy. This approach is most likely the best
Expand All @@ -122,6 +122,11 @@ For the environment, there are three ways to implement your tree environment:

## Providing the data for the tree

:::info
There is another guide specifically for using static tree data providers [here](/docs/guides/static-data-provider),
which goes a bit more into detail.
:::

When integrating React Complex Tree with an uncontrolled environment and a static tree data provider, items
must be provided as [explicit data source](/docs/api/interfaces/ExplicitDataSource). An example for such
items looks like this:
Expand Down
151 changes: 2 additions & 149 deletions packages/docs/docs/guides/custom-data-provider.mdx
Original file line number Diff line number Diff line change
@@ -1,156 +1,9 @@
---
sidebar_position: 2
sidebar_position: 2.5
---

# Data Provider
# Custom Tree Data Provider

When using an uncontrolled environment, you need to provide your data by supplying a data provider. The
easiest way to get started is using the [Static Tree Data Provider](/docs/api/classes/StaticTreeDataProvider).
It allows you to provide your data as record which maps item ids to tree items, and gives you the possibility
to react to changes in the tree structure, as well as inject your own changes through change events.

# Static Tree Data Provider

The following example gives a good example of what is possible with static tree data providers. We will look
into the details of the data provider below.

```jsx live
function App() {
const items = useMemo(() => ({ ...shortTree.items }), []);
const dataProvider = useMemo(
() =>
new StaticTreeDataProvider(items, (item, data) => ({
...item,
data,
})),
[items]
);

const injectItem = () => {
const rand = `${Math.random()}`;
items[rand] = { data: 'New Item', index: rand };
items.root.children.push(rand);
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};

const removeItem = () => {
if (items.root.children.length === 0) return;
items.root.children.pop();
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};

return (
<UncontrolledTreeEnvironment
canDragAndDrop
canDropOnFolder
canReorderItems
dataProvider={dataProvider}
getItemTitle={item => item.data}
viewState={{
'tree-1': {
expandedItems: [],
},
}}
>
<button type="button" onClick={injectItem}>
Inject item
</button>
<button type="button" onClick={removeItem}>
Remove item
</button>
<Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
</UncontrolledTreeEnvironment>
);
}
```

## Creating the data provider with data

First, create the data provider. You want to make sure it isn't recreated on re-renders, so memoize
it in the component in which it is defined.

```tsx
const dataProvider = useMemo(
() =>
new StaticTreeDataProvider(items, (item, data) => ({
...item,
data,
})),
[items]
);
```

The items is a record mapping item ids to tree items, for example:

```typescript
const items = [
{
index: "item-id",
data: { arbitraryData: 123, name: "Hello" },
children: ["item-id-1", "item-id-2"],
isFolder: true
}
]
```

Note that, whatever you provide to the `getItemTitle` prop is used to infer the item display name.

```ts jsx
<UncontrolledTreeEnvironment
getItemTitle={item => item.data.name}
/>
```

## Apply changes from outside

You can apply changes to the underlying data source. Just make sure to let RCT know about that by
emitting a change event on the affected items. Note that, if you add or remove items, the affected item
is the parent item, not the added or removed items.

```ts
const injectItem = () => {
const rand = `${Math.random()}`;
items[rand] = { data: 'New Item', index: rand };
items.root.children.push(rand);
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};

const removeItem = () => {
if (items.root.children.length === 0) return;
items.root.children.pop();
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};
```

## Reacting to Drag Events

Drag changes are always immediately applied to the visualization, so make sure to implement the `canDropAt`
prop to customize if that should not work in all cases. The static tree data emits tree change events similar
to the ones you would emit when applying changes from outside, so you can react to them in the same way.

```typescript
dataProvider.onDidChangeTreeData(changedItemIds => {
console.log(changedItemIds);
});
```

## Reacting to Rename Events

The second (optional) parameter of the static tree data provider lets you react to rename events. Note that
you can customize whether renaming is possible in the first place through the `canRename` prop.

```typescript
const dataProvider = new StaticTreeDataProvider(items, (item, newName) => {
// Return the patched item with new item name here
return {
...item,
data: { ...item.data, name: newName },
};
});
`
```

## Custom Data Provider

In more complex scenarios, it's probably easiest to implement your own data provider.
This provider must implement the [TreeDataProvider interface](/docs/api/interfaces/TreeDataProvider), i.e.
Expand Down
153 changes: 153 additions & 0 deletions packages/docs/docs/guides/static-data-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
sidebar_position: 2
---

# Static Tree Data Provider

When using an uncontrolled environment, you need to provide your data by supplying a data provider. The
easiest way to get started is using the [Static Tree Data Provider](/docs/api/classes/StaticTreeDataProvider).
It allows you to provide your data as record which maps item ids to tree items, and gives you the possibility
to react to changes in the tree structure, as well as inject your own changes through change events.

:::info
If you want to implement a custom data provider your own, you can find a comprehensive guide [here](/docs/guides/custom-data-provider).
:::

The following example gives a good example of what is possible with static tree data providers. We will look
into the details of the data provider below.

```jsx live
function App() {
const items = useMemo(() => ({ ...shortTree.items }), []);
const dataProvider = useMemo(
() =>
new StaticTreeDataProvider(items, (item, data) => ({
...item,
data,
})),
[items]
);

const injectItem = () => {
const rand = `${Math.random()}`;
items[rand] = { data: 'New Item', index: rand };
items.root.children.push(rand);
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};

const removeItem = () => {
if (items.root.children.length === 0) return;
items.root.children.pop();
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};

return (
<UncontrolledTreeEnvironment
canDragAndDrop
canDropOnFolder
canReorderItems
dataProvider={dataProvider}
getItemTitle={item => item.data}
viewState={{
'tree-1': {
expandedItems: [],
},
}}
>
<button type="button" onClick={injectItem}>
Inject item
</button>
<button type="button" onClick={removeItem}>
Remove item
</button>
<Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
</UncontrolledTreeEnvironment>
);
}
```

## Creating the data provider with data

First, create the data provider. You want to make sure it isn't recreated on re-renders, so memoize
it in the component in which it is defined.

```tsx
const dataProvider = useMemo(
() =>
new StaticTreeDataProvider(items, (item, data) => ({
...item,
data,
})),
[items]
);
```

The items is a record mapping item ids to tree items, for example:

```typescript
const items = [
{
index: "item-id",
data: { arbitraryData: 123, name: "Hello" },
children: ["item-id-1", "item-id-2"],
isFolder: true
}
]
```

Note that, whatever you provide to the `getItemTitle` prop is used to infer the item display name.

```ts jsx
<UncontrolledTreeEnvironment
getItemTitle={item => item.data.name}
/>
```

## Apply changes from outside

You can apply changes to the underlying data source. Just make sure to let RCT know about that by
emitting a change event on the affected items. Note that, if you add or remove items, the affected item
is the parent item, not the added or removed items.

```ts
const injectItem = () => {
const rand = `${Math.random()}`;
items[rand] = { data: 'New Item', index: rand };
items.root.children.push(rand);
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};

const removeItem = () => {
if (items.root.children.length === 0) return;
items.root.children.pop();
dataProvider.onDidChangeTreeDataEmitter.emit(['root']);
};
```

## Reacting to Drag Events

Drag changes are always immediately applied to the visualization, so make sure to implement the `canDropAt`
prop to customize if that should not work in all cases. The static tree data emits tree change events similar
to the ones you would emit when applying changes from outside, so you can react to them in the same way.

```typescript
dataProvider.onDidChangeTreeData(changedItemIds => {
console.log(changedItemIds);
});
```

## Reacting to Rename Events

The second (optional) parameter of the static tree data provider lets you react to rename events. Note that
you can customize whether renaming is possible in the first place through the `canRename` prop.

```typescript
const dataProvider = new StaticTreeDataProvider(items, (item, newName) => {
// Return the patched item with new item name here
return {
...item,
data: { ...item.data, name: newName },
};
});
`
```

0 comments on commit 2d0764f

Please sign in to comment.