akfatimeline 1.1.1 → 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.
Files changed (36) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/Timeline.js +16 -48
  3. package/{src/components/Timeline/DailyView.js → dist/components/Timeline/DailyView.jsx} +1 -0
  4. package/{src/components/Timeline/EventTooltip.js → dist/components/Timeline/EventTooltip.jsx} +207 -206
  5. package/{src/components/Timeline/Indicator.js → dist/components/Timeline/Indicator.jsx} +27 -26
  6. package/{src/components/Timeline/MasterHeader.js → dist/components/Timeline/MasterHeader.jsx} +105 -104
  7. package/{src/components/Timeline/Resources.js → dist/components/Timeline/Resources.jsx} +54 -53
  8. package/{src/components/Timeline/ResourcesHeader.js → dist/components/Timeline/ResourcesHeader.jsx} +15 -14
  9. package/{src/components/Timeline/Timeline.js → dist/components/Timeline/Timeline.jsx} +572 -607
  10. package/{src/components/Timeline/TimelineContent.js → dist/components/Timeline/TimelineContent.jsx} +837 -838
  11. package/{src/components/Timeline/TimelineHeader.js → dist/components/Timeline/TimelineHeader.jsx} +55 -54
  12. package/dist/components/Timeline/TimelineMonthContainer.js +2 -2
  13. package/package.json +55 -4
  14. package/src/components/Timeline/AutocompleteSelect.jsx +150 -0
  15. package/src/components/Timeline/ContextMenu.jsx +149 -0
  16. package/src/components/Timeline/DailyView.jsx +256 -0
  17. package/src/components/Timeline/EventBadge.jsx +26 -0
  18. package/src/components/Timeline/EventDetailModal.jsx +138 -0
  19. package/src/components/Timeline/EventIcon.jsx +47 -0
  20. package/src/components/Timeline/EventTooltip.jsx +207 -0
  21. package/src/components/Timeline/Indicator.jsx +27 -0
  22. package/src/components/Timeline/LoadingSpinner.jsx +48 -0
  23. package/src/components/Timeline/MasterHeader.jsx +105 -0
  24. package/src/components/Timeline/Resources.jsx +54 -0
  25. package/src/components/Timeline/ResourcesHeader.jsx +15 -0
  26. package/src/components/Timeline/Timeline.jsx +572 -0
  27. package/src/components/Timeline/TimelineContent.jsx +837 -0
  28. package/src/components/Timeline/TimelineHeader.jsx +55 -0
  29. package/src/components/Timeline/TimelineMonthContainer.js +2 -2
  30. package/src/library.js +8 -8
  31. /package/{src/components/Timeline/AutocompleteSelect.js → dist/components/Timeline/AutocompleteSelect.jsx} +0 -0
  32. /package/{src/components/Timeline/ContextMenu.js → dist/components/Timeline/ContextMenu.jsx} +0 -0
  33. /package/{src/components/Timeline/EventBadge.js → dist/components/Timeline/EventBadge.jsx} +0 -0
  34. /package/{src/components/Timeline/EventDetailModal.js → dist/components/Timeline/EventDetailModal.jsx} +0 -0
  35. /package/{src/components/Timeline/EventIcon.js → dist/components/Timeline/EventIcon.jsx} +0 -0
  36. /package/{src/components/Timeline/LoadingSpinner.js → dist/components/Timeline/LoadingSpinner.jsx} +0 -0
@@ -1,54 +1,55 @@
1
- import React from "react";
2
- import "./Timeline.css"; // CSS dosyasını import etmeyi unutma
3
- import { parseDate } from "../../utils/dateUtils";
4
-
5
- const TimelineHeader = ({ dates, monthHeaders, highlightWeekends = false }) => {
6
- return (
7
- <div className="timeline-header-container">
8
- {/* Ay ve Yıl Başlıkları */}
9
- <div className="timeline-header-month-row">
10
- {monthHeaders.map((monthHeader, index) => (
11
- <div
12
- key={index}
13
- className="timeline-header-month-cell"
14
- style={{
15
- flex: monthHeader.endIndex - monthHeader.startIndex + 1,
16
- borderRight:
17
- index < monthHeaders.length - 1 ? "1px solid var(--border-color)" : "none",
18
- }}
19
- >
20
- {monthHeader.monthName} {monthHeader.year}
21
- </div>
22
- ))}
23
- </div>
24
-
25
- {/* Günlük Hücreler */}
26
- <div className="timeline-header-day-row">
27
- {dates.map((date, index) => {
28
- // Hafta sonu kontrolü
29
- let isWeekend = false;
30
- if (highlightWeekends) {
31
- const cellDate = parseDate(date.fullDate);
32
- const dayOfWeek = cellDate.getDay(); // 0 = Pazar, 6 = Cumartesi
33
- isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
34
- }
35
-
36
- return (
37
- <div
38
- key={index}
39
- className={`timeline-header-day-cell ${isWeekend ? "timeline-header-day-weekend" : ""}`}
40
- style={{
41
- flex: 1,
42
- borderRight: index < dates.length - 1 ? "1px solid var(--border-color)" : "none",
43
- }}
44
- >
45
- {date.display}
46
- </div>
47
- );
48
- })}
49
- </div>
50
- </div>
51
- );
52
- };
53
-
54
- export default TimelineHeader;
1
+ import React from "react";
2
+ import "./Timeline.css"; // CSS dosyasını import etmeyi unutma
3
+ import { parseDate } from "../../utils/dateUtils";
4
+
5
+ const TimelineHeader = ({ dates, monthHeaders, highlightWeekends = false }) => {
6
+ return (
7
+ <div className="timeline-header-container">
8
+ {/* Ay ve Yıl Başlıkları */}
9
+ <div className="timeline-header-month-row">
10
+ {monthHeaders.map((monthHeader, index) => (
11
+ <div
12
+ key={index}
13
+ className="timeline-header-month-cell"
14
+ style={{
15
+ flex: monthHeader.endIndex - monthHeader.startIndex + 1,
16
+ borderRight:
17
+ index < monthHeaders.length - 1 ? "1px solid var(--border-color)" : "none",
18
+ }}
19
+ >
20
+ {monthHeader.monthName} {monthHeader.year}
21
+ </div>
22
+ ))}
23
+ </div>
24
+
25
+ {/* Günlük Hücreler */}
26
+ <div className="timeline-header-day-row">
27
+ {dates.map((date, index) => {
28
+ // Hafta sonu kontrolü
29
+ let isWeekend = false;
30
+ if (highlightWeekends) {
31
+ const cellDate = parseDate(date.fullDate);
32
+ const dayOfWeek = cellDate.getDay(); // 0 = Pazar, 6 = Cumartesi
33
+ isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
34
+ }
35
+
36
+ return (
37
+ <div
38
+ key={index}
39
+ className={`timeline-header-day-cell ${isWeekend ? "timeline-header-day-weekend" : ""}`}
40
+ style={{
41
+ flex: 1,
42
+ borderRight: index < dates.length - 1 ? "1px solid var(--border-color)" : "none",
43
+ }}
44
+ >
45
+ {date.display}
46
+ </div>
47
+ );
48
+ })}
49
+ </div>
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default TimelineHeader;
55
+
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
- import TimelineHeader from "./TimelineHeader";
3
- import TimelineContent from "./TimelineContent";
2
+ import TimelineHeader from "./TimelineHeader.jsx";
3
+ import TimelineContent from "./TimelineContent.jsx";
4
4
 
5
5
  const TimelineMonthContainer = ({
6
6
  dates,
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "akfatimeline",
3
- "version": "1.1.1",
3
+ "version": "2.0.0",
4
4
  "description": "A customizable timeline component for React applications",
5
- "main": "dist/Timeline.js",
6
- "module": "src/library.js",
5
+ "main": "./src/library.js",
6
+ "module": "./src/library.js",
7
+ "types": "./src/library.js",
7
8
  "files": [
8
9
  "dist",
9
10
  "src",
@@ -12,11 +13,61 @@
12
13
  ],
13
14
  "exports": {
14
15
  ".": {
16
+ "types": "./src/library.js",
15
17
  "import": "./src/library.js",
16
18
  "require": "./dist/Timeline.js",
17
19
  "default": "./src/library.js"
18
20
  },
19
- "./components/Timeline/Timeline.css": "./src/components/Timeline/Timeline.css"
21
+ "./css": "./src/components/Timeline/Timeline.css",
22
+ "./components/Timeline/Timeline.css": "./src/components/Timeline/Timeline.css",
23
+ "./Timeline": {
24
+ "types": "./src/components/Timeline/Timeline.jsx",
25
+ "import": "./src/components/Timeline/Timeline.jsx",
26
+ "require": "./dist/components/Timeline/Timeline.js",
27
+ "default": "./src/components/Timeline/Timeline.jsx"
28
+ },
29
+ "./DailyView": {
30
+ "types": "./src/components/Timeline/DailyView.jsx",
31
+ "import": "./src/components/Timeline/DailyView.jsx",
32
+ "require": "./dist/components/Timeline/DailyView.js",
33
+ "default": "./src/components/Timeline/DailyView.jsx"
34
+ },
35
+ "./ContextMenu": {
36
+ "types": "./src/components/Timeline/ContextMenu.jsx",
37
+ "import": "./src/components/Timeline/ContextMenu.jsx",
38
+ "require": "./dist/components/Timeline/ContextMenu.js",
39
+ "default": "./src/components/Timeline/ContextMenu.jsx"
40
+ },
41
+ "./EventDetailModal": {
42
+ "types": "./src/components/Timeline/EventDetailModal.jsx",
43
+ "import": "./src/components/Timeline/EventDetailModal.jsx",
44
+ "require": "./dist/components/Timeline/EventDetailModal.js",
45
+ "default": "./src/components/Timeline/EventDetailModal.jsx"
46
+ },
47
+ "./EventIcon": {
48
+ "types": "./src/components/Timeline/EventIcon.jsx",
49
+ "import": "./src/components/Timeline/EventIcon.jsx",
50
+ "require": "./dist/components/Timeline/EventIcon.js",
51
+ "default": "./src/components/Timeline/EventIcon.jsx"
52
+ },
53
+ "./EventBadge": {
54
+ "types": "./src/components/Timeline/EventBadge.jsx",
55
+ "import": "./src/components/Timeline/EventBadge.jsx",
56
+ "require": "./dist/components/Timeline/EventBadge.js",
57
+ "default": "./src/components/Timeline/EventBadge.jsx"
58
+ },
59
+ "./LoadingSpinner": {
60
+ "types": "./src/components/Timeline/LoadingSpinner.jsx",
61
+ "import": "./src/components/Timeline/LoadingSpinner.jsx",
62
+ "require": "./dist/components/Timeline/LoadingSpinner.js",
63
+ "default": "./src/components/Timeline/LoadingSpinner.jsx"
64
+ },
65
+ "./AutocompleteSelect": {
66
+ "types": "./src/components/Timeline/AutocompleteSelect.jsx",
67
+ "import": "./src/components/Timeline/AutocompleteSelect.jsx",
68
+ "require": "./dist/components/Timeline/AutocompleteSelect.js",
69
+ "default": "./src/components/Timeline/AutocompleteSelect.jsx"
70
+ }
20
71
  },
21
72
  "dependencies": {
22
73
  "react": "^19.2.3",
@@ -0,0 +1,150 @@
1
+ import React, { useState, useRef, useEffect } from "react";
2
+ import "./Timeline.css";
3
+
4
+ const AutocompleteSelect = ({
5
+ options = [],
6
+ value = null,
7
+ onChange = () => {},
8
+ placeholder = "Seçiniz...",
9
+ getOptionLabel = (option) => option?.label || option?.name || String(option),
10
+ getOptionValue = (option) => option?.value || option?.id || option,
11
+ filterOptions = (options, inputValue) => {
12
+ if (!inputValue) return options;
13
+ const lowerInput = inputValue.toLowerCase();
14
+ return options.filter((option) => {
15
+ const label = getOptionLabel(option).toLowerCase();
16
+ return label.includes(lowerInput);
17
+ });
18
+ },
19
+ }) => {
20
+ const [isOpen, setIsOpen] = useState(false);
21
+ const [inputValue, setInputValue] = useState("");
22
+ const [filteredOptions, setFilteredOptions] = useState(options);
23
+ const containerRef = useRef(null);
24
+ const inputRef = useRef(null);
25
+
26
+ // Seçili değerin label'ını bul
27
+ const selectedOption = options.find(
28
+ (opt) => getOptionValue(opt) === value
29
+ );
30
+ const displayValue = selectedOption
31
+ ? getOptionLabel(selectedOption)
32
+ : inputValue || placeholder;
33
+
34
+ // Input değiştiğinde filtrele
35
+ useEffect(() => {
36
+ if (isOpen) {
37
+ const filtered = filterOptions(options, inputValue);
38
+ setFilteredOptions(filtered);
39
+ } else {
40
+ setFilteredOptions(options);
41
+ }
42
+ }, [inputValue, isOpen, options, filterOptions]);
43
+
44
+ // Dışarı tıklandığında kapat
45
+ useEffect(() => {
46
+ const handleClickOutside = (event) => {
47
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
48
+ setIsOpen(false);
49
+ setInputValue("");
50
+ }
51
+ };
52
+
53
+ if (isOpen) {
54
+ document.addEventListener("mousedown", handleClickOutside);
55
+ return () => {
56
+ document.removeEventListener("mousedown", handleClickOutside);
57
+ };
58
+ }
59
+ }, [isOpen]);
60
+
61
+ const handleInputChange = (e) => {
62
+ const newValue = e.target.value;
63
+ setInputValue(newValue);
64
+ setIsOpen(true);
65
+ };
66
+
67
+ const handleSelect = (option) => {
68
+ const optionValue = getOptionValue(option);
69
+ onChange(optionValue, option);
70
+ setInputValue("");
71
+ setIsOpen(false);
72
+ };
73
+
74
+ const handleFocus = () => {
75
+ setIsOpen(true);
76
+ if (selectedOption) {
77
+ setInputValue(getOptionLabel(selectedOption));
78
+ }
79
+ };
80
+
81
+ const handleBlur = () => {
82
+ // Input blur olduğunda hemen kapatma, click outside ile kapatılacak
83
+ setTimeout(() => {
84
+ if (!containerRef.current?.contains(document.activeElement)) {
85
+ setIsOpen(false);
86
+ if (selectedOption) {
87
+ setInputValue("");
88
+ }
89
+ }
90
+ }, 200);
91
+ };
92
+
93
+ return (
94
+ <div className="autocomplete-select-container" ref={containerRef}>
95
+ <div
96
+ className={`autocomplete-select-input ${isOpen ? "open" : ""}`}
97
+ onClick={() => {
98
+ setIsOpen(!isOpen);
99
+ inputRef.current?.focus();
100
+ }}
101
+ >
102
+ <input
103
+ ref={inputRef}
104
+ type="text"
105
+ value={isOpen ? inputValue : displayValue}
106
+ onChange={handleInputChange}
107
+ onFocus={handleFocus}
108
+ onBlur={handleBlur}
109
+ placeholder={placeholder}
110
+ className="autocomplete-select-input-field"
111
+ />
112
+ <span className="autocomplete-select-arrow">
113
+ {isOpen ? "▲" : "▼"}
114
+ </span>
115
+ </div>
116
+
117
+ {isOpen && (
118
+ <div className="autocomplete-select-dropdown">
119
+ {filteredOptions.length > 0 ? (
120
+ filteredOptions.map((option, index) => {
121
+ const optionValue = getOptionValue(option);
122
+ const optionLabel = getOptionLabel(option);
123
+ const isSelected = optionValue === value;
124
+
125
+ return (
126
+ <div
127
+ key={index}
128
+ className={`autocomplete-select-option ${
129
+ isSelected ? "selected" : ""
130
+ }`}
131
+ onClick={() => handleSelect(option)}
132
+ onMouseDown={(e) => e.preventDefault()} // Blur'u engelle
133
+ >
134
+ {optionLabel}
135
+ </div>
136
+ );
137
+ })
138
+ ) : (
139
+ <div className="autocomplete-select-no-results">
140
+ Sonuç bulunamadı
141
+ </div>
142
+ )}
143
+ </div>
144
+ )}
145
+ </div>
146
+ );
147
+ };
148
+
149
+ export default AutocompleteSelect;
150
+
@@ -0,0 +1,149 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import './Timeline.css';
3
+
4
+ /**
5
+ * Context Menu Component
6
+ * Sağ tık menüsü için özelleştirilebilir menü bileşeni
7
+ */
8
+ const ContextMenu = ({
9
+ isOpen,
10
+ position,
11
+ onClose,
12
+ menuItems = [],
13
+ resource = null,
14
+ date = null,
15
+ }) => {
16
+ const menuRef = useRef(null);
17
+ const [adjustedPosition, setAdjustedPosition] = useState(position);
18
+
19
+ // Menü pozisyonunu mouse'a yakın tut ve ekran sınırları içinde tut
20
+ useEffect(() => {
21
+ if (!menuRef.current || !position || !isOpen) {
22
+ setAdjustedPosition(position);
23
+ return;
24
+ }
25
+
26
+ // Menü render edildikten sonra pozisyonu ayarla
27
+ const updatePosition = () => {
28
+ const menuRect = menuRef.current.getBoundingClientRect();
29
+ const viewportWidth = window.innerWidth;
30
+ const viewportHeight = window.innerHeight;
31
+
32
+ let adjustedX = position.x;
33
+ let adjustedY = position.y;
34
+
35
+ // Sağa taşma kontrolü
36
+ if (position.x + menuRect.width > viewportWidth) {
37
+ adjustedX = position.x - menuRect.width;
38
+ }
39
+
40
+ // Aşağıya taşma kontrolü
41
+ if (position.y + menuRect.height > viewportHeight) {
42
+ adjustedY = position.y - menuRect.height;
43
+ }
44
+
45
+ // Sola taşma kontrolü
46
+ if (adjustedX < 10) {
47
+ adjustedX = 10;
48
+ }
49
+
50
+ // Yukarıya taşma kontrolü
51
+ if (adjustedY < 10) {
52
+ adjustedY = 10;
53
+ }
54
+
55
+ setAdjustedPosition({ x: adjustedX, y: adjustedY });
56
+ };
57
+
58
+ // Menü render edildikten sonra pozisyonu güncelle
59
+ setTimeout(updatePosition, 0);
60
+ }, [position, isOpen]);
61
+
62
+ // Menü dışına tıklanınca kapat
63
+ useEffect(() => {
64
+ const handleClickOutside = (event) => {
65
+ if (menuRef.current && !menuRef.current.contains(event.target)) {
66
+ onClose();
67
+ }
68
+ };
69
+
70
+ const handleEscape = (event) => {
71
+ if (event.key === 'Escape') {
72
+ onClose();
73
+ }
74
+ };
75
+
76
+ if (isOpen) {
77
+ document.addEventListener('mousedown', handleClickOutside);
78
+ document.addEventListener('keydown', handleEscape);
79
+ // Scroll olduğunda menüyü kapat
80
+ document.addEventListener('scroll', onClose, true);
81
+ }
82
+
83
+ return () => {
84
+ document.removeEventListener('mousedown', handleClickOutside);
85
+ document.removeEventListener('keydown', handleEscape);
86
+ document.removeEventListener('scroll', onClose, true);
87
+ };
88
+ }, [isOpen, onClose]);
89
+
90
+ if (!isOpen || !position) return null;
91
+
92
+ const handleItemClick = (item) => {
93
+ if (item.onClick) {
94
+ item.onClick(resource, date);
95
+ }
96
+ if (item.closeOnClick !== false) {
97
+ onClose();
98
+ }
99
+ };
100
+
101
+ return (
102
+ <div
103
+ ref={menuRef}
104
+ className="context-menu"
105
+ style={{
106
+ position: 'fixed',
107
+ left: `${(adjustedPosition?.x ?? position.x) - 150}px`,
108
+ top: `${(adjustedPosition?.y ?? position.y) - 150}px`,
109
+ zIndex: 10005,
110
+ }}
111
+ >
112
+ <div className="context-menu-content">
113
+ {menuItems.length === 0 ? (
114
+ <div className="context-menu-item context-menu-item-disabled">
115
+ Menü öğesi yok
116
+ </div>
117
+ ) : (
118
+ menuItems.map((item, index) => {
119
+ if (item.separator) {
120
+ return <div key={`separator-${index}`} className="context-menu-separator" />;
121
+ }
122
+
123
+ if (item.hidden) {
124
+ return null;
125
+ }
126
+
127
+ return (
128
+ <div
129
+ key={item.id || index}
130
+ className={`context-menu-item ${item.disabled ? 'context-menu-item-disabled' : ''} ${item.danger ? 'context-menu-item-danger' : ''}`}
131
+ onClick={() => !item.disabled && handleItemClick(item)}
132
+ title={item.tooltip || item.label}
133
+ >
134
+ {item.icon && <span className="context-menu-item-icon">{item.icon}</span>}
135
+ <span className="context-menu-item-label">{item.label}</span>
136
+ {item.shortcut && (
137
+ <span className="context-menu-item-shortcut">{item.shortcut}</span>
138
+ )}
139
+ </div>
140
+ );
141
+ })
142
+ )}
143
+ </div>
144
+ </div>
145
+ );
146
+ };
147
+
148
+ export default ContextMenu;
149
+