frappe / gantt

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

[Update] Bar draw in minutes #421

Open vipperxvr opened 2 months ago

vipperxvr commented 2 months ago

Bar draw starts and ends using only the hour as parameter.

I changed the code to use minutes.

image

At bar.js

  compute_x() {
    const { step, column_width } = this.gantt.options;
    const task_start = this.task._start;
    const gantt_start = this.gantt.gantt_start;

    const diff = date_utils.diff(task_start, gantt_start, "minutes");
    let x = (diff / 60 / step) * column_width;

    if (this.gantt.view_is("Month")) {
      const diff = date_utils.diff(task_start, gantt_start, "day");
      x = (diff * column_width) / 30;
    }
    this.x = Math.floor(x);
  }

  compute_duration() {
    this.duration =
      date_utils.diff(this.task._end, this.task._start, "minute") / 60 /
      this.gantt.options.step;
  }
vipperxvr commented 2 months ago

Changed the grid ticks to match the labels, making it more easy to read. In the process I fixed the month highlight too.

image

At bar.js

compute_x() {
    const { step, column_width } = this.gantt.options;
    const task_start = this.task._start;
    const gantt_start = this.gantt.gantt_start;

    const diff = date_utils.diff(task_start, gantt_start, "minute");
    let x = (diff / 60 / step) * column_width + column_width / 2;

    if (this.gantt.view_is("Month")) {
      const diff = date_utils.diff(task_start, gantt_start, "day");
      x = (diff * column_width) / 30 + column_width / 2;
    }
    this.x = Math.floor(x);
  }

At index.js

  make_grid_ticks() {
    if (!['both', 'vertical', 'horizontal'].includes(this.options.lines)) return
    let tick_x = this.options.column_width / 2;
    let tick_y = this.options.header_height + this.options.padding / 2;
    let tick_height = (this.options.bar_height + this.options.padding) * this.tasks.length;
    let $lines_layer = createSVG("g", { class: 'lines_layer', append_to: this.layers.grid });
    let row_y = this.options.header_height + this.options.padding / 2;

    const row_width = this.dates.length * this.options.column_width;
    const row_height = this.options.bar_height + this.options.padding;
    if (this.options.lines !== 'vertical') {
      for (let _ of this.tasks) {
        createSVG("line", {
          x1: 0,
          y1: row_y + row_height,
          x2: row_width,
          y2: row_y + row_height,
          class: "row-line",
          append_to: $lines_layer,
        });
        row_y += row_height;
      }
    }
    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
      ) {
        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)) {
        tick_x += (date_utils.get_days_in_month(date) * this.options.column_width) / 30;
      } else {
        tick_x += this.options.column_width;
      }
    }
  }

  make_grid_current_time(view_mode) {
    let tick_x = this.options.column_width / 2;
    let tick_y = this.options.header_height + this.options.padding / 2;
    let tick_height = (this.options.bar_height + this.options.padding) * this.tasks.length;
    createSVG("g", { class: 'current_time', append_to: this.layers.grid });

    let today = date_utils.today();
    for (let date of this.dates) {
      if (date.toString() === today.toString()) {
        let current_time_class = "current-time";
        let day_columns = 1;
        if (view_mode === VIEW_MODE.HOUR) {
          day_columns = 24;
        }
        if (view_mode === VIEW_MODE.QUARTER_DAY) {
          day_columns = 4;
        }
        if (view_mode === VIEW_MODE.HALF_DAY) {
          day_columns = 2;
        }
        let tick_secs = (this.options.column_width * day_columns) / 86400;
        let timestamp = Math.floor(new Date().getTime()) / 1000;
        let today_timestamp = Math.floor(new Date(today).getTime()) / 1000;
        let today_secs = timestamp - today_timestamp;
        tick_x += today_secs * tick_secs;

        createSVG("path", {
          d: `M ${tick_x} ${tick_y} v ${tick_height}`,
          class: current_time_class,
          append_to: this.layers.grid,
        });
        return;
      }
      tick_x += this.options.column_width;
    }
  }

  computeGridHighlightDimensions(view_mode) {
    let x = this.options.column_width / 2;

    if (this.view_is(VIEW_MODE.DAY)) {
      let today = date_utils.today();
      return {
        x: x +
          (date_utils.diff(today, this.gantt_start, "hour") / this.options.step) *
          this.options.column_width,
        date: today
      }
    }

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

Also changed index.html to make the example dynamic.

<script>
    function getDate(number, scale) {
        let today = new Date();
        switch (scale) {
            case 'minute':
                today.setMinutes(today.getMinutes() + number);
                break;
            case 'hour':
                today.setHours(today.getHours() + number);
                break;
            case 'day':
                today.setDate(today.getDate() + number);
                break;
            case 'month':
                today.setMonth(today.getMonth() + number);
                break;
            case 'year':
                today.setFullYear(today.getFullYear() + number);
                break;
        }
        return today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() + ' ' + today.getHours() + ':' + today.getMinutes() + ':' + today.getSeconds();
    }

    let tasks = [
        {
            id: "Task 0",
            start: getDate(),
            end: getDate(1, 'hour'),
            name: 'Redesign website',
            progress: 100
        },
        {
            id: "Task 1",
            start: getDate(),
            // Utilizes duration
            duration: '4d',
            name: 'Write new content',
            progress: 50,
            important: true
        },
        {
            id: "Task 2",
            start: getDate(1, 'day'),
            end: getDate(2, 'day'),
            name: 'Apply new styles',
            progress: 80,
            dependencies: 'Task 1'
        },
        {
            id: "Task 3",
            start: getDate(2, 'day'),
            end: getDate(3, 'day'),
            name: 'Review',
            progress: 20,
            dependencies: 'Task 2'
        },
        {
            id: "Task 4",
            start: getDate(3, 'day'),
            end: getDate(4, 'day'),
            name: 'Deploy',
            progress: 10,
            dependencies: 'Task 2'
        },
        {
            id: "Task 5",
            start: getDate(4, 'day'),
            end: getDate(5, 'day'),
            name: 'Go Live!',
            progress: 0,
            dependencies: 'Task 2',
            custom_class: 'bar-milestone'
        }
    ];
safwansamsudeen commented 2 months ago

Great job here! Do you want to send in a PR?

vipperxvr commented 2 months ago

Sure, check PR #428.