feat: add support for custom views with units(hour,day,month)

This commit is contained in:
Morad larbi messaoudi 2024-11-28 11:38:54 +00:00
parent bdcf9f921b
commit e4df4fe30a
4 changed files with 486 additions and 383 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -101,17 +101,25 @@
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);
}, },
// on_hover (task, x, y) { // on_hover (task, x, y) {
// console.log("Hover", x, y); // console.log("Hover", x, y);
// }, // }
view_mode: 'Day', view_mode: 'Day',
view_mode_padding: { DAY: '3d' }, view_mode_padding: { DAY: '3d' },
// popup_on: 'click', // popup_on: 'click',
custom_view_modes: [//option to add custom views
{
name: 'Custom Day',
padding: '1m',
step: 3,
unit: "day",
}
]
// popup: false,
// scroll_to: 'today', // scroll_to: 'today',
// view_mode_select: true, // view_mode_select: true,
// dates_readonly: true, // dates_readonly: true,

View File

@ -110,11 +110,13 @@ export default class Gantt {
setup_options(options) { setup_options(options) {
this.options = { ...DEFAULT_OPTIONS, ...options }; this.options = { ...DEFAULT_OPTIONS, ...options };
if (!options.view_mode_padding) options.view_mode_padding = {}; const custom_mode = this.options.custom_view_modes ? this.options.custom_view_modes.find(m => m.name === this.options.view_mode) : null;
for (let [key, value] of Object.entries(options.view_mode_padding)) { 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') { if (typeof value === 'string') {
// Configure for single value given // Configure for single value given
options.view_mode_padding[key] = [value, value]; this.options.view_mode_padding[key] = [value, value];
} }
} }
@ -122,6 +124,7 @@ export default class Gantt {
...VIEW_MODE_PADDING, ...VIEW_MODE_PADDING,
...options.view_mode_padding, ...options.view_mode_padding,
}; };
} }
setup_tasks(tasks) { setup_tasks(tasks) {
@ -205,7 +208,6 @@ export default class Gantt {
return task; return task;
}); });
this.setup_dependencies(); this.setup_dependencies();
} }
@ -234,6 +236,23 @@ export default class Gantt {
update_view_scale(view_mode) { update_view_scale(view_mode) {
this.options.view_mode = 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) { if (view_mode === VIEW_MODE.HOUR) {
this.options.step = 24 / 24; this.options.step = 24 / 24;
this.options.column_width = 38; this.options.column_width = 38;
@ -281,16 +300,22 @@ export default class Gantt {
if (!this.gantt_end) gantt_end = new Date(); if (!this.gantt_end) gantt_end = new Date();
else gantt_end = date_utils.start_of(this.gantt_end, 'day'); else gantt_end = date_utils.start_of(this.gantt_end, 'day');
// add date padding on both sides const custom_mode = this.options.custom_mode
let viewKey; let [padding_start, padding_end] = [{duration: 1, scale: 'day'},{duration: 1, scale: 'day'}]
for (let [key, value] of Object.entries(VIEW_MODE)) { if (custom_mode) {
if (value === this.options.view_mode) { [padding_start, padding_end] = [custom_mode.padding, custom_mode.padding].map(date_utils.parse_duration)
viewKey = key; }
} 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);
} }
const [padding_start, padding_end] = this.options.view_mode_padding[
viewKey
].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,
@ -323,19 +348,31 @@ export default class Gantt {
let cur_date = null; let cur_date = null;
while (cur_date === null || cur_date < this.gantt_end) { while (cur_date === null || cur_date < this.gantt_end) {
if (!cur_date) { if (this.options.custom_mode) {
cur_date = date_utils.clone(this.gantt_start); const step = this.options.custom_mode.step || 1;
} else { const unit = this.options.custom_mode.unit || 'day';
if (this.view_is(VIEW_MODE.YEAR)) {
cur_date = date_utils.add(cur_date, 1, 'year'); if (!cur_date) {
} else if (this.view_is(VIEW_MODE.MONTH)) { cur_date = date_utils.clone(this.gantt_start);
cur_date = date_utils.add(cur_date, 1, 'month');
} else { } else {
cur_date = date_utils.add( cur_date = date_utils.add(cur_date, step, unit);
cur_date, }
this.options.step, }
'hour', 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);
@ -392,7 +429,6 @@ export default class Gantt {
this.options.padding + this.options.padding +
(this.options.bar_height + this.options.padding) * (this.options.bar_height + this.options.padding) *
this.tasks.length; this.tasks.length;
createSVG('rect', { createSVG('rect', {
x: 0, x: 0,
y: 0, y: 0,
@ -415,7 +451,6 @@ export default class Gantt {
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;
for (let _ of this.tasks) { for (let _ of this.tasks) {
createSVG('rect', { createSVG('rect', {
x: 0, x: 0,
@ -596,7 +631,6 @@ export default class Gantt {
if (this.view_is(VIEW_MODE.MONTH) && date.getMonth() % 3 === 0) { if (this.view_is(VIEW_MODE.MONTH) && date.getMonth() % 3 === 0) {
tick_class += ' thick'; tick_class += ' thick';
} }
createSVG('path', { createSVG('path', {
d: `M ${tick_x} ${tick_y} v ${tick_height}`, d: `M ${tick_x} ${tick_y} v ${tick_height}`,
class: tick_class, class: tick_class,
@ -744,6 +778,7 @@ export default class Gantt {
classes: 'lower-text', classes: 'lower-text',
append_to: this.$lower_header, append_to: this.$lower_header,
}); });
$lower_text.innerText = date.lower_text; $lower_text.innerText = date.lower_text;
$lower_text.style.left = $lower_text.style.left =
+$lower_text.style.left.slice(0, -2) + 'px'; +$lower_text.style.left.slice(0, -2) + 'px';
@ -776,70 +811,104 @@ export default class Gantt {
} }
get_date_info(date, last_date_info) { get_date_info(date, last_date_info) {
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');
const date_text = { let date_text = {}
Hour_lower: date_utils.format(date, 'HH', this.options.language), const custom_mode = this.options.custom_mode
'Quarter Day_lower': date_utils.format( if (custom_mode) {
date, let lower_text,upper_text
'HH', const unit = custom_mode ? custom_mode.unit.toLowerCase() : 'day';
this.options.language, if (unit === 'hour') {
), lower_text = date_utils.format(date, 'HH', this.options.language);
'Half Day_lower': date_utils.format( upper_text = date.getDate() !== last_date.getDate()
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) ? date_utils.format(date, 'D MMMM', this.options.language)
: '', : '';
'Quarter Day_upper': } else if (unit === 'day') {
date.getDate() !== last_date.getDate() lower_text = date.getDate() !== last_date.getDate()
? date_utils.format(date, 'D MMM', this.options.language) ? date_utils.format(date, 'D', this.options.language)
: '', : '';
'Half Day_upper': upper_text = date.getMonth() !== last_date.getMonth() || !last_date_info
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) ? date_utils.format(date, 'MMMM', this.options.language)
: '', : '';
Week_upper: } else if (unit === 'month') {
date.getMonth() !== last_date.getMonth() lower_text = date_utils.format(date, 'MMMM', this.options.language);
? date_utils.format(date, 'MMMM', this.options.language) upper_text = date.getFullYear() !== last_date.getFullYear()
: '',
Month_upper:
date.getFullYear() !== last_date.getFullYear()
? date_utils.format(date, 'YYYY', this.options.language) ? date_utils.format(date, 'YYYY', this.options.language)
: '', : '';
Year_upper: } else {
date.getFullYear() !== last_date.getFullYear() lower_text = date_utils.format(date, 'YYYY', this.options.language);
? 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
let column_width = this.view_is(VIEW_MODE.MONTH) 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 = 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) / ? (date_utils.get_days_in_month(date) * this.options.column_width) /
30 30
: this.options.column_width; : this.options.column_width;
const base_pos = { const base_pos = {
x: last_date_info x: last_date_info
? last_date_info.base_pos_x + last_date_info.column_width ? last_date_info.base_pos_x + last_date_info.column_width
@ -863,6 +932,10 @@ 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){
x_pos[`${custom_mode.name}_upper`] = column_width / 2
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(' ', '_'),