akfatimeline 1.2.0 → 2.0.0
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.
- package/dist/Timeline.js +16 -48
- package/{src/components/Timeline/DailyView.js → dist/components/Timeline/DailyView.jsx} +1 -0
- package/{src/components/Timeline/EventTooltip.js → dist/components/Timeline/EventTooltip.jsx} +207 -206
- package/{src/components/Timeline/Indicator.js → dist/components/Timeline/Indicator.jsx} +27 -26
- package/{src/components/Timeline/MasterHeader.js → dist/components/Timeline/MasterHeader.jsx} +105 -104
- package/{src/components/Timeline/Resources.js → dist/components/Timeline/Resources.jsx} +54 -53
- package/{src/components/Timeline/ResourcesHeader.js → dist/components/Timeline/ResourcesHeader.jsx} +15 -14
- package/{src/components/Timeline/Timeline.js → dist/components/Timeline/Timeline.jsx} +572 -607
- package/{src/components/Timeline/TimelineContent.js → dist/components/Timeline/TimelineContent.jsx} +837 -838
- package/{src/components/Timeline/TimelineHeader.js → dist/components/Timeline/TimelineHeader.jsx} +55 -54
- package/dist/components/Timeline/TimelineMonthContainer.js +2 -2
- package/package.json +25 -25
- package/src/components/Timeline/AutocompleteSelect.jsx +150 -0
- package/src/components/Timeline/ContextMenu.jsx +149 -0
- package/src/components/Timeline/DailyView.jsx +256 -0
- package/src/components/Timeline/EventBadge.jsx +26 -0
- package/src/components/Timeline/EventDetailModal.jsx +138 -0
- package/src/components/Timeline/EventIcon.jsx +47 -0
- package/src/components/Timeline/EventTooltip.jsx +207 -0
- package/src/components/Timeline/Indicator.jsx +27 -0
- package/src/components/Timeline/LoadingSpinner.jsx +48 -0
- package/src/components/Timeline/MasterHeader.jsx +105 -0
- package/src/components/Timeline/Resources.jsx +54 -0
- package/src/components/Timeline/ResourcesHeader.jsx +15 -0
- package/src/components/Timeline/Timeline.jsx +572 -0
- package/src/components/Timeline/TimelineContent.jsx +837 -0
- package/src/components/Timeline/TimelineHeader.jsx +55 -0
- package/src/components/Timeline/TimelineMonthContainer.js +2 -2
- package/src/library.js +8 -8
- /package/{src/components/Timeline/AutocompleteSelect.js → dist/components/Timeline/AutocompleteSelect.jsx} +0 -0
- /package/{src/components/Timeline/ContextMenu.js → dist/components/Timeline/ContextMenu.jsx} +0 -0
- /package/{src/components/Timeline/EventBadge.js → dist/components/Timeline/EventBadge.jsx} +0 -0
- /package/{src/components/Timeline/EventDetailModal.js → dist/components/Timeline/EventDetailModal.jsx} +0 -0
- /package/{src/components/Timeline/EventIcon.js → dist/components/Timeline/EventIcon.jsx} +0 -0
- /package/{src/components/Timeline/LoadingSpinner.js → dist/components/Timeline/LoadingSpinner.jsx} +0 -0
package/{src/components/Timeline/TimelineContent.js → dist/components/Timeline/TimelineContent.jsx}
RENAMED
|
@@ -1,838 +1,837 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect, useCallback } from "react";
|
|
2
|
-
import { parseDate } from "../../utils/dateUtils";
|
|
3
|
-
import useDragAndDrop from "../../hooks/useDragAndDrop";
|
|
4
|
-
import useEventDragDrop from "../../hooks/useEventDragDrop";
|
|
5
|
-
import Indicator from "./Indicator";
|
|
6
|
-
import EventIcon from "./EventIcon";
|
|
7
|
-
import EventBadge from "./EventBadge";
|
|
8
|
-
import ContextMenu from "./ContextMenu";
|
|
9
|
-
// import "./Timeline.css"; // varsayalım "Timeline.css" globalde import ediliyor
|
|
10
|
-
|
|
11
|
-
const TimelineContent = ({
|
|
12
|
-
groupedResources,
|
|
13
|
-
dates,
|
|
14
|
-
collapsedGroups,
|
|
15
|
-
events,
|
|
16
|
-
setEvents,
|
|
17
|
-
onEventClick,
|
|
18
|
-
todayIndex,
|
|
19
|
-
indicatorOn,
|
|
20
|
-
resourceSettings,
|
|
21
|
-
setDropInfo,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
eventsDragOn = true,
|
|
25
|
-
eventsExtendOn = true,
|
|
26
|
-
createNewEventOn = true,
|
|
27
|
-
|
|
28
|
-
onExtendInfo,
|
|
29
|
-
onCreateEventInfo,
|
|
30
|
-
onEventRightClick,
|
|
31
|
-
onEventDoubleClick = null,
|
|
32
|
-
selectedEvents = [],
|
|
33
|
-
onEventSelect = null,
|
|
34
|
-
|
|
35
|
-
eventTooltipOn = true,
|
|
36
|
-
tooltipComponent: TooltipComponent,
|
|
37
|
-
tempEventStyle = {},
|
|
38
|
-
|
|
39
|
-
eventStyleResolver = () => ({}),
|
|
40
|
-
|
|
41
|
-
// Event Alignment Mode
|
|
42
|
-
eventAlignmentMode = "center", // "center" | "full"
|
|
43
|
-
|
|
44
|
-
// Past Date Protection
|
|
45
|
-
preventPastEvents = false,
|
|
46
|
-
minDate = null,
|
|
47
|
-
|
|
48
|
-
// Weekend Highlighting
|
|
49
|
-
highlightWeekends = false,
|
|
50
|
-
|
|
51
|
-
// Cell Tooltip
|
|
52
|
-
cellTooltipOn = false,
|
|
53
|
-
cellTooltipResolver = null,
|
|
54
|
-
|
|
55
|
-
// Event Icons & Badges
|
|
56
|
-
eventIconsOn = false, // İkonları göster/gizle
|
|
57
|
-
eventIconResolver = null, // (event) => icon type döndüren fonksiyon
|
|
58
|
-
eventBadgesOn = false, // Badge'leri göster/gizle
|
|
59
|
-
eventBadgeResolver = null, // (event) => { text, type, position } döndüren fonksiyon
|
|
60
|
-
|
|
61
|
-
// Loading State
|
|
62
|
-
isLoading = false,
|
|
63
|
-
loadingType = 'spinner', // 'spinner', 'dots', 'pulse'
|
|
64
|
-
|
|
65
|
-
// Context Menu
|
|
66
|
-
cellContextMenuOn = false, // Cell context menu'yu aç/kapa
|
|
67
|
-
cellContextMenuItems = [], // Context menu öğeleri
|
|
68
|
-
onCellContextMenu = null, // Context menu açıldığında çağrılacak callback
|
|
69
|
-
}) => {
|
|
70
|
-
// ------------------- HOOKS & STATE -------------------
|
|
71
|
-
const containerRef = useRef(null);
|
|
72
|
-
|
|
73
|
-
// Drag
|
|
74
|
-
const { dragStart, dragEnd } = useDragAndDrop(events, setEvents);
|
|
75
|
-
const { handleDragStart, handleDragOver, handleDrop, handleDragEnd } = useEventDragDrop(
|
|
76
|
-
events,
|
|
77
|
-
setEvents,
|
|
78
|
-
setDropInfo // Doğrudan setDropInfo'yu geçiriyoruz
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
const [
|
|
88
|
-
const [
|
|
89
|
-
const [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const [
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
e.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
startDate,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (!
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
window.addEventListener("
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
window.removeEventListener("
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
document.addEventListener("
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
document.removeEventListener("
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
{
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
dragImage.style.
|
|
657
|
-
dragImage.style.
|
|
658
|
-
dragImage.style.
|
|
659
|
-
dragImage.style.
|
|
660
|
-
dragImage.style.
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
>
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
export default TimelineContent;
|
|
1
|
+
import React, { useState, useRef, useEffect, useCallback } from "react";
|
|
2
|
+
import { parseDate } from "../../utils/dateUtils";
|
|
3
|
+
import useDragAndDrop from "../../hooks/useDragAndDrop";
|
|
4
|
+
import useEventDragDrop from "../../hooks/useEventDragDrop";
|
|
5
|
+
import Indicator from "./Indicator.jsx";
|
|
6
|
+
import EventIcon from "./EventIcon.jsx";
|
|
7
|
+
import EventBadge from "./EventBadge.jsx";
|
|
8
|
+
import ContextMenu from "./ContextMenu.jsx";
|
|
9
|
+
// import "./Timeline.css"; // varsayalım "Timeline.css" globalde import ediliyor
|
|
10
|
+
|
|
11
|
+
const TimelineContent = ({
|
|
12
|
+
groupedResources,
|
|
13
|
+
dates,
|
|
14
|
+
collapsedGroups,
|
|
15
|
+
events,
|
|
16
|
+
setEvents,
|
|
17
|
+
onEventClick,
|
|
18
|
+
todayIndex,
|
|
19
|
+
indicatorOn,
|
|
20
|
+
resourceSettings,
|
|
21
|
+
setDropInfo,
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
eventsDragOn = true,
|
|
25
|
+
eventsExtendOn = true,
|
|
26
|
+
createNewEventOn = true,
|
|
27
|
+
|
|
28
|
+
onExtendInfo,
|
|
29
|
+
onCreateEventInfo,
|
|
30
|
+
onEventRightClick,
|
|
31
|
+
onEventDoubleClick = null,
|
|
32
|
+
selectedEvents = [],
|
|
33
|
+
onEventSelect = null,
|
|
34
|
+
|
|
35
|
+
eventTooltipOn = true,
|
|
36
|
+
tooltipComponent: TooltipComponent,
|
|
37
|
+
tempEventStyle = {},
|
|
38
|
+
|
|
39
|
+
eventStyleResolver = () => ({}),
|
|
40
|
+
|
|
41
|
+
// Event Alignment Mode
|
|
42
|
+
eventAlignmentMode = "center", // "center" | "full"
|
|
43
|
+
|
|
44
|
+
// Past Date Protection
|
|
45
|
+
preventPastEvents = false,
|
|
46
|
+
minDate = null,
|
|
47
|
+
|
|
48
|
+
// Weekend Highlighting
|
|
49
|
+
highlightWeekends = false,
|
|
50
|
+
|
|
51
|
+
// Cell Tooltip
|
|
52
|
+
cellTooltipOn = false,
|
|
53
|
+
cellTooltipResolver = null,
|
|
54
|
+
|
|
55
|
+
// Event Icons & Badges
|
|
56
|
+
eventIconsOn = false, // İkonları göster/gizle
|
|
57
|
+
eventIconResolver = null, // (event) => icon type döndüren fonksiyon
|
|
58
|
+
eventBadgesOn = false, // Badge'leri göster/gizle
|
|
59
|
+
eventBadgeResolver = null, // (event) => { text, type, position } döndüren fonksiyon
|
|
60
|
+
|
|
61
|
+
// Loading State
|
|
62
|
+
isLoading = false,
|
|
63
|
+
loadingType = 'spinner', // 'spinner', 'dots', 'pulse'
|
|
64
|
+
|
|
65
|
+
// Context Menu
|
|
66
|
+
cellContextMenuOn = false, // Cell context menu'yu aç/kapa
|
|
67
|
+
cellContextMenuItems = [], // Context menu öğeleri
|
|
68
|
+
onCellContextMenu = null, // Context menu açıldığında çağrılacak callback
|
|
69
|
+
}) => {
|
|
70
|
+
// ------------------- HOOKS & STATE -------------------
|
|
71
|
+
const containerRef = useRef(null);
|
|
72
|
+
|
|
73
|
+
// Drag
|
|
74
|
+
const { dragStart, dragEnd } = useDragAndDrop(events, setEvents);
|
|
75
|
+
const { handleDragStart, handleDragOver, handleDrop, handleDragEnd } = useEventDragDrop(
|
|
76
|
+
events,
|
|
77
|
+
setEvents,
|
|
78
|
+
setDropInfo // Doğrudan setDropInfo'yu geçiriyoruz
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// Extend
|
|
85
|
+
// extendEvent removed - not used (extend logic handled manually)
|
|
86
|
+
const [mode, setMode] = useState(null); // null | "extend"
|
|
87
|
+
const [extendingEvent, setExtendingEvent] = useState(null);
|
|
88
|
+
const [originalEndDate, setOriginalEndDate] = useState(null);
|
|
89
|
+
const [startMouseX, setStartMouseX] = useState(null);
|
|
90
|
+
|
|
91
|
+
// Create new event
|
|
92
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
93
|
+
const [tempEvent, setTempEvent] = useState(null);
|
|
94
|
+
|
|
95
|
+
// Cell Tooltip State
|
|
96
|
+
const [cellTooltip, setCellTooltip] = useState(null);
|
|
97
|
+
const [cellTooltipPosition, setCellTooltipPosition] = useState({ top: 0, left: 0 });
|
|
98
|
+
|
|
99
|
+
// Context Menu State
|
|
100
|
+
const [contextMenu, setContextMenu] = useState({
|
|
101
|
+
isOpen: false,
|
|
102
|
+
position: null,
|
|
103
|
+
resource: null,
|
|
104
|
+
date: null,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Tooltip
|
|
108
|
+
const [selectedEvent, setSelectedEvent] = useState(null);
|
|
109
|
+
const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
|
|
110
|
+
|
|
111
|
+
const totalDays = dates.length;
|
|
112
|
+
|
|
113
|
+
// ------------------- Tooltip Logic -------------------
|
|
114
|
+
const handleEventClickInternal = (event, e) => {
|
|
115
|
+
e.stopPropagation();
|
|
116
|
+
// Eğer mod "extend" ise tooltip'i açma
|
|
117
|
+
if (mode === "extend") {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Multi-select için Ctrl+Click kontrolü
|
|
122
|
+
if (onEventSelect && e.ctrlKey) {
|
|
123
|
+
onEventSelect(event.id, true); // multiSelect = true
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Harici callback
|
|
128
|
+
if (onEventClick) onEventClick(event, e);
|
|
129
|
+
|
|
130
|
+
// Tooltip göstermek
|
|
131
|
+
const eventElement = e.currentTarget;
|
|
132
|
+
if (eventElement) {
|
|
133
|
+
const rect = eventElement.getBoundingClientRect();
|
|
134
|
+
setTooltipPosition({
|
|
135
|
+
top: rect.top + window.scrollY,
|
|
136
|
+
left: rect.left + rect.width / 2 + window.scrollX,
|
|
137
|
+
});
|
|
138
|
+
setSelectedEvent(event);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const handleEventDoubleClickInternal = (event, e) => {
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
if (onEventDoubleClick) {
|
|
145
|
+
onEventDoubleClick(event);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
const handleCloseTooltip = () => {
|
|
151
|
+
setSelectedEvent(null);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// ------------------- Context Menu -------------------
|
|
155
|
+
const handleCellContextMenu = useCallback((e, resource, dateObj) => {
|
|
156
|
+
if (!cellContextMenuOn) return;
|
|
157
|
+
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
e.stopPropagation();
|
|
160
|
+
|
|
161
|
+
// Resource'u bul
|
|
162
|
+
const resourceObj = groupedResources
|
|
163
|
+
.flatMap(group => group.resources || [])
|
|
164
|
+
.find(r => r.id === resource.id || resource === r.id);
|
|
165
|
+
|
|
166
|
+
// Mouse pozisyonunu doğrudan kullan (scroll offset'i dahil etme)
|
|
167
|
+
setContextMenu({
|
|
168
|
+
isOpen: true,
|
|
169
|
+
position: {
|
|
170
|
+
x: e.clientX,
|
|
171
|
+
y: e.clientY
|
|
172
|
+
},
|
|
173
|
+
resource: resourceObj || resource,
|
|
174
|
+
date: dateObj,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (onCellContextMenu) {
|
|
178
|
+
onCellContextMenu(resourceObj || resource, dateObj, e);
|
|
179
|
+
}
|
|
180
|
+
}, [cellContextMenuOn, groupedResources, onCellContextMenu]);
|
|
181
|
+
|
|
182
|
+
const handleCloseContextMenu = useCallback(() => {
|
|
183
|
+
setContextMenu({
|
|
184
|
+
isOpen: false,
|
|
185
|
+
position: null,
|
|
186
|
+
resource: null,
|
|
187
|
+
date: null,
|
|
188
|
+
});
|
|
189
|
+
}, []);
|
|
190
|
+
|
|
191
|
+
// ------------------- Create New Event -------------------
|
|
192
|
+
const handleCellClick = (resourceId, date, e) => {
|
|
193
|
+
if (!createNewEventOn) return; // create devrede değilse
|
|
194
|
+
|
|
195
|
+
// Sağ tıklamayı engelle (button 2 = sağ tık, button 0 = sol tık)
|
|
196
|
+
if (e.button === 2 || e.which === 3) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const startDate = parseDate(date.fullDate);
|
|
201
|
+
|
|
202
|
+
// Geçmiş tarih kontrolü
|
|
203
|
+
if (preventPastEvents && minDate) {
|
|
204
|
+
const minDateObj = parseDate(minDate);
|
|
205
|
+
// Sadece tarih karşılaştırması (saat bilgisi olmadan)
|
|
206
|
+
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
|
|
207
|
+
const minDateOnly = new Date(minDateObj.getFullYear(), minDateObj.getMonth(), minDateObj.getDate());
|
|
208
|
+
|
|
209
|
+
if (startDateOnly < minDateOnly) {
|
|
210
|
+
// Geçmiş tarihe tıklama engellendi
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const newEvent = {
|
|
216
|
+
id: Date.now(),
|
|
217
|
+
title: "1 Gece",
|
|
218
|
+
startDate,
|
|
219
|
+
endDate: new Date(startDate.getTime() + 24 * 60 * 60 * 1000),
|
|
220
|
+
resourceId,
|
|
221
|
+
// Mouse başlangıç pozisyonunu kaydet
|
|
222
|
+
startX: e?.clientX || 0,
|
|
223
|
+
startCellIndex: dates.findIndex((d) => parseDate(d.fullDate).toDateString() === startDate.toDateString()),
|
|
224
|
+
// color => var(--timeline-new-event-background-color) => => Sonra inline style yerine className
|
|
225
|
+
color: "", // Bunu .css'te "var(--timeline-new-event-background-color)" atayabilirsin
|
|
226
|
+
};
|
|
227
|
+
setTempEvent(newEvent);
|
|
228
|
+
setIsCreating(true);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (!createNewEventOn) return;
|
|
233
|
+
if (!isCreating) return;
|
|
234
|
+
if (mode === "extend") {
|
|
235
|
+
console.log(">>> 'extend' mode, skip new event creation");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const handleMouseMove = (e) => {
|
|
240
|
+
if (!isCreating || !tempEvent) return;
|
|
241
|
+
|
|
242
|
+
// Timeline container'ı bul
|
|
243
|
+
const timelineContainer = containerRef.current?.closest('.timeline-scrollable-container');
|
|
244
|
+
if (!timelineContainer) return;
|
|
245
|
+
|
|
246
|
+
// Container'ın sol pozisyonunu al
|
|
247
|
+
const containerRect = timelineContainer.getBoundingClientRect();
|
|
248
|
+
const scrollLeft = timelineContainer.scrollLeft;
|
|
249
|
+
|
|
250
|
+
// Mouse'un container içindeki pozisyonunu hesapla
|
|
251
|
+
const mouseX = e.clientX - containerRect.left + scrollLeft;
|
|
252
|
+
|
|
253
|
+
// Gerçek cell genişliğini hesapla (container genişliği / toplam gün sayısı)
|
|
254
|
+
const containerWidth = timelineContainer.scrollWidth;
|
|
255
|
+
const cellWidth = containerWidth / totalDays;
|
|
256
|
+
|
|
257
|
+
// Hangi cell'in üzerinde olduğumuzu hesapla
|
|
258
|
+
let currentCellIndex = Math.floor(mouseX / cellWidth);
|
|
259
|
+
currentCellIndex = Math.max(0, Math.min(currentCellIndex, totalDays - 1)); // Sınırları kontrol et
|
|
260
|
+
|
|
261
|
+
// Başlangıç cell index'ini al
|
|
262
|
+
const startCellIndex = tempEvent.startCellIndex ?? 0;
|
|
263
|
+
|
|
264
|
+
// Geçmiş tarih kontrolü - eğer aktifse, minimum tarihten önceki cell'lere gitmeyi engelle
|
|
265
|
+
if (preventPastEvents && minDate && dates[currentCellIndex]) {
|
|
266
|
+
const currentDate = parseDate(dates[currentCellIndex].fullDate);
|
|
267
|
+
const minDateObj = parseDate(minDate);
|
|
268
|
+
const currentDateOnly = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
|
|
269
|
+
const minDateOnly = new Date(minDateObj.getFullYear(), minDateObj.getMonth(), minDateObj.getDate());
|
|
270
|
+
|
|
271
|
+
// Eğer geçmiş tarihe gidiyorsak, minimum tarihe sabitle
|
|
272
|
+
if (currentDateOnly < minDateOnly) {
|
|
273
|
+
// Minimum tarihin cell index'ini bul
|
|
274
|
+
const minDateIndex = dates.findIndex((d) => {
|
|
275
|
+
const dDate = parseDate(d.fullDate);
|
|
276
|
+
const dDateOnly = new Date(dDate.getFullYear(), dDate.getMonth(), dDate.getDate());
|
|
277
|
+
return dDateOnly.getTime() === minDateOnly.getTime();
|
|
278
|
+
});
|
|
279
|
+
if (minDateIndex !== -1) {
|
|
280
|
+
currentCellIndex = Math.max(startCellIndex, minDateIndex);
|
|
281
|
+
} else {
|
|
282
|
+
currentCellIndex = startCellIndex; // Minimum tarih bulunamazsa başlangıç pozisyonuna dön
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Kaç gün ekleneceğini hesapla (daha hassas)
|
|
288
|
+
const daysToAdd = Math.max(1, Math.abs(currentCellIndex - startCellIndex) + 1);
|
|
289
|
+
|
|
290
|
+
// Yeni bitiş tarihini hesapla
|
|
291
|
+
const newEndDate = new Date(tempEvent.startDate.getTime());
|
|
292
|
+
newEndDate.setDate(newEndDate.getDate() + daysToAdd - 1); // -1 çünkü başlangıç günü dahil
|
|
293
|
+
|
|
294
|
+
setTempEvent({
|
|
295
|
+
...tempEvent,
|
|
296
|
+
endDate: newEndDate,
|
|
297
|
+
title: `${daysToAdd} Gece`,
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const handleMouseUp = () => {
|
|
302
|
+
if (isCreating && tempEvent) {
|
|
303
|
+
setEvents([...events, tempEvent]);
|
|
304
|
+
if (onCreateEventInfo) {
|
|
305
|
+
onCreateEventInfo(tempEvent);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
setTempEvent(null);
|
|
309
|
+
setIsCreating(false);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
313
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
314
|
+
|
|
315
|
+
return () => {
|
|
316
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
317
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
318
|
+
};
|
|
319
|
+
}, [createNewEventOn, isCreating, mode, tempEvent, events, onCreateEventInfo, setEvents, totalDays, dates, preventPastEvents, minDate]);
|
|
320
|
+
|
|
321
|
+
// ------------------- Drag Logic -------------------
|
|
322
|
+
const handleDragStartSafe = (e, eventId) => {
|
|
323
|
+
if (!eventsDragOn) {
|
|
324
|
+
e.preventDefault();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
handleDragStart(e, eventId);
|
|
328
|
+
};
|
|
329
|
+
const handleDragEndSafe = (e) => {
|
|
330
|
+
if (!eventsDragOn) {
|
|
331
|
+
e.preventDefault();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
handleDragEnd();
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
// ------------------- Extend Logic -------------------
|
|
342
|
+
const handleMouseDownExtend = (mouseEvent, event) => {
|
|
343
|
+
if (!eventsExtendOn) return;
|
|
344
|
+
mouseEvent.stopPropagation();
|
|
345
|
+
console.log(">>> Extend start ID:", event.id);
|
|
346
|
+
setMode("extend");
|
|
347
|
+
setExtendingEvent(event);
|
|
348
|
+
setOriginalEndDate(event.endDate);
|
|
349
|
+
setStartMouseX(mouseEvent.clientX);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const handleMouseMoveExtend = useCallback((e) => {
|
|
353
|
+
if (mode !== "extend" || !extendingEvent) return;
|
|
354
|
+
if (!eventsExtendOn) return;
|
|
355
|
+
|
|
356
|
+
const currentMouseX = e.clientX;
|
|
357
|
+
const deltaX = currentMouseX - (startMouseX ?? 0);
|
|
358
|
+
const cellW = 30;
|
|
359
|
+
const daysToAdd = Math.floor(deltaX / cellW);
|
|
360
|
+
|
|
361
|
+
const newEndDate = new Date((originalEndDate ?? new Date()).getTime());
|
|
362
|
+
newEndDate.setDate(newEndDate.getDate() + daysToAdd);
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
setEvents((prev) =>
|
|
366
|
+
prev.map((evt) => (evt.id === extendingEvent.id ? { ...evt, endDate: newEndDate } : evt))
|
|
367
|
+
);
|
|
368
|
+
}, [mode, extendingEvent, eventsExtendOn, originalEndDate, startMouseX, setEvents]);
|
|
369
|
+
|
|
370
|
+
const handleMouseUpExtend = useCallback(() => {
|
|
371
|
+
console.log(">>> Extend finished ID:", extendingEvent?.id);
|
|
372
|
+
if (onExtendInfo && extendingEvent) {
|
|
373
|
+
// callback
|
|
374
|
+
const updatedEvent = events.find((ev) => ev.id === extendingEvent.id);
|
|
375
|
+
if (updatedEvent) {
|
|
376
|
+
onExtendInfo({
|
|
377
|
+
eventId: extendingEvent.id,
|
|
378
|
+
newEndDate: updatedEvent.endDate,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Tooltip açılmasını engellemek için modun null olmasını geciktiriyoruz
|
|
384
|
+
setTimeout(() => {
|
|
385
|
+
setMode(null);
|
|
386
|
+
}, 100); // 100ms gecikme
|
|
387
|
+
setExtendingEvent(null);
|
|
388
|
+
setOriginalEndDate(null);
|
|
389
|
+
setStartMouseX(null);
|
|
390
|
+
}, [extendingEvent, onExtendInfo, events]);
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
if (mode === "extend") {
|
|
395
|
+
const onMove = (e) => handleMouseMoveExtend(e);
|
|
396
|
+
const onUp = () => handleMouseUpExtend();
|
|
397
|
+
document.addEventListener("mousemove", onMove);
|
|
398
|
+
document.addEventListener("mouseup", onUp);
|
|
399
|
+
return () => {
|
|
400
|
+
document.removeEventListener("mousemove", onMove);
|
|
401
|
+
document.removeEventListener("mouseup", onUp);
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}, [mode, handleMouseMoveExtend, handleMouseUpExtend]);
|
|
405
|
+
|
|
406
|
+
// ------------------- Right Click (context) -------------------
|
|
407
|
+
const handleRightClickEvent = (evt, reactEvent) => {
|
|
408
|
+
reactEvent.preventDefault();
|
|
409
|
+
if (onEventRightClick) onEventRightClick(evt, reactEvent);
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// ------------------- Helper isCellSelected -------------------
|
|
413
|
+
const isCellSelected = (resourceId, date) => {
|
|
414
|
+
if (!dragStart || !dragEnd) return false;
|
|
415
|
+
if (resourceId !== dragStart.resourceId) return false;
|
|
416
|
+
|
|
417
|
+
const startIndex = dates.findIndex((d) => parseDate(d.fullDate).getTime() === parseDate(dragStart.date).getTime());
|
|
418
|
+
const endIndex = dates.findIndex((d) => parseDate(d.fullDate).getTime() === parseDate(dragEnd.date).getTime());
|
|
419
|
+
const currentIndex = dates.findIndex((d) => parseDate(d.fullDate).getTime() === parseDate(date.fullDate).getTime());
|
|
420
|
+
|
|
421
|
+
if (startIndex === -1 || endIndex === -1 || currentIndex === -1) return false;
|
|
422
|
+
|
|
423
|
+
return currentIndex >= Math.min(startIndex, endIndex) && currentIndex <= Math.max(startIndex, endIndex);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// ------------------- calculatePosition -------------------
|
|
427
|
+
const calculatePosition = (ev, dateArr) => {
|
|
428
|
+
const startDate = parseDate(ev.startDate);
|
|
429
|
+
const endDate = parseDate(ev.endDate);
|
|
430
|
+
|
|
431
|
+
const startIndex = dateArr.findIndex((d) => parseDate(d.fullDate).toDateString() === startDate.toDateString());
|
|
432
|
+
const endIndex = dateArr.findIndex((d) => parseDate(d.fullDate).toDateString() === endDate.toDateString());
|
|
433
|
+
|
|
434
|
+
const totalDays = dateArr.length;
|
|
435
|
+
if (startIndex < 0 && endIndex < 0) {
|
|
436
|
+
return { isVisible: false, left: 0, width: 0, isPartialStart: false, isPartialEnd: false };
|
|
437
|
+
}
|
|
438
|
+
if (startIndex >= totalDays && endIndex >= totalDays) {
|
|
439
|
+
return { isVisible: false, left: 0, width: 0, isPartialStart: false, isPartialEnd: false };
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const effectiveStartIndex = Math.max(startIndex, 0);
|
|
443
|
+
const effectiveEndIndex = Math.min(endIndex, totalDays - 1);
|
|
444
|
+
|
|
445
|
+
const isPartialStart = startIndex < 0;
|
|
446
|
+
const isPartialEnd = endIndex >= totalDays;
|
|
447
|
+
|
|
448
|
+
// Event alignment mode'a göre pozisyon hesaplama
|
|
449
|
+
let leftPercentage, rightPercentage;
|
|
450
|
+
|
|
451
|
+
if (eventAlignmentMode === "full") {
|
|
452
|
+
// Full mode: Gün başından başlayıp gün sonunda bitiyor
|
|
453
|
+
// Bitiş tarihi hariç (exclusive) - örn: 3 Ocak bitiş tarihi ise 2 Ocak'ın sonunda biter
|
|
454
|
+
leftPercentage = (effectiveStartIndex / totalDays) * 100;
|
|
455
|
+
// endIndex zaten bitiş tarihini gösteriyor, bu yüzden endIndex'in başlangıcı = bir önceki günün sonu
|
|
456
|
+
rightPercentage = (effectiveEndIndex / totalDays) * 100;
|
|
457
|
+
} else {
|
|
458
|
+
// Center mode (varsayılan): Gün ortasından başlayıp gün ortasında bitiyor
|
|
459
|
+
leftPercentage = ((effectiveStartIndex + (isPartialStart ? 0 : 0.5)) / totalDays) * 100;
|
|
460
|
+
rightPercentage = ((effectiveEndIndex + (isPartialEnd ? 1 : 0.5)) / totalDays) * 100;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const widthPercentage = rightPercentage - leftPercentage;
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
isVisible: true,
|
|
467
|
+
left: `${leftPercentage}%`,
|
|
468
|
+
width: `${widthPercentage}%`,
|
|
469
|
+
isPartialStart,
|
|
470
|
+
isPartialEnd,
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
// ------------------- RENDER -------------------
|
|
479
|
+
return (
|
|
480
|
+
<>
|
|
481
|
+
{/* Cell Tooltip */}
|
|
482
|
+
{cellTooltip && cellTooltipOn && (
|
|
483
|
+
<div
|
|
484
|
+
className="cell-tooltip"
|
|
485
|
+
style={{
|
|
486
|
+
position: 'fixed',
|
|
487
|
+
top: `${cellTooltipPosition.top - 152}px`, // Mouse'un hemen altında
|
|
488
|
+
left: `${cellTooltipPosition.left - 168}px`, // Mouse'un hemen sağında
|
|
489
|
+
pointerEvents: 'none',
|
|
490
|
+
zIndex: 10002,
|
|
491
|
+
}}
|
|
492
|
+
>
|
|
493
|
+
<div className="cell-tooltip-content">
|
|
494
|
+
{typeof cellTooltip.content === 'string'
|
|
495
|
+
? cellTooltip.content
|
|
496
|
+
: cellTooltip.content}
|
|
497
|
+
</div>
|
|
498
|
+
<div className="cell-tooltip-arrow"></div>
|
|
499
|
+
</div>
|
|
500
|
+
)}
|
|
501
|
+
|
|
502
|
+
<div
|
|
503
|
+
ref={containerRef}
|
|
504
|
+
className="timeline-content-container" // Yeni class, stilini timeline.css'e ekleyebilirsin
|
|
505
|
+
>
|
|
506
|
+
{indicatorOn && (
|
|
507
|
+
<Indicator todayIndex={todayIndex} totalDays={totalDays} />
|
|
508
|
+
)}
|
|
509
|
+
|
|
510
|
+
{groupedResources.map((group, groupIndex) => (
|
|
511
|
+
<div key={groupIndex} className="timeline-group-container">
|
|
512
|
+
{/* Grup Başlığı */}
|
|
513
|
+
{resourceSettings.isGrouped && (
|
|
514
|
+
<div className="timeline-group-header-row">
|
|
515
|
+
{dates.map((dateObj, colIndex) => {
|
|
516
|
+
// Hafta sonu kontrolü
|
|
517
|
+
let isWeekend = false;
|
|
518
|
+
if (highlightWeekends) {
|
|
519
|
+
const cellDate = parseDate(dateObj.fullDate);
|
|
520
|
+
const dayOfWeek = cellDate.getDay(); // 0 = Pazar, 6 = Cumartesi
|
|
521
|
+
isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
<div
|
|
526
|
+
key={`group-header-${groupIndex}-${colIndex}`}
|
|
527
|
+
className={`timeline-group-header-cell ${isWeekend ? "timeline-cell-weekend" : ""}`}
|
|
528
|
+
></div>
|
|
529
|
+
);
|
|
530
|
+
})}
|
|
531
|
+
</div>
|
|
532
|
+
)}
|
|
533
|
+
|
|
534
|
+
{/* Kaynaklar */}
|
|
535
|
+
{!collapsedGroups[group.groupName] &&
|
|
536
|
+
group.resources.map((resource, rowIndex) => {
|
|
537
|
+
// Saatlik rezervasyonları ayrı işle
|
|
538
|
+
const hourlyEvents = events.filter((ev) => ev.resourceId === resource.id && ev.isHourly === true);
|
|
539
|
+
const normalEvents = events.filter((ev) => ev.resourceId === resource.id && ev.isHourly !== true);
|
|
540
|
+
|
|
541
|
+
// Saatlik rezervasyonları günlere göre grupla ve tek event'e dönüştür
|
|
542
|
+
const hourlyEventsGrouped = {};
|
|
543
|
+
hourlyEvents.forEach(event => {
|
|
544
|
+
const eventDate = new Date(event.startDate);
|
|
545
|
+
const dateKey = `${eventDate.getFullYear()}-${eventDate.getMonth()}-${eventDate.getDate()}`;
|
|
546
|
+
|
|
547
|
+
if (!hourlyEventsGrouped[dateKey]) {
|
|
548
|
+
hourlyEventsGrouped[dateKey] = {
|
|
549
|
+
events: [],
|
|
550
|
+
startDate: new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate()),
|
|
551
|
+
endDate: new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate() + 1),
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
hourlyEventsGrouped[dateKey].events.push(event);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Gruplanmış saatlik rezervasyonları tek event'e dönüştür
|
|
558
|
+
const groupedHourlyEvents = Object.values(hourlyEventsGrouped).map(group => {
|
|
559
|
+
const count = group.events.length;
|
|
560
|
+
const totalMinutes = group.events.reduce((sum, ev) => {
|
|
561
|
+
return sum + (new Date(ev.endDate).getTime() - new Date(ev.startDate).getTime()) / (1000 * 60);
|
|
562
|
+
}, 0);
|
|
563
|
+
const totalHours = Math.round(totalMinutes / 60 * 10) / 10; // 1 ondalık basamak
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
id: `hourly-group-${group.startDate.getTime()}`,
|
|
567
|
+
title: count > 1 ? `${count} Saatlik Rezervasyon (${totalHours} saat)` : `${totalHours} Saatlik Rezervasyon`,
|
|
568
|
+
startDate: group.startDate,
|
|
569
|
+
endDate: group.endDate,
|
|
570
|
+
resourceId: resource.id,
|
|
571
|
+
isHourly: true,
|
|
572
|
+
isGrouped: true,
|
|
573
|
+
hourlyCount: count,
|
|
574
|
+
hourlyTotalHours: totalHours,
|
|
575
|
+
};
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
// Normal event'ler ve gruplanmış saatlik event'leri birleştir
|
|
579
|
+
const resourceEvents = [...normalEvents, ...groupedHourlyEvents];
|
|
580
|
+
|
|
581
|
+
return (
|
|
582
|
+
<div key={resource.id} className="timeline-resource-row">
|
|
583
|
+
{/* Her resource row'u */}
|
|
584
|
+
{resourceEvents.map((event) => {
|
|
585
|
+
const { isVisible, left, width, isPartialStart, isPartialEnd } =
|
|
586
|
+
calculatePosition(event, dates);
|
|
587
|
+
if (!isVisible) return null;
|
|
588
|
+
|
|
589
|
+
// Kullanıcıdan gelen stil
|
|
590
|
+
const eventStyle = eventStyleResolver ? eventStyleResolver(event) : {};
|
|
591
|
+
|
|
592
|
+
// Icon ve Badge bilgilerini al
|
|
593
|
+
const iconType = eventIconsOn && eventIconResolver ? eventIconResolver(event) : null;
|
|
594
|
+
const badgeInfo = eventBadgesOn && eventBadgeResolver ? eventBadgeResolver(event) : null;
|
|
595
|
+
|
|
596
|
+
// Saatlik rezervasyon kontrolü
|
|
597
|
+
const isHourly = event.isHourly === true;
|
|
598
|
+
|
|
599
|
+
return (
|
|
600
|
+
<div
|
|
601
|
+
key={event.id}
|
|
602
|
+
className={`timeline-event timeline-event-enter ${selectedEvents.includes(event.id) ? "selected" : ""} ${isHourly ? "timeline-event-hourly" : ""}`}
|
|
603
|
+
draggable={false}
|
|
604
|
+
onContextMenu={(reactEvent) => handleRightClickEvent(event, reactEvent)}
|
|
605
|
+
onClick={(ev) => handleEventClickInternal(event, ev)}
|
|
606
|
+
onDoubleClick={(e) => handleEventDoubleClickInternal(event, e)}
|
|
607
|
+
style={{
|
|
608
|
+
left,
|
|
609
|
+
width: width, // Hesaplanan genişliği kullan
|
|
610
|
+
maxWidth: isHourly ? width : "none", // Saatlik rezervasyonlar için max genişlik sınırlaması
|
|
611
|
+
top: "5px",
|
|
612
|
+
borderTopLeftRadius: isPartialStart ? "0px" : "20px",
|
|
613
|
+
borderBottomLeftRadius: isPartialStart ? "0px" : "20px",
|
|
614
|
+
borderTopRightRadius: isPartialEnd ? "0px" : "20px",
|
|
615
|
+
borderBottomRightRadius: isPartialEnd ? "0px" : "20px",
|
|
616
|
+
cursor: isHourly ? "default" : (mode === "extend" ? "col-resize" : "default"),
|
|
617
|
+
pointerEvents: isHourly ? "none" : "auto", // Saatlik rezervasyonlarda tıklama/etkileşim yok
|
|
618
|
+
...eventStyle, // Kullanıcı tarafından tanımlanan stiller
|
|
619
|
+
}}
|
|
620
|
+
>
|
|
621
|
+
{/* Event Badge */}
|
|
622
|
+
{badgeInfo && (
|
|
623
|
+
<EventBadge
|
|
624
|
+
text={badgeInfo.text}
|
|
625
|
+
type={badgeInfo.type || 'default'}
|
|
626
|
+
position={badgeInfo.position || 'top-right'}
|
|
627
|
+
style={badgeInfo.style}
|
|
628
|
+
/>
|
|
629
|
+
)}
|
|
630
|
+
|
|
631
|
+
{/* Drag Handle - Sol Taraf - Saatlik rezervasyonlarda gösterilmez */}
|
|
632
|
+
{eventsDragOn && mode !== "extend" && !isHourly && (
|
|
633
|
+
<div
|
|
634
|
+
className="timeline-event-drag-handle"
|
|
635
|
+
draggable={true}
|
|
636
|
+
onDragStart={(e) => {
|
|
637
|
+
if (mode === "extend") {
|
|
638
|
+
e.preventDefault();
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
e.stopPropagation();
|
|
642
|
+
|
|
643
|
+
// Tüm event elementini drag image olarak ayarla
|
|
644
|
+
const eventElement = e.currentTarget.closest('.timeline-event');
|
|
645
|
+
if (eventElement) {
|
|
646
|
+
// Mouse pozisyonunu event elementine göre hesapla
|
|
647
|
+
const eventRect = eventElement.getBoundingClientRect();
|
|
648
|
+
const handleRect = e.currentTarget.getBoundingClientRect();
|
|
649
|
+
const offsetX = handleRect.left - eventRect.left + (handleRect.width / 2);
|
|
650
|
+
const offsetY = handleRect.top - eventRect.top + (handleRect.height / 2);
|
|
651
|
+
|
|
652
|
+
// Geçici bir görüntü oluştur
|
|
653
|
+
const dragImage = eventElement.cloneNode(true);
|
|
654
|
+
dragImage.style.position = 'absolute';
|
|
655
|
+
dragImage.style.top = '-1000px';
|
|
656
|
+
dragImage.style.left = '-1000px';
|
|
657
|
+
dragImage.style.opacity = '0.8';
|
|
658
|
+
dragImage.style.pointerEvents = 'none';
|
|
659
|
+
dragImage.style.transform = 'none';
|
|
660
|
+
dragImage.style.width = eventRect.width + 'px';
|
|
661
|
+
document.body.appendChild(dragImage);
|
|
662
|
+
|
|
663
|
+
// Drag image'i ayarla
|
|
664
|
+
e.dataTransfer.setDragImage(dragImage, offsetX, offsetY);
|
|
665
|
+
|
|
666
|
+
// Geçici elementi temizle
|
|
667
|
+
setTimeout(() => {
|
|
668
|
+
if (document.body.contains(dragImage)) {
|
|
669
|
+
document.body.removeChild(dragImage);
|
|
670
|
+
}
|
|
671
|
+
}, 0);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
handleDragStartSafe(e, event.id);
|
|
675
|
+
}}
|
|
676
|
+
onDragEnd={(e) => {
|
|
677
|
+
if (mode === "extend") {
|
|
678
|
+
e.preventDefault();
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
handleDragEndSafe(e);
|
|
682
|
+
}}
|
|
683
|
+
onMouseDown={(e) => {
|
|
684
|
+
e.stopPropagation();
|
|
685
|
+
}}
|
|
686
|
+
></div>
|
|
687
|
+
)}
|
|
688
|
+
{/* Event Icon - Title'dan önce */}
|
|
689
|
+
{iconType && <EventIcon type={iconType} />}
|
|
690
|
+
<span className="timeline-event-title">
|
|
691
|
+
{event.title}
|
|
692
|
+
</span>
|
|
693
|
+
{/* Extend Handle - Saatlik rezervasyonlarda gösterilmez */}
|
|
694
|
+
{eventsExtendOn && !isHourly && (
|
|
695
|
+
<div
|
|
696
|
+
className="timeline-event-extend-handle"
|
|
697
|
+
onMouseDown={(mouseEvent) => {
|
|
698
|
+
mouseEvent.stopPropagation();
|
|
699
|
+
handleMouseDownExtend(mouseEvent, event);
|
|
700
|
+
}}
|
|
701
|
+
></div>
|
|
702
|
+
)}
|
|
703
|
+
</div>
|
|
704
|
+
);
|
|
705
|
+
})}
|
|
706
|
+
|
|
707
|
+
{/* Geçici (yeni) event */}
|
|
708
|
+
{tempEvent && tempEvent.resourceId === resource.id && (
|
|
709
|
+
<div
|
|
710
|
+
className="timeline-temp-event"
|
|
711
|
+
style={{
|
|
712
|
+
...calculatePosition(tempEvent, dates),
|
|
713
|
+
...tempEventStyle, // Kullanıcının geçtiği stiller
|
|
714
|
+
}}
|
|
715
|
+
>
|
|
716
|
+
{tempEvent.title}
|
|
717
|
+
</div>
|
|
718
|
+
)}
|
|
719
|
+
|
|
720
|
+
{/* Tarih Hücreleri */}
|
|
721
|
+
{dates.map((dateObj, colIndex) => {
|
|
722
|
+
// Geçmiş tarih kontrolü
|
|
723
|
+
let isPastDate = false;
|
|
724
|
+
if (preventPastEvents && minDate) {
|
|
725
|
+
const cellDate = parseDate(dateObj.fullDate);
|
|
726
|
+
const minDateObj = parseDate(minDate);
|
|
727
|
+
const cellDateOnly = new Date(cellDate.getFullYear(), cellDate.getMonth(), cellDate.getDate());
|
|
728
|
+
const minDateOnly = new Date(minDateObj.getFullYear(), minDateObj.getMonth(), minDateObj.getDate());
|
|
729
|
+
isPastDate = cellDateOnly < minDateOnly;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Hafta sonu kontrolü
|
|
733
|
+
let isWeekend = false;
|
|
734
|
+
if (highlightWeekends) {
|
|
735
|
+
const cellDate = parseDate(dateObj.fullDate);
|
|
736
|
+
const dayOfWeek = cellDate.getDay(); // 0 = Pazar, 6 = Cumartesi
|
|
737
|
+
isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return (
|
|
741
|
+
<div
|
|
742
|
+
key={`cell-${groupIndex}-${rowIndex}-${colIndex}`}
|
|
743
|
+
className={`timeline-cell ${
|
|
744
|
+
isCellSelected(resource.id, dateObj) ? "selected" : ""
|
|
745
|
+
} ${isPastDate ? "timeline-cell-past" : ""} ${
|
|
746
|
+
isWeekend ? "timeline-cell-weekend" : ""
|
|
747
|
+
}`}
|
|
748
|
+
data-date={JSON.stringify(dateObj)}
|
|
749
|
+
data-resource-id={resource.id}
|
|
750
|
+
onMouseDown={(e) => {
|
|
751
|
+
// Sağ tıklamayı engelle (sadece sol tık ile event oluştur)
|
|
752
|
+
if (e.button === 2 || e.which === 3) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (!isPastDate) {
|
|
756
|
+
handleCellClick(resource.id, dateObj, e);
|
|
757
|
+
}
|
|
758
|
+
}}
|
|
759
|
+
onContextMenu={(e) => {
|
|
760
|
+
e.preventDefault(); // Varsayılan context menu'yu engelle
|
|
761
|
+
e.stopPropagation(); // Event bubbling'i durdur
|
|
762
|
+
if (cellContextMenuOn) {
|
|
763
|
+
handleCellContextMenu(e, resource, dateObj);
|
|
764
|
+
}
|
|
765
|
+
}}
|
|
766
|
+
onMouseEnter={(e) => {
|
|
767
|
+
if (cellTooltipOn && cellTooltipResolver) {
|
|
768
|
+
const tooltipContent = cellTooltipResolver(resource, dateObj);
|
|
769
|
+
if (tooltipContent) {
|
|
770
|
+
// Mouse pozisyonunu kullan
|
|
771
|
+
setCellTooltip({
|
|
772
|
+
content: tooltipContent,
|
|
773
|
+
resource: resource,
|
|
774
|
+
date: dateObj,
|
|
775
|
+
});
|
|
776
|
+
setCellTooltipPosition({
|
|
777
|
+
top: e.clientY,
|
|
778
|
+
left: e.clientX,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}}
|
|
783
|
+
onMouseMove={(e) => {
|
|
784
|
+
if (cellTooltipOn && cellTooltip) {
|
|
785
|
+
// Mouse hareket ettikçe tooltip'i takip et
|
|
786
|
+
setCellTooltipPosition({
|
|
787
|
+
top: e.clientY,
|
|
788
|
+
left: e.clientX,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
}}
|
|
792
|
+
onMouseLeave={() => {
|
|
793
|
+
if (cellTooltipOn) {
|
|
794
|
+
setCellTooltip(null);
|
|
795
|
+
}
|
|
796
|
+
}}
|
|
797
|
+
onDragOver={(e) => handleDragOver(e)}
|
|
798
|
+
onDrop={(e) =>
|
|
799
|
+
handleDrop(e, resource.id, parseDate(dateObj.fullDate))
|
|
800
|
+
}
|
|
801
|
+
></div>
|
|
802
|
+
);
|
|
803
|
+
})}
|
|
804
|
+
</div>
|
|
805
|
+
);
|
|
806
|
+
})}
|
|
807
|
+
</div>
|
|
808
|
+
))}
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
{/* Tooltip vb. */}
|
|
812
|
+
{eventTooltipOn && selectedEvent && TooltipComponent && mode !== "extend" && (
|
|
813
|
+
<TooltipComponent
|
|
814
|
+
event={selectedEvent}
|
|
815
|
+
position={tooltipPosition}
|
|
816
|
+
onClose={handleCloseTooltip}
|
|
817
|
+
/>
|
|
818
|
+
)}
|
|
819
|
+
|
|
820
|
+
{/* Context Menu */}
|
|
821
|
+
{cellContextMenuOn && (
|
|
822
|
+
<ContextMenu
|
|
823
|
+
isOpen={contextMenu.isOpen}
|
|
824
|
+
position={contextMenu.position}
|
|
825
|
+
onClose={handleCloseContextMenu}
|
|
826
|
+
menuItems={cellContextMenuItems}
|
|
827
|
+
resource={contextMenu.resource}
|
|
828
|
+
date={contextMenu.date}
|
|
829
|
+
/>
|
|
830
|
+
)}
|
|
831
|
+
</div>
|
|
832
|
+
</>
|
|
833
|
+
);
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
export default TimelineContent;
|
|
837
|
+
|