Skip to content

Commit d56300d

Browse files
committed
layouts enhancements, fixed data processing for agenda
1 parent 8087c96 commit d56300d

6 files changed

Lines changed: 919 additions & 303 deletions

File tree

index.html

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<meta name="description" content="Join Java Champions for SouJava's 30-Year Celebration Week - Free online conference June 16-20, 2025">
77
<title>Java 30Y #CelebrationWeek</title>
8+
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
9+
810

911
<!-- Performance Optimizations -->
1012
<link rel="preconnect" href="https://fonts.googleapis.com">
@@ -143,7 +145,7 @@ <h2 class="section-title">Agenda</h2>
143145
<div class="timezone-buttons">
144146
<button class="timezone-btn active" data-tz="America/Sao_Paulo">BRT</button>
145147
<button class="timezone-btn" data-tz="Europe/London">CET</button>
146-
<button class="timezone-btn" data-tz="America/New_York">EST</button>
148+
<button class="timezone-btn" data-tz="America/New_York">EDT</button>
147149
</div>
148150
</div>
149151

@@ -258,17 +260,6 @@ <h3 class="speaker-profile-name" id="modalSpeakerName"></h3>
258260
</div>
259261
</div>
260262

261-
<div class="modal" id="sessionModal" role="dialog" aria-labelledby="sessionTitle" aria-modal="true">
262-
<div class="modal-content">
263-
<div class="modal-header">
264-
<button class="modal-close" aria-label="Close session details">×</button>
265-
</div>
266-
<div class="modal-body">
267-
<div id="sessionDetails"></div>
268-
</div>
269-
</div>
270-
</div>
271-
272263
<script src="main.js"></script>
273264
</body>
274-
</html>
265+
</html>

js/components.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Component System
2+
import { templates } from './templates.js';
3+
4+
export class Component {
5+
constructor(container) {
6+
this.container = typeof container === 'string'
7+
? document.querySelector(container)
8+
: container;
9+
}
10+
11+
render(html) {
12+
if (this.container) {
13+
this.container.innerHTML = html;
14+
}
15+
}
16+
17+
append(html) {
18+
if (this.container) {
19+
this.container.insertAdjacentHTML('beforeend', html);
20+
}
21+
}
22+
}
23+
24+
export class SpeakerComponent extends Component {
25+
constructor(container, speakers, options = {}) {
26+
super(container);
27+
this.speakers = speakers;
28+
this.options = options;
29+
this.loadedCount = 0;
30+
this.batchSize = options.batchSize || 12;
31+
}
32+
33+
renderCard(id, speaker) {
34+
return templates.speakerCard({
35+
id,
36+
name: speaker.name,
37+
title: speaker.title,
38+
image: speaker.image,
39+
social: speaker.social
40+
});
41+
}
42+
43+
renderBatch(start, end) {
44+
const speakers = Object.entries(this.speakers).slice(start, end);
45+
return speakers.map(([id, speaker]) => this.renderCard(id, speaker)).join('');
46+
}
47+
}
48+
49+
export class CarouselComponent extends Component {
50+
constructor(container, speakers) {
51+
super(container);
52+
this.speakers = speakers;
53+
}
54+
55+
render() {
56+
const availableSpeakers = Object.entries(this.speakers).slice(0, 12);
57+
const duplicated = [...availableSpeakers, ...availableSpeakers];
58+
59+
const html = duplicated.map(([id, speaker]) =>
60+
templates.carouselSpeaker({
61+
id,
62+
name: speaker.name,
63+
title: speaker.title,
64+
image: speaker.image
65+
})
66+
).join('');
67+
68+
super.render(html);
69+
}
70+
}

js/icons.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/templates.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// HTML Templates Module
2+
import { createIcon } from './icons.js';
3+
4+
export const templates = {
5+
speakerCard({ id, name, title, image, social }) {
6+
const socialLinks = social ? Object.entries(social)
7+
.map(([platform, url]) => `
8+
<a href="${url}" target="_blank" rel="noopener"
9+
class="speaker-social-link" aria-label="${platform}">
10+
${createIcon(platform)}
11+
</a>
12+
`).join('') : '';
13+
14+
return `
15+
<div class="speaker-card" data-speaker-id="${id}">
16+
<img src="${image}" alt="${name}" class="speaker-image"
17+
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
18+
<div class="speaker-card-fallback" style="display: none;">
19+
${this.getInitials(name)}
20+
</div>
21+
<div class="speaker-info">
22+
<h3 class="speaker-name">${this.sanitize(name)}</h3>
23+
<p class="speaker-title">${this.sanitize(title)}</p>
24+
${socialLinks ? `<div class="speaker-social">${socialLinks}</div>` : ''}
25+
</div>
26+
</div>
27+
`;
28+
},
29+
30+
carouselSpeaker({ id, name, title, image }) {
31+
return `
32+
<a href="#speakers" class="carousel-speaker" data-speaker-id="${id}">
33+
<img src="${image}" alt="${name}" class="carousel-speaker-image"
34+
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
35+
<div class="carousel-speaker-fallback" style="display: none;">
36+
${this.getInitials(name)}
37+
</div>
38+
<div class="carousel-speaker-info">
39+
<div class="carousel-speaker-name">${this.sanitize(name)}</div>
40+
<div class="carousel-speaker-title">${this.sanitize(title)}</div>
41+
</div>
42+
</a>
43+
`;
44+
},
45+
46+
sessionSpeakerDetail({ id, name, title, image }) {
47+
return `
48+
<div class="session-speaker-detail" data-speaker-id="${id}">
49+
<img src="${image}" alt="${name}" class="session-speaker-avatar"
50+
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
51+
<div class="session-speaker-fallback" style="display: none;">
52+
${this.getInitials(name)}
53+
</div>
54+
<div class="session-speaker-info">
55+
<div class="session-speaker-name">${this.sanitize(name)}</div>
56+
<div class="session-speaker-title">${this.sanitize(title)}</div>
57+
</div>
58+
</div>
59+
`;
60+
},
61+
62+
speakerAvatar({ id, name, image }) {
63+
return `
64+
<img src="${image}" alt="${name}" class="speaker-avatar"
65+
data-speaker-id="${id}"
66+
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
67+
<div class="speaker-fallback" style="display: none;" data-speaker-id="${id}">
68+
${this.getInitials(name)}
69+
</div>
70+
`;
71+
},
72+
73+
javaChampionBadge() {
74+
return `
75+
<span class="java-champion-badge">
76+
${createIcon('crown')}
77+
Java Champion
78+
</span>
79+
`;
80+
},
81+
82+
// Helper methods
83+
sanitize(str) {
84+
const div = document.createElement('div');
85+
div.textContent = str;
86+
return div.innerHTML;
87+
},
88+
89+
getInitials(name) {
90+
return name.split(' ')
91+
.map(word => word.charAt(0))
92+
.join('')
93+
.toUpperCase()
94+
.substring(0, 2);
95+
}
96+
};

0 commit comments

Comments
 (0)