ferst-core 0.6.1 → 0.7.0
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.
- package/bin/ferst-update.mjs +135 -6
- package/components/BlockRenderer.astro +107 -103
- package/components/blocks/Prose.astro +56 -39
- package/components/recipes/Documents.astro +146 -0
- package/components/recipes/Hero.astro +383 -191
- package/components/recipes/Tiles.astro +23 -4
- package/components/recipes/Video.astro +157 -0
- package/content/blocks.ts +773 -719
- package/content/cookies.ts +112 -103
- package/examples/primitives.ts +28 -28
- package/examples/recipes.ts +4 -0
- package/lib/documents.ts +62 -0
- package/lib/inline-markdown.ts +59 -52
- package/lib/prose.ts +29 -0
- package/lib/video.ts +56 -0
- package/package.json +1 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Documents — a tidy list of files (newsletters, minutes, policies, forms): each row
|
|
4
|
+
* a type badge, the title, its date and an optional note, newest first, optionally
|
|
5
|
+
* grouped by year. Files open in the browser's own viewer (a PDF in a new tab) rather
|
|
6
|
+
* than an in-page viewer, which works poorly on phones. Logic lives in lib/documents.
|
|
7
|
+
*/
|
|
8
|
+
import Heading from '../blocks/Heading.astro';
|
|
9
|
+
import Icon from '../Icon.astro';
|
|
10
|
+
import { documentGroups, type DocumentItem } from '../../lib/documents';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
title?: string;
|
|
14
|
+
intro?: string;
|
|
15
|
+
groupByYear?: boolean;
|
|
16
|
+
items?: DocumentItem[];
|
|
17
|
+
}
|
|
18
|
+
const { title, intro, groupByYear = false, items = [] } = Astro.props;
|
|
19
|
+
const groups = documentGroups(items, groupByYear);
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<section class="b-docs">
|
|
23
|
+
{(title || intro) && (
|
|
24
|
+
<div class="b-docs__head">
|
|
25
|
+
{title && <Heading text={title} level={2} />}
|
|
26
|
+
{intro && <p class="b-docs__intro">{intro}</p>}
|
|
27
|
+
</div>
|
|
28
|
+
)}
|
|
29
|
+
{items.length === 0 ? (
|
|
30
|
+
<p class="b-docs__intro">No documents yet.</p>
|
|
31
|
+
) : (
|
|
32
|
+
groups.map((g) => (
|
|
33
|
+
<div class="b-docs__group">
|
|
34
|
+
{g.year && <h3 class="b-docs__year">{g.year}</h3>}
|
|
35
|
+
<ul class="b-docs__list">
|
|
36
|
+
{g.items.map((d) => (
|
|
37
|
+
<li>
|
|
38
|
+
<a class="b-docs__row" href={d.file} target={d.kind === 'PDF' ? '_blank' : undefined} rel={d.kind === 'PDF' ? 'noopener' : undefined}>
|
|
39
|
+
<span class="b-docs__icon" aria-hidden="true"><Icon name="document" size={22} /></span>
|
|
40
|
+
<span class="b-docs__body">
|
|
41
|
+
<span class="b-docs__title">{d.title}</span>
|
|
42
|
+
{(d.dateLabel || d.note) && (
|
|
43
|
+
<span class="b-docs__meta">{[d.dateLabel, d.note].filter(Boolean).join(' · ')}</span>
|
|
44
|
+
)}
|
|
45
|
+
</span>
|
|
46
|
+
<span class="b-docs__kind">{d.kind}</span>
|
|
47
|
+
</a>
|
|
48
|
+
</li>
|
|
49
|
+
))}
|
|
50
|
+
</ul>
|
|
51
|
+
</div>
|
|
52
|
+
))
|
|
53
|
+
)}
|
|
54
|
+
</section>
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
.b-docs {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
gap: 1.5rem;
|
|
61
|
+
max-width: var(--max);
|
|
62
|
+
margin-inline: auto;
|
|
63
|
+
padding-inline: 1rem;
|
|
64
|
+
width: 100%;
|
|
65
|
+
}
|
|
66
|
+
.b-docs__head {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: 0.5rem;
|
|
70
|
+
}
|
|
71
|
+
.b-docs__intro {
|
|
72
|
+
margin: 0;
|
|
73
|
+
color: var(--muted);
|
|
74
|
+
line-height: 1.6;
|
|
75
|
+
}
|
|
76
|
+
.b-docs__group {
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
gap: 0.5rem;
|
|
80
|
+
}
|
|
81
|
+
.b-docs__year {
|
|
82
|
+
margin: 0;
|
|
83
|
+
font-size: 0.8125rem;
|
|
84
|
+
font-weight: 600;
|
|
85
|
+
letter-spacing: 0.08em;
|
|
86
|
+
text-transform: uppercase;
|
|
87
|
+
color: var(--muted);
|
|
88
|
+
}
|
|
89
|
+
.b-docs__list {
|
|
90
|
+
list-style: none;
|
|
91
|
+
margin: 0;
|
|
92
|
+
padding: 0;
|
|
93
|
+
border: var(--card-border);
|
|
94
|
+
border-radius: var(--radius);
|
|
95
|
+
background: var(--bg-surface);
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
}
|
|
98
|
+
.b-docs__list li + li {
|
|
99
|
+
border-top: var(--card-border);
|
|
100
|
+
}
|
|
101
|
+
.b-docs__row {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 0.9rem;
|
|
105
|
+
padding: 0.85rem 1rem;
|
|
106
|
+
color: var(--fg);
|
|
107
|
+
text-decoration: none;
|
|
108
|
+
}
|
|
109
|
+
.b-docs__row:hover {
|
|
110
|
+
background: var(--bg-section);
|
|
111
|
+
}
|
|
112
|
+
.b-docs__row:focus-visible {
|
|
113
|
+
outline: 2px solid var(--gold);
|
|
114
|
+
outline-offset: -2px;
|
|
115
|
+
}
|
|
116
|
+
.b-docs__icon {
|
|
117
|
+
flex: none;
|
|
118
|
+
color: var(--gold);
|
|
119
|
+
display: grid;
|
|
120
|
+
}
|
|
121
|
+
.b-docs__body {
|
|
122
|
+
flex: 1;
|
|
123
|
+
min-width: 0;
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
gap: 0.15rem;
|
|
127
|
+
}
|
|
128
|
+
.b-docs__title {
|
|
129
|
+
font-weight: 600;
|
|
130
|
+
overflow-wrap: anywhere;
|
|
131
|
+
}
|
|
132
|
+
.b-docs__meta {
|
|
133
|
+
font-size: 0.875rem;
|
|
134
|
+
color: var(--muted);
|
|
135
|
+
}
|
|
136
|
+
.b-docs__kind {
|
|
137
|
+
flex: none;
|
|
138
|
+
font-size: 0.75rem;
|
|
139
|
+
font-weight: 600;
|
|
140
|
+
letter-spacing: 0.04em;
|
|
141
|
+
padding: 0.2rem 0.55rem;
|
|
142
|
+
border-radius: 999px;
|
|
143
|
+
background: var(--bg-tag);
|
|
144
|
+
color: var(--fg);
|
|
145
|
+
}
|
|
146
|
+
</style>
|