feat: update ganttastic version
This commit is contained in:
parent
0f1c5e9394
commit
1d495b8603
6 changed files with 1599 additions and 1626 deletions
|
@ -23,7 +23,7 @@
|
||||||
"@fortawesome/free-solid-svg-icons": "6.2.0",
|
"@fortawesome/free-solid-svg-icons": "6.2.0",
|
||||||
"@fortawesome/vue-fontawesome": "3.0.1",
|
"@fortawesome/vue-fontawesome": "3.0.1",
|
||||||
"@github/hotkey": "2.0.1",
|
"@github/hotkey": "2.0.1",
|
||||||
"@infectoone/vue-ganttastic": "^2.0.4",
|
"@infectoone/vue-ganttastic": "./vendor/infectoone-vue-ganttastic-2.1.1.tgz",
|
||||||
"@kyvg/vue3-notification": "2.4.1",
|
"@kyvg/vue3-notification": "2.4.1",
|
||||||
"@sentry/tracing": "7.14.1",
|
"@sentry/tracing": "7.14.1",
|
||||||
"@sentry/vue": "7.14.1",
|
"@sentry/vue": "7.14.1",
|
||||||
|
|
3083
pnpm-lock.yaml
3083
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Loading class="gantt-container" v-if="taskService.loading || taskCollectionService.loading"/>
|
<Loading class="gantt-container" v-if="taskService.loading || taskCollectionService.loading"/>
|
||||||
<div class="gantt-container" v-else>
|
<div class="gantt-container" v-else>
|
||||||
<g-gantt-chart
|
<GGanttChart
|
||||||
:chart-start="`${dateFrom} 00:00`"
|
:chart-start="`${dateFrom} 00:00`"
|
||||||
:chart-end="`${dateTo} 23:59`"
|
:chart-end="`${dateTo} 23:59`"
|
||||||
:precision="PRECISION"
|
:precision="PRECISION"
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
:grid="true"
|
:grid="true"
|
||||||
@dragend-bar="updateTask"
|
@dragend-bar="updateTask"
|
||||||
@dblclick-bar="openTask"
|
@dblclick-bar="openTask"
|
||||||
font="inherit"
|
|
||||||
:width="ganttChartWidth + 'px'"
|
:width="ganttChartWidth + 'px'"
|
||||||
>
|
>
|
||||||
<template #timeunit="{label, value}">
|
<template #timeunit="{label, value}">
|
||||||
|
@ -23,75 +22,45 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<g-gantt-row
|
<GGanttRow
|
||||||
v-for="(bar, k) in ganttBars"
|
v-for="(bar, k) in ganttBars"
|
||||||
:key="k"
|
:key="k"
|
||||||
label=""
|
label=""
|
||||||
:bars="bar"
|
:bars="bar"
|
||||||
/>
|
/>
|
||||||
</g-gantt-chart>
|
</GGanttChart>
|
||||||
</div>
|
</div>
|
||||||
<TaskForm v-if="canWrite" @create-task="createTask" />
|
<TaskForm v-if="canWrite" @create-task="createTask" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {computed, ref, watchEffect, shallowReactive, type Ref, type PropType} from 'vue'
|
import {computed, ref, watch, watchEffect, shallowReactive, type PropType} from 'vue'
|
||||||
import {useRouter} from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import {format, parse} from 'date-fns'
|
import {format, parse} from 'date-fns'
|
||||||
|
|
||||||
import TaskCollectionService from '@/services/taskCollection'
|
import TaskCollectionService from '@/services/taskCollection'
|
||||||
import TaskService from '@/services/task'
|
import TaskService from '@/services/task'
|
||||||
import TaskModel from '@/models/task'
|
import TaskModel, { getHexColor } from '@/models/task'
|
||||||
|
|
||||||
import type ListModel from '@/models/list'
|
import type ListModel from '@/models/list'
|
||||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||||
import {RIGHTS} from '@/constants/rights'
|
import {RIGHTS} from '@/constants/rights'
|
||||||
|
|
||||||
|
import {
|
||||||
|
extendDayjs,
|
||||||
|
GGanttChart,
|
||||||
|
GGanttRow,
|
||||||
|
type GanttBarObject,
|
||||||
|
} from '@infectoone/vue-ganttastic'
|
||||||
|
|
||||||
import Loading from '@/components/misc/loading.vue'
|
import Loading from '@/components/misc/loading.vue'
|
||||||
import TaskForm from '@/components/tasks/TaskForm.vue'
|
import TaskForm from '@/components/tasks/TaskForm.vue'
|
||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
|
|
||||||
// FIXME: these types should be exported from vue-ganttastic
|
extendDayjs()
|
||||||
// see: https://github.com/InfectoOne/vue-ganttastic/blob/master/src/models/models.ts
|
|
||||||
|
|
||||||
export interface GanttBarConfig {
|
|
||||||
id: string,
|
|
||||||
label?: string
|
|
||||||
hasHandles?: boolean
|
|
||||||
immobile?: boolean
|
|
||||||
bundle?: string
|
|
||||||
pushOnOverlap?: boolean
|
|
||||||
dragLimitLeft?: number
|
|
||||||
dragLimitRight?: number
|
|
||||||
style?: CSSStyleSheet
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GanttBarObject = {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
[key: string]: any,
|
|
||||||
ganttBarConfig: GanttBarConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GGanttChartPropsRefs = {
|
|
||||||
chartStart: Ref<string>
|
|
||||||
chartEnd: Ref<string>
|
|
||||||
precision: Ref<'hour' | 'day' | 'month'>
|
|
||||||
barStart: Ref<string>
|
|
||||||
barEnd: Ref<string>
|
|
||||||
rowHeight: Ref<number>
|
|
||||||
dateFormat: Ref<string>
|
|
||||||
width: Ref<string>
|
|
||||||
hideTimeaxis: Ref<boolean>
|
|
||||||
colorScheme: Ref<string>
|
|
||||||
grid: Ref<boolean>
|
|
||||||
pushOnOverlap: Ref<boolean>
|
|
||||||
noOverlap: Ref<boolean>
|
|
||||||
gGanttChart: Ref<HTMLElement | null>
|
|
||||||
font: Ref<string>
|
|
||||||
}
|
|
||||||
|
|
||||||
const PRECISION = 'day'
|
|
||||||
|
|
||||||
|
const PRECISION = 'day' as const
|
||||||
const DATE_FORMAT = 'yyyy-LL-dd HH:mm'
|
const DATE_FORMAT = 'yyyy-LL-dd HH:mm'
|
||||||
|
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
|
@ -111,7 +80,7 @@ const props = defineProps({
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
showTasksWithoutDates: {
|
showTasksWithoutDates: {
|
||||||
type: Boolean as PropType<boolean>,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -134,6 +103,18 @@ const canWrite = computed(() => baseStore.currentList.maxRight > RIGHTS.READ)
|
||||||
const tasks = ref<Map<TaskModel['id'], TaskModel>>(new Map())
|
const tasks = ref<Map<TaskModel['id'], TaskModel>>(new Map())
|
||||||
const ganttBars = ref<GanttBarObject[][]>([])
|
const ganttBars = ref<GanttBarObject[][]>([])
|
||||||
|
|
||||||
|
watch(
|
||||||
|
tasks,
|
||||||
|
// We need a "real" ref object for the gantt bars to instantly update the tasks when they are dragged on the chart.
|
||||||
|
// A computed won't work directly.
|
||||||
|
// function mapGanttBars() {
|
||||||
|
() => {
|
||||||
|
ganttBars.value = []
|
||||||
|
tasks.value.forEach(t => ganttBars.value.push(transformTaskToGanttBar(t)))
|
||||||
|
},
|
||||||
|
{deep: true}
|
||||||
|
)
|
||||||
|
|
||||||
const defaultStartDate = format(new Date(), DATE_FORMAT)
|
const defaultStartDate = format(new Date(), DATE_FORMAT)
|
||||||
const defaultEndDate = format(new Date((new Date()).setDate((new Date()).getDate() + 7)), DATE_FORMAT)
|
const defaultEndDate = format(new Date((new Date()).setDate((new Date()).getDate() + 7)), DATE_FORMAT)
|
||||||
|
|
||||||
|
@ -143,12 +124,12 @@ function transformTaskToGanttBar(t: TaskModel) {
|
||||||
startDate: t.startDate ? format(t.startDate, DATE_FORMAT) : defaultStartDate,
|
startDate: t.startDate ? format(t.startDate, DATE_FORMAT) : defaultStartDate,
|
||||||
endDate: t.endDate ? format(t.endDate, DATE_FORMAT) : defaultEndDate,
|
endDate: t.endDate ? format(t.endDate, DATE_FORMAT) : defaultEndDate,
|
||||||
ganttBarConfig: {
|
ganttBarConfig: {
|
||||||
id: t.id,
|
id: String(t.id),
|
||||||
label: t.title,
|
label: t.title,
|
||||||
hasHandles: true,
|
hasHandles: true,
|
||||||
style: {
|
style: {
|
||||||
color: t.startDate ? (colorIsDark(t.getHexColor(t.hexColor)) ? black : 'white') : black,
|
color: t.startDate ? (colorIsDark(getHexColor(t.hexColor)) ? black : 'white') : black,
|
||||||
backgroundColor: t.startDate ? t.getHexColor(t.hexColor) : 'var(--grey-100)',
|
backgroundColor: t.startDate ? getHexColor(t.hexColor) : 'var(--grey-100)',
|
||||||
border: t.startDate ? '' : '2px dashed var(--grey-300)',
|
border: t.startDate ? '' : '2px dashed var(--grey-300)',
|
||||||
'text-decoration': t.done ? 'line-through' : null,
|
'text-decoration': t.done ? 'line-through' : null,
|
||||||
},
|
},
|
||||||
|
@ -156,13 +137,7 @@ function transformTaskToGanttBar(t: TaskModel) {
|
||||||
} as GanttBarObject]
|
} as GanttBarObject]
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need a "real" ref object for the gantt bars to instantly update the tasks when they are dragged on the chart.
|
|
||||||
// A computed won't work directly.
|
|
||||||
function mapGanttBars() {
|
|
||||||
ganttBars.value = []
|
|
||||||
|
|
||||||
tasks.value.forEach(t => ganttBars.value.push(transformTaskToGanttBar(t)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: unite with other filter params types
|
// FIXME: unite with other filter params types
|
||||||
interface GetAllTasksParams {
|
interface GetAllTasksParams {
|
||||||
|
@ -208,8 +183,6 @@ async function loadTasks({
|
||||||
const loadedTasks = await getAllTasks(params)
|
const loadedTasks = await getAllTasks(params)
|
||||||
|
|
||||||
loadedTasks.forEach(t => tasks.value.set(t.id, t))
|
loadedTasks.forEach(t => tasks.value.set(t.id, t))
|
||||||
|
|
||||||
mapGanttBars()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => loadTasks({
|
watchEffect(() => loadTasks({
|
||||||
|
@ -226,7 +199,6 @@ async function createTask(title: TaskModel['title']) {
|
||||||
endDate: defaultEndDate,
|
endDate: defaultEndDate,
|
||||||
}))
|
}))
|
||||||
tasks.value.set(newTask.id, newTask)
|
tasks.value.set(newTask.id, newTask)
|
||||||
mapGanttBars()
|
|
||||||
|
|
||||||
return newTask
|
return newTask
|
||||||
}
|
}
|
||||||
|
@ -268,14 +240,25 @@ function dayIsToday(label: string): boolean {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.gantt-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
// Not scoped because we need to style the elements inside the gantt chart component
|
// Not scoped because we need to style the elements inside the gantt chart component
|
||||||
|
.g-gantt-chart {
|
||||||
|
width: 2000px;
|
||||||
|
}
|
||||||
|
|
||||||
.g-gantt-row-label {
|
.g-gantt-row-label {
|
||||||
display: none !important;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-upper-timeunit, .g-timeunit {
|
.g-upper-timeunit, .g-timeunit {
|
||||||
background: var(--white) !important;
|
background: var(--white);
|
||||||
font-family: $vikunja-font;
|
font-family: $vikunja-font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +270,7 @@ function dayIsToday(label: string): boolean {
|
||||||
|
|
||||||
.g-timeunit .timeunit-wrapper {
|
.g-timeunit .timeunit-wrapper {
|
||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
font-size: 1rem !important;
|
font-size: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -306,13 +289,13 @@ function dayIsToday(label: string): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-timeaxis {
|
.g-timeaxis {
|
||||||
height: auto !important;
|
height: auto;
|
||||||
box-shadow: none !important;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-gantt-row > .g-gantt-row-bars-container {
|
.g-gantt-row > .g-gantt-row-bars-container {
|
||||||
border-bottom: none !important;
|
border-bottom: none;
|
||||||
border-top: none !important;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.g-gantt-row:nth-child(odd) {
|
.g-gantt-row:nth-child(odd) {
|
||||||
|
@ -326,21 +309,11 @@ function dayIsToday(label: string): boolean {
|
||||||
|
|
||||||
&-handle-left,
|
&-handle-left,
|
||||||
&-handle-right {
|
&-handle-right {
|
||||||
width: 6px !important;
|
width: 6px;
|
||||||
height: 75% !important;
|
height: 75%;
|
||||||
opacity: .75 !important;
|
opacity: .75;
|
||||||
border-radius: $radius !important;
|
border-radius: $radius;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.gantt-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#g-gantt-chart {
|
|
||||||
width: 2000px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -4,7 +4,7 @@
|
||||||
* @param color
|
* @param color
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function colorFromHex(color) {
|
export function colorFromHex(color: string) {
|
||||||
if (color.substring(0, 1) === '#') {
|
if (color.substring(0, 1) === '#') {
|
||||||
color = color.substring(1, 7)
|
color = color.substring(1, 7)
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ import FontAwesomeIcon from '@/components/misc/Icon'
|
||||||
import Button from '@/components/input/button.vue'
|
import Button from '@/components/input/button.vue'
|
||||||
import Modal from '@/components/misc/modal.vue'
|
import Modal from '@/components/misc/modal.vue'
|
||||||
import Card from '@/components/misc/card.vue'
|
import Card from '@/components/misc/card.vue'
|
||||||
import ganttastic from '@infectoone/vue-ganttastic'
|
|
||||||
|
|
||||||
app.component('icon', FontAwesomeIcon)
|
app.component('icon', FontAwesomeIcon)
|
||||||
app.component('x-button', Button)
|
app.component('x-button', Button)
|
||||||
|
@ -103,8 +102,6 @@ if (window.SENTRY_ENABLED) {
|
||||||
import('./sentry').then(sentry => sentry.default(app, router))
|
import('./sentry').then(sentry => sentry.default(app, router))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app.use(ganttastic)
|
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
|
|
BIN
vendor/infectoone-vue-ganttastic-2.1.1.tgz
vendored
Normal file
BIN
vendor/infectoone-vue-ganttastic-2.1.1.tgz
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue