diff --git a/.gitignore b/.gitignore
index 8ee493f7..faae2ff3 100755
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ coverage
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
+dist/*
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
@@ -29,4 +30,5 @@ node_modules
.DS_Store
-gh-pages
\ No newline at end of file
+gh-pages
+feedback.md
diff --git a/index.html b/index.html
index bf3a2760..69468e83 100644
--- a/index.html
+++ b/index.html
@@ -37,7 +37,7 @@
let tasks = [
{
start: '2024-04-01',
- end: '2024-04-01',
+ end: '2024-04-04',
name: 'Redesign website',
id: 'Task 0',
progress: 30,
@@ -68,8 +68,8 @@
dependencies: 'Task 2',
},
{
- start: '2024-04-08',
- end: '2024-04-10',
+ start: '2024-03-08',
+ end: '2024-05-10',
name: 'Deploy',
id: 'Task 4',
progress: 0,
@@ -77,7 +77,7 @@
},
{
start: '2024-04-21',
- end: '2024-04-29',
+ end: '2024-05-29',
name: 'Go Live!',
id: 'Task 5',
progress: 0,
@@ -85,22 +85,22 @@
custom_class: 'bar-milestone',
},
// {
- // start: '2014-01-05',
- // end: '2019-10-12',
- // name: 'Long term task',
- // id: "Task 6",
- // progress: 0
- // }
+ // start: '2014-01-05',
+ // end: '2019-10-12',
+ // name: 'Long term task',
+ // id: 'Task 6',
+ // progress: 0,
+ // },
];
// Uncomment to test fixed header
- tasks = [
- ...tasks,
- ...Array.from({ length: tasks.length * 3 }, (_, i) => ({
- ...tasks[i % 3],
- id: i,
- })),
- ];
+ // tasks = [
+ // ...tasks,
+ // ...Array.from({ length: tasks.length * 3 }, (_, i) => ({
+ // ...tasks[i % 3],
+ // id: i,
+ // })),
+ // ];
let gantt_chart = new Gantt('.gantt-target', tasks, {
on_click(task) {
console.log('Click', task);
@@ -108,16 +108,14 @@
// on_hover (task, x, y) {
// console.log("Hover", x, y);
// }
- view_mode: 'Day',
- view_mode_padding: { DAY: '3d' },
- custom_view_modes: [
- {
- name: 'Custom Day',
- padding: '1m',
- step: 3,
- unit: 'day',
- },
- ],
+ view_mode: 'Month',
+ // view_modes: [
+ // {
+ // name: 'Custom Day',
+ // padding: '1m',
+ // step: '1d',
+ // },
+ // ],
// popup_on: 'click',
// move_dependencies: false,
// scroll_to: 'today',
diff --git a/src/bar.js b/src/bar.js
index 94f29261..fc60db46 100644
--- a/src/bar.js
+++ b/src/bar.js
@@ -28,9 +28,9 @@ export default class Bar {
this.compute_y();
this.compute_duration();
this.corner_radius = this.gantt.options.bar_corner_radius;
- this.width = this.gantt.options.column_width * this.duration;
+ this.width = this.gantt.config.column_width * this.duration;
this.progress_width =
- this.gantt.options.column_width *
+ this.gantt.config.column_width *
this.duration *
(this.task.progress / 100) || 0;
this.group = createSVG('g', {
@@ -107,7 +107,6 @@ export default class Bar {
: ''),
append_to: this.bar_group,
});
-
animateSVG(this.$bar, 'width', 0, this.width);
if (this.invalid) {
@@ -138,6 +137,7 @@ export default class Bar {
draw_progress_bar() {
if (this.invalid) return;
+
this.$bar_progress = createSVG('rect', {
x: this.x,
y: this.y,
@@ -150,8 +150,8 @@ export default class Bar {
});
const x =
(date_utils.diff(this.task._start, this.gantt.gantt_start, 'hour') /
- this.gantt.options.step) *
- this.gantt.options.column_width;
+ this.gantt.config.step) *
+ this.gantt.config.column_width;
let $date_highlight = document.createElement('div');
$date_highlight.id = `highlight-${this.task.id}`;
@@ -285,7 +285,6 @@ export default class Bar {
if (this.gantt.options.popup_on === 'click') {
let opened = false;
$.on(this.group, 'click', (e) => {
- console.log(opened);
if (!opened) {
this.show_popup(e.offsetX || e.layerX);
document.getElementById(
@@ -313,6 +312,7 @@ export default class Bar {
$.on(this.group, 'mouseleave', () => {
clearTimeout(timeout);
this.gantt.popup?.hide?.();
+
document.getElementById(`highlight-${task_id}`).style.display =
'none';
});
@@ -428,7 +428,6 @@ export default class Bar {
date_changed() {
let changed = false;
const { new_start_date, new_end_date } = this.compute_start_end_date();
-
if (Number(this.task._start) !== Number(new_start_date)) {
changed = true;
this.task._start = new_start_date;
@@ -461,15 +460,16 @@ export default class Bar {
compute_start_end_date() {
const bar = this.$bar;
- const x_in_units = bar.getX() / this.gantt.options.column_width;
+ const x_in_units = bar.getX() / this.gantt.config.column_width;
let new_start_date = date_utils.add(
this.gantt.gantt_start,
- x_in_units * this.gantt.options.step,
- 'hour',
+ x_in_units * this.gantt.config.step,
+ this.gantt.config.unit,
);
const start_offset =
this.gantt.gantt_start.getTimezoneOffset() -
new_start_date.getTimezoneOffset();
+
if (start_offset) {
new_start_date = date_utils.add(
new_start_date,
@@ -478,11 +478,11 @@ export default class Bar {
);
}
- const width_in_units = bar.getWidth() / this.gantt.options.column_width;
+ const width_in_units = bar.getWidth() / this.gantt.config.column_width;
const new_end_date = date_utils.add(
new_start_date,
- width_in_units * this.gantt.options.step,
- 'hour',
+ width_in_units * this.gantt.config.step,
+ this.gantt.config.unit,
);
return { new_start_date, new_end_date };
@@ -497,7 +497,7 @@ export default class Bar {
compute_expected_progress() {
this.expected_progress =
date_utils.diff(date_utils.today(), this.task._start, 'hour') /
- this.gantt.options.step;
+ this.gantt.config.step;
this.expected_progress =
((this.expected_progress < this.duration
? this.expected_progress
@@ -507,28 +507,36 @@ export default class Bar {
}
compute_x() {
- const { step, column_width } = this.gantt.options;
+ const { step, column_width } = this.gantt.config;
const task_start = this.task._start;
const gantt_start = this.gantt.gantt_start;
- const diff = date_utils.diff(task_start, gantt_start, 'hour');
- let x = (diff / step) * column_width;
+ const diff =
+ date_utils.diff(task_start, gantt_start, this.gantt.config.unit) /
+ this.gantt.config.step;
+ let x = diff * column_width;
/* Since the column width is based on 30,
we count the month-difference, multiply it by 30 for a "pseudo-month"
and then add the days in the month, making sure the number does not exceed 29
so it is within the column */
+
if (this.gantt.view_is('Month')) {
const diffDaysBasedOn30DayMonths =
date_utils.diff(task_start, gantt_start, 'month') * 30;
const dayInMonth = Math.min(
29,
- date_utils.format(task_start, 'DD'),
+ date_utils.format(
+ task_start,
+ 'DD',
+ this.gantt.options.language,
+ ),
);
const diff = diffDaysBasedOn30DayMonths + dayInMonth;
x = (diff * column_width) / 30;
}
+
this.x = x;
}
@@ -541,8 +549,11 @@ export default class Bar {
compute_duration() {
this.duration =
- date_utils.diff(this.task._end, this.task._start, 'hour') /
- this.gantt.options.step;
+ date_utils.diff(
+ this.task._end,
+ this.task._start,
+ this.gantt.config.unit,
+ ) / this.gantt.config.step;
}
get_snap_position(dx) {
@@ -550,31 +561,31 @@ export default class Bar {
rem,
position;
- if (this.gantt.view_is('Week')) {
- rem = dx % (this.gantt.options.column_width / 7);
- position =
- odx -
- rem +
- (rem < this.gantt.options.column_width / 14
- ? 0
- : this.gantt.options.column_width / 7);
- } else if (this.gantt.view_is('Month')) {
- rem = dx % (this.gantt.options.column_width / 30);
- position =
- odx -
- rem +
- (rem < this.gantt.options.column_width / 60
- ? 0
- : this.gantt.options.column_width / 30);
- } else {
- rem = dx % this.gantt.options.column_width;
- position =
- odx -
- rem +
- (rem < this.gantt.options.column_width / 2
- ? 0
- : this.gantt.options.column_width);
- }
+ // 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 =
+ odx -
+ rem +
+ (rem < this.gantt.config.column_width / 2
+ ? 0
+ : this.gantt.config.column_width);
+ // }
return position;
}
@@ -592,7 +603,7 @@ export default class Bar {
this.compute_expected_progress();
this.$expected_bar_progress.setAttribute(
'width',
- this.gantt.options.column_width *
+ this.gantt.config.column_width *
this.duration *
(this.expected_progress / 100) || 0,
);
diff --git a/src/date_utils.js b/src/date_utils.js
index 0dc6e36b..f0526bf6 100644
--- a/src/date_utils.js
+++ b/src/date_utils.js
@@ -122,7 +122,7 @@ export default {
return str;
},
- diff(date_a, date_b, scale = DAY) {
+ diff(date_a, date_b, scale = 'day') {
let milliseconds, seconds, hours, minutes, days, months, years;
milliseconds = date_a - date_b;
@@ -131,13 +131,14 @@ export default {
hours = minutes / 60;
days = hours / 24;
// Calculate months across years
- const yearDiff = date_a.getFullYear() - date_b.getFullYear();
- const monthDiff = date_a.getMonth() - date_b.getMonth();
+ let yearDiff = date_a.getFullYear() - date_b.getFullYear();
+ let monthDiff = date_a.getMonth() - date_b.getMonth();
+ // calculate extra
+ monthDiff += (days % 30) / 30;
/* If monthDiff is negative, date_b is in an earlier month than
date_a and thus subtracted from the year difference in months */
months = yearDiff * 12 + monthDiff;
-
/* If date_a's (e.g. march 1st) day of the month is smaller than date_b (e.g. february 28th),
adjust the month difference */
if (date_a.getDate() < date_b.getDate()) {
@@ -151,16 +152,18 @@ export default {
scale += 's';
}
- return Math.floor(
- {
- milliseconds,
- seconds,
- minutes,
- hours,
- days,
- months,
- years,
- }[scale],
+ return (
+ Math.round(
+ {
+ milliseconds,
+ seconds,
+ minutes,
+ hours,
+ days,
+ months,
+ years,
+ }[scale] * 100,
+ ) / 100
);
},
@@ -232,6 +235,21 @@ export default {
];
},
+ convert_scales(period, to_scale) {
+ const TO_DAYS = {
+ millisecond: 1 / 60 / 60 / 24 / 1000,
+ second: 1 / 60 / 60 / 24,
+ minute: 1 / 60 / 24,
+ hour: 1 / 24,
+ day: 1,
+ month: 30,
+ year: 365,
+ };
+ const { duration, scale } = this.parse_duration(period);
+ let in_days = duration * TO_DAYS[scale];
+ return in_days / TO_DAYS[to_scale];
+ },
+
get_days_in_month(date) {
const no_of_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
@@ -248,6 +266,10 @@ export default {
}
return 28;
},
+
+ get_days_in_year(date) {
+ return date.getFullYear() % 4 ? 365 : 366;
+ },
};
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
diff --git a/src/defaults.js b/src/defaults.js
new file mode 100644
index 00000000..e1385218
--- /dev/null
+++ b/src/defaults.js
@@ -0,0 +1,122 @@
+import date_utils from './date_utils';
+
+const DEFAULT_VIEW_MODES = [
+ {
+ name: 'Hour',
+ padding: '7d',
+ step: '1h',
+ lower_text: 'HH',
+ upper_text: (d, ld, lang) =>
+ d.getDate() !== ld.getDate()
+ ? date_utils.format(d, 'D MMMM', lang)
+ : '',
+ upper_text_frequency: 24,
+ },
+ {
+ name: 'Quarter Day',
+ padding: '7d',
+ step: '6h',
+ format_string: 'YYYY-MM-DD HH',
+ lower_text: 'HH',
+ upper_text: (d, ld, lang) =>
+ d.getDate() !== ld.getDate()
+ ? date_utils.format(d, 'D MMM', lang)
+ : '',
+ upper_text_frequency: 4,
+ },
+ {
+ name: 'Half Day',
+ padding: '7d',
+ step: '12h',
+ format_string: 'YYYY-MM-DD HH',
+ lower_text: 'HH',
+ upper_text: (d, ld, lang) =>
+ d.getDate() !== ld.getDate()
+ ? d.getMonth() !== d.getMonth()
+ ? date_utils.format(d, 'D MMM', lang)
+ : date_utils.format(d, 'D', lang)
+ : '',
+ upper_text_frequency: 2,
+ },
+ {
+ name: 'Day',
+ padding: '14d',
+ format_string: 'YYYY-MM-DD',
+ step: '1d',
+ lower_text: (d, ld, lang) =>
+ d.getDate() !== ld.getDate() ? date_utils.format(d, 'D', lang) : '',
+ upper_text: (d, ld, lang) =>
+ d.getMonth() !== ld.getMonth()
+ ? date_utils.format(d, 'MMMM', lang)
+ : '',
+ thick_line: (d) => d.getDay() === 1,
+ },
+ {
+ name: 'Week',
+ padding: '1m',
+ step: '7d',
+ column_width: 140,
+ lower_text: (d, ld, lang) =>
+ d.getMonth() !== ld.getMonth()
+ ? date_utils.format(d, 'D MMM', lang)
+ : date_utils.format(d, 'D', lang),
+ upper_text: (d, ld, lang) =>
+ d.getMonth() !== ld.getMonth()
+ ? date_utils.format(d, 'MMMM', lang)
+ : '',
+ thick_line: (d) => d.getDate() >= 1 && d.getDate() <= 7,
+ upper_text_frequency: 4,
+ },
+ {
+ name: 'Month',
+ padding: '2m',
+ step: '1m',
+ column_width: 120,
+ format_string: 'YYYY-MM',
+ lower_text: 'MMMM',
+ upper_text: (d, ld, lang) =>
+ !ld || d.getFullYear() !== ld.getFullYear()
+ ? date_utils.format(d, 'YYYY', lang)
+ : '',
+ thick_line: (d) => d.getMonth() % 3 === 0,
+ default_snap: '7d',
+ },
+ {
+ name: 'Year',
+ padding: '2y',
+ step: '1y',
+ column_width: 120,
+ format_string: 'YYYY',
+ upper_text: 'YYYY',
+ default_snap: '30d',
+ },
+];
+
+const DEFAULT_OPTIONS = {
+ header_height: 65,
+ column_width: 30,
+ view_modes: DEFAULT_VIEW_MODES,
+ bar_height: 30,
+ bar_corner_radius: 3,
+ arrow_curve: 5,
+ padding: 18,
+ view_mode: 'Day',
+ date_format: 'YYYY-MM-DD',
+ move_dependencies: true,
+ show_expected_progress: false,
+ popup: null,
+ popup_on: 'hover',
+ language: 'en',
+ readonly: false,
+ progress_readonly: false,
+ dates_readonly: false,
+ highlight_weekend: true,
+ scroll_to: 'start',
+ lines: 'both',
+ auto_move_label: true,
+ today_button: true,
+ view_mode_select: false,
+ default_snap: '1d',
+};
+
+export { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES };
diff --git a/src/gantt.css b/src/gantt.css
index ad428382..2fa33285 100644
--- a/src/gantt.css
+++ b/src/gantt.css
@@ -10,10 +10,10 @@
--light-border-color: #ebeff2;
--light-yellow: #f6e796;
--holiday-color: #f9fafa;
- --text-muted: #666;
+ --text-muted: #7c7c7c;
--text-grey: #98a1a9;
--text-light: #fff;
- --text-dark: #111;
+ --text-dark: #171717;
--progress: #ebeef0;
--handle-color: #dcdce4;
--handle-color-important: #94c4f4;
@@ -74,7 +74,6 @@
& .lower-text,
& .upper-text {
text-anchor: middle;
- color: var(--text-dark);
}
& .upper-header {
@@ -90,6 +89,7 @@
position: absolute;
width: fit-content;
transform: translateX(-50%);
+ color: var(--text-muted);
}
& .upper-text {
@@ -97,6 +97,7 @@
width: fit-content;
font-weight: 500;
font-size: 16px;
+ color: var(--text-dark);
}
& .current-upper {
@@ -245,6 +246,10 @@
& .bar-label {
fill: var(--text-light);
+
+ &.big {
+ fill: var(--text-dark);
+ }
}
& .handle {
diff --git a/src/index.js b/src/index.js
index fc12e721..ce52dd05 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,63 +1,19 @@
import date_utils from './date_utils';
import { $, createSVG } from './svg_utils';
-import Bar from './bar';
+
import Arrow from './arrow';
+import Bar from './bar';
import Popup from './popup';
-import './gantt.css';
-
-const VIEW_MODE = {
- HOUR: 'Hour',
- QUARTER_DAY: 'Quarter Day',
- HALF_DAY: 'Half Day',
- DAY: 'Day',
- WEEK: 'Week',
- MONTH: 'Month',
- YEAR: 'Year',
-};
-
-const VIEW_MODE_PADDING = {
- HOUR: ['7d', '7d'],
- QUARTER_DAY: ['7d', '7d'],
- HALF_DAY: ['7d', '7d'],
- DAY: ['1m', '1m'],
- WEEK: ['1m', '1m'],
- MONTH: ['1m', '1m'],
- YEAR: ['2y', '2y'],
-};
+import { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES } from './defaults';
-const DEFAULT_OPTIONS = {
- header_height: 65,
- column_width: 30,
- view_modes: [...Object.values(VIEW_MODE)],
- bar_height: 30,
- bar_corner_radius: 3,
- arrow_curve: 5,
- padding: 18,
- view_mode: 'Day',
- date_format: 'YYYY-MM-DD',
- move_dependencies: true,
- show_expected_progress: false,
- popup: null,
- popup_on: 'hover',
- language: 'en',
- readonly: false,
- progress_readonly: false,
- dates_readonly: false,
- highlight_weekend: true,
- scroll_to: 'start',
- lines: 'both',
- auto_move_label: true,
- today_button: true,
- view_mode_select: false,
-};
+import './gantt.css';
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();
}
@@ -113,108 +69,95 @@ export default class Gantt {
this.options = { ...DEFAULT_OPTIONS, ...options };
const custom_mode = this.options.custom_view_modes
? this.options.custom_view_modes.find(
- (m) => m.name === this.options.view_mode,
+ (m) => m.name === this.config.view_mode.name,
)
: null;
if (custom_mode) this.options = { ...this.options, custom_mode };
- if (!this.options.view_mode_padding)
- this.options.view_mode_padding = {};
- for (let [key, value] of Object.entries(
- this.options.view_mode_padding,
- )) {
- if (typeof value === 'string') {
- // Configure for single value given
- this.options.view_mode_padding[key] = [value, value];
- }
- }
- this.options.view_mode_padding = {
- ...VIEW_MODE_PADDING,
- ...options.view_mode_padding,
- };
+ this.config = {};
}
setup_tasks(tasks) {
// prepare tasks
- this.tasks = tasks.map((task, i) => {
- // convert to Date objects
- task._start = date_utils.parse(task.start);
- if (task.end === undefined && task.duration !== undefined) {
- task.end = task._start;
- let durations = task.duration.split(' ');
-
- durations.forEach((tmpDuration) => {
- let { duration, scale } =
- date_utils.parse_duration(tmpDuration);
- task.end = date_utils.add(task.end, duration, scale);
- });
- }
- task._end = date_utils.parse(task.end);
- let diff = date_utils.diff(task._end, task._start, 'year');
- if (diff < 0) {
- throw Error(
- "start of task can't be after end of task: in task #, " +
- (i + 1),
- );
- }
- // make task invalid if duration too large
- if (date_utils.diff(task._end, task._start, 'year') > 10) {
- task.end = null;
- }
+ this.tasks = tasks
+ .map((task, i) => {
+ if (!task.start) {
+ console.error(
+ `task "${task.id}" doesn't have a start date`,
+ );
+ return false;
+ }
- // cache index
- task._index = i;
+ task._start = date_utils.parse(task.start);
+ if (task.end === undefined && task.duration !== undefined) {
+ task.end = task._start;
+ let durations = task.duration.split(' ');
- // invalid dates
- if (!task.start && !task.end) {
- const today = date_utils.today();
- task._start = today;
- task._end = date_utils.add(today, 2, 'day');
- }
+ durations.forEach((tmpDuration) => {
+ let { duration, scale } =
+ date_utils.parse_duration(tmpDuration);
+ task.end = date_utils.add(task.end, duration, scale);
+ });
+ }
+ if (!task.end) {
+ console.error(`task "${task.id}" doesn't have an end date`);
+ return false;
+ }
+ task._end = date_utils.parse(task.end);
- if (!task.start && task.end) {
- task._start = date_utils.add(task._end, -2, 'day');
- }
+ let diff = date_utils.diff(task._end, task._start, 'year');
+ if (diff < 0) {
+ console.error(
+ `start of task can't be after end of task: in task "${task.id}"`,
+ );
+ return false;
+ }
- if (task.start && !task.end) {
- task._end = date_utils.add(task._start, 2, 'day');
- }
+ // make task invalid if duration too large
+ if (date_utils.diff(task._end, task._start, 'year') > 10) {
+ console.error(
+ `the duration of task "${task.id}" is too long (above ten years)`,
+ );
+ return false;
+ }
- // 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');
- }
+ // cache index
+ task._index = i;
- // invalid flag
- if (!task.start || !task.end) {
- task.invalid = true;
- }
+ // 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');
+ }
- // dependencies
- if (typeof task.dependencies === 'string' || !task.dependencies) {
- let deps = [];
- if (task.dependencies) {
- deps = task.dependencies
- .split(',')
- .map((d) => d.trim().replaceAll(' ', '_'))
- .filter((d) => d);
+ // dependencies
+ if (
+ typeof task.dependencies === 'string' ||
+ !task.dependencies
+ ) {
+ let deps = [];
+ if (task.dependencies) {
+ deps = task.dependencies
+ .split(',')
+ .map((d) => d.trim().replaceAll(' ', '_'))
+ .filter((d) => d);
+ }
+ task.dependencies = deps;
}
- task.dependencies = deps;
- }
- // uids
- if (!task.id) {
- task.id = generate_id(task);
- } else if (typeof task.id === 'string') {
- task.id = task.id.replaceAll(' ', '_');
- } else {
- task.id = `${task.id}`;
- }
+ // uids
+ if (!task.id) {
+ task.id = generate_id(task);
+ } else if (typeof task.id === 'string') {
+ task.id = task.id.replaceAll(' ', '_');
+ } else {
+ task.id = `${task.id}`;
+ }
- return task;
- });
+ return task;
+ })
+ .filter((t) => t);
this.setup_dependencies();
}
@@ -234,54 +177,22 @@ export default class Gantt {
}
change_view_mode(mode = this.options.view_mode) {
+ if (typeof mode === 'string') {
+ mode = this.options.view_modes.find((d) => d.name === mode);
+ }
+ this.config.view_mode = 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;
- const custom_mode = this.options.custom_mode;
- if (custom_mode) {
- //configure step and column width for custom view case
- if (custom_mode.unit === 'hour') {
- this.options.step = custom_mode.step;
- this.options.column_width = 38;
- } else if (custom_mode.unit === 'day') {
- this.options.step = custom_mode.step * 24;
- this.options.column_width = 38;
- } else if (custom_mode.unit === 'month') {
- this.options.step = custom_mode.step * 24 * 30;
- this.options.column_width = 120;
- } else {
- this.options.step = 24;
- this.options.column_width = 38;
- }
- }
- if (view_mode === VIEW_MODE.HOUR) {
- this.options.step = 24 / 24;
- this.options.column_width = 38;
- } else 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;
- }
+ update_view_scale(mode) {
+ let { duration, scale } = date_utils.parse_duration(mode.step);
+ this.config.step = duration;
+ this.config.unit = scale;
+ this.config.column_width =
+ mode.column_width || this.options.column_width;
}
setup_dates() {
@@ -290,62 +201,49 @@ export default class Gantt {
}
setup_gantt_dates() {
- this.gantt_start = this.gantt_end = null;
+ let gantt_start, gantt_end;
+ if (!this.tasks.length) {
+ gantt_start = new Date();
+ gantt_end = new Date();
+ }
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;
+ if (!gantt_start || task._start < gantt_start) {
+ gantt_start = task._start;
}
- }
- let gantt_start, gantt_end;
- if (!this.gantt_start) gantt_start = new Date();
- else gantt_start = date_utils.start_of(this.gantt_start, 'day');
- if (!this.gantt_end) gantt_end = new Date();
- else gantt_end = date_utils.start_of(this.gantt_end, 'day');
-
- const custom_mode = this.options.custom_mode;
- let [padding_start, padding_end] = [
- { duration: 1, scale: 'day' },
- { duration: 1, scale: 'day' },
- ];
- if (custom_mode) {
- [padding_start, padding_end] = [
- custom_mode.padding,
- custom_mode.padding,
- ].map(date_utils.parse_duration);
- } else {
- let viewKey;
- for (let [key, value] of Object.entries(VIEW_MODE)) {
- if (value === this.options.view_mode) {
- viewKey = key;
- }
+ if (!gantt_end || task._end > gantt_end) {
+ gantt_end = task._end;
}
- [padding_start, padding_end] = this.options.view_mode_padding[
- viewKey
- ].map(date_utils.parse_duration);
}
+
+ gantt_start = date_utils.start_of(gantt_start, 'day');
+ gantt_end = date_utils.start_of(gantt_end, 'day');
+
+ // handle single value for padding
+ if (typeof this.config.view_mode.padding === 'string')
+ this.config.view_mode.padding = [
+ this.config.view_mode.padding,
+ this.config.view_mode.padding,
+ ];
+
+ let [padding_start, padding_end] = this.config.view_mode.padding.map(
+ date_utils.parse_duration,
+ );
gantt_start = date_utils.add(
gantt_start,
-padding_start.duration,
padding_start.scale,
);
- let format_string;
- if (this.view_is(VIEW_MODE.YEAR)) {
- format_string = 'YYYY';
- } else if (this.view_is(VIEW_MODE.MONTH)) {
- format_string = 'YYYY-MM';
- } else if (this.view_is(VIEW_MODE.DAY)) {
- format_string = 'YYYY-MM-DD';
- } else {
- format_string = 'YYYY-MM-DD HH';
- }
+ let format_string =
+ this.config.view_mode.format_string || 'YYYY-MM-DD HH';
+
this.gantt_start = date_utils.parse(
- date_utils.format(gantt_start, format_string),
+ date_utils.format(
+ gantt_start,
+ format_string,
+ this.options.language,
+ ),
);
this.gantt_start.setHours(0, 0, 0, 0);
this.gantt_end = date_utils.add(
@@ -356,36 +254,15 @@ export default class Gantt {
}
setup_date_values() {
- this.dates = [];
- let cur_date = null;
-
- while (cur_date === null || cur_date < this.gantt_end) {
- if (this.options.custom_mode) {
- const step = this.options.custom_mode.step || 1;
- const unit = this.options.custom_mode.unit || 'day';
-
- if (!cur_date) {
- cur_date = date_utils.clone(this.gantt_start);
- } else {
- cur_date = date_utils.add(cur_date, step, unit);
- }
- } else {
- 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',
- );
- }
- }
- }
+ let cur_date = this.gantt_start;
+ this.dates = [cur_date];
+
+ while (cur_date < this.gantt_end) {
+ cur_date = date_utils.add(
+ cur_date,
+ this.config.step,
+ this.config.unit,
+ );
this.dates.push(cur_date);
}
}
@@ -434,7 +311,7 @@ export default class Gantt {
}
make_grid_background() {
- const grid_width = this.dates.length * this.options.column_width;
+ const grid_width = this.dates.length * this.config.column_width;
const grid_height =
this.options.header_height +
this.options.padding +
@@ -458,7 +335,7 @@ export default class Gantt {
make_grid_rows() {
const rows_layer = createSVG('g', { append_to: this.layers.grid });
- const row_width = this.dates.length * this.options.column_width;
+ const row_width = this.dates.length * this.config.column_width;
const row_height = this.options.bar_height + this.options.padding;
let row_y = this.options.header_height + this.options.padding / 2;
@@ -471,6 +348,7 @@ export default class Gantt {
class: 'grid-row',
append_to: rows_layer,
});
+ // FIX
if (
this.options.lines === 'both' ||
this.options.lines === 'horizontal'
@@ -485,7 +363,7 @@ export default class Gantt {
let $header = document.createElement('div');
$header.style.height = this.options.header_height + 10 + 'px';
$header.style.width =
- this.dates.length * this.options.column_width + 'px';
+ this.dates.length * this.config.column_width + 'px';
$header.classList.add('grid-header');
this.$header = $header;
this.$container.appendChild($header);
@@ -518,13 +396,13 @@ export default class Gantt {
$el.textContent = 'Mode';
$select.appendChild($el);
- for (const key in VIEW_MODE) {
+ for (const mode of this.options.view_modes) {
const $option = document.createElement('option');
- $option.value = VIEW_MODE[key];
- $option.textContent = VIEW_MODE[key];
+ $option.value = mode.name;
+ $option.textContent = mode.name;
$select.appendChild($option);
}
- // $select.value = this.options.view_mode
+
$select.addEventListener(
'change',
function () {
@@ -593,8 +471,7 @@ export default class Gantt {
}
make_grid_ticks() {
- if (!['both', 'vertical', 'horizontal'].includes(this.options.lines))
- return;
+ if (this.options.lines === 'none') return;
let tick_x = 0;
let tick_y = this.options.header_height + this.options.padding / 2;
let tick_height =
@@ -608,7 +485,7 @@ export default class Gantt {
let row_y = this.options.header_height + this.options.padding / 2;
- const row_width = this.dates.length * this.options.column_width;
+ const row_width = this.dates.length * this.config.column_width;
const row_height = this.options.bar_height + this.options.padding;
if (this.options.lines !== 'vertical') {
for (let _ of this.tasks) {
@@ -624,42 +501,40 @@ export default class Gantt {
}
}
if (this.options.lines === 'horizontal') return;
+
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
+ this.config.view_mode.thick_line &&
+ this.config.view_mode.thick_line(date)
) {
tick_class += ' thick';
}
- // thick ticks for quarters
- if (this.view_is(VIEW_MODE.MONTH) && date.getMonth() % 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)) {
+ if (this.view_is('month')) {
tick_x +=
(date_utils.get_days_in_month(date) *
- this.options.column_width) /
+ this.config.column_width) /
30;
+ } else if (this.view_is('year')) {
+ tick_x +=
+ (date_utils.get_days_in_year(date) *
+ this.config.column_width) /
+ 365;
} else {
- tick_x += this.options.column_width;
+ tick_x += this.config.column_width;
}
}
}
highlightWeekends() {
+ // FIX
if (!this.view_is('Day') && !this.view_is('Half Day')) return;
for (
let d = new Date(this.gantt_start);
@@ -668,9 +543,9 @@ export default class Gantt {
) {
if (d.getDay() === 0 || d.getDay() === 6) {
const x =
- (date_utils.diff(d, this.gantt_start, 'hour') /
- this.options.step) *
- this.options.column_width;
+ (date_utils.diff(d, this.gantt_start, this.config.unit) /
+ this.config.step) *
+ this.config.column_width;
const height =
(this.options.bar_height + this.options.padding) *
this.tasks.length;
@@ -679,7 +554,7 @@ export default class Gantt {
y: this.options.header_height + this.options.padding / 2,
width:
(this.view_is('Day') ? 1 : 2) *
- this.options.column_width,
+ this.config.column_width,
height,
class: 'holiday-highlight',
append_to: this.layers.grid,
@@ -694,84 +569,47 @@ 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) {
- const todayDate = new Date();
- if (todayDate < this.gantt_start || todayDate > this.gantt_end)
- return null;
-
- let x = this.options.column_width / 2;
-
- if (this.view_is(VIEW_MODE.DAY)) {
- return {
- x:
- x +
- (date_utils.diff(todayDate, this.gantt_start, 'hour') /
- this.options.step) *
- this.options.column_width,
- date: todayDate,
- };
- }
-
- for (let date of this.dates) {
- const todayDate = new Date();
- const startDate = new Date(date);
- const endDate = new Date(date);
- switch (view_mode) {
- case VIEW_MODE.WEEK:
- endDate.setDate(date.getDate() + 7);
- break;
- case VIEW_MODE.MONTH:
- endDate.setMonth(date.getMonth() + 1);
- break;
- case VIEW_MODE.YEAR:
- endDate.setFullYear(date.getFullYear() + 1);
- break;
- }
- if (todayDate >= startDate && todayDate <= endDate) {
- return { x, date: startDate };
- } else {
- x += this.options.column_width;
- }
- }
-
- return { x };
+ const today = new Date();
+ if (today < this.gantt_start || today > this.gantt_end) return null;
+ let diff_in_units = date_utils.diff(
+ today,
+ 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();
- // highlight today's | week's | month's | year's
- if (
- this.view_is(VIEW_MODE.DAY) ||
- this.view_is(VIEW_MODE.WEEK) ||
- this.view_is(VIEW_MODE.MONTH) ||
- this.view_is(VIEW_MODE.YEAR)
- ) {
- // Used as we must find the _end_ of session if view is not Day
- const highlightDimensions = this.computeGridHighlightDimensions(
- this.options.view_mode,
- );
- if (!highlightDimensions) return;
- const { x: left, date } = highlightDimensions;
- if (!this.dates.find((d) => d.getTime() == date.getTime())) return;
- 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,
- height,
- classes: 'current-highlight',
- append_to: this.$container,
- });
- let $today = document.getElementById(
- date_utils.format(date).replaceAll(' ', '_'),
- );
- if ($today) {
- $today.classList.add('current-date-highlight');
- $today.style.top = +$today.style.top.slice(0, -2) - 4 + 'px';
- $today.style.left = +$today.style.left.slice(0, -2) - 8 + 'px';
- }
+ const highlightDimensions = this.computeGridHighlightDimensions(
+ this.config.view_mode,
+ );
+ 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,
+ height,
+ classes: 'current-highlight',
+ append_to: this.$container,
+ });
+ let $today = document.getElementById(date.replaceAll(' ', '_'));
+ if ($today) {
+ $today.classList.add('current-date-highlight');
+ $today.style.top = +$today.style.top.slice(0, -2) - 4 + 'px';
}
}
@@ -820,217 +658,56 @@ export default class Gantt {
}
get_dates_to_draw() {
- let last_date = null;
+ let last_date_info = null;
const dates = this.dates.map((date, i) => {
- const d = this.get_date_info(date, last_date, i);
- last_date = d;
+ console.log('starting', date, last_date_info);
+ const d = this.get_date_info(date, last_date_info, i);
+ last_date_info = d;
return d;
});
return dates;
}
get_date_info(date, last_date_info) {
- let last_date = last_date_info
- ? last_date_info.date
- : date_utils.add(date, 1, 'day');
- let date_text = {};
- const custom_mode = this.options.custom_mode;
- if (custom_mode) {
- let lower_text, upper_text;
- const unit = custom_mode ? custom_mode.unit.toLowerCase() : 'day';
- if (unit === 'hour') {
- lower_text = date_utils.format(
- date,
- 'HH',
- this.options.language,
- );
- upper_text =
- date.getDate() !== last_date.getDate()
- ? date_utils.format(
- date,
- 'D MMMM',
- this.options.language,
- )
- : '';
- } else if (unit === 'day') {
- lower_text =
- date.getDate() !== last_date.getDate()
- ? date_utils.format(date, 'D', this.options.language)
- : '';
- upper_text =
- date.getMonth() !== last_date.getMonth() || !last_date_info
- ? date_utils.format(date, 'MMMM', this.options.language)
- : '';
- } else if (unit === 'month') {
- lower_text = date_utils.format(
- date,
- 'MMMM',
- this.options.language,
- );
- upper_text =
- date.getFullYear() !== last_date.getFullYear()
- ? date_utils.format(date, 'YYYY', this.options.language)
- : '';
- } else {
- lower_text = date_utils.format(
- date,
- 'YYYY',
- this.options.language,
- );
- upper_text = ''; // Default to no upper text for very large units
- }
- date_text[`${custom_mode.name}_upper`] = upper_text;
- date_text[`${custom_mode.name}_lower`] = lower_text;
- } else {
- date_text = {
- Hour_lower: date_utils.format(
- date,
- 'HH',
- this.options.language,
- ),
- '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,
- ),
- Hour_upper:
- date.getDate() !== last_date.getDate()
- ? date_utils.format(
- date,
- 'D MMMM',
- 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() || !last_date_info
- ? 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)
- : '',
- };
- }
+ let last_date = last_date_info ? last_date_info.date : null;
- let column_width =
- custom_mode &&
- custom_mode.lower_text &&
- custom_mode.lower_text.column_width
- ? custom_mode.lower_text.column_width * (custom_mode.step ?? 1)
- : this.view_is(VIEW_MODE.MONTH)
- ? (date_utils.get_days_in_month(date) *
- this.options.column_width) /
- 30
- : this.options.column_width;
+ let column_width = this.config.column_width;
const base_pos = {
x: last_date_info
? last_date_info.base_pos_x + last_date_info.column_width
- : 0,
+ : 20,
lower_y: this.options.header_height - 20,
upper_y: this.options.header_height - 50,
};
- const x_pos = {
- Hour_lower: column_width / 2,
- Hour_upper: column_width * 12,
- 'Quarter Day_lower': column_width / 2,
- 'Quarter Day_upper': column_width * 2,
- 'Half Day_lower': column_width / 2,
- 'Half Day_upper': column_width,
- Day_lower: column_width / 2,
- Day_upper: column_width / 2,
- Week_lower: column_width / 2,
- Week_upper: (column_width * 4) / 2,
- Month_lower: column_width / 2,
- Month_upper: column_width / 2,
- Year_lower: column_width / 2,
- Year_upper: (column_width * 30) / 2,
- };
- if (custom_mode) {
- x_pos[`${custom_mode.name}_upper`] = column_width / 2;
- x_pos[`${custom_mode.name}_lower`] =
- column_width /
- (custom_mode.unit.toLowerCase() === 'day' ? 1 : 2);
- }
+
+ let upper_text = this.config.view_mode.upper_text;
+ let lower_text = this.config.view_mode.lower_text;
+ if (!upper_text) upper_text = () => '';
+ if (!lower_text) lower_text = () => '';
+
return {
date,
- formatted_date: date_utils.format(date).replaceAll(' ', '_'),
- column_width,
+ formatted_date: date_utils
+ .format(date, this.config.view_mode.format_string)
+ .replaceAll(' ', '_'),
+ column_width: this.config.column_width,
base_pos_x: base_pos.x,
- upper_text: this.options.upper_text
- ? this.options.upper_text(
- date,
- this.options.view_mode,
- date_text[`${this.options.view_mode}_upper`],
- )
- : date_text[`${this.options.view_mode}_upper`],
- lower_text: this.options.lower_text
- ? this.options.lower_text(
- date,
- this.options.view_mode,
- date_text[`${this.options.view_mode}_lower`],
- )
- : date_text[`${this.options.view_mode}_lower`],
- upper_x: base_pos.x + x_pos[`${this.options.view_mode}_upper`],
+ upper_text:
+ typeof upper_text === 'string'
+ ? date_utils.format(date, upper_text, this.options.language)
+ : upper_text(date, last_date, this.options.language),
+ lower_text:
+ typeof lower_text === 'string'
+ ? date_utils.format(date, lower_text, this.options.language)
+ : lower_text(date, last_date, this.options.language),
+ upper_x:
+ base_pos.x +
+ (column_width * this.config.view_mode.upper_text_frequency ||
+ 1) /
+ 2,
upper_y: base_pos.upper_y,
- lower_x: base_pos.x + x_pos[`${this.options.view_mode}_lower`],
+ lower_x: base_pos.x + column_width / 2 - 20,
lower_y: base_pos.lower_y,
};
}
@@ -1093,18 +770,18 @@ export default class Gantt {
} else if (typeof date === 'string') {
date = date_utils.parse(date);
}
-
const parent_element = this.$svg.parentElement;
if (!parent_element) return;
-
- const hours_before_first_task =
- date_utils.diff(date, this.gantt_start, 'hour') + 24;
-
+ const units_since_first_task = date_utils.diff(
+ date,
+ this.gantt_start,
+ this.config.unit,
+ );
const scroll_pos =
- (hours_before_first_task / this.options.step) *
- this.options.column_width -
- this.options.column_width;
- parent_element.scrollTo({ left: scroll_pos, behavior: 'smooth' });
+ (units_since_first_task / this.config.step) *
+ this.config.column_width -
+ this.config.column_width;
+ parent_element.scrollTo({ left: 400, behavior: 'smooth' });
}
scroll_today() {
@@ -1140,7 +817,6 @@ export default class Gantt {
$.on(this.$svg, 'mousedown', '.bar-wrapper, .handle', (e, element) => {
const bar_wrapper = $.closest('.bar-wrapper', element);
bars.forEach((bar) => bar.group.classList.remove('active'));
-
if (element.classList.contains('left')) {
is_resizing_left = true;
} else if (element.classList.contains('right')) {
@@ -1189,13 +865,13 @@ export default class Gantt {
}
const daysSinceStart =
- ((e.currentTarget.scrollLeft / this.options.column_width) *
- this.options.step) /
+ ((e.currentTarget.scrollLeft / this.config.column_width) *
+ this.config.step) /
24;
let format_str = 'D MMM';
- if (['Year', 'Month'].includes(this.options.view_mode))
+ if (['Year', 'Month'].includes(this.config.view_mode.name))
format_str = 'YYYY';
- else if (['Day', 'Week'].includes(this.options.view_mode))
+ else if (['Day', 'Week'].includes(this.config.view_mode.name))
format_str = 'MMMM';
else if (this.view_is('Half Day')) format_str = 'D';
else if (this.view_is('Hour')) format_str = 'D MMMM';
@@ -1203,6 +879,7 @@ export default class Gantt {
let currentUpper = date_utils.format(
date_utils.add(this.gantt_start, daysSinceStart, 'day'),
format_str,
+ this.options.language,
);
const upperTexts = Array.from(
document.querySelectorAll('.upper-text'),
@@ -1312,7 +989,6 @@ export default class Gantt {
is_resizing = true;
x_on_start = e.offsetX || e.layerX;
y_on_start = e.offsetY || e.layerY;
- console.log(e, handle);
const $bar_wrapper = $.closest('.bar-wrapper', handle);
const id = $bar_wrapper.getAttribute('data-id');
@@ -1330,12 +1006,6 @@ export default class Gantt {
$.on(this.$svg, 'mousemove', (e) => {
if (!is_resizing) return;
let dx = (e.offsetX || e.layerX) - x_on_start;
- console.log(
- dx,
- $bar_progress.getWidth(),
- $bar_progress.min_dx,
- $bar_progress.max_dx,
- );
if (dx > $bar_progress.max_dx) {
dx = $bar_progress.max_dx;
}
@@ -1383,31 +1053,24 @@ export default class Gantt {
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);
+ let unit_length = 1;
+ const default_snap =
+ this.config.view_mode.default_snap || this.options.default_snap;
+ if (default_snap !== 'unit') {
+ const { duration, scale } = date_utils.parse_duration(default_snap);
+ unit_length =
+ date_utils.convert_scales(this.config.view_mode.step, scale) /
+ duration;
}
+
+ rem = dx % (this.config.column_width / unit_length);
+
+ position =
+ odx -
+ rem +
+ (rem < (this.config.column_width / unit_length) * 2
+ ? 0
+ : this.config.column_width / unit_length);
return position;
}
@@ -1420,14 +1083,14 @@ export default class Gantt {
view_is(modes) {
if (typeof modes === 'string') {
- return this.options.view_mode === modes;
+ return this.config.view_mode.name === modes;
}
if (Array.isArray(modes)) {
- return modes.some((mode) => this.options.view_mode === mode);
+ return modes.some(view_is);
}
- return false;
+ return this.config.view_mode.name === modes.name;
}
get_task(id) {
@@ -1488,7 +1151,15 @@ export default class Gantt {
}
}
-Gantt.VIEW_MODE = VIEW_MODE;
+Gantt.VIEW_MODE = {
+ HOUR: DEFAULT_VIEW_MODES[0],
+ QUARTER_DAY: DEFAULT_VIEW_MODES[1],
+ HALF_DAY: DEFAULT_VIEW_MODES[2],
+ DAY: DEFAULT_VIEW_MODES[3],
+ WEEK: DEFAULT_VIEW_MODES[4],
+ MONTH: DEFAULT_VIEW_MODES[5],
+ YEAR: DEFAULT_VIEW_MODES[6],
+};
function generate_id(task) {
return task.name + '_' + Math.random().toString(36).slice(2, 12);