Skip to content

Commit

Permalink
add drag and drop support for vuejs tree component
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Feb 14, 2017
1 parent ea07de8 commit 48b607d
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 42 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ name | type | description
--- | --- | ---
data | [TreeData](#tree-data-structure)[] | the data of the tree
checkbox | boolean? | show checkbox for node
draggable | boolean? | whether the node is draggable
toggle | (eventData: [EventData](#event-data-structure)) => void | triggered when opening or closing a node
change | (eventData: [EventData](#event-data-structure)) => void | triggered when selecting or deselecting a node
drop | (dropData: [DropData](#drop-data-structure)) => void | triggered when drag a node, then drop it

#### tree data structure

Expand Down Expand Up @@ -113,6 +115,17 @@ type EventData = {
};
```

#### drop structure

```ts
type DropData = {
sourceData: TreeData;
sourcePath: number[];
targetData: TreeData;
targetPath: number[];
};
```

#### features

+ vuejs component
Expand All @@ -125,3 +138,4 @@ type EventData = {
+ highlighted
+ checkbox
+ custom icon or no icon
+ drag and drop
11 changes: 1 addition & 10 deletions demo/angular/index.ejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,7 @@
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../css/style.min.css" />
<style>
.jstree-anchor {
user-select: none;
}
.my-custom-icon {
background-image: url("../tree-icon.png") !important;
background-position: center center !important;
background-size: auto;
}
</style>
<link rel="stylesheet" href="../index.css" />
</head>

<body>
Expand Down
14 changes: 9 additions & 5 deletions demo/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TreeData, TreeNodeState, EventData } from "../dist/common";
import { TreeData, TreeNodeState, EventData, DropPosition } from "../dist/common";

const rawData: Data[] = [
{
Expand Down Expand Up @@ -87,10 +87,14 @@ function standardize(treeData: Data) {
if (treeData.state.highlighted === undefined) {
treeData.state.highlighted = false;
}
if (treeData.children) {
for (const child of treeData.children) {
standardize(child);
}
if (treeData.state.dropPosition === undefined) {
treeData.state.dropPosition = DropPosition.empty;
}
if (treeData.children === undefined) {
treeData.children = [];
}
for (const child of treeData.children) {
standardize(child);
}
}

Expand Down
61 changes: 61 additions & 0 deletions demo/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.jstree-anchor {
user-select: none;
}

.my-custom-icon {
background-image: url("../tree-icon.png") !important;
background-position: center center !important;
background-size: auto;
}

.tree-relative {
position: relative;
}

.tree-marker-1 {
top: 0;
left: 0;
position: absolute;
margin: -5px 0 0 0;
padding: 0;
border-right: 0;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid;
width: 0;
height: 0;
font-size: 0;
line-height: 0;
}

.tree-marker-2 {
top: 12px;
left: 0;
position: absolute;
margin: -5px 0 0 0;
padding: 0;
border-right: 0;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid;
width: 0;
height: 0;
font-size: 0;
line-height: 0;
}

.tree-marker-3 {
top: 24px;
left: 0;
position: absolute;
margin: -5px 0 0 0;
padding: 0;
border-right: 0;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid;
width: 0;
height: 0;
font-size: 0;
line-height: 0;
}
11 changes: 1 addition & 10 deletions demo/react/index.ejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,7 @@
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../css/style.min.css" />
<style>
.jstree-anchor {
user-select: none;
}
.my-custom-icon {
background-image: url("../tree-icon.png") !important;
background-position: center center !important;
background-size: auto;
}
</style>
<link rel="stylesheet" href="../index.css" />
</head>

<body>
Expand Down
17 changes: 7 additions & 10 deletions demo/vue/index.ejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,7 @@
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="../css/style.min.css" />
<style>
.jstree-anchor {
user-select: none;
}
.my-custom-icon {
background-image: url("../tree-icon.png") !important;
background-position: center center !important;
background-size: auto;
}
</style>
<link rel="stylesheet" href="../index.css" />
</head>

<body>
Expand All @@ -27,6 +18,12 @@
:checkbox="true"
@toggle="toggle2(arguments[0])"
@change="change2(arguments[0])"></tree>
<hr/>
<tree :data="data3"
:draggable="true"
@toggle="toggle3(arguments[0])"
@change="change3(arguments[0])"
@drop="drop3(arguments[0])"></tree>
</div>
<script src="../<%=demoVueBundleJs %>" crossOrigin="anonymous" integrity="<%=sri.demoVueBundleJs %>"></script>
</body>
Expand Down
31 changes: 30 additions & 1 deletion demo/vue/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ new Vue({
data,
selectedId: null,
data2: JSON.parse(JSON.stringify(data)),
selectedId2: null,
data3: JSON.parse(JSON.stringify(data)),
};
},
methods: {
Expand All @@ -35,11 +35,40 @@ new Vue({
setSelectionOfTree(eventData.data, !eventData.data.state.selected);
setParentsSelection(this.data2, eventData.path);
},
toggle3(eventData: common.EventData) {
toggle(eventData);
},
change3(this: This, eventData: common.EventData) {
if (!eventData.data.state.selected) {
for (const child of this.data3) {
clearSelectionOfTree(child);
}
}
eventData.data.state.selected = !eventData.data.state.selected;
},
drop3(this: This, dropData: common.DropData) {
if (dropData.targetData.state.dropPosition === common.DropPosition.inside) {
if (dropData.targetData.children) {
dropData.targetData.children.push(JSON.parse(JSON.stringify(dropData.sourceData)));
} else {
dropData.targetData.children = [JSON.parse(JSON.stringify(dropData.sourceData))];
}
} else {
const startIndex = dropData.targetPath[dropData.targetPath.length - 1] + (dropData.targetData.state.dropPosition === common.DropPosition.up ? 0 : 1);
const parent = common.getNodeFromPath(this.data3, dropData.targetPath.slice(0, dropData.targetPath.length - 1));
if (parent && parent.children) {
parent.children!.splice(startIndex, 0, JSON.parse(JSON.stringify(dropData.sourceData)));
} else {
this.data3.splice(startIndex, 0, JSON.parse(JSON.stringify(dropData.sourceData)));
}
}
},
},
});

type This = {
data: common.TreeData[];
selectedId: null | number;
data2: common.TreeData[];
data3: common.TreeData[];
} & Vue;
55 changes: 54 additions & 1 deletion src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type TreeNodeState = {
disabled: boolean;
loading: boolean;
highlighted: boolean;
dropPosition: DropPosition;
};

export type EventData = {
Expand Down Expand Up @@ -65,7 +66,7 @@ export function getNodeClassName(data: TreeData, last: boolean) {
}

export function getAnchorClassName(data: TreeData, hovered: boolean) {
const values = ["jstree-anchor"];
const values = ["jstree-anchor", "tree-relative"];
if (data.state.selected) {
values.push("jstree-clicked");
}
Expand Down Expand Up @@ -98,3 +99,55 @@ export function getRootClassName(checkbox: boolean | undefined) {
export function getIconClassName(icon: string | false | undefined) {
return `jstree-icon jstree-themeicon ${icon ? `${icon} jstree-themeicon-custom` : ""}`;
}

export function getMarkerClassName(data: TreeData) {
return `tree-marker-${data.state.dropPosition}`;
}

export const enum DropPosition {
empty,
up,
inside,
down,
};

export type DropData = {
sourceData: TreeData;
sourcePath: number[];
targetData: TreeData;
targetPath: number[];
};

export function getNodeFromPath(rootData: TreeData[], path: number[]) {
let node: TreeData | null = null;
for (const index of path) {
if (node) {
node = node.children![index];
} else {
node = rootData[index];
}
}
return node!;
}

export function getDropPosition(pageY: number, offsetTop: number) {
const top = pageY - offsetTop;
if (top < 8) {
return DropPosition.up;
} else if (top > 16) {
return DropPosition.down;
} else {
return DropPosition.inside;
}
}

export function clearDropPositionOfTree(tree: TreeData) {
if (tree.state.dropPosition) {
tree.state.dropPosition = DropPosition.empty;
}
if (tree.children) {
for (const child of tree.children) {
clearDropPositionOfTree(child);
}
}
}

0 comments on commit 48b607d

Please sign in to comment.