Skip to content

Commit 0046a57

Browse files
moved news to _included & added News section sliding animation
1 parent d6f9583 commit 0046a57

File tree

2 files changed

+196
-20
lines changed

2 files changed

+196
-20
lines changed

_includes/news.qmd

+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
listing:
3+
- id: news
4+
contents:
5+
- "news/posts/*/index.qmd"
6+
sort: date desc
7+
type: grid
8+
grid-columns: 3
9+
categories: false
10+
sort-ui: false
11+
filter-ui: false
12+
fields: [title, description, date, reading-time, author]
13+
image-height: "200"
14+
---
15+
16+
### News
17+
18+
::: {#news}
19+
20+
:::
21+
22+
[See all news →](news/)
23+
24+
25+
```{=html}
26+
<style>
27+
/* hide default Quarto grid once JS enhancement is active */
28+
#listing-news.enhanced-carousel .list.grid.quarto-listing-cols-3 {
29+
display: none !important;
30+
}
31+
32+
/* carousel wrapper */
33+
#carousel-container {
34+
width: 100%;
35+
overflow: hidden;
36+
position: relative;
37+
}
38+
/* focus outline for accessibility */
39+
#carousel-container:focus {
40+
outline: 2px solid #007acc;
41+
outline-offset: 4px;
42+
}
43+
44+
/* sliding track */
45+
#carousel-track {
46+
display: flex;
47+
align-items: flex-start;
48+
transition: transform 0.5s ease;
49+
will-change: transform;
50+
}
51+
52+
/* each slide sizing & height animation */
53+
#carousel-track > .g-col-1 {
54+
flex: 0 0 33.3333%;
55+
padding: 1rem;
56+
box-sizing: border-box;
57+
display: block !important;
58+
transition: height 0.3s ease;
59+
}
60+
61+
/* single‑column on mobile */
62+
@media (max-width: 768px) {
63+
#carousel-track > .g-col-1 {
64+
flex: 0 0 100%;
65+
}
66+
}
67+
68+
/* remove default card styling */
69+
#carousel-track > .g-col-1 .card {
70+
background: none;
71+
box-shadow: none;
72+
border: none;
73+
}
74+
75+
/* trim default listing padding */
76+
.quarto-listing {
77+
padding-bottom: 0 !important;
78+
}
79+
</style>
80+
81+
<script>
82+
// initialize carousel after DOM is ready
83+
document.addEventListener('DOMContentLoaded', function () {
84+
const listing = document.getElementById('listing-news');
85+
if (!listing) return;
86+
listing.classList.add('enhanced-carousel'); // flag JS enhancement
87+
88+
const items = Array.from(
89+
listing.querySelectorAll('.list.grid.quarto-listing-cols-3 > .g-col-1')
90+
); // collect slides
91+
const N = items.length; // total number of slides
92+
if (!N) return;
93+
94+
// create carousel wrapper with accessibility roles
95+
const carouselContainer = document.createElement('div');
96+
carouselContainer.id = 'carousel-container';
97+
carouselContainer.setAttribute('role', 'region');
98+
carouselContainer.setAttribute('aria-live', 'polite');
99+
carouselContainer.setAttribute('tabindex', '0');
100+
101+
// create track element
102+
const carouselTrack = document.createElement('div');
103+
carouselTrack.id = 'carousel-track';
104+
105+
items.forEach(i => carouselTrack.appendChild(i)); // move slides into track
106+
carouselContainer.appendChild(carouselTrack);
107+
listing.parentNode.insertBefore(carouselContainer, listing.nextSibling); // insert carousel
108+
109+
// determine items per view (responsive)
110+
function getItemsPerView() { return window.innerWidth < 768 ? 1 : 3; }
111+
let itemsPerView = getItemsPerView();
112+
if (N <= itemsPerView) { // handle few slides
113+
const h = Math.max(...items.map(i => i.offsetHeight));
114+
carouselContainer.style.height = h + 'px';
115+
return;
116+
}
117+
118+
let currentIndex = 0;
119+
let maxIndex = N - itemsPerView;
120+
let shiftPercent = 100 / itemsPerView;
121+
const displayDuration = 2000; // slide interval
122+
123+
// normalize visible slide heights
124+
function recalcHeight() {
125+
items.forEach(i => i.style.height = 'auto');
126+
const vis = items.slice(currentIndex, currentIndex + itemsPerView);
127+
const h = Math.max(...vis.map(i => i.offsetHeight));
128+
vis.forEach(i => i.style.height = h + 'px');
129+
carouselContainer.style.height = h + 'px';
130+
}
131+
132+
// move track and adjust heights
133+
function updateSlide(idx) {
134+
carouselTrack.style.transform = `translateX(-${idx * shiftPercent}%)`;
135+
recalcHeight();
136+
}
137+
138+
// slide controls
139+
function nextSlide() {
140+
currentIndex = currentIndex < maxIndex ? currentIndex + 1 : 0;
141+
updateSlide(currentIndex);
142+
}
143+
function prevSlide() {
144+
currentIndex = currentIndex > 0 ? currentIndex - 1 : maxIndex;
145+
updateSlide(currentIndex);
146+
}
147+
148+
// initial render
149+
recalcHeight();
150+
updateSlide(0);
151+
152+
// auto-play with pause on hover/focus/visibility
153+
let intervalId = setInterval(nextSlide, displayDuration);
154+
['mouseenter','focusin'].forEach(e =>
155+
carouselContainer.addEventListener(e, () => clearInterval(intervalId))
156+
);
157+
['mouseleave','focusout'].forEach(e =>
158+
carouselContainer.addEventListener(e, () => {
159+
clearInterval(intervalId);
160+
intervalId = setInterval(nextSlide, displayDuration);
161+
})
162+
);
163+
document.addEventListener('visibilitychange', () => {
164+
if (document.hidden) clearInterval(intervalId);
165+
else {
166+
clearInterval(intervalId);
167+
intervalId = setInterval(nextSlide, displayDuration);
168+
}
169+
});
170+
171+
// keyboard navigation
172+
carouselContainer.addEventListener('keydown', e => {
173+
if (e.key === 'ArrowRight') { nextSlide(); e.preventDefault(); }
174+
if (e.key === 'ArrowLeft') { prevSlide(); e.preventDefault(); }
175+
});
176+
177+
// debounce on window resize
178+
let resizeTimeout = null;
179+
window.addEventListener('resize', () => {
180+
clearTimeout(resizeTimeout);
181+
resizeTimeout = setTimeout(() => {
182+
const v = getItemsPerView();
183+
if (v !== itemsPerView) {
184+
itemsPerView = v;
185+
maxIndex = N - itemsPerView;
186+
shiftPercent = 100 / itemsPerView;
187+
currentIndex = Math.min(currentIndex, maxIndex);
188+
}
189+
recalcHeight();
190+
updateSlide(currentIndex);
191+
}, 150);
192+
});
193+
});
194+
</script>
195+
```

index.qmd

+1-20
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,6 @@ toc: false
33
page-layout: full
44
section-divs: false
55
hide-description: true
6-
listing:
7-
- id: news
8-
contents:
9-
- "news/posts/*/index.qmd"
10-
sort: date desc
11-
type: grid
12-
grid-columns: 3
13-
categories: false
14-
sort-ui: false
15-
filter-ui: false
16-
fields: [title, description, date]
17-
max-items: 3
18-
image-height: "200"
196
description: |
207
Turing.jl is a probabilistic programming language and Bayesian modelling framework for the Julia programming language.
218
---
@@ -94,13 +81,7 @@ end
9481

9582
:::::
9683

97-
### News {.pb-3}
98-
99-
::: {#news}
100-
101-
:::
102-
103-
[See all news &rarr;](news/)
84+
{{< include _includes/news.qmd >}}
10485

10586
```{=html}
10687
<h3 class="pb-3 section-start-space">

0 commit comments

Comments
 (0)