From 539482e6d901f47addad35daf6bccebc8d5bddc4 Mon Sep 17 00:00:00 2001 From: Safwan Samsudeen Date: Fri, 13 Dec 2024 12:23:36 +0530 Subject: [PATCH] feat: cleaner styling, enable theming --- src/arrow.js | 4 +- src/bar.js | 7 +-- src/dark.css | 99 -------------------------------- src/defaults.js | 3 +- src/index.js | 63 ++++++++++++--------- src/popup.js | 2 +- src/styles/dark.css | 99 ++++++++++++++++++++++++++++++++ src/{ => styles}/gantt.css | 113 +++++++++++++++---------------------- src/styles/light.css | 23 ++++++++ 9 files changed, 212 insertions(+), 201 deletions(-) delete mode 100644 src/dark.css create mode 100644 src/styles/dark.css rename src/{ => styles}/gantt.css (69%) create mode 100644 src/styles/light.css diff --git a/src/arrow.js b/src/arrow.js index 258a44d..6ba329a 100644 --- a/src/arrow.js +++ b/src/arrow.js @@ -23,7 +23,7 @@ export default class Arrow { } const start_y = - this.gantt.options.header_height + + this.gantt.config.header_height + this.gantt.options.bar_height + (this.gantt.options.padding + this.gantt.options.bar_height) * this.from_task.task._index + @@ -32,7 +32,7 @@ export default class Arrow { const end_x = this.to_task.$bar.getX() - this.gantt.options.padding / 2 - 7; const end_y = - this.gantt.options.header_height + + this.gantt.config.header_height + this.gantt.options.bar_height / 2 + (this.gantt.options.padding + this.gantt.options.bar_height) * this.to_task.task._index + diff --git a/src/bar.js b/src/bar.js index 72a08eb..f376f4c 100644 --- a/src/bar.js +++ b/src/bar.js @@ -159,10 +159,9 @@ export default class Bar { let $date_highlight = document.createElement('div'); $date_highlight.classList.add('date-highlight'); $date_highlight.classList.add(`highlight-${this.task.id}`); - $date_highlight.style.height = this.height * 0.8 + 'px'; + // $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 - 25 + 'px'; + // $date_highlight.style.top = this.gantt.config.header_height - 25 + 'px'; $date_highlight.style.left = x + 'px'; this.$date_highlight = $date_highlight; this.gantt.$lower_header.prepend($date_highlight); @@ -596,7 +595,7 @@ export default class Bar { compute_y() { this.y = - this.gantt.options.header_height + + this.gantt.config.header_height + this.gantt.options.padding + this.task._index * (this.height + this.gantt.options.padding); } diff --git a/src/dark.css b/src/dark.css deleted file mode 100644 index c5bc9af..0000000 --- a/src/dark.css +++ /dev/null @@ -1,99 +0,0 @@ -:root { - --bar-color-dark: #616161; - --bar-stroke-dark: #c6ccd2; - --border-color-dark: #616161; - --light-bg-dark: #3e3e3e; - --light-border-color-dark: #3e3e3e; - --text-muted-dark: #eee; - --text-light-dark: #ececec; - --text-color-dark: #f7f7f7; - --blue-dark: #8a8aff; -} - -.dark>.gantt-container .gantt { - & .grid-row { - fill: #252525; - } - - /* & .grid-row:nth-child(even) { - fill: var(--light-bg-dark); - } */ - - & .row-line { - stroke: var(--light-border-color-dark); - } - - & .tick { - stroke: var(--border-color-dark); - } - - & .holiday-highlight { - fill: var(--light-bg-dark); - } - - & .arrow { - stroke: var(--text-muted-dark); - } - - & .bar { - fill: var(--bar-color-dark); - stroke: none; - } - - & .bar-progress { - fill: var(--blue-dark); - } - - & .bar-invalid { - fill: transparent; - stroke: var(--bar-stroke-dark); - - &~.bar-label { - fill: var(--text-light-dark); - } - } - - & .bar-label.big { - fill: var(--text-light-dark); - } - - & .bar-wrapper { - &:hover { - .bar { - fill: lighten(var(--bar-color-dark, 5)); - } - - & .bar-progress { - fill: lighten(var(--blue-dark, 5)); - } - } - - &.active { - .bar { - fill: lighten(var(--bar-color-dark, 5)); - } - - & .bar-progress { - fill: lighten(var(--blue-dark, 5)); - } - } - } -} - -.dark>.gantt-container { - & .grid-header { - background-color: #252525; - } - - & .popup-wrapper { - background-color: #333; - - & .title { - border-color: lighten(var(--blue-dark, 5)); - } - - & .pointer { - border-top-color: #333; - } - } -} \ No newline at end of file diff --git a/src/defaults.js b/src/defaults.js index fe958fb..65d2c29 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -103,8 +103,7 @@ const DEFAULT_OPTIONS = { column_width: null, date_format: 'YYYY-MM-DD', snap_at: null, - infinite_padding: false, - header_height: 65, + infinite_padding: true, holidays: { '#fff7ed': 'weekend' }, ignore: [], language: 'en', diff --git a/src/index.js b/src/index.js index 104c925..7d8a83b 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,7 @@ import Popup from './popup'; import { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES } from './defaults'; -import './gantt.css'; +import './styles/gantt.css'; export default class Gantt { constructor(wrapper, tasks, options) { @@ -75,6 +75,18 @@ export default class Gantt { this.original_options = options; this.options = { ...DEFAULT_OPTIONS, ...options }; + const CSS_VARIABLES = { + 'grid-height': 'container_height', + 'bar-height': 'bar_height', + }; + + for (let name in CSS_VARIABLES) { + this.$container.style.setProperty( + '--gv-' + name, + this.options[CSS_VARIABLES[name]] + 'px', + ); + } + this.config = { ignored_dates: [], ignored_positions: [], @@ -224,6 +236,11 @@ export default class Gantt { this.config.unit = scale; this.config.column_width = this.options.column_width || mode.column_width || 30; + this.$container.style.setProperty( + '--gv-column-width', + this.config.column_width + 'px', + ); + this.config.header_height = this.config.column_width * 2.5; } setup_dates() { @@ -357,10 +374,11 @@ export default class Gantt { make_grid_background() { const grid_width = this.dates.length * this.config.column_width; const grid_height = - this.options.header_height + + this.config.header_height + this.options.padding + (this.options.bar_height + this.options.padding) * this.tasks.length; + createSVG('rect', { x: 0, y: 0, @@ -382,7 +400,7 @@ export default class Gantt { const row_width = this.dates.length * this.config.column_width; 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.config.header_height + this.options.padding / 2; for (let _ of this.tasks) { createSVG('rect', { x: 0, @@ -405,7 +423,7 @@ export default class Gantt { make_grid_header() { let $header = document.createElement('div'); - $header.style.height = this.options.header_height + 10 + 'px'; + $header.style.height = this.config.header_height + 'px'; $header.style.width = this.dates.length * this.config.column_width + 'px'; $header.classList.add('grid-header'); @@ -471,7 +489,7 @@ export default class Gantt { make_grid_ticks() { if (this.options.lines === 'none') return; let tick_x = 0; - let tick_y = this.options.header_height + this.options.padding / 2; + let tick_y = this.config.header_height + this.options.padding / 2; let tick_height = (this.options.bar_height + this.options.padding) * this.tasks.length; @@ -481,7 +499,7 @@ export default class Gantt { append_to: this.layers.grid, }); - let row_y = this.options.header_height + this.options.padding / 2; + let row_y = this.config.header_height + this.options.padding / 2; const row_width = this.dates.length * this.config.column_width; const row_height = this.options.bar_height + this.options.padding; @@ -603,9 +621,7 @@ export default class Gantt { } let el = createSVG('rect', { x: Math.round(x), - y: - this.options.header_height + - this.options.padding / 2, + y: this.config.header_height + this.options.padding / 2, width: this.config.column_width / date_utils.convert_scales( @@ -649,26 +665,22 @@ export default class Gantt { ); this.$current_highlight = this.create_el({ - top: this.options.header_height - 20, + top: this.config.header_height - 20, left, height, classes: 'current-highlight', append_to: this.$extras, }); - let $today = this.$container.querySelector( - '.date_' + date.replaceAll(' ', '_'), - ); - if ($today) { - $today.classList.add('current-date-highlight'); - $today.style.top = +$today.style.top.slice(0, -2) - 4 + 'px'; - } + let $today = this.$container + .querySelector('.date_' + date.replaceAll(' ', '_')) + .classList.add('current-date-highlight'); } make_grid_highlights() { this.highlightHolidays(); - const top = this.options.header_height + this.options.padding / 2; + const top = this.config.header_height + this.options.padding / 2; const height = (this.options.bar_height + this.options.padding) * this.tasks.length; @@ -676,7 +688,7 @@ export default class Gantt { + style="stroke:grey; stroke-width:0.3" /> `; for ( @@ -776,9 +788,9 @@ export default class Gantt { const base_pos = { x: last_date_info ? last_date_info.base_pos_x + last_date_info.column_width - : 20, - lower_y: this.options.header_height - 20, - upper_y: this.options.header_height - 50, + : 0, + lower_y: this.config.column_width * 1.5, + upper_y: 15, }; let upper_text = this.config.view_mode.upper_text; @@ -821,7 +833,7 @@ export default class Gantt { 1) / 2, upper_y: base_pos.upper_y, - lower_x: base_pos.x + column_width / 2 - 20, + lower_x: base_pos.x, lower_y: base_pos.lower_y, }; } @@ -867,16 +879,13 @@ export default class Gantt { } set_dimensions() { - const { width: cur_width, height } = this.$svg.getBoundingClientRect(); + const { width: cur_width } = this.$svg.getBoundingClientRect(); const actual_width = this.$svg.querySelector('.grid .grid-row') ? this.$svg.querySelector('.grid .grid-row').getAttribute('width') : 0; if (cur_width < actual_width) { this.$svg.setAttribute('width', actual_width); } - this.$container.style.height = - { auto: height }[this.options.container_height] || - this.options.container_height + 'px'; } set_scroll_position(date) { diff --git a/src/popup.js b/src/popup.js index 8598d9c..56ea444 100644 --- a/src/popup.js +++ b/src/popup.js @@ -50,7 +50,7 @@ export default class Popup { this.parent.classList.remove('hidden'); this.pointer.style.left = this.parent.clientWidth / 2 + 'px'; - this.pointer.style.top = '-15px'; + this.pointer.style.top = '-10px'; // show this.parent.style.opacity = 1; diff --git a/src/styles/dark.css b/src/styles/dark.css new file mode 100644 index 0000000..1adfb37 --- /dev/null +++ b/src/styles/dark.css @@ -0,0 +1,99 @@ +:root { + --g-bar-stroke-dark: #c6ccd2; + --g-border-color-dark: #616161; + --g-bar-color-dark: #616161; + --g-light-bg-dark: #3e3e3e; + --g-light-border-color-dark: #3e3e3e; + --g-text-muted-dark: #eee; + --g-text-light-dark: #ececec; + --g-text-color-dark: #f7f7f7; + --g-blue-dark: #8a8aff; +} + +.dark > .gantt-container .gantt { + & .grid-row { + fill: #252525; + } + + & .grid-row:nth-child(even) { + fill: var(--g-light-bg-dark); + } + + & .row-line { + stroke: var(--g-light-border-color-dark); + } + + & .tick { + stroke: var(--g-border-color-dark); + } + + & .holiday-highlight { + fill: var(--g-light-bg-dark); + } + + & .arrow { + stroke: var(--g-text-muted-dark); + } + + & .bar { + fill: var(--g-bar-color-dark); + stroke: none; + } + + & .bar-progress { + fill: var(--g-blue-dark); + } + + & .bar-invalid { + fill: transparent; + stroke: var(--g-bar-stroke-dark); + + & ~ .bar-label { + fill: var(--g-text-light-dark); + } + } + + & .bar-label.big { + fill: var(--g-text-light-dark); + } + + & .bar-wrapper { + &:hover { + .bar { + fill: lighten(var(--g-bar-color-dark, 5)); + } + + & .bar-progress { + fill: lighten(var(--g-blue-dark, 5)); + } + } + + &.active { + .bar { + fill: lighten(var(--g-bar-color-dark, 5)); + } + + & .bar-progress { + fill: lighten(var(--g-blue-dark, 5)); + } + } + } +} + +.dark > .gantt-container { + & .grid-header { + background-color: #252525; + } + + & .popup-wrapper { + background-color: #333; + + & .title { + border-color: lighten(var(--g-blue-dark, 5)); + } + + & .pointer { + border-top-color: #333; + } + } +} diff --git a/src/gantt.css b/src/styles/gantt.css similarity index 69% rename from src/gantt.css rename to src/styles/gantt.css index 0d49439..4f7ec11 100644 --- a/src/gantt.css +++ b/src/styles/gantt.css @@ -1,33 +1,11 @@ -@import './dark.css'; - -:root { - --bar-color: #fff; - --bar-color-important: #94c4f4; - --bar-stroke: #fff; - --dark-stroke-color: #e0e0e0; - --stroke-color: #ebeef0; - --light-bg: #f5f5f5; - --light-border-color: #ebeff2; - --light-yellow: #f6e796; - --holiday-color: #f9fafa; - --text-muted: #7c7c7c; - --text-grey: #98a1a9; - --text-light: #fff; - --text-dark: #171717; - --progress: #ebeef0; - --handle-color: #dcdce4; - --handle-color-important: #94c4f4; - --light-blue: #c4c4e9; - --middle-blue: #62b2f9; - --dark-blue: #2c94ec; -} +@import './light.css'; .gantt-container { line-height: 14.5px; position: relative; overflow: auto; font-size: 12px; - height: 300px; + height: var(--gv-grid-height); & .popup-wrapper { position: absolute; @@ -46,24 +24,23 @@ margin-bottom: 5px; text-align: -webkit-center; text-align: center; - color: var(--text-light); + color: var(--g-text-light); } & .subtitle { - color: var(--text-grey); + color: var(--g-text-secondary); } & .pointer { position: absolute; height: 5px; - margin: 0 0 0 -5px; border: 5px solid transparent; border-bottom-color: rgba(0, 0, 0, 0.8); } } & .grid-header { - background-color: #ffffff; + background-color: var(--g-header-background); position: sticky; top: 0; left: 0; @@ -76,19 +53,22 @@ } & .upper-header { - height: 40px; + height: calc(var(--gv-column-width) * 1.5); } & .lower-header { - height: 30px; + height: var(--gv-column-width); } & .lower-text { - font-size: 14px; + font-size: 12px; position: absolute; - width: fit-content; - transform: translateX(-50%); - color: var(--text-muted); + width: calc(var(--gv-column-width) * 0.8); + height: calc(var(--gv-column-width) * 0.8); + margin: 0 calc(var(--gv-column-width) * 0.1); + align-content: center; + text-align: center; + color: var(--g-text-muted); } & .upper-text { @@ -96,12 +76,12 @@ width: fit-content; font-weight: 500; font-size: 16px; - color: var(--text-dark); + color: var(--g-text-dark); } & .current-upper { position: sticky; - left: 2px !important; + left: 0 !important; padding: 0 10px; height: 20px; background: white; @@ -121,14 +101,14 @@ } & .side-header * { - background: #f4f5f6; + background: var(--g-actions-background); text-align: -webkit-center; text-align: center; height: 30px; border-radius: 0; border: 1px dotted grey; border-right: none; - color: var(--text-dark); + color: var(--g-text-dark); padding: 4px 10px; position: sticky; float: right; @@ -147,24 +127,25 @@ } & .date-highlight { - background-color: var(--progress); + background-color: var(--g-progress-color); border-radius: 12px; + height: calc(var(--gv-bar-height) * 0.8); + top: calc(var(--gv-column-width) * 1.5); position: absolute; display: none; } & .current-highlight { position: absolute; - background: var(--dark-blue); + background: var(--g-blue-dark); width: 1px; z-index: 999; } & .current-date-highlight { - background: var(--dark-blue); - color: var(--text-light); - padding: 4px 8px; - border-radius: 5px; + background: var(--g-blue-dark); + color: var(--g-text-light); + border-radius: 100px; } & .holiday-label { @@ -173,7 +154,7 @@ left: 0; opacity: 0; z-index: 1000; - background: #dcdce4; + background: --g-holiday-label-color; border-radius: 5px; padding: 2px 5px; @@ -193,96 +174,96 @@ } & .grid-row { - fill: #ffffff; + fill: var(--g-row-color); } & .row-line { - stroke: var(--light-border-color); + stroke: var(--g-border-color); } & .tick { - stroke: var(--stroke-color); + stroke: var(--g-tick-color); stroke-width: 0.4; &.thick { - stroke: var(--dark-stroke-color); + stroke: var(--g-tick-color-thick); stroke-width: 0.7; } } & .arrow { fill: none; - stroke: #9fa9b1; + stroke: var(--arrow-color); stroke-width: 1; } & .bar-wrapper .bar { - fill: var(--bar-color); - stroke: var(--bar-stroke); + fill: var(--g-bar-color); + stroke: var(--g-bar-border); stroke-width: 0; transition: stroke-width 0.3s ease; } & .bar-progress { - fill: var(--progress); + fill: var(--g-progress-color); } & .bar-expected-progress { - fill: var(--light-blue); + fill: var(--g-blue-light); } & .bar-invalid { fill: transparent; - stroke: var(--bar-stroke); + stroke: var(--g-bar-border); stroke-width: 1; stroke-dasharray: 5; & ~ .bar-label { - fill: var(--text-light); + fill: var(--g-text-light); } } & .bar-label { - fill: var(--text-dark); + fill: var(--g-text-dark); dominant-baseline: central; font-family: Helvetica; font-size: 13px; font-weight: 400; &.big { - fill: var(--text-dark); + fill: var(--g-text-dark); text-anchor: start; } } & .bar-wrapper.important { & .bar { - fill: var(--bar-color-important); + fill: var(--g-bar-color-important); } & .bar-progress { - fill: var(--dark-blue); + fill: var(--g-blue-dark); } & .bar-label { - fill: var(--text-light); + fill: var(--g-text-light); &.big { - fill: var(--text-dark); + fill: var(--g-text-dark); } } & .handle { - fill: var(--handle-color-important); + fill: var(--g-handle-color-important); } & .handle.progress { - fill: var(--text-light); + fill: var(--g-text-light); } } & .handle { - fill: var(--handle-color); + fill: var(--g-handle-color); cursor: ew-resize; opacity: 0; visibility: hidden; @@ -290,7 +271,7 @@ } & .handle.progress { - fill: var(--text-muted); + fill: var(--g-text-muted); } & .bar-wrapper { diff --git a/src/styles/light.css b/src/styles/light.css new file mode 100644 index 0000000..2c38c79 --- /dev/null +++ b/src/styles/light.css @@ -0,0 +1,23 @@ +:root { + --g-arrow-color: #9fa9b1; + --g-bar-color: #fff; + --g-bar-color-important: #94c4f4; + --g-bar-border: #fff; + --g-tick-color-thick: #e0e0e0; + --g-tick-color: #ebeef0; + --g-light-bg: #f5f5f5; + --g-actions-background: #f4f5f6; + --g-border-color: #ebeff2; + --g-text-muted: #7c7c7c; + --g-text-light: #fff; + --g-text-secondary: #98a1a9; + --g-text-dark: #171717; + --g-progress-color: #ebeef0; + --g-handle-color: #dcdce4; + --g-handle-color-important: #94c4f4; + --g-holiday-label-color: #dcdce4; + --g-blue-light: #c4c4e9; + --g-blue-dark: #2c94ec; + --g-header-background: #ffffff; + --g-row-color: #fff; +}