Skip to content

Commit

Permalink
Merge pull request #3771 from marmelab/ra-tree-v2
Browse files Browse the repository at this point in the history
[RFR] New ra-tree for react-admin v2
  • Loading branch information
fzaninotto authored Oct 18, 2019
2 parents 9bef7bb + 081fac5 commit 45f2f0a
Show file tree
Hide file tree
Showing 71 changed files with 3,546 additions and 1,866 deletions.
24 changes: 24 additions & 0 deletions examples/simple/src/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,110 +382,134 @@ export default {
id: 1,
name: 'Sport',
published: 1,
position: 0,
parent_id: null,
},
{
id: 2,
name: 'Technology',
published: false,
position: 1,
parent_id: null,
},
{
id: 3,
name: 'Code',
published: true,
position: 2,
parent_id: null,
},
{
id: 4,
name: 'Photo',
published: false,
position: 3,
parent_id: null,
},
{
id: 5,
name: 'Music',
published: 1,
position: 4,
parent_id: null,
},
{
id: 6,
name: 'Parkour',
published: 1,
parent_id: 1,
position: 0,
},
{
id: 7,
name: 'Crossfit',
published: 1,
parent_id: 1,
position: 1,
},
{
id: 8,
name: 'Computing',
published: 1,
parent_id: 2,
position: 0,
},
{
id: 9,
name: 'Nanoscience',
published: 1,
parent_id: 2,
position: 1,
},
{
id: 10,
name: 'Blockchain',
published: 1,
parent_id: 2,
position: 2,
},
{
id: 11,
name: 'Node',
published: 1,
parent_id: 3,
position: 0,
},
{
id: 12,
name: 'React',
published: 1,
parent_id: 3,
position: 1,
},
{
id: 13,
name: 'Nature',
published: 1,
parent_id: 4,
position: 0,
},
{
id: 14,
name: 'People',
published: 1,
parent_id: 4,
position: 1,
},
{
id: 15,
name: 'Animals',
published: 1,
parent_id: 13,
position: 0,
},
{
id: 16,
name: 'Moutains',
published: 1,
parent_id: 13,
position: 1,
},
{
id: 17,
name: 'Rap',
published: 1,
parent_id: 5,
position: 0,
},
{
id: 18,
name: 'Rock',
published: 1,
parent_id: 5,
position: 1,
},
{
id: 19,
name: 'World',
published: 1,
parent_id: 5,
position: 2,
},
],
users: [
Expand Down
91 changes: 90 additions & 1 deletion examples/simple/src/dataProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,97 @@ import jsonRestProvider from 'ra-data-fakerest';

import data from './data';
import addUploadFeature from './addUploadFeature';
import {
GET_TREE_ROOT_NODES,
GET_TREE_CHILDREN_NODES,
MOVE_NODE,
} from 'ra-tree-core';
import { CREATE } from 'ra-core';

const dataProvider = jsonRestProvider(data, true);
const uploadCapableDataProvider = addUploadFeature(dataProvider);

const dataProviderWithTree = async (type, resource, params) => {
if (type === GET_TREE_ROOT_NODES) {
return dataProvider('GET_LIST', resource, {
filter: { parent_id: null },
sort: { field: 'position', order: 'ASC' },
pagination: { page: 1, perPage: 1000 },
});
}

if (type === GET_TREE_CHILDREN_NODES) {
return dataProvider('GET_LIST', resource, {
filter: { parent_id: params.id },
sort: { field: 'position', order: 'ASC' },
pagination: { page: 1, perPage: 1000 },
});
}

if (type === MOVE_NODE) {
// Fetch all nodes with the same parent so that we can update their position too
const { data } = await dataProvider('GET_LIST', resource, {
filter: { parent_id: params.data.parent_id },
sort: { field: 'position', order: 'ASC' },
pagination: { page: 1, perPage: 1000 },
});

await Promise.all(
data.map(node => {
// If this node is before the moved node, we have nothing to do
if (node.position < params.data.position) {
return Promise.resolve();
}
// Otherwise, update its position according to the moved node
return dataProvider('UPDATE', resource, {
id: node.id,
data: {
position: node.position + 1,
},
});
})
);

// Finally, move the node as requested
return dataProvider('UPDATE', resource, {
id: params.data.id,
data: params.data,
});
}

if (type === CREATE && resource === 'tags') {
// The new node may have been inserted at a position previously occupied by another node
// Fetch all nodes with the same parent so that we can update their position too
const { data } = await dataProvider('GET_LIST', resource, {
filter: { parent_id: params.data.parent_id },
sort: { field: 'position', order: 'ASC' },
pagination: { page: 1, perPage: 1000 },
});

await Promise.all(
data.map(node => {
// If this node is before the new node, we have nothing to do
if (node.position < params.data.position) {
return Promise.resolve();
}
// Otherwise, update its position according to the new node
return dataProvider('UPDATE', resource, {
id: node.id,
data: {
position: node.position + 1,
},
});
})
);

// Finally, create the new node as requested
return dataProvider(type, resource, params);
}

return dataProvider(type, resource, params);
};

const uploadCapableDataProvider = addUploadFeature(dataProviderWithTree);

const sometimesFailsDataProvider = (type, resource, params) =>
new Promise((resolve, reject) => {
// add rejection by type or resource here for tests, e.g.
Expand All @@ -13,6 +101,7 @@ const sometimesFailsDataProvider = (type, resource, params) =>
// }
return resolve(uploadCapableDataProvider(type, resource, params));
});

const delayedDataProvider = (type, resource, params) =>
new Promise(resolve =>
setTimeout(
Expand Down
20 changes: 20 additions & 0 deletions examples/simple/src/tags/TagCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint react/jsx-key: off */
import React from 'react';
import {
Create,
SimpleForm,
TextField,
TextInput,
required,
} from 'react-admin';

const TagCreate = props => (
<Create {...props}>
<SimpleForm redirect="list">
<TextField source="id" />
<TextInput source="name" validate={[required()]} />
</SimpleForm>
</Create>
);

export default TagCreate;
2 changes: 1 addition & 1 deletion examples/simple/src/tags/TagEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Edit, SimpleForm, TextField, TextInput, required } from 'react-admin';

const TagEdit = props => (
<Edit {...props}>
<SimpleForm>
<SimpleForm redirect="list">
<TextField source="id" />
<TextInput source="name" validate={[required()]} />
</SimpleForm>
Expand Down
65 changes: 29 additions & 36 deletions examples/simple/src/tags/TagList.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,41 @@
import React from 'react';
import { TextField } from 'react-admin';
import {
DeleteButton,
EditButton,
List,
SaveButton,
ShowButton,
TextInput,
} from 'react-admin';
import {
DragPreview,
IgnoreFormProps,
NodeForm,
AddChildNodeMenuItem,
AddNodeAfterMenuItem,
AddNodeBeforeMenuItem,
DeleteMenuItem,
EditMenuItem,
Tree,
NodeActions,
TreeNode,
TreeList,
TreeNodeActions,
TreeNodeActionsMenu,
} from 'ra-tree-ui-materialui';

const TagDragPreview = props => (
<DragPreview {...props}>{({ node }) => node.record.name}</DragPreview>
const TagNodeActions = props => (
<TreeNodeActions {...props}>
<TreeNodeActionsMenu {...props}>
<AddChildNodeMenuItem />
<AddNodeBeforeMenuItem />
<AddNodeAfterMenuItem />
<EditMenuItem />
<DeleteMenuItem />
</TreeNodeActionsMenu>
</TreeNodeActions>
);

const CustomNodeActions = props => (
<NodeActions {...props}>
<SaveButton variant="flat" />
<IgnoreFormProps>
<EditButton />
<ShowButton />
<DeleteButton />
</IgnoreFormProps>
</NodeActions>
);
// Disallow dragging of items without parent (top level items)
const canDrag = record => !!record.parent_id;

const TagList = props => (
<List {...props} perPage={1000}>
<Tree
allowDropOnRoot
enableDragAndDrop
parentSource="parent_id"
dragPreviewComponent={TagDragPreview}
>
<NodeForm actions={<CustomNodeActions />}>
<TextInput source="name" />
</NodeForm>
</Tree>
</List>
<Tree positionSource="position" {...props}>
<TreeList>
<TreeNode actions={<TagNodeActions />} canDrag={canDrag}>
<TextField source="name" />
</TreeNode>
</TreeList>
</Tree>
);

export default TagList;
2 changes: 2 additions & 0 deletions examples/simple/src/tags/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import TagCreate from './TagCreate';
import TagEdit from './TagEdit';
import TagList from './TagList';
import TagShow from './TagShow';

export default {
create: TagCreate,
edit: TagEdit,
list: TagList,
show: TagShow,
Expand Down
Loading

0 comments on commit 45f2f0a

Please sign in to comment.