frappe / gantt

Open Source Javascript Gantt
https://frappe.io/gantt
MIT License
4.67k stars 1.04k forks source link

How about a 'Quarter' and 'Year' view mode? #223

Open ericspletzer opened 3 years ago

ericspletzer commented 3 years ago

Has anyone built a Quarter or Year view mode? Or can anyone point me to where I can edit and add? Thanks!

mikeott commented 3 years ago

If it helps to know, year is already baked-in. Just change view_mode:

var gantt = new Gantt("#gantt", tasks, {
    view_mode: 'Year'
});
ericspletzer commented 3 years ago

Thanks

Eric Spletzer, CEO, PE, CSLB, AIA Assoc, LEED AP Crafted Earth, Inc. 166 Alpine Street, San Rafael, CA 94901 415.699.9662


From: Michael Ott @.> Sent: Thursday, April 22, 2021 11:53:28 PM To: frappe/gantt @.> Cc: Eric Spletzer @.>; Author @.> Subject: Re: [frappe/gantt] How about a 'Quarter' and 'Year' view mode? (#223)

If it helps to know, year is already baked-in. Just change view_mode:

var gantt = new Gantt("#gantt", tasks, { view_mode: 'Year' });

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/frappe/gantt/issues/223#issuecomment-825434505, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ALGO7PG7L355IIZX53DL6HTTKEKORANCNFSM43N7BNUA.

cridenour commented 3 years ago

Just had to write this. I didn't implement the dragging behavior as our charts are read only but it should be simple to do. Just search the source Gantt class for "view_mode" to find the functions I didn't change.

import Gantt from 'frappe-gantt';
import date_utils from "frappe-gantt/src/date_utils";
import {format, startOfQuarter, addQuarters, endOfQuarter} from 'date-fns';

class QuarterGantt extends Gantt {

  update_view_scale(view_mode) {
    this.options.view_mode = view_mode;

    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.QUARTER) {
      this.options.step = 24 * 90;
      this.options.column_width = 120;
    } else if (view_mode === VIEW_MODE.YEAR) {
      this.options.step = 24 * 365;
      this.options.column_width = 120;
    }
  }

  get_date_info(date, last_date, i) {
    if (!last_date) {
      last_date = date_utils.add(date, 1, 'year');
    }
    const date_text = {
      '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),
      Quarter_lower: format(date, 'QQQ'),
      Year_lower: date_utils.format(date, 'YYYY', 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()
          ? 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)
          : '',
      Quarter_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)
          : ''
    };

    const base_pos = {
      x: i * this.options.column_width,
      lower_y: this.options.header_height,
      upper_y: this.options.header_height - 25
    };

    const x_pos = {
      'Quarter Day_lower': this.options.column_width * 4 / 2,
      'Quarter Day_upper': 0,
      'Half Day_lower': this.options.column_width * 2 / 2,
      'Half Day_upper': 0,
      Day_lower: this.options.column_width / 2,
      Day_upper: this.options.column_width * 30 / 2,
      Week_lower: 0,
      Week_upper: this.options.column_width * 4 / 2,
      Month_lower: this.options.column_width / 2,
      Quarter_lower: this.options.column_width / 2,
      Month_upper: this.options.column_width * 12 / 2,
      Quarter_upper: this.options.column_width * 4 / 2,
      Year_lower: this.options.column_width / 2,
      Year_upper: this.options.column_width * 30 / 2
    };

    return {
      upper_text: date_text[`${this.options.view_mode}_upper`],
      lower_text: 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,
      lower_x: base_pos.x + x_pos[`${this.options.view_mode}_lower`],
      lower_y: base_pos.lower_y
    };
  }

  setup_date_values() {
    this.dates = [];
    let cur_date = null;

    while (cur_date === null || cur_date < this.gantt_end) {
      if (!cur_date) {
        cur_date = date_utils.clone(this.gantt_start);
        if (this.view_is(VIEW_MODE.QUARTER)) {
           cur_date = startOfQuarter(cur_date)
        }
      } else {
        if (this.view_is(VIEW_MODE.YEAR)) {
          cur_date = date_utils.add(cur_date, 1, 'year');
        } else if (this.view_is(VIEW_MODE.QUARTER)) {
          cur_date = addQuarters(cur_date, 1)
        } 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);
    }
  }

  make_grid_ticks() {
    let tick_x = 0;
    let tick_y = this.options.header_height + this.options.padding / 2;
    let tick_height =
      (this.options.bar_height + this.options.padding) *
      this.tasks.length;

    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
      ) {
        tick_class += ' thick';
      }
      // thick ticks for quarters
      if (this.view_is(VIEW_MODE.MONTH) && (date.getMonth() + 1) % 3 === 0) {
        tick_class += ' thick';
      }

      if (this.view_is(VIEW_MODE.QUARTER)) {
        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)) {
        tick_x +=
          date_utils.get_days_in_month(date) *
          this.options.column_width /
          30;
      } else {
        tick_x += this.options.column_width;
      }
    }
  }

  setup_gantt_dates() {
    this.gantt_start = this.gantt_end = null;

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

    this.gantt_start = date_utils.start_of(this.gantt_start, 'day');
    this.gantt_end = date_utils.start_of(this.gantt_end, 'day');

    // I only wanted padding on quarter, but you can see the original if you want it on other modes
    if (this.view_is(VIEW_MODE.QUARTER)) {
      this.gantt_start = startOfQuarter(this.gantt_start);
      this.gantt_end = endOfQuarter(this.gantt_end);
    }
  }
}
DakotaWray2 commented 3 years ago

@cridenour mind sharing your date_utils and date-fns?

cridenour commented 3 years ago

@DakotaWray2 date_utils is imported from frappe-gantt/src/date_utils and date-fns can be found on npm.

DakotaWray2 commented 3 years ago

@cridenour ahh ok thanks for the quick reply! Thought you might have modified. much appreciated!

aloisdg commented 2 years ago

Looking to use quarter in my own project. I am willing to create A PR for this project. Would it be useful?

NaTTechnologies commented 1 year ago

core.js:6456 ERROR ReferenceError: createSVG is not defined at QuarterGantt.make_grid_ticks (FrappeCustom.js:184:1) at QuarterGantt.make_grid (index.js:300:1) at QuarterGantt.render (index.js:275:1) at QuarterGantt.change_view_mode (index.js:177:1) at new Gantt (index.js:24:1) at new QuarterGantt (FrappeCustom.js:5:8) at DiagramaGanttV2Component.ngOnChanges (diagrama-gantt-v2.component.ts:54:17) at DiagramaGanttV2Component.rememberChangeHistoryAndInvokeOnChangesHook (core.js:1498:1) at callHook (core.js:2536:1) at callHooks (core.js:2495:1)

NaTTechnologies commented 1 year ago
import Gantt from 'frappe-gantt';
import date_utils from "frappe-gantt/src/date_utils";
import * as svg_utils from "frappe-gantt/src/svg_utils";
import {format, startOfQuarter, addQuarters, endOfQuarter} from 'date-fns';

Gantt.VIEW_MODE.QUARTER = "Quarter";

var viewMode = Gantt.VIEW_MODE;

console.log(viewMode)

export class QuarterGantt extends Gantt {

  update_view_scale(view_mode) {
    this.options.view_mode = view_mode;

    if (view_mode === Gantt.VIEW_MODE.DAY) {
      this.options.step = 24;
      this.options.column_width = 38;
    } else if (view_mode === Gantt.VIEW_MODE.HALF_DAY) {
      this.options.step = 24 / 2;
      this.options.column_width = 38;
    } else if (view_mode === Gantt.VIEW_MODE.QUARTER_DAY) {
      this.options.step = 24 / 4;
      this.options.column_width = 38;
    } else if (view_mode === Gantt.VIEW_MODE.WEEK) {
      this.options.step = 24 * 7;
      this.options.column_width = 140;
    } else if (view_mode === Gantt.VIEW_MODE.MONTH) {
      this.options.step = 24 * 30;
      this.options.column_width = 120;
    } else if (view_mode === Gantt.VIEW_MODE.QUARTER) {
      this.options.step = 24 * 90;
      this.options.column_width = 120;
    } else if (view_mode === Gantt.VIEW_MODE.YEAR) {
      this.options.step = 24 * 365;
      this.options.column_width = 120;
    }
  }

  get_date_info(date, last_date, i) {
    if (!last_date) {
      last_date = date_utils.add(date, 1, 'year');
    }
    const date_text = {
      '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),
      Quarter_lower: format(date, 'QQQ'),
      Year_lower: date_utils.format(date, 'YYYY', 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()
          ? 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)
          : '',
      Quarter_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)
          : ''
    };

    const base_pos = {
      x: i * this.options.column_width,
      lower_y: this.options.header_height,
      upper_y: this.options.header_height - 25
    };

    const x_pos = {
      'Quarter Day_lower': this.options.column_width * 4 / 2,
      'Quarter Day_upper': 0,
      'Half Day_lower': this.options.column_width * 2 / 2,
      'Half Day_upper': 0,
      Day_lower: this.options.column_width / 2,
      Day_upper: this.options.column_width * 30 / 2,
      Week_lower: 0,
      Week_upper: this.options.column_width * 4 / 2,
      Month_lower: this.options.column_width / 2,
      Quarter_lower: this.options.column_width / 2,
      Month_upper: this.options.column_width * 12 / 2,
      Quarter_upper: this.options.column_width * 4 / 2,
      Year_lower: this.options.column_width / 2,
      Year_upper: this.options.column_width * 30 / 2
    };

    return {
      upper_text: date_text[`${this.options.view_mode}_upper`],
      lower_text: 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,
      lower_x: base_pos.x + x_pos[`${this.options.view_mode}_lower`],
      lower_y: base_pos.lower_y
    };
  }

  setup_date_values() {
    this.dates = [];
    let cur_date = null;

    while (cur_date === null || cur_date < this.gantt_end) {
      if (!cur_date) {
        cur_date = date_utils.clone(this.gantt_start);
        if (this.view_is(Gantt.VIEW_MODE.QUARTER)) {
           cur_date = startOfQuarter(cur_date)
        }
      } else {
        if (this.view_is(Gantt.VIEW_MODE.YEAR)) {
          cur_date = date_utils.add(cur_date, 1, 'year');
        } else if (this.view_is(Gantt.VIEW_MODE.QUARTER)) {
          cur_date = addQuarters(cur_date, 1)
        } else if (this.view_is(Gantt.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);
    }
  }

  make_grid_ticks() {
    let tick_x = 0;
    let tick_y = this.options.header_height + this.options.padding / 2;
    let tick_height =
      (this.options.bar_height + this.options.padding) *
      this.tasks.length;

    for (let date of this.dates) {
      let tick_class = 'tick';
      // thick tick for monday
      if (this.view_is(Gantt.VIEW_MODE.DAY) && date.getDate() === 1) {
        tick_class += ' thick';
      }
      // thick tick for first week
      if (
        this.view_is(Gantt.VIEW_MODE.WEEK) &&
        date.getDate() >= 1 &&
        date.getDate() < 8
      ) {
        tick_class += ' thick';
      }
      // thick ticks for quarters
      if (this.view_is(Gantt.VIEW_MODE.MONTH) && (date.getMonth() + 1) % 3 === 0) {
        tick_class += ' thick';
      }

      if (this.view_is(Gantt.VIEW_MODE.QUARTER)) {
        tick_class += ' thick';
      }

      svg_utils.createSVG('path', {
        d: `M ${tick_x} ${tick_y} v ${tick_height}`,
        class: tick_class,
        append_to: this.layers.grid
      });

      if (this.view_is(Gantt.VIEW_MODE.MONTH)) {
        tick_x +=
          date_utils.get_days_in_month(date) *
          this.options.column_width /
          30;
      } else {
        tick_x += this.options.column_width;
      }
    }
  }

  setup_gantt_dates() {
    this.gantt_start = this.gantt_end = null;

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

    this.gantt_start = date_utils.start_of(this.gantt_start, 'day');
    this.gantt_end = date_utils.start_of(this.gantt_end, 'day');

    // I only wanted padding on quarter, but you can see the original if you want it on other modes
    if (this.view_is(Gantt.VIEW_MODE.QUARTER)) {
      this.gantt_start = startOfQuarter(this.gantt_start);
      this.gantt_end = endOfQuarter(this.gantt_end);
    }
  }
}
MariaBee3 commented 12 months ago

Unfortunately I couldn't use date-fns in my project so I had to write a work-around based on this solution. So if anyone is interested in this solution without using date-fns, here are my adjustments to the code. : var date_utils = { format(date, format_string = "YYYY-MM-DD HH:mm:ss.SSS", lang = "en") { const values = this.get_date_values(date).map((d) => padStart(d, 2, 0)); const month = +values[1] + 1; //new const quarter = Math.ceil(month / 3); //new const format_map = { YYYY: values[0], MM: padStart(+values[1] + 1, 2, 0), DD: values[2], HH: values[3], mm: values[4], ss: values[5], SSS: values[6], D: values[2], MMMM: month_names[lang][+values[1]], MMM: month_names[lang][+values[1]], QQ: "Q" + quarter.toString(), // new - add quarter format };

    let str = format_string;
    const formatted_values = [];

    Object.keys(format_map)
      .sort((a, b) => b.length - a.length)
      .forEach((key) => {
        if (str.includes(key)) {
          str = str.replace(key, `$${formatted_values.length}`);
          formatted_values.push(format_map[key]);
        }
      });

    formatted_values.forEach((value, i) => {
      str = str.replace(`$${i}`, value);
    });

    return str;
  },

  diff(date_a, date_b, scale = DAY) {
    let milliseconds, seconds, hours, minutes, days, months, quarters, years; //new - add quarters

    milliseconds = date_a - date_b;
    seconds = milliseconds / 1000;
    minutes = seconds / 60;
    hours = minutes / 60;
    days = hours / 24;
    months = days / 30;
    quarters = months / 3; //new
    years = months / 12;

    if (!scale.endsWith("s")) {
      scale += "s";
    }

    return Math.floor(
      {
        milliseconds,
        seconds,
        minutes,
        hours,
        days,
        months,
        quarters, //new
        years,
      }[scale]
    );
  },

}

const VIEW_MODE = { QUARTER_DAY: "Quarter Day", HALF_DAY: "Half Day", DAY: "Day", WEEK: "Week", MONTH: "Month", QUARTER: "Quarter", //new - adds quarter to options view_mode YEAR: "Year", };

 //within class Gantt

update_view_scale(view_mode) { this.options.view_mode = view_mode;

     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.QUARTER) { //adds steps in view_mode = Quarter
       this.options.step = 24 * 30 * 3;
       this.options.column_width = 120;
     } else if (view_mode === VIEW_MODE.YEAR) {
       this.options.step = 24 * 365;
       this.options.column_width = 120;
     }
   }

setup_gantt_dates() { this.gantt_start = this.gantt_end = null;

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

     this.gantt_start = date_utils.start_of(this.gantt_start, "day");
     this.gantt_end = date_utils.start_of(this.gantt_end, "day");

     // add date padding on both sides
     if (this.view_is([VIEW_MODE.QUARTER_DAY, VIEW_MODE.HALF_DAY])) {
       this.gantt_start = date_utils.add(this.gantt_start, -7, "day");
       this.gantt_end = date_utils.add(this.gantt_end, 7, "day");
     } else if (this.view_is(VIEW_MODE.MONTH)) {
       this.gantt_start = date_utils.start_of(this.gantt_start, "year");
       this.gantt_end = date_utils.add(this.gantt_end, 1, "year");
     } else if (this.view_is(VIEW_MODE.QUARTER)) {  //new 0 adds quarter
       this.gantt_start = date_utils.start_of(this.gantt_start, "year");
       this.gantt_end = date_utils.add(this.gantt_end, 1, "year");
     } else if (this.view_is(VIEW_MODE.YEAR)) {
       this.gantt_start = date_utils.add(this.gantt_start, -2, "year");
       this.gantt_end = date_utils.add(this.gantt_end, 2, "year");
     } else {
       this.gantt_start = date_utils.add(this.gantt_start, -1, "month");
       this.gantt_end = date_utils.add(this.gantt_end, 1, "month");
     }
   }

setup_date_values() { this.dates = []; let cur_date = null; let currentQuarter = null; //new

     while (cur_date === null || cur_date < this.gantt_end) {
       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");
           currentQuarter = null; // Reset quarter on year change
         } else if (this.view_is(VIEW_MODE.QUARTER)) { //new
           const month = cur_date.getMonth() + 1; //new
           const quarter = Math.ceil(month / 3); //new

           if (currentQuarter === null) {  //new
             currentQuarter = quarter; //new
           } else if (currentQuarter < 4) { //new
             currentQuarter += 1; //new
           } else { //new
             currentQuarter = 1; //new
           }
           cur_date = date_utils.add(cur_date, 3, "month"); //new
         } 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);
     }
   }

make_grid_ticks() { let tick_x = 0; let tick_y = this.options.header_height + this.options.padding / 2; let tick_height = (this.options.bar_height + this.options.padding) * this.tasks.length;

     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
       ) {
         tick_class += " thick";
       }
       // thick ticks for quarters
       if (this.view_is(VIEW_MODE.MONTH) && (date.getMonth() + 1) % 3 === 0) {
         tick_class += " thick";
       }

       if (this.view_is(VIEW_MODE.QUARTER)) { //new
         tick_class += " thick"; //new
       } //new

       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)) {
         tick_x +=
           (date_utils.get_days_in_month(date) * this.options.column_width) /
           30;
       } else {
         tick_x += this.options.column_width;
       }
     }
   }

get_date_info(date, last_date, i) {
     if (!last_date) {
       last_date = date_utils.add(date, 1, "year");
     }
     const date_text = {
       "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),
       Quarter_lower: date_utils.format(date, "QQ", this.options.language), //new
       Year_lower: date_utils.format(date, "YYYY", 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()
           ? 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)
           : "",
       Quarter_upper: //new
         date.getFullYear() !== last_date.getFullYear() //new
           ? date_utils.format(date, "YYYY", this.options.language) //new
           : "",
       Year_upper:
         date.getFullYear() !== last_date.getFullYear()
           ? date_utils.format(date, "YYYY", this.options.language)
           : "",
     };
}

const x_pos = {
       "Quarter Day_lower": (this.options.column_width * 4) / 2,
       "Quarter Day_upper": 0,
       "Half Day_lower": (this.options.column_width * 2) / 2,
       "Half Day_upper": 0,
       Day_lower: this.options.column_width / 2,
       Day_upper: (this.options.column_width * 30) / 2,
       Week_lower: 0,
       Week_upper: (this.options.column_width * 4) / 2,
       Month_lower: this.options.column_width / 2,
       Month_upper: (this.options.column_width * 12) / 2,
       Quarter_lower: this.options.column_width / 2, //new
       Quarter_upper: (this.options.column_width * 4) / 2, //new
       Year_lower: this.options.column_width / 2, 
       Year_upper: (this.options.column_width * 30) / 2,
     };
safwansamsudeen commented 6 months ago

I'm thinking of a dynamic time if we implement this feature. If not, I'm not sure how adding more views would help -as mentioned in #349, different people require different things (quarter, semester, term).