@stonecrop/atable 0.4.22 → 0.4.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,46 +1,82 @@
1
1
  <template>
2
2
  <td class="aganttcell" :colspan="colspan">
3
- <div ref="container" class="gantt-handler">
3
+ <div ref="container" class="gantt-container">
4
4
  <!-- Draggable gantt bar -->
5
5
  <div
6
6
  ref="bar"
7
+ :data-rowindex="rowIndex"
8
+ :data-colindex="colIndex"
7
9
  class="gantt-bar"
8
- :class="{ 'is-dragging': isBarDragging || isLeftDragging || isRightDragging }"
9
- :style="barStyle">
10
- <!-- Left resizer handle -->
11
- <div ref="leftHandle" class="gantt-handle left-handle" :class="{ 'is-dragging': isLeftDragging }">
10
+ :class="{ 'is-dragging': isAnyDragging }"
11
+ :style="barStyle"
12
+ @mouseenter="showConnectionHandles"
13
+ @mouseleave="hideConnectionHandles">
14
+ <!-- Connection handles for linking bars -->
15
+ <div
16
+ ref="leftConnectionHandle"
17
+ class="connection-handle left-connection-handle"
18
+ :class="{ visible: isLeftConnectionVisible, 'is-dragging': isLeftConnectionDragging }"
19
+ @mousedown.stop="startConnectionDrag('left', $event)">
20
+ <div class="connection-dot"></div>
21
+ </div>
22
+
23
+ <div
24
+ ref="rightConnectionHandle"
25
+ class="connection-handle right-connection-handle"
26
+ :class="{ visible: isRightConnectionVisible, 'is-dragging': isRightConnectionDragging }"
27
+ @mousedown.stop="startConnectionDrag('right', $event)">
28
+ <div class="connection-dot"></div>
29
+ </div>
30
+
31
+ <!-- Resize handles for changing bar length -->
32
+ <div ref="leftResizeHandle" class="resize-handle left-resize-handle" :class="{ 'is-dragging': isLeftResizing }">
12
33
  <div class="handle-grip"></div>
13
34
  <div class="vertical-indicator left-indicator"></div>
14
35
  </div>
15
36
 
16
37
  <label v-if="label" class="gantt-label">{{ label }}</label>
17
38
 
18
- <!-- Right resizer handle -->
19
- <div ref="rightHandle" class="gantt-handle right-handle" :class="{ 'is-dragging': isRightDragging }">
39
+ <div
40
+ ref="rightResizeHandle"
41
+ class="resize-handle right-resize-handle"
42
+ :class="{ 'is-dragging': isRightResizing }">
20
43
  <div class="handle-grip"></div>
21
44
  <div class="vertical-indicator right-indicator"></div>
22
45
  </div>
23
46
  </div>
24
47
  </div>
48
+
49
+ <!-- Connection drag preview line -->
50
+ <svg v-if="showDragPreview" :style="connectionDragStyle">
51
+ <line
52
+ :x1="dragPreview.startX"
53
+ :y1="dragPreview.startY"
54
+ :x2="dragPreview.endX"
55
+ :y2="dragPreview.endY"
56
+ stroke="#2196f3"
57
+ stroke-width="2"
58
+ stroke-dasharray="5,5" />
59
+ </svg>
25
60
  </td>
26
61
  </template>
27
62
 
28
63
  <script setup lang="ts">
29
64
  import { useDraggable, useElementBounding } from '@vueuse/core'
30
- import { ref, computed, onMounted, useTemplateRef } from 'vue'
65
+ import { ref, computed, onMounted, onUnmounted, useTemplateRef, type StyleValue } from 'vue'
31
66
 
32
67
  import { createTableStore } from '../stores/table'
68
+ import type { ConnectionPath } from '../types'
33
69
 
34
70
  const {
35
71
  store,
36
72
  columnsCount,
37
73
  rowIndex,
38
74
  colIndex,
39
- start,
75
+ start = 0,
40
76
  end,
41
77
  colspan = 1,
42
78
  label,
43
- color,
79
+ color = '#cccccc',
44
80
  } = defineProps<{
45
81
  store: ReturnType<typeof createTableStore>
46
82
  columnsCount: number
@@ -53,182 +89,366 @@ const {
53
89
  color?: string
54
90
  }>()
55
91
 
56
- const baseColor = ref()
92
+ const emit = defineEmits<{
93
+ 'connection:create': [connection: ConnectionPath]
94
+ }>()
57
95
 
58
- onMounted(() => {
59
- if (!color || color == '' || color.length < 6) {
60
- baseColor.value = '#cccccc'
61
- } else {
62
- baseColor.value = color
63
- }
64
- })
96
+ // Core refs and state
97
+ const barColor = ref(color.length >= 6 ? color : '#cccccc')
98
+ const barId = `gantt-bar-row-${rowIndex}-col-${colIndex}`
65
99
 
100
+ // Template refs
66
101
  const containerRef = useTemplateRef('container')
67
102
  const barRef = useTemplateRef('bar')
68
- const leftHandleRef = useTemplateRef('leftHandle')
69
- const rightHandleRef = useTemplateRef('rightHandle')
103
+ const leftResizeHandleRef = useTemplateRef('leftResizeHandle')
104
+ const rightResizeHandleRef = useTemplateRef('rightResizeHandle')
105
+ const leftConnectionHandleRef = useTemplateRef('leftConnectionHandle')
106
+ const rightConnectionHandleRef = useTemplateRef('rightConnectionHandle')
70
107
 
108
+ // Position tracking
71
109
  const { width: totalBarWidth } = useElementBounding(containerRef)
72
110
  const { left: barLeft, right: barRight } = useElementBounding(barRef)
73
- const currentStart = ref(start || 0)
111
+
112
+ // Bar positioning
113
+ const currentStart = ref(start)
74
114
  const currentEnd = ref(end || currentStart.value + colspan)
75
- const dragStartData = ref({ startX: 0, startPos: 0 })
115
+
116
+ // Drag states
117
+ const isLeftConnectionVisible = ref(false)
118
+ const isRightConnectionVisible = ref(false)
119
+ const isLeftConnectionDragging = ref(false)
120
+ const isRightConnectionDragging = ref(false)
121
+ const showDragPreview = ref(false)
122
+ const dragPreview = ref({ startX: 0, startY: 0, endX: 0, endY: 0 })
123
+
124
+ // Computed properties
125
+ const isAnyDragging = computed(() => isBarDragging.value || isLeftResizing.value || isRightResizing.value)
76
126
 
77
127
  const pixelsPerColumn = computed(() => (colspan > 0 ? totalBarWidth.value / colspan : 0))
78
128
 
79
- const barStyle = computed(() => {
129
+ const barStyle = computed((): StyleValue => {
80
130
  const startPercent = (currentStart.value / colspan) * 100
81
131
  const endPercent = (currentEnd.value / colspan) * 100
82
-
83
132
  return {
84
133
  left: `${startPercent}%`,
85
134
  width: `${endPercent - startPercent}%`,
86
- backgroundColor: baseColor.value,
135
+ backgroundColor: barColor.value,
87
136
  }
88
137
  })
89
138
 
90
- const { isDragging: isLeftDragging } = useDraggable(leftHandleRef, {
139
+ const connectionDragStyle = computed(
140
+ (): StyleValue => ({
141
+ position: 'fixed',
142
+ top: 0,
143
+ left: 0,
144
+ width: '100vw',
145
+ height: '100vh',
146
+ pointerEvents: 'none',
147
+ zIndex: 1000,
148
+ })
149
+ )
150
+
151
+ // Drag setup data
152
+ const dragStartData = ref({ startX: 0, startPos: 0 })
153
+
154
+ // Left resize handle dragging
155
+ const { isDragging: isLeftResizing } = useDraggable(leftResizeHandleRef, {
91
156
  axis: 'x',
92
- onStart: () => {
93
- if (barRef.value) barRef.value.style.transition = 'none'
94
- dragStartData.value = {
95
- startX: barLeft.value,
96
- startPos: currentStart.value,
97
- }
98
- },
99
- onMove: ({ x }) => {
100
- if (isLeftDragging.value && barRef.value) {
101
- const deltaX = x - dragStartData.value.startX
102
- const deltaColumns = deltaX / pixelsPerColumn.value
103
- const newStart = Math.max(0, Math.min(currentEnd.value - 1, dragStartData.value.startPos + deltaColumns))
104
- barRef.value.style.left = `${(newStart / colspan) * 100}%`
105
- barRef.value.style.width = `${((currentEnd.value - newStart) / colspan) * 100}%`
106
- }
107
- },
108
- onEnd: ({ x }) => {
109
- if (barRef.value) {
110
- const deltaX = x - dragStartData.value.startX
111
- const deltaColumns = Math.round(deltaX / pixelsPerColumn.value)
112
- const oldStart = currentStart.value
113
- const newStart = Math.max(0, Math.min(currentEnd.value - 1, dragStartData.value.startPos + deltaColumns))
114
- currentStart.value = newStart
115
-
116
- store.updateGanttBar({
117
- rowIndex,
118
- colIndex,
119
- type: 'resize',
120
- edge: 'start',
121
- oldStart,
122
- newStart,
123
- end: currentEnd.value,
124
- delta: deltaColumns,
125
- oldColspan: currentEnd.value - oldStart,
126
- newColspan: currentEnd.value - newStart,
127
- })
128
- }
129
- },
157
+ onStart: () => setupDragStart(barLeft.value, currentStart.value),
158
+ onMove: ({ x }) => handleLeftResize(x),
159
+ onEnd: ({ x }) => finishLeftResize(x),
130
160
  })
131
161
 
132
- const { isDragging: isRightDragging } = useDraggable(rightHandleRef, {
162
+ // Right resize handle dragging
163
+ const { isDragging: isRightResizing } = useDraggable(rightResizeHandleRef, {
133
164
  axis: 'x',
134
- onStart: () => {
135
- if (barRef.value) barRef.value.style.transition = 'none'
136
- dragStartData.value = {
137
- startX: barRight.value,
138
- startPos: currentEnd.value,
139
- }
140
- },
141
- onMove: ({ x }) => {
142
- if (isRightDragging.value && barRef.value) {
143
- const deltaX = x - dragStartData.value.startX
144
- const deltaColumns = deltaX / pixelsPerColumn.value
145
- const newEnd = Math.max(
146
- currentStart.value + 1,
147
- Math.min(columnsCount, dragStartData.value.startPos + deltaColumns)
148
- )
149
- barRef.value.style.width = `${((newEnd - currentStart.value) / colspan) * 100}%`
150
- }
151
- },
152
- onEnd: ({ x }) => {
153
- if (barRef.value) {
154
- const deltaX = x - dragStartData.value.startX
155
- const deltaColumns = Math.round(deltaX / pixelsPerColumn.value)
156
- const oldEnd = currentEnd.value
157
- const newEnd = Math.max(
158
- currentStart.value + 1,
159
- Math.min(columnsCount, dragStartData.value.startPos + deltaColumns)
160
- )
161
- currentEnd.value = newEnd
162
-
163
- store.updateGanttBar({
164
- rowIndex,
165
- colIndex,
166
- type: 'resize',
167
- edge: 'end',
168
- oldEnd,
169
- newEnd,
170
- start: currentStart.value,
171
- delta: deltaColumns,
172
- oldColspan: oldEnd - currentStart.value,
173
- newColspan: newEnd - currentStart.value,
174
- })
175
- }
176
- },
165
+ onStart: () => setupDragStart(barRight.value, currentEnd.value),
166
+ onMove: ({ x }) => handleRightResize(x),
167
+ onEnd: ({ x }) => finishRightResize(x),
177
168
  })
178
169
 
170
+ // Bar movement dragging
179
171
  const { isDragging: isBarDragging } = useDraggable(barRef, {
180
- exact: true, // to avoid triggering when the left and right handles are being used
172
+ exact: true,
181
173
  axis: 'x',
182
- onStart: () => {
183
- if (barRef.value) barRef.value.style.transition = 'none'
184
- dragStartData.value = {
185
- startX: barLeft.value,
186
- startPos: currentStart.value,
187
- }
188
- },
189
- onMove: ({ x }) => {
190
- if (isBarDragging.value && barRef.value) {
191
- const deltaX = x - dragStartData.value.startX
192
- const deltaColumns = deltaX / pixelsPerColumn.value
193
- const barWidth = currentEnd.value - currentStart.value
194
- const newStart = Math.max(0, Math.min(dragStartData.value.startPos + deltaColumns, columnsCount - barWidth))
195
- barRef.value.style.left = `${(newStart / colspan) * 100}%`
196
- }
197
- },
198
- onEnd: ({ x }) => {
199
- if (barRef.value) {
200
- const deltaX = x - dragStartData.value.startX
201
- const deltaColumns = Math.round(deltaX / pixelsPerColumn.value)
202
- const barWidth = currentEnd.value - currentStart.value
203
-
204
- const oldStart = currentStart.value
205
- const oldEnd = currentEnd.value
206
- let newStart = dragStartData.value.startPos + deltaColumns
207
- let newEnd = newStart + barWidth
208
- if (newStart < 0) {
209
- newStart = 0
210
- newEnd = barWidth
211
- } else if (newEnd > columnsCount) {
212
- newEnd = columnsCount
213
- newStart = newEnd - barWidth
214
- }
174
+ onStart: () => setupDragStart(barLeft.value, currentStart.value),
175
+ onMove: ({ x }) => handleBarMove(x),
176
+ onEnd: ({ x }) => finishBarMove(x),
177
+ })
178
+
179
+ // Lifecycle
180
+ onMounted(() => {
181
+ registerGanttComponents()
182
+ })
183
+
184
+ onUnmounted(() => {
185
+ unregisterGanttComponents()
186
+ })
187
+
188
+ // Helper functions
189
+ function setupDragStart(startX: number, startPos: number) {
190
+ if (barRef.value) barRef.value.style.transition = 'none'
191
+ dragStartData.value = { startX, startPos }
192
+ }
193
+
194
+ function handleLeftResize(x: number) {
195
+ if (!isLeftResizing.value || !barRef.value) return
196
+
197
+ const deltaX = x - dragStartData.value.startX
198
+ const deltaColumns = deltaX / pixelsPerColumn.value
199
+ const newStart = Math.max(0, Math.min(currentEnd.value - 1, dragStartData.value.startPos + deltaColumns))
200
+
201
+ barRef.value.style.left = `${(newStart / colspan) * 100}%`
202
+ barRef.value.style.width = `${((currentEnd.value - newStart) / colspan) * 100}%`
203
+ }
204
+
205
+ function finishLeftResize(x: number) {
206
+ if (!barRef.value) return
207
+
208
+ const deltaX = x - dragStartData.value.startX
209
+ const deltaColumns = Math.round(deltaX / pixelsPerColumn.value)
210
+ const oldStart = currentStart.value
211
+ const newStart = Math.max(0, Math.min(currentEnd.value - 1, dragStartData.value.startPos + deltaColumns))
212
+
213
+ currentStart.value = newStart
214
+ store.updateGanttBar({
215
+ rowIndex,
216
+ colIndex,
217
+ type: 'resize',
218
+ edge: 'start',
219
+ oldStart,
220
+ newStart,
221
+ end: currentEnd.value,
222
+ delta: deltaColumns,
223
+ oldColspan: currentEnd.value - oldStart,
224
+ newColspan: currentEnd.value - newStart,
225
+ })
226
+ }
227
+
228
+ function handleRightResize(x: number) {
229
+ if (!isRightResizing.value || !barRef.value) return
230
+
231
+ const deltaX = x - dragStartData.value.startX
232
+ const deltaColumns = deltaX / pixelsPerColumn.value
233
+ const newEnd = Math.max(currentStart.value + 1, Math.min(columnsCount, dragStartData.value.startPos + deltaColumns))
234
+
235
+ barRef.value.style.width = `${((newEnd - currentStart.value) / colspan) * 100}%`
236
+ }
237
+
238
+ function finishRightResize(x: number) {
239
+ if (!barRef.value) return
240
+
241
+ const deltaX = x - dragStartData.value.startX
242
+ const deltaColumns = Math.round(deltaX / pixelsPerColumn.value)
243
+ const oldEnd = currentEnd.value
244
+ const newEnd = Math.max(currentStart.value + 1, Math.min(columnsCount, dragStartData.value.startPos + deltaColumns))
245
+
246
+ currentEnd.value = newEnd
247
+ store.updateGanttBar({
248
+ rowIndex,
249
+ colIndex,
250
+ type: 'resize',
251
+ edge: 'end',
252
+ oldEnd,
253
+ newEnd,
254
+ start: currentStart.value,
255
+ delta: deltaColumns,
256
+ oldColspan: oldEnd - currentStart.value,
257
+ newColspan: newEnd - currentStart.value,
258
+ })
259
+ }
260
+
261
+ function handleBarMove(x: number) {
262
+ if (!isBarDragging.value || !barRef.value) return
263
+
264
+ const deltaX = x - dragStartData.value.startX
265
+ const deltaColumns = deltaX / pixelsPerColumn.value
266
+ const barWidth = currentEnd.value - currentStart.value
267
+ const newStart = Math.max(0, Math.min(dragStartData.value.startPos + deltaColumns, columnsCount - barWidth))
268
+
269
+ barRef.value.style.left = `${(newStart / colspan) * 100}%`
270
+ }
271
+
272
+ function finishBarMove(x: number) {
273
+ if (!barRef.value) return
274
+
275
+ const deltaX = x - dragStartData.value.startX
276
+ const deltaColumns = Math.round(deltaX / pixelsPerColumn.value)
277
+ const barWidth = currentEnd.value - currentStart.value
278
+
279
+ const oldStart = currentStart.value
280
+ const oldEnd = currentEnd.value
281
+ let newStart = dragStartData.value.startPos + deltaColumns
282
+ let newEnd = newStart + barWidth
283
+
284
+ // Boundary checks
285
+ if (newStart < 0) {
286
+ newStart = 0
287
+ newEnd = barWidth
288
+ } else if (newEnd > columnsCount) {
289
+ newEnd = columnsCount
290
+ newStart = newEnd - barWidth
291
+ }
292
+
293
+ currentStart.value = newStart
294
+ currentEnd.value = newEnd
295
+
296
+ store.updateGanttBar({
297
+ rowIndex,
298
+ colIndex,
299
+ type: 'bar',
300
+ oldStart,
301
+ oldEnd,
302
+ newStart,
303
+ newEnd,
304
+ delta: deltaColumns,
305
+ colspan: newEnd - newStart,
306
+ })
307
+ }
215
308
 
216
- currentStart.value = newStart
217
- currentEnd.value = newEnd
218
-
219
- store.updateGanttBar({
220
- rowIndex,
221
- colIndex,
222
- type: 'bar',
223
- oldStart,
224
- oldEnd,
225
- newStart,
226
- newEnd,
227
- delta: deltaColumns,
228
- colspan: newEnd - newStart,
229
- })
309
+ function registerGanttComponents() {
310
+ const { x: barX, y: barY } = useElementBounding(barRef)
311
+ const { x: leftX, y: leftY } = useElementBounding(leftConnectionHandleRef)
312
+ const { x: rightX, y: rightY } = useElementBounding(rightConnectionHandleRef)
313
+
314
+ store.registerGanttBar({
315
+ id: barId,
316
+ rowIndex,
317
+ colIndex,
318
+ startIndex: currentStart,
319
+ endIndex: currentEnd,
320
+ color: barColor,
321
+ label,
322
+ position: { x: barX, y: barY },
323
+ })
324
+
325
+ store.registerConnectionHandle({
326
+ id: `${barId}-connection-left`,
327
+ rowIndex,
328
+ colIndex,
329
+ side: 'left',
330
+ position: { x: leftX, y: leftY },
331
+ visible: isLeftConnectionVisible,
332
+ barId,
333
+ })
334
+
335
+ store.registerConnectionHandle({
336
+ id: `${barId}-connection-right`,
337
+ rowIndex,
338
+ colIndex,
339
+ side: 'right',
340
+ position: { x: rightX, y: rightY },
341
+ visible: isRightConnectionVisible,
342
+ barId,
343
+ })
344
+ }
345
+
346
+ function unregisterGanttComponents() {
347
+ store.unregisterGanttBar(barId)
348
+ store.unregisterConnectionHandle(`${barId}-connection-left`)
349
+ store.unregisterConnectionHandle(`${barId}-connection-right`)
350
+ }
351
+
352
+ function showConnectionHandles() {
353
+ isLeftConnectionVisible.value = true
354
+ isRightConnectionVisible.value = true
355
+ }
356
+
357
+ function hideConnectionHandles() {
358
+ if (!isLeftConnectionDragging.value && !isRightConnectionDragging.value) {
359
+ isLeftConnectionVisible.value = false
360
+ isRightConnectionVisible.value = false
361
+ }
362
+ }
363
+
364
+ function startConnectionDrag(side: 'left' | 'right', event: MouseEvent) {
365
+ event.preventDefault()
366
+ event.stopPropagation()
367
+
368
+ showDragPreview.value = true
369
+ if (side === 'left') {
370
+ isLeftConnectionDragging.value = true
371
+ } else {
372
+ isRightConnectionDragging.value = true
373
+ }
374
+
375
+ // Set initial drag preview position
376
+ const handle = side === 'left' ? leftConnectionHandleRef.value : rightConnectionHandleRef.value
377
+ if (handle) {
378
+ const handleRect = handle.getBoundingClientRect()
379
+ const centerX = handleRect.left + handleRect.width / 2
380
+ const centerY = handleRect.top + handleRect.height / 2
381
+ dragPreview.value = { startX: centerX, startY: centerY, endX: centerX, endY: centerY }
382
+ }
383
+
384
+ const handleMouseMove = (moveEvent: MouseEvent) => {
385
+ dragPreview.value.endX = moveEvent.clientX
386
+ dragPreview.value.endY = moveEvent.clientY
387
+ }
388
+
389
+ const handleMouseUp = (event: MouseEvent) => {
390
+ handleConnectionDrop(event, side)
391
+ cleanupConnectionDrag(handleMouseMove, handleMouseUp)
392
+ }
393
+
394
+ document.addEventListener('mousemove', handleMouseMove)
395
+ document.addEventListener('mouseup', handleMouseUp)
396
+ }
397
+
398
+ function handleConnectionDrop(upEvent: MouseEvent, sourceSide: 'left' | 'right') {
399
+ const targetElement = document.elementFromPoint(upEvent.clientX, upEvent.clientY)
400
+ const targetHandle = targetElement?.closest('.connection-handle')
401
+
402
+ if (
403
+ targetHandle &&
404
+ targetHandle !== (sourceSide === 'left' ? leftConnectionHandleRef.value : rightConnectionHandleRef.value)
405
+ ) {
406
+ const targetBar = targetHandle.closest('.gantt-bar')
407
+
408
+ if (targetBar) {
409
+ const targetRowIndex = parseInt(targetBar.getAttribute('data-rowindex') || '0')
410
+ const targetColIndex = parseInt(targetBar.getAttribute('data-colindex') || '0')
411
+ const targetSide = targetHandle.classList.contains('left-connection-handle') ? 'left' : 'right'
412
+ const targetBarId = `gantt-bar-row-${targetRowIndex}-col-${targetColIndex}`
413
+
414
+ const connection = store.createConnection(
415
+ `${barId}-connection-${sourceSide}`,
416
+ `${targetBarId}-connection-${targetSide}`
417
+ )
418
+
419
+ if (connection) {
420
+ emit('connection:create', connection)
421
+ }
230
422
  }
231
- },
423
+ }
424
+ }
425
+
426
+ function cleanupConnectionDrag(
427
+ handleMouseMove: (event: MouseEvent) => void,
428
+ handleMouseUp: (event: MouseEvent) => void
429
+ ) {
430
+ showDragPreview.value = false
431
+ isLeftConnectionDragging.value = false
432
+ isRightConnectionDragging.value = false
433
+
434
+ document.removeEventListener('mousemove', handleMouseMove)
435
+ document.removeEventListener('mouseup', handleMouseUp)
436
+
437
+ if (!barRef.value?.matches(':hover')) {
438
+ hideConnectionHandles()
439
+ }
440
+ }
441
+
442
+ defineExpose({
443
+ barStyle,
444
+ cleanupConnectionDrag,
445
+ currentEnd,
446
+ handleConnectionDrop,
447
+ isLeftConnectionDragging,
448
+ isLeftConnectionVisible,
449
+ isRightConnectionDragging,
450
+ isRightConnectionVisible,
451
+ showDragPreview,
232
452
  })
233
453
  </script>
234
454
 
@@ -240,17 +460,16 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
240
460
  height: 100%;
241
461
  }
242
462
 
243
- .gantt-handler {
463
+ .gantt-container {
244
464
  position: relative;
245
465
  height: 100%;
246
466
  background-color: #f0f0f0;
247
467
  border-radius: 4px;
248
- overflow: visible; /* Changed from hidden to allow indicators to extend out */
468
+ overflow: visible;
249
469
  }
250
470
 
251
471
  .gantt-bar {
252
472
  position: absolute;
253
- height: 100%;
254
473
  border-radius: 4px;
255
474
  display: flex;
256
475
  align-items: center;
@@ -259,6 +478,10 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
259
478
  box-sizing: border-box;
260
479
  border: 1px solid rgba(0, 0, 0, 0.5);
261
480
  transition: left 0.1s ease-out, width 0.1s ease-out;
481
+ height: 80%;
482
+ top: 50%;
483
+ z-index: 2;
484
+ transform: translateY(-50%);
262
485
  }
263
486
 
264
487
  .gantt-bar:active {
@@ -281,7 +504,7 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
281
504
  user-select: none;
282
505
  }
283
506
 
284
- .gantt-handle {
507
+ .resize-handle {
285
508
  position: relative;
286
509
  width: 12px;
287
510
  height: 100%;
@@ -293,10 +516,11 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
293
516
  background: rgba(0, 0, 0, 0.25);
294
517
  }
295
518
 
296
- .left-handle {
519
+ .left-resize-handle {
297
520
  border-right: 1px solid rgba(0, 0, 0, 0.5);
298
521
  }
299
- .right-handle {
522
+
523
+ .right-resize-handle {
300
524
  border-left: 1px solid rgba(0, 0, 0, 0.5);
301
525
  }
302
526
 
@@ -307,21 +531,20 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
307
531
  background: rgba(0, 0, 0, 0.8);
308
532
  }
309
533
 
310
- .gantt-handle:hover {
534
+ .resize-handle:hover {
311
535
  background-color: rgba(255, 255, 255, 0.5);
312
536
  }
313
537
 
314
- /* Vertical indicators for handles */
315
538
  .vertical-indicator {
316
539
  position: absolute;
317
540
  width: 2px;
318
541
  opacity: 0;
319
542
  pointer-events: none;
320
543
  transition: opacity 0.2s ease;
321
- top: -100vh; /* Extend up */
322
- height: 100vh; /* Full height, but will be clipped by tbody */
544
+ top: -100vh;
545
+ height: 100vh;
323
546
  z-index: 5;
324
- background-color: v-bind(baseColor);
547
+ background-color: v-bind(barColor);
325
548
  }
326
549
 
327
550
  .left-indicator {
@@ -334,11 +557,11 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
334
557
  transform: translateX(50%);
335
558
  }
336
559
 
337
- .gantt-handle.is-dragging .vertical-indicator {
560
+ .resize-handle.is-dragging .vertical-indicator {
338
561
  opacity: 0.7;
339
562
  }
340
563
 
341
- .gantt-handler::after {
564
+ .gantt-container::after {
342
565
  content: '';
343
566
  position: absolute;
344
567
  top: 0;
@@ -350,4 +573,55 @@ const { isDragging: isBarDragging } = useDraggable(barRef, {
350
573
  pointer-events: none;
351
574
  z-index: 1;
352
575
  }
576
+
577
+ .connection-handle {
578
+ position: absolute;
579
+ top: 50%;
580
+ transform: translateY(-50%);
581
+ width: 16px;
582
+ height: 16px;
583
+ opacity: 0;
584
+ transition: opacity 0.2s ease;
585
+ cursor: crosshair;
586
+ z-index: 15;
587
+ display: flex;
588
+ align-items: center;
589
+ justify-content: center;
590
+ }
591
+
592
+ .connection-handle.visible {
593
+ opacity: 1;
594
+ }
595
+
596
+ .left-connection-handle {
597
+ left: -16px;
598
+ }
599
+
600
+ .right-connection-handle {
601
+ right: -16px;
602
+ }
603
+
604
+ .connection-dot {
605
+ width: 8px;
606
+ height: 8px;
607
+ border-radius: 50%;
608
+ background-color: #2196f3;
609
+ border: 2px solid white;
610
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
611
+ }
612
+
613
+ .connection-handle:hover .connection-dot {
614
+ background-color: #1976d2;
615
+ transform: scale(1.2);
616
+ }
617
+
618
+ .connection-handle.is-dragging {
619
+ opacity: 1 !important;
620
+ }
621
+
622
+ .connection-handle.is-dragging .connection-dot {
623
+ background-color: #1976d2;
624
+ transform: scale(1.3);
625
+ box-shadow: 0 2px 8px rgba(33, 150, 243, 0.4);
626
+ }
353
627
  </style>