diff --git a/src/bar.js b/src/bar.js index 441994e..af6cf5a 100644 --- a/src/bar.js +++ b/src/bar.js @@ -149,7 +149,7 @@ export default class Bar { $date_highlight.classList.add('date-highlight') $date_highlight.style.height = this.height * 0.8 + 'px' $date_highlight.style.width = this.width + 'px' - $date_highlight.style.top = this.gantt.options.header_height - 19 + 'px' + $date_highlight.style.top = this.gantt.options.header_height - 24 + 'px' $date_highlight.style.left = x + 'px' this.$date_highlight = $date_highlight this.gantt.$lower_header.prepend($date_highlight) diff --git a/src/gantt.scss b/src/gantt.scss index 1395ab5..26e39ec 100644 --- a/src/gantt.scss +++ b/src/gantt.scss @@ -3,21 +3,27 @@ $bar-color: #fff !default; $bar-color-important: #94c4f4 !default; $bar-stroke: #fff !default; -$border-color: #e0e0e0 !default; +$dark-stroke-color: #e0e0e0 !default; +$stroke-color: #EBEEF0 !default; $light-bg: #f5f5f5 !default; $light-border-color: #ebeff2 !default; $light-yellow: #f6e796 !default; -$holiday-color: #f3f4f7 !default; +$holiday-color: #F9FAFA !default; $text-muted: #666 !default; +$text-grey: #98A1A9; $text-light: #fff !default; $text-dark: #111 !default; -$progress-important: #2c94ec !default; -$progress: #dedfe0 !default; +$progress: #EBEEF0 !default; $handle-color: #dcdce4 !default; $handle-color-important: #94c4f4 !default; $light-blue: #c4c4e9 !default; +$middle-blue: #62B2F9 !default; +$dark-blue: #2c94ec !default; + .gantt-container { + line-height: 14.5px; + .grid-header { background-color: #ffffff; position: sticky; @@ -27,12 +33,20 @@ $light-blue: #c4c4e9 !default; .lower-text, .upper-text { - font-size: 14px; text-anchor: middle; - color: $text-muted; + color: $text-dark; + } + + .upper-header { + height: 50px; + } + + .lower-header { + height: 56px; } .lower-text { + font-size: 14px; position: absolute; width: fit-content; } @@ -40,6 +54,8 @@ $light-blue: #c4c4e9 !default; .upper-text { position: absolute; width: fit-content; + font-weight: 500; + font-size: 16px; } .current-upper { @@ -49,32 +65,51 @@ $light-blue: #c4c4e9 !default; .side-header { position: fixed; padding: 0 10px; + margin-right: 10px; background: white; + line-height: 20px; + font-weight: 400; + font-size: 13px; } - .today-button, - .viewmode-select { - background-color: $text-light; - border: 1px solid black; + .today-button { + background: #F4F5F6; + border: none; color: $text-dark; padding: 4px 10px; - font-size: 14px; - border-radius: 5px; + border-radius: 8px; + float: right; } .viewmode-select { - padding: 3px 5px; - margin-right: 4px; + background: #F4F5F6; + text-align: center; + outline: none !important; + padding: 4px 8px; + border-radius: 6px; + // display: block; + margin-bottom: 4px; } .date-highlight { - background-color: $text-muted; + background-color: $progress; border-radius: 12px; - z-index: 1; position: absolute; - opacity: 0.4; display: none; } + + .current-highlight { + position: absolute; + background: $dark-blue; + width: 1px; + } + + .current-date-highlight { + background: $dark-blue; + color: $text-light; + padding: 4px; + border-radius: 200px; + } } .gantt { @@ -101,38 +136,19 @@ $light-blue: #c4c4e9 !default; } .tick { - stroke: $border-color; + stroke: $stroke-color; stroke-width: 0.2; &.thick { - stroke: #000; - stroke-width: 0.5; + stroke: $dark-stroke-color; + stroke-width: 0.7; } } - .today-highlight { - fill: $light-yellow; - opacity: 0.5; - } - .week-highlight { - fill: $light-yellow; - opacity: 0.5; - } .holiday-highlight { fill: $holiday-color; - opacity: 0.5; - } - - .month-highlight { - fill: $light-yellow; - opacity: 0.5; - } - - .year-highlight { - fill: $light-yellow; - opacity: 0.5; } .arrow { @@ -148,14 +164,10 @@ $light-blue: #c4c4e9 !default; transition: stroke-width 0.3s ease; } - - .bar-progress { fill: $progress; } - - .bar-expected-progress { fill: $light-blue; } @@ -176,7 +188,7 @@ $light-blue: #c4c4e9 !default; dominant-baseline: central; // text-anchor: middle; font-family: Helvetica; - font-size: 14px; + font-size: 13px; font-weight: 400; &.big { @@ -191,7 +203,7 @@ $light-blue: #c4c4e9 !default; } .bar-progress { - fill: $progress-important; + fill: $dark-blue; } .bar-label { @@ -257,27 +269,24 @@ $light-blue: #c4c4e9 !default; position: absolute; top: 0; left: 0; - background: rgba(0, 0, 0); - padding: 0; - color: #959da5; - border-radius: 3px; - border: 1px solid rgba(0, 0, 0); + background: #171B1F; + padding: 10px; + border-radius: 5px; width: max-content; &.hidden { opacity: 0 !important; } + .title { - border-bottom: 1px solid $progress; - padding: 10px; + margin-bottom: 5px; text-align: center; color: $text-light; } .subtitle { - padding: 10px; - color: #dfe2e5; + color: $text-grey; } .pointer { diff --git a/src/index.js b/src/index.js index 4cc6424..2529f0b 100644 --- a/src/index.js +++ b/src/index.js @@ -85,7 +85,7 @@ export default class Gantt { setup_options(options) { const default_options = { - header_height: 50, + header_height: 65, column_width: 30, step: 24, view_modes: [...Object.values(VIEW_MODE)], @@ -340,6 +340,7 @@ export default class Gantt { this.make_grid(); this.make_dates(); this.make_bars(); + this.make_grid_extras(); this.make_arrows(); this.map_arrows_on_bars(); this.set_width(); @@ -363,8 +364,11 @@ export default class Gantt { this.make_grid_background(); this.make_grid_rows(); this.make_grid_header(); - this.make_grid_ticks(); + } + + make_grid_extras() { this.make_grid_highlights(); + this.make_grid_ticks(); } make_grid_background() { @@ -391,14 +395,13 @@ export default class Gantt { make_grid_rows() { const rows_layer = createSVG("g", { append_to: this.layers.grid }); - const lines_layer = createSVG("g", { append_to: this.layers.grid }); const row_width = this.dates.length * this.options.column_width; const row_height = this.options.bar_height + this.options.padding; let row_y = this.options.header_height + this.options.padding / 2; - for (let task of this.tasks) { + for (let _ of this.tasks) { createSVG("rect", { x: 0, y: row_y, @@ -408,14 +411,7 @@ export default class Gantt { append_to: rows_layer, }); if (this.options.lines === 'both' || this.options.lines === 'horizontal') { - 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 += this.options.bar_height + this.options.padding; @@ -445,10 +441,13 @@ export default class Gantt { this.$lower_header = $lower_header this.$header.appendChild($lower_header) + this.make_side_header() + } + + make_side_header() { let $side_header = document.createElement('div') $side_header.classList.add('side-header') - // Create view mode change select const $select = document.createElement("select"); $select.classList.add('viewmode-select') @@ -473,19 +472,40 @@ export default class Gantt { $side_header.appendChild($today_button) this.$header.appendChild($side_header) - const { left, y } = $header.getBoundingClientRect(); + const { left, y } = this.$header.getBoundingClientRect(); const width = Math.min(this.$header.clientWidth, this.$container.clientWidth) $side_header.style.left = left + this.$container.scrollLeft + width - $side_header.clientWidth + 'px'; - $side_header.style.top = y + 5 + 'px'; + $side_header.style.top = y + 20 + 'px'; } make_grid_ticks() { - if (this.options.lines !== 'both' && this.options.lines !== 'vertical') return + if (!['both', 'vertical', 'horizontal'].includes(this.options.lines)) return 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; + 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 @@ -542,14 +562,16 @@ export default class Gantt { //compute the horizontal x distance computeGridHighlightDimensions(view_mode) { - let xDist = 0; + let x = this.options.column_width / 2; if (this.view_is(VIEW_MODE.DAY)) { - return ( - (date_utils.diff(date_utils.today(), this.gantt_start, "hour") / - this.options.step) * - this.options.column_width - ); + 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) { @@ -568,12 +590,11 @@ export default class Gantt { break; } if (todayDate >= startDate && todayDate <= endDate) { - break; + return { x, date: startDate } } else { - xDist += this.options.column_width; + x += this.options.column_width; } } - return xDist; } make_grid_highlights() { @@ -585,49 +606,43 @@ export default class Gantt { this.view_is(VIEW_MODE.MONTH) || this.view_is(VIEW_MODE.YEAR) ) { - const x = this.computeGridHighlightDimensions(this.options.view_mode); - const y = 0; - const width = this.options.column_width; - const height = - (this.options.bar_height + this.options.padding) * this.tasks.length + - this.options.header_height + - this.options.padding / 2; + // Used as we must find the _end_ of session if view is not Day + const { x: left, date } = this.computeGridHighlightDimensions(this.options.view_mode) + const top = this.options.header_height + this.options.padding / 2; + const height = (this.options.bar_height + this.options.padding) * this.tasks.length; + this.create_el({ top, left, height, classes: 'current-highlight', append_to: this.$container }) + let $today = document.getElementById(date_utils.format(date).replaceAll(' ', '_')) - let className = ""; - switch (this.options.view_mode) { - case VIEW_MODE.DAY: - className = "today-highlight"; - break; - case VIEW_MODE.WEEK: - className = "week-highlight"; - break; - case VIEW_MODE.MONTH: - className = "month-highlight"; - break; - case VIEW_MODE.YEAR: - className = "year-highlight"; - break; - } - createSVG("rect", { - x, - y, - width, - height, - class: className, - append_to: this.layers.grid, - }); + $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) - 4 + 'px' } } + create_el({ left, top, width, height, id, classes, append_to }) { + let $el = document.createElement("div"); + $el.classList.add(classes) + $el.style.top = top + 'px' + $el.style.left = left + 'px' + if (id) $el.id = id + if (width) $el.style.width = height + 'px' + if (height) $el.style.height = height + 'px' + append_to.appendChild($el) + return $el + } + make_dates() { this.upper_texts_x = {} this.get_dates_to_draw().forEach((date, i) => { - let $lower_text = document.createElement('div'); - $lower_text.classList.add('lower-text') + let $lower_text = this.create_el({ + left: date.lower_x, + top: date.lower_y, + id: date.formatted_date, + classes: 'lower-text', + append_to: this.$lower_header + }) $lower_text.innerText = date.lower_text - this.$lower_header.appendChild($lower_text) - $lower_text.style.left = date.lower_x - (true ? $lower_text.clientWidth / 2 : 0) + 'px' - $lower_text.style.top = date.lower_y + 'px' + $lower_text.style.left = +$lower_text.style.left.slice(0, -2) - $lower_text.clientWidth / 2 + 'px' if (date.upper_text) { this.upper_texts_x[date.upper_text] = date.upper_x @@ -708,8 +723,8 @@ export default class Gantt { x: last_date_info ? last_date_info.base_pos_x + last_date_info.column_width : 0, - lower_y: this.options.header_height - 15, - upper_y: this.options.header_height - 40, + lower_y: this.options.header_height - 20, + upper_y: this.options.header_height - 50, }; const x_pos = { Hour_lower: column_width / 2, @@ -729,6 +744,7 @@ export default class Gantt { }; return { date, + formatted_date: date_utils.format(date).replaceAll(' ', '_'), column_width, base_pos_x: base_pos.x, upper_text: date_text[`${this.options.view_mode}_upper`], @@ -907,13 +923,13 @@ export default class Gantt { if ($current) { $current.classList.remove('current-upper') $current.style.left = this.upper_texts_x[$current.textContent] + 'px'; - $current.style.top = this.options.header_height - 40 + 'px'; + $current.style.top = this.options.header_height - 50 + 'px'; } $el.classList.add('current-upper') let dimensions = this.$svg.getBoundingClientRect() $el.style.left = dimensions.x + this.$container.scrollLeft + 10 + 'px'; - $el.style.top = dimensions.y + this.options.header_height - 40 + 'px'; + $el.style.top = dimensions.y + this.options.header_height - 50 + 'px'; } Array.prototype.forEach.call(elements, function (el, i) {