|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934 |
- import date_utils from './date_utils';
- import { $, createSVG } from './svg_utils';
- import Bar from './bar';
- import Arrow from './arrow';
- import Popup from './popup';
-
- import './gantt.scss';
-
- const VIEW_MODE = {
- QUARTER_DAY: 'Quarter Day',
- HALF_DAY: 'Half Day',
- DAY: 'Day',
- WEEK: 'Week',
- MONTH: 'Month',
- YEAR: 'Year',
- };
-
- export default class Gantt {
- constructor(wrapper, tasks, options) {
- this.setup_wrapper(wrapper);
- this.setup_options(options);
- this.setup_tasks(tasks);
- // initialize with default view mode
- this.change_view_mode();
- this.bind_events();
- }
-
- setup_wrapper(element) {
- let svg_element, wrapper_element;
-
- // CSS Selector is passed
- if (typeof element === 'string') {
- element = document.querySelector(element);
- }
-
- // get the SVGElement
- if (element instanceof HTMLElement) {
- wrapper_element = element;
- svg_element = element.querySelector('svg');
- } else if (element instanceof SVGElement) {
- svg_element = element;
- } else {
- throw new TypeError(
- 'XhiveFramework Gantt only supports usage of a string CSS selector,' +
- " HTML DOM element or SVG DOM element for the 'element' parameter"
- );
- }
-
- // svg element
- if (!svg_element) {
- // create it
- this.$svg = createSVG('svg', {
- append_to: wrapper_element,
- class: 'gantt',
- });
- } else {
- this.$svg = svg_element;
- this.$svg.classList.add('gantt');
- }
-
- // wrapper element
- this.$container = document.createElement('div');
- this.$container.classList.add('gantt-container');
-
- const parent_element = this.$svg.parentElement;
- parent_element.appendChild(this.$container);
- this.$container.appendChild(this.$svg);
-
- // popup wrapper
- this.popup_wrapper = document.createElement('div');
- this.popup_wrapper.classList.add('popup-wrapper');
- this.$container.appendChild(this.popup_wrapper);
- }
-
- setup_options(options) {
- const default_options = {
- header_height: 50,
- column_width: 30,
- step: 24,
- view_modes: [...Object.values(VIEW_MODE)],
- bar_height: 20,
- bar_corner_radius: 3,
- arrow_curve: 5,
- padding: 18,
- view_mode: 'Day',
- date_format: 'YYYY-MM-DD',
- popup_trigger: 'click',
- custom_popup_html: null,
- language: 'en',
- };
- this.options = Object.assign({}, default_options, options);
- }
-
- setup_tasks(tasks) {
- // prepare tasks
- this.tasks = tasks.map((task, i) => {
- // convert to Date objects
- task._start = date_utils.parse(task.start);
- task._end = date_utils.parse(task.end);
-
- // make task invalid if duration too large
- if (date_utils.diff(task._end, task._start, 'year') > 10) {
- task.end = null;
- }
-
- // cache index
- task._index = i;
-
- // invalid dates
- if (!task.start && !task.end) {
- const today = date_utils.today();
- task._start = today;
- task._end = date_utils.add(today, 2, 'day');
- }
-
- if (!task.start && task.end) {
- task._start = date_utils.add(task._end, -2, 'day');
- }
-
- if (task.start && !task.end) {
- task._end = date_utils.add(task._start, 2, 'day');
- }
-
- // if hours is not set, assume the last day is full day
- // e.g: 2018-09-09 becomes 2018-09-09 23:59:59
- const task_end_values = date_utils.get_date_values(task._end);
- if (task_end_values.slice(3).every((d) => d === 0)) {
- task._end = date_utils.add(task._end, 24, 'hour');
- }
-
- // invalid flag
- if (!task.start || !task.end) {
- task.invalid = true;
- }
-
- // dependencies
- if (typeof task.dependencies === 'string' || !task.dependencies) {
- let deps = [];
- if (task.dependencies) {
- deps = task.dependencies
- .split(',')
- .map((d) => d.trim())
- .filter((d) => d);
- }
- task.dependencies = deps;
- }
-
- // uids
- if (!task.id) {
- task.id = generate_id(task);
- }
-
- return task;
- });
-
- this.setup_dependencies();
- }
-
- setup_dependencies() {
- this.dependency_map = {};
- for (let t of this.tasks) {
- for (let d of t.dependencies) {
- this.dependency_map[d] = this.dependency_map[d] || [];
- this.dependency_map[d].push(t.id);
- }
- }
- }
-
- refresh(tasks) {
- this.setup_tasks(tasks);
- this.change_view_mode();
- }
-
- change_view_mode(mode = this.options.view_mode) {
- this.update_view_scale(mode);
- this.setup_dates();
- this.render();
- // fire viewmode_change event
- this.trigger_event('view_change', [mode]);
- }
-
- update_view_scale(view_mode) {
- this.options.view_mode = view_mode;
-
- if (view_mode === VIEW_MODE.DAY) {
- this.options.step = 24;
- this.options.column_width = 38;
- } else if (view_mode === VIEW_MODE.HALF_DAY) {
- this.options.step = 24 / 2;
- this.options.column_width = 38;
- } else if (view_mode === VIEW_MODE.QUARTER_DAY) {
- this.options.step = 24 / 4;
- this.options.column_width = 38;
- } else if (view_mode === VIEW_MODE.WEEK) {
- this.options.step = 24 * 7;
- this.options.column_width = 140;
- } else if (view_mode === VIEW_MODE.MONTH) {
- this.options.step = 24 * 30;
- this.options.column_width = 120;
- } else if (view_mode === VIEW_MODE.YEAR) {
- this.options.step = 24 * 365;
- this.options.column_width = 120;
- }
- }
-
- setup_dates() {
- this.setup_gantt_dates();
- this.setup_date_values();
- }
-
- setup_gantt_dates() {
- this.gantt_start = this.gantt_end = null;
-
- for (let task of this.tasks) {
- // set global start and end date
- if (!this.gantt_start || task._start < this.gantt_start) {
- this.gantt_start = task._start;
- }
- if (!this.gantt_end || task._end > this.gantt_end) {
- this.gantt_end = task._end;
- }
- }
-
- this.gantt_start = date_utils.start_of(this.gantt_start, 'day');
- this.gantt_end = date_utils.start_of(this.gantt_end, 'day');
-
- // add date padding on both sides
- if (this.view_is([VIEW_MODE.QUARTER_DAY, VIEW_MODE.HALF_DAY])) {
- this.gantt_start = date_utils.add(this.gantt_start, -7, 'day');
- this.gantt_end = date_utils.add(this.gantt_end, 7, 'day');
- } else if (this.view_is(VIEW_MODE.MONTH)) {
- this.gantt_start = date_utils.start_of(this.gantt_start, 'year');
- this.gantt_end = date_utils.add(this.gantt_end, 1, 'year');
- } else if (this.view_is(VIEW_MODE.YEAR)) {
- this.gantt_start = date_utils.add(this.gantt_start, -2, 'year');
- this.gantt_end = date_utils.add(this.gantt_end, 2, 'year');
- } else {
- this.gantt_start = date_utils.add(this.gantt_start, -1, 'month');
- this.gantt_end = date_utils.add(this.gantt_end, 1, 'month');
- }
- }
-
- setup_date_values() {
- this.dates = [];
- let cur_date = null;
-
- while (cur_date === null || cur_date < this.gantt_end) {
- if (!cur_date) {
- cur_date = date_utils.clone(this.gantt_start);
- } else {
- if (this.view_is(VIEW_MODE.YEAR)) {
- cur_date = date_utils.add(cur_date, 1, 'year');
- } else if (this.view_is(VIEW_MODE.MONTH)) {
- cur_date = date_utils.add(cur_date, 1, 'month');
- } else {
- cur_date = date_utils.add(
- cur_date,
- this.options.step,
- 'hour'
- );
- }
- }
- this.dates.push(cur_date);
- }
- }
-
- bind_events() {
- this.bind_grid_click();
- this.bind_bar_events();
- }
-
- render() {
- this.clear();
- this.setup_layers();
- this.make_grid();
- this.make_dates();
- this.make_bars();
- this.make_arrows();
- this.map_arrows_on_bars();
- this.set_width();
- this.set_scroll_position();
- }
-
- setup_layers() {
- this.layers = {};
- const layers = ['grid', 'date', 'arrow', 'progress', 'bar', 'details'];
- // make group layers
- for (let layer of layers) {
- this.layers[layer] = createSVG('g', {
- class: layer,
- append_to: this.$svg,
- });
- }
- }
-
- make_grid() {
- this.make_grid_background();
- this.make_grid_rows();
- this.make_grid_header();
- this.make_grid_ticks();
- this.make_grid_highlights();
- }
-
- make_grid_background() {
- const grid_width = this.dates.length * this.options.column_width;
- const grid_height =
- this.options.header_height +
- this.options.padding +
- (this.options.bar_height + this.options.padding) *
- this.tasks.length;
-
- createSVG('rect', {
- x: 0,
- y: 0,
- width: grid_width,
- height: grid_height,
- class: 'grid-background',
- append_to: this.layers.grid,
- });
-
- $.attr(this.$svg, {
- height: grid_height + this.options.padding + 100,
- width: '100%',
- });
- }
-
- make_grid_rows() {
- const rows_layer = createSVG('g', { append_to: this.layers.grid });
- const lines_layer = createSVG('g', { append_to: this.layers.grid });
-
- const row_width = this.dates.length * this.options.column_width;
- const row_height = this.options.bar_height + this.options.padding;
-
- let row_y = this.options.header_height + this.options.padding / 2;
-
- for (let task of this.tasks) {
- createSVG('rect', {
- x: 0,
- y: row_y,
- width: row_width,
- height: row_height,
- class: 'grid-row',
- append_to: rows_layer,
- });
-
- createSVG('line', {
- x1: 0,
- y1: row_y + row_height,
- x2: row_width,
- y2: row_y + row_height,
- class: 'row-line',
- append_to: lines_layer,
- });
-
- row_y += this.options.bar_height + this.options.padding;
- }
- }
-
- make_grid_header() {
- const header_width = this.dates.length * this.options.column_width;
- const header_height = this.options.header_height + 10;
- createSVG('rect', {
- x: 0,
- y: 0,
- width: header_width,
- height: header_height,
- class: 'grid-header',
- append_to: this.layers.grid,
- });
- }
-
- make_grid_ticks() {
- let tick_x = 0;
- let tick_y = this.options.header_height + this.options.padding / 2;
- let tick_height =
- (this.options.bar_height + this.options.padding) *
- this.tasks.length;
-
- for (let date of this.dates) {
- let tick_class = 'tick';
- // thick tick for monday
- if (this.view_is(VIEW_MODE.DAY) && date.getDate() === 1) {
- tick_class += ' thick';
- }
- // thick tick for first week
- if (
- this.view_is(VIEW_MODE.WEEK) &&
- date.getDate() >= 1 &&
- date.getDate() < 8
- ) {
- tick_class += ' thick';
- }
- // thick ticks for quarters
- if (
- this.view_is(VIEW_MODE.MONTH) &&
- (date.getMonth() + 1) % 3 === 0
- ) {
- tick_class += ' thick';
- }
-
- createSVG('path', {
- d: `M ${tick_x} ${tick_y} v ${tick_height}`,
- class: tick_class,
- append_to: this.layers.grid,
- });
-
- if (this.view_is(VIEW_MODE.MONTH)) {
- tick_x +=
- (date_utils.get_days_in_month(date) *
- this.options.column_width) /
- 30;
- } else {
- tick_x += this.options.column_width;
- }
- }
- }
-
- make_grid_highlights() {
- // highlight today's date
- if (this.view_is(VIEW_MODE.DAY)) {
- const x =
- (date_utils.diff(date_utils.today(), this.gantt_start, 'hour') /
- this.options.step) *
- this.options.column_width;
- const y = 0;
-
- const width = this.options.column_width;
- const height =
- (this.options.bar_height + this.options.padding) *
- this.tasks.length +
- this.options.header_height +
- this.options.padding / 2;
-
- createSVG('rect', {
- x,
- y,
- width,
- height,
- class: 'today-highlight',
- append_to: this.layers.grid,
- });
- }
- }
-
- make_dates() {
- for (let date of this.get_dates_to_draw()) {
- createSVG('text', {
- x: date.lower_x,
- y: date.lower_y,
- innerHTML: date.lower_text,
- class: 'lower-text',
- append_to: this.layers.date,
- });
-
- if (date.upper_text) {
- const $upper_text = createSVG('text', {
- x: date.upper_x,
- y: date.upper_y,
- innerHTML: date.upper_text,
- class: 'upper-text',
- append_to: this.layers.date,
- });
-
- // remove out-of-bound dates
- if (
- $upper_text.getBBox().x2 > this.layers.grid.getBBox().width
- ) {
- $upper_text.remove();
- }
- }
- }
- }
-
- get_dates_to_draw() {
- let last_date = null;
- const dates = this.dates.map((date, i) => {
- const d = this.get_date_info(date, last_date, i);
- last_date = date;
- return d;
- });
- return dates;
- }
-
- get_date_info(date, last_date, i) {
- if (!last_date) {
- last_date = date_utils.add(date, 1, 'year');
- }
- const date_text = {
- 'Quarter Day_lower': date_utils.format(
- date,
- 'HH',
- this.options.language
- ),
- 'Half Day_lower': date_utils.format(
- date,
- 'HH',
- this.options.language
- ),
- Day_lower:
- date.getDate() !== last_date.getDate()
- ? date_utils.format(date, 'D', this.options.language)
- : '',
- Week_lower:
- date.getMonth() !== last_date.getMonth()
- ? date_utils.format(date, 'D MMM', this.options.language)
- : date_utils.format(date, 'D', this.options.language),
- Month_lower: date_utils.format(date, 'MMMM', this.options.language),
- Year_lower: date_utils.format(date, 'YYYY', this.options.language),
- 'Quarter Day_upper':
- date.getDate() !== last_date.getDate()
- ? date_utils.format(date, 'D MMM', this.options.language)
- : '',
- 'Half Day_upper':
- date.getDate() !== last_date.getDate()
- ? date.getMonth() !== last_date.getMonth()
- ? date_utils.format(
- date,
- 'D MMM',
- this.options.language
- )
- : date_utils.format(date, 'D', this.options.language)
- : '',
- Day_upper:
- date.getMonth() !== last_date.getMonth()
- ? date_utils.format(date, 'MMMM', this.options.language)
- : '',
- Week_upper:
- date.getMonth() !== last_date.getMonth()
- ? date_utils.format(date, 'MMMM', this.options.language)
- : '',
- Month_upper:
- date.getFullYear() !== last_date.getFullYear()
- ? date_utils.format(date, 'YYYY', this.options.language)
- : '',
- Year_upper:
- date.getFullYear() !== last_date.getFullYear()
- ? date_utils.format(date, 'YYYY', this.options.language)
- : '',
- };
-
- const base_pos = {
- x: i * this.options.column_width,
- lower_y: this.options.header_height,
- upper_y: this.options.header_height - 25,
- };
-
- const x_pos = {
- 'Quarter Day_lower': (this.options.column_width * 4) / 2,
- 'Quarter Day_upper': 0,
- 'Half Day_lower': (this.options.column_width * 2) / 2,
- 'Half Day_upper': 0,
- Day_lower: this.options.column_width / 2,
- Day_upper: (this.options.column_width * 30) / 2,
- Week_lower: 0,
- Week_upper: (this.options.column_width * 4) / 2,
- Month_lower: this.options.column_width / 2,
- Month_upper: (this.options.column_width * 12) / 2,
- Year_lower: this.options.column_width / 2,
- Year_upper: (this.options.column_width * 30) / 2,
- };
-
- return {
- upper_text: date_text[`${this.options.view_mode}_upper`],
- lower_text: date_text[`${this.options.view_mode}_lower`],
- upper_x: base_pos.x + x_pos[`${this.options.view_mode}_upper`],
- upper_y: base_pos.upper_y,
- lower_x: base_pos.x + x_pos[`${this.options.view_mode}_lower`],
- lower_y: base_pos.lower_y,
- };
- }
-
- make_bars() {
- this.bars = this.tasks.map((task) => {
- const bar = new Bar(this, task);
- this.layers.bar.appendChild(bar.group);
- return bar;
- });
- }
-
- make_arrows() {
- this.arrows = [];
- for (let task of this.tasks) {
- let arrows = [];
- arrows = task.dependencies
- .map((task_id) => {
- const dependency = this.get_task(task_id);
- if (!dependency) return;
- const arrow = new Arrow(
- this,
- this.bars[dependency._index], // from_task
- this.bars[task._index] // to_task
- );
- this.layers.arrow.appendChild(arrow.element);
- return arrow;
- })
- .filter(Boolean); // filter falsy values
- this.arrows = this.arrows.concat(arrows);
- }
- }
-
- map_arrows_on_bars() {
- for (let bar of this.bars) {
- bar.arrows = this.arrows.filter((arrow) => {
- return (
- arrow.from_task.task.id === bar.task.id ||
- arrow.to_task.task.id === bar.task.id
- );
- });
- }
- }
-
- set_width() {
- const cur_width = this.$svg.getBoundingClientRect().width;
- const actual_width = this.$svg
- .querySelector('.grid .grid-row')
- .getAttribute('width');
- if (cur_width < actual_width) {
- this.$svg.setAttribute('width', actual_width);
- }
- }
-
- set_scroll_position() {
- const parent_element = this.$svg.parentElement;
- if (!parent_element) return;
-
- const hours_before_first_task = date_utils.diff(
- this.get_oldest_starting_date(),
- this.gantt_start,
- 'hour'
- );
-
- const scroll_pos =
- (hours_before_first_task / this.options.step) *
- this.options.column_width -
- this.options.column_width;
-
- parent_element.scrollLeft = scroll_pos;
- }
-
- bind_grid_click() {
- $.on(
- this.$svg,
- this.options.popup_trigger,
- '.grid-row, .grid-header',
- () => {
- this.unselect_all();
- this.hide_popup();
- }
- );
- }
-
- bind_bar_events() {
- let is_dragging = false;
- let x_on_start = 0;
- let y_on_start = 0;
- let is_resizing_left = false;
- let is_resizing_right = false;
- let parent_bar_id = null;
- let bars = []; // instanceof Bar
- this.bar_being_dragged = null;
-
- function action_in_progress() {
- return is_dragging || is_resizing_left || is_resizing_right;
- }
-
- $.on(this.$svg, 'mousedown', '.bar-wrapper, .handle', (e, element) => {
- const bar_wrapper = $.closest('.bar-wrapper', element);
-
- if (element.classList.contains('left')) {
- is_resizing_left = true;
- } else if (element.classList.contains('right')) {
- is_resizing_right = true;
- } else if (element.classList.contains('bar-wrapper')) {
- is_dragging = true;
- }
-
- bar_wrapper.classList.add('active');
-
- x_on_start = e.offsetX;
- y_on_start = e.offsetY;
-
- parent_bar_id = bar_wrapper.getAttribute('data-id');
- const ids = [
- parent_bar_id,
- ...this.get_all_dependent_tasks(parent_bar_id),
- ];
- bars = ids.map((id) => this.get_bar(id));
-
- this.bar_being_dragged = parent_bar_id;
-
- bars.forEach((bar) => {
- const $bar = bar.$bar;
- $bar.ox = $bar.getX();
- $bar.oy = $bar.getY();
- $bar.owidth = $bar.getWidth();
- $bar.finaldx = 0;
- });
- });
-
- $.on(this.$svg, 'mousemove', (e) => {
- if (!action_in_progress()) return;
- const dx = e.offsetX - x_on_start;
- const dy = e.offsetY - y_on_start;
-
- bars.forEach((bar) => {
- const $bar = bar.$bar;
- $bar.finaldx = this.get_snap_position(dx);
- this.hide_popup();
- if (is_resizing_left) {
- if (parent_bar_id === bar.task.id) {
- bar.update_bar_position({
- x: $bar.ox + $bar.finaldx,
- width: $bar.owidth - $bar.finaldx,
- });
- } else {
- bar.update_bar_position({
- x: $bar.ox + $bar.finaldx,
- });
- }
- } else if (is_resizing_right) {
- if (parent_bar_id === bar.task.id) {
- bar.update_bar_position({
- width: $bar.owidth + $bar.finaldx,
- });
- }
- } else if (is_dragging) {
- bar.update_bar_position({ x: $bar.ox + $bar.finaldx });
- }
- });
- });
-
- document.addEventListener('mouseup', (e) => {
- if (is_dragging || is_resizing_left || is_resizing_right) {
- bars.forEach((bar) => bar.group.classList.remove('active'));
- }
-
- is_dragging = false;
- is_resizing_left = false;
- is_resizing_right = false;
- });
-
- $.on(this.$svg, 'mouseup', (e) => {
- this.bar_being_dragged = null;
- bars.forEach((bar) => {
- const $bar = bar.$bar;
- if (!$bar.finaldx) return;
- bar.date_changed();
- bar.set_action_completed();
- });
- });
-
- this.bind_bar_progress();
- }
-
- bind_bar_progress() {
- let x_on_start = 0;
- let y_on_start = 0;
- let is_resizing = null;
- let bar = null;
- let $bar_progress = null;
- let $bar = null;
-
- $.on(this.$svg, 'mousedown', '.handle.progress', (e, handle) => {
- is_resizing = true;
- x_on_start = e.offsetX;
- y_on_start = e.offsetY;
-
- const $bar_wrapper = $.closest('.bar-wrapper', handle);
- const id = $bar_wrapper.getAttribute('data-id');
- bar = this.get_bar(id);
-
- $bar_progress = bar.$bar_progress;
- $bar = bar.$bar;
-
- $bar_progress.finaldx = 0;
- $bar_progress.owidth = $bar_progress.getWidth();
- $bar_progress.min_dx = -$bar_progress.getWidth();
- $bar_progress.max_dx = $bar.getWidth() - $bar_progress.getWidth();
- });
-
- $.on(this.$svg, 'mousemove', (e) => {
- if (!is_resizing) return;
- let dx = e.offsetX - x_on_start;
- let dy = e.offsetY - y_on_start;
-
- if (dx > $bar_progress.max_dx) {
- dx = $bar_progress.max_dx;
- }
- if (dx < $bar_progress.min_dx) {
- dx = $bar_progress.min_dx;
- }
-
- const $handle = bar.$handle_progress;
- $.attr($bar_progress, 'width', $bar_progress.owidth + dx);
- $.attr($handle, 'points', bar.get_progress_polygon_points());
- $bar_progress.finaldx = dx;
- });
-
- $.on(this.$svg, 'mouseup', () => {
- is_resizing = false;
- if (!($bar_progress && $bar_progress.finaldx)) return;
- bar.progress_changed();
- bar.set_action_completed();
- });
- }
-
- get_all_dependent_tasks(task_id) {
- let out = [];
- let to_process = [task_id];
- while (to_process.length) {
- const deps = to_process.reduce((acc, curr) => {
- acc = acc.concat(this.dependency_map[curr]);
- return acc;
- }, []);
-
- out = out.concat(deps);
- to_process = deps.filter((d) => !to_process.includes(d));
- }
-
- return out.filter(Boolean);
- }
-
- get_snap_position(dx) {
- let odx = dx,
- rem,
- position;
-
- if (this.view_is(VIEW_MODE.WEEK)) {
- rem = dx % (this.options.column_width / 7);
- position =
- odx -
- rem +
- (rem < this.options.column_width / 14
- ? 0
- : this.options.column_width / 7);
- } else if (this.view_is(VIEW_MODE.MONTH)) {
- rem = dx % (this.options.column_width / 30);
- position =
- odx -
- rem +
- (rem < this.options.column_width / 60
- ? 0
- : this.options.column_width / 30);
- } else {
- rem = dx % this.options.column_width;
- position =
- odx -
- rem +
- (rem < this.options.column_width / 2
- ? 0
- : this.options.column_width);
- }
- return position;
- }
-
- unselect_all() {
- [...this.$svg.querySelectorAll('.bar-wrapper')].forEach((el) => {
- el.classList.remove('active');
- });
- }
-
- view_is(modes) {
- if (typeof modes === 'string') {
- return this.options.view_mode === modes;
- }
-
- if (Array.isArray(modes)) {
- return modes.some((mode) => this.options.view_mode === mode);
- }
-
- return false;
- }
-
- get_task(id) {
- return this.tasks.find((task) => {
- return task.id === id;
- });
- }
-
- get_bar(id) {
- return this.bars.find((bar) => {
- return bar.task.id === id;
- });
- }
-
- show_popup(options) {
- if (!this.popup) {
- this.popup = new Popup(
- this.popup_wrapper,
- this.options.custom_popup_html
- );
- }
- this.popup.show(options);
- }
-
- hide_popup() {
- this.popup && this.popup.hide();
- }
-
- trigger_event(event, args) {
- if (this.options['on_' + event]) {
- this.options['on_' + event].apply(null, args);
- }
- }
-
- /**
- * Gets the oldest starting date from the list of tasks
- *
- * @returns Date
- * @memberof Gantt
- */
- get_oldest_starting_date() {
- return this.tasks
- .map((task) => task._start)
- .reduce((prev_date, cur_date) =>
- cur_date <= prev_date ? cur_date : prev_date
- );
- }
-
- /**
- * Clear all elements from the parent svg element
- *
- * @memberof Gantt
- */
- clear() {
- this.$svg.innerHTML = '';
- }
- }
-
- Gantt.VIEW_MODE = VIEW_MODE;
-
- function generate_id(task) {
- return task.name + '_' + Math.random().toString(36).slice(2, 12);
- }
|