Skip to content

Commit

Permalink
support checkbox
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Feb 8, 2017
1 parent a3583d9 commit 4c5305e
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 23 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ the source code of the demo: https://github.com/plantain-00/tree-component/tree/
name | type | description
--- | --- | ---
data | [TreeData](#tree-data-structure)[] | the data of the tree
checkbox | boolean? | show checkbox for node
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

Expand Down Expand Up @@ -107,6 +108,7 @@ type TreeNodeState = {
```ts
type EventData = {
data: TreeData;
path: number[];
};
```

Expand All @@ -120,3 +122,4 @@ type EventData = {
+ disabled
+ loading
+ highlighted
+ checkbox
18 changes: 17 additions & 1 deletion demo/angular/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ enableProdMode();

import { Component } from "@angular/core";

import { data, clearSelectionOfTree, toggle } from "../common";
import { data, clearSelectionOfTree, toggle, setSelectionOfTree, setParentsSelection } from "../common";
import * as common from "../../dist/common";

@Component({
Expand All @@ -19,11 +19,19 @@ import * as common from "../../dist/common";
(toggle)="ontoggle($event)"
(change)="onchange($event)"></tree>
selected id: {{selectedId}}
<hr/>
<tree [data]="data2"
[checkbox]="true"
(toggle)="ontoggle2($event)"
(change)="onchange2($event)"></tree>
selected id: {{selectedId2}}
`,
})
export class MainComponent {
data = data;
selectedId: number | null = null;
data2 = JSON.parse(JSON.stringify(data));
selectedId2: number | null = null;
ontoggle(eventData: common.EventData) {
toggle(eventData);
}
Expand All @@ -36,6 +44,14 @@ export class MainComponent {
}
eventData.data.state.selected = !eventData.data.state.selected;
}
ontoggle2(eventData: common.EventData) {
toggle(eventData);
}
onchange2(eventData: common.EventData) {
this.selectedId2 = eventData.data.state.selected ? null : eventData.data.value.id;
setSelectionOfTree(eventData.data, !eventData.data.state.selected);
setParentsSelection(this.data2, eventData.path);
}
}

import { NgModule } from "@angular/core";
Expand Down
34 changes: 31 additions & 3 deletions demo/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ const rawData: Data[] = [
text: "Child node 21",
value: { id: 21 },
},
{
text: "Child node 22",
value: { id: 22 },
},
],
},
];
Expand Down Expand Up @@ -84,8 +88,6 @@ for (const child of rawData) {
standardize(child);
}

console.log(rawData);

export const data: TreeData[] = rawData as any;

export function clearSelectionOfTree(tree: TreeData) {
Expand All @@ -108,7 +110,7 @@ export function toggle(eventData: EventData, next?: () => void) {
if (next) {
next();
}
}, 2000);
}, 1000);
} else {
eventData.data.state.opened = !eventData.data.state.opened;
}
Expand All @@ -117,6 +119,32 @@ export function toggle(eventData: EventData, next?: () => void) {
}
}

export function setSelectionOfTree(tree: TreeData, selected: boolean) {
if (tree.state.selected !== selected) {
tree.state.selected = selected;
}
if (tree.children) {
for (const child of tree.children) {
setSelectionOfTree(child, selected);
}
}
}

export function setParentsSelection(tree: TreeData[], path: number[]) {
const parents: TreeData[] = [];
const parentPath = path.slice(0, path.length - 1);
for (const index of parentPath) {
if (parents.length === 0) {
parents.unshift(tree[index]);
} else {
parents.unshift(parents[0].children![index]);
}
}
for (const parent of parents) {
parent.state.selected = parent.children!.every(child => child.state.selected);
}
}

type Data = {
text: string;
value?: any;
Expand Down
24 changes: 22 additions & 2 deletions demo/react/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Tree } from "../../dist/react";
import { data, clearSelectionOfTree, toggle } from "../common";
import { data, clearSelectionOfTree, toggle, setSelectionOfTree, setParentsSelection } from "../common";
import * as common from "../../dist/common";

class Main extends React.Component<{}, {}> {
data = data;
selectedId: number | null = null;
data2 = JSON.parse(JSON.stringify(data));
selectedId2: number | null = null;

render() {
return (
<div>
<Tree data={data}
<Tree data={this.data}
toggle={(eventData: common.EventData) => this.toggle(eventData)}
change={(eventData: common.EventData) => this.change(eventData)}>
</Tree>
selected id: {this.selectedId}
<hr />
<Tree data={this.data2}
checkbox={true}
toggle={(eventData: common.EventData) => this.toggle2(eventData)}
change={(eventData: common.EventData) => this.change2(eventData)}>
</Tree>
selected id: {this.selectedId2}
</div>
);
}
Expand All @@ -36,6 +45,17 @@ class Main extends React.Component<{}, {}> {
eventData.data.state.selected = !eventData.data.state.selected;
this.setState({ data: this.data });
}
toggle2(eventData: common.EventData) {
toggle(eventData, () => {
this.setState({ data: this.data });
});
}
change2(eventData: common.EventData) {
this.selectedId2 = eventData.data.state.selected ? null : eventData.data.value.id;
setSelectionOfTree(eventData.data, !eventData.data.state.selected);
setParentsSelection(this.data2, eventData.path);
this.setState({ data: this.data });
}
}

ReactDOM.render(<Main />, document.getElementById("container"));
6 changes: 6 additions & 0 deletions demo/vue/index.ejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
@toggle="toggle(arguments[0])"
@change="change(arguments[0])"></tree>
selected id: {{selectedId}}
<hr/>
<tree :data="data2"
:checkbox="true"
@toggle="toggle2(arguments[0])"
@change="change2(arguments[0])"></tree>
selected id: {{selectedId2}}
</div>
<script src="../<%=demoVueBundleJs %>" crossOrigin="anonymous" integrity="<%=sri.demoVueBundleJs %>"></script>
</body>
Expand Down
15 changes: 13 additions & 2 deletions demo/vue/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Vue from "vue";
import "../../dist/vue";
import { data, clearSelectionOfTree, toggle } from "../common";
import { data, clearSelectionOfTree, toggle, setSelectionOfTree, setParentsSelection } from "../common";
import * as common from "../../dist/common";

/* tslint:disable:no-unused-new */
Expand All @@ -11,6 +11,8 @@ new Vue({
return {
data,
selectedId: null,
data2: JSON.parse(JSON.stringify(data)),
selectedId2: null,
};
},
methods: {
Expand All @@ -26,11 +28,20 @@ new Vue({
}
eventData.data.state.selected = !eventData.data.state.selected;
},

toggle2(eventData: common.EventData) {
toggle(eventData);
},
change2(this: This, eventData: common.EventData) {
this.selectedId2 = eventData.data.state.selected ? null : eventData.data.value.id;
setSelectionOfTree(eventData.data, !eventData.data.state.selected);
setParentsSelection(this.data2, eventData.path);
},
},
});

type This = {
data: common.TreeData[];
selectedId: null | number;
data2: common.TreeData[];
selectedId2: null | number;
} & Vue;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tree-component",
"version": "1.2.0",
"version": "1.3.0",
"description": "A reactjs, angular2 and vuejs tree component.",
"main": "index.js",
"scripts": {
Expand Down
30 changes: 26 additions & 4 deletions src/angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import * as common from "./common";
selector: "node",
template: `
<li role="treeitem" [class]="nodeClassName">
<i class="jstree-icon jstree-ocl" role="presentation" (click)="ontoggle()"></i><a [class]="anchorClassName" href="javascript:void(0)" (click)="onchange()" (dblclick)="ontoggle()" (mouseenter)="hover(true)" (mouseleave)="hover(false)"><i class="jstree-icon jstree-themeicon" role="presentation"></i>{{data.text}}</a>
<i class="jstree-icon jstree-ocl" role="presentation" (click)="ontoggle()"></i><a [class]="anchorClassName" href="javascript:void(0)" (click)="onchange()" (dblclick)="ontoggle()" (mouseenter)="hover(true)" (mouseleave)="hover(false)"><i *ngIf="checkbox" [class]="checkboxClassName" role="presentation"></i><i class="jstree-icon jstree-themeicon" role="presentation"></i>{{data.text}}</a>
<ul *ngIf="data.children" role="group" class="jstree-children">
<node *ngFor="let child of data.children; let i = index"
[data]="child"
[last]="i === data.children.length - 1"
[checkbox]="checkbox"
[path]="geChildPath(i)"
(toggle)="ontoggle($event)"
(change)="onchange($event)">
</node>
Expand All @@ -22,6 +24,10 @@ export class NodeComponent {
data: common.TreeData;
@Input()
last: boolean;
@Input()
checkbox: boolean;
@Input()
path: number[];

@Output()
toggle = new EventEmitter<common.EventData>();
Expand All @@ -39,6 +45,14 @@ export class NodeComponent {
return common.getAnchorClassName(this.data, this.hovered);
}

get checkboxClassName() {
return common.getCheckboxClassName(this.data);
}

geChildPath(index: number) {
return this.path.concat(index);
}

hover(hovered: boolean) {
this.hovered = hovered;
}
Expand All @@ -47,7 +61,7 @@ export class NodeComponent {
this.toggle.emit(eventData);
} else {
if (this.data.children && this.data.children.length > 0) {
this.toggle.emit({ data: this.data });
this.toggle.emit({ data: this.data, path: this.path });
}
}
}
Expand All @@ -60,7 +74,7 @@ export class NodeComponent {
}

this.doubleClick.onclick(() => {
this.change.emit({ data: this.data });
this.change.emit({ data: this.data, path: this.path });
});
}
}
Expand All @@ -69,11 +83,13 @@ export class NodeComponent {
@Component({
selector: "tree",
template: `
<div class="jstree jstree-default jstree-default-dark" role="tree">
<div [class]="rootClassName" role="tree">
<ul class="jstree-container-ul jstree-children" role="group">
<node *ngFor="let child of data; let i = index"
[data]="child"
[last]="i === data.length - 1"
[checkbox]="checkbox"
[path]="[i]"
(toggle)="ontoggle($event)"
(change)="onchange($event)"></node>
</ul>
Expand All @@ -83,12 +99,18 @@ export class NodeComponent {
export class TreeComponent {
@Input()
data: common.TreeData[];
@Input()
checkbox?: boolean;

@Output()
toggle = new EventEmitter<common.EventData>();
@Output()
change = new EventEmitter<common.EventData>();

get rootClassName() {
return common.getRootClassName(this.checkbox);
}

ontoggle(eventData: common.EventData) {
this.toggle.emit(eventData);
}
Expand Down
15 changes: 15 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type TreeNodeState = {

export type EventData = {
data: TreeData;
path: number[];
};

import "tslib";
Expand Down Expand Up @@ -78,3 +79,17 @@ export function getAnchorClassName(data: TreeData, hovered: boolean) {
}
return values.join(" ");
}

export function getCheckboxClassName(data: TreeData) {
const values = ["jstree-icon", "jstree-checkbox"];
if (data.children
&& data.children.some(child => child.state.selected)
&& data.children.some(child => !child.state.selected)) {
values.push("jstree-undetermined");
}
return values.join(" ");
}

export function getRootClassName(checkbox: boolean | undefined) {
return `jstree jstree-default jstree-default-dark ${checkbox ? "jstree-checkbox-selection jstree-checkbox-no-clicked" : ""}`;
}
Loading

0 comments on commit 4c5305e

Please sign in to comment.