mixc
This commit is contained in:
1 parent
00edb9dfc2
commit
3865c45642
18 files changed
+439
-87
No files matched your search
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import { feedType, feedTypes, getFeed } from '../data/insights';
|
||||
import { Picture } from 'astro:assets';
|
||||
import Base from '../layouts/Base.astro';
|
||||
import { formatDate, localePath, t, type Lang } from '../i18n/ui';
|
||||
@@ -7,7 +7,16 @@ import { formatDate, localePath, t, type Lang } from '../i18n/ui';
|
||||
interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const ui = t(lang);
|
||||
const posts = (await getCollection('insights')).sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
const feed = await getFeed();
|
||||
const m = ui.insights.mention;
|
||||
const f = ui.insights.filter;
|
||||
/* Only filters that have entries; "All" first. */
|
||||
const filters = [
|
||||
{ key: 'all', label: f.all, count: feed.length },
|
||||
...feedTypes
|
||||
.map((key) => ({ key, label: f[key], count: feed.filter((item) => feedType(item) === key).length }))
|
||||
.filter((x) => x.count > 0),
|
||||
];
|
||||
---
|
||||
<Base lang={lang} title={ui.meta.insights.title} description={ui.meta.insights.description}>
|
||||
<section class="section section--intro">
|
||||
@@ -19,16 +28,29 @@ const posts = (await getCollection('insights')).sort((a, b) => b.data.date.value
|
||||
|
||||
<section class="section--list" aria-label={ui.insights.title}>
|
||||
<div class="container">
|
||||
<ul class="posts" role="list">
|
||||
{posts.map((post) => (
|
||||
<li class="post">
|
||||
<a class="post__link" href={localePath(lang, `/insights/${post.id}`)} lang={post.data.lang !== lang ? post.data.lang : undefined}>
|
||||
{filters.length > 2 && (
|
||||
<fieldset class="choice-group filters" data-filters>
|
||||
<legend class="choice-group__legend">{f.label}</legend>
|
||||
<div class="choices">
|
||||
{filters.map((x) => (
|
||||
<label class="choice">
|
||||
<input type="radio" name="insights-type" value={x.key} checked={x.key === 'all'} />
|
||||
<span>{x.label} <span class="choice__n">{x.count}</span></span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</fieldset>
|
||||
)}
|
||||
<ul class="posts" role="list" data-feed>
|
||||
{feed.map((item) => item.kind === 'post' ? (
|
||||
<li class="post" data-type={feedType(item)}>
|
||||
<a class="post__link" href={localePath(lang, `/insights/${item.entry.id}`)} lang={item.entry.data.lang !== lang ? item.entry.data.lang : undefined}>
|
||||
<div class="post__media">
|
||||
<Picture
|
||||
src={post.data.cover}
|
||||
alt={post.data.coverAlt}
|
||||
src={item.entry.data.cover}
|
||||
alt={item.entry.data.coverAlt}
|
||||
formats={['avif']}
|
||||
fallbackFormat="webp"
|
||||
fallbackFormat="webp"
|
||||
widths={[400, 640, 960]}
|
||||
sizes="(min-width: 48rem) 40vw, 100vw"
|
||||
loading="lazy"
|
||||
@@ -38,13 +60,32 @@ const posts = (await getCollection('insights')).sort((a, b) => b.data.date.value
|
||||
</div>
|
||||
<div class="post__text">
|
||||
<p class="small muted post__meta">
|
||||
<span>{post.data.category}</span>
|
||||
<time datetime={post.data.date.toISOString().slice(0, 10)}>{formatDate(post.data.date, lang)}</time>
|
||||
<span>{ui.insights.readingTime(post.data.readingTime)}</span>
|
||||
<span>{item.entry.data.category}</span>
|
||||
<time datetime={item.date.toISOString().slice(0, 10)}>{formatDate(item.date, lang)}</time>
|
||||
<span>{ui.insights.readingTime(item.entry.data.readingTime)}</span>
|
||||
</p>
|
||||
<h2 class="h2">{post.data.title}</h2>
|
||||
{post.data.subtitle && <p class="muted">{post.data.subtitle}</p>}
|
||||
{post.data.lang !== lang && <p class="small muted">{ui.insights.inEnglish}</p>}
|
||||
<h2 class="h2">{item.entry.data.title}</h2>
|
||||
{item.entry.data.subtitle && <p class="muted">{item.entry.data.subtitle}</p>}
|
||||
{item.entry.data.lang !== lang && <p class="small muted">{ui.insights.inEnglish}</p>}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
) : (
|
||||
<li class="post" data-type={feedType(item)}>
|
||||
<a class="post__link" href={localePath(lang, `/insights/${item.entry.id}`)} lang={item.entry.data.lang !== lang ? item.entry.data.lang : undefined}>
|
||||
<div class="post__media mention__panel" aria-hidden="true">
|
||||
<span class="mention__source">{item.entry.data.source}</span>
|
||||
<span class="mention__platform">{item.entry.data.platform}</span>
|
||||
</div>
|
||||
<div class="post__text">
|
||||
<p class="small muted post__meta">
|
||||
<span>{m.category(item.entry.data.platform)}</span>
|
||||
<time datetime={item.date.toISOString().slice(0, 10)}>{formatDate(item.date, lang)}</time>
|
||||
<span>{m.source(item.entry.data.source)}</span>
|
||||
</p>
|
||||
<h2 class="h2">{item.entry.data.title}</h2>
|
||||
<p class="muted mention__summary">{item.entry.data.summary}</p>
|
||||
{item.entry.data.lang !== lang && <p class="small muted">{item.entry.data.lang === 'en' ? m.inEnglish : m.inGerman}</p>}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
@@ -54,6 +95,29 @@ const posts = (await getCollection('insights')).sort((a, b) => b.data.date.value
|
||||
</section>
|
||||
</Base>
|
||||
|
||||
<script>
|
||||
/* Filter the list by type. The choice lives in ?type= so a filtered view can be linked;
|
||||
without script every entry shows. */
|
||||
const bar = document.querySelector<HTMLElement>('[data-filters]');
|
||||
const items = document.querySelectorAll<HTMLElement>('[data-feed] > li');
|
||||
if (bar) {
|
||||
const inputs = bar.querySelectorAll<HTMLInputElement>('input[name="insights-type"]');
|
||||
const keys = [...inputs].map((i) => i.value);
|
||||
const apply = (key: string, push: boolean) => {
|
||||
if (!keys.includes(key)) key = 'all';
|
||||
inputs.forEach((i) => { i.checked = i.value === key; });
|
||||
items.forEach((li) => { li.hidden = key !== 'all' && li.dataset.type !== key; });
|
||||
if (push) {
|
||||
const url = new URL(location.href);
|
||||
if (key === 'all') url.searchParams.delete('type'); else url.searchParams.set('type', key);
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
};
|
||||
bar.addEventListener('change', (e) => apply((e.target as HTMLInputElement).value, true));
|
||||
apply(new URL(location.href).searchParams.get('type') ?? 'all', false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.index__title { grid-column: 1 / -1; }
|
||||
.index__lead { grid-column: 1 / -1; max-width: var(--measure); color: var(--ink-muted); }
|
||||
@@ -62,6 +126,9 @@ const posts = (await getCollection('insights')).sort((a, b) => b.data.date.value
|
||||
.index__lead { grid-column: 1 / span 5; }
|
||||
}
|
||||
.section--list { padding-bottom: var(--section); }
|
||||
/* Filter row: the shared segmented choice (base.css), as on the research list. */
|
||||
.filters { padding-bottom: var(--space-6); }
|
||||
.post[hidden] { display: none; }
|
||||
.posts { border-top: var(--hairline) solid var(--line); }
|
||||
.post { border-bottom: var(--hairline) solid var(--line); }
|
||||
.post__link {
|
||||
@@ -74,6 +141,18 @@ const posts = (await getCollection('insights')).sort((a, b) => b.data.date.value
|
||||
.post__img, :global(.post__img) { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }
|
||||
.post__text { display: grid; gap: var(--space-4); align-content: start; }
|
||||
.post__meta { display: flex; flex-wrap: wrap; gap: var(--space-4); }
|
||||
/* A mention has no cover: a typographic panel names who posted it and where. */
|
||||
.mention__panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
aspect-ratio: 3 / 2;
|
||||
padding: var(--space-5);
|
||||
background: var(--paper-deep);
|
||||
}
|
||||
.mention__source { font-size: var(--step-title); line-height: var(--leading-title); letter-spacing: var(--track-h2); color: var(--ink-muted); }
|
||||
.mention__platform { font-size: var(--step-small); line-height: var(--leading-small); color: var(--ink-muted); }
|
||||
.mention__summary { max-width: var(--measure); }
|
||||
@media (min-width: 48rem) {
|
||||
.post__link { grid-template-columns: 5fr 7fr; }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user