feat: ignore regions

This commit is contained in:
safwansamsudeen 2024-12-05 11:09:03 +05:30
parent 6984d3e544
commit 9caf2322b9
3 changed files with 207 additions and 82 deletions

View File

@ -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}<br/>Progress: ${this.task.progress}`;
const subtitle = `${start_date} - ${end_date} (${this.actual_duration_in_days} days)<br/>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]'
);
}

View File

@ -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 };

View File

@ -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 += `<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
<path d="M-1,1 l2,-2
M0,4 l4,-4
M3,5 l2,-2"
style="stroke:black; stroke-width:0.5" />
</pattern>`;
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;
}