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
|
@@ -1,607 +1,572 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef, useCallback } from "react";
|
|
2
|
-
import MasterHeader from "./MasterHeader";
|
|
3
|
-
import ResourcesHeader from "./ResourcesHeader";
|
|
4
|
-
import Resources from "./Resources";
|
|
5
|
-
import TimelineHeader from "./TimelineHeader";
|
|
6
|
-
import TimelineContent from "./TimelineContent";
|
|
7
|
-
import "./Timeline.css";
|
|
8
|
-
import EventTooltip from "./EventTooltip";
|
|
9
|
-
import EventDetailModal from "./EventDetailModal";
|
|
10
|
-
import DailyView from "./DailyView";
|
|
11
|
-
import { generateTimelineData } from "../../utils/timelineUtils";
|
|
12
|
-
import { useTouchGestures } from "../../hooks/useTouchGestures";
|
|
13
|
-
import useKeyboardShortcuts from "../../hooks/useKeyboardShortcuts";
|
|
14
|
-
import useEventManagement from "../../hooks/useEventManagement";
|
|
15
|
-
|
|
16
|
-
const Timeline = ({
|
|
17
|
-
resources,
|
|
18
|
-
programDate = null,
|
|
19
|
-
events = [],
|
|
20
|
-
resourceSettings = {
|
|
21
|
-
showIdAsName: false,
|
|
22
|
-
isGrouped: true,
|
|
23
|
-
isCollapsible: true,
|
|
24
|
-
},
|
|
25
|
-
indicatorOn = false,
|
|
26
|
-
dropInfo,
|
|
27
|
-
setDropInfo,
|
|
28
|
-
|
|
29
|
-
masterHeaderView = true,
|
|
30
|
-
resourceHeaderContent = "Akfa Timeline", // String veya React component olabilir
|
|
31
|
-
eventsDragOn = true,
|
|
32
|
-
eventsExtendOn = true,
|
|
33
|
-
createNewEventOn = true,
|
|
34
|
-
onDragInfo,
|
|
35
|
-
onExtendInfo,
|
|
36
|
-
onCreateEventInfo,
|
|
37
|
-
// İsteğe bağlı event tıklama callback'leri
|
|
38
|
-
onEventClick,
|
|
39
|
-
onEventRightClick,
|
|
40
|
-
|
|
41
|
-
// Yatay scroll özelliği aç/kapa
|
|
42
|
-
horizontalScrollOn = false, // Varsayılan false
|
|
43
|
-
|
|
44
|
-
dayRange = 30,
|
|
45
|
-
setDayRange,
|
|
46
|
-
themeType = "light", // Tema bilgisi varsayılan olarak light
|
|
47
|
-
|
|
48
|
-
// Zoom özelliği
|
|
49
|
-
zoomLevel = 1.0, // Zoom seviyesi (0.5 = %50, 1.0 = %100, 2.0 = %200)
|
|
50
|
-
setZoomLevel, // Zoom seviyesini değiştiren fonksiyon
|
|
51
|
-
zoomOn = true, // Zoom özelliğini aç/kapa
|
|
52
|
-
minZoomLevel = 0.5, // Minimum zoom seviyesi
|
|
53
|
-
maxZoomLevel = 3.0, // Maksimum zoom seviyesi
|
|
54
|
-
zoomStep = 0.25, // Her zoom adımında değişecek miktar
|
|
55
|
-
|
|
56
|
-
eventTooltipOn = true,
|
|
57
|
-
tooltipComponent: TooltipComponent, // Özelleştirilebilir Tooltip bileşeni
|
|
58
|
-
tempEventStyle = {},
|
|
59
|
-
eventStyleResolver = () => ({}),
|
|
60
|
-
indicatorDate = new Date(),
|
|
61
|
-
onToday,
|
|
62
|
-
onAdvance,
|
|
63
|
-
onRetreat,
|
|
64
|
-
onMonthAdvance,
|
|
65
|
-
onMonthRetreat,
|
|
66
|
-
|
|
67
|
-
// Event Alignment Mode
|
|
68
|
-
eventAlignmentMode = "center", // "center" | "full" - center: gün ortasından başlar, full: gün başından başlar
|
|
69
|
-
|
|
70
|
-
// Past Date Protection
|
|
71
|
-
preventPastEvents = false, // Geçmiş tarihlere rezervasyon oluşturmayı engelle
|
|
72
|
-
minDate = null, // Minimum tarih (eğer belirtilmezse indicatorDate kullanılır)
|
|
73
|
-
|
|
74
|
-
// Weekend Highlighting
|
|
75
|
-
highlightWeekends = false, // Hafta sonlarını farklı renkte göster
|
|
76
|
-
|
|
77
|
-
// Cell Tooltip
|
|
78
|
-
cellTooltipOn = false, // Cell tooltip'lerini aktif et
|
|
79
|
-
cellTooltipResolver = null, // (resource, date) => tooltip content döndüren fonksiyon
|
|
80
|
-
|
|
81
|
-
// Cell Context Menu
|
|
82
|
-
cellContextMenuOn = false, // Cell context menu'yu aç/kapa
|
|
83
|
-
cellContextMenuItems = [], // Context menu öğeleri: [{ id, label, icon, onClick, disabled, separator, danger, shortcut, tooltip, hidden }]
|
|
84
|
-
onCellContextMenu = null, // Context menu açıldığında çağrılacak callback: (resource, date, event) => {}
|
|
85
|
-
|
|
86
|
-
// Daily View
|
|
87
|
-
dailyViewOn = true, // Daily view özelliğini aç/kapa
|
|
88
|
-
|
|
89
|
-
// Event Icons & Badges
|
|
90
|
-
eventIconsOn = false, // Event ikonlarını göster/gizle
|
|
91
|
-
eventIconResolver = null, // (event) => icon type döndüren fonksiyon
|
|
92
|
-
eventBadgesOn = false, // Event badge'lerini göster/gizle
|
|
93
|
-
eventBadgeResolver = null, // (event) => { text, type, position } döndüren fonksiyon
|
|
94
|
-
|
|
95
|
-
// Loading State
|
|
96
|
-
isLoading = false, // Timeline yükleniyor mu?
|
|
97
|
-
loadingType = 'spinner', // 'spinner', 'dots', 'pulse'
|
|
98
|
-
|
|
99
|
-
// Event Management
|
|
100
|
-
eventManagementOn = false, // Event yönetimi özelliklerini aktif et
|
|
101
|
-
onEventDelete = null, // Event silme callback
|
|
102
|
-
onEventUpdate = null, // Event güncelleme callback
|
|
103
|
-
onEventCopy = null, // Event kopyalama callback
|
|
104
|
-
onEventPaste = null, // Event yapıştırma callback
|
|
105
|
-
|
|
106
|
-
// Keyboard Shortcuts
|
|
107
|
-
keyboardShortcutsOn = false, // Keyboard shortcuts'ları aktif et
|
|
108
|
-
keyboardShortcutsConfig = {
|
|
109
|
-
onNavigateLeft: null,
|
|
110
|
-
onNavigateRight: null,
|
|
111
|
-
onNavigateUp: null,
|
|
112
|
-
onNavigateDown: null,
|
|
113
|
-
onDelete: null,
|
|
114
|
-
onUndo: null,
|
|
115
|
-
onRedo: null,
|
|
116
|
-
onCopy: null,
|
|
117
|
-
onPaste: null,
|
|
118
|
-
},
|
|
119
|
-
keyboardShortcutsKeyMap = {}, // Özelleştirilebilir tuş haritası
|
|
120
|
-
}) => {
|
|
121
|
-
// ---------------------------------------------------------
|
|
122
|
-
// 1) timelineData oluştur (dates, monthHeaders vs.)
|
|
123
|
-
// ---------------------------------------------------------
|
|
124
|
-
const timelineData = generateTimelineData(2020, 2030); // 10 yıllık veri
|
|
125
|
-
const { dates, monthHeaders } = timelineData;
|
|
126
|
-
const [selectedDate, setSelectedDate] = useState(() => {
|
|
127
|
-
const date = programDate ? new Date(programDate) : new Date();
|
|
128
|
-
date.setDate(date.getDate() - 3); // Program tarihinden 3 gün öncesini al
|
|
129
|
-
return date;
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// ---------------------------------------------------------
|
|
133
|
-
// 2) local state
|
|
134
|
-
// ---------------------------------------------------------
|
|
135
|
-
const [collapsedGroups, setCollapsedGroups] = useState({});
|
|
136
|
-
|
|
137
|
-
// Event Management
|
|
138
|
-
const eventManagement = useEventManagement(
|
|
139
|
-
events,
|
|
140
|
-
(newEvents) => {
|
|
141
|
-
setLocalEvents(newEvents);
|
|
142
|
-
if (onEventUpdate) {
|
|
143
|
-
onEventUpdate(newEvents);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const [localEvents, setLocalEvents] = useState(events);
|
|
149
|
-
|
|
150
|
-
// Update local events when events prop changes
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
setLocalEvents(events);
|
|
153
|
-
}, [events]);
|
|
154
|
-
|
|
155
|
-
const [selectedEvent, setSelectedEvent] = useState(null);
|
|
156
|
-
const [tooltipPosition] = useState({ top: 0, left: 0 });
|
|
157
|
-
const [editingEvent, setEditingEvent] = useState(null);
|
|
158
|
-
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
159
|
-
|
|
160
|
-
// Daily View State
|
|
161
|
-
const [dailyView, setDailyView] = useState({
|
|
162
|
-
isOpen: false,
|
|
163
|
-
resource: null,
|
|
164
|
-
date: null,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// dayRange = ekranda göstermeyi istediğimiz gün/hücre sayısı (ör. 30 gün)
|
|
168
|
-
|
|
169
|
-
const [isDarkMode, setIsDarkMode] = useState(themeType === "dark");
|
|
170
|
-
|
|
171
|
-
useEffect(() => {
|
|
172
|
-
if (themeType !== undefined) {
|
|
173
|
-
setIsDarkMode(themeType === "dark");
|
|
174
|
-
}
|
|
175
|
-
}, [themeType]);
|
|
176
|
-
|
|
177
|
-
// toggleDarkMode removed - not used
|
|
178
|
-
// ---------------------------------------------------------
|
|
179
|
-
// 3) Zoom seviyesine göre hücre genişliği
|
|
180
|
-
// Container genişliği = dayRange * cellWidth
|
|
181
|
-
// ---------------------------------------------------------
|
|
182
|
-
const baseCellWidth = 56.95; // Temel hücre genişliği (zoom = 1.0 için)
|
|
183
|
-
const cellWidth = baseCellWidth * zoomLevel; // Zoom seviyesine göre hücre genişliği
|
|
184
|
-
const containerWidth = dayRange * cellWidth;
|
|
185
|
-
|
|
186
|
-
// ---------------------------------------------------------
|
|
187
|
-
// 4) Touch Gestures (Mobil desteği)
|
|
188
|
-
// ---------------------------------------------------------
|
|
189
|
-
const timelineContainerRef = useRef(null);
|
|
190
|
-
const touchGestures = useTouchGestures({
|
|
191
|
-
onSwipeLeft: () => {
|
|
192
|
-
if (onAdvance) onAdvance();
|
|
193
|
-
},
|
|
194
|
-
onSwipeRight: () => {
|
|
195
|
-
if (onRetreat) onRetreat();
|
|
196
|
-
},
|
|
197
|
-
enabled: true,
|
|
198
|
-
});
|
|
199
|
-
// örneğin dayRange=30 => containerWidth=30*56.95=1708.5 px
|
|
200
|
-
|
|
201
|
-
// ---------------------------------------------------------
|
|
202
|
-
// 4) Event Tooltip logic
|
|
203
|
-
// ---------------------------------------------------------
|
|
204
|
-
// handleEventClick removed - handled in TimelineContent
|
|
205
|
-
|
|
206
|
-
const handleCloseTooltip = () => {
|
|
207
|
-
setSelectedEvent(null);
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
// Event Management Handlers
|
|
211
|
-
const handleEventDoubleClick = (event) => {
|
|
212
|
-
if (eventManagementOn) {
|
|
213
|
-
setEditingEvent(event);
|
|
214
|
-
setIsModalOpen(true);
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const handleEventRightClickInternal = (event, e) => {
|
|
219
|
-
if (eventManagementOn) {
|
|
220
|
-
e.preventDefault();
|
|
221
|
-
eventManagement.selectEvent(event.id, false);
|
|
222
|
-
}
|
|
223
|
-
if (onEventRightClick) {
|
|
224
|
-
onEventRightClick(event, e);
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const handleEventSave = (updatedEvent) => {
|
|
229
|
-
if (eventManagementOn) {
|
|
230
|
-
const newEvents = localEvents.map((ev) =>
|
|
231
|
-
ev.id === updatedEvent.id ? updatedEvent : ev
|
|
232
|
-
);
|
|
233
|
-
eventManagement.setEvents(newEvents, true);
|
|
234
|
-
if (onEventUpdate) {
|
|
235
|
-
onEventUpdate(newEvents);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
setIsModalOpen(false);
|
|
239
|
-
setEditingEvent(null);
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const handleEventDelete = (eventId) => {
|
|
243
|
-
if (eventManagementOn) {
|
|
244
|
-
const newEvents = localEvents.filter((ev) => ev.id !== eventId);
|
|
245
|
-
eventManagement.setEvents(newEvents, true);
|
|
246
|
-
if (onEventDelete) {
|
|
247
|
-
onEventDelete([eventId]);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
setIsModalOpen(false);
|
|
251
|
-
setEditingEvent(null);
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
// ---------------------------------------------------------
|
|
255
|
-
// 5) Tarih filtreleme => filteredDates
|
|
256
|
-
// ---------------------------------------------------------
|
|
257
|
-
const startIndex = dates.findIndex((d) => d.fullDate >= selectedDate);
|
|
258
|
-
const endIndex = startIndex + dayRange;
|
|
259
|
-
const filteredDates =
|
|
260
|
-
startIndex !== -1 ? dates.slice(startIndex, Math.min(endIndex, dates.length)) : [];
|
|
261
|
-
|
|
262
|
-
const today = programDate ? new Date(programDate) : new Date();
|
|
263
|
-
today.setDate(today.getDate() - 3);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const todayIndex = filteredDates.findIndex(
|
|
268
|
-
(d) => new Date(d.fullDate).toDateString() === new Date(indicatorDate).toDateString()
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
// ---------------------------------------------------------
|
|
272
|
-
// 6) Grupları aç/kapa
|
|
273
|
-
// ---------------------------------------------------------
|
|
274
|
-
const toggleGroupCollapse = (groupName) => {
|
|
275
|
-
setCollapsedGroups((prev) => ({
|
|
276
|
-
...prev,
|
|
277
|
-
[groupName]: !prev[groupName],
|
|
278
|
-
}));
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
// ---------------------------------------------------------
|
|
282
|
-
// 7) Navigation fonksiyonları
|
|
283
|
-
// --------------------------------------------------------- const { dates, monthHeaders } = timelineData;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const handleDateChange = (newDate) => {
|
|
288
|
-
setSelectedDate(new Date(newDate));
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
const handleToday = () => {
|
|
292
|
-
const today = programDate ? new Date(programDate) : new Date();
|
|
293
|
-
today.setDate(today.getDate() - 3); // Program tarihinden 3 gün öncesini ayarla
|
|
294
|
-
setSelectedDate(today);
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
newDate
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
newDate
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
<
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
onClose={() => setDailyView({ isOpen: false, resource: null, date: null })}
|
|
574
|
-
resource={dailyView.resource}
|
|
575
|
-
date={dailyView.date}
|
|
576
|
-
events={localEvents}
|
|
577
|
-
onEventCreate={(newEvent) => {
|
|
578
|
-
const updatedEvents = [...localEvents, newEvent];
|
|
579
|
-
setLocalEvents(updatedEvents);
|
|
580
|
-
if (onEventUpdate) {
|
|
581
|
-
onEventUpdate(updatedEvents);
|
|
582
|
-
}
|
|
583
|
-
}}
|
|
584
|
-
onEventUpdate={(updatedEvent) => {
|
|
585
|
-
const updatedEvents = localEvents.map(e =>
|
|
586
|
-
e.id === updatedEvent.id ? updatedEvent : e
|
|
587
|
-
);
|
|
588
|
-
setLocalEvents(updatedEvents);
|
|
589
|
-
if (onEventUpdate) {
|
|
590
|
-
onEventUpdate(updatedEvents);
|
|
591
|
-
}
|
|
592
|
-
}}
|
|
593
|
-
onEventDelete={(eventId) => {
|
|
594
|
-
const updatedEvents = localEvents.filter(e => e.id !== eventId);
|
|
595
|
-
setLocalEvents(updatedEvents);
|
|
596
|
-
if (onEventUpdate) {
|
|
597
|
-
onEventUpdate(updatedEvents);
|
|
598
|
-
}
|
|
599
|
-
}}
|
|
600
|
-
themeType={themeType}
|
|
601
|
-
/>
|
|
602
|
-
)}
|
|
603
|
-
</div>
|
|
604
|
-
);
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
export default Timeline;
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from "react";
|
|
2
|
+
import MasterHeader from "./MasterHeader.jsx";
|
|
3
|
+
import ResourcesHeader from "./ResourcesHeader.jsx";
|
|
4
|
+
import Resources from "./Resources.jsx";
|
|
5
|
+
import TimelineHeader from "./TimelineHeader.jsx";
|
|
6
|
+
import TimelineContent from "./TimelineContent.jsx";
|
|
7
|
+
import "./Timeline.css";
|
|
8
|
+
import EventTooltip from "./EventTooltip.jsx";
|
|
9
|
+
import EventDetailModal from "./EventDetailModal.jsx";
|
|
10
|
+
import DailyView from "./DailyView.jsx";
|
|
11
|
+
import { generateTimelineData } from "../../utils/timelineUtils";
|
|
12
|
+
import { useTouchGestures } from "../../hooks/useTouchGestures";
|
|
13
|
+
import useKeyboardShortcuts from "../../hooks/useKeyboardShortcuts";
|
|
14
|
+
import useEventManagement from "../../hooks/useEventManagement";
|
|
15
|
+
|
|
16
|
+
const Timeline = ({
|
|
17
|
+
resources,
|
|
18
|
+
programDate = null,
|
|
19
|
+
events = [],
|
|
20
|
+
resourceSettings = {
|
|
21
|
+
showIdAsName: false,
|
|
22
|
+
isGrouped: true,
|
|
23
|
+
isCollapsible: true,
|
|
24
|
+
},
|
|
25
|
+
indicatorOn = false,
|
|
26
|
+
dropInfo,
|
|
27
|
+
setDropInfo,
|
|
28
|
+
|
|
29
|
+
masterHeaderView = true,
|
|
30
|
+
resourceHeaderContent = "Akfa Timeline", // String veya React component olabilir
|
|
31
|
+
eventsDragOn = true,
|
|
32
|
+
eventsExtendOn = true,
|
|
33
|
+
createNewEventOn = true,
|
|
34
|
+
onDragInfo,
|
|
35
|
+
onExtendInfo,
|
|
36
|
+
onCreateEventInfo,
|
|
37
|
+
// İsteğe bağlı event tıklama callback'leri
|
|
38
|
+
onEventClick,
|
|
39
|
+
onEventRightClick,
|
|
40
|
+
|
|
41
|
+
// Yatay scroll özelliği aç/kapa
|
|
42
|
+
horizontalScrollOn = false, // Varsayılan false
|
|
43
|
+
|
|
44
|
+
dayRange = 30,
|
|
45
|
+
setDayRange,
|
|
46
|
+
themeType = "light", // Tema bilgisi varsayılan olarak light
|
|
47
|
+
|
|
48
|
+
// Zoom özelliği
|
|
49
|
+
zoomLevel = 1.0, // Zoom seviyesi (0.5 = %50, 1.0 = %100, 2.0 = %200)
|
|
50
|
+
setZoomLevel, // Zoom seviyesini değiştiren fonksiyon
|
|
51
|
+
zoomOn = true, // Zoom özelliğini aç/kapa
|
|
52
|
+
minZoomLevel = 0.5, // Minimum zoom seviyesi
|
|
53
|
+
maxZoomLevel = 3.0, // Maksimum zoom seviyesi
|
|
54
|
+
zoomStep = 0.25, // Her zoom adımında değişecek miktar
|
|
55
|
+
|
|
56
|
+
eventTooltipOn = true,
|
|
57
|
+
tooltipComponent: TooltipComponent, // Özelleştirilebilir Tooltip bileşeni
|
|
58
|
+
tempEventStyle = {},
|
|
59
|
+
eventStyleResolver = () => ({}),
|
|
60
|
+
indicatorDate = new Date(),
|
|
61
|
+
onToday,
|
|
62
|
+
onAdvance,
|
|
63
|
+
onRetreat,
|
|
64
|
+
onMonthAdvance,
|
|
65
|
+
onMonthRetreat,
|
|
66
|
+
|
|
67
|
+
// Event Alignment Mode
|
|
68
|
+
eventAlignmentMode = "center", // "center" | "full" - center: gün ortasından başlar, full: gün başından başlar
|
|
69
|
+
|
|
70
|
+
// Past Date Protection
|
|
71
|
+
preventPastEvents = false, // Geçmiş tarihlere rezervasyon oluşturmayı engelle
|
|
72
|
+
minDate = null, // Minimum tarih (eğer belirtilmezse indicatorDate kullanılır)
|
|
73
|
+
|
|
74
|
+
// Weekend Highlighting
|
|
75
|
+
highlightWeekends = false, // Hafta sonlarını farklı renkte göster
|
|
76
|
+
|
|
77
|
+
// Cell Tooltip
|
|
78
|
+
cellTooltipOn = false, // Cell tooltip'lerini aktif et
|
|
79
|
+
cellTooltipResolver = null, // (resource, date) => tooltip content döndüren fonksiyon
|
|
80
|
+
|
|
81
|
+
// Cell Context Menu
|
|
82
|
+
cellContextMenuOn = false, // Cell context menu'yu aç/kapa
|
|
83
|
+
cellContextMenuItems = [], // Context menu öğeleri: [{ id, label, icon, onClick, disabled, separator, danger, shortcut, tooltip, hidden }]
|
|
84
|
+
onCellContextMenu = null, // Context menu açıldığında çağrılacak callback: (resource, date, event) => {}
|
|
85
|
+
|
|
86
|
+
// Daily View
|
|
87
|
+
dailyViewOn = true, // Daily view özelliğini aç/kapa
|
|
88
|
+
|
|
89
|
+
// Event Icons & Badges
|
|
90
|
+
eventIconsOn = false, // Event ikonlarını göster/gizle
|
|
91
|
+
eventIconResolver = null, // (event) => icon type döndüren fonksiyon
|
|
92
|
+
eventBadgesOn = false, // Event badge'lerini göster/gizle
|
|
93
|
+
eventBadgeResolver = null, // (event) => { text, type, position } döndüren fonksiyon
|
|
94
|
+
|
|
95
|
+
// Loading State
|
|
96
|
+
isLoading = false, // Timeline yükleniyor mu?
|
|
97
|
+
loadingType = 'spinner', // 'spinner', 'dots', 'pulse'
|
|
98
|
+
|
|
99
|
+
// Event Management
|
|
100
|
+
eventManagementOn = false, // Event yönetimi özelliklerini aktif et
|
|
101
|
+
onEventDelete = null, // Event silme callback
|
|
102
|
+
onEventUpdate = null, // Event güncelleme callback
|
|
103
|
+
onEventCopy = null, // Event kopyalama callback
|
|
104
|
+
onEventPaste = null, // Event yapıştırma callback
|
|
105
|
+
|
|
106
|
+
// Keyboard Shortcuts
|
|
107
|
+
keyboardShortcutsOn = false, // Keyboard shortcuts'ları aktif et
|
|
108
|
+
keyboardShortcutsConfig = {
|
|
109
|
+
onNavigateLeft: null,
|
|
110
|
+
onNavigateRight: null,
|
|
111
|
+
onNavigateUp: null,
|
|
112
|
+
onNavigateDown: null,
|
|
113
|
+
onDelete: null,
|
|
114
|
+
onUndo: null,
|
|
115
|
+
onRedo: null,
|
|
116
|
+
onCopy: null,
|
|
117
|
+
onPaste: null,
|
|
118
|
+
},
|
|
119
|
+
keyboardShortcutsKeyMap = {}, // Özelleştirilebilir tuş haritası
|
|
120
|
+
}) => {
|
|
121
|
+
// ---------------------------------------------------------
|
|
122
|
+
// 1) timelineData oluştur (dates, monthHeaders vs.)
|
|
123
|
+
// ---------------------------------------------------------
|
|
124
|
+
const timelineData = generateTimelineData(2020, 2030); // 10 yıllık veri
|
|
125
|
+
const { dates, monthHeaders } = timelineData;
|
|
126
|
+
const [selectedDate, setSelectedDate] = useState(() => {
|
|
127
|
+
const date = programDate ? new Date(programDate) : new Date();
|
|
128
|
+
date.setDate(date.getDate() - 3); // Program tarihinden 3 gün öncesini al
|
|
129
|
+
return date;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ---------------------------------------------------------
|
|
133
|
+
// 2) local state
|
|
134
|
+
// ---------------------------------------------------------
|
|
135
|
+
const [collapsedGroups, setCollapsedGroups] = useState({});
|
|
136
|
+
|
|
137
|
+
// Event Management
|
|
138
|
+
const eventManagement = useEventManagement(
|
|
139
|
+
events,
|
|
140
|
+
(newEvents) => {
|
|
141
|
+
setLocalEvents(newEvents);
|
|
142
|
+
if (onEventUpdate) {
|
|
143
|
+
onEventUpdate(newEvents);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const [localEvents, setLocalEvents] = useState(events);
|
|
149
|
+
|
|
150
|
+
// Update local events when events prop changes
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
setLocalEvents(events);
|
|
153
|
+
}, [events]);
|
|
154
|
+
|
|
155
|
+
const [selectedEvent, setSelectedEvent] = useState(null);
|
|
156
|
+
const [tooltipPosition] = useState({ top: 0, left: 0 });
|
|
157
|
+
const [editingEvent, setEditingEvent] = useState(null);
|
|
158
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
159
|
+
|
|
160
|
+
// Daily View State
|
|
161
|
+
const [dailyView, setDailyView] = useState({
|
|
162
|
+
isOpen: false,
|
|
163
|
+
resource: null,
|
|
164
|
+
date: null,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// dayRange = ekranda göstermeyi istediğimiz gün/hücre sayısı (ör. 30 gün)
|
|
168
|
+
|
|
169
|
+
const [isDarkMode, setIsDarkMode] = useState(themeType === "dark");
|
|
170
|
+
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (themeType !== undefined) {
|
|
173
|
+
setIsDarkMode(themeType === "dark");
|
|
174
|
+
}
|
|
175
|
+
}, [themeType]);
|
|
176
|
+
|
|
177
|
+
// toggleDarkMode removed - not used
|
|
178
|
+
// ---------------------------------------------------------
|
|
179
|
+
// 3) Zoom seviyesine göre hücre genişliği
|
|
180
|
+
// Container genişliği = dayRange * cellWidth
|
|
181
|
+
// ---------------------------------------------------------
|
|
182
|
+
const baseCellWidth = 56.95; // Temel hücre genişliği (zoom = 1.0 için)
|
|
183
|
+
const cellWidth = baseCellWidth * zoomLevel; // Zoom seviyesine göre hücre genişliği
|
|
184
|
+
const containerWidth = dayRange * cellWidth;
|
|
185
|
+
|
|
186
|
+
// ---------------------------------------------------------
|
|
187
|
+
// 4) Touch Gestures (Mobil desteği)
|
|
188
|
+
// ---------------------------------------------------------
|
|
189
|
+
const timelineContainerRef = useRef(null);
|
|
190
|
+
const touchGestures = useTouchGestures({
|
|
191
|
+
onSwipeLeft: () => {
|
|
192
|
+
if (onAdvance) onAdvance();
|
|
193
|
+
},
|
|
194
|
+
onSwipeRight: () => {
|
|
195
|
+
if (onRetreat) onRetreat();
|
|
196
|
+
},
|
|
197
|
+
enabled: true,
|
|
198
|
+
});
|
|
199
|
+
// örneğin dayRange=30 => containerWidth=30*56.95=1708.5 px
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------
|
|
202
|
+
// 4) Event Tooltip logic
|
|
203
|
+
// ---------------------------------------------------------
|
|
204
|
+
// handleEventClick removed - handled in TimelineContent
|
|
205
|
+
|
|
206
|
+
const handleCloseTooltip = () => {
|
|
207
|
+
setSelectedEvent(null);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Event Management Handlers
|
|
211
|
+
const handleEventDoubleClick = (event) => {
|
|
212
|
+
if (eventManagementOn) {
|
|
213
|
+
setEditingEvent(event);
|
|
214
|
+
setIsModalOpen(true);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const handleEventRightClickInternal = (event, e) => {
|
|
219
|
+
if (eventManagementOn) {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
eventManagement.selectEvent(event.id, false);
|
|
222
|
+
}
|
|
223
|
+
if (onEventRightClick) {
|
|
224
|
+
onEventRightClick(event, e);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const handleEventSave = (updatedEvent) => {
|
|
229
|
+
if (eventManagementOn) {
|
|
230
|
+
const newEvents = localEvents.map((ev) =>
|
|
231
|
+
ev.id === updatedEvent.id ? updatedEvent : ev
|
|
232
|
+
);
|
|
233
|
+
eventManagement.setEvents(newEvents, true);
|
|
234
|
+
if (onEventUpdate) {
|
|
235
|
+
onEventUpdate(newEvents);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
setIsModalOpen(false);
|
|
239
|
+
setEditingEvent(null);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const handleEventDelete = (eventId) => {
|
|
243
|
+
if (eventManagementOn) {
|
|
244
|
+
const newEvents = localEvents.filter((ev) => ev.id !== eventId);
|
|
245
|
+
eventManagement.setEvents(newEvents, true);
|
|
246
|
+
if (onEventDelete) {
|
|
247
|
+
onEventDelete([eventId]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
setIsModalOpen(false);
|
|
251
|
+
setEditingEvent(null);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------
|
|
255
|
+
// 5) Tarih filtreleme => filteredDates
|
|
256
|
+
// ---------------------------------------------------------
|
|
257
|
+
const startIndex = dates.findIndex((d) => d.fullDate >= selectedDate);
|
|
258
|
+
const endIndex = startIndex + dayRange;
|
|
259
|
+
const filteredDates =
|
|
260
|
+
startIndex !== -1 ? dates.slice(startIndex, Math.min(endIndex, dates.length)) : [];
|
|
261
|
+
|
|
262
|
+
const today = programDate ? new Date(programDate) : new Date();
|
|
263
|
+
today.setDate(today.getDate() - 3);
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
const todayIndex = filteredDates.findIndex(
|
|
268
|
+
(d) => new Date(d.fullDate).toDateString() === new Date(indicatorDate).toDateString()
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// ---------------------------------------------------------
|
|
272
|
+
// 6) Grupları aç/kapa
|
|
273
|
+
// ---------------------------------------------------------
|
|
274
|
+
const toggleGroupCollapse = (groupName) => {
|
|
275
|
+
setCollapsedGroups((prev) => ({
|
|
276
|
+
...prev,
|
|
277
|
+
[groupName]: !prev[groupName],
|
|
278
|
+
}));
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// ---------------------------------------------------------
|
|
282
|
+
// 7) Navigation fonksiyonları
|
|
283
|
+
// --------------------------------------------------------- const { dates, monthHeaders } = timelineData;
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
const handleDateChange = (newDate) => {
|
|
288
|
+
setSelectedDate(new Date(newDate));
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const handleToday = () => {
|
|
292
|
+
const today = programDate ? new Date(programDate) : new Date();
|
|
293
|
+
today.setDate(today.getDate() - 3); // Program tarihinden 3 gün öncesini ayarla
|
|
294
|
+
setSelectedDate(today);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
const handleAdvance = () => {
|
|
299
|
+
setSelectedDate((prev) => new Date(prev.getTime() + 5 * 24 * 60 * 60 * 1000));
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const handleRetreat = () => {
|
|
303
|
+
setSelectedDate((prev) => new Date(prev.getTime() - 5 * 24 * 60 * 60 * 1000));
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const handleMonthAdvance = () => {
|
|
307
|
+
setSelectedDate((prev) => {
|
|
308
|
+
const newDate = new Date(prev);
|
|
309
|
+
newDate.setMonth(newDate.getMonth() + 1);
|
|
310
|
+
return newDate;
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const handleMonthRetreat = () => {
|
|
315
|
+
setSelectedDate((prev) => {
|
|
316
|
+
const newDate = new Date(prev);
|
|
317
|
+
newDate.setMonth(newDate.getMonth() - 1);
|
|
318
|
+
return newDate;
|
|
319
|
+
});
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// ---------------------------------------------------------
|
|
323
|
+
// 8) Dark Mode
|
|
324
|
+
// ---------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
// ---------------------------------------------------------
|
|
328
|
+
// 8.5) Zoom handlers
|
|
329
|
+
// ---------------------------------------------------------
|
|
330
|
+
const handleZoomIn = useCallback(() => {
|
|
331
|
+
if (setZoomLevel && zoomOn) {
|
|
332
|
+
setZoomLevel((prev) => Math.min(maxZoomLevel, prev + zoomStep));
|
|
333
|
+
}
|
|
334
|
+
}, [setZoomLevel, zoomOn, maxZoomLevel, zoomStep]);
|
|
335
|
+
|
|
336
|
+
const handleZoomOut = useCallback(() => {
|
|
337
|
+
if (setZoomLevel && zoomOn) {
|
|
338
|
+
setZoomLevel((prev) => Math.max(minZoomLevel, prev - zoomStep));
|
|
339
|
+
}
|
|
340
|
+
}, [setZoomLevel, zoomOn, minZoomLevel, zoomStep]);
|
|
341
|
+
|
|
342
|
+
// ---------------------------------------------------------
|
|
343
|
+
// 8.6) Keyboard Shortcuts
|
|
344
|
+
// ---------------------------------------------------------
|
|
345
|
+
useKeyboardShortcuts({
|
|
346
|
+
enabled: keyboardShortcutsOn,
|
|
347
|
+
onNavigateLeft: keyboardShortcutsConfig.onNavigateLeft || onRetreat,
|
|
348
|
+
onNavigateRight: keyboardShortcutsConfig.onNavigateRight || onAdvance,
|
|
349
|
+
onNavigateUp: keyboardShortcutsConfig.onNavigateUp || (() => console.log("Navigate Up")),
|
|
350
|
+
onNavigateDown: keyboardShortcutsConfig.onNavigateDown || (() => console.log("Navigate Down")),
|
|
351
|
+
onDelete: keyboardShortcutsConfig.onDelete || (eventManagementOn ? eventManagement.deleteSelectedEvents : null),
|
|
352
|
+
onUndo: keyboardShortcutsConfig.onUndo || (eventManagementOn ? eventManagement.undo : null),
|
|
353
|
+
onRedo: keyboardShortcutsConfig.onRedo || (eventManagementOn ? eventManagement.redo : null),
|
|
354
|
+
onCopy: keyboardShortcutsConfig.onCopy || (eventManagementOn ? eventManagement.copySelectedEvents : null),
|
|
355
|
+
onPaste: keyboardShortcutsConfig.onPaste || null,
|
|
356
|
+
onZoomIn: keyboardShortcutsConfig.onZoomIn || (zoomOn ? handleZoomIn : null),
|
|
357
|
+
onZoomOut: keyboardShortcutsConfig.onZoomOut || (zoomOn ? handleZoomOut : null),
|
|
358
|
+
keyMap: keyboardShortcutsKeyMap,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// ---------------------------------------------------------
|
|
362
|
+
// 9) Ay başlıklarını filtrele
|
|
363
|
+
// ---------------------------------------------------------
|
|
364
|
+
const filteredMonthHeaders = monthHeaders
|
|
365
|
+
.map((header) => {
|
|
366
|
+
const adjustedStartIndex = Math.max(header.startIndex, startIndex);
|
|
367
|
+
const adjustedEndIndex = Math.min(header.endIndex, endIndex - 1);
|
|
368
|
+
return {
|
|
369
|
+
...header,
|
|
370
|
+
startIndex: adjustedStartIndex,
|
|
371
|
+
endIndex: adjustedEndIndex,
|
|
372
|
+
};
|
|
373
|
+
})
|
|
374
|
+
.filter((header) => header.startIndex <= header.endIndex);
|
|
375
|
+
|
|
376
|
+
// ---------------------------------------------------------
|
|
377
|
+
// 10) Return
|
|
378
|
+
// ---------------------------------------------------------
|
|
379
|
+
return (
|
|
380
|
+
<div
|
|
381
|
+
className={`timeline-container ${isDarkMode ? "dark-mode" : ""}`}
|
|
382
|
+
ref={timelineContainerRef}
|
|
383
|
+
{...touchGestures}
|
|
384
|
+
>
|
|
385
|
+
{/* Üst kısım: MasterHeader */}
|
|
386
|
+
{masterHeaderView && (
|
|
387
|
+
<div className="timeline-master-header">
|
|
388
|
+
<MasterHeader
|
|
389
|
+
selectedDate={selectedDate} // Seçili tarihi gönder
|
|
390
|
+
onDateSelect={handleDateChange}
|
|
391
|
+
onToday={handleToday}
|
|
392
|
+
onAdvance={handleAdvance}
|
|
393
|
+
onRetreat={handleRetreat}
|
|
394
|
+
onMonthAdvance={handleMonthAdvance}
|
|
395
|
+
onMonthRetreat={handleMonthRetreat}
|
|
396
|
+
dayRange={dayRange}
|
|
397
|
+
setDayRange={setDayRange}
|
|
398
|
+
zoomLevel={zoomLevel}
|
|
399
|
+
setZoomLevel={setZoomLevel}
|
|
400
|
+
zoomOn={zoomOn}
|
|
401
|
+
minZoomLevel={minZoomLevel}
|
|
402
|
+
maxZoomLevel={maxZoomLevel}
|
|
403
|
+
zoomStep={zoomStep}
|
|
404
|
+
/>
|
|
405
|
+
</div>
|
|
406
|
+
)}
|
|
407
|
+
{/* Body: Sol kısım => Resources, Sağ kısım => timeline */}
|
|
408
|
+
<div className="timeline-body">
|
|
409
|
+
<div className="timeline-resources-container"
|
|
410
|
+
style={{ overflow: "hidden"}}>
|
|
411
|
+
<ResourcesHeader content={resourceHeaderContent} />
|
|
412
|
+
<Resources
|
|
413
|
+
groupedResources={resources}
|
|
414
|
+
toggleGroupCollapse={toggleGroupCollapse}
|
|
415
|
+
collapsedGroups={collapsedGroups}
|
|
416
|
+
resourceSettings={resourceSettings}
|
|
417
|
+
/>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
<div
|
|
422
|
+
className="timeline-scrollable-container"
|
|
423
|
+
style={{
|
|
424
|
+
overflowX: horizontalScrollOn ? "auto" : "hidden",
|
|
425
|
+
}}
|
|
426
|
+
>
|
|
427
|
+
|
|
428
|
+
<div
|
|
429
|
+
className="timeline-header-content-wrapper"
|
|
430
|
+
style={{
|
|
431
|
+
width: horizontalScrollOn ? `${containerWidth}px` : "100%",
|
|
432
|
+
}}
|
|
433
|
+
>
|
|
434
|
+
<TimelineHeader
|
|
435
|
+
dates={filteredDates}
|
|
436
|
+
monthHeaders={filteredMonthHeaders}
|
|
437
|
+
highlightWeekends={highlightWeekends}
|
|
438
|
+
/>
|
|
439
|
+
|
|
440
|
+
<TimelineContent
|
|
441
|
+
groupedResources={resources}
|
|
442
|
+
dates={filteredDates}
|
|
443
|
+
collapsedGroups={collapsedGroups}
|
|
444
|
+
events={localEvents}
|
|
445
|
+
setEvents={setLocalEvents}
|
|
446
|
+
onEventClick={onEventClick}
|
|
447
|
+
onEventDoubleClick={eventManagementOn ? handleEventDoubleClick : null}
|
|
448
|
+
onEventRightClick={eventManagementOn ? handleEventRightClickInternal : onEventRightClick}
|
|
449
|
+
selectedEvents={eventManagementOn ? eventManagement.selectedEvents : []}
|
|
450
|
+
onEventSelect={eventManagementOn ? eventManagement.selectEvent : null}
|
|
451
|
+
todayIndex={todayIndex}
|
|
452
|
+
indicatorOn={indicatorOn}
|
|
453
|
+
resourceSettings={resourceSettings}
|
|
454
|
+
toggleGroupCollapse={toggleGroupCollapse}
|
|
455
|
+
setDropInfo={setDropInfo}
|
|
456
|
+
eventsDragOn={eventsDragOn}
|
|
457
|
+
eventsExtendOn={eventsExtendOn}
|
|
458
|
+
createNewEventOn={createNewEventOn}
|
|
459
|
+
onDragInfo={onDragInfo}
|
|
460
|
+
onExtendInfo={onExtendInfo}
|
|
461
|
+
onCreateEventInfo={onCreateEventInfo}
|
|
462
|
+
onEventRightClick={onEventRightClick}
|
|
463
|
+
eventTooltipOn={eventTooltipOn} // Tooltip kontrolü
|
|
464
|
+
tooltipComponent={TooltipComponent} // Özelleştirilebilir Tooltip bileşeni
|
|
465
|
+
tempEventStyle = {tempEventStyle}
|
|
466
|
+
eventStyleResolver={eventStyleResolver}
|
|
467
|
+
eventAlignmentMode={eventAlignmentMode}
|
|
468
|
+
preventPastEvents={preventPastEvents}
|
|
469
|
+
minDate={minDate || indicatorDate}
|
|
470
|
+
highlightWeekends={highlightWeekends}
|
|
471
|
+
cellTooltipOn={cellTooltipOn}
|
|
472
|
+
cellTooltipResolver={cellTooltipResolver}
|
|
473
|
+
cellContextMenuOn={cellContextMenuOn}
|
|
474
|
+
cellContextMenuItems={cellContextMenuItems.map(item => {
|
|
475
|
+
// Daily timeline item'ını özel handle et
|
|
476
|
+
if (item.id === 'daily-timeline' && dailyViewOn) {
|
|
477
|
+
return {
|
|
478
|
+
...item,
|
|
479
|
+
onClick: (resource, date) => {
|
|
480
|
+
setDailyView({
|
|
481
|
+
isOpen: true,
|
|
482
|
+
resource,
|
|
483
|
+
date,
|
|
484
|
+
});
|
|
485
|
+
if (item.onClick) {
|
|
486
|
+
item.onClick(resource, date);
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return item;
|
|
492
|
+
})}
|
|
493
|
+
onCellContextMenu={onCellContextMenu}
|
|
494
|
+
eventIconsOn={eventIconsOn}
|
|
495
|
+
eventIconResolver={eventIconResolver}
|
|
496
|
+
eventBadgesOn={eventBadgesOn}
|
|
497
|
+
eventBadgeResolver={eventBadgeResolver}
|
|
498
|
+
isLoading={isLoading}
|
|
499
|
+
loadingType={loadingType}
|
|
500
|
+
|
|
501
|
+
/>
|
|
502
|
+
|
|
503
|
+
{/* Tooltip */}
|
|
504
|
+
{selectedEvent && (
|
|
505
|
+
<EventTooltip
|
|
506
|
+
event={selectedEvent}
|
|
507
|
+
position={tooltipPosition}
|
|
508
|
+
onClose={handleCloseTooltip}
|
|
509
|
+
onDelete={(eventId) =>
|
|
510
|
+
setLocalEvents((prev) => prev.filter((e) => e.id !== eventId))
|
|
511
|
+
}
|
|
512
|
+
/>
|
|
513
|
+
)}
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
|
|
518
|
+
{/* Event Detail Modal */}
|
|
519
|
+
{eventManagementOn && (
|
|
520
|
+
<EventDetailModal
|
|
521
|
+
event={editingEvent}
|
|
522
|
+
isOpen={isModalOpen}
|
|
523
|
+
onClose={() => {
|
|
524
|
+
setIsModalOpen(false);
|
|
525
|
+
setEditingEvent(null);
|
|
526
|
+
}}
|
|
527
|
+
onSave={handleEventSave}
|
|
528
|
+
onDelete={handleEventDelete}
|
|
529
|
+
resources={resources.flatMap((group) => group.resources || [])}
|
|
530
|
+
/>
|
|
531
|
+
)}
|
|
532
|
+
|
|
533
|
+
{/* Daily View */}
|
|
534
|
+
{dailyViewOn && (
|
|
535
|
+
<DailyView
|
|
536
|
+
isOpen={dailyView.isOpen}
|
|
537
|
+
onClose={() => setDailyView({ isOpen: false, resource: null, date: null })}
|
|
538
|
+
resource={dailyView.resource}
|
|
539
|
+
date={dailyView.date}
|
|
540
|
+
events={localEvents}
|
|
541
|
+
onEventCreate={(newEvent) => {
|
|
542
|
+
const updatedEvents = [...localEvents, newEvent];
|
|
543
|
+
setLocalEvents(updatedEvents);
|
|
544
|
+
if (onEventUpdate) {
|
|
545
|
+
onEventUpdate(updatedEvents);
|
|
546
|
+
}
|
|
547
|
+
}}
|
|
548
|
+
onEventUpdate={(updatedEvent) => {
|
|
549
|
+
const updatedEvents = localEvents.map(e =>
|
|
550
|
+
e.id === updatedEvent.id ? updatedEvent : e
|
|
551
|
+
);
|
|
552
|
+
setLocalEvents(updatedEvents);
|
|
553
|
+
if (onEventUpdate) {
|
|
554
|
+
onEventUpdate(updatedEvents);
|
|
555
|
+
}
|
|
556
|
+
}}
|
|
557
|
+
onEventDelete={(eventId) => {
|
|
558
|
+
const updatedEvents = localEvents.filter(e => e.id !== eventId);
|
|
559
|
+
setLocalEvents(updatedEvents);
|
|
560
|
+
if (onEventUpdate) {
|
|
561
|
+
onEventUpdate(updatedEvents);
|
|
562
|
+
}
|
|
563
|
+
}}
|
|
564
|
+
themeType={themeType}
|
|
565
|
+
/>
|
|
566
|
+
)}
|
|
567
|
+
</div>
|
|
568
|
+
);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
export default Timeline;
|
|
572
|
+
|