From 9caf2322b9552139b1b38f65b8f37af366e0b1a0 Mon Sep 17 00:00:00 2001 From: safwansamsudeen Date: Thu, 5 Dec 2024 11:09:03 +0530 Subject: [PATCH] feat: ignore regions --- src/bar.js | 138 +++++++++++++++++++++++++++----------------- src/defaults.js | 2 +- src/index.js | 149 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 207 insertions(+), 82 deletions(-) diff --git a/src/bar.js b/src/bar.js index fc60db4..dd79357 100644 --- a/src/bar.js +++ b/src/bar.js @@ -29,10 +29,18 @@ export default class Bar { this.compute_duration(); this.corner_radius = this.gantt.options.bar_corner_radius; this.width = this.gantt.config.column_width * this.duration; + this.progress_width = this.gantt.config.column_width * - this.duration * + this.actual_duration * (this.task.progress / 100) || 0; + // Adjust for ignored areas + const progress_area = this.x + this.progress_width; + this.progress_width += + this.gantt.config.ignored_positions.reduce((acc, val) => { + return acc + (val >= this.x && val <= progress_area); + }, 0) * this.gantt.config.column_width; + this.group = createSVG('g', { class: 'bar-wrapper' + @@ -149,7 +157,11 @@ export default class Bar { append_to: this.bar_group, }); const x = - (date_utils.diff(this.task._start, this.gantt.gantt_start, 'hour') / + (date_utils.diff( + this.task._start, + this.gantt.gantt_start, + this.gantt.config.unit, + ) / this.gantt.config.step) * this.gantt.config.column_width; @@ -348,7 +360,8 @@ export default class Bar { 'MMM D', this.gantt.options.language, ); - const subtitle = `${start_date} - ${end_date}
Progress: ${this.task.progress}`; + + const subtitle = `${start_date} - ${end_date} (${this.actual_duration_in_days} days)
Progress: ${this.task.progress}`; this.gantt.show_popup({ x, target_element: this.$bar, @@ -358,14 +371,13 @@ export default class Bar { }); } - update_bar_position({ x = null, width = null }) { + async update_bar_position({ x = null, width = null }) { const bar = this.$bar; + if (x) { - // get all x values of parent task const xs = this.task.dependencies.map((dep) => { return this.gantt.get_bar(dep).$bar.getX(); }); - // child task must not go before parent const valid_x = xs.reduce((_, curr) => { return x >= curr; }, x); @@ -374,17 +386,20 @@ export default class Bar { return; } this.update_attr(bar, 'x', x); + this.x = x; this.$date_highlight.style.left = x + 'px'; } if (width) { this.update_attr(bar, 'width', width); this.$date_highlight.style.width = width + 'px'; } + this.update_label_position(); this.update_handle_position(); + this.date_changed(); + this.compute_duration(); + if (this.gantt.options.show_expected_progress) { - this.date_changed(); - this.compute_duration(); this.update_expected_progressbar_position(); } this.update_progressbar_position(); @@ -448,9 +463,11 @@ export default class Bar { } progress_changed() { - const new_progress = this.compute_progress(); - this.task.progress = new_progress; - this.gantt.trigger_event('progress_change', [this.task, new_progress]); + this.task.progress = this.compute_progress(); + this.gantt.trigger_event('progress_change', [ + this.task, + this.task.progress, + ]); } set_action_completed() { @@ -489,9 +506,20 @@ export default class Bar { } compute_progress() { + this.progress_width = this.$bar_progress.getWidth(); + this.x = this.$bar_progress.getBBox().x; + const progress_area = this.x + this.progress_width; const progress = - (this.$bar_progress.getWidth() / this.$bar.getWidth()) * 100; - return parseInt(progress, 10); + this.progress_width - + this.gantt.config.ignored_positions.reduce((acc, val) => { + return acc + (val >= this.x && val <= progress_area); + }, 0) * + this.gantt.config.column_width; + if (progress < 0) return 0; + const total = + this.$bar.getWidth() - + this.ignored_duration * this.gantt.config.column_width; + return parseInt((progress / total) * 100, 10); } compute_expected_progress() { @@ -548,44 +576,48 @@ export default class Bar { } compute_duration() { + let actual_duration_in_days = 0, + duration_in_days = 0; + for ( + let d = new Date(this.task._start); + d < this.task._end; + d.setDate(d.getDate() + 1) + ) { + duration_in_days++; + if ( + !this.gantt.config.ignored_dates.find( + (k) => k.getTime() === d.getTime(), + ) && + this.gantt.config.ignored_function && + !this.gantt.config.ignored_function(d) + ) { + actual_duration_in_days++; + } + } + this.actual_duration_in_days = actual_duration_in_days; + this.duration = - date_utils.diff( - this.task._end, - this.task._start, + date_utils.convert_scales( + duration_in_days + 'd', this.gantt.config.unit, ) / this.gantt.config.step; + + this.actual_duration = + date_utils.convert_scales( + actual_duration_in_days + 'd', + this.gantt.config.unit, + ) / this.gantt.config.step; + this.ignored_duration = this.duration - this.actual_duration; } get_snap_position(dx) { - let odx = dx, - rem, - position; - - // if (this.gantt.view_is('Week')) { - // rem = dx % (this.gantt.config.column_width / 7); - // position = - // odx - - // rem + - // (rem < this.gantt.config.column_width / 14 - // ? 0 - // : this.gantt.config.column_width / 7); - // } else if (this.gantt.view_is('Month')) { - // rem = dx % (this.gantt.config.column_width / 30); - // position = - // odx - - // rem + - // (rem < this.gantt.config.column_width / 60 - // ? 0 - // : this.gantt.config.column_width / 30); - // } else { - rem = dx % this.gantt.config.column_width; - position = + let rem = odx % this.gantt.config.column_width; + let position = odx - rem + (rem < this.gantt.config.column_width / 2 ? 0 : this.gantt.config.column_width); - // } return position; } @@ -604,7 +636,7 @@ export default class Bar { this.$expected_bar_progress.setAttribute( 'width', this.gantt.config.column_width * - this.duration * + this.actual_duration * (this.expected_progress / 100) || 0, ); } @@ -612,10 +644,18 @@ export default class Bar { update_progressbar_position() { if (this.invalid || this.gantt.options.readonly) return; this.$bar_progress.setAttribute('x', this.$bar.getX()); - this.$bar_progress.setAttribute( - 'width', - this.$bar.getWidth() * (this.task.progress / 100), - ); + let new_width = + this.actual_duration * + this.gantt.config.column_width * + (this.task.progress / 100); + const progress_area = this.x + this.progress_width; + new_width += + this.gantt.config.ignored_positions.reduce((acc, val) => { + return acc + (val >= this.x && val <= progress_area); + }, 0) * this.gantt.config.column_width; + + this.progress_width = new_width; + this.$bar_progress.setAttribute('width', new_width); } update_label_position() { @@ -681,11 +721,3 @@ export default class Bar { } } } - -function isFunction(functionToCheck) { - let getType = {}; - return ( - functionToCheck && - getType.toString.call(functionToCheck) === '[object Function]' - ); -} diff --git a/src/defaults.js b/src/defaults.js index 4025e29..4262358 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -112,7 +112,6 @@ const DEFAULT_OPTIONS = { readonly: false, progress_readonly: false, dates_readonly: false, - highlight_weekend: true, scroll_to: 'start', lines: 'both', auto_move_label: true, @@ -120,6 +119,7 @@ const DEFAULT_OPTIONS = { view_mode_select: false, default_snap: '1d', holiday_highlight: { green: 'weekend' }, + ignore: ['weekend'], }; export { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES }; diff --git a/src/index.js b/src/index.js index 408c9ea..b956d15 100644 --- a/src/index.js +++ b/src/index.js @@ -75,10 +75,31 @@ export default class Gantt { if (custom_mode) this.options = { ...this.options, custom_mode }; this.config = {}; + + this.config.ignored_dates = []; + this.config.ignored_positions = []; + + if (typeof this.options.ignore !== 'function') { + if (typeof this.options.ignore === 'string') + this.options.ignore = [this.options.ignord]; + for (let option of this.options.ignore) { + if (typeof option === 'function') { + this.config.ignored_function = option; + continue; + } + if (typeof option === 'string') { + if (option === 'weekend') + this.config.ignored_function = (d) => + d.getDay() == 6 || d.getDay() == 0; + else this.config.ignored_dates.push(new Date(option + ' ')); + } + } + } else { + this.config.ignored_function = this.options.ignore; + } } setup_tasks(tasks) { - // prepare tasks this.tasks = tasks .map((task, i) => { if (!task.start) { @@ -278,8 +299,8 @@ export default class Gantt { this.setup_layers(); this.make_grid(); this.make_dates(); - this.make_bars(); this.make_grid_extras(); + this.make_bars(); this.make_arrows(); this.map_arrows_on_bars(); this.set_width(); @@ -533,13 +554,12 @@ export default class Gantt { } } - highlightWeekends() { + highlightHolidays() { for (let color in this.options.holiday_highlight) { let check_highlight = this.options.holiday_highlight[color]; if (check_highlight === 'weekend') check_highlight = (d) => d.getDay() === 0 || d.getDay() === 6; - console.log(check_highlight); let extra_func; if (typeof check_highlight === 'object') { @@ -560,6 +580,14 @@ export default class Gantt { d <= this.gantt_end; d.setDate(d.getDate() + 1) ) { + if ( + this.config.ignored_dates.find( + (k) => k.getDate() == d.getDate(), + ) || + (this.config.ignored_function && + this.config.ignored_function(d)) + ) + continue; if (check_highlight(d) || (extra_func && extra_func(d))) { const x = (date_utils.diff( @@ -572,6 +600,7 @@ export default class Gantt { const height = (this.options.bar_height + this.options.padding) * this.tasks.length; + createSVG('rect', { x, y: @@ -592,7 +621,7 @@ export default class Gantt { * * @returns Object containing the x-axis distance and date of the current date, or null if the current date is out of the gantt range. */ - computeGridHighlightDimensions(view_mode) { + highlightToday(view_mode) { const today = new Date(); if (today < this.gantt_start || today > this.gantt_end) return null; let diff_in_units = date_utils.diff( @@ -600,29 +629,14 @@ export default class Gantt { this.gantt_start, this.config.unit, ); - return { - x: (diff_in_units / this.config.step) * this.config.column_width, - date: date_utils.format( - today, - this.config.view_mode.format_string, - this.options.language, - ), - }; - } - - make_grid_highlights() { - if (this.options.highlight_weekend) this.highlightWeekends(); - - const highlightDimensions = this.computeGridHighlightDimensions( - this.config.view_mode, + let left = + (diff_in_units / this.config.step) * this.config.column_width; + let date = date_utils.format( + today, + this.config.view_mode.format_string, + this.options.language, ); - if (!highlightDimensions) return; - const { x: left, date } = highlightDimensions; - const top = this.options.header_height + this.options.padding / 2; - const height = - (this.options.bar_height + this.options.padding) * - this.tasks.length; this.$current_highlight = this.create_el({ top, left, @@ -637,6 +651,55 @@ export default class Gantt { } } + make_grid_highlights() { + this.highlightHolidays(); + + const top = this.options.header_height + this.options.padding / 2; + const height = + (this.options.bar_height + this.options.padding) * + this.tasks.length; + this.layers.grid.innerHTML += ` + + `; + for ( + let d = new Date(this.gantt_start); + d <= this.gantt_end; + d.setDate(d.getDate() + 1) + ) { + if ( + !this.config.ignored_dates.find( + (k) => k.getDate() == d.getDate(), + ) && + this.config.ignored_function && + !this.config.ignored_function(d) + ) + continue; + let diff = + date_utils.convert_scales( + date_utils.diff(d, this.gantt_start) + 'd', + this.config.unit, + ) / this.config.step; + + this.config.ignored_positions.push(diff * this.config.column_width); + createSVG('rect', { + x: diff * this.config.column_width, + y: top, + width: this.config.column_width, + height: height, + class: 'ignored-bar', + style: 'fill: url(#diagonalHatch);', + append_to: this.$svg, + }); + } + + const highlightDimensions = this.highlightToday(this.config.view_mode); + + if (!highlightDimensions) return; + } + create_el({ left, top, width, height, id, classes, append_to }) { let $el = document.createElement('div'); $el.classList.add(classes); @@ -684,7 +747,6 @@ export default class Gantt { get_dates_to_draw() { let last_date_info = null; const dates = this.dates.map((date, i) => { - console.log('starting', date, last_date_info); const d = this.get_date_info(date, last_date_info, i); last_date_info = d; return d; @@ -994,6 +1056,7 @@ export default class Gantt { const $bar = bar.$bar; if (!$bar.finaldx) return; bar.date_changed(); + bar.compute_progress(); bar.set_action_completed(); }); }); @@ -1027,9 +1090,39 @@ export default class Gantt { $bar_progress.max_dx = $bar.getWidth() - $bar_progress.getWidth(); }); + const range_positions = this.config.ignored_positions.map((d) => [ + d, + d + this.config.column_width, + ]); + $.on(this.$svg, 'mousemove', (e) => { if (!is_resizing) return; - let dx = (e.offsetX || e.layerX) - x_on_start; + let now_x = e.offsetX || e.layerX; + + let moving_right = now_x > x_on_start; + if (moving_right) { + let k = range_positions.find( + ([begin, end]) => now_x >= begin && now_x < end, + ); + while (k) { + now_x = k[1]; + k = range_positions.find( + ([begin, end]) => now_x >= begin && now_x < end, + ); + } + } else { + let k = range_positions.find( + ([begin, end]) => now_x > begin && now_x <= end, + ); + while (k) { + now_x = k[0]; + k = range_positions.find( + ([begin, end]) => now_x > begin && now_x <= end, + ); + } + } + + let dx = now_x - x_on_start; if (dx > $bar_progress.max_dx) { dx = $bar_progress.max_dx; }