make daily mode work after rewrite of date system

This commit is contained in:
safwansamsudeen 2024-11-29 16:34:33 +05:30
parent 8f03ba0572
commit 6e0e0a9704
6 changed files with 287 additions and 451 deletions

3
.gitignore vendored
View File

@ -21,6 +21,7 @@ coverage
# Compiled binary addons (http://nodejs.org/api/addons.html) # Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release build/Release
dist/*
# Dependency directory # Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
@ -29,4 +30,4 @@ node_modules
.DS_Store .DS_Store
gh-pages gh-pages

View File

@ -37,7 +37,7 @@
let tasks = [ let tasks = [
{ {
start: '2024-04-01', start: '2024-04-01',
end: '2024-04-01', end: '2024-04-04',
name: 'Redesign website', name: 'Redesign website',
id: 'Task 0', id: 'Task 0',
progress: 30, progress: 30,
@ -77,7 +77,7 @@
}, },
{ {
start: '2024-04-21', start: '2024-04-21',
end: '2024-04-29', end: '2024-05-29',
name: 'Go Live!', name: 'Go Live!',
id: 'Task 5', id: 'Task 5',
progress: 0, progress: 0,
@ -85,22 +85,22 @@
custom_class: 'bar-milestone', custom_class: 'bar-milestone',
}, },
// { // {
// start: '2014-01-05', // start: '2014-01-05',
// end: '2019-10-12', // end: '2019-10-12',
// name: 'Long term task', // name: 'Long term task',
// id: "Task 6", // id: 'Task 6',
// progress: 0 // progress: 0,
// } // },
]; ];
// Uncomment to test fixed header // Uncomment to test fixed header
tasks = [ // tasks = [
...tasks, // ...tasks,
...Array.from({ length: tasks.length * 3 }, (_, i) => ({ // ...Array.from({ length: tasks.length * 3 }, (_, i) => ({
...tasks[i % 3], // ...tasks[i % 3],
id: i, // id: i,
})), // })),
]; // ];
let gantt_chart = new Gantt('.gantt-target', tasks, { let gantt_chart = new Gantt('.gantt-target', tasks, {
on_click(task) { on_click(task) {
console.log('Click', task); console.log('Click', task);
@ -109,15 +109,13 @@
// console.log("Hover", x, y); // console.log("Hover", x, y);
// } // }
view_mode: 'Day', view_mode: 'Day',
view_mode_padding: { DAY: '3d' }, // view_modes: [
custom_view_modes: [ // {
{ // name: 'Custom Day',
name: 'Custom Day', // padding: '1m',
padding: '1m', // step: '1d',
step: 3, // },
unit: 'day', // ],
},
],
// popup_on: 'click', // popup_on: 'click',
// move_dependencies: false, // move_dependencies: false,
// scroll_to: 'today', // scroll_to: 'today',

View File

@ -28,9 +28,9 @@ export default class Bar {
this.compute_y(); this.compute_y();
this.compute_duration(); this.compute_duration();
this.corner_radius = this.gantt.options.bar_corner_radius; 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.progress_width =
this.gantt.options.column_width * this.gantt.config.column_width *
this.duration * this.duration *
(this.task.progress / 100) || 0; (this.task.progress / 100) || 0;
this.group = createSVG('g', { this.group = createSVG('g', {
@ -107,7 +107,6 @@ export default class Bar {
: ''), : ''),
append_to: this.bar_group, append_to: this.bar_group,
}); });
animateSVG(this.$bar, 'width', 0, this.width); animateSVG(this.$bar, 'width', 0, this.width);
if (this.invalid) { if (this.invalid) {
@ -138,6 +137,7 @@ export default class Bar {
draw_progress_bar() { draw_progress_bar() {
if (this.invalid) return; if (this.invalid) return;
this.$bar_progress = createSVG('rect', { this.$bar_progress = createSVG('rect', {
x: this.x, x: this.x,
y: this.y, y: this.y,
@ -150,8 +150,8 @@ export default class Bar {
}); });
const x = const x =
(date_utils.diff(this.task._start, this.gantt.gantt_start, 'hour') / (date_utils.diff(this.task._start, this.gantt.gantt_start, 'hour') /
this.gantt.options.step) * this.gantt.config.step) *
this.gantt.options.column_width; this.gantt.config.column_width;
let $date_highlight = document.createElement('div'); let $date_highlight = document.createElement('div');
$date_highlight.id = `highlight-${this.task.id}`; $date_highlight.id = `highlight-${this.task.id}`;
@ -285,7 +285,6 @@ export default class Bar {
if (this.gantt.options.popup_on === 'click') { if (this.gantt.options.popup_on === 'click') {
let opened = false; let opened = false;
$.on(this.group, 'click', (e) => { $.on(this.group, 'click', (e) => {
console.log(opened);
if (!opened) { if (!opened) {
this.show_popup(e.offsetX || e.layerX); this.show_popup(e.offsetX || e.layerX);
document.getElementById( document.getElementById(
@ -304,17 +303,12 @@ export default class Bar {
(e) => (e) =>
(timeout = setTimeout(() => { (timeout = setTimeout(() => {
this.show_popup(e.offsetX || e.layerX); this.show_popup(e.offsetX || e.layerX);
document.getElementById(
`${task_id}-highlight`,
).style.display = 'block';
}, 200)), }, 200)),
); );
$.on(this.group, 'mouseleave', () => { $.on(this.group, 'mouseleave', () => {
clearTimeout(timeout); clearTimeout(timeout);
this.gantt.popup?.hide?.(); this.gantt.popup?.hide?.();
document.getElementById(`${task_id}-highlight`).style.display =
'none';
}); });
} }
@ -428,7 +422,6 @@ export default class Bar {
date_changed() { date_changed() {
let changed = false; let changed = false;
const { new_start_date, new_end_date } = this.compute_start_end_date(); const { new_start_date, new_end_date } = this.compute_start_end_date();
if (Number(this.task._start) !== Number(new_start_date)) { if (Number(this.task._start) !== Number(new_start_date)) {
changed = true; changed = true;
this.task._start = new_start_date; this.task._start = new_start_date;
@ -461,15 +454,16 @@ export default class Bar {
compute_start_end_date() { compute_start_end_date() {
const bar = this.$bar; 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( let new_start_date = date_utils.add(
this.gantt.gantt_start, this.gantt.gantt_start,
x_in_units * this.gantt.options.step, x_in_units * this.gantt.config.step,
'hour', this.gantt.config.unit,
); );
const start_offset = const start_offset =
this.gantt.gantt_start.getTimezoneOffset() - this.gantt.gantt_start.getTimezoneOffset() -
new_start_date.getTimezoneOffset(); new_start_date.getTimezoneOffset();
if (start_offset) { if (start_offset) {
new_start_date = date_utils.add( new_start_date = date_utils.add(
new_start_date, new_start_date,
@ -478,11 +472,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( const new_end_date = date_utils.add(
new_start_date, new_start_date,
width_in_units * this.gantt.options.step, width_in_units * this.gantt.config.step,
'hour', this.gantt.config.unit,
); );
return { new_start_date, new_end_date }; return { new_start_date, new_end_date };
@ -497,7 +491,7 @@ export default class Bar {
compute_expected_progress() { compute_expected_progress() {
this.expected_progress = this.expected_progress =
date_utils.diff(date_utils.today(), this.task._start, 'hour') / 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.expected_progress < this.duration ((this.expected_progress < this.duration
? this.expected_progress ? this.expected_progress
@ -507,17 +501,22 @@ export default class Bar {
} }
compute_x() { compute_x() {
const { step, column_width } = this.gantt.options; const { step, column_width } = this.gantt.config;
const task_start = this.task._start; const task_start = this.task._start;
const gantt_start = this.gantt.gantt_start; const gantt_start = this.gantt.gantt_start;
const diff = date_utils.diff(task_start, gantt_start, 'hour'); const diff = date_utils.diff(
let x = (diff / step) * column_width; task_start,
gantt_start,
this.gantt.config.unit,
);
let x = diff * column_width;
/* Since the column width is based on 30, /* Since the column width is based on 30,
we count the month-difference, multiply it by 30 for a "pseudo-month" 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 and then add the days in the month, making sure the number does not exceed 29
so it is within the column */ so it is within the column */
if (this.gantt.view_is('Month')) { if (this.gantt.view_is('Month')) {
const diffDaysBasedOn30DayMonths = const diffDaysBasedOn30DayMonths =
date_utils.diff(task_start, gantt_start, 'month') * 30; date_utils.diff(task_start, gantt_start, 'month') * 30;
@ -529,6 +528,7 @@ export default class Bar {
x = (diff * column_width) / 30; x = (diff * column_width) / 30;
} }
this.x = x; this.x = x;
} }
@ -541,8 +541,11 @@ export default class Bar {
compute_duration() { compute_duration() {
this.duration = this.duration =
date_utils.diff(this.task._end, this.task._start, 'hour') / date_utils.diff(
this.gantt.options.step; this.task._end,
this.task._start,
this.gantt.config.unit,
) / this.gantt.config.step;
} }
get_snap_position(dx) { get_snap_position(dx) {
@ -551,29 +554,29 @@ export default class Bar {
position; position;
if (this.gantt.view_is('Week')) { if (this.gantt.view_is('Week')) {
rem = dx % (this.gantt.options.column_width / 7); rem = dx % (this.gantt.config.column_width / 7);
position = position =
odx - odx -
rem + rem +
(rem < this.gantt.options.column_width / 14 (rem < this.gantt.config.column_width / 14
? 0 ? 0
: this.gantt.options.column_width / 7); : this.gantt.config.column_width / 7);
} else if (this.gantt.view_is('Month')) { } else if (this.gantt.view_is('Month')) {
rem = dx % (this.gantt.options.column_width / 30); rem = dx % (this.gantt.config.column_width / 30);
position = position =
odx - odx -
rem + rem +
(rem < this.gantt.options.column_width / 60 (rem < this.gantt.config.column_width / 60
? 0 ? 0
: this.gantt.options.column_width / 30); : this.gantt.config.column_width / 30);
} else { } else {
rem = dx % this.gantt.options.column_width; rem = dx % this.gantt.config.column_width;
position = position =
odx - odx -
rem + rem +
(rem < this.gantt.options.column_width / 2 (rem < this.gantt.config.column_width / 2
? 0 ? 0
: this.gantt.options.column_width); : this.gantt.config.column_width);
} }
return position; return position;
} }
@ -592,7 +595,7 @@ export default class Bar {
this.compute_expected_progress(); this.compute_expected_progress();
this.$expected_bar_progress.setAttribute( this.$expected_bar_progress.setAttribute(
'width', 'width',
this.gantt.options.column_width * this.gantt.config.column_width *
this.duration * this.duration *
(this.expected_progress / 100) || 0, (this.expected_progress / 100) || 0,
); );

View File

@ -6,7 +6,6 @@ const MINUTE = 'minute';
const SECOND = 'second'; const SECOND = 'second';
const MILLISECOND = 'millisecond'; const MILLISECOND = 'millisecond';
export default { export default {
parse_duration(duration) { parse_duration(duration) {
const regex = /([0-9]+)(y|m|d|h|min|s|ms)/gm; const regex = /([0-9]+)(y|m|d|h|min|s|ms)/gm;
@ -84,7 +83,7 @@ export default {
month: 'long', month: 'long',
}); });
const dateTimeFormatShort = new Intl.DateTimeFormat(lang, { const dateTimeFormatShort = new Intl.DateTimeFormat(lang, {
month: "short", month: 'short',
}); });
const month_name = dateTimeFormat.format(date); const month_name = dateTimeFormat.format(date);
const month_name_capitalized = const month_name_capitalized =

115
src/defaults.js Normal file
View File

@ -0,0 +1,115 @@
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)
: '',
},
{
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)
: '',
},
{
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)
: '',
},
{
name: 'Day',
padding: '14d',
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)
: '',
},
{
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)
: '',
},
{
name: 'Month',
padding: '1m',
step: '1m',
column_width: 120,
format_string: 'YYYY-MM',
lower_text: 'MMMM',
upper_text: (d, ld, lang) =>
d.getMonth() !== ld.getMonth()
? date_utils.format(d, 'YYYY', lang)
: '',
},
{
name: 'Year',
padding: '1m',
step: '1y',
column_width: 120,
format_string: 'YYYY',
lower_text: 'YYYY',
upper_text: (d, ld, lang) =>
d.getMonth() !== ld.getMonth()
? date_utils.format(d, 'YYYY', lang)
: '',
},
];
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,
};
export { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES };

View File

@ -1,55 +1,22 @@
import date_utils from './date_utils'; import date_utils from './date_utils';
import { $, createSVG } from './svg_utils'; import { $, createSVG } from './svg_utils';
import Bar from './bar';
import Arrow from './arrow'; import Arrow from './arrow';
import Bar from './bar';
import Popup from './popup'; import Popup from './popup';
import { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES } from './defaults';
import './gantt.css'; import './gantt.css';
const VIEW_MODE = { const VIEW_MODE = {
HOUR: 'Hour', HOUR: DEFAULT_VIEW_MODES[0],
QUARTER_DAY: 'Quarter Day', QUARTER_DAY: DEFAULT_VIEW_MODES[1],
HALF_DAY: 'Half Day', HALF_DAY: DEFAULT_VIEW_MODES[2],
DAY: 'Day', DAY: DEFAULT_VIEW_MODES[3],
WEEK: 'Week', WEEK: DEFAULT_VIEW_MODES[4],
MONTH: 'Month', MONTH: DEFAULT_VIEW_MODES[5],
YEAR: 'Year', YEAR: DEFAULT_VIEW_MODES[6],
};
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'],
};
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,
}; };
export default class Gantt { export default class Gantt {
@ -57,7 +24,6 @@ export default class Gantt {
this.setup_wrapper(wrapper); this.setup_wrapper(wrapper);
this.setup_options(options); this.setup_options(options);
this.setup_tasks(tasks); this.setup_tasks(tasks);
// initialize with default view mode
this.change_view_mode(); this.change_view_mode();
this.bind_events(); this.bind_events();
} }
@ -113,25 +79,12 @@ export default class Gantt {
this.options = { ...DEFAULT_OPTIONS, ...options }; this.options = { ...DEFAULT_OPTIONS, ...options };
const custom_mode = this.options.custom_view_modes const custom_mode = this.options.custom_view_modes
? this.options.custom_view_modes.find( ? this.options.custom_view_modes.find(
(m) => m.name === this.options.view_mode, (m) => m.name === this.config.view_mode.name,
) )
: null; : null;
if (custom_mode) this.options = { ...this.options, custom_mode }; 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 = { this.config = {};
...VIEW_MODE_PADDING,
...options.view_mode_padding,
};
} }
setup_tasks(tasks) { setup_tasks(tasks) {
@ -234,54 +187,21 @@ export default class Gantt {
} }
change_view_mode(mode = this.options.view_mode) { 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.update_view_scale(mode);
this.setup_dates(); this.setup_dates();
this.render(); this.render();
// fire viewmode_change event
this.trigger_event('view_change', [mode]); this.trigger_event('view_change', [mode]);
} }
update_view_scale(view_mode) { update_view_scale(mode) {
this.options.view_mode = view_mode; let { duration, scale } = date_utils.parse_duration(mode.step);
const custom_mode = this.options.custom_mode; this.config.step = duration;
if (custom_mode) { this.config.unit = scale;
//configure step and column width for custom view case this.config.column_width = mode.column_width || 38;
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;
}
} }
setup_dates() { setup_dates() {
@ -290,60 +210,39 @@ export default class Gantt {
} }
setup_gantt_dates() { setup_gantt_dates() {
this.gantt_start = this.gantt_end = null; // set global start and end 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;
}
}
let gantt_start, gantt_end; let gantt_start, gantt_end;
if (!this.gantt_start) gantt_start = new Date(); for (let task of this.tasks) {
else gantt_start = date_utils.start_of(this.gantt_start, 'day'); if (!gantt_start || task._start < gantt_start) {
if (!this.gantt_end) gantt_end = new Date(); gantt_start = task._start;
else gantt_end = date_utils.start_of(this.gantt_end, 'day'); }
if (!gantt_end || task._end > gantt_end) {
const custom_mode = this.options.custom_mode; gantt_end = task._end;
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;
}
} }
[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 = date_utils.add(
gantt_start, gantt_start,
-padding_start.duration, -padding_start.duration,
padding_start.scale, padding_start.scale,
); );
let format_string; let format_string =
if (this.view_is(VIEW_MODE.YEAR)) { this.config.view_mode.format_string || 'YYYY-MM-DD HH';
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';
}
this.gantt_start = date_utils.parse( this.gantt_start = date_utils.parse(
date_utils.format(gantt_start, format_string), date_utils.format(gantt_start, format_string),
); );
@ -356,36 +255,15 @@ export default class Gantt {
} }
setup_date_values() { setup_date_values() {
this.dates = []; let cur_date = this.gantt_start;
let cur_date = null; this.dates = [cur_date];
while (cur_date === null || cur_date < this.gantt_end) { while (cur_date < this.gantt_end) {
if (this.options.custom_mode) { cur_date = date_utils.add(
const step = this.options.custom_mode.step || 1; cur_date,
const unit = this.options.custom_mode.unit || 'day'; this.config.step,
this.config.unit,
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',
);
}
}
}
this.dates.push(cur_date); this.dates.push(cur_date);
} }
} }
@ -434,7 +312,7 @@ export default class Gantt {
} }
make_grid_background() { 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 = const grid_height =
this.options.header_height + this.options.header_height +
this.options.padding + this.options.padding +
@ -458,7 +336,7 @@ export default class Gantt {
make_grid_rows() { make_grid_rows() {
const rows_layer = createSVG('g', { append_to: this.layers.grid }); 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; const row_height = this.options.bar_height + this.options.padding;
let row_y = this.options.header_height + this.options.padding / 2; let row_y = this.options.header_height + this.options.padding / 2;
@ -485,7 +363,7 @@ export default class Gantt {
let $header = document.createElement('div'); let $header = document.createElement('div');
$header.style.height = this.options.header_height + 10 + 'px'; $header.style.height = this.options.header_height + 10 + 'px';
$header.style.width = $header.style.width =
this.dates.length * this.options.column_width + 'px'; this.dates.length * this.config.column_width + 'px';
$header.classList.add('grid-header'); $header.classList.add('grid-header');
this.$header = $header; this.$header = $header;
this.$container.appendChild($header); this.$container.appendChild($header);
@ -524,7 +402,7 @@ export default class Gantt {
$option.textContent = VIEW_MODE[key]; $option.textContent = VIEW_MODE[key];
$select.appendChild($option); $select.appendChild($option);
} }
// $select.value = this.options.view_mode
$select.addEventListener( $select.addEventListener(
'change', 'change',
function () { function () {
@ -608,7 +486,7 @@ export default class Gantt {
let row_y = this.options.header_height + this.options.padding / 2; 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; const row_height = this.options.bar_height + this.options.padding;
if (this.options.lines !== 'vertical') { if (this.options.lines !== 'vertical') {
for (let _ of this.tasks) { for (let _ of this.tasks) {
@ -651,10 +529,10 @@ export default class Gantt {
if (this.view_is(VIEW_MODE.MONTH)) { if (this.view_is(VIEW_MODE.MONTH)) {
tick_x += tick_x +=
(date_utils.get_days_in_month(date) * (date_utils.get_days_in_month(date) *
this.options.column_width) / this.config.column_width) /
30; 30;
} else { } else {
tick_x += this.options.column_width; tick_x += this.config.column_width;
} }
} }
} }
@ -669,8 +547,8 @@ export default class Gantt {
if (d.getDay() === 0 || d.getDay() === 6) { if (d.getDay() === 0 || d.getDay() === 6) {
const x = const x =
(date_utils.diff(d, this.gantt_start, 'hour') / (date_utils.diff(d, this.gantt_start, 'hour') /
this.options.step) * this.config.step) *
this.options.column_width; this.config.column_width;
const height = const height =
(this.options.bar_height + this.options.padding) * (this.options.bar_height + this.options.padding) *
this.tasks.length; this.tasks.length;
@ -679,7 +557,7 @@ export default class Gantt {
y: this.options.header_height + this.options.padding / 2, y: this.options.header_height + this.options.padding / 2,
width: width:
(this.view_is('Day') ? 1 : 2) * (this.view_is('Day') ? 1 : 2) *
this.options.column_width, this.config.column_width,
height, height,
class: 'holiday-highlight', class: 'holiday-highlight',
append_to: this.layers.grid, append_to: this.layers.grid,
@ -689,23 +567,24 @@ export default class Gantt {
} }
/** /**
* Compute the horizontal x-axis distance and associated date for the current date and view. * Compute the horizontal x-axis distance and associated date for the current date and view.
* *
* @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. * @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) { computeGridHighlightDimensions(view_mode) {
const todayDate = new Date(); const todayDate = new Date();
if (todayDate < this.gantt_start || todayDate > this.gantt_end) return null; if (todayDate < this.gantt_start || todayDate > this.gantt_end)
return null;
let x = this.options.column_width / 2; let x = this.config.column_width / 2;
if (this.view_is(VIEW_MODE.DAY)) { if (this.view_is(VIEW_MODE.DAY)) {
return { return {
x: x:
x + x +
(date_utils.diff(today, this.gantt_start, 'hour') / (date_utils.diff(today, this.gantt_start, 'hour') /
this.options.step) * this.config.step) *
this.options.column_width, this.config.column_width,
date: today, date: today,
}; };
} }
@ -728,7 +607,7 @@ export default class Gantt {
if (todayDate >= startDate && todayDate <= endDate) { if (todayDate >= startDate && todayDate <= endDate) {
return { x, date: startDate }; return { x, date: startDate };
} else { } else {
x += this.options.column_width; x += this.config.column_width;
} }
} }
@ -746,7 +625,9 @@ export default class Gantt {
this.view_is(VIEW_MODE.YEAR) this.view_is(VIEW_MODE.YEAR)
) { ) {
// Used as we must find the _end_ of session if view is not Day // Used as we must find the _end_ of session if view is not Day
const highlightDimensions = this.computeGridHighlightDimensions(this.options.view_mode); const highlightDimensions = this.computeGridHighlightDimensions(
this.config.view_mode,
);
if (!highlightDimensions) return; if (!highlightDimensions) return;
const { x: left, date } = highlightDimensions; const { x: left, date } = highlightDimensions;
if (!this.dates.find((d) => d.getTime() == date.getTime())) return; if (!this.dates.find((d) => d.getTime() == date.getTime())) return;
@ -830,152 +711,8 @@ export default class Gantt {
let last_date = last_date_info let last_date = last_date_info
? last_date_info.date ? last_date_info.date
: date_utils.add(date, 1, 'day'); : 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 column_width = let column_width = this.config.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;
const base_pos = { const base_pos = {
x: last_date_info x: last_date_info
@ -1000,34 +737,25 @@ export default class Gantt {
Year_lower: column_width / 2, Year_lower: column_width / 2,
Year_upper: (column_width * 30) / 2, Year_upper: (column_width * 30) / 2,
}; };
if (custom_mode) { let upper_text = this.config.view_mode.upper_text;
x_pos[`${custom_mode.name}_upper`] = column_width / 2; let lower_text = this.config.view_mode.lower_text;
x_pos[`${custom_mode.name}_lower`] =
column_width /
(custom_mode.unit.toLowerCase() === 'day' ? 1 : 2);
}
return { return {
date, date,
formatted_date: date_utils.format(date).replaceAll(' ', '_'), formatted_date: date_utils.format(date).replaceAll(' ', '_'),
column_width, column_width: this.config.column_width,
base_pos_x: base_pos.x, base_pos_x: base_pos.x,
upper_text: this.options.upper_text upper_text:
? this.options.upper_text( typeof upper_text === 'string'
date, ? date_utils.format(date, upper_text, this.options.language)
this.options.view_mode, : upper_text(date, last_date, this.options.language),
date_text[`${this.options.view_mode}_upper`], lower_text:
) typeof upper_text === 'string'
: date_text[`${this.options.view_mode}_upper`], ? date_utils.format(date, lower_text, this.options.language)
lower_text: this.options.lower_text : lower_text(date, last_date, this.options.language),
? this.options.lower_text( upper_x: base_pos.x + x_pos[`${this.config.view_mode.name}_upper`],
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_y: base_pos.upper_y, upper_y: base_pos.upper_y,
lower_x: base_pos.x + x_pos[`${this.options.view_mode}_lower`], lower_x: base_pos.x + x_pos[`${this.config.view_mode.name}_lower`],
lower_y: base_pos.lower_y, lower_y: base_pos.lower_y,
}; };
} }
@ -1098,9 +826,9 @@ export default class Gantt {
date_utils.diff(date, this.gantt_start, 'hour') + 24; date_utils.diff(date, this.gantt_start, 'hour') + 24;
const scroll_pos = const scroll_pos =
(hours_before_first_task / this.options.step) * (hours_before_first_task / this.config.step) *
this.options.column_width - this.config.column_width -
this.options.column_width; this.config.column_width;
parent_element.scrollTo({ left: scroll_pos, behavior: 'smooth' }); parent_element.scrollTo({ left: scroll_pos, behavior: 'smooth' });
} }
@ -1137,7 +865,6 @@ export default class Gantt {
$.on(this.$svg, 'mousedown', '.bar-wrapper, .handle', (e, element) => { $.on(this.$svg, 'mousedown', '.bar-wrapper, .handle', (e, element) => {
const bar_wrapper = $.closest('.bar-wrapper', element); const bar_wrapper = $.closest('.bar-wrapper', element);
bars.forEach((bar) => bar.group.classList.remove('active')); bars.forEach((bar) => bar.group.classList.remove('active'));
if (element.classList.contains('left')) { if (element.classList.contains('left')) {
is_resizing_left = true; is_resizing_left = true;
} else if (element.classList.contains('right')) { } else if (element.classList.contains('right')) {
@ -1187,13 +914,13 @@ export default class Gantt {
} }
const daysSinceStart = const daysSinceStart =
((e.currentTarget.scrollLeft / this.options.column_width) * ((e.currentTarget.scrollLeft / this.config.column_width) *
this.options.step) / this.config.step) /
24; 24;
let format_str = 'D MMM'; 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'; 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'; format_str = 'MMMM';
else if (this.view_is('Half Day')) format_str = 'D'; else if (this.view_is('Half Day')) format_str = 'D';
else if (this.view_is('Hour')) format_str = 'D MMMM'; else if (this.view_is('Hour')) format_str = 'D MMMM';
@ -1310,7 +1037,6 @@ export default class Gantt {
is_resizing = true; is_resizing = true;
x_on_start = e.offsetX || e.layerX; x_on_start = e.offsetX || e.layerX;
y_on_start = e.offsetY || e.layerY; y_on_start = e.offsetY || e.layerY;
console.log(e, handle);
const $bar_wrapper = $.closest('.bar-wrapper', handle); const $bar_wrapper = $.closest('.bar-wrapper', handle);
const id = $bar_wrapper.getAttribute('data-id'); const id = $bar_wrapper.getAttribute('data-id');
@ -1328,12 +1054,6 @@ export default class Gantt {
$.on(this.$svg, 'mousemove', (e) => { $.on(this.$svg, 'mousemove', (e) => {
if (!is_resizing) return; if (!is_resizing) return;
let dx = (e.offsetX || e.layerX) - x_on_start; 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) { if (dx > $bar_progress.max_dx) {
dx = $bar_progress.max_dx; dx = $bar_progress.max_dx;
} }
@ -1382,29 +1102,29 @@ export default class Gantt {
position; position;
if (this.view_is(VIEW_MODE.WEEK)) { if (this.view_is(VIEW_MODE.WEEK)) {
rem = dx % (this.options.column_width / 7); rem = dx % (this.config.column_width / 7);
position = position =
odx - odx -
rem + rem +
(rem < this.options.column_width / 14 (rem < this.config.column_width / 14
? 0 ? 0
: this.options.column_width / 7); : this.config.column_width / 7);
} else if (this.view_is(VIEW_MODE.MONTH)) { } else if (this.view_is(VIEW_MODE.MONTH)) {
rem = dx % (this.options.column_width / 30); rem = dx % (this.config.column_width / 30);
position = position =
odx - odx -
rem + rem +
(rem < this.options.column_width / 60 (rem < this.config.column_width / 60
? 0 ? 0
: this.options.column_width / 30); : this.config.column_width / 30);
} else { } else {
rem = dx % this.options.column_width; rem = dx % this.config.column_width;
position = position =
odx - odx -
rem + rem +
(rem < this.options.column_width / 2 (rem < this.config.column_width / 2
? 0 ? 0
: this.options.column_width); : this.config.column_width);
} }
return position; return position;
} }
@ -1418,11 +1138,11 @@ export default class Gantt {
view_is(modes) { view_is(modes) {
if (typeof modes === 'string') { if (typeof modes === 'string') {
return this.options.view_mode === modes; return this.config.view_mode.name === modes;
} }
if (Array.isArray(modes)) { if (Array.isArray(modes)) {
return modes.some((mode) => this.options.view_mode === mode); return modes.some((mode) => this.config.view_mode.name === mode);
} }
return false; return false;