2
0
mirror of https://github.com/tenrok/vue-ganttastic.git synced 2026-06-20 07:50:33 +03:00

refactor: remove hardcode format and unit of time

This commit is contained in:
yicone
2021-02-02 15:54:56 +08:00
parent 0b79d21097
commit ac56ec12d7
4 changed files with 686 additions and 514 deletions
+334 -250
View File
@@ -1,46 +1,45 @@
<template>
<template>
<div>
<div
class="g-gantt-bar"
<div
class="g-gantt-bar"
ref="g-gantt-bar"
:style="barStyle"
@mouseenter.stop="onMouseenter($event)"
@mouseleave.stop ="onMouseleave($event)"
@mouseleave.stop="onMouseleave($event)"
@mousedown.stop="onMousedown($event)"
@click.stop="onClick($event)"
@dblclick="onDblclick($event)"
@contextmenu="onContextmenu($event)"
>
<div class="g-gantt-bar-label">
<slot
name="bar-label"
:bar="bar"
>
{{barConfig.label || ""}}
<slot name="bar-label" :bar="bar">
{{ barConfig.label || '' }}
</slot>
</div>
<template v-if="barConfig.handles">
<div class="g-gantt-bar-handle-left"/>
<div class="g-gantt-bar-handle-right"/>
<div class="g-gantt-bar-handle-left" />
<div class="g-gantt-bar-handle-right" />
</template>
</div>
<transition name="fade" mode="out-in">
<div
<div
v-if="!barConfig.noTooltip && (showTooltip || isDragging)"
class="g-gantt-tooltip"
:style="tooltipStyle"
>
<div
<div
class="color-indicator"
:style="{background: this.barStyle.background || this.barStyle.backgroundColor}"
:style="{
background:
this.barStyle.background || this.barStyle.backgroundColor,
}"
/>
{{bar[barStart] | TimeFilter}}
-
{{bar[barEnd] | TimeFilter}}
{{ bar[barStart] }}
-
{{ bar[barEnd] }}
</div>
</transition>
</div>
</template>
@@ -48,28 +47,30 @@
import moment from 'moment'
export default {
name: "GGanttBar",
name: 'GGanttBar',
props:{
bar: {type: Object},
barStart: {type: String}, // property name of the bar objects that represents the start datetime
barEnd: {type: String}, // property name of the bar objects that represents the end datetime,
props: {
bar: { type: Object },
barStart: { type: String }, // property name of the bar objects that represents the start datetime
barEnd: { type: String }, // property name of the bar objects that represents the end datetime,
barContainer: [Object, DOMRect],
allBarsInRow: {type: Array}
allBarsInRow: { type: Array },
},
inject: [
"getHourCount",
"ganttChartProps",
"initDragOfBarsFromBundle",
"moveBarsFromBundleOfPushedBar",
"setDragLimitsOfGanttBar",
"onBarEvent",
"onDragendBar",
"getMinGapBetweenBars",
'getTimeCount',
'ganttChartProps',
'initDragOfBarsFromBundle',
'moveBarsFromBundleOfPushedBar',
'setDragLimitsOfGanttBar',
'onBarEvent',
'onDragendBar',
'getMinGapBetweenBars',
'getTimeUnit',
'getTimeFormat',
],
data(){
data() {
return {
showTooltip: false,
tooltipTimeout: null,
@@ -77,48 +78,56 @@ export default {
dragLimitRight: null,
isDragging: false,
isMainBarOfDrag: false, // is this the bar that was clicked on when starting to drag
// or is it dragged along some other bar from the same bundle
// or is it dragged along some other bar from the same bundle
cursorOffsetX: 0,
mousemoveCallback: null, // gets initialized when starting to drag
// possible values: drag, dragByHandleLeft, dragByHandleRight,
mousemoveCallback: null, // gets initialized when starting to drag
// possible values: drag, dragByHandleLeft, dragByHandleRight,
barStartBeforeDrag: null,
barEndBeforeDrag: null,
timeUnit: this.getTimeUnit(),
timeChildKey: this.getTimeUnit() === 'days' ? 'hours' : 'minutes',
timeFormat: this.getTimeFormat(),
}
},
computed:{
computed: {
// use these computed moment objects to work with the bar's start/end dates:
// instead of directly mutating them:
barStartMoment:{
get(){
barStartMoment: {
get() {
return moment(this.bar[this.barStart])
},
set(value){
this.bar[this.barStart] = moment(value).format("YYYY-MM-DD HH:mm:ss")
}
set(value) {
this.bar[this.barStart] = moment(value).format(this.timeFormat)
},
},
barEndMoment: {
get(){
get() {
return moment(this.bar[this.barEnd])
},
set(value){
this.bar[this.barEnd] = moment(value).format("YYYY-MM-DD HH:mm:ss")
}
set(value) {
this.bar[this.barEnd] = moment(value).format(this.timeFormat)
},
},
barConfig(){
if(this.bar.ganttBarConfig) {
barConfig() {
if (this.bar.ganttBarConfig) {
return {
...this.bar.ganttBarConfig,
background: this.bar.ganttBarConfig.isShadow ? "grey" : this.bar.ganttBarConfig.background || this.bar.ganttBarConfig.backgroundColor,
opacity: this.bar.ganttBarConfig.isShadow ? "0.3" : this.bar.ganttBarConfig.opacity
background: this.bar.ganttBarConfig.isShadow
? 'grey'
: this.bar.ganttBarConfig.background ||
this.bar.ganttBarConfig.backgroundColor,
opacity: this.bar.ganttBarConfig.isShadow
? '0.3'
: this.bar.ganttBarConfig.opacity,
}
}
return {}
},
barStyle(){
barStyle() {
let xStart = this.mapTimeToPosition(this.barStartMoment)
let xEnd = this.mapTimeToPosition(this.barEndMoment)
return {
@@ -126,77 +135,87 @@ export default {
left: `${xStart}px`,
width: `${xEnd - xStart}px`,
height: `${this.ganttChartProps.rowHeight - 6}px`,
zIndex: this.barConfig.zIndex || (this.isDragging ? 2 : 1)
zIndex: this.barConfig.zIndex || (this.isDragging ? 2 : 1),
}
},
tooltipStyle(){
return{
tooltipStyle() {
return {
left: this.barStyle.left,
top:`${this.ganttChartProps.rowHeight}px`,
top: `${this.ganttChartProps.rowHeight}px`,
}
},
chartStartMoment(){
chartStartMoment() {
return moment(this.ganttChartProps.chartStart)
},
chartEndMoment(){
chartEndMoment() {
return moment(this.ganttChartProps.chartEnd)
}
},
},
methods:{
onMouseenter(e){
if(this.tooltipTimeout){
methods: {
onMouseenter(e) {
if (this.tooltipTimeout) {
clearTimeout(this.tooltipTimeout)
}
this.tooltipTimeout = setTimeout(() => this.showTooltip = true, 800)
this.onBarEvent({event: e, type: e.type}, this)
this.tooltipTimeout = setTimeout(() => (this.showTooltip = true), 800)
this.onBarEvent({ event: e, type: e.type }, this)
},
onMouseleave(e){
onMouseleave(e) {
clearTimeout(this.tooltipTimeout)
this.showTooltip = false
this.onBarEvent({event: e, type: e.type}, this)
this.onBarEvent({ event: e, type: e.type }, this)
},
onContextmenu(e){
const time = this.mapPositionToTime(e.clientX - this.barContainer.left).format("YYYY-MM-DD HH:mm:ss")
this.onBarEvent({event: e, type: e.type, time}, this)
onContextmenu(e) {
const time = this.mapPositionToTime(
e.clientX - this.barContainer.left
).format(this.timeFormat)
this.onBarEvent({ event: e, type: e.type, time }, this)
},
onClick(e){
const time = this.mapPositionToTime(e.clientX - this.barContainer.left).format("YYYY-MM-DD HH:mm:ss")
this.onBarEvent({event: e, type: e.type, time}, this)
onClick(e) {
const time = this.mapPositionToTime(
e.clientX - this.barContainer.left
).format(this.timeFormat)
this.onBarEvent({ event: e, type: e.type, time }, this)
},
onDblclick(e) {
const time = this.mapPositionToTime(e.clientX - this.barContainer.left).format("YYYY-MM-DD HH:mm:ss")
this.onBarEvent({event: e, type: e.type, time}, this)
const time = this.mapPositionToTime(
e.clientX - this.barContainer.left
).format(this.timeFormat)
this.onBarEvent({ event: e, type: e.type, time }, this)
},
onMousedown(e){
onMousedown(e) {
e.preventDefault()
if(e.button === 2){
if (e.button === 2) {
return
}
if(!this.barConfig.immobile && !this.barConfig.isShadow) {
if (!this.barConfig.immobile && !this.barConfig.isShadow) {
this.setDragLimitsOfGanttBar(this)
// initialize the dragging on next mousemove event:
window.addEventListener("mousemove", this.onFirstMousemove, {once: true})
window.addEventListener('mousemove', this.onFirstMousemove, {
once: true,
})
// if next mousemove happens after mouse up (if user just presses mouse button down, then up, without moving):
window.addEventListener("mouseup",
() => window.removeEventListener("mousemove", this.onFirstMousemove),
{once: true}
window.addEventListener(
'mouseup',
() => window.removeEventListener('mousemove', this.onFirstMousemove),
{ once: true }
)
}
const time = this.mapPositionToTime(e.clientX - this.barContainer.left).format("YYYY-MM-DD HH:mm:ss")
this.onBarEvent({event: e, type: e.type, time}, this)
const time = this.mapPositionToTime(
e.clientX - this.barContainer.left
).format(this.timeFormat)
this.onBarEvent({ event: e, type: e.type, time }, this)
},
onFirstMousemove(e){
onFirstMousemove(e) {
this.isMainBarOfDrag = true
// this method is injected here by GGanttChart.vue, and calls initDrag()
// for all GGanttBars that belong to the same bundle as this bar:
@@ -206,165 +225,230 @@ export default {
/* --------------------------------------------------------- */
/* ------------- METHODS FOR DRAGGING THE BAR -------------- */
/* --------------------------------------------------------- */
initDrag(e){ // "e" must be the mousedown event
initDrag(e) {
// "e" must be the mousedown event
this.isDragging = true
this.barStartBeforeDrag = this.bar[this.barStart]
this.barEndBeforeDrag = this.bar[this.barEnd]
let barX = this.$refs["g-gantt-bar"].getBoundingClientRect().left
let barX = this.$refs['g-gantt-bar'].getBoundingClientRect().left
this.cursorOffsetX = e.clientX - barX
let mousedownType = e.target.className
switch(mousedownType){
case "g-gantt-bar-handle-left":
document.body.style.cursor = "w-resize"
switch (mousedownType) {
case 'g-gantt-bar-handle-left':
document.body.style.cursor = 'w-resize'
this.mousemoveCallback = this.dragByHandleLeft
break
case "g-gantt-bar-handle-right":
document.body.style.cursor = "w-resize"
case 'g-gantt-bar-handle-right':
document.body.style.cursor = 'w-resize'
this.mousemoveCallback = this.dragByHandleRight
break
default: this.mousemoveCallback = this.drag
default:
this.mousemoveCallback = this.drag
}
window.addEventListener("mousemove", this.mousemoveCallback)
window.addEventListener("mouseup", this.endDrag)
window.addEventListener('mousemove', this.mousemoveCallback)
window.addEventListener('mouseup', this.endDrag)
},
drag(e){
let barWidth = this.$refs["g-gantt-bar"].getBoundingClientRect().width
let newXStart = (e.clientX-this.barContainer.left) - this.cursorOffsetX
drag(e) {
let barWidth = this.$refs['g-gantt-bar'].getBoundingClientRect().width
let newXStart = e.clientX - this.barContainer.left - this.cursorOffsetX
let newXEnd = newXStart + barWidth
if(this.isPosOutOfDragRange(newXStart, newXEnd)){
if (this.isPosOutOfDragRange(newXStart, newXEnd)) {
return
}
this.barStartMoment = this.mapPositionToTime(newXStart)
this.barEndMoment = this.mapPositionToTime(newXEnd)
this.manageOverlapping()
this.onBarEvent({event: e, type: "drag"}, this)
this.onBarEvent({ event: e, type: 'drag' }, this)
},
dragByHandleLeft(e){
dragByHandleLeft(e) {
let newXStart = e.clientX - this.barContainer.left
let newStartMoment = this.mapPositionToTime(newXStart)
if(newStartMoment.isSameOrAfter(this.barEndMoment) || this.isPosOutOfDragRange(newXStart, null)){
if (
newStartMoment.isSameOrAfter(this.barEndMoment) ||
this.isPosOutOfDragRange(newXStart, null)
) {
return
}
this.barStartMoment = newStartMoment
this.manageOverlapping()
},
dragByHandleRight(e){
dragByHandleRight(e) {
let newXEnd = e.clientX - this.barContainer.left
let newEndMoment = this.mapPositionToTime(newXEnd)
if(newEndMoment.isSameOrBefore(this.barStartMoment) || this.isPosOutOfDragRange(null, newXEnd)){
if (
newEndMoment.isSameOrBefore(this.barStartMoment) ||
this.isPosOutOfDragRange(null, newXEnd)
) {
return
}
this.barEndMoment = newEndMoment
this.manageOverlapping()
},
isPosOutOfDragRange(xStart, xEnd){
isPosOutOfDragRange(xStart, xEnd) {
// 不能推动旁边的bar时,拖拽就不停止
if(!this.ganttChartProps.pushOnOverlap) {
if (!this.ganttChartProps.pushOnOverlap) {
return false
}
if(xStart && xStart < 0) {
if (xStart && xStart < 0) {
return true
}
if(xStart && this.dragLimitLeft !== null && xStart < this.dragLimitLeft + this.getMinGapBetweenBars()){
if (
xStart &&
this.dragLimitLeft !== null &&
xStart < this.dragLimitLeft + this.getMinGapBetweenBars()
) {
return true
}
if(xEnd && this.dragLimitRight !== null && xEnd > this.dragLimitRight - this.getMinGapBetweenBars()){
if (
xEnd &&
this.dragLimitRight !== null &&
xEnd > this.dragLimitRight - this.getMinGapBetweenBars()
) {
return true
}
return false
},
endDrag(e){
endDrag(e) {
this.isDragging = false
this.dragLimitLeft = null
this.dragLimitRight = null
document.body.style.cursor = "auto"
window.removeEventListener("mousemove", this.mousemoveCallback)
window.removeEventListener("mouseup", this.endDrag)
if(this.isMainBarOfDrag){
document.body.style.cursor = 'auto'
window.removeEventListener('mousemove', this.mousemoveCallback)
window.removeEventListener('mouseup', this.endDrag)
if (this.isMainBarOfDrag) {
this.onDragendBar(e, this)
this.isMainBarOfDrag = false
}
},
snapBack(){
snapBack() {
this.barStartMoment = this.barStartBeforeDrag
this.barEndMoment = this.barEndBeforeDrag
},
manageOverlapping(){
if(!this.ganttChartProps.pushOnOverlap || this.barConfig.pushOnOverlap === false){
manageOverlapping() {
if (
!this.ganttChartProps.pushOnOverlap ||
this.barConfig.pushOnOverlap === false
) {
return
}
let currentBar = this.bar
let {overlapBar, overlapType} = this.getOverlapBarAndType(currentBar)
while(overlapBar){
let { overlapBar, overlapType } = this.getOverlapBarAndType(currentBar)
while (overlapBar) {
let minuteDiff
let currentStartMoment = moment(currentBar[this.barStart])
let currentEndMoment = moment(currentBar[this.barEnd])
let overlapStartMoment = moment(overlapBar[this.barStart])
let overlapEndMoment = moment(overlapBar[this.barEnd])
switch(overlapType){
case "left":
minuteDiff = overlapEndMoment.diff(currentStartMoment, "minutes", true) + this.getMinGapBetweenBars()
overlapBar[this.barEnd] = currentStartMoment.subtract(this.getMinGapBetweenBars(), "minutes", true).format("YYYY-MM-DD HH:mm:ss")
overlapBar[this.barStart] = overlapStartMoment.subtract(minuteDiff, "minutes", true).format("YYYY-MM-DD HH:mm:ss")
let overlapEndMoment = moment(overlapBar[this.barEnd])
switch (overlapType) {
case 'left':
minuteDiff =
overlapEndMoment.diff(
currentStartMoment,
this.timeChildKey,
true
) + this.getMinGapBetweenBars()
overlapBar[this.barEnd] = currentStartMoment
.subtract(this.getMinGapBetweenBars(), this.timeChildKey, true)
.format(this.timeFormat)
overlapBar[this.barStart] = overlapStartMoment
.subtract(minuteDiff, this.timeChildKey, true)
.format(this.timeFormat)
break
case "right":
minuteDiff = currentEndMoment.diff(overlapStartMoment, "minutes", true) + this.getMinGapBetweenBars()
overlapBar[this.barStart] = currentEndMoment.add(this.getMinGapBetweenBars(), "minutes", true).format("YYYY-MM-DD HH:mm:ss")
overlapBar[this.barEnd] = overlapEndMoment.add(minuteDiff, "minutes", true).format("YYYY-MM-DD HH:mm:ss")
case 'right':
minuteDiff =
currentEndMoment.diff(
overlapStartMoment,
this.timeChildKey,
true
) + this.getMinGapBetweenBars()
overlapBar[this.barStart] = currentEndMoment
.add(this.getMinGapBetweenBars(), this.timeChildKey, true)
.format(this.timeFormat)
overlapBar[this.barEnd] = overlapEndMoment
.add(minuteDiff, this.timeChildKey, true)
.format(this.timeFormat)
break
default:
// eslint-disable-next-line
console.warn("One bar is inside of the other one! This should never occur while push-on-overlap is active!")
console.warn(
'One bar is inside of the other one! This should never occur while push-on-overlap is active!'
)
return
}
this.moveBarsFromBundleOfPushedBar(overlapBar, minuteDiff, overlapType)
currentBar = overlapBar;
({overlapBar, overlapType} = this.getOverlapBarAndType(overlapBar))
currentBar = overlapBar
;({ overlapBar, overlapType } = this.getOverlapBarAndType(overlapBar))
}
},
getOverlapBarAndType(bar){
getOverlapBarAndType(bar) {
let barStartMoment = moment(bar[this.barStart])
let barEndMoment = moment(bar[this.barEnd])
let overlapLeft, overlapRight, overlapInBetween
let overlapBar = this.allBarsInRow.find(otherBar => {
if(otherBar === bar || otherBar.ganttBarConfig.pushOnOverlap === false){
let overlapBar = this.allBarsInRow.find((otherBar) => {
if (
otherBar === bar ||
otherBar.ganttBarConfig.pushOnOverlap === false
) {
return false
}
let otherBarStart = moment(otherBar[this.barStart])
let otherBarEnd = moment(otherBar[this.barEnd])
overlapLeft = barStartMoment.isBetween(otherBarStart, otherBarEnd)
overlapLeft = barStartMoment.isBetween(otherBarStart, otherBarEnd)
overlapRight = barEndMoment.isBetween(otherBarStart, otherBarEnd)
overlapInBetween = otherBarStart.isBetween(barStartMoment, barEndMoment)
|| otherBarEnd.isBetween(barStartMoment, barEndMoment)
overlapInBetween =
otherBarStart.isBetween(barStartMoment, barEndMoment) ||
otherBarEnd.isBetween(barStartMoment, barEndMoment)
return overlapLeft || overlapRight || overlapInBetween
})
let overlapType = overlapLeft ? "left" : (overlapRight ? "right" : (overlapInBetween ? "between" : null))
return {overlapBar, overlapType}
let overlapType = overlapLeft
? 'left'
: overlapRight
? 'right'
: overlapInBetween
? 'between'
: null
return { overlapBar, overlapType }
},
// this is used in GGanttChart, when a bar from a bundle is pushed
// so that bars from its bundle also get pushed
moveBarByMinutesAndPush(minuteCount, direction){
switch(direction){
case "left":
this.barStartMoment = moment(this.barStartMoment).subtract(minuteCount, "minutes", true)
this.barEndMoment = moment(this.barEndMoment).subtract(minuteCount, "minutes", true)
moveBarByMinutesAndPush(minuteCount, direction) {
switch (direction) {
case 'left':
this.barStartMoment = moment(this.barStartMoment).subtract(
minuteCount,
this.timeChildKey,
true
)
this.barEndMoment = moment(this.barEndMoment).subtract(
minuteCount,
this.timeChildKey,
true
)
break
case "right":
this.barStartMoment = moment(this.barStartMoment).add(minuteCount, "minutes", true)
this.barEndMoment = moment(this.barEndMoment).add(minuteCount, "minutes", true)
case 'right':
this.barStartMoment = moment(this.barStartMoment).add(
minuteCount,
this.timeChildKey,
true
)
this.barEndMoment = moment(this.barEndMoment).add(
minuteCount,
this.timeChildKey,
true
)
break
default:
// eslint-disable-next-line
console.warn("wrong direction in moveBarByMinutesAndPush")
console.warn('wrong direction in moveBarByMinutesAndPush')
return
}
this.manageOverlapping()
@@ -373,127 +457,127 @@ export default {
/* --------------------------------------------------------- */
/* ------- MAPPING POSITION TO TIME (AND VICE VERSA) ------- */
/* --------------------------------------------------------- */
mapTimeToPosition(time){
let hourDiffFromStart = moment(time).diff(this.chartStartMoment, "hour", true)
return (hourDiffFromStart / this.getHourCount()) * this.barContainer.width
mapTimeToPosition(time) {
let timeDiffFromStart = moment(time).diff(
this.chartStartMoment,
this.timeUnit,
true
)
return (timeDiffFromStart / this.getTimeCount()) * this.barContainer.width
},
mapPositionToTime(xPos){
let hourDiffFromStart = (xPos/this.barContainer.width)*this.getHourCount()
return this.chartStartMoment.clone().add(hourDiffFromStart, "hours")
mapPositionToTime(xPos) {
let timeDiffFromStart =
(xPos / this.barContainer.width) * this.getTimeCount()
return this.chartStartMoment.clone().add(timeDiffFromStart, this.timeUnit)
},
},
filters:{
TimeFilter(value){
return moment(value).format("HH:mm")
}
}
}
</script>
<style scoped>
.g-gantt-bar {
position: absolute;
top: 2px;
left: 30px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
width: 300px;
height: 34px;
border-radius: 15px;
background: #79869c;
overflow: hidden;
}
.g-gantt-bar {
position: absolute;
top: 2px;
left: 30px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
width: 300px;
height: 34px;
border-radius: 15px;
background: #79869c;
overflow: hidden;
}
.g-gantt-bar-label {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0 14px 0 14px; /* 14px is the width of the handle */
display: flex;
justify-content: center;
align-items: center;
}
.g-gantt-bar-label {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0 14px 0 14px; /* 14px is the width of the handle */
display: flex;
justify-content: center;
align-items: center;
}
.g-gantt-bar-label > * {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.g-gantt-bar-label > * {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.g-gantt-bar > .g-gantt-bar-handle-left, .g-gantt-bar > .g-gantt-bar-handle-right {
position: absolute;
width: 10px;
height: 100%;
background: white;
opacity: 0.7;
border-radius: 40px;
cursor: w-resize;
}
.g-gantt-bar > .g-gantt-bar-handle-left,
.g-gantt-bar > .g-gantt-bar-handle-right {
position: absolute;
width: 10px;
height: 100%;
background: white;
opacity: 0.7;
border-radius: 40px;
cursor: w-resize;
}
.g-gantt-bar-handle-left {
left: 0;
}
.g-gantt-bar-handle-left {
left: 0;
}
.g-gantt-bar-handle-right {
right: 0;
}
.g-gantt-bar-handle-right {
right: 0;
}
.g-gantt-bar-label img {
pointer-events: none;
}
.g-gantt-bar-label img {
pointer-events: none;
}
.g-gantt-tooltip{
position: absolute;
background: black;
color: white;
z-index: 3;
font-size: 0.70em;
padding: 3px;
border-radius: 3px;
transition: opacity 0.2s;
display: flex;
align-items: center;
}
.g-gantt-tooltip {
position: absolute;
background: black;
color: white;
z-index: 3;
font-size: 0.7em;
padding: 3px;
border-radius: 3px;
transition: opacity 0.2s;
display: flex;
align-items: center;
}
.g-gantt-tooltip:before {
content: '';
position: absolute;
top: 0;
left: 10%;
width: 0;
height: 0;
border: 10px solid transparent;
border-bottom-color: black;
border-top: 0;
margin-left: -5px;
margin-top: -5px;
}
.g-gantt-tooltip:before {
content: '';
position: absolute;
top: 0;
left: 10%;
width: 0;
height: 0;
border: 10px solid transparent;
border-bottom-color: black;
border-top: 0;
margin-left: -5px;
margin-top: -5px;
}
.g-gantt-tooltip > .color-indicator {
width: 8px;
height: 8px;
border-radius: 100%;
margin-right: 4px;
}
.g-gantt-tooltip > .color-indicator {
width: 8px;
height: 8px;
border-radius: 100%;
margin-right: 4px;
}
.fade-enter-active {
animation: fade-in .3s;
}
.fade-enter-active {
animation: fade-in 0.3s;
}
.fade-leave-active {
animation: fade-in .3s reverse;
.fade-leave-active {
animation: fade-in 0.3s reverse;
}
@keyframes fade-in {
from {
opacity: 0;
}
@keyframes fade-in {
from {
opacity: 0;
} to {
opacity: 1;
}
to {
opacity: 1;
}
}
</style>
+228 -149
View File
@@ -1,7 +1,7 @@
<template>
<div
<div
id="g-gantt-chart"
:style="{width: width, background: themeColors.background}"
:style="{ width: width, background: themeColors.background }"
>
<g-gantt-timeaxis
v-if="!hideTimeaxis"
@@ -13,18 +13,18 @@
:locale="locale"
/>
<g-gantt-grid
<g-gantt-grid
v-if="grid"
:chart-start="chartStart"
:chart-end="chartEnd"
:row-label-width="rowLabelWidth"
:highlighted-hours="highlightedHours"
/>
<div id="g-gantt-rows-container">
<slot/> <!-- the g-gantt-row components go here -->
<slot />
<!-- the g-gantt-row components go here -->
</div>
</div>
</template>
@@ -37,82 +37,93 @@ import GGanttRow from './GGanttRow.vue'
import GGanttBar from './GGanttBar.vue'
export default {
name: 'GGanttChart',
name: "GGanttChart",
components:{
components: {
GGanttTimeaxis,
GGanttGrid
GGanttGrid,
},
props:{
chartStart: {type: String, default: moment().startOf("day").format("YYYY-MM-DD HH:mm:ss")},
chartEnd: {type: String, default: moment().startOf("day").add(12,"hours").format("YYYY-MM-DD HH:mm:ss")},
props: {
chartStart: { type: String },
chartEnd: { type: String },
hideTimeaxis: Boolean,
rowLabelWidth: {type: String, default: "10%"},
rowHeight: {type: Number, default: 40},
locale: {type: String, default: "en"},
theme: {type: String},
grid: {type: Boolean},
highlightedHours: {type: Array, default: () => []},
width: {type: String, default: "100%"}, // the total width of the entire ganttastic component in %
pushOnOverlap: {type: Boolean},
snapBackOnOverlap: {type: Boolean},
rowLabelWidth: { type: String, default: '10%' },
rowHeight: { type: Number, default: 40 },
locale: { type: String, default: 'en' },
theme: { type: String },
grid: { type: Boolean },
highlightedHours: { type: Array, default: () => [] },
width: { type: String, default: '100%' }, // the total width of the entire ganttastic component in %
pushOnOverlap: { type: Boolean },
snapBackOnOverlap: { type: Boolean },
minGapBetweenBars: {
type: Number,
default: 0
default: 0,
},
defaultBarLength: { type: Number, default: 1 },
// ["month_days", "day_hours"]
timeaxisMode: {type: String, default: "month_days"}
timeaxisMode: { type: String, default: 'month_days' },
},
data(){
return{
data() {
return {
timemarkerOffset: 0,
movedBarsInDrag: new Set()
movedBarsInDrag: new Set(),
timeUnit: this.timeaxisMode === 'month_days' ? 'days' : 'hours',
timeFormat:
this.timeaxisMode === 'month_days' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm',
}
},
computed:{
hourCount(){
computed: {
timeCount() {
let momentChartStart = moment(this.chartStart)
let momentChartEnd = moment(this.chartEnd)
return Math.floor(momentChartEnd.diff(momentChartStart, "hour", true))
return Math.floor(momentChartEnd.diff(momentChartStart, this.timeUnit, true))
},
themeColors(){
themeColors() {
return GanttasticThemeColors[this.theme] || GanttasticThemeColors.default
},
},
methods: {
getGanttBarChildrenList(){
getGanttBarChildrenList() {
let ganttBarChildren = []
let ganttRowChildrenList = this.$children.filter(childComp => childComp.$options.name === GGanttRow.name)
ganttRowChildrenList.forEach(row => {
let ganttBarChildrenOfRow = row.$children.filter(childComp => childComp.$options.name === GGanttBar.name)
let ganttRowChildrenList = this.$children.filter(
(childComp) => childComp.$options.name === GGanttRow.name
)
ganttRowChildrenList.forEach((row) => {
let ganttBarChildrenOfRow = row.$children.filter(
(childComp) => childComp.$options.name === GGanttBar.name
)
ganttBarChildren.push(...ganttBarChildrenOfRow)
})
return ganttBarChildren
},
getBarsFromBundle(bundleId){
if(bundleId === undefined || bundleId === null){
getBarsFromBundle(bundleId) {
if (bundleId === undefined || bundleId === null) {
return []
}
return this.getGanttBarChildrenList().filter(ganttBarChild => ganttBarChild.barConfig.bundle === bundleId)
return this.getGanttBarChildrenList().filter(
(ganttBarChild) => ganttBarChild.barConfig.bundle === bundleId
)
},
initDragOfBarsFromBundle(gGanttBar, e){
initDragOfBarsFromBundle(gGanttBar, e) {
gGanttBar.initDrag(e)
this.movedBarsInDrag.add(gGanttBar.bar)
if(gGanttBar.barConfig.bundle !== null && gGanttBar.barConfig.bundle !== undefined){
this.getGanttBarChildrenList().forEach(ganttBarChild => {
if(ganttBarChild.barConfig.bundle === gGanttBar.barConfig.bundle && ganttBarChild !== gGanttBar){
if (
gGanttBar.barConfig.bundle !== null &&
gGanttBar.barConfig.bundle !== undefined
) {
this.getGanttBarChildrenList().forEach((ganttBarChild) => {
if (
ganttBarChild.barConfig.bundle === gGanttBar.barConfig.bundle &&
ganttBarChild !== gGanttBar
) {
ganttBarChild.initDrag(e)
this.movedBarsInDrag.add(ganttBarChild.bar)
}
@@ -120,47 +131,56 @@ export default {
}
},
moveBarsFromBundleOfPushedBar(pushedBar, minuteDiff, overlapType){
moveBarsFromBundleOfPushedBar(pushedBar, minuteDiff, overlapType) {
this.movedBarsInDrag.add(pushedBar)
let bundleId = pushedBar.ganttBarConfig.bundle
if(bundleId === undefined || bundleId === null){
if (bundleId === undefined || bundleId === null) {
return
}
this.getGanttBarChildrenList().forEach(ganttBarChild => {
if(ganttBarChild.barConfig.bundle === bundleId && ganttBarChild.bar !== pushedBar){
this.getGanttBarChildrenList().forEach((ganttBarChild) => {
if (
ganttBarChild.barConfig.bundle === bundleId &&
ganttBarChild.bar !== pushedBar
) {
ganttBarChild.moveBarByMinutesAndPush(minuteDiff, overlapType)
this.movedBarsInDrag.add(ganttBarChild.bar)
}
})
},
shouldSnapBackBar(ganttBar){
if(this.snapBackOnOverlap && ganttBar.barConfig.pushOnOverlap !== false){
let {overlapBar} = ganttBar.getOverlapBarAndType(ganttBar.bar)
shouldSnapBackBar(ganttBar) {
if (
this.snapBackOnOverlap &&
ganttBar.barConfig.pushOnOverlap !== false
) {
let { overlapBar } = ganttBar.getOverlapBarAndType(ganttBar.bar)
return !!overlapBar
}
return false
},
snapBackBundleIfNeeded(ganttBar){
snapBackBundleIfNeeded(ganttBar) {
let barsFromBundle = this.getBarsFromBundle(ganttBar.barConfig.bundle)
if(this.shouldSnapBackBar(ganttBar) || barsFromBundle.some(gBar => this.shouldSnapBackBar(gBar))){
if (
this.shouldSnapBackBar(ganttBar) ||
barsFromBundle.some((gBar) => this.shouldSnapBackBar(gBar))
) {
ganttBar.snapBack()
barsFromBundle.forEach(gBar => gBar.snapBack())
barsFromBundle.forEach((gBar) => gBar.snapBack())
return true
}
return false
},
onBarEvent({event, type, time}, ganttBar){
this.$emit(`${type}-bar`, {event, bar: ganttBar.bar, time})
onBarEvent({ event, type, time }, ganttBar) {
this.$emit(`${type}-bar`, { event, bar: ganttBar.bar, time })
},
onDragendBar(e, ganttBar){
onDragendBar(e, ganttBar) {
let didSnapBack = this.snapBackBundleIfNeeded(ganttBar)
let movedBars = didSnapBack ? new Set() : this.movedBarsInDrag
this.movedBarsInDrag = new Set()
this.$emit("dragend-bar", {event: e, bar: ganttBar.bar, movedBars})
this.$emit('dragend-bar', { event: e, bar: ganttBar.bar, movedBars })
},
// ------------------------------------------------------------------------
@@ -170,162 +190,221 @@ export default {
// how far you can drag a bar depends on the position of the closest immobile bar
// note that if a bar from the same row belongs to a bundle
// other rows might need to be taken into consideration, too
setDragLimitsOfGanttBar(bar){
if(!this.pushOnOverlap || bar.barConfig.pushOnOverlap === false){
setDragLimitsOfGanttBar(bar) {
if (!this.pushOnOverlap || bar.barConfig.pushOnOverlap === false) {
return
}
for(let side of ["left", "right"]){
let [totalGapDistance, bundleBarsOnPath] = this.countGapDistanceToNextImmobileBar(bar, null, side, false)
for(let i=0; i< bundleBarsOnPath.length; i++){
for (let side of ['left', 'right']) {
let [
totalGapDistance,
bundleBarsOnPath,
] = this.countGapDistanceToNextImmobileBar(bar, null, side, false)
for (let i = 0; i < bundleBarsOnPath.length; i++) {
let barFromBundle = bundleBarsOnPath[i].bar
let gapDist = bundleBarsOnPath[i].gapDistance
let otherBarsFromBundle = this.getBarsFromBundle(barFromBundle.barConfig.bundle).filter(otherBar => otherBar !== barFromBundle)
otherBarsFromBundle.forEach(otherBar => {
let [newGapDistance, newBundleBars] = this.countGapDistanceToNextImmobileBar(otherBar, gapDist, side)
if(newGapDistance !== null && (newGapDistance < totalGapDistance || !totalGapDistance)){
let otherBarsFromBundle = this.getBarsFromBundle(
barFromBundle.barConfig.bundle
).filter((otherBar) => otherBar !== barFromBundle)
otherBarsFromBundle.forEach((otherBar) => {
let [
newGapDistance,
newBundleBars,
] = this.countGapDistanceToNextImmobileBar(otherBar, gapDist, side)
if (
newGapDistance !== null &&
(newGapDistance < totalGapDistance || !totalGapDistance)
) {
totalGapDistance = newGapDistance
}
newBundleBars.forEach(newBundleBar => {
if(!bundleBarsOnPath.find(barAndGap => barAndGap.bar === newBundleBar.bar)){
newBundleBars.forEach((newBundleBar) => {
if (
!bundleBarsOnPath.find(
(barAndGap) => barAndGap.bar === newBundleBar.bar
)
) {
bundleBarsOnPath.push(newBundleBar)
}
})
})
}
if(totalGapDistance != null && side === "left"){
bar.dragLimitLeft = bar.$refs['g-gantt-bar'].offsetLeft - totalGapDistance
} else if(totalGapDistance != null && side === "right"){
bar.dragLimitRight = bar.$refs['g-gantt-bar'].offsetLeft+ bar.$refs['g-gantt-bar'].offsetWidth + totalGapDistance
if (totalGapDistance != null && side === 'left') {
bar.dragLimitLeft =
bar.$refs['g-gantt-bar'].offsetLeft - totalGapDistance
} else if (totalGapDistance != null && side === 'right') {
bar.dragLimitRight =
bar.$refs['g-gantt-bar'].offsetLeft +
bar.$refs['g-gantt-bar'].offsetWidth +
totalGapDistance
}
}
// all bars from the bundle of the clicked bar need to have the same drag limit:
let barsFromBundleOfClickedBar = this.getBarsFromBundle(bar.barConfig.bundle)
barsFromBundleOfClickedBar.forEach(barFromBundle => {
barFromBundle.dragLimitLeft = bar.dragLimitLeft
let barsFromBundleOfClickedBar = this.getBarsFromBundle(
bar.barConfig.bundle
)
barsFromBundleOfClickedBar.forEach((barFromBundle) => {
barFromBundle.dragLimitLeft = bar.dragLimitLeft
barFromBundle.dragLimitRight = bar.dragLimitRight
})
},
// returns the gap distance to the next immobile bar
// in the row where the given bar (parameter) is (added to gapDistanceSoFar)
// and a list of all bars on that path that belong to a bundle
countGapDistanceToNextImmobileBar(bar, gapDistanceSoFar, side="left", ignoreShadows = true){
let bundleBarsAndGapDist = bar.barConfig.bundle ? [{bar, gapDistance: gapDistanceSoFar}] : []
countGapDistanceToNextImmobileBar(
bar,
gapDistanceSoFar,
side = 'left',
ignoreShadows = true
) {
let bundleBarsAndGapDist = bar.barConfig.bundle
? [{ bar, gapDistance: gapDistanceSoFar }]
: []
let currentBar = bar
let nextBar = this.getNextGanttBar(currentBar, side)
// left side:
if(side === "left"){
while(nextBar){
let nextBarOffsetRight = nextBar.$refs['g-gantt-bar'].offsetLeft + nextBar.$refs['g-gantt-bar'].offsetWidth
gapDistanceSoFar += currentBar.$refs['g-gantt-bar'].offsetLeft - nextBarOffsetRight
if(nextBar.barConfig.immobile || (nextBar.barConfig.isShadow && !ignoreShadows)){
if (side === 'left') {
while (nextBar) {
let nextBarOffsetRight =
nextBar.$refs['g-gantt-bar'].offsetLeft +
nextBar.$refs['g-gantt-bar'].offsetWidth
gapDistanceSoFar +=
currentBar.$refs['g-gantt-bar'].offsetLeft - nextBarOffsetRight
if (
nextBar.barConfig.immobile ||
(nextBar.barConfig.isShadow && !ignoreShadows)
) {
return [gapDistanceSoFar, bundleBarsAndGapDist]
} else if(nextBar.barConfig.bundle){
bundleBarsAndGapDist.push({bar: nextBar, gapDistance: gapDistanceSoFar})
} else if (nextBar.barConfig.bundle) {
bundleBarsAndGapDist.push({
bar: nextBar,
gapDistance: gapDistanceSoFar,
})
}
currentBar = nextBar
nextBar = this.getNextGanttBar(nextBar, "left")
nextBar = this.getNextGanttBar(nextBar, 'left')
}
}
if(side === "right"){
while(nextBar){
let currentBarOffsetRight = currentBar.$refs['g-gantt-bar'].offsetLeft + currentBar.$refs['g-gantt-bar'].offsetWidth
gapDistanceSoFar += nextBar.$refs['g-gantt-bar'].offsetLeft - currentBarOffsetRight
if(nextBar.barConfig.immobile || (nextBar.barConfig.isShadow && !ignoreShadows)){
if (side === 'right') {
while (nextBar) {
let currentBarOffsetRight =
currentBar.$refs['g-gantt-bar'].offsetLeft +
currentBar.$refs['g-gantt-bar'].offsetWidth
gapDistanceSoFar +=
nextBar.$refs['g-gantt-bar'].offsetLeft - currentBarOffsetRight
if (
nextBar.barConfig.immobile ||
(nextBar.barConfig.isShadow && !ignoreShadows)
) {
return [gapDistanceSoFar, bundleBarsAndGapDist]
} else if(nextBar.barConfig.bundle){
bundleBarsAndGapDist.push({bar: nextBar, gapDistance: gapDistanceSoFar})
} else if (nextBar.barConfig.bundle) {
bundleBarsAndGapDist.push({
bar: nextBar,
gapDistance: gapDistanceSoFar,
})
}
currentBar = nextBar
nextBar = this.getNextGanttBar(nextBar, "right")
nextBar = this.getNextGanttBar(nextBar, 'right')
}
}
return [null, bundleBarsAndGapDist]
},
getNextGanttBar(bar, side="left"){
getNextGanttBar(bar, side = 'left') {
let allBarsLeftOrRight = []
if(side === "left"){
allBarsLeftOrRight = bar.$parent.$children.filter(gBar => {
return gBar.$options.name === GGanttBar.name
&& gBar.$parent === bar.$parent
&& gBar.$refs['g-gantt-bar']
&& gBar.$refs['g-gantt-bar'].offsetLeft < bar.$refs['g-gantt-bar'].offsetLeft
&& gBar.barConfig.pushOnOverlap !== false
if (side === 'left') {
allBarsLeftOrRight = bar.$parent.$children.filter((gBar) => {
return (
gBar.$options.name === GGanttBar.name &&
gBar.$parent === bar.$parent &&
gBar.$refs['g-gantt-bar'] &&
gBar.$refs['g-gantt-bar'].offsetLeft <
bar.$refs['g-gantt-bar'].offsetLeft &&
gBar.barConfig.pushOnOverlap !== false
)
})
} else {
allBarsLeftOrRight = bar.$parent.$children.filter(gBar => {
return gBar.$options.name === GGanttBar.name
&& gBar.$parent === bar.$parent
&& gBar.$refs['g-gantt-bar']
&& gBar.$refs['g-gantt-bar'].offsetLeft > bar.$refs['g-gantt-bar'].offsetLeft
&& gBar.barConfig.pushOnOverlap !== false
allBarsLeftOrRight = bar.$parent.$children.filter((gBar) => {
return (
gBar.$options.name === GGanttBar.name &&
gBar.$parent === bar.$parent &&
gBar.$refs['g-gantt-bar'] &&
gBar.$refs['g-gantt-bar'].offsetLeft >
bar.$refs['g-gantt-bar'].offsetLeft &&
gBar.barConfig.pushOnOverlap !== false
)
})
}
if(allBarsLeftOrRight.length > 0){
return allBarsLeftOrRight.reduce(
(bar1, bar2) => {
let bar1Dist = Math.abs(bar1.$refs['g-gantt-bar'].offsetLeft - bar.$refs['g-gantt-bar'].offsetLeft)
let bar2Dist = Math.abs(bar2.$refs['g-gantt-bar'].offsetLeft - bar.$refs['g-gantt-bar'].offsetLeft)
return bar1Dist < bar2Dist ? bar1 : bar2
},
allBarsLeftOrRight[0]
)
if (allBarsLeftOrRight.length > 0) {
return allBarsLeftOrRight.reduce((bar1, bar2) => {
let bar1Dist = Math.abs(
bar1.$refs['g-gantt-bar'].offsetLeft -
bar.$refs['g-gantt-bar'].offsetLeft
)
let bar2Dist = Math.abs(
bar2.$refs['g-gantt-bar'].offsetLeft -
bar.$refs['g-gantt-bar'].offsetLeft
)
return bar1Dist < bar2Dist ? bar1 : bar2
}, allBarsLeftOrRight[0])
} else {
return null
}
}
},
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
},
// all child components of GGanttChart may have access to
// the following values by using Vue's "inject" option:
provide(){
provide() {
return {
getChartStart: () => this.chartStart,
getChartEnd: () => this.chartEnd,
getHourCount: () => this.hourCount,
getTimeCount: () => this.timeCount,
ganttChartProps: this.$props,
getThemeColors: () => this.themeColors,
initDragOfBarsFromBundle: (bundleId, e) => this.initDragOfBarsFromBundle(bundleId, e),
moveBarsFromBundleOfPushedBar: (bar, minuteDiff, overlapType) => this.moveBarsFromBundleOfPushedBar(bar, minuteDiff, overlapType),
setDragLimitsOfGanttBar : (ganttBar) => this.setDragLimitsOfGanttBar(ganttBar),
initDragOfBarsFromBundle: (bundleId, e) =>
this.initDragOfBarsFromBundle(bundleId, e),
moveBarsFromBundleOfPushedBar: (bar, minuteDiff, overlapType) =>
this.moveBarsFromBundleOfPushedBar(bar, minuteDiff, overlapType),
setDragLimitsOfGanttBar: (ganttBar) =>
this.setDragLimitsOfGanttBar(ganttBar),
onBarEvent: (e, ganttBar) => this.onBarEvent(e, ganttBar),
onDragendBar: (e, ganttBar) => this.onDragendBar(e, ganttBar),
shouldSnapBackOnOverlap: () => this.snapBackOnOverlap,
snapBackBundle: (ganttBar) => this.snapBackBundle(ganttBar),
getMinGapBetweenBars: () => this.minGapBetweenBars,
getDefaultBarLength: () => this.defaultBarLength,
getTimeaxisMode:() => this.timeaxisMode
getTimeaxisMode: () => this.timeaxisMode,
getTimeUnit: () => this.timeUnit,
getTimeFormat: () => this.timeFormat
}
}
},
}
</script>
<style scoped>
#g-gantt-chart{
position: relative;
display: flex;
flex-direction: column;
overflow-x: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
padding-bottom: 23px;
}
#g-gantt-chart {
position: relative;
display: flex;
flex-direction: column;
overflow-x: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
padding-bottom: 23px;
}
#g-gantt-chart >>> * {
font-family: Roboto, Verdana;
}
#g-gantt-chart >>> * {
font-family: Roboto, Verdana;
}
#g-gantt-rows-container{
position: relative;
}
#g-gantt-rows-container {
position: relative;
}
</style>
+110 -101
View File
@@ -2,18 +2,15 @@
<div
class="g-gantt-row"
ref="g-gantt-row"
:style="{height: `${$parent.rowHeight}px`}"
:style="{ height: `${$parent.rowHeight}px` }"
v-on="$listeners"
>
<div
class="g-gantt-row-label"
:style="rowLabelStyle"
>
<div class="g-gantt-row-label" :style="rowLabelStyle">
<slot name="label">
{{label}}
{{ label }}
</slot>
</div>
<div
<div
class="g-gantt-row-bars-container"
ref="barContainer"
:style="barsContainerStyle"
@@ -23,8 +20,8 @@
@dblclick="onDoubleClick($event)"
@mouseover="onMouseover()"
@mouseleave="onMouseleave()"
>
<g-gantt-bar
>
<g-gantt-bar
v-for="(bar, index) in bars"
:key="`ganttastic_bar_${index}`"
:bar="bar"
@@ -34,11 +31,8 @@
:bar-container="barContainer"
:all-bars-in-row="bars"
>
<template #bar-label="{bar}">
<slot
name="bar-label"
:bar="bar"
/>
<template #bar-label="{ bar }">
<slot name="bar-label" :bar="bar" />
</template>
</g-gantt-bar>
</div>
@@ -50,147 +44,162 @@ import GGanttBar from './GGanttBar.vue'
import moment from 'moment'
export default {
name: 'GGanttRow',
name: "GGanttRow",
components:{
GGanttBar
components: {
GGanttBar,
},
props:{
label: {type: String, default: "Row"},
bars: {type: Array, default: () => []},
barStart: {type: String, required: true}, // property name of the bar objects that represents the start datetime
barEnd: {type: String, required: true}, // property name of the bar objects that represents the end datetime,
props: {
label: { type: String, default: 'Row' },
bars: { type: Array, default: () => [] },
barStart: { type: String, required: true }, // property name of the bar objects that represents the start datetime
barEnd: { type: String, required: true }, // property name of the bar objects that represents the end datetime,
highlightOnHover: Boolean,
},
inject: [
"ganttChartProps",
"getThemeColors",
"getHourCount",
"getChartStart",
"getChartEnd",
"getDefaultBarLength"
'ganttChartProps',
'getThemeColors',
'getTimeCount',
'getChartStart',
'getChartEnd',
'getDefaultBarLength',
'getTimeUnit',
'getTimeFormat',
],
data(){
data() {
return {
barContainer: {}
barContainer: {},
timeUnit: this.getTimeUnit(),
timeFormat: this.getTimeFormat(),
}
},
computed:{
rowLabelStyle(){
computed: {
rowLabelStyle() {
return {
width: this.ganttChartProps.rowLabelWidth,
height: this.ganttChartProps.rowHeight,
background: this.$parent.themeColors.ternary,
color: this.$parent.themeColors.text
color: this.$parent.themeColors.text,
}
},
barsContainerStyle(){
return{
width: `${100 - this.ganttChartProps.rowLabelWidth.replace('%','')}%`,
barsContainerStyle() {
return {
width: `${100 - this.ganttChartProps.rowLabelWidth.replace('%', '')}%`,
}
},
},
mounted(){
this.barContainer = this.$refs.barContainer.getBoundingClientRect()
window.addEventListener("resize", this.onWindowResize)
mounted() {
this.barContainer = this.$refs.barContainer.getBoundingClientRect()
window.addEventListener('resize', this.onWindowResize)
},
methods:{
methods: {
onDragover(e) {
e.preventDefault() // enables dropping content on row
if(this.highlightOnHover){
console.log({backgroundColor: this.$refs["g-gantt-row"].style.backgroundColor })
this.$refs["g-gantt-row"].style.backgroundColor = this.getThemeColors().hoverHighlight
console.log({backgroundColor: this.$refs["g-gantt-row"].style.backgroundColor })
e.preventDefault() // enables dropping content on row
if (this.highlightOnHover) {
console.log({
backgroundColor: this.$refs['g-gantt-row'].style.backgroundColor,
})
this.$refs[
'g-gantt-row'
].style.backgroundColor = this.getThemeColors().hoverHighlight
console.log({
backgroundColor: this.$refs['g-gantt-row'].style.backgroundColor,
})
}
},
onDragleave() {
this.$refs["g-gantt-row"].style.backgroundColor = null
this.$refs['g-gantt-row'].style.backgroundColor = null
},
onDrop(e){
onDrop(e) {
let barContainer = this.$refs.barContainer.getBoundingClientRect()
let xPos = e.clientX - barContainer.left
let hourDiffFromStart = (xPos/barContainer.width) * this.getHourCount()
let time = moment(this.getChartStart()).add(hourDiffFromStart, "hours")
let bar = this.bars.find(bar => time.isBetween(bar[this.barStart], bar[this.barEnd]))
this.$emit("drop", {event: e, bar, time: time.format("YYYY-MM-DD HH:mm:ss")})
let timeDiffFromStart = (xPos / barContainer.width) * this.getTimeCount()
let time = moment(this.getChartStart()).add(
timeDiffFromStart,
this.timeUnit
)
let bar = this.bars.find((bar) =>
time.isBetween(bar[this.barStart], bar[this.barEnd])
)
this.$emit('drop', { event: e, bar, time: time.format(this.timeFormat) })
},
onDoubleClick(e){
onDoubleClick(e) {
let barContainer = this.$refs.barContainer.getBoundingClientRect()
let xPos = e.clientX - barContainer.left
let hourDiffFromStart = (xPos/barContainer.width) * this.getHourCount()
let time = moment(this.getChartStart()).add(hourDiffFromStart, "hours")
let bar = {};
bar[this.barStart] = time.format("YYYY-MM-DD HH:mm:ss")
bar[this.barEnd] = time.add(this.getDefaultBarLength(), "hours").format("YYYY-MM-DD HH:mm:ss")
bar.ganttBarConfig = {handles: true}
let timeDiffFromStart = (xPos / barContainer.width) * this.getTimeCount()
let time = moment(this.getChartStart()).add(
timeDiffFromStart,
this.timeUnit
)
let bar = {}
bar[this.barStart] = time
bar[this.barEnd] = time.add(this.getDefaultBarLength(), this.timeUnit)
bar.ganttBarConfig = { handles: true }
this.bars.push(bar)
},
onMouseover(){
if(this.highlightOnHover){
this.$refs["g-gantt-row"].style.backgroundColor = this.getThemeColors().hoverHighlight
onMouseover() {
if (this.highlightOnHover) {
this.$refs[
'g-gantt-row'
].style.backgroundColor = this.getThemeColors().hoverHighlight
}
},
onMouseleave(){
this.$refs["g-gantt-row"].style.backgroundColor = null
onMouseleave() {
this.$refs['g-gantt-row'].style.backgroundColor = null
},
onWindowResize(){
onWindowResize() {
// re-initialize the barContainer DOMRect variable, which will trigger re-rendering in the gantt bars
this.barContainer = this.$refs.barContainer.getBoundingClientRect()
}
this.barContainer = this.$refs.barContainer.getBoundingClientRect()
},
},
watch:{
'ganttChartProps.rowLabelWidth' : function(){
this.barContainer = this.$refs.barContainer.getBoundingClientRect()
}
}
watch: {
'ganttChartProps.rowLabelWidth': function () {
this.barContainer = this.$refs.barContainer.getBoundingClientRect()
},
},
}
</script>
<style scoped>
.g-gantt-row{
display: flex;
width: 100%;
height: 40px;
transition: background-color 0.2s;
}
.g-gantt-row {
display: flex;
width: 100%;
height: 40px;
transition: background-color 0.2s;
}
.g-gantt-row > .g-gantt-row-label{
display: flex;
justify-content: center;
align-items: center;
width: 20%;
background: #E8E8E8;
color: #424242;
font-size: 0.9em;
z-index: 3;
overflow: hidden;
font-weight: bold;
}
.g-gantt-row > .g-gantt-row-bars-container{
position: relative;
border-top: 1px solid #eaeaea;
width: 70%;
border-bottom: 1px solid #eaeaea;
}
.g-gantt-row > .g-gantt-row-label {
display: flex;
justify-content: center;
align-items: center;
width: 20%;
background: #e8e8e8;
color: #424242;
font-size: 0.9em;
z-index: 3;
overflow: hidden;
font-weight: bold;
}
.g-gantt-row > .g-gantt-row-bars-container {
position: relative;
border-top: 1px solid #eaeaea;
width: 70%;
border-bottom: 1px solid #eaeaea;
}
</style>
+14 -14
View File
@@ -55,7 +55,7 @@ import moment from 'moment'
export default {
name: 'GGanttTimeaxis',
inject: ['getTimeaxisMode'],
inject: ['getTimeaxisMode', 'getTimeUnit', 'getTimeFormat'],
props: {
chartStart: String,
@@ -74,6 +74,8 @@ export default {
hourFontSize: '11px',
dayFormat: 'MM-DD', // ISO 8601
mode: this.getTimeaxisMode(),
timeUnit: this.getTimeUnit(),
timeFormat: this.getTimeFormat()
}
},
@@ -100,10 +102,9 @@ export default {
let end = moment(this.chartEnd)
this.childPointCount = Math.floor(end.diff(start, 'day', true))
while (start.isBefore(end)) {
let dayCountOfMonth =
start.isSame(end, 'day')
? end.date()
: start.daysInMonth() - start.date() + 1
let dayCountOfMonth = start.isSame(end, 'month')
? end.date()
: start.daysInMonth() - start.date() + 1
let widthPercentage = (dayCountOfMonth / this.childPointCount) * 100
let endDay =
start.month() === end.month() ? end.date() : end.daysInMonth()
@@ -119,10 +120,9 @@ export default {
let end = moment(this.chartEnd)
this.childPointCount = Math.floor(end.diff(start, 'hour', true))
while (start.isBefore(end)) {
let hourCountOfDay =
start.isSame(end, 'day')
? end.hour()
: 24 - start.hour()
let hourCountOfDay = start.isSame(end, 'day')
? end.hour()
: 24 - start.hour()
let widthPercentage = (hourCountOfDay / this.childPointCount) * 100
let endHour = start.day() === end.day() ? end.hour() - 1 : 23 // -1 because the last hour is not included e.g if chartEnd=04:00 the last interval we display is between 03 and 04
@@ -137,14 +137,14 @@ export default {
let datetimeMoment = moment(datetime)
let axisMonthObject = {
widthPercentage: widthPercentage,
value: moment(datetime, 'YYYY-MM'), // ISO 8601
value: moment(datetime, 'YYYY-MM'), // ISO 8601
ganttDays: [],
}
let startDay = datetimeMoment.date()
for (let i = 0; i <= endDay - startDay; i++) {
let day = {
text: datetimeMoment.format('D'),
fullDatetime: datetimeMoment.format('YYYY-MM-DD'), // ISO 8601
fullDatetime: datetimeMoment.format(this.timeFormat),
}
axisMonthObject.ganttDays.push(day)
datetimeMoment.add(1, 'day')
@@ -156,14 +156,14 @@ export default {
let datetimeMoment = moment(datetime)
let axisDayObject = {
widthPercentage: widthPercentage,
value: moment(datetime, 'YYYY-MM-DD'), // ISO 8601
value: moment(datetime, 'YYYY-MM-DD'), // ISO 8601
ganttHours: [],
}
let startHour = datetimeMoment.hour()
for (let i = 0; i <= endHour - startHour; i++) {
let hour = {
text: datetimeMoment.format('HH'),
fullDatetime: datetimeMoment.format('YYYY-MM-DD HH:mm'), // ISO 8601
fullDatetime: datetimeMoment.format(this.timeFormat),
}
axisDayObject.ganttHours.push(hour)
datetimeMoment.add(1, 'hour')
@@ -190,7 +190,7 @@ export default {
) + 'px'
},
pointFormatted(point){
pointFormatted(point) {
if (this.mode === 'month_days') {
return this.monthFormatted(point)
} else if (this.mode === 'day_hours') {