Skip to content

Commit

Permalink
Add dxGantt widget
Browse files Browse the repository at this point in the history
  • Loading branch information
kruglikovstepan committed Jul 2, 2019
1 parent 7e09a64 commit 128634f
Show file tree
Hide file tree
Showing 22 changed files with 717 additions and 3 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Expand Up @@ -289,6 +289,7 @@
"fullscreen",
"func",
"funcs",
"gantt",
"gaussian",
"geo",
"geocode",
Expand Down
6 changes: 6 additions & 0 deletions build/gulp/modules_metadata.json
Expand Up @@ -356,6 +356,12 @@
"default": { "path": "ui.dxGallery", "isWidget": true }
}
},
{
"name": "ui/gantt",
"exports": {
"default": { "path": "ui.dxGantt", "isWidget": true }
}
},
{
"name": "ui/html_editor",
"exports": {
Expand Down
4 changes: 3 additions & 1 deletion build/gulp/npm.js
Expand Up @@ -43,12 +43,14 @@ var DIST_GLOBS = [
'!artifacts/js/jszip*',
'!artifacts/js/dx.custom*',
'!artifacts/js/dx-diagram*',
'!artifacts/js/dx-gantt*',
'!artifacts/ts/jquery*',
'!artifacts/ts/knockout*',
'!artifacts/ts/globalize*',
'!artifacts/ts/cldr*',
'!artifacts/css/dx*legacy*.css',
'!artifacts/css/dx-diagram.*'
'!artifacts/css/dx-diagram.*',
'!artifacts/css/dx-gantt.*'
];

var MODULES = require('./modules_metadata.json');
Expand Down
6 changes: 6 additions & 0 deletions build/gulp/vendor.js
Expand Up @@ -41,12 +41,18 @@ const JS_VENDORS = [
},
{
path: '/devexpress-diagram/dist/dx-@(diagram|diagram.min).js'
},
{
path: '/devexpress-gantt/dist/dx-@(gantt|gantt.min).js'
}
];

const CSS_VENDORS = [
{
path: '/devexpress-diagram/dist/dx-@(diagram|diagram.min).css'
},
{
path: '/devexpress-gantt/dist/dx-@(gantt|gantt.min).css'
}
];

Expand Down
3 changes: 2 additions & 1 deletion build/style-compiler/LessRegistry.cs
Expand Up @@ -183,7 +183,8 @@ static string[] GenerateWidgetsLessFileList(string themeName)
"htmlEditor",
"floatingActionButton",
"fileManager",
"diagram"
"diagram",
"gantt"
});

// Non-themeable components that have only common styles
Expand Down
1 change: 1 addition & 0 deletions js/bundles/modules/parts/widgets-web.js
Expand Up @@ -18,4 +18,5 @@ ui.dxTreeView = require("../../../ui/tree_view");
ui.dxFilterBuilder = require("../../../ui/filter_builder");
ui.dxFileManager = require("../../../ui/file_manager");
ui.dxDiagram = require("../../../ui/diagram");
ui.dxGantt = require("../../../ui/gantt");
/// BUNDLER_PARTS_END
7 changes: 7 additions & 0 deletions js/ui/gantt.js
@@ -0,0 +1,7 @@
/**
* @name dxGantt
* @inherits Widget
* @module ui/gantt
* @export default
*/
module.exports = require("./gantt/ui.gantt");
25 changes: 25 additions & 0 deletions js/ui/gantt/gantt_importer.js
@@ -0,0 +1,25 @@
import Errors from "../widget/ui.errors";
import { getWindow } from "../../core/utils/window";

let ganttViewCore;

function getGanttViewCore() {
if(!ganttViewCore) {
ganttViewCore = requestGantt();
}

return ganttViewCore;
}

function requestGantt() {
const window = getWindow();
const ganttViewCore = window && window.DevExpress && window.DevExpress.Gantt || require("devexpress-gantt");

if(!ganttViewCore) {
throw Errors.Error("E1041", "devexpress-gantt");
}

return ganttViewCore;
}

export { getGanttViewCore };
150 changes: 150 additions & 0 deletions js/ui/gantt/ui.gantt.js
@@ -0,0 +1,150 @@
import $ from "../../core/renderer";
import Widget from "../widget/ui.widget";
import registerComponent from "../../core/component_registrator";
import { GanttView } from "./ui.gantt.view";
import dxTreeList from "../tree_list";
import { extend } from "../../core/utils/extend";

const GANTT_CLASS = "dx-gantt";
const GANTT_SPLITTER_CLASS = "dx-gantt-splitter";
const GANTT_VIEW_CLASS = "dx-gantt-view";

class Gantt extends Widget {
_initMarkup() {
super._initMarkup();
this.$element().addClass(GANTT_CLASS);

this._$treeList = $("<div>")
.appendTo(this.$element());
this._$splitter = $("<div>")
.addClass(GANTT_SPLITTER_CLASS)
.appendTo(this.$element());
}

_render() {
this._treeList = this._createComponent(this._$treeList, dxTreeList, {
dataSource: this.option("tasks"),
width: "100%",
selection: { mode: "single" },
sorting: { mode: "none" },
scrolling: { showScrollbar: "never", mode: "standard" },
allowColumnResizing: true,
autoExpandAll: true,
showRowLines: true,
onContentReady: (e) => { this._onTreeListContentReady(e); },
onSelectionChanged: (e) => this._ganttView._selectTask(e.currentSelectedRowKeys[0]),
onRowCollapsed: () => this._updateGanttView(),
onRowExpanded: () => this._updateGanttView()
});
}

_initGanttView() {
if(this._ganttView) {
return;
}
const $ganttView = $("<div>")
.addClass(GANTT_VIEW_CLASS)
.appendTo(this.$element());

this._ganttView = this._createComponent($ganttView, GanttView, {
height: this._treeList._$element.get(0).offsetHeight,
tasks: this._getTasks(),
dependencies: this.option("dependencies"),
resources: this.option("resources"),
resourceAssignments: this.option("resourceAssignments"),
onSelectionChanged: (e) => { this._onGanttViewSelectionChanged(e); },
onScroll: (e) => { this._onGanttViewScroll(e); }
});
}

_onTreeListContentReady(e) {
if(e.component.getDataSource()) {
this._initGanttView();
this._initScrollSync(e.component);
}
}
_onGanttViewSelectionChanged(e) {
this._treeList.option("selectedRowKeys", [e.id]);
}
_onGanttViewScroll(e) {
const treeListScrollable = this._treeList.getScrollable();
if(treeListScrollable) {
const diff = e.scrollableElement.scrollTop - treeListScrollable.scrollTop();
if(diff !== 0) {
treeListScrollable.scrollBy(diff);
}
}
}
_onTreeListScroll(e) {
const ganttViewScrollable = this._ganttView._getScrollable();
if(ganttViewScrollable.scrollTop !== e.scrollOffset.top) {
ganttViewScrollable.scrollTop = e.scrollOffset.top;
}
}

_updateGanttView() {
this._ganttView.option("tasks", this._getTasks());
this._ganttView._update();
}
_getTasks() {
return this._treeList.getVisibleRows().map(r => r.data);
}
_initScrollSync(treeList) {
const scrollable = treeList.getScrollable();
if(scrollable) {
scrollable.off("scroll", (e) => { this._onTreeListScroll(e); });
scrollable.on("scroll", (e) => { this._onTreeListScroll(e); });
}
}

_getDefaultOptions() {
return extend(super._getDefaultOptions(), {
/**
* @name dxGanttOptions.tasks
* @type Array<object>|DataSource|DataSourceOptions
* @default null
*/
tasks: null,
/**
* @name dxGanttOptions.dependencies
* @type Array<object>|DataSource|DataSourceOptions
* @default null
*/
dependencies: null,
/**
* @name dxGanttOptions.resources
* @type Array<object>|DataSource|DataSourceOptions
* @default null
*/
resources: null,
/**
* @name dxGanttOptions.resourceAssignments
* @type Array<object>|DataSource|DataSourceOptions
* @default null
*/
resourceAssignments: null
});
}

_optionChanged(args) {
switch(args.name) {
case "tasks":
// TODO
break;
case "dependencies":
// TODO
break;
case "resources":
// TODO
break;
case "resourceAssignments":
// TODO
break;
default:
super._optionChanged(args);
}
}
}

registerComponent("dxGantt", Gantt);
module.exports = Gantt;
90 changes: 90 additions & 0 deletions js/ui/gantt/ui.gantt.view.js
@@ -0,0 +1,90 @@
import Widget from "../widget/ui.widget";
import { getGanttViewCore } from "./gantt_importer";

const GANTT_VIEW_HEADER_HEIGHT = 48;

export class GanttView extends Widget {
_init() {
super._init();

this._onSelectionChanged = this._createActionByOption("onSelectionChanged");
this._onScroll = this._createActionByOption("onScroll");
}
_initMarkup() {
const { GanttView } = getGanttViewCore();
this._ganttViewCore = new GanttView(this);
this._ganttViewCore.createView();
this._ganttViewCore.setViewType(2);
}

_getScrollable() {
return this._ganttViewCore.taskAreaContainer;
}
_selectTask(id) {
if(this.lastSelectedId !== undefined) {
this._ganttViewCore.unselectTask(parseInt(this.lastSelectedId));
}
this._ganttViewCore.selectTask(id);
this.lastSelectedId = id;
}
_update() {
this._ganttViewCore.loadOptionsFromTreeList();
this._ganttViewCore.resetAndUpdate();
}

// IGanttView
getGanttSize() {
let height = this.option("height") - GANTT_VIEW_HEADER_HEIGHT;
return { width: 700, height: height };
}
getGanttTickSize() {
return { width: 100, height: 34 };
}
getGanttContainer() {
return this.$element().get(0);
}
getGanttViewStartDate() {
const tasks = this.getGanttTasksData();
return tasks.reduce((min, t) => t.start < min ? t.start : min, new Date());
}
getGanttViewEndDate() {
const tasks = this.getGanttTasksData();
return tasks.reduce((max, t) => t.end > max ? t.end : max, new Date());
}
getGanttTasksData() {
return this.option("tasks");
}
getGanttDependenciesData() {
return this.option("dependencies");
}
getGanttResourcesData() {
return this.option("resources");
}
getGanttResourceAssignmentsData() {
return this.option("resourceAssignments");
}
getGanttWorkTimeRules() {
return {};
}
getGanttViewSettings() {
return {};
}
changeGanttTaskSelection(id, selected) {
this._onSelectionChanged({ id: id, selected: selected });
}
onGanttScroll(scrollableElement) {
this._onScroll({ scrollableElement: scrollableElement });
}
areAlternateRowsEnabled() {
return true;
}
areVerticalBordersEnabled() {
return true;
}
areHorizontalBordersEnabled() {
return true;
}
allowSelectTask() {
return true;
}
}
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -28,6 +28,7 @@
"license": "SEE LICENSE IN README.md",
"dependencies": {
"devexpress-diagram": "0.0.29",
"devexpress-gantt": "0.0.3",
"jszip": "^2.0.0 || ^3.0.0",
"quill": "^1.3.6",
"showdown": "^1.8.6",
Expand Down
8 changes: 8 additions & 0 deletions styles/widgets/common/gantt.less
@@ -0,0 +1,8 @@
.dx-gantt {
display: flex;

.dx-gantt-si {
text-align: center;
vertical-align: middle;
}
}

0 comments on commit 128634f

Please sign in to comment.