@meith/theme-default 0.33.2 → 0.33.4
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/package.json +3 -3
- package/src/shared.tsx +74 -5
- package/src/slots/announcement.tsx +4 -4
- package/src/slots/auth-page.tsx +7 -5
- package/src/slots/board-index.tsx +7 -7
- package/src/slots/board-stats.tsx +10 -8
- package/src/slots/category-block.tsx +7 -6
- package/src/slots/discovery-view.tsx +36 -33
- package/src/slots/editor-toolbar.tsx +2 -2
- package/src/slots/footer.tsx +2 -2
- package/src/slots/forum-display.tsx +30 -31
- package/src/slots/forum-row.tsx +35 -12
- package/src/slots/header.tsx +62 -33
- package/src/slots/latest-posts.tsx +33 -21
- package/src/slots/latest-threads.tsx +16 -10
- package/src/slots/member-profile.tsx +35 -22
- package/src/slots/navigation.tsx +3 -3
- package/src/slots/pagination.tsx +2 -2
- package/src/slots/panel-nav.tsx +15 -13
- package/src/slots/panel-page.tsx +12 -9
- package/src/slots/panel-shell.tsx +35 -13
- package/src/slots/post-bit.tsx +61 -34
- package/src/slots/post-form.tsx +7 -7
- package/src/slots/subforum-list.tsx +41 -35
- package/src/slots/thread-row.tsx +15 -12
- package/src/slots/thread-view.tsx +4 -4
- package/src/slots/user-panel.tsx +13 -10
- package/src/slots/who-is-online.tsx +11 -5
- package/src/theme.ts +1 -1
package/src/slots/header.tsx
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
import type { HeaderModel, SlotCopy } from '@meith/theme-kit'
|
|
2
2
|
import { fromSlotCopy, linkTarget } from '@meith/theme-kit'
|
|
3
3
|
|
|
4
|
-
import { PAGE } from '../shared'
|
|
4
|
+
import { BELOW_HEADER, PAGE } from '../shared'
|
|
5
5
|
|
|
6
6
|
const DESKTOP_LINK =
|
|
7
|
-
'inline-flex h-
|
|
7
|
+
'inline-flex h-9 items-center rounded-full px-3 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground'
|
|
8
8
|
|
|
9
9
|
const MOBILE_LINK =
|
|
10
|
-
'flex min-h-11 items-center rounded-
|
|
10
|
+
'flex min-h-11 items-center rounded-lg px-3 text-sm font-medium text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
11
11
|
|
|
12
12
|
const SUMMARY =
|
|
13
13
|
'cursor-default list-none select-none [&::-webkit-details-marker]:hidden [&::marker]:content-none'
|
|
14
14
|
|
|
15
15
|
function BoardMark({ boardTitle, logo }: Pick<HeaderModel, 'boardTitle' | 'logo'>) {
|
|
16
|
-
if (logo === undefined)
|
|
16
|
+
if (logo === undefined) {
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<span
|
|
20
|
+
aria-hidden="true"
|
|
21
|
+
className="flex size-8 shrink-0 items-center justify-center rounded-lg bg-primary text-sm font-bold text-primary-foreground shadow-sm"
|
|
22
|
+
>
|
|
23
|
+
{(Array.from(boardTitle.trim())[0] ?? '?').toUpperCase()}
|
|
24
|
+
</span>
|
|
25
|
+
<span className="truncate">{boardTitle}</span>
|
|
26
|
+
</>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
const image = (
|
|
19
31
|
<img
|
|
@@ -38,13 +50,13 @@ function Submenu({ items }: { items: HeaderModel['navigation'][number]['submenu'
|
|
|
38
50
|
if (items === undefined || items.length === 0) return null
|
|
39
51
|
|
|
40
52
|
return (
|
|
41
|
-
<ul className="invisible absolute top-full left-0 z-30 min-w-
|
|
53
|
+
<ul className="invisible absolute top-full left-0 z-30 mt-1 min-w-52 rounded-xl border border-border bg-card p-1.5 opacity-0 shadow-elevation transition-opacity group-focus-within:visible group-focus-within:opacity-100 group-hover:visible group-hover:opacity-100">
|
|
42
54
|
{items.map((child) => (
|
|
43
55
|
<li key={child.href}>
|
|
44
56
|
<a
|
|
45
57
|
href={child.href}
|
|
46
58
|
{...linkTarget(child)}
|
|
47
|
-
className="flex items-center rounded-
|
|
59
|
+
className="flex items-center rounded-lg px-2.5 py-1.5 text-sm text-muted-foreground pointer-coarse:min-h-11 hover:bg-muted hover:text-foreground"
|
|
48
60
|
>
|
|
49
61
|
{child.label}
|
|
50
62
|
</a>
|
|
@@ -103,7 +115,7 @@ function MobileNavItem({ item }: { item: HeaderModel['navigation'][number] }) {
|
|
|
103
115
|
<li>
|
|
104
116
|
<details data-nav-disclosure name="default-header-submenu" className="group/submenu">
|
|
105
117
|
<summary
|
|
106
|
-
className={`${SUMMARY} flex min-h-11 items-center justify-between gap-2 rounded-
|
|
118
|
+
className={`${SUMMARY} flex min-h-11 items-center justify-between gap-2 rounded-lg pl-3 text-sm font-medium text-muted-foreground hover:bg-muted hover:text-foreground`}
|
|
107
119
|
>
|
|
108
120
|
<a
|
|
109
121
|
href={item.href}
|
|
@@ -141,30 +153,67 @@ export function Header({
|
|
|
141
153
|
const c = (key: string) => fromSlotCopy(copy, `default.header.${key}`)
|
|
142
154
|
|
|
143
155
|
const hasNavigation = navigation.length > 0
|
|
156
|
+
const opensMenus = navigation.some(
|
|
157
|
+
(item) => item.submenu !== undefined && item.submenu.length > 0,
|
|
158
|
+
)
|
|
144
159
|
|
|
145
160
|
return (
|
|
146
|
-
<header
|
|
147
|
-
|
|
161
|
+
<header
|
|
162
|
+
data-header-peek
|
|
163
|
+
className="sticky top-0 z-40 border-b border-b-border bg-card/95 backdrop-blur motion-safe:transition-transform motion-safe:duration-200 data-peek:-translate-y-full supports-[backdrop-filter]:bg-card/85"
|
|
164
|
+
>
|
|
165
|
+
<div className={`${PAGE} flex min-h-14 items-center gap-x-3 sm:gap-x-4`}>
|
|
148
166
|
<a
|
|
149
167
|
href={homeHref}
|
|
150
|
-
className="
|
|
168
|
+
className="flex min-w-0 shrink-0 items-center gap-2.5 text-lg font-semibold tracking-tight text-foreground transition-colors hover:text-primary"
|
|
151
169
|
>
|
|
152
170
|
<BoardMark boardTitle={boardTitle} logo={logo} />
|
|
153
171
|
</a>
|
|
154
172
|
|
|
155
|
-
{
|
|
173
|
+
{hasNavigation && (
|
|
174
|
+
<nav
|
|
175
|
+
aria-label={c('sections')}
|
|
176
|
+
className={
|
|
177
|
+
opensMenus
|
|
178
|
+
? 'hidden min-w-0 flex-1 lg:block'
|
|
179
|
+
: 'hidden min-w-0 flex-1 overflow-x-auto [mask-image:linear-gradient(to_right,black_calc(100%-1.5rem),transparent)] [scrollbar-width:none] lg:block'
|
|
180
|
+
}
|
|
181
|
+
>
|
|
182
|
+
<ul
|
|
183
|
+
data-nav-view="desktop"
|
|
184
|
+
className={
|
|
185
|
+
opensMenus
|
|
186
|
+
? 'flex flex-wrap items-center gap-0.5 py-1'
|
|
187
|
+
: 'flex items-center gap-0.5 pr-6 whitespace-nowrap'
|
|
188
|
+
}
|
|
189
|
+
>
|
|
190
|
+
{navigation.map((item) => (
|
|
191
|
+
<li key={item.href} className="group relative shrink-0">
|
|
192
|
+
<a href={item.href} {...linkTarget(item)} className={DESKTOP_LINK}>
|
|
193
|
+
{item.label}
|
|
194
|
+
</a>
|
|
195
|
+
<Submenu items={item.submenu} />
|
|
196
|
+
</li>
|
|
197
|
+
))}
|
|
198
|
+
</ul>
|
|
199
|
+
</nav>
|
|
200
|
+
)}
|
|
201
|
+
|
|
202
|
+
<div className="ms-auto flex min-w-0 shrink-0 items-center gap-2 sm:gap-3">{children}</div>
|
|
156
203
|
|
|
157
204
|
{hasNavigation && (
|
|
158
205
|
<nav aria-label={c('sections')} data-nav-view="mobile" className="lg:hidden">
|
|
159
206
|
<details data-nav-disclosure className="group/menu">
|
|
160
207
|
<summary
|
|
161
|
-
className={`${SUMMARY} flex size-
|
|
208
|
+
className={`${SUMMARY} flex size-10 items-center justify-center rounded-lg border border-border bg-card text-foreground transition-colors hover:border-primary/40 hover:text-primary group-open/menu:border-primary/40 group-open/menu:bg-primary/8 group-open/menu:text-primary`}
|
|
162
209
|
>
|
|
163
210
|
<MenuGlyph />
|
|
164
211
|
<span className="sr-only">{c('sections')}</span>
|
|
165
212
|
</summary>
|
|
166
213
|
|
|
167
|
-
<div
|
|
214
|
+
<div
|
|
215
|
+
className={`absolute inset-x-0 ${BELOW_HEADER} z-30 max-h-[calc(100dvh-3.5rem)] overflow-y-auto border-b border-border bg-card shadow-elevation`}
|
|
216
|
+
>
|
|
168
217
|
<ul className={`${PAGE} flex flex-col gap-0.5 py-2`}>
|
|
169
218
|
{navigation.map((item) => (
|
|
170
219
|
<MobileNavItem key={item.href} item={item} />
|
|
@@ -175,26 +224,6 @@ export function Header({
|
|
|
175
224
|
</nav>
|
|
176
225
|
)}
|
|
177
226
|
</div>
|
|
178
|
-
|
|
179
|
-
{hasNavigation && (
|
|
180
|
-
<nav aria-label={c('sections')} className="hidden border-t border-border lg:block">
|
|
181
|
-
<div className={PAGE}>
|
|
182
|
-
<ul
|
|
183
|
-
data-nav-view="desktop"
|
|
184
|
-
className="-mx-3 flex flex-wrap items-center gap-0.5 py-1 text-sm"
|
|
185
|
-
>
|
|
186
|
-
{navigation.map((item) => (
|
|
187
|
-
<li key={item.href} className="group relative shrink-0">
|
|
188
|
-
<a href={item.href} {...linkTarget(item)} className={DESKTOP_LINK}>
|
|
189
|
-
{item.label}
|
|
190
|
-
</a>
|
|
191
|
-
<Submenu items={item.submenu} />
|
|
192
|
-
</li>
|
|
193
|
-
))}
|
|
194
|
-
</ul>
|
|
195
|
-
</div>
|
|
196
|
-
</nav>
|
|
197
|
-
)}
|
|
198
227
|
</header>
|
|
199
228
|
)
|
|
200
229
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LatestPostsModel, SlotCopy } from '@meith/theme-kit'
|
|
2
2
|
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
3
|
import {
|
|
4
|
+
Avatar,
|
|
4
5
|
Card,
|
|
5
6
|
CardContent,
|
|
6
7
|
CardHeader,
|
|
@@ -10,15 +11,15 @@ import {
|
|
|
10
11
|
EmptyTitle,
|
|
11
12
|
} from '@meith/ui'
|
|
12
13
|
|
|
13
|
-
import { LINK, MUTED_LINK, NUMERIC,
|
|
14
|
+
import { LINK, MUTED_LINK, NUMERIC, Stamp, UserRef } from '../shared'
|
|
14
15
|
|
|
15
16
|
export function LatestPosts({ posts, capturedAt, copy }: LatestPostsModel & { copy: SlotCopy }) {
|
|
16
17
|
const c = (key: string) => fromSlotCopy(copy, `default.latestPosts.${key}`)
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
|
-
<Card aria-labelledby="latest-posts-heading">
|
|
20
|
-
<CardHeader className=
|
|
21
|
-
<CardTitle id="latest-posts-heading" className="text-
|
|
20
|
+
<Card aria-labelledby="latest-posts-heading" className="rounded-xl">
|
|
21
|
+
<CardHeader className="bg-card">
|
|
22
|
+
<CardTitle id="latest-posts-heading" className="text-sm">
|
|
22
23
|
{c('heading')}
|
|
23
24
|
</CardTitle>
|
|
24
25
|
<p className={`text-xs text-muted-foreground ${NUMERIC}`}>
|
|
@@ -35,25 +36,36 @@ export function LatestPosts({ posts, capturedAt, copy }: LatestPostsModel & { co
|
|
|
35
36
|
<CardContent className="px-0 py-0">
|
|
36
37
|
<ul className="divide-y divide-border">
|
|
37
38
|
{posts.map((post) => (
|
|
38
|
-
<li
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{
|
|
44
|
-
|
|
39
|
+
<li
|
|
40
|
+
key={post.href}
|
|
41
|
+
className="flex gap-3 px-4 py-2.5 transition-colors hover:bg-muted/50"
|
|
42
|
+
>
|
|
43
|
+
<Avatar
|
|
44
|
+
src={null}
|
|
45
|
+
name={post.author.username}
|
|
46
|
+
size={28}
|
|
47
|
+
className="mt-0.5 rounded-full"
|
|
48
|
+
/>
|
|
49
|
+
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
50
|
+
<a
|
|
51
|
+
href={post.href}
|
|
52
|
+
className={`truncate text-sm font-medium text-foreground ${LINK}`}
|
|
53
|
+
>
|
|
54
|
+
{post.threadTitle}
|
|
55
|
+
</a>
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
{post.excerpt !== '' && (
|
|
58
|
+
<p className="line-clamp-2 text-xs text-muted-foreground">{post.excerpt}</p>
|
|
59
|
+
)}
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
<p className="truncate text-xs text-muted-foreground">
|
|
62
|
+
<UserRef user={post.author} className="font-normal" /> {c('in')}{' '}
|
|
63
|
+
<a href={post.forum.href} className={MUTED_LINK}>
|
|
64
|
+
{post.forum.label}
|
|
65
|
+
</a>{' '}
|
|
66
|
+
{c('dot')} <Stamp at={post.postedAt} />
|
|
67
|
+
</p>
|
|
68
|
+
</div>
|
|
57
69
|
</li>
|
|
58
70
|
))}
|
|
59
71
|
</ul>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
EmptyTitle,
|
|
11
11
|
} from '@meith/ui'
|
|
12
12
|
|
|
13
|
-
import { LINK, MUTED_LINK, NUMERIC,
|
|
13
|
+
import { LINK, MUTED_LINK, NUMERIC, PILL, Stamp, UserRef } from '../shared'
|
|
14
14
|
|
|
15
15
|
export function LatestThreads({
|
|
16
16
|
threads,
|
|
@@ -20,9 +20,9 @@ export function LatestThreads({
|
|
|
20
20
|
const c = (key: string) => fromSlotCopy(copy, `default.latestThreads.${key}`)
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
|
-
<Card aria-labelledby="latest-threads-heading">
|
|
24
|
-
<CardHeader className=
|
|
25
|
-
<CardTitle id="latest-threads-heading" className="text-
|
|
23
|
+
<Card aria-labelledby="latest-threads-heading" className="rounded-xl">
|
|
24
|
+
<CardHeader className="bg-card">
|
|
25
|
+
<CardTitle id="latest-threads-heading" className="text-sm">
|
|
26
26
|
{c('heading')}
|
|
27
27
|
</CardTitle>
|
|
28
28
|
<p className={`text-xs text-muted-foreground ${NUMERIC}`}>
|
|
@@ -39,17 +39,23 @@ export function LatestThreads({
|
|
|
39
39
|
<CardContent className="px-0 py-0">
|
|
40
40
|
<ul className="divide-y divide-border">
|
|
41
41
|
{threads.map((thread) => (
|
|
42
|
-
<li
|
|
43
|
-
|
|
42
|
+
<li
|
|
43
|
+
key={thread.href}
|
|
44
|
+
className="flex flex-col gap-0.5 px-4 py-2.5 transition-colors hover:bg-muted/50"
|
|
45
|
+
>
|
|
46
|
+
<div className="flex items-start justify-between gap-2">
|
|
44
47
|
<a
|
|
45
48
|
href={thread.href}
|
|
46
|
-
className={`
|
|
49
|
+
className={`line-clamp-2 text-sm font-medium text-foreground ${LINK}`}
|
|
47
50
|
>
|
|
48
51
|
{thread.title}
|
|
49
52
|
</a>
|
|
50
|
-
<span className={
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
<span className={`${PILL} shrink-0 ${NUMERIC}`}>
|
|
54
|
+
{thread.replyCount.label}
|
|
55
|
+
<span className="sr-only">
|
|
56
|
+
{' '}
|
|
57
|
+
{thread.replyCount.value === 1 ? c('reply.one') : c('reply.other')}
|
|
58
|
+
</span>
|
|
53
59
|
</span>
|
|
54
60
|
</div>
|
|
55
61
|
|
|
@@ -22,22 +22,33 @@ export function MemberProfile({
|
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
24
|
<div className={PAGE_BODY}>
|
|
25
|
-
<Card>
|
|
26
|
-
<CardContent className="flex flex-col gap-4
|
|
25
|
+
<Card className="rounded-xl">
|
|
26
|
+
<CardContent className="flex flex-col gap-4 px-5 py-5 sm:flex-row sm:items-center sm:justify-between sm:px-6">
|
|
27
27
|
<div className="flex min-w-0 items-center gap-4">
|
|
28
|
-
<Avatar src={avatarUrl} name={user.username} size={
|
|
28
|
+
<Avatar src={avatarUrl} name={user.username} size={80} className="rounded-2xl" />
|
|
29
29
|
|
|
30
30
|
<div className="min-w-0">
|
|
31
31
|
<h1
|
|
32
|
-
className={cn(
|
|
32
|
+
className={cn(
|
|
33
|
+
'font-heading text-2xl font-semibold tracking-tight break-words',
|
|
34
|
+
user.nameClass,
|
|
35
|
+
)}
|
|
33
36
|
>
|
|
34
37
|
{user.username}
|
|
35
38
|
</h1>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<span
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
<p className="mt-1.5 flex flex-wrap gap-1.5">
|
|
40
|
+
{groupTags(groups, title).map((group) => (
|
|
41
|
+
<span
|
|
42
|
+
key={group.title}
|
|
43
|
+
className={cn(
|
|
44
|
+
'inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground',
|
|
45
|
+
group.nameClass,
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
{group.title}
|
|
49
|
+
</span>
|
|
50
|
+
))}
|
|
51
|
+
</p>
|
|
41
52
|
</div>
|
|
42
53
|
</div>
|
|
43
54
|
|
|
@@ -62,18 +73,20 @@ export function MemberProfile({
|
|
|
62
73
|
)}
|
|
63
74
|
</CardContent>
|
|
64
75
|
|
|
65
|
-
<dl className="grid grid-cols-3
|
|
66
|
-
<div>
|
|
67
|
-
<dd className={`text-lg font-semibold text-
|
|
76
|
+
<dl className="grid grid-cols-3 divide-x divide-border border-t border-border">
|
|
77
|
+
<div className="px-5 py-3.5 sm:px-6">
|
|
78
|
+
<dd className={`text-lg font-semibold text-foreground ${NUMERIC}`}>
|
|
79
|
+
{postCount.label}
|
|
80
|
+
</dd>
|
|
68
81
|
<dt className="text-xs text-muted-foreground">{c('postsLabel')}</dt>
|
|
69
82
|
</div>
|
|
70
|
-
<div>
|
|
83
|
+
<div className="px-5 py-3.5 sm:px-6">
|
|
71
84
|
<dd className="text-sm font-medium text-foreground">
|
|
72
85
|
<Stamp at={joinedAt} />
|
|
73
86
|
</dd>
|
|
74
87
|
<dt className="text-xs text-muted-foreground">{c('joinedLabel')}</dt>
|
|
75
88
|
</div>
|
|
76
|
-
<div>
|
|
89
|
+
<div className="px-5 py-3.5 sm:px-6">
|
|
77
90
|
<dd className="text-sm font-medium text-foreground">
|
|
78
91
|
{lastVisitAt === null ? c('never') : <Stamp at={lastVisitAt} />}
|
|
79
92
|
</dd>
|
|
@@ -83,18 +96,18 @@ export function MemberProfile({
|
|
|
83
96
|
</Card>
|
|
84
97
|
|
|
85
98
|
{fields.length > 0 && (
|
|
86
|
-
<Card aria-labelledby="profile-fields-heading">
|
|
87
|
-
<CardHeader>
|
|
99
|
+
<Card aria-labelledby="profile-fields-heading" className="rounded-xl">
|
|
100
|
+
<CardHeader className="bg-card">
|
|
88
101
|
<CardTitle id="profile-fields-heading" className="text-sm">
|
|
89
102
|
{c('about')}
|
|
90
103
|
</CardTitle>
|
|
91
104
|
</CardHeader>
|
|
92
|
-
<CardContent>
|
|
93
|
-
<dl className="grid gap-x-6 gap-y-
|
|
105
|
+
<CardContent className="px-5 py-4">
|
|
106
|
+
<dl className="grid gap-x-6 gap-y-4 sm:grid-cols-2">
|
|
94
107
|
{fields.map((field) => (
|
|
95
108
|
<div key={field.label}>
|
|
96
109
|
<dt className="text-xs text-muted-foreground">{field.label}</dt>
|
|
97
|
-
<dd className="text-sm break-words">{field.value}</dd>
|
|
110
|
+
<dd className="mt-0.5 text-sm break-words">{field.value}</dd>
|
|
98
111
|
</div>
|
|
99
112
|
))}
|
|
100
113
|
</dl>
|
|
@@ -103,13 +116,13 @@ export function MemberProfile({
|
|
|
103
116
|
)}
|
|
104
117
|
|
|
105
118
|
{signatureHtml !== null && (
|
|
106
|
-
<Card aria-labelledby="profile-signature-heading">
|
|
107
|
-
<CardHeader>
|
|
119
|
+
<Card aria-labelledby="profile-signature-heading" className="rounded-xl">
|
|
120
|
+
<CardHeader className="bg-card">
|
|
108
121
|
<CardTitle id="profile-signature-heading" className="text-sm">
|
|
109
122
|
{c('signature')}
|
|
110
123
|
</CardTitle>
|
|
111
124
|
</CardHeader>
|
|
112
|
-
<CardContent>
|
|
125
|
+
<CardContent className="px-5 py-4">
|
|
113
126
|
<div
|
|
114
127
|
className="prose-md text-sm text-muted-foreground"
|
|
115
128
|
dangerouslySetInnerHTML={{ __html: signatureHtml }}
|
package/src/slots/navigation.tsx
CHANGED
|
@@ -16,21 +16,21 @@ export function Navigation({ items, copy }: NavigationModel & { copy: SlotCopy }
|
|
|
16
16
|
return (
|
|
17
17
|
<li key={item.href} className="flex shrink-0 items-center">
|
|
18
18
|
{index > 0 && (
|
|
19
|
-
<span aria-hidden="true" className="px-
|
|
19
|
+
<span aria-hidden="true" className="px-1 text-muted-foreground/50 select-none">
|
|
20
20
|
{c('separator')}
|
|
21
21
|
</span>
|
|
22
22
|
)}
|
|
23
23
|
{isLast ? (
|
|
24
24
|
<span
|
|
25
25
|
aria-current="page"
|
|
26
|
-
className="max-w-[
|
|
26
|
+
className="max-w-[28ch] truncate px-1.5 py-1 font-medium text-foreground sm:max-w-none"
|
|
27
27
|
>
|
|
28
28
|
{item.label}
|
|
29
29
|
</span>
|
|
30
30
|
) : (
|
|
31
31
|
<a
|
|
32
32
|
href={item.href}
|
|
33
|
-
className="rounded-md px-1.5 py-1 transition-colors hover:bg-
|
|
33
|
+
className="rounded-md px-1.5 py-1 transition-colors hover:bg-muted hover:text-foreground"
|
|
34
34
|
>
|
|
35
35
|
{item.label}
|
|
36
36
|
</a>
|
package/src/slots/pagination.tsx
CHANGED
|
@@ -17,7 +17,7 @@ export function Pagination({
|
|
|
17
17
|
|
|
18
18
|
const c = (key: string) => fromSlotCopy(copy, `default.pagination.${key}`)
|
|
19
19
|
|
|
20
|
-
const step = cn(buttonVariants({ variant: 'outline', size: 'sm' }), 'min-w-20')
|
|
20
|
+
const step = cn(buttonVariants({ variant: 'outline', size: 'sm' }), 'min-w-20 rounded-full')
|
|
21
21
|
const stepDisabled = cn(step, 'pointer-events-none opacity-40')
|
|
22
22
|
|
|
23
23
|
return (
|
|
@@ -53,7 +53,7 @@ export function Pagination({
|
|
|
53
53
|
variant: entry.isCurrent ? 'primary' : 'ghost',
|
|
54
54
|
size: 'sm',
|
|
55
55
|
}),
|
|
56
|
-
'min-w-8 px-2',
|
|
56
|
+
'min-w-8 rounded-full px-2',
|
|
57
57
|
NUMERIC,
|
|
58
58
|
entry.isCurrent ? '' : 'hidden sm:inline-flex',
|
|
59
59
|
)}
|
package/src/slots/panel-nav.tsx
CHANGED
|
@@ -3,14 +3,14 @@ import { fromSlotCopy } from '@meith/theme-kit'
|
|
|
3
3
|
import { cn, Disclosure } from '@meith/ui'
|
|
4
4
|
|
|
5
5
|
import { PanelIcon } from '../panel-icons'
|
|
6
|
+
import { BELOW_HEADER } from '../shared'
|
|
6
7
|
|
|
7
8
|
const ITEM =
|
|
8
|
-
'relative flex items-center gap-
|
|
9
|
+
'group/item relative flex items-center gap-3 rounded-lg px-3 py-2 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
|
|
9
10
|
|
|
10
|
-
const HERE =
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const ELSEWHERE = 'text-muted-foreground hover:bg-accent hover:text-foreground'
|
|
11
|
+
const HERE = 'bg-primary/10 font-semibold text-primary [&_svg]:text-primary'
|
|
12
|
+
const OPEN = 'font-semibold text-foreground hover:bg-muted'
|
|
13
|
+
const ELSEWHERE = 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
14
14
|
|
|
15
15
|
function Count({ count, copy }: { count: number; copy: SlotCopy }) {
|
|
16
16
|
const c = (key: string) => fromSlotCopy(copy, `default.panelNav.${key}`)
|
|
@@ -19,7 +19,7 @@ function Count({ count, copy }: { count: number; copy: SlotCopy }) {
|
|
|
19
19
|
<>
|
|
20
20
|
<span
|
|
21
21
|
aria-hidden="true"
|
|
22
|
-
className="ml-auto min-w-5 rounded-full bg-primary px-1.5 py-0.5 text-center text-
|
|
22
|
+
className="ml-auto min-w-5 rounded-full bg-primary px-1.5 py-0.5 text-center text-[0.6875rem] leading-4 font-semibold tabular-nums text-primary-foreground"
|
|
23
23
|
>
|
|
24
24
|
{count > 99 ? c('countCap') : count}
|
|
25
25
|
</span>
|
|
@@ -42,7 +42,7 @@ function Item({
|
|
|
42
42
|
const body = (
|
|
43
43
|
<>
|
|
44
44
|
{item.icon !== null && (
|
|
45
|
-
<span className="text-muted-foreground/
|
|
45
|
+
<span className="text-muted-foreground/70 transition-colors group-hover/item:text-foreground">
|
|
46
46
|
<PanelIcon icon={item.icon} />
|
|
47
47
|
</span>
|
|
48
48
|
)}
|
|
@@ -81,11 +81,11 @@ function Sections({
|
|
|
81
81
|
}: Pick<PanelNavModel, 'label' | 'sections'> & { copy: SlotCopy }) {
|
|
82
82
|
return (
|
|
83
83
|
<nav aria-label={label} className="text-sm">
|
|
84
|
-
<ul className="flex flex-col gap-0.5
|
|
84
|
+
<ul className="flex flex-col gap-0.5">
|
|
85
85
|
{sections.map((section) => (
|
|
86
86
|
<li
|
|
87
87
|
key={section.href}
|
|
88
|
-
className={cn(section.isOverview && 'mb-1 border-b border-border pb-1')}
|
|
88
|
+
className={cn(section.isOverview && 'mb-1.5 border-b border-border pb-1.5')}
|
|
89
89
|
>
|
|
90
90
|
<Item
|
|
91
91
|
item={section}
|
|
@@ -94,12 +94,12 @@ function Sections({
|
|
|
94
94
|
/>
|
|
95
95
|
|
|
96
96
|
{section.isOpen && section.children.length > 0 && (
|
|
97
|
-
<ul className="
|
|
97
|
+
<ul className="my-1 ml-5 flex flex-col gap-0.5 border-l-2 border-border pl-2">
|
|
98
98
|
{section.children.map((child) => (
|
|
99
99
|
<li key={child.href}>
|
|
100
100
|
<Item
|
|
101
101
|
item={child}
|
|
102
|
-
className={child.current === 'here' ? HERE : ELSEWHERE}
|
|
102
|
+
className={cn('py-1.5', child.current === 'here' ? HERE : ELSEWHERE)}
|
|
103
103
|
copy={copy}
|
|
104
104
|
/>
|
|
105
105
|
</li>
|
|
@@ -121,7 +121,9 @@ export function PanelNav({
|
|
|
121
121
|
}: PanelNavModel & { copy: SlotCopy }) {
|
|
122
122
|
return (
|
|
123
123
|
<>
|
|
124
|
-
<div
|
|
124
|
+
<div
|
|
125
|
+
className={`sticky ${BELOW_HEADER} z-20 -mx-4 bg-background/95 px-4 py-2 backdrop-blur-sm sm:-mx-6 sm:px-6 lg:hidden`}
|
|
126
|
+
>
|
|
125
127
|
<Disclosure
|
|
126
128
|
summary={label}
|
|
127
129
|
contentClassName="p-2"
|
|
@@ -131,7 +133,7 @@ export function PanelNav({
|
|
|
131
133
|
</Disclosure>
|
|
132
134
|
</div>
|
|
133
135
|
|
|
134
|
-
<div className="hidden rounded-
|
|
136
|
+
<div className="hidden rounded-xl border border-border bg-card p-2 shadow-elevation lg:block">
|
|
135
137
|
<Sections label={label} sections={sections} copy={copy} />
|
|
136
138
|
</div>
|
|
137
139
|
</>
|
package/src/slots/panel-page.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import type { PanelPageModel, PanelSectionModel, SlotCopy } from '@meith/theme-k
|
|
|
2
2
|
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
3
|
import { cn } from '@meith/ui'
|
|
4
4
|
|
|
5
|
-
import { MUTED_LINK, PAGE, pageAt } from '../shared'
|
|
5
|
+
import { MUTED_LINK, PAGE, PAGE_TITLE, pageAt, SECTION_TITLE } from '../shared'
|
|
6
6
|
|
|
7
7
|
export function PanelPage({
|
|
8
8
|
title,
|
|
@@ -21,25 +21,28 @@ export function PanelPage({
|
|
|
21
21
|
id="board-content"
|
|
22
22
|
tabIndex={-1}
|
|
23
23
|
className={cn(
|
|
24
|
-
'flex w-full flex-col py-8',
|
|
24
|
+
'flex w-full flex-col py-6 lg:py-8',
|
|
25
25
|
frame === 'standalone'
|
|
26
26
|
? `flex-1 ${width === 'wide' ? PAGE : pageAt('max-w-4xl')}`
|
|
27
|
-
: `px-6 ${width === 'wide' ? 'max-w-none' : 'max-w-4xl'}`,
|
|
27
|
+
: `px-4 sm:px-6 ${width === 'wide' ? 'max-w-none' : 'max-w-4xl'}`,
|
|
28
28
|
gap === 'loose' ? 'gap-8' : 'gap-6',
|
|
29
29
|
)}
|
|
30
30
|
>
|
|
31
|
-
<div className="flex flex-wrap items-
|
|
31
|
+
<div className="flex flex-wrap items-end justify-between gap-x-6 gap-y-3 border-b border-border pb-5">
|
|
32
32
|
<div className="flex min-w-0 flex-col gap-1">
|
|
33
33
|
{back !== null && (
|
|
34
|
-
<a
|
|
35
|
-
{
|
|
34
|
+
<a
|
|
35
|
+
href={back.href}
|
|
36
|
+
className={`mb-1 inline-flex w-fit items-center gap-1 text-sm font-medium ${MUTED_LINK}`}
|
|
37
|
+
>
|
|
38
|
+
<span aria-hidden="true">{c('back')}</span> {back.label}
|
|
36
39
|
</a>
|
|
37
40
|
)}
|
|
38
41
|
|
|
39
|
-
<h1 className=
|
|
42
|
+
<h1 className={PAGE_TITLE}>{title}</h1>
|
|
40
43
|
|
|
41
44
|
{regions.lede !== undefined && (
|
|
42
|
-
<p className="text-sm text-muted-foreground">{regions.lede}</p>
|
|
45
|
+
<p className="max-w-prose text-sm text-muted-foreground">{regions.lede}</p>
|
|
43
46
|
)}
|
|
44
47
|
{regions.meta !== undefined && (
|
|
45
48
|
<p className="text-xs text-muted-foreground">{regions.meta}</p>
|
|
@@ -61,7 +64,7 @@ export function PanelSection({ title, headingId, regions, children }: PanelSecti
|
|
|
61
64
|
<section aria-labelledby={headingId} className="flex flex-col gap-3">
|
|
62
65
|
<div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-1">
|
|
63
66
|
<div className="flex min-w-0 flex-col gap-0.5">
|
|
64
|
-
<h2 id={headingId} className=
|
|
67
|
+
<h2 id={headingId} className={SECTION_TITLE}>
|
|
65
68
|
{title}
|
|
66
69
|
</h2>
|
|
67
70
|
{regions.description !== undefined && (
|