Change Bar hover behaviour, Bar animation
This commit is contained in:
parent
e55107ee82
commit
390fd2d324
8
dist/frappe-gantt.css
vendored
8
dist/frappe-gantt.css
vendored
@ -68,12 +68,16 @@
|
|||||||
.gantt .bar-wrapper {
|
.gantt .bar-wrapper {
|
||||||
cursor: pointer; }
|
cursor: pointer; }
|
||||||
.gantt .bar-wrapper:hover .bar {
|
.gantt .bar-wrapper:hover .bar {
|
||||||
stroke-width: 2; }
|
fill: #a9b5c1; }
|
||||||
|
.gantt .bar-wrapper:hover .bar-progress {
|
||||||
|
fill: #8a8aff; }
|
||||||
.gantt .bar-wrapper:hover .handle {
|
.gantt .bar-wrapper:hover .handle {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1; }
|
opacity: 1; }
|
||||||
.gantt .bar-wrapper.active .bar {
|
.gantt .bar-wrapper.active .bar {
|
||||||
stroke-width: 2; }
|
fill: #a9b5c1; }
|
||||||
|
.gantt .bar-wrapper.active .bar-progress {
|
||||||
|
fill: #8a8aff; }
|
||||||
|
|
||||||
.gantt .lower-text, .gantt .upper-text {
|
.gantt .lower-text, .gantt .upper-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
120
dist/frappe-gantt.js
vendored
120
dist/frappe-gantt.js
vendored
@ -245,7 +245,64 @@ function createSVG(tag, attrs) {
|
|||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function animateSVG(svgElement, attr, from, to) {
|
||||||
|
const animatedSvgElement = getAnimationElement(svgElement, attr, from, to);
|
||||||
|
|
||||||
|
if (animatedSvgElement === svgElement) {
|
||||||
|
// triggered 2nd time programmatically
|
||||||
|
// trigger artificial click event
|
||||||
|
const event = document.createEvent('HTMLEvents');
|
||||||
|
event.initEvent('click', true, true);
|
||||||
|
event.eventName = 'click';
|
||||||
|
animatedSvgElement.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAnimationElement(
|
||||||
|
svgElement,
|
||||||
|
attr,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
dur = '0.4s',
|
||||||
|
begin = '0.1s'
|
||||||
|
) {
|
||||||
|
const animEl = svgElement.querySelector('animate');
|
||||||
|
if (animEl) {
|
||||||
|
$.attr(animEl, {
|
||||||
|
attributeName: attr,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
dur,
|
||||||
|
begin: 'click + ' + begin // artificial click
|
||||||
|
});
|
||||||
|
return svgElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const animateElement = createSVG('animate', {
|
||||||
|
attributeName: attr,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
dur,
|
||||||
|
begin,
|
||||||
|
calcMode: 'spline',
|
||||||
|
values: from + ';' + to,
|
||||||
|
keyTimes: '0; 1',
|
||||||
|
keySplines: cubic_bezier('ease-out')
|
||||||
|
});
|
||||||
|
svgElement.appendChild(animateElement);
|
||||||
|
|
||||||
|
return svgElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cubic_bezier(name) {
|
||||||
|
return {
|
||||||
|
ease: '.25 .1 .25 1',
|
||||||
|
linear: '0 0 1 1',
|
||||||
|
'ease-in': '.42 0 1 1',
|
||||||
|
'ease-out': '0 0 .58 1',
|
||||||
|
'ease-in-out': '.42 0 .58 1'
|
||||||
|
}[name];
|
||||||
|
}
|
||||||
|
|
||||||
$.on = (element, event, selector, callback) => {
|
$.on = (element, event, selector, callback) => {
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
@ -385,6 +442,8 @@ class Bar {
|
|||||||
append_to: this.bar_group
|
append_to: this.bar_group
|
||||||
});
|
});
|
||||||
|
|
||||||
|
animateSVG(this.$bar, 'width', 0, this.width);
|
||||||
|
|
||||||
if (this.invalid) {
|
if (this.invalid) {
|
||||||
this.$bar.classList.add('bar-invalid');
|
this.$bar.classList.add('bar-invalid');
|
||||||
}
|
}
|
||||||
@ -402,6 +461,8 @@ class Bar {
|
|||||||
class: 'bar-progress',
|
class: 'bar-progress',
|
||||||
append_to: this.bar_group
|
append_to: this.bar_group
|
||||||
});
|
});
|
||||||
|
|
||||||
|
animateSVG(this.$bar_progress, 'width', 0, this.progress_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_label() {
|
draw_label() {
|
||||||
@ -412,7 +473,8 @@ class Bar {
|
|||||||
class: 'bar-label',
|
class: 'bar-label',
|
||||||
append_to: this.bar_group
|
append_to: this.bar_group
|
||||||
});
|
});
|
||||||
this.update_label_position();
|
// labels get BBox in the next tick
|
||||||
|
requestAnimationFrame(() => this.update_label_position());
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_resize_handles() {
|
draw_resize_handles() {
|
||||||
@ -467,27 +529,35 @@ class Bar {
|
|||||||
bind() {
|
bind() {
|
||||||
if (this.invalid) return;
|
if (this.invalid) return;
|
||||||
this.setup_click_event();
|
this.setup_click_event();
|
||||||
this.show_details();
|
|
||||||
// this.bind_resize_progress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show_details() {
|
setup_click_event() {
|
||||||
this.group.onclick = e => {
|
$.on(this.group, 'click', e => {
|
||||||
if (this.action_completed) {
|
if (this.action_completed) {
|
||||||
// just finished a move action, wait for a few seconds
|
// just finished a move action, wait for a few seconds
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start_date = date_utils.format(this.task._start, 'MMM D');
|
if (this.group.classList.contains('active')) {
|
||||||
const end_date = date_utils.format(this.task._end, 'MMM D');
|
this.gantt.trigger_event('click', [this.task]);
|
||||||
const subtitle = start_date + ' - ' + end_date;
|
}
|
||||||
|
this.gantt.unselect_all();
|
||||||
|
this.group.classList.toggle('active');
|
||||||
|
|
||||||
this.gantt.show_popup({
|
this.show_popup();
|
||||||
target_element: this.$bar,
|
});
|
||||||
title: this.task.name,
|
}
|
||||||
subtitle: subtitle
|
|
||||||
});
|
show_popup() {
|
||||||
};
|
const start_date = date_utils.format(this.task._start, 'MMM D');
|
||||||
|
const end_date = date_utils.format(this.task._end, 'MMM D');
|
||||||
|
const subtitle = start_date + ' - ' + end_date;
|
||||||
|
|
||||||
|
this.gantt.show_popup({
|
||||||
|
target_element: this.$bar,
|
||||||
|
title: this.task.name,
|
||||||
|
subtitle: subtitle
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update_bar_position({ x = null, width = null }) {
|
update_bar_position({ x = null, width = null }) {
|
||||||
@ -517,20 +587,6 @@ class Bar {
|
|||||||
// this.update_details_position();
|
// this.update_details_position();
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_click_event() {
|
|
||||||
this.group.onclick = () => {
|
|
||||||
if (this.action_completed) {
|
|
||||||
// just finished a move action, wait for a few seconds
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.group.classList.contains('active')) {
|
|
||||||
this.gantt.trigger_event('click', [this.task]);
|
|
||||||
}
|
|
||||||
this.gantt.unselect_all();
|
|
||||||
this.group.classList.toggle('active');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
date_changed() {
|
date_changed() {
|
||||||
const { new_start_date, new_end_date } = this.compute_start_end_date();
|
const { new_start_date, new_end_date } = this.compute_start_end_date();
|
||||||
this.task._start = new_start_date;
|
this.task._start = new_start_date;
|
||||||
@ -662,6 +718,7 @@ class Bar {
|
|||||||
update_label_position() {
|
update_label_position() {
|
||||||
const bar = this.$bar,
|
const bar = this.$bar,
|
||||||
label = this.group.querySelector('.bar-label');
|
label = this.group.querySelector('.bar-label');
|
||||||
|
|
||||||
if (label.getBBox().width > bar.getWidth()) {
|
if (label.getBBox().width > bar.getWidth()) {
|
||||||
label.classList.add('big');
|
label.classList.add('big');
|
||||||
label.setAttribute('x', bar.getX() + bar.getWidth() + 5);
|
label.setAttribute('x', bar.getX() + bar.getWidth() + 5);
|
||||||
@ -849,6 +906,7 @@ class Popup {
|
|||||||
this.pointer.style.top =
|
this.pointer.style.top =
|
||||||
this.title.clientHeight / 2 -
|
this.title.clientHeight / 2 -
|
||||||
this.pointer.getBoundingClientRect().height +
|
this.pointer.getBoundingClientRect().height +
|
||||||
|
2 +
|
||||||
'px';
|
'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1421,7 +1479,7 @@ class Gantt {
|
|||||||
bind_grid_click() {
|
bind_grid_click() {
|
||||||
this.layers.grid.onclick = () => {
|
this.layers.grid.onclick = () => {
|
||||||
this.unselect_all();
|
this.unselect_all();
|
||||||
this.popup && this.popup.hide();
|
this.hide_popup();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1661,6 +1719,10 @@ class Gantt {
|
|||||||
this.popup.show(options);
|
this.popup.show(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hide_popup() {
|
||||||
|
this.popup && this.popup.hide();
|
||||||
|
}
|
||||||
|
|
||||||
trigger_event(event, args) {
|
trigger_event(event, args) {
|
||||||
if (this.options['on_' + event]) {
|
if (this.options['on_' + event]) {
|
||||||
this.options['on_' + event].apply(null, args);
|
this.options['on_' + event].apply(null, args);
|
||||||
|
|||||||
2
dist/frappe-gantt.min.js
vendored
2
dist/frappe-gantt.min.js
vendored
File diff suppressed because one or more lines are too long
58
src/Bar.js
58
src/Bar.js
@ -1,5 +1,5 @@
|
|||||||
import date_utils from './date_utils';
|
import date_utils from './date_utils';
|
||||||
import { createSVG } from './svg_utils';
|
import { $, createSVG, animateSVG } from './svg_utils';
|
||||||
|
|
||||||
export default class Bar {
|
export default class Bar {
|
||||||
constructor(gantt, task) {
|
constructor(gantt, task) {
|
||||||
@ -85,6 +85,8 @@ export default class Bar {
|
|||||||
append_to: this.bar_group
|
append_to: this.bar_group
|
||||||
});
|
});
|
||||||
|
|
||||||
|
animateSVG(this.$bar, 'width', 0, this.width);
|
||||||
|
|
||||||
if (this.invalid) {
|
if (this.invalid) {
|
||||||
this.$bar.classList.add('bar-invalid');
|
this.$bar.classList.add('bar-invalid');
|
||||||
}
|
}
|
||||||
@ -102,6 +104,8 @@ export default class Bar {
|
|||||||
class: 'bar-progress',
|
class: 'bar-progress',
|
||||||
append_to: this.bar_group
|
append_to: this.bar_group
|
||||||
});
|
});
|
||||||
|
|
||||||
|
animateSVG(this.$bar_progress, 'width', 0, this.progress_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_label() {
|
draw_label() {
|
||||||
@ -112,7 +116,8 @@ export default class Bar {
|
|||||||
class: 'bar-label',
|
class: 'bar-label',
|
||||||
append_to: this.bar_group
|
append_to: this.bar_group
|
||||||
});
|
});
|
||||||
this.update_label_position();
|
// labels get BBox in the next tick
|
||||||
|
requestAnimationFrame(() => this.update_label_position());
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_resize_handles() {
|
draw_resize_handles() {
|
||||||
@ -167,27 +172,35 @@ export default class Bar {
|
|||||||
bind() {
|
bind() {
|
||||||
if (this.invalid) return;
|
if (this.invalid) return;
|
||||||
this.setup_click_event();
|
this.setup_click_event();
|
||||||
this.show_details();
|
|
||||||
// this.bind_resize_progress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show_details() {
|
setup_click_event() {
|
||||||
this.group.onclick = e => {
|
$.on(this.group, 'click', e => {
|
||||||
if (this.action_completed) {
|
if (this.action_completed) {
|
||||||
// just finished a move action, wait for a few seconds
|
// just finished a move action, wait for a few seconds
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start_date = date_utils.format(this.task._start, 'MMM D');
|
if (this.group.classList.contains('active')) {
|
||||||
const end_date = date_utils.format(this.task._end, 'MMM D');
|
this.gantt.trigger_event('click', [this.task]);
|
||||||
const subtitle = start_date + ' - ' + end_date;
|
}
|
||||||
|
this.gantt.unselect_all();
|
||||||
|
this.group.classList.toggle('active');
|
||||||
|
|
||||||
this.gantt.show_popup({
|
this.show_popup();
|
||||||
target_element: this.$bar,
|
});
|
||||||
title: this.task.name,
|
}
|
||||||
subtitle: subtitle
|
|
||||||
});
|
show_popup() {
|
||||||
};
|
const start_date = date_utils.format(this.task._start, 'MMM D');
|
||||||
|
const end_date = date_utils.format(this.task._end, 'MMM D');
|
||||||
|
const subtitle = start_date + ' - ' + end_date;
|
||||||
|
|
||||||
|
this.gantt.show_popup({
|
||||||
|
target_element: this.$bar,
|
||||||
|
title: this.task.name,
|
||||||
|
subtitle: subtitle
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update_bar_position({ x = null, width = null }) {
|
update_bar_position({ x = null, width = null }) {
|
||||||
@ -217,20 +230,6 @@ export default class Bar {
|
|||||||
// this.update_details_position();
|
// this.update_details_position();
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_click_event() {
|
|
||||||
this.group.onclick = () => {
|
|
||||||
if (this.action_completed) {
|
|
||||||
// just finished a move action, wait for a few seconds
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.group.classList.contains('active')) {
|
|
||||||
this.gantt.trigger_event('click', [this.task]);
|
|
||||||
}
|
|
||||||
this.gantt.unselect_all();
|
|
||||||
this.group.classList.toggle('active');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
date_changed() {
|
date_changed() {
|
||||||
const { new_start_date, new_end_date } = this.compute_start_end_date();
|
const { new_start_date, new_end_date } = this.compute_start_end_date();
|
||||||
this.task._start = new_start_date;
|
this.task._start = new_start_date;
|
||||||
@ -362,6 +361,7 @@ export default class Bar {
|
|||||||
update_label_position() {
|
update_label_position() {
|
||||||
const bar = this.$bar,
|
const bar = this.$bar,
|
||||||
label = this.group.querySelector('.bar-label');
|
label = this.group.querySelector('.bar-label');
|
||||||
|
|
||||||
if (label.getBBox().width > bar.getWidth()) {
|
if (label.getBBox().width > bar.getWidth()) {
|
||||||
label.classList.add('big');
|
label.classList.add('big');
|
||||||
label.setAttribute('x', bar.getX() + bar.getWidth() + 5);
|
label.setAttribute('x', bar.getX() + bar.getWidth() + 5);
|
||||||
|
|||||||
@ -92,7 +92,11 @@ $handle-color: #ddd;
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.bar {
|
.bar {
|
||||||
stroke-width: 2;
|
fill: darken($bar-color, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-progress {
|
||||||
|
fill: darken($blue, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.handle {
|
.handle {
|
||||||
@ -103,7 +107,11 @@ $handle-color: #ddd;
|
|||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
.bar {
|
.bar {
|
||||||
stroke-width: 2;
|
fill: darken($bar-color, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-progress {
|
||||||
|
fill: darken($blue, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -566,7 +566,7 @@ export default class Gantt {
|
|||||||
bind_grid_click() {
|
bind_grid_click() {
|
||||||
this.layers.grid.onclick = () => {
|
this.layers.grid.onclick = () => {
|
||||||
this.unselect_all();
|
this.unselect_all();
|
||||||
this.popup && this.popup.hide();
|
this.hide_popup();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,6 +806,10 @@ export default class Gantt {
|
|||||||
this.popup.show(options);
|
this.popup.show(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hide_popup() {
|
||||||
|
this.popup && this.popup.hide();
|
||||||
|
}
|
||||||
|
|
||||||
trigger_event(event, args) {
|
trigger_event(event, args) {
|
||||||
if (this.options['on_' + event]) {
|
if (this.options['on_' + event]) {
|
||||||
this.options['on_' + event].apply(null, args);
|
this.options['on_' + event].apply(null, args);
|
||||||
|
|||||||
@ -55,6 +55,7 @@ export default class Popup {
|
|||||||
this.pointer.style.top =
|
this.pointer.style.top =
|
||||||
this.title.clientHeight / 2 -
|
this.title.clientHeight / 2 -
|
||||||
this.pointer.getBoundingClientRect().height +
|
this.pointer.getBoundingClientRect().height +
|
||||||
|
2 +
|
||||||
'px';
|
'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,8 +37,8 @@ function getAnimationElement(
|
|||||||
attr,
|
attr,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
dur = '0.3s',
|
dur = '0.4s',
|
||||||
begin = '0s'
|
begin = '0.1s'
|
||||||
) {
|
) {
|
||||||
const animEl = svgElement.querySelector('animate');
|
const animEl = svgElement.querySelector('animate');
|
||||||
if (animEl) {
|
if (animEl) {
|
||||||
@ -57,13 +57,27 @@ function getAnimationElement(
|
|||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
dur,
|
dur,
|
||||||
begin
|
begin,
|
||||||
|
calcMode: 'spline',
|
||||||
|
values: from + ';' + to,
|
||||||
|
keyTimes: '0; 1',
|
||||||
|
keySplines: cubic_bezier('ease-out')
|
||||||
});
|
});
|
||||||
svgElement.appendChild(animateElement);
|
svgElement.appendChild(animateElement);
|
||||||
|
|
||||||
return svgElement;
|
return svgElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cubic_bezier(name) {
|
||||||
|
return {
|
||||||
|
ease: '.25 .1 .25 1',
|
||||||
|
linear: '0 0 1 1',
|
||||||
|
'ease-in': '.42 0 1 1',
|
||||||
|
'ease-out': '0 0 .58 1',
|
||||||
|
'ease-in-out': '.42 0 .58 1'
|
||||||
|
}[name];
|
||||||
|
}
|
||||||
|
|
||||||
$.on = (element, event, selector, callback) => {
|
$.on = (element, event, selector, callback) => {
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
callback = selector;
|
callback = selector;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user