dbwatcher 1.1.2 → 1.1.4
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.
- checksums.yaml +4 -4
- data/app/assets/config/dbwatcher_manifest.js +1 -0
- data/app/assets/javascripts/dbwatcher/components/changes_table_hybrid.js +184 -97
- data/app/assets/javascripts/dbwatcher/components/timeline.js +211 -0
- data/app/assets/javascripts/dbwatcher/dbwatcher.js +5 -0
- data/app/assets/stylesheets/dbwatcher/application.css +298 -1
- data/app/assets/stylesheets/dbwatcher/application.scss +1 -0
- data/app/assets/stylesheets/dbwatcher/components/_timeline.scss +326 -0
- data/app/controllers/dbwatcher/api/v1/sessions_controller.rb +27 -17
- data/app/controllers/dbwatcher/sessions_controller.rb +1 -1
- data/app/views/dbwatcher/sessions/_layout.html.erb +5 -2
- data/app/views/dbwatcher/sessions/_summary.html.erb +1 -1
- data/app/views/dbwatcher/sessions/{_changes.html.erb → _tables.html.erb} +84 -5
- data/app/views/dbwatcher/sessions/_timeline.html.erb +260 -0
- data/app/views/dbwatcher/sessions/show.html.erb +3 -1
- data/app/views/layouts/dbwatcher/application.html.erb +1 -0
- data/config/routes.rb +2 -1
- data/lib/dbwatcher/configuration.rb +11 -0
- data/lib/dbwatcher/logging.rb +23 -1
- data/lib/dbwatcher/services/analyzers/table_summary_builder.rb +102 -1
- data/lib/dbwatcher/services/api/{changes_data_service.rb → tables_data_service.rb} +6 -6
- data/lib/dbwatcher/services/diagram_analyzers/inferred_relationship_analyzer.rb +62 -36
- data/lib/dbwatcher/services/diagram_generator.rb +35 -69
- data/lib/dbwatcher/services/diagram_strategies/base_diagram_strategy.rb +23 -9
- data/lib/dbwatcher/services/diagram_strategies/class_diagram_strategy.rb +16 -22
- data/lib/dbwatcher/services/diagram_strategies/diagram_strategy_helpers.rb +33 -0
- data/lib/dbwatcher/services/diagram_strategies/erd_diagram_strategy.rb +20 -25
- data/lib/dbwatcher/services/diagram_strategies/flowchart_diagram_strategy.rb +20 -25
- data/lib/dbwatcher/services/diagram_strategies/standard_diagram_strategy.rb +80 -0
- data/lib/dbwatcher/services/diagram_system.rb +14 -1
- data/lib/dbwatcher/services/mermaid_syntax/base_builder.rb +2 -0
- data/lib/dbwatcher/services/mermaid_syntax_builder.rb +10 -8
- data/lib/dbwatcher/services/timeline_data_service/enhancement_utilities.rb +100 -0
- data/lib/dbwatcher/services/timeline_data_service/entry_builder.rb +125 -0
- data/lib/dbwatcher/services/timeline_data_service/metadata_builder.rb +93 -0
- data/lib/dbwatcher/services/timeline_data_service.rb +130 -0
- data/lib/dbwatcher/storage/api/concerns/table_analyzer.rb +1 -1
- data/lib/dbwatcher/version.rb +1 -1
- data/lib/dbwatcher.rb +1 -1
- metadata +13 -4
@@ -0,0 +1,211 @@
|
|
1
|
+
/**
|
2
|
+
* Timeline Component
|
3
|
+
* Interactive timeline visualization for database operations
|
4
|
+
* API-first implementation for DBWatcher timeline tab
|
5
|
+
*/
|
6
|
+
|
7
|
+
// Register component with DBWatcher
|
8
|
+
(function() {
|
9
|
+
function registerTimeline() {
|
10
|
+
if (window.DBWatcher && window.DBWatcher.ComponentRegistry) {
|
11
|
+
DBWatcher.ComponentRegistry.register('timeline', function(config) {
|
12
|
+
return Object.assign(DBWatcher.BaseComponent(config), {
|
13
|
+
// Component-specific state
|
14
|
+
sessionId: config.sessionId,
|
15
|
+
timelineData: [],
|
16
|
+
filteredData: [],
|
17
|
+
metadata: {},
|
18
|
+
|
19
|
+
// Filter state
|
20
|
+
filters: {
|
21
|
+
tables: [],
|
22
|
+
searchText: ""
|
23
|
+
},
|
24
|
+
tableSearch: "",
|
25
|
+
|
26
|
+
// Component initialization
|
27
|
+
componentInit() {
|
28
|
+
this.loadTimelineData();
|
29
|
+
this.setupEventListeners();
|
30
|
+
},
|
31
|
+
|
32
|
+
// Component cleanup
|
33
|
+
componentDestroy() {
|
34
|
+
// Clean up any event listeners or intervals
|
35
|
+
},
|
36
|
+
|
37
|
+
// Load timeline data from API
|
38
|
+
async loadTimelineData() {
|
39
|
+
if (!this.sessionId) {
|
40
|
+
console.error('No session ID provided to timeline component');
|
41
|
+
this.handleError(new Error('No session ID provided'));
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
|
45
|
+
this.setLoading(true);
|
46
|
+
this.clearError();
|
47
|
+
|
48
|
+
try {
|
49
|
+
const url = `/dbwatcher/api/v1/sessions/${this.sessionId}/timeline_data`;
|
50
|
+
const data = await this.fetchData(url);
|
51
|
+
|
52
|
+
if (!data.error) {
|
53
|
+
this.timelineData = data.timeline || [];
|
54
|
+
this.metadata = data.metadata || {};
|
55
|
+
this.filteredData = [...this.timelineData];
|
56
|
+
|
57
|
+
this.setupInitialView();
|
58
|
+
} else {
|
59
|
+
throw new Error(data.error || 'No timeline data received');
|
60
|
+
}
|
61
|
+
} catch (error) {
|
62
|
+
this.handleError(error);
|
63
|
+
} finally {
|
64
|
+
this.setLoading(false);
|
65
|
+
}
|
66
|
+
},
|
67
|
+
|
68
|
+
// Setup initial view after data load
|
69
|
+
setupInitialView() {
|
70
|
+
// Initialize filters based on available data
|
71
|
+
if (this.metadata.tables_affected) {
|
72
|
+
// Set up available filter options but don't apply any filters initially
|
73
|
+
}
|
74
|
+
},
|
75
|
+
|
76
|
+
// Setup event listeners
|
77
|
+
setupEventListeners() {
|
78
|
+
// Reserved for future keyboard shortcuts
|
79
|
+
},
|
80
|
+
|
81
|
+
// ==========================================
|
82
|
+
// Filtering functionality
|
83
|
+
// ==========================================
|
84
|
+
|
85
|
+
// Apply all filters to timeline data
|
86
|
+
applyFilters() {
|
87
|
+
this.filteredData = this.timelineData.filter((entry) => {
|
88
|
+
return (
|
89
|
+
this.matchesTableFilter(entry) &&
|
90
|
+
this.matchesSearchFilter(entry)
|
91
|
+
);
|
92
|
+
});
|
93
|
+
},
|
94
|
+
|
95
|
+
// Filter by table name
|
96
|
+
matchesTableFilter(entry) {
|
97
|
+
return (
|
98
|
+
this.filters.tables.length === 0 ||
|
99
|
+
this.filters.tables.includes(entry.table_name)
|
100
|
+
);
|
101
|
+
},
|
102
|
+
|
103
|
+
// Filter by search text
|
104
|
+
matchesSearchFilter(entry) {
|
105
|
+
if (!this.filters.searchText) return true;
|
106
|
+
|
107
|
+
const searchLower = this.filters.searchText.toLowerCase();
|
108
|
+
return (
|
109
|
+
entry.table_name.toLowerCase().includes(searchLower) ||
|
110
|
+
entry.operation.toLowerCase().includes(searchLower) ||
|
111
|
+
(entry.record_id && entry.record_id.toString().includes(searchLower))
|
112
|
+
);
|
113
|
+
},
|
114
|
+
|
115
|
+
// Clear all filters
|
116
|
+
clearFilters() {
|
117
|
+
this.filters = {
|
118
|
+
tables: [],
|
119
|
+
searchText: ""
|
120
|
+
};
|
121
|
+
this.applyFilters();
|
122
|
+
},
|
123
|
+
|
124
|
+
// ==========================================
|
125
|
+
// Utility methods
|
126
|
+
// ==========================================
|
127
|
+
|
128
|
+
// Format timestamp for display
|
129
|
+
formatTimestamp(timestamp) {
|
130
|
+
if (!timestamp) return 'N/A';
|
131
|
+
return this.formatDate(new Date(timestamp), 'MMM dd, yyyy HH:mm:ss');
|
132
|
+
},
|
133
|
+
|
134
|
+
// Format duration in milliseconds
|
135
|
+
formatDuration(ms) {
|
136
|
+
if (!ms || ms < 0) return '0ms';
|
137
|
+
if (ms < 1000) return `${ms}ms`;
|
138
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
139
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
140
|
+
},
|
141
|
+
|
142
|
+
// Get color for operation type
|
143
|
+
getOperationColor(operation) {
|
144
|
+
const colors = {
|
145
|
+
INSERT: "#10b981", // green
|
146
|
+
UPDATE: "#f59e0b", // amber
|
147
|
+
DELETE: "#ef4444", // red
|
148
|
+
SELECT: "#3b82f6" // blue
|
149
|
+
};
|
150
|
+
return colors[operation] || "#6b7280";
|
151
|
+
},
|
152
|
+
|
153
|
+
// Get operation icon
|
154
|
+
getOperationIcon(operation) {
|
155
|
+
const icons = {
|
156
|
+
INSERT: "plus",
|
157
|
+
UPDATE: "pencil",
|
158
|
+
DELETE: "trash",
|
159
|
+
SELECT: "eye"
|
160
|
+
};
|
161
|
+
return icons[operation] || "circle";
|
162
|
+
},
|
163
|
+
|
164
|
+
// Get available tables for filtering
|
165
|
+
getAvailableTables() {
|
166
|
+
return this.metadata.tables_affected || [];
|
167
|
+
},
|
168
|
+
|
169
|
+
// Get available operations for filtering
|
170
|
+
getAvailableOperations() {
|
171
|
+
return Object.keys(this.metadata.operation_counts || {});
|
172
|
+
},
|
173
|
+
|
174
|
+
// Get operation count for display
|
175
|
+
getOperationCount(operation) {
|
176
|
+
return this.metadata.operation_counts?.[operation] || 0;
|
177
|
+
},
|
178
|
+
|
179
|
+
// Get total filtered operations count
|
180
|
+
getTotalFilteredOperations() {
|
181
|
+
return this.filteredData.length;
|
182
|
+
},
|
183
|
+
|
184
|
+
// Get session statistics
|
185
|
+
getSessionStats() {
|
186
|
+
return {
|
187
|
+
totalOperations: this.timelineData.length,
|
188
|
+
filteredOperations: this.filteredData.length,
|
189
|
+
tablesAffected: this.getAvailableTables().length,
|
190
|
+
sessionDuration: this.metadata.session_duration || 'N/A',
|
191
|
+
timeRange: this.metadata.time_range || {}
|
192
|
+
};
|
193
|
+
},
|
194
|
+
|
195
|
+
// Format relative time from session start
|
196
|
+
formatRelativeTime(operation) {
|
197
|
+
if (!operation) return '00:00';
|
198
|
+
return operation.relative_time || '00:00';
|
199
|
+
}
|
200
|
+
}); // End of Object.assign
|
201
|
+
}); // End of registerComponent
|
202
|
+
console.log('✅ Timeline component registered successfully');
|
203
|
+
} else {
|
204
|
+
console.warn('DBWatcher ComponentRegistry not ready, retrying timeline registration...');
|
205
|
+
setTimeout(registerTimeline, 100);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
// Register immediately without waiting for DOM
|
210
|
+
registerTimeline();
|
211
|
+
})();
|
@@ -101,6 +101,11 @@ window.DBWatcher = {
|
|
101
101
|
return factory(config);
|
102
102
|
},
|
103
103
|
|
104
|
+
// Get component (alias for createComponent for compatibility)
|
105
|
+
getComponent(name, config = {}) {
|
106
|
+
return this.createComponent(name, config);
|
107
|
+
},
|
108
|
+
|
104
109
|
// Register a component using the ComponentRegistry
|
105
110
|
register(name, factory) {
|
106
111
|
if (!this.ComponentRegistry) {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* DBWatcher Application Styles
|
3
3
|
* Compiled CSS for all components
|
4
|
-
* Generated at 2025-07-
|
4
|
+
* Generated at 2025-07-11 21:59:56 +0700
|
5
5
|
*/
|
6
6
|
|
7
7
|
/**
|
@@ -747,6 +747,303 @@ pre[x-ref="codeContainer"]::-webkit-scrollbar-thumb:hover {
|
|
747
747
|
z-index: 100;
|
748
748
|
}
|
749
749
|
|
750
|
+
.timeline-container {
|
751
|
+
@apply h-full flex flex-col;
|
752
|
+
}
|
753
|
+
|
754
|
+
.timeline-controls {
|
755
|
+
@apply p-3 border-b border-gray-300 bg-gray-50;
|
756
|
+
}
|
757
|
+
|
758
|
+
.timeline-header {
|
759
|
+
@apply flex items-center justify-between mb-3;
|
760
|
+
}
|
761
|
+
|
762
|
+
.timeline-title {
|
763
|
+
@apply text-sm font-medium text-gray-900;
|
764
|
+
}
|
765
|
+
|
766
|
+
.timeline-zoom-controls {
|
767
|
+
@apply flex items-center gap-2;
|
768
|
+
}
|
769
|
+
|
770
|
+
.timeline-filter-controls {
|
771
|
+
@apply flex items-center gap-4 text-xs flex-wrap;
|
772
|
+
}
|
773
|
+
|
774
|
+
.timeline-filter-group {
|
775
|
+
@apply flex items-center gap-2;
|
776
|
+
}
|
777
|
+
|
778
|
+
.timeline-filter-label {
|
779
|
+
@apply text-gray-700 font-medium;
|
780
|
+
}
|
781
|
+
|
782
|
+
.timeline-visualization {
|
783
|
+
@apply flex-1 overflow-hidden;
|
784
|
+
}
|
785
|
+
|
786
|
+
.timeline-time-header {
|
787
|
+
@apply h-8 bg-gray-100 border-b border-gray-200 relative;
|
788
|
+
}
|
789
|
+
|
790
|
+
.timeline-time-scale {
|
791
|
+
@apply absolute inset-0 flex items-center px-4;
|
792
|
+
}
|
793
|
+
|
794
|
+
.timeline-content {
|
795
|
+
@apply flex-1 overflow-auto p-4 bg-white;
|
796
|
+
}
|
797
|
+
|
798
|
+
.timeline-track {
|
799
|
+
@apply relative h-16 bg-gray-50 rounded border border-gray-200 mb-4;
|
800
|
+
}
|
801
|
+
|
802
|
+
.timeline-line {
|
803
|
+
@apply absolute top-1/2 left-4 right-4 h-0.5 bg-gray-300 transform -translate-y-1/2;
|
804
|
+
}
|
805
|
+
|
806
|
+
.timeline-marker {
|
807
|
+
@apply absolute top-1/2 transform -translate-y-1/2 -translate-x-1/2 cursor-pointer;
|
808
|
+
}
|
809
|
+
|
810
|
+
.timeline-marker .timeline-marker-dot {
|
811
|
+
@apply w-3 h-3 rounded-full border-2 border-white shadow-sm transition-transform;
|
812
|
+
}
|
813
|
+
|
814
|
+
.timeline-marker .timeline-marker-dot:hover {
|
815
|
+
@apply scale-125;
|
816
|
+
}
|
817
|
+
|
818
|
+
.timeline-stats {
|
819
|
+
@apply grid grid-cols-2 gap-4 text-xs;
|
820
|
+
}
|
821
|
+
|
822
|
+
@screen md {
|
823
|
+
.timeline-stats {
|
824
|
+
@apply grid-cols-4;
|
825
|
+
}
|
826
|
+
}
|
827
|
+
|
828
|
+
.timeline-stat-card {
|
829
|
+
@apply bg-gray-50 p-3 rounded;
|
830
|
+
}
|
831
|
+
|
832
|
+
.timeline-stat-label {
|
833
|
+
@apply text-gray-500 font-medium;
|
834
|
+
}
|
835
|
+
|
836
|
+
.timeline-stat-value {
|
837
|
+
@apply text-lg font-bold text-gray-900;
|
838
|
+
}
|
839
|
+
|
840
|
+
.timeline-stat-detail {
|
841
|
+
@apply text-xs text-gray-500;
|
842
|
+
}
|
843
|
+
|
844
|
+
.timeline-operations {
|
845
|
+
@apply mt-6;
|
846
|
+
}
|
847
|
+
|
848
|
+
.timeline-operations-title {
|
849
|
+
@apply text-sm font-medium text-gray-900 mb-3;
|
850
|
+
}
|
851
|
+
|
852
|
+
.timeline-operations-list {
|
853
|
+
@apply space-y-2 max-h-64 overflow-auto;
|
854
|
+
}
|
855
|
+
|
856
|
+
.timeline-operation-item {
|
857
|
+
@apply flex items-center justify-between p-2 bg-gray-50 rounded hover:bg-gray-100 cursor-pointer text-xs;
|
858
|
+
}
|
859
|
+
|
860
|
+
.timeline-operation-info {
|
861
|
+
@apply flex items-center gap-3;
|
862
|
+
}
|
863
|
+
|
864
|
+
.timeline-operation-marker {
|
865
|
+
@apply w-2 h-2 rounded-full;
|
866
|
+
}
|
867
|
+
|
868
|
+
.timeline-operation-type {
|
869
|
+
@apply font-medium;
|
870
|
+
}
|
871
|
+
|
872
|
+
.timeline-operation-time {
|
873
|
+
@apply text-gray-500;
|
874
|
+
}
|
875
|
+
|
876
|
+
.timeline-operation-record {
|
877
|
+
@apply text-gray-500;
|
878
|
+
}
|
879
|
+
|
880
|
+
.timeline-empty {
|
881
|
+
@apply text-center py-8 text-gray-500;
|
882
|
+
}
|
883
|
+
|
884
|
+
.timeline-empty-icon {
|
885
|
+
@apply w-12 h-12 mx-auto mb-4 text-gray-300;
|
886
|
+
}
|
887
|
+
|
888
|
+
.timeline-empty-action {
|
889
|
+
@apply mt-2 text-blue-600 underline;
|
890
|
+
}
|
891
|
+
|
892
|
+
.timeline-loading {
|
893
|
+
@apply flex items-center justify-center h-64;
|
894
|
+
}
|
895
|
+
|
896
|
+
.timeline-loading-spinner {
|
897
|
+
@apply animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500;
|
898
|
+
}
|
899
|
+
|
900
|
+
.timeline-loading-text {
|
901
|
+
@apply ml-2 text-gray-600;
|
902
|
+
}
|
903
|
+
|
904
|
+
.timeline-error {
|
905
|
+
@apply p-4 bg-red-50 border border-red-200 rounded m-4;
|
906
|
+
}
|
907
|
+
|
908
|
+
.timeline-error-text {
|
909
|
+
@apply text-red-700;
|
910
|
+
}
|
911
|
+
|
912
|
+
.timeline-error-retry {
|
913
|
+
@apply mt-2 text-red-600 underline;
|
914
|
+
}
|
915
|
+
|
916
|
+
.timeline-modal-overlay {
|
917
|
+
@apply fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50;
|
918
|
+
}
|
919
|
+
|
920
|
+
.timeline-modal-content {
|
921
|
+
@apply bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-96 overflow-auto;
|
922
|
+
}
|
923
|
+
|
924
|
+
.timeline-modal-header {
|
925
|
+
@apply p-4 border-b border-gray-200 flex justify-between items-center;
|
926
|
+
}
|
927
|
+
|
928
|
+
.timeline-modal-title {
|
929
|
+
@apply text-lg font-medium;
|
930
|
+
}
|
931
|
+
|
932
|
+
.timeline-modal-close {
|
933
|
+
@apply text-gray-400 hover:text-gray-600;
|
934
|
+
}
|
935
|
+
|
936
|
+
.timeline-modal-body {
|
937
|
+
@apply p-4;
|
938
|
+
}
|
939
|
+
|
940
|
+
.timeline-operation-details {
|
941
|
+
@apply grid grid-cols-1 gap-4 text-sm;
|
942
|
+
}
|
943
|
+
|
944
|
+
@screen md {
|
945
|
+
.timeline-operation-details {
|
946
|
+
@apply grid-cols-2;
|
947
|
+
}
|
948
|
+
}
|
949
|
+
|
950
|
+
.timeline-operation-detail-label {
|
951
|
+
@apply font-medium;
|
952
|
+
}
|
953
|
+
|
954
|
+
.timeline-operation-detail-value {
|
955
|
+
@apply ml-2;
|
956
|
+
}
|
957
|
+
|
958
|
+
.timeline-operation-detail-value.operation-badge {
|
959
|
+
@apply px-2 py-1 rounded text-xs;
|
960
|
+
}
|
961
|
+
|
962
|
+
.timeline-operation-detail-value.monospace {
|
963
|
+
@apply font-mono;
|
964
|
+
}
|
965
|
+
|
966
|
+
.timeline-operation-detail-value.small {
|
967
|
+
@apply text-xs;
|
968
|
+
}
|
969
|
+
|
970
|
+
.timeline-changes-section {
|
971
|
+
@apply mt-4;
|
972
|
+
}
|
973
|
+
|
974
|
+
.timeline-changes-title {
|
975
|
+
@apply text-sm font-medium;
|
976
|
+
}
|
977
|
+
|
978
|
+
.timeline-changes-content {
|
979
|
+
@apply mt-2 p-3 bg-gray-50 rounded text-xs overflow-auto border max-h-32;
|
980
|
+
}
|
981
|
+
|
982
|
+
.timeline-metadata-section {
|
983
|
+
@apply mt-4;
|
984
|
+
}
|
985
|
+
|
986
|
+
.timeline-metadata-title {
|
987
|
+
@apply text-sm font-medium;
|
988
|
+
}
|
989
|
+
|
990
|
+
.timeline-metadata-grid {
|
991
|
+
@apply mt-2 grid grid-cols-2 gap-2 text-xs;
|
992
|
+
}
|
993
|
+
|
994
|
+
.timeline-metadata-key {
|
995
|
+
@apply font-medium capitalize;
|
996
|
+
}
|
997
|
+
|
998
|
+
@screen sm {
|
999
|
+
.timeline-filter-controls {
|
1000
|
+
@apply flex-nowrap;
|
1001
|
+
}
|
1002
|
+
.timeline-stats {
|
1003
|
+
@apply grid-cols-4;
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
|
1007
|
+
.operation-insert {
|
1008
|
+
@apply text-green-600 bg-green-100;
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
.operation-update {
|
1012
|
+
@apply text-blue-600 bg-blue-100;
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
.operation-delete {
|
1016
|
+
@apply text-red-600 bg-red-100;
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
.operation-select {
|
1020
|
+
@apply text-purple-600 bg-purple-100;
|
1021
|
+
}
|
1022
|
+
|
1023
|
+
.timeline-transition-enter {
|
1024
|
+
@apply transition ease-out duration-300;
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
.timeline-transition-enter-start {
|
1028
|
+
@apply opacity-0;
|
1029
|
+
}
|
1030
|
+
|
1031
|
+
.timeline-transition-enter-end {
|
1032
|
+
@apply opacity-100;
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
.timeline-transition-leave {
|
1036
|
+
@apply transition ease-in duration-200;
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
.timeline-transition-leave-start {
|
1040
|
+
@apply opacity-100;
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
.timeline-transition-leave-end {
|
1044
|
+
@apply opacity-0;
|
1045
|
+
}
|
1046
|
+
|
750
1047
|
/**
|
751
1048
|
* Tabulator Vendor Overrides
|
752
1049
|
* Overrides for the Tabulator.js vendor CSS
|