Skip to content

Commit 5a2a1e8

Browse files
authored
Merge pull request #36 from rviscomi/v3
V3
2 parents f2a7eb3 + b6aff29 commit 5a2a1e8

5 files changed

Lines changed: 1473 additions & 6 deletions

File tree

app.js

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
import { browsers, features } from './data.js';
22
import { browserIcons } from './browser-icons.js';
33
import { downloadICal } from './ical-generator.js';
4+
import { fetchDeveloperSignals } from './developer-signals.js';
45

56
class TimelineApp {
67
constructor() {
78
this.timelineContent = document.querySelector('.timeline-content');
8-
this.features = this.processFeatures();
9+
this.developerSignals = null; // Will be populated after fetch
10+
this.features = [];
911
this.selectedFeatures = new Set(); // Track selected features
1012
this.init();
1113
}
1214

15+
async init() {
16+
try {
17+
// Fetch developer signals data
18+
this.developerSignals = await fetchDeveloperSignals();
19+
20+
// Process features after getting developer signals
21+
this.features = this.processFeatures();
22+
23+
// Render the timeline
24+
this.renderTimeline();
25+
26+
// Initialize event listeners
27+
this.initEventListeners();
28+
} catch (error) {
29+
console.error('Error initializing timeline:', error);
30+
}
31+
}
32+
1333
processFeatures() {
1434
const processedFeatures = [];
1535

1636
Object.entries(features)
1737
.forEach(([id, data]) => {
38+
// Skip non-feature kinds (moved or split)
39+
if (data.kind && data.kind !== 'feature') {
40+
return;
41+
}
42+
1843
// Get all ship dates from browsers
1944
const shipDates = Object.entries(data.status?.support || {})
2045
.map(([browser, version]) => {
@@ -58,7 +83,9 @@ class TimelineApp {
5883
description_html: data.description_html || data.description,
5984
spec: data.spec,
6085
status: data.status,
61-
shipDates: shipDates
86+
shipDates: shipDates,
87+
// Add developer signals data if available
88+
developerSignal: this.developerSignals?.[id] || null
6289
};
6390

6491
// Get current date for comparison
@@ -261,12 +288,42 @@ class TimelineApp {
261288
const nameText = document.createTextNode(feature.name);
262289
title.appendChild(nameText);
263290

291+
// Add upvote info next to the feature name if available
292+
if (feature.developerSignal) {
293+
const upvoteInfo = document.createElement('div');
294+
upvoteInfo.className = 'upvote-info title-upvote';
295+
296+
// Create a linked upvote count (the entire element is clickable)
297+
const upvoteLink = document.createElement('a');
298+
upvoteLink.href = feature.developerSignal.url;
299+
upvoteLink.className = 'upvote-count';
300+
upvoteLink.target = '_blank';
301+
upvoteLink.rel = 'noopener noreferrer';
302+
upvoteLink.innerHTML = `<span class="upvote-icon">👍</span>${feature.developerSignal.votes}`;
303+
upvoteLink.title = 'Add your support for this feature on GitHub';
304+
305+
// Add event to prevent card selection when clicking the button
306+
upvoteLink.addEventListener('click', (e) => {
307+
e.stopPropagation();
308+
});
309+
310+
upvoteInfo.appendChild(upvoteLink);
311+
title.appendChild(upvoteInfo);
312+
}
313+
264314
// Add a link icon for deep linking to this feature
265315
const linkIcon = document.createElement('a');
266316
linkIcon.href = `#${uniqueCardId}`;
267317
linkIcon.className = 'feature-link-icon';
268318
linkIcon.innerHTML = '🔗';
269-
linkIcon.title = `Link to ${feature.name} (${feature.displayType})`;
319+
320+
// Use the correct display type in tooltip - check if it's a limited availability feature
321+
let displayTypeForTooltip = feature.displayType;
322+
if (feature.status?.baseline === false) {
323+
displayTypeForTooltip = 'limited-availability';
324+
}
325+
linkIcon.title = `Link to ${feature.name} (${displayTypeForTooltip})`;
326+
270327
linkIcon.style.fontSize = '0.8em';
271328
linkIcon.style.marginLeft = '0.5em';
272329
linkIcon.style.opacity = '0';
@@ -617,7 +674,12 @@ class TimelineApp {
617674

618675
// Create a unique ID that includes both the feature name and its display type
619676
// This ensures each instance (newly vs widely available) has a unique ID
620-
const uniqueCardId = `feature-${feature.id}-${feature.displayType}`;
677+
let cardType = feature.displayType;
678+
// Use 'limited-availability' in the ID for limited availability features instead of 'newly-available'
679+
if (feature.status?.baseline === false) {
680+
cardType = 'limited-availability';
681+
}
682+
const uniqueCardId = `feature-${feature.id}-${cardType}`;
621683

622684
// Add a unique ID to the feature card for deep linking
623685
card.id = uniqueCardId;
@@ -725,7 +787,7 @@ class TimelineApp {
725787
});
726788
}
727789

728-
init() {
790+
initEventListeners() {
729791
// Add event listeners for iCal download buttons
730792
const downloadTopBtn = document.getElementById('download-ical-top');
731793
const downloadBottomBtn = document.getElementById('download-ical-bottom');
@@ -735,13 +797,18 @@ class TimelineApp {
735797
downloadICal(this.getSelectedFeatures());
736798
});
737799
}
738-
800+
739801
if (downloadBottomBtn) {
740802
downloadBottomBtn.addEventListener('click', () => {
741803
downloadICal(this.getSelectedFeatures());
742804
});
743805
}
806+
}
744807

808+
renderTimeline() {
809+
// Clear existing content
810+
this.timelineContent.innerHTML = '';
811+
745812
const groups = this.groupFeaturesByDate();
746813

747814
// Find the current month for scrolling

0 commit comments

Comments
 (0)