akfatimeline 1.0.6 → 1.2.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +98 -35
  2. package/dist/Timeline.js +4309 -1677
  3. package/dist/components/Timeline/AutocompleteSelect.js +150 -0
  4. package/dist/components/Timeline/ContextMenu.js +149 -0
  5. package/dist/components/Timeline/DailyView.js +255 -0
  6. package/dist/components/Timeline/DatePickerComponent.js +13 -0
  7. package/{public/dist/dist → dist}/components/Timeline/DragAndDropHandler.js +34 -34
  8. package/dist/components/Timeline/EventBadge.js +26 -0
  9. package/dist/components/Timeline/EventDetailModal.js +138 -0
  10. package/dist/components/Timeline/EventIcon.js +47 -0
  11. package/dist/{dist/components → components}/Timeline/EventTooltip.js +206 -206
  12. package/dist/components/Timeline/FilterPanel.js +179 -0
  13. package/dist/{dist/components → components}/Timeline/Indicator.js +26 -26
  14. package/dist/components/Timeline/LoadingSpinner.js +48 -0
  15. package/dist/{dist/components → components}/Timeline/MasterHeader.js +104 -68
  16. package/{public/dist/dist → dist}/components/Timeline/Resources.js +53 -53
  17. package/dist/{dist/components → components}/Timeline/ResourcesHeader.js +14 -14
  18. package/dist/components/Timeline/Timeline.css +2491 -0
  19. package/dist/components/Timeline/Timeline.js +607 -0
  20. package/dist/{dist/components → components}/Timeline/TimelineCell.js +8 -8
  21. package/dist/components/Timeline/TimelineContent.js +838 -0
  22. package/{public/dist/dist → dist}/components/Timeline/TimelineEvents.js +114 -114
  23. package/dist/components/Timeline/TimelineHeader.js +54 -0
  24. package/{public/dist/dist → dist}/components/Timeline/TimelineMonthContainer.js +29 -29
  25. package/{public/dist/dist → dist}/components/Timeline/TimelineResources.js +16 -16
  26. package/{public/dist/dist → dist}/hooks/useDragAndDrop.js +80 -80
  27. package/dist/{dist/hooks → hooks}/useEventDragDrop.js +126 -126
  28. package/dist/hooks/useEventManagement.js +173 -0
  29. package/dist/hooks/useEventSelection.js +82 -0
  30. package/{public/dist/dist → dist}/hooks/useExtendEvent.js +28 -28
  31. package/dist/hooks/useKeyboardShortcuts.js +158 -0
  32. package/dist/hooks/useTouchGestures.js +90 -0
  33. package/dist/utils/conflictUtils.js +105 -0
  34. package/dist/{dist/utils → utils}/dateUtils.js +36 -36
  35. package/dist/{dist/utils → utils}/filterTimelineData.js +20 -20
  36. package/dist/utils/filterUtils.js +106 -0
  37. package/dist/utils/timeUtils.js +179 -0
  38. package/dist/{dist/utils → utils}/timelineUtils.js +39 -39
  39. package/dist/utils/viewModeUtils.js +54 -0
  40. package/package.json +89 -19
  41. package/src/App.js +300 -19
  42. package/src/components/Timeline/AutocompleteSelect.js +150 -0
  43. package/src/components/Timeline/ContextMenu.js +149 -0
  44. package/src/components/Timeline/DailyView.js +255 -0
  45. package/src/components/Timeline/DatePickerComponent.js +13 -17
  46. package/src/components/Timeline/DragAndDropHandler.js +34 -34
  47. package/src/components/Timeline/EventBadge.js +26 -0
  48. package/src/components/Timeline/EventDetailModal.js +138 -0
  49. package/src/components/Timeline/EventIcon.js +47 -0
  50. package/src/components/Timeline/EventTooltip.js +206 -206
  51. package/src/components/Timeline/FilterPanel.js +179 -0
  52. package/src/components/Timeline/Indicator.js +26 -26
  53. package/src/components/Timeline/LoadingSpinner.js +48 -0
  54. package/src/components/Timeline/MasterHeader.js +104 -68
  55. package/src/components/Timeline/Resources.js +53 -53
  56. package/src/components/Timeline/ResourcesHeader.js +14 -14
  57. package/src/components/Timeline/Timeline.css +2491 -616
  58. package/src/components/Timeline/Timeline.js +607 -309
  59. package/src/components/Timeline/TimelineCell.js +8 -8
  60. package/src/components/Timeline/TimelineContent.js +838 -446
  61. package/src/components/Timeline/TimelineEvents.js +114 -114
  62. package/src/components/Timeline/TimelineHeader.js +54 -43
  63. package/src/components/Timeline/TimelineMonthContainer.js +29 -29
  64. package/src/components/Timeline/TimelineResources.js +16 -16
  65. package/src/demo.css +4 -0
  66. package/src/hooks/useDragAndDrop.js +80 -80
  67. package/src/hooks/useEventDragDrop.js +126 -126
  68. package/src/hooks/useEventManagement.js +173 -0
  69. package/src/hooks/useEventSelection.js +82 -0
  70. package/src/hooks/useExtendEvent.js +28 -28
  71. package/src/hooks/useKeyboardShortcuts.js +158 -0
  72. package/src/hooks/useTouchGestures.js +90 -0
  73. package/src/index.js +1 -7
  74. package/src/library.js +26 -0
  75. package/src/utils/conflictUtils.js +105 -0
  76. package/src/utils/dateUtils.js +36 -36
  77. package/src/utils/filterTimelineData.js +20 -20
  78. package/src/utils/filterUtils.js +106 -0
  79. package/src/utils/timeUtils.js +179 -0
  80. package/src/utils/timelineUtils.js +39 -39
  81. package/src/utils/viewModeUtils.js +54 -0
  82. package/.babelrc +0 -6
  83. package/babel.config.json +0 -4
  84. package/dist/dist/components/Timeline/DatePickerComponent.js +0 -17
  85. package/dist/dist/components/Timeline/DragAndDropHandler.js +0 -35
  86. package/dist/dist/components/Timeline/Resources.js +0 -53
  87. package/dist/dist/components/Timeline/Timeline.css +0 -616
  88. package/dist/dist/components/Timeline/Timeline.js +0 -309
  89. package/dist/dist/components/Timeline/TimelineContent.js +0 -446
  90. package/dist/dist/components/Timeline/TimelineEvents.js +0 -114
  91. package/dist/dist/components/Timeline/TimelineHeader.js +0 -43
  92. package/dist/dist/components/Timeline/TimelineMonthContainer.js +0 -29
  93. package/dist/dist/components/Timeline/TimelineResources.js +0 -16
  94. package/dist/dist/hooks/useDragAndDrop.js +0 -80
  95. package/dist/dist/hooks/useExtendEvent.js +0 -28
  96. package/public/dist/Timeline.js +0 -3277
  97. package/public/dist/dist/components/Timeline/DatePickerComponent.js +0 -17
  98. package/public/dist/dist/components/Timeline/EventTooltip.js +0 -206
  99. package/public/dist/dist/components/Timeline/Indicator.js +0 -29
  100. package/public/dist/dist/components/Timeline/MasterHeader.js +0 -68
  101. package/public/dist/dist/components/Timeline/ResourcesHeader.js +0 -14
  102. package/public/dist/dist/components/Timeline/Timeline.css +0 -616
  103. package/public/dist/dist/components/Timeline/Timeline.js +0 -304
  104. package/public/dist/dist/components/Timeline/TimelineCell.js +0 -8
  105. package/public/dist/dist/components/Timeline/TimelineContent.js +0 -447
  106. package/public/dist/dist/components/Timeline/TimelineHeader.js +0 -43
  107. package/public/dist/dist/hooks/useEventDragDrop.js +0 -126
  108. package/public/dist/dist/utils/HorizontalVirtualScroll.js +0 -0
  109. package/public/dist/dist/utils/dateUtils.js +0 -36
  110. package/public/dist/dist/utils/filterTimelineData.js +0 -21
  111. package/public/dist/dist/utils/timelineUtils.js +0 -40
  112. package/public/favicon.ico +0 -0
  113. package/public/index kutuphane /304/261c/304/261n.html" +0 -43
  114. package/public/index tasarim icin.html +0 -20
  115. package/public/index.html +0 -43
  116. package/public/logo192.png +0 -0
  117. package/public/logo512.png +0 -0
  118. package/public/manifest.json +0 -25
  119. package/public/robots.txt +0 -3
  120. package/src/App.css +0 -38
  121. package/src/App.test.js +0 -8
  122. package/src/dist/Timeline.js +0 -277
  123. package/src/index.css +0 -13
  124. package/src/logo.svg +0 -1
  125. package/src/reportWebVitals.js +0 -13
  126. package/src/setupTests.js +0 -5
  127. package/webpack.config.js +0 -49
  128. /package/dist/{dist/utils → utils}/HorizontalVirtualScroll.js +0 -0
@@ -1,309 +1,607 @@
1
- import React, { useState, useEffect } 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 { generateTimelineData } from "../../utils/timelineUtils";
10
-
11
- const Timeline = ({
12
- resources,
13
- programDate = null,
14
- events = [],
15
- resourceSettings = {
16
- showIdAsName: false,
17
- isGrouped: true,
18
- isCollapsible: true,
19
- },
20
- indicatorOn = false,
21
- dropInfo,
22
- setDropInfo,
23
-
24
- masterHeaderView = true,
25
- resourceHeaderContent = "Akfa Timeline",
26
- eventsDragOn = true,
27
- eventsExtendOn = true,
28
- createNewEventOn = true,
29
- onDragInfo,
30
- onExtendInfo,
31
- onCreateEventInfo,
32
- // İsteğe bağlı event tıklama callback'leri
33
- onEventClick,
34
- onEventRightClick,
35
-
36
- // Yatay scroll özelliği aç/kapa
37
- horizontalScrollOn = false, // Varsayılan false
38
-
39
- dayRange = 30,
40
- setDayRange,
41
- themeType = "light", // Tema bilgisi varsayılan olarak light
42
-
43
- eventTooltipOn = true,
44
- tooltipComponent: TooltipComponent, // Özelleştirilebilir Tooltip bileşeni
45
- tempEventStyle = {},
46
- eventStyleResolver = () => ({}),
47
- indicatorDate = new Date(),
48
- onToday,
49
- onAdvance,
50
- onRetreat,
51
- onMonthAdvance,
52
- onMonthRetreat,
53
- }) => {
54
- // ---------------------------------------------------------
55
- // 1) timelineData oluştur (dates, monthHeaders vs.)
56
- // ---------------------------------------------------------
57
- const timelineData = generateTimelineData(2020, 2030); // 10 yıllık veri
58
- const { dates, monthHeaders } = timelineData;
59
- const [selectedDate, setSelectedDate] = useState(() => {
60
- const date = programDate ? new Date(programDate) : new Date();
61
- date.setDate(date.getDate() - 3); // Program tarihinden 3 gün öncesini al
62
- return date;
63
- });
64
-
65
- // ---------------------------------------------------------
66
- // 2) local state
67
- // ---------------------------------------------------------
68
- const [collapsedGroups, setCollapsedGroups] = useState({});
69
-
70
- const [localEvents, setLocalEvents] = useState(events);
71
-
72
- const [selectedEvent, setSelectedEvent] = useState(null);
73
- const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
74
-
75
- // dayRange = ekranda göstermeyi istediğimiz gün/hücre sayısı (ör. 30 gün)
76
-
77
- const [isDarkMode, setIsDarkMode] = useState(themeType === "dark");
78
-
79
- useEffect(() => {
80
- if (themeType !== undefined) {
81
- setIsDarkMode(themeType === "dark");
82
- }
83
- }, [themeType]);
84
-
85
- const toggleDarkMode = () => {
86
- setIsDarkMode((prev) => !prev);
87
- };
88
- // ---------------------------------------------------------
89
- // 3) Sabit hücre genişliği (örneğin 56.95 px)
90
- // Container genişliği = dayRange * cellWidth
91
- // ---------------------------------------------------------
92
- const cellWidth = 56.95; // her gün/hücre ~57 piksel
93
- const containerWidth = dayRange * cellWidth;
94
- // örneğin dayRange=30 => containerWidth=30*56.95=1708.5 px
95
-
96
- // ---------------------------------------------------------
97
- // 4) Event Tooltip logic
98
- // ---------------------------------------------------------
99
- const handleEventClick = (event, e) => {
100
- // Harici onEventClick callback'i varsa, önce onu tetikleyelim
101
- if (onEventClick) {
102
- onEventClick(event, e);
103
- }
104
- // Ardından tooltip göstermek istiyorsak:
105
- const eventElement = e.currentTarget;
106
- if (eventElement) {
107
- const rect = eventElement.getBoundingClientRect();
108
- setTooltipPosition({
109
- top: rect.top + window.scrollY,
110
- left: rect.left + rect.width / 2 + window.scrollX,
111
- });
112
- setSelectedEvent(event);
113
- }
114
- };
115
-
116
- const handleCloseTooltip = () => {
117
- setSelectedEvent(null);
118
- };
119
-
120
- // ---------------------------------------------------------
121
- // 5) Tarih filtreleme => filteredDates
122
- // ---------------------------------------------------------
123
- const startIndex = dates.findIndex((d) => d.fullDate >= selectedDate);
124
- const endIndex = startIndex + dayRange;
125
- const filteredDates =
126
- startIndex !== -1 ? dates.slice(startIndex, Math.min(endIndex, dates.length)) : [];
127
-
128
- const today = programDate ? new Date(programDate) : new Date();
129
- today.setDate(today.getDate() - 3);
130
-
131
-
132
-
133
- const todayIndex = filteredDates.findIndex(
134
- (d) => new Date(d.fullDate).toDateString() === new Date(indicatorDate).toDateString()
135
- );
136
-
137
- const totalDays = filteredDates.length;
138
-
139
- // ---------------------------------------------------------
140
- // 6) Grupları aç/kapa
141
- // ---------------------------------------------------------
142
- const toggleGroupCollapse = (groupName) => {
143
- setCollapsedGroups((prev) => ({
144
- ...prev,
145
- [groupName]: !prev[groupName],
146
- }));
147
- };
148
-
149
- // ---------------------------------------------------------
150
- // 7) Navigation fonksiyonları
151
- // --------------------------------------------------------- const { dates, monthHeaders } = timelineData;
152
-
153
-
154
-
155
- const handleDateChange = (newDate) => {
156
- setSelectedDate(new Date(newDate));
157
- };
158
-
159
- const handleToday = () => {
160
- const today = programDate ? new Date(programDate) : new Date();
161
- today.setDate(today.getDate() - 3); // Program tarihinden 3 gün öncesini ayarla
162
- setSelectedDate(today);
163
- };
164
-
165
-
166
-
167
- const handleAdvance = () => {
168
- setSelectedDate((prev) => new Date(prev.getTime() + 5 * 24 * 60 * 60 * 1000));
169
- };
170
-
171
- const handleRetreat = () => {
172
- setSelectedDate((prev) => new Date(prev.getTime() - 5 * 24 * 60 * 60 * 1000));
173
- };
174
-
175
- const handleMonthAdvance = () => {
176
- setSelectedDate((prev) => {
177
- const newDate = new Date(prev);
178
- newDate.setMonth(newDate.getMonth() + 1);
179
- return newDate;
180
- });
181
- };
182
-
183
- const handleMonthRetreat = () => {
184
- setSelectedDate((prev) => {
185
- const newDate = new Date(prev);
186
- newDate.setMonth(newDate.getMonth() - 1);
187
- return newDate;
188
- });
189
- };
190
-
191
- // ---------------------------------------------------------
192
- // 8) Dark Mode
193
- // ---------------------------------------------------------
194
-
195
-
196
- // ---------------------------------------------------------
197
- // 9) Ay başlıklarını filtrele
198
- // ---------------------------------------------------------
199
- const filteredMonthHeaders = monthHeaders
200
- .map((header) => {
201
- const adjustedStartIndex = Math.max(header.startIndex, startIndex);
202
- const adjustedEndIndex = Math.min(header.endIndex, endIndex - 1);
203
- return {
204
- ...header,
205
- startIndex: adjustedStartIndex,
206
- endIndex: adjustedEndIndex,
207
- };
208
- })
209
- .filter((header) => header.startIndex <= header.endIndex);
210
-
211
- // ---------------------------------------------------------
212
- // 10) Return
213
- // ---------------------------------------------------------
214
- return (
215
- <div className={`timeline-container ${isDarkMode ? "dark-mode" : ""}`}>
216
- {/* Üst kısım: MasterHeader */}
217
- {masterHeaderView && (
218
- <div className="timeline-master-header">
219
- <MasterHeader
220
- selectedDate={selectedDate} // Seçili tarihi gönder
221
- onDateSelect={handleDateChange}
222
- onToday={handleToday}
223
- onAdvance={handleAdvance}
224
- onRetreat={handleRetreat}
225
- onMonthAdvance={handleMonthAdvance}
226
- onMonthRetreat={handleMonthRetreat}
227
- dayRange={dayRange}
228
- setDayRange={setDayRange}
229
- />
230
- </div>
231
- )}
232
- {/* Body: Sol kısım => Resources, Sağ kısım => timeline */}
233
- <div className="timeline-body">
234
- <div className="timeline-resources-container"
235
- style={{ overflow: "hidden"}}>
236
- <ResourcesHeader content={resourceHeaderContent} />
237
- <Resources
238
- groupedResources={resources}
239
- toggleGroupCollapse={toggleGroupCollapse}
240
- collapsedGroups={collapsedGroups}
241
- resourceSettings={resourceSettings}
242
- />
243
- </div>
244
-
245
-
246
- <div
247
- className="timeline-scrollable-container"
248
- style={{
249
- overflowX: horizontalScrollOn ? "auto" : "hidden",
250
- }}
251
- >
252
-
253
- <div
254
- className="timeline-header-content-wrapper"
255
- style={{
256
- width: horizontalScrollOn ? `${containerWidth}px` : "100%",
257
- }}
258
- >
259
- <TimelineHeader
260
- dates={filteredDates}
261
- monthHeaders={filteredMonthHeaders}
262
- />
263
-
264
- <TimelineContent
265
- groupedResources={resources}
266
- dates={filteredDates}
267
- collapsedGroups={collapsedGroups}
268
- events={localEvents}
269
- setEvents={setLocalEvents}
270
- onEventClick={onEventClick}
271
-
272
- todayIndex={todayIndex}
273
- indicatorOn={indicatorOn}
274
- resourceSettings={resourceSettings}
275
- toggleGroupCollapse={toggleGroupCollapse}
276
- setDropInfo={setDropInfo}
277
- eventsDragOn={eventsDragOn}
278
- eventsExtendOn={eventsExtendOn}
279
- createNewEventOn={createNewEventOn}
280
- onDragInfo={onDragInfo}
281
- onExtendInfo={onExtendInfo}
282
- onCreateEventInfo={onCreateEventInfo}
283
- onEventRightClick={onEventRightClick}
284
- eventTooltipOn={eventTooltipOn} // Tooltip kontrolü
285
- tooltipComponent={TooltipComponent} // Özelleştirilebilir Tooltip bileşeni
286
- tempEventStyle = {tempEventStyle}
287
- eventStyleResolver={eventStyleResolver}
288
-
289
- />
290
-
291
- {/* Tooltip */}
292
- {selectedEvent && (
293
- <EventTooltip
294
- event={selectedEvent}
295
- position={tooltipPosition}
296
- onClose={handleCloseTooltip}
297
- onDelete={(eventId) =>
298
- setLocalEvents((prev) => prev.filter((e) => e.id !== eventId))
299
- }
300
- />
301
- )}
302
- </div>
303
- </div>
304
- </div>
305
- </div>
306
- );
307
- };
308
-
309
- export default Timeline;
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
+ const handleAdvance = () => {
300
+ setSelectedDate((prev) => new Date(prev.getTime() + 5 * 24 * 60 * 60 * 1000));
301
+ };
302
+
303
+ const handleRetreat = () => {
304
+ setSelectedDate((prev) => new Date(prev.getTime() - 5 * 24 * 60 * 60 * 1000));
305
+ };
306
+
307
+ const handleMonthAdvance = () => {
308
+ setSelectedDate((prev) => {
309
+ const newDate = new Date(prev);
310
+ newDate.setMonth(newDate.getMonth() + 1);
311
+ return newDate;
312
+ });
313
+ };
314
+
315
+ const handleMonthRetreat = () => {
316
+ setSelectedDate((prev) => {
317
+ const newDate = new Date(prev);
318
+ newDate.setMonth(newDate.getMonth() - 1);
319
+ return newDate;
320
+ });
321
+ };
322
+
323
+ // ---------------------------------------------------------
324
+ // 8) Dark Mode
325
+ // ---------------------------------------------------------
326
+
327
+
328
+ // ---------------------------------------------------------
329
+ // 8.5) Zoom handlers
330
+ // ---------------------------------------------------------
331
+ const handleZoomIn = useCallback(() => {
332
+ if (setZoomLevel && zoomOn) {
333
+ setZoomLevel((prev) => Math.min(maxZoomLevel, prev + zoomStep));
334
+ }
335
+ }, [setZoomLevel, zoomOn, maxZoomLevel, zoomStep]);
336
+
337
+ const handleZoomOut = useCallback(() => {
338
+ if (setZoomLevel && zoomOn) {
339
+ setZoomLevel((prev) => Math.max(minZoomLevel, prev - zoomStep));
340
+ }
341
+ }, [setZoomLevel, zoomOn, minZoomLevel, zoomStep]);
342
+
343
+ // ---------------------------------------------------------
344
+ // 8.6) Keyboard Shortcuts
345
+ // ---------------------------------------------------------
346
+ useKeyboardShortcuts({
347
+ enabled: keyboardShortcutsOn,
348
+ onNavigateLeft: keyboardShortcutsConfig.onNavigateLeft || onRetreat,
349
+ onNavigateRight: keyboardShortcutsConfig.onNavigateRight || onAdvance,
350
+ onNavigateUp: keyboardShortcutsConfig.onNavigateUp || (() => console.log("Navigate Up")),
351
+ onNavigateDown: keyboardShortcutsConfig.onNavigateDown || (() => console.log("Navigate Down")),
352
+ onDelete: keyboardShortcutsConfig.onDelete || (eventManagementOn ? eventManagement.deleteSelectedEvents : null),
353
+ onUndo: keyboardShortcutsConfig.onUndo || (eventManagementOn ? eventManagement.undo : null),
354
+ onRedo: keyboardShortcutsConfig.onRedo || (eventManagementOn ? eventManagement.redo : null),
355
+ onCopy: keyboardShortcutsConfig.onCopy || (eventManagementOn ? eventManagement.copySelectedEvents : null),
356
+ onPaste: keyboardShortcutsConfig.onPaste || null,
357
+ onZoomIn: keyboardShortcutsConfig.onZoomIn || (zoomOn ? handleZoomIn : null),
358
+ onZoomOut: keyboardShortcutsConfig.onZoomOut || (zoomOn ? handleZoomOut : null),
359
+ keyMap: keyboardShortcutsKeyMap,
360
+ });
361
+
362
+ // ---------------------------------------------------------
363
+ // 9) Ay başlıklarını filtrele
364
+ // ---------------------------------------------------------
365
+ const filteredMonthHeaders = monthHeaders
366
+ .map((header) => {
367
+ const adjustedStartIndex = Math.max(header.startIndex, startIndex);
368
+ const adjustedEndIndex = Math.min(header.endIndex, endIndex - 1);
369
+ return {
370
+ ...header,
371
+ startIndex: adjustedStartIndex,
372
+ endIndex: adjustedEndIndex,
373
+ };
374
+ })
375
+ .filter((header) => header.startIndex <= header.endIndex);
376
+
377
+ // ---------------------------------------------------------
378
+ // 10) Return
379
+ // ---------------------------------------------------------
380
+ return (
381
+ <div
382
+ className={`timeline-container ${isDarkMode ? "dark-mode" : ""}`}
383
+ ref={timelineContainerRef}
384
+ {...touchGestures}
385
+ >
386
+ {/* Üst kısım: MasterHeader */}
387
+ {masterHeaderView && (
388
+ <div className="timeline-master-header">
389
+ <MasterHeader
390
+ selectedDate={selectedDate} // Seçili tarihi gönder
391
+ onDateSelect={handleDateChange}
392
+ onToday={handleToday}
393
+ onAdvance={handleAdvance}
394
+ onRetreat={handleRetreat}
395
+ onMonthAdvance={handleMonthAdvance}
396
+ onMonthRetreat={handleMonthRetreat}
397
+ dayRange={dayRange}
398
+ setDayRange={setDayRange}
399
+ zoomLevel={zoomLevel}
400
+ setZoomLevel={setZoomLevel}
401
+ zoomOn={zoomOn}
402
+ minZoomLevel={minZoomLevel}
403
+ maxZoomLevel={maxZoomLevel}
404
+ zoomStep={zoomStep}
405
+ />
406
+ </div>
407
+ )}
408
+ {/* Body: Sol kısım => Resources, Sağ kısım => timeline */}
409
+ <div className="timeline-body">
410
+ <div className="timeline-resources-container"
411
+ style={{ overflow: "hidden"}}>
412
+ <ResourcesHeader content={resourceHeaderContent} />
413
+ <Resources
414
+ groupedResources={resources}
415
+ toggleGroupCollapse={toggleGroupCollapse}
416
+ collapsedGroups={collapsedGroups}
417
+ resourceSettings={resourceSettings}
418
+ />
419
+ </div>
420
+
421
+
422
+ <div
423
+ className="timeline-scrollable-container"
424
+ style={{
425
+ overflowX: horizontalScrollOn ? "auto" : "hidden",
426
+ }}
427
+ >
428
+
429
+ <div
430
+ className="timeline-header-content-wrapper"
431
+ style={{
432
+ width: horizontalScrollOn ? `${containerWidth}px` : "100%",
433
+ }}
434
+ >
435
+ <TimelineHeader
436
+ dates={filteredDates}
437
+ monthHeaders={filteredMonthHeaders}
438
+ highlightWeekends={highlightWeekends}
439
+ />
440
+
441
+ <TimelineContent
442
+ groupedResources={resources}
443
+ dates={filteredDates}
444
+ collapsedGroups={collapsedGroups}
445
+ events={localEvents}
446
+ setEvents={setLocalEvents}
447
+ onEventClick={onEventClick}
448
+ onEventDoubleClick={eventManagementOn ? handleEventDoubleClick : null}
449
+ onEventRightClick={eventManagementOn ? handleEventRightClickInternal : onEventRightClick}
450
+ selectedEvents={eventManagementOn ? eventManagement.selectedEvents : []}
451
+ onEventSelect={eventManagementOn ? eventManagement.selectEvent : null}
452
+ todayIndex={todayIndex}
453
+ indicatorOn={indicatorOn}
454
+ resourceSettings={resourceSettings}
455
+ toggleGroupCollapse={toggleGroupCollapse}
456
+ setDropInfo={setDropInfo}
457
+ eventsDragOn={eventsDragOn}
458
+ eventsExtendOn={eventsExtendOn}
459
+ createNewEventOn={createNewEventOn}
460
+ onDragInfo={onDragInfo}
461
+ onExtendInfo={onExtendInfo}
462
+ onCreateEventInfo={onCreateEventInfo}
463
+ onEventRightClick={onEventRightClick}
464
+ eventTooltipOn={eventTooltipOn} // Tooltip kontrolü
465
+ tooltipComponent={TooltipComponent} // Özelleştirilebilir Tooltip bileşeni
466
+ tempEventStyle = {tempEventStyle}
467
+ eventStyleResolver={eventStyleResolver}
468
+ eventAlignmentMode={eventAlignmentMode}
469
+ preventPastEvents={preventPastEvents}
470
+ minDate={minDate || indicatorDate}
471
+ highlightWeekends={highlightWeekends}
472
+ cellTooltipOn={cellTooltipOn}
473
+ cellTooltipResolver={cellTooltipResolver}
474
+ cellContextMenuOn={cellContextMenuOn}
475
+ cellContextMenuItems={cellContextMenuItems.map(item => {
476
+ // Daily timeline item'ını özel handle et
477
+ if (item.id === 'daily-timeline' && dailyViewOn) {
478
+ return {
479
+ ...item,
480
+ onClick: (resource, date) => {
481
+ setDailyView({
482
+ isOpen: true,
483
+ resource,
484
+ date,
485
+ });
486
+ if (item.onClick) {
487
+ item.onClick(resource, date);
488
+ }
489
+ },
490
+ };
491
+ }
492
+ return item;
493
+ })}
494
+ onCellContextMenu={onCellContextMenu}
495
+ eventIconsOn={eventIconsOn}
496
+ eventIconResolver={eventIconResolver}
497
+ eventBadgesOn={eventBadgesOn}
498
+ eventBadgeResolver={eventBadgeResolver}
499
+ isLoading={isLoading}
500
+ loadingType={loadingType}
501
+
502
+ />
503
+
504
+ {/* Tooltip */}
505
+ {selectedEvent && (
506
+ <EventTooltip
507
+ event={selectedEvent}
508
+ position={tooltipPosition}
509
+ onClose={handleCloseTooltip}
510
+ onDelete={(eventId) =>
511
+ setLocalEvents((prev) => prev.filter((e) => e.id !== eventId))
512
+ }
513
+ />
514
+ )}
515
+ </div>
516
+ </div>
517
+ </div>
518
+
519
+ {/* Event Detail Modal */}
520
+ {eventManagementOn && (
521
+ <EventDetailModal
522
+ event={editingEvent}
523
+ isOpen={isModalOpen}
524
+ onClose={() => {
525
+ setIsModalOpen(false);
526
+ setEditingEvent(null);
527
+ }}
528
+ onSave={handleEventSave}
529
+ onDelete={handleEventDelete}
530
+ resources={resources.flatMap((group) => group.resources || [])}
531
+ />
532
+ )}
533
+
534
+ {/* Daily View */}
535
+ {dailyViewOn && (
536
+ <DailyView
537
+ isOpen={dailyView.isOpen}
538
+ onClose={() => setDailyView({ isOpen: false, resource: null, date: null })}
539
+ resource={dailyView.resource}
540
+ date={dailyView.date}
541
+ events={localEvents}
542
+ onEventCreate={(newEvent) => {
543
+ const updatedEvents = [...localEvents, newEvent];
544
+ setLocalEvents(updatedEvents);
545
+ if (onEventUpdate) {
546
+ onEventUpdate(updatedEvents);
547
+ }
548
+ }}
549
+ onEventUpdate={(updatedEvent) => {
550
+ const updatedEvents = localEvents.map(e =>
551
+ e.id === updatedEvent.id ? updatedEvent : e
552
+ );
553
+ setLocalEvents(updatedEvents);
554
+ if (onEventUpdate) {
555
+ onEventUpdate(updatedEvents);
556
+ }
557
+ }}
558
+ onEventDelete={(eventId) => {
559
+ const updatedEvents = localEvents.filter(e => e.id !== eventId);
560
+ setLocalEvents(updatedEvents);
561
+ if (onEventUpdate) {
562
+ onEventUpdate(updatedEvents);
563
+ }
564
+ }}
565
+ themeType={themeType}
566
+ />
567
+ )}
568
+
569
+ {/* Daily View */}
570
+ {dailyViewOn && (
571
+ <DailyView
572
+ isOpen={dailyView.isOpen}
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;