Skip to content

Commit 40b2c6f

Browse files
authored
Merge pull request #164 from grapoza/drag-and-drop
Add drag-and-drop support
2 parents 1e344c7 + d4ac666 commit 40b2c6f

23 files changed

+1752
-115
lines changed

docsrc/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
This directory contains the sources used to build the GitHub Pages for this project.
44

5-
If you came here looking for the actual documentation, you can find it [here](https://grapoza.github.io/vue-tree/).
5+
If you came here looking for the actual documentation, you can find it on [my github.io page](https://grapoza.github.io/vue-tree/).

docsrc/demos.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,4 +1398,120 @@ In the next example, a treeview has been given a `skin-class` prop value of `gra
13981398
}
13991399
}).$mount('#app-custom-gray');
14001400
</script>
1401+
```
1402+
1403+
<!--- -------------------------------------------------------------------------------------- --->
1404+
### Drag and Drop
1405+
1406+
You can drag a node that has the `draggable` property in a node's `treeNodeSpec` set to `true`. Any node with `allowDrop` set to `true` in the `treeNodeSpec` can accept a drop from any TreeView.
1407+
1408+
```{=html5}
1409+
<details>
1410+
<summary>
1411+
```
1412+
```html
1413+
<tree id="customtree-dnd" :initial-model="model" :model-defaults="modelDefaults"></tree>
1414+
```
1415+
```{=html5}
1416+
</summary>
1417+
```
1418+
<!--- The leading spaces are to render the html aligned correctly --->
1419+
```html
1420+
<div id="app-dnd" class="demo-tree">
1421+
<tree id="customtree-dnd" :initial-model="model" :model-defaults="modelDefaults"></tree>
1422+
</div>
1423+
<script type='module'>
1424+
import TreeView from "@grapoza/vue-tree"
1425+
new Vue({
1426+
components: {
1427+
tree: TreeView
1428+
},
1429+
data() {
1430+
return {
1431+
model: [
1432+
{
1433+
id: "dnd-rootnode",
1434+
label: "Root Node",
1435+
children: [
1436+
{
1437+
id: "child-1",
1438+
label: "Subnode 1"
1439+
},
1440+
{
1441+
id: "child-2",
1442+
label: "Subnode 2"
1443+
}
1444+
]
1445+
}
1446+
],
1447+
modelDefaults: {
1448+
draggable: true,
1449+
allowDrop: true
1450+
}
1451+
};
1452+
}
1453+
}).$mount('#app-dnd');
1454+
</script>
1455+
```
1456+
```{=html5}
1457+
</details>
1458+
```
1459+
1460+
```{=html5}
1461+
<div id="app-dnd" class="demo-tree">
1462+
<h4>Tree 1</h4>
1463+
<tree id="customtree-dnd-1" :initial-model="model1" :model-defaults="modelDefaults"></tree>
1464+
<h4>Tree 2</h4>
1465+
<tree id="customtree-dnd-2" :initial-model="model2" :model-defaults="modelDefaults"></tree>
1466+
</div>
1467+
<script type='module'>
1468+
new Vue({
1469+
components: {
1470+
tree: window['vue-tree']
1471+
},
1472+
data() {
1473+
return {
1474+
model1: [
1475+
{
1476+
id: "dnd-rootnode-1",
1477+
label: "Root Node 1",
1478+
children: [
1479+
{
1480+
id: "child-1",
1481+
label: "Subnode 1"
1482+
},
1483+
{
1484+
id: "child-2",
1485+
label: "Subnode 2"
1486+
}
1487+
]
1488+
}
1489+
],
1490+
model2: [
1491+
{
1492+
id: "dnd-rootnode-2",
1493+
label: "Root Node 2",
1494+
children: [
1495+
{
1496+
id: "child-3",
1497+
label: "Subnode 3"
1498+
},
1499+
{
1500+
id: "child-4",
1501+
label: "Subnode 4"
1502+
}
1503+
]
1504+
}
1505+
],
1506+
modelDefaults: {
1507+
draggable: true,
1508+
allowDrop: true,
1509+
state: {
1510+
expanded: true
1511+
}
1512+
}
1513+
};
1514+
}
1515+
}).$mount('#app-dnd');
1516+
</script>
14011517
```

docsrc/index.md

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@ Features include:
2121
- Skinning
2222
- Asynchronous loading of child nodes
2323
- Follows ARIA guidelines for treeview accessibility
24+
- Drag and drop (single nodes, works between trees)
2425

25-
Planned:
26-
27-
- Icons ([#22](https://github.com/grapoza/vue-tree/issues/22))
28-
- Searching ([#4](https://github.com/grapoza/vue-tree/issues/4))
29-
- Drag n' Drop ([#6](https://github.com/grapoza/vue-tree/issues/6))
26+
For future plans, see the project's [Issues](https://github.com/grapoza/vue-tree/issues) page.
3027

3128
## Installation
3229

@@ -201,6 +198,8 @@ The `treeNodeSpec` property contains any data about the node's capabilities and
201198
| selectable | Boolean | True to allow the node to be selected | `false` | |
202199
| deletable | Boolean | True to allow the node to be deleted | `false` | |
203200
| focusable | Boolean | True to make the node the focus when the treeview is focused | See [Aria](#focusable) for details | |
201+
| draggable | Boolean | True to make this node draggable | `false` | |
202+
| allowDrop | Boolean | True to allow dropping TreeViewNode data onto this node | `false` | |
204203
| expanderTitle | String | The text to use as the title for the expander button | `null` | |
205204
| addChildTitle | String | The text to use as the title for the Add Child button | `null` | |
206205
| deleteTitle | String | The text to use as the title for the Delete button | `null` | |
@@ -272,26 +271,33 @@ If specified, the `modelDefaults` property of the treeview will be merged with n
272271

273272
The display of the treeview can be customized via CSS using the following classes. Class names are organized in a hierarchy, so a containing node's class is the prefix of its child classes.
274273

275-
| Class | Affects |
276-
|:-------------------------------------------|:-------------------------------------------------------------------------------|
277-
| `tree-view` | The top-level tree view list |
278-
| `tree-view-node` | A single node's list item |
279-
| `tree-view-node-self-selected` | A selected node |
280-
| `tree-view-node-self` | The div containing the current node's UI |
281-
| `tree-view-node-self-expander` | The button used to expand the children |
282-
| `tree-view-node-self-expanded` | Applied to the expander button when the node is expanded |
283-
| `tree-view-node-self-expanded-indicator` | The `<i>` element containing the expansion indicator |
284-
| `tree-view-node-self-spacer` | An empty spacer to replace fixed-width elements, _e.g._ the expander or checkbox |
285-
| `tree-view-node-self-label` | The label for the checkbox of checkable nodes |
286-
| `tree-view-node-self-input` | Any type of input field within the tree node |
287-
| `tree-view-node-self-checkbox` | The checkbox |
288-
| `tree-view-node-self-radio` | The radio button |
289-
| `tree-view-node-self-text` | The text for a non-input node |
290-
| `tree-view-node-self-action` | The action buttons (e.g., add child or delete) |
291-
| `tree-view-node-self-add-child-icon` | The `<i>` element containing the add child icon |
292-
| `tree-view-node-self-delete-icon` | The `<i>` element containing the delete icon |
293-
| `tree-view-node-children` | The list of child nodes |
294-
| `tree-view-node-loading` | The placeholder shown when child nodes are loading asynchronously |
274+
| Class | Affects |
275+
|:--------------------------------------------|:-------------------------------------------------------------------------------|
276+
| `tree-view` | The top-level tree view list |
277+
| `tree-view-node` | A single node's list item |
278+
| `tree-view-node-self-selected` | A selected node |
279+
| `tree-view-node-self` | The div containing the current node's UI |
280+
| `tree-view-node-self-expander` | The button used to expand the children |
281+
| `tree-view-node-self-expanded` | Applied to the expander button when the node is expanded |
282+
| `tree-view-node-self-expanded-indicator` | The `<i>` element containing the expansion indicator |
283+
| `tree-view-node-self-spacer` | An empty spacer to replace fixed-width elements, _e.g._ the expander or checkbox |
284+
| `tree-view-node-self-label` | The label for the checkbox of checkable nodes |
285+
| `tree-view-node-self-input` | Any type of input field within the tree node |
286+
| `tree-view-node-self-checkbox` | The checkbox |
287+
| `tree-view-node-self-radio` | The radio button |
288+
| `tree-view-node-self-text` | The text for a non-input node |
289+
| `tree-view-node-self-action` | The action buttons (e.g., add child or delete) |
290+
| `tree-view-node-self-add-child-icon` | The `<i>` element containing the add child icon |
291+
| `tree-view-node-self-delete-icon` | The `<i>` element containing the delete icon |
292+
| `tree-view-node-self-drop-target` | A node has another node dragged over it and can accept drops |
293+
| `tree-view-node-self-child-drop-target` | A node has another node dragged over its child drop target |
294+
| `tree-view-node-self-sibling-drop-target` | Either the previous or next sibling node drop target |
295+
| `tree-view-node-self-sibling-drop-target-hover` | A node has another node dragged over one of the sibling drop targets |
296+
| `tree-view-node-self-prev-target` | The previous sibling node drop target |
297+
| `tree-view-node-self-next-target` | The next sibling node drop target |
298+
| `tree-view-node-children` | The list of child nodes |
299+
| `tree-view-node-loading` | The placeholder shown when child nodes are loading asynchronously |
300+
| `tree-view-node-dragging` | The node is dragged as part of a drag and drop operation |
295301

296302
## Customizing the TreeView
297303

@@ -395,6 +401,20 @@ The method may accept one argument, which will be the model of the node that has
395401
396402
The load method is called once, and after that the children are part of the model and are not reloaded.
397403
404+
## Drag and Drop
405+
406+
A user can drag and drop an individual TreeViewNode. A drag only affects the node where the dragging starts, and has nothing to do with any node selection within the tree. To make a node draggable, the node's `treeNodeSpec.draggable` must be `true`. To make a node accept drops, the node's `treeNodeSpec.allowDrop` must be `true`. Both Move and Copy operations are supported. To copy in most browsers hold down the `Ctrl` key while dragging.
407+
408+
When dropping a node on another node, there are three areas of the target node where a drop can occur. If dropped at the top of the target node in the shaded area then the node will be inserted before the target. If dropped at the bottom of the target node in the shaded area then the node will be inserted after the target. If dropped directly on the of the target node then the node will be inserted as a child of the target. The drop can occur on a node in the same tree or in a different tree as long as the receiving node allows drops.
409+
410+
The drop can also occur anywhere that allows dropping data with the `application/json` or `text/plain` MIME types (_e.g._, a simple text input field or a text editor).
411+
412+
When copying a node the newly created node will have its own unique identifier, will not be the currently focusable node even if the source node was the focusable node.
413+
414+
When moving a node within the same tree, the actual node is moved within the tree data. If the node is copied within the same tree, any function members of the node data (_e.g._, the addChildCallback) are copied.
415+
416+
When a node is moved or copied to a different tree, the node data that passes between trees does not contain any of the functions from the original node data.
417+
398418
## Aria
399419

400420
ARIA Accessibility recommendations have been implemented at a basic level. This means keyboard navigation follows ARIA recommendations, but the component has not been tested with a screen reader and, since many screen readers exhibit different behaviors for treeview controls anyway, it would be expected to fail articulation checks in many cases. Additionally, some recommended keyboard controls are not implemented (e.g., Expand All Nodes, Type-ahead Focusing). When using the component, there are only a couple of things you need to know.

0 commit comments

Comments
 (0)