feat: ignore regions
This commit is contained in:
parent
6984d3e544
commit
9caf2322b9
138
src/bar.js
138
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}<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]'
|
||||
);
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
|
||||
149
src/index.js
149
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 += `<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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user