Skip to content

Commit 4ad70e0

Browse files
committed
Merge branch 'release/2.2.0'
2 parents fe82c32 + 3ae7be5 commit 4ad70e0

27 files changed

+791
-105
lines changed

CHANGELOG.md

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ All notable changes to this project will be documented in this file.
88

99
[Full Changelog](https://github.com/Xeyos88/HyVueGantt/compare/v2.0.0...v2.1.0)
1010

11-
12-
1311
**✨ New Features:**
1412

1513
- Drag and drop rows

docs/.vitepress/theme/components/ConnectionsGanttDemo.vue

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ onMounted(() => {
9595
:push-on-connect="true"
9696
color-scheme="dark"
9797
:holidayHighlight="'US'"
98+
:markerConnection="'none'"
9899
>
99100
<g-gantt-row
100101
v-for="row in rows"

docs/.vitepress/theme/components/OtherGanttDemo.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ onMounted(() => {
274274
label-column-title="Rows"
275275
:dayOptionLabel="['day','name','doy']"
276276
:milestones="milestones"
277-
277+
:markerConnection="'bidirectional'"
278278
>
279279
<g-gantt-row
280280
v-for="row in rows"

docs/api/props.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ interface VisualProps {
4545
highlightedDaysInMonth?: number[]; // Array of days to highlight (1-31)
4646
highlightedMonths?: number[]; // Array of months to highlight (0-11, 0 is January)
4747
highlightedWeek?: number[]; // Array of weeks to highlight (1-53)
48-
locale?: string; // Locale for dayjs
49-
48+
locale?: string; // Locale for dayjs
49+
markConnection?: MarkConnection // Type of marker in connections
5050

5151
}
5252
```

docs/api/types.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type DayOptionLabel = "day" | "doy" | "name" | "number"
1313
type ConnectionType = 'bezier' | 'straight' | 'squared';
1414
type ConnectionPattern = 'solid' | 'dot' | 'dash' | 'dashdot';
1515
type ConnectionSpeed = 'slow' | 'normal' | 'fast';
16+
type MarkerConnection = 'none' | 'forward' | 'bidirectional'
1617
```
1718
1819
## Bar Configuration

docs/components/g-gantt-chart.md

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Here's a minimal example of using the GGanttChart component:
7070
| dayOptionLabel| `DayOptionLabel[]` | `['day']` | Customization for time unit day |
7171
| locale| `string` | `'en'` | Locale for dayjs |
7272
| enableRowDragAndDrop| `boolean` | `false` | Enable drag and drop of rows |
73+
| markerConnection| `MarkerConnection` | `forward` | Choose the type of marker in connection |
7374

7475

7576
### Events

docs/guide/chart-configuration.md

-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ The GGanttChart component accepts several key configuration properties that defi
1414
:bar-start="'start'"
1515
:bar-end="'end'"
1616
:row-height="40"
17-
:width="'100%'"
1817
:color-scheme="'default'"
1918
:grid="true"
2019
:push-on-overlap="true"
@@ -29,7 +28,6 @@ The GGanttChart component accepts several key configuration properties that defi
2928
- `precision`: Sets the time unit ('hour', 'day', 'week', 'month')
3029
- `bar-start` and `bar-end`: Specify data properties for dates
3130
- `row-height`: Controls row height in pixels
32-
- `width`: Chart width (percentage or pixels)
3331

3432
### Advanced Configuration
3533

docs/live/events.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Live demo of events issued by the Gantt component:
88
<EventsGanttDemo />
99
</ClientOnly>
1010

11-
## Codice
11+
## Code
1212

1313
```vue
1414
<script setup lang="ts">

eslint.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@ export default [
2323
...vueTsEslintConfig(),
2424

2525
skipFormatting,
26-
includeIgnoreFile(gitignorePath)
26+
includeIgnoreFile(gitignorePath),
27+
{
28+
ignores: ["**/tests/*", "**/docs/*"]
29+
}
2730
]

package-lock.json

+17-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "hy-vue-gantt",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Evolution of vue-ganttastic package",
55
"author": "Eugenio Topa (@Xeyos88)",
66
"scripts": {
7-
"serve": "vite",
7+
"serve": "vite --host",
88
"build": "npm run build:types && npm run build:lib",
99
"build:lib": "vite build",
1010
"build:types": "vue-tsc --declaration --emitDeclarationOnly --outDir lib_types -p tsconfig.build.json",
@@ -92,6 +92,7 @@
9292
"url": "https://github.com/Xeyos88/HyVueGantt"
9393
},
9494
"dependencies": {
95-
"@tsconfig/node22": "^22.0.0"
95+
"@tsconfig/node22": "^22.0.0",
96+
"uuid": "^11.0.5"
9697
}
9798
}

src/components/GGanttBar.vue

+37-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import useBarDragManagement from "../composables/useBarDragManagement"
55
import useTimePositionMapping from "../composables/useTimePositionMapping"
66
import useBarDragLimit from "../composables/useBarDragLimit"
77
import { useBarKeyboardControl } from "../composables/useBarKeyboardControl"
8+
import { useTouchEvents } from "../composables/useTouchEvents"
89
import type { GanttBarObject } from "../types"
910
import provideEmitBarEvent from "../provider/provideEmitBarEvent"
1011
import provideConfig from "../provider/provideConfig"
@@ -46,11 +47,10 @@ const prepareForDrag = () => {
4647
4748
window.addEventListener("mousemove", firstMousemoveCallback, {
4849
once: true
49-
}) // on first mousemove event
50+
})
5051
window.addEventListener(
5152
"mouseup",
5253
() => {
53-
// in case user does not move the mouse after mousedown at all
5454
window.removeEventListener("mousemove", firstMousemoveCallback)
5555
isDragging.value = false
5656
},
@@ -78,6 +78,37 @@ const { barStart, barEnd, width, chartStart, chartEnd, chartSize } = config
7878
const xStart = ref(0)
7979
const xEnd = ref(0)
8080
81+
const { handleTouchStart, handleTouchMove, handleTouchEnd, handleTouchCancel } = useTouchEvents(
82+
(_draggedBar, e) => {
83+
firstMousemoveCallback(e)
84+
isDragging.value = true
85+
}
86+
)
87+
const onTouchEvent = (e: TouchEvent) => {
88+
if (bar.value.ganttBarConfig.immobile) return
89+
90+
let mouseEvent: MouseEvent | undefined
91+
92+
switch (e.type) {
93+
case "touchstart":
94+
mouseEvent = handleTouchStart(e, bar.value)!
95+
break
96+
case "touchmove":
97+
mouseEvent = handleTouchMove(e)!
98+
break
99+
case "touchend":
100+
mouseEvent = handleTouchEnd(e)!
101+
break
102+
case "touchcancel":
103+
mouseEvent = handleTouchCancel(e)!
104+
break
105+
}
106+
107+
if (mouseEvent) {
108+
onMouseEvent(mouseEvent)
109+
}
110+
}
111+
81112
onMounted(() => {
82113
xStart.value = mapTimeToPosition(bar.value[barStart.value])
83114
xEnd.value = mapTimeToPosition(bar.value[barEnd.value])
@@ -133,6 +164,10 @@ const getGroupBarPath = (width: number, height: number) => {
133164
@mouseenter="onMouseEvent"
134165
@mouseleave="onMouseEvent"
135166
@contextmenu="onMouseEvent"
167+
@touchstart="onTouchEvent"
168+
@touchmove="onTouchEvent"
169+
@touchend="onTouchEvent"
170+
@touchcancel="onTouchEvent"
136171
@keydown="onBarKeyDown"
137172
role="listitem"
138173
:aria-label="`Activity ${barConfig.label}`"

src/components/GGanttChart.vue

+43-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import type {
5959
import type { CSSProperties } from "vue"
6060
import useTimeaxisUnits from "../composables/useTimeaxisUnits"
6161
import { ganttWidth } from "../composables/useSimpleStore"
62+
import { v4 as uuidv4 } from "uuid"
6263
6364
// Props & Emits Definition
6465
const props = withDefaults(defineProps<GGanttChartProps>(), {
@@ -104,10 +105,11 @@ const props = withDefaults(defineProps<GGanttChartProps>(), {
104105
highlightedMonths: () => [],
105106
highlightedWeek: () => [],
106107
locale: "en",
107-
enableRowDragAndDrop: false
108+
enableRowDragAndDrop: false,
109+
markerConnection: "forward"
108110
})
109111
110-
const id = ref(crypto.randomUUID())
112+
const id = ref(uuidv4())
111113
const slots = useSlots()
112114
113115
const rowManager = useRows(
@@ -312,6 +314,40 @@ const handleTimeaxisMouseUp = () => {
312314
isDraggingTimeaxis.value = false
313315
}
314316
317+
const handleTimeaxisTouch = {
318+
startX: 0,
319+
isDragging: false
320+
}
321+
322+
const handleTimeaxisTouchStart = (e: TouchEvent) => {
323+
const touch = e.touches[0]
324+
if (!touch) return
325+
326+
handleTimeaxisTouch.isDragging = true
327+
handleTimeaxisTouch.startX = touch.clientX
328+
e.preventDefault()
329+
}
330+
331+
const handleTimeaxisTouchMove = (e: TouchEvent) => {
332+
if (!handleTimeaxisTouch.isDragging || !ganttWrapper.value) return
333+
334+
const touch = e.touches[0]
335+
if (!touch) return
336+
337+
const deltaX = touch.clientX - handleTimeaxisTouch.startX
338+
handleTimeaxisTouch.startX = touch.clientX
339+
340+
ganttWrapper.value.scrollLeft -= deltaX
341+
const maxScroll = ganttWrapper.value.scrollWidth - ganttWrapper.value.clientWidth
342+
scrollPosition.value = (ganttWrapper.value.scrollLeft / maxScroll) * 100
343+
344+
e.preventDefault()
345+
}
346+
347+
const handleTimeaxisTouchEnd = () => {
348+
handleTimeaxisTouch.isDragging = false
349+
}
350+
315351
// Bar Event Handling
316352
const emitBarEvent = (
317353
e: MouseEvent,
@@ -489,6 +525,10 @@ provide(GANTT_ID_KEY, id.value)
489525
v-if="!hideTimeaxis"
490526
ref="timeaxisComponent"
491527
@drag-start="handleTimeaxisMouseDown"
528+
@touchstart="handleTimeaxisTouchStart"
529+
@touchmove="handleTimeaxisTouchMove"
530+
@touchend="handleTimeaxisTouchEnd"
531+
@touchcancel="handleTimeaxisTouchEnd"
492532
:timeaxisUnits="timeaxisUnits"
493533
:internalPrecision="internalPrecision"
494534
>
@@ -541,6 +581,7 @@ provide(GANTT_ID_KEY, id.value)
541581
<g-gantt-connector
542582
v-if="barPositions.get(conn.sourceId) && barPositions.get(conn.targetId)"
543583
v-bind="getConnectorProps(conn)!"
584+
:marker="markerConnection"
544585
/>
545586
</template>
546587
</template>

src/components/GGanttConnector.vue

+18-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { BarPosition, ConnectionType } from "../types"
2+
import type { BarPosition, ConnectionType, MarkerConnection } from "../types"
33
import { computed, ref } from "vue"
44
55
interface Props {
@@ -11,6 +11,7 @@ interface Props {
1111
pattern?: "solid" | "dash" | "dot" | "dashdot"
1212
animated?: boolean
1313
animationSpeed?: "slow" | "normal" | "fast"
14+
marker: MarkerConnection
1415
}
1516
1617
const props = withDefaults(defineProps<Props>(), {
@@ -29,41 +30,46 @@ const animationClass = computed(() => {
2930
return `connector-animated-${props.pattern}-${props.animationSpeed}`
3031
})
3132
33+
const markerId = computed(() => `marker-start-${props.sourceBar.id}-${props.targetBar.id}`)
34+
const hasMarkerEnd = computed(() => props.marker === "bidirectional" || props.marker === "forward")
35+
const hasMarkerStart = computed(() => props.marker === "bidirectional")
36+
const markerDeltaEnd = computed(() => (hasMarkerEnd.value ? 4 : 0))
37+
const markerDeltaStart = computed(() => (hasMarkerStart.value ? 4 : 0))
38+
3239
const pathData = computed(() => {
3340
const sourceX = props.sourceBar.x + props.sourceBar.width
3441
const sourceY = props.sourceBar.y + props.sourceBar.height / 2
3542
const targetX = props.targetBar.x
3643
const targetY = props.targetBar.y + props.targetBar.height / 2
3744
3845
const OFFSET = 20
39-
const isGoingBack = targetX < sourceX
40-
46+
const isGoingBack = targetX <= sourceX
4147
switch (props.type) {
4248
case "straight":
43-
return `M ${sourceX},${sourceY} L ${targetX - 4},${targetY}`
49+
return `M ${sourceX},${sourceY} L ${targetX - markerDeltaEnd.value},${targetY}`
4450
4551
case "squared":
4652
if (isGoingBack) {
47-
return `M ${sourceX},${sourceY}
53+
return `M ${sourceX + markerDeltaStart.value},${sourceY}
4854
h ${OFFSET}
4955
v ${(targetY - sourceY) / 2}
5056
h -${Math.abs(targetX - sourceX) + OFFSET * 2}
5157
v ${(targetY - sourceY) / 2}
52-
h ${OFFSET - 4}`
58+
h ${OFFSET - markerDeltaEnd.value * 2}`
5359
}
5460
55-
return `M ${sourceX},${sourceY}
61+
return `M ${sourceX + markerDeltaStart.value},${sourceY}
5662
h ${OFFSET}
5763
v ${targetY - sourceY}
58-
h ${targetX - sourceX - OFFSET - 4}`
64+
h ${targetX - sourceX - OFFSET - markerDeltaEnd.value * 2}`
5965
6066
case "bezier":
6167
default:
6268
const controlPointX = (sourceX + targetX) / 2
63-
return `M ${sourceX},${sourceY}
69+
return `M ${sourceX + markerDeltaStart.value},${sourceY}
6470
C ${controlPointX},${sourceY}
6571
${controlPointX},${targetY}
66-
${targetX - 4},${targetY}`
72+
${targetX - markerDeltaEnd.value},${targetY}`
6773
}
6874
})
6975
@@ -81,8 +87,6 @@ const nonAnimatedDashArray = computed(() => {
8187
return ""
8288
}
8389
})
84-
85-
const markerId = computed(() => `marker-start-${props.sourceBar.id}-${props.targetBar.id}`)
8690
</script>
8791

8892
<template>
@@ -152,8 +156,8 @@ const markerId = computed(() => `marker-start-${props.sourceBar.id}-${props.targ
152156
:stroke-dasharray="nonAnimatedDashArray"
153157
:class="['connector-path', animationClass]"
154158
:style="{
155-
markerStart: 'none',
156-
markerEnd: `url(#${markerId})`
159+
markerStart: hasMarkerStart ? `url(#${markerId})` : 'none',
160+
markerEnd: hasMarkerEnd ? `url(#${markerId})` : 'none'
157161
}"
158162
/>
159163
</svg>

0 commit comments

Comments
 (0)