@meith/theme-midnight 0.6.0 → 0.8.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/package.json +3 -3
- package/src/copy.ts +186 -0
- package/src/index.ts +4 -4
- package/src/messages/en.json +90 -0
- package/src/messages/index.ts +5 -0
- package/src/slots/announcement.tsx +14 -4
- package/src/slots/board-index.tsx +10 -4
- package/src/slots/board-stats.tsx +16 -12
- package/src/slots/category-block.tsx +13 -6
- package/src/slots/footer.tsx +14 -3
- package/src/slots/forum-display.tsx +25 -8
- package/src/slots/forum-row.tsx +8 -6
- package/src/slots/header.tsx +13 -3
- package/src/slots/latest-posts.tsx +8 -5
- package/src/slots/latest-threads.tsx +12 -5
- package/src/slots/member-profile.tsx +13 -9
- package/src/slots/navigation.tsx +6 -3
- package/src/slots/notice.tsx +11 -5
- package/src/slots/pagination.tsx +18 -7
- package/src/slots/post-actions.tsx +17 -10
- package/src/slots/post-bit.tsx +29 -16
- package/src/slots/shell.tsx +6 -3
- package/src/slots/subforum-list.tsx +10 -4
- package/src/slots/thread-row.tsx +18 -9
- package/src/slots/thread-view.tsx +18 -5
- package/src/slots/user-panel.tsx +12 -9
- package/src/slots/who-is-online.tsx +17 -12
- package/src/theme.ts +53 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meith/theme-midnight",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A dark theme for Meith, built on the default theme's slots.",
|
|
5
5
|
"license": "LGPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@meith/theme-default": "^0.
|
|
24
|
-
"@meith/theme-kit": "^0.
|
|
23
|
+
"@meith/theme-default": "^0.8.0",
|
|
24
|
+
"@meith/theme-kit": "^0.8.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"react": "^19.2.0"
|
package/src/copy.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { SlotCopy, Translator } from '@meith/theme-kit'
|
|
2
|
+
|
|
3
|
+
function copyFor(t: Translator, keys: readonly string[]): SlotCopy {
|
|
4
|
+
const copy: Record<string, string> = {}
|
|
5
|
+
for (const key of keys) copy[key] = t.t(key)
|
|
6
|
+
return copy
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function announcementCopy(t: Translator): SlotCopy {
|
|
10
|
+
return copyFor(t, ['midnight.announcement.posted', 'midnight.announcement.postedBy'])
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function boardIndexCopy(t: Translator): SlotCopy {
|
|
14
|
+
return copyFor(t, ['midnight.boardIndex.activityLabel', 'midnight.boardIndex.markAllRead'])
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function boardStatsCopy(t: Translator): SlotCopy {
|
|
18
|
+
return copyFor(t, [
|
|
19
|
+
'midnight.boardStats.heading',
|
|
20
|
+
'midnight.boardStats.threads',
|
|
21
|
+
'midnight.boardStats.posts',
|
|
22
|
+
'midnight.boardStats.members',
|
|
23
|
+
'midnight.boardStats.newest',
|
|
24
|
+
'midnight.boardStats.notCounted',
|
|
25
|
+
'midnight.boardStats.counted',
|
|
26
|
+
])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function categoryBlockCopy(t: Translator): SlotCopy {
|
|
30
|
+
return copyFor(t, [
|
|
31
|
+
'midnight.categoryBlock.forumHeader',
|
|
32
|
+
'midnight.categoryBlock.threadsHeader',
|
|
33
|
+
'midnight.categoryBlock.postsHeader',
|
|
34
|
+
'midnight.categoryBlock.lastPostHeader',
|
|
35
|
+
])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function footerCopy(t: Translator): SlotCopy {
|
|
39
|
+
return copyFor(t, ['midnight.footer.allTimes'])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function forumDisplayCopy(t: Translator): SlotCopy {
|
|
43
|
+
return copyFor(t, [
|
|
44
|
+
'midnight.forumDisplay.newThread',
|
|
45
|
+
'midnight.forumDisplay.threadHeader',
|
|
46
|
+
'midnight.forumDisplay.repliesHeader',
|
|
47
|
+
'midnight.forumDisplay.viewsHeader',
|
|
48
|
+
'midnight.forumDisplay.lastPostHeader',
|
|
49
|
+
'midnight.forumDisplay.markForumRead',
|
|
50
|
+
])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function forumRowCopy(t: Translator): SlotCopy {
|
|
54
|
+
return copyFor(t, ['midnight.forumRow.newPosts', 'midnight.forumRow.noPosts'])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function headerCopy(t: Translator): SlotCopy {
|
|
58
|
+
return copyFor(t, ['midnight.header.sectionsLabel'])
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function latestPostsCopy(t: Translator): SlotCopy {
|
|
62
|
+
return copyFor(t, [
|
|
63
|
+
'midnight.latestPosts.heading',
|
|
64
|
+
'midnight.latestPosts.empty',
|
|
65
|
+
'midnight.latestPosts.asOf',
|
|
66
|
+
])
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function latestThreadsCopy(t: Translator): SlotCopy {
|
|
70
|
+
return copyFor(t, [
|
|
71
|
+
'midnight.latestThreads.heading',
|
|
72
|
+
'midnight.latestThreads.empty',
|
|
73
|
+
'midnight.latestThreads.asOf',
|
|
74
|
+
])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function memberProfileCopy(t: Translator): SlotCopy {
|
|
78
|
+
return copyFor(t, [
|
|
79
|
+
'midnight.memberProfile.joined',
|
|
80
|
+
'midnight.memberProfile.lastVisit',
|
|
81
|
+
'midnight.memberProfile.never',
|
|
82
|
+
'midnight.memberProfile.posts',
|
|
83
|
+
'midnight.memberProfile.signatureLabel',
|
|
84
|
+
'midnight.memberProfile.actionsLabel',
|
|
85
|
+
])
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function navigationCopy(t: Translator): SlotCopy {
|
|
89
|
+
return copyFor(t, ['midnight.navigation.breadcrumbLabel'])
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function noticeCopy(t: Translator): SlotCopy {
|
|
93
|
+
return copyFor(t, [
|
|
94
|
+
'midnight.notice.dismiss',
|
|
95
|
+
'midnight.notice.kind.info',
|
|
96
|
+
'midnight.notice.kind.success',
|
|
97
|
+
'midnight.notice.kind.warning',
|
|
98
|
+
'midnight.notice.kind.error',
|
|
99
|
+
])
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function paginationCopy(t: Translator): SlotCopy {
|
|
103
|
+
return copyFor(t, [
|
|
104
|
+
'midnight.pagination.ariaLabel',
|
|
105
|
+
'midnight.pagination.prev',
|
|
106
|
+
'midnight.pagination.next',
|
|
107
|
+
'midnight.pagination.page',
|
|
108
|
+
'midnight.pagination.pageOf',
|
|
109
|
+
])
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function postActionsCopy(t: Translator): SlotCopy {
|
|
113
|
+
return copyFor(t, [
|
|
114
|
+
'midnight.postActions.ariaLabel',
|
|
115
|
+
'midnight.postActions.quote',
|
|
116
|
+
'midnight.postActions.edit',
|
|
117
|
+
'midnight.postActions.restore',
|
|
118
|
+
'midnight.postActions.rate',
|
|
119
|
+
'midnight.postActions.report',
|
|
120
|
+
'midnight.postActions.warn',
|
|
121
|
+
'midnight.postActions.moderate',
|
|
122
|
+
])
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function postBitCopy(t: Translator): SlotCopy {
|
|
126
|
+
return copyFor(t, [
|
|
127
|
+
'midnight.postBit.deleted',
|
|
128
|
+
'midnight.postBit.unapproved',
|
|
129
|
+
'midnight.postBit.posts',
|
|
130
|
+
'midnight.postBit.joined',
|
|
131
|
+
'midnight.postBit.rep',
|
|
132
|
+
'midnight.postBit.online',
|
|
133
|
+
'midnight.postBit.ignoredPrefix',
|
|
134
|
+
'midnight.postBit.ignoredSuffix',
|
|
135
|
+
'midnight.postBit.showIt',
|
|
136
|
+
])
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function shellCopy(t: Translator): SlotCopy {
|
|
140
|
+
return copyFor(t, ['midnight.shell.skipToContent'])
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function subforumListCopy(t: Translator): SlotCopy {
|
|
144
|
+
return copyFor(t, ['midnight.subforumList.ariaLabel'])
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function threadRowCopy(t: Translator): SlotCopy {
|
|
148
|
+
return copyFor(t, [
|
|
149
|
+
'midnight.threadRow.newPosts',
|
|
150
|
+
'midnight.threadRow.pinned',
|
|
151
|
+
'midnight.threadRow.locked',
|
|
152
|
+
'midnight.threadRow.moved',
|
|
153
|
+
'midnight.threadRow.by',
|
|
154
|
+
])
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function threadViewCopy(t: Translator): SlotCopy {
|
|
158
|
+
return copyFor(t, [
|
|
159
|
+
'midnight.threadView.in',
|
|
160
|
+
'midnight.threadView.reply',
|
|
161
|
+
'midnight.threadView.markRead',
|
|
162
|
+
])
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function userPanelCopy(t: Translator): SlotCopy {
|
|
166
|
+
return copyFor(t, [
|
|
167
|
+
'midnight.userPanel.guest',
|
|
168
|
+
'midnight.userPanel.signedIn',
|
|
169
|
+
'midnight.userPanel.new',
|
|
170
|
+
'midnight.userPanel.notificationsSrOnly',
|
|
171
|
+
'midnight.userPanel.pm',
|
|
172
|
+
'midnight.userPanel.messagesSrOnly',
|
|
173
|
+
])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function whoIsOnlineCopy(t: Translator): SlotCopy {
|
|
177
|
+
return copyFor(t, [
|
|
178
|
+
'midnight.whoIsOnline.onlineNow',
|
|
179
|
+
'midnight.whoIsOnline.noMembers',
|
|
180
|
+
'midnight.whoIsOnline.hidden',
|
|
181
|
+
'midnight.whoIsOnline.guests',
|
|
182
|
+
'midnight.whoIsOnline.fullList',
|
|
183
|
+
'midnight.whoIsOnline.record',
|
|
184
|
+
'midnight.whoIsOnline.on',
|
|
185
|
+
])
|
|
186
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"midnight.announcement.posted": "Posted",
|
|
3
|
+
"midnight.announcement.postedBy": "Posted by",
|
|
4
|
+
"midnight.boardIndex.activityLabel": "Board activity",
|
|
5
|
+
"midnight.boardIndex.markAllRead": "mark all forums read",
|
|
6
|
+
"midnight.boardStats.counted": "counted",
|
|
7
|
+
"midnight.boardStats.heading": "Board statistics",
|
|
8
|
+
"midnight.boardStats.members": "members",
|
|
9
|
+
"midnight.boardStats.newest": "newest",
|
|
10
|
+
"midnight.boardStats.notCounted": "not counted yet",
|
|
11
|
+
"midnight.boardStats.posts": "posts",
|
|
12
|
+
"midnight.boardStats.threads": "threads",
|
|
13
|
+
"midnight.categoryBlock.forumHeader": "Forum",
|
|
14
|
+
"midnight.categoryBlock.lastPostHeader": "Last post",
|
|
15
|
+
"midnight.categoryBlock.postsHeader": "Posts",
|
|
16
|
+
"midnight.categoryBlock.threadsHeader": "Threads",
|
|
17
|
+
"midnight.footer.allTimes": "all times",
|
|
18
|
+
"midnight.forumDisplay.lastPostHeader": "Last post",
|
|
19
|
+
"midnight.forumDisplay.markForumRead": "mark this forum read",
|
|
20
|
+
"midnight.forumDisplay.newThread": "new thread",
|
|
21
|
+
"midnight.forumDisplay.repliesHeader": "Replies",
|
|
22
|
+
"midnight.forumDisplay.threadHeader": "Thread",
|
|
23
|
+
"midnight.forumDisplay.viewsHeader": "Views",
|
|
24
|
+
"midnight.forumRow.newPosts": "(new posts)",
|
|
25
|
+
"midnight.forumRow.noPosts": "no posts yet",
|
|
26
|
+
"midnight.header.sectionsLabel": "Board sections",
|
|
27
|
+
"midnight.latestPosts.asOf": "as of",
|
|
28
|
+
"midnight.latestPosts.empty": "nothing said yet",
|
|
29
|
+
"midnight.latestPosts.heading": "Latest posts",
|
|
30
|
+
"midnight.latestThreads.asOf": "as of",
|
|
31
|
+
"midnight.latestThreads.empty": "nothing started yet",
|
|
32
|
+
"midnight.latestThreads.heading": "Latest threads",
|
|
33
|
+
"midnight.memberProfile.actionsLabel": "Member actions",
|
|
34
|
+
"midnight.memberProfile.joined": "joined",
|
|
35
|
+
"midnight.memberProfile.lastVisit": "last visit",
|
|
36
|
+
"midnight.memberProfile.never": "never",
|
|
37
|
+
"midnight.memberProfile.posts": "posts",
|
|
38
|
+
"midnight.memberProfile.signatureLabel": "Signature",
|
|
39
|
+
"midnight.navigation.breadcrumbLabel": "Breadcrumb",
|
|
40
|
+
"midnight.notice.dismiss": "dismiss",
|
|
41
|
+
"midnight.notice.kind.error": "error",
|
|
42
|
+
"midnight.notice.kind.info": "info",
|
|
43
|
+
"midnight.notice.kind.success": "success",
|
|
44
|
+
"midnight.notice.kind.warning": "warning",
|
|
45
|
+
"midnight.pagination.ariaLabel": "Pagination",
|
|
46
|
+
"midnight.pagination.next": "next",
|
|
47
|
+
"midnight.pagination.page": "page",
|
|
48
|
+
"midnight.pagination.pageOf": "of",
|
|
49
|
+
"midnight.pagination.prev": "prev",
|
|
50
|
+
"midnight.postActions.ariaLabel": "Post actions",
|
|
51
|
+
"midnight.postActions.edit": "edit",
|
|
52
|
+
"midnight.postActions.moderate": "moderate",
|
|
53
|
+
"midnight.postActions.quote": "quote",
|
|
54
|
+
"midnight.postActions.rate": "rate",
|
|
55
|
+
"midnight.postActions.report": "report",
|
|
56
|
+
"midnight.postActions.restore": "restore",
|
|
57
|
+
"midnight.postActions.warn": "warn",
|
|
58
|
+
"midnight.postBit.deleted": "deleted — visible to moderators only",
|
|
59
|
+
"midnight.postBit.ignoredPrefix": "Post from",
|
|
60
|
+
"midnight.postBit.ignoredSuffix": ", who you are ignoring.",
|
|
61
|
+
"midnight.postBit.joined": "joined",
|
|
62
|
+
"midnight.postBit.online": "online",
|
|
63
|
+
"midnight.postBit.posts": "posts",
|
|
64
|
+
"midnight.postBit.rep": "rep",
|
|
65
|
+
"midnight.postBit.showIt": "Show it",
|
|
66
|
+
"midnight.postBit.unapproved": "awaiting approval — visible to moderators only",
|
|
67
|
+
"midnight.shell.skipToContent": "Skip to content",
|
|
68
|
+
"midnight.subforumList.ariaLabel": "Subforums",
|
|
69
|
+
"midnight.threadRow.by": "by",
|
|
70
|
+
"midnight.threadRow.locked": "locked",
|
|
71
|
+
"midnight.threadRow.moved": "moved",
|
|
72
|
+
"midnight.threadRow.newPosts": "(new posts)",
|
|
73
|
+
"midnight.threadRow.pinned": "pinned",
|
|
74
|
+
"midnight.threadView.in": "in",
|
|
75
|
+
"midnight.threadView.markRead": "mark read",
|
|
76
|
+
"midnight.threadView.reply": "reply",
|
|
77
|
+
"midnight.userPanel.guest": "guest",
|
|
78
|
+
"midnight.userPanel.messagesSrOnly": "unread messages",
|
|
79
|
+
"midnight.userPanel.new": "new",
|
|
80
|
+
"midnight.userPanel.notificationsSrOnly": "notifications",
|
|
81
|
+
"midnight.userPanel.pm": "pm",
|
|
82
|
+
"midnight.userPanel.signedIn": "signed in",
|
|
83
|
+
"midnight.whoIsOnline.fullList": "full list",
|
|
84
|
+
"midnight.whoIsOnline.guests": "guests",
|
|
85
|
+
"midnight.whoIsOnline.hidden": "(hidden)",
|
|
86
|
+
"midnight.whoIsOnline.noMembers": "no members",
|
|
87
|
+
"midnight.whoIsOnline.on": "on",
|
|
88
|
+
"midnight.whoIsOnline.onlineNow": "Online now —",
|
|
89
|
+
"midnight.whoIsOnline.record": "record"
|
|
90
|
+
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import type { AnnouncementModel } from '@meith/theme-kit'
|
|
1
|
+
import type { AnnouncementModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
2
3
|
|
|
3
4
|
import { UserRef } from '../shared'
|
|
4
5
|
|
|
5
|
-
export function Announcement({
|
|
6
|
+
export function Announcement({
|
|
7
|
+
title,
|
|
8
|
+
bodyHtml,
|
|
9
|
+
postedBy,
|
|
10
|
+
postedAt,
|
|
11
|
+
forum,
|
|
12
|
+
copy,
|
|
13
|
+
}: AnnouncementModel & { copy: SlotCopy }) {
|
|
14
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.announcement.${key}`)
|
|
15
|
+
|
|
6
16
|
return (
|
|
7
17
|
<article className="flex flex-col gap-2 rounded-lg border border-border bg-muted/40 p-4">
|
|
8
18
|
<h2 className="font-heading text-lg font-semibold text-foreground">{title}</h2>
|
|
@@ -14,10 +24,10 @@ export function Announcement({ title, bodyHtml, postedBy, postedAt, forum }: Ann
|
|
|
14
24
|
|
|
15
25
|
<p className="text-xs text-muted-foreground">
|
|
16
26
|
{postedBy === null ? (
|
|
17
|
-
'
|
|
27
|
+
c('posted')
|
|
18
28
|
) : (
|
|
19
29
|
<>
|
|
20
|
-
|
|
30
|
+
{c('postedBy')}{' '}
|
|
21
31
|
<UserRef
|
|
22
32
|
user={postedBy}
|
|
23
33
|
className={
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import type { BoardIndexModel } from '@meith/theme-kit'
|
|
1
|
+
import type { BoardIndexModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
2
3
|
|
|
3
|
-
export function BoardIndex({
|
|
4
|
+
export function BoardIndex({
|
|
5
|
+
markAllReadAction,
|
|
6
|
+
regions,
|
|
7
|
+
copy,
|
|
8
|
+
}: BoardIndexModel & { copy: SlotCopy }) {
|
|
4
9
|
const latest = regions.latest ?? null
|
|
5
10
|
const hasRail = latest !== null || regions.online !== null || regions.stats !== null
|
|
11
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.boardIndex.${key}`)
|
|
6
12
|
|
|
7
13
|
return (
|
|
8
14
|
<div className="flex flex-col gap-3 p-3">
|
|
@@ -20,7 +26,7 @@ export function BoardIndex({ markAllReadAction, regions }: BoardIndexModel) {
|
|
|
20
26
|
<div className="flex min-w-0 flex-col gap-3">{regions.categories}</div>
|
|
21
27
|
|
|
22
28
|
{hasRail && (
|
|
23
|
-
<aside aria-label=
|
|
29
|
+
<aside aria-label={c('activityLabel')} className="flex min-w-0 flex-col gap-3">
|
|
24
30
|
{latest}
|
|
25
31
|
{regions.online}
|
|
26
32
|
{regions.stats}
|
|
@@ -36,7 +42,7 @@ export function BoardIndex({ markAllReadAction, regions }: BoardIndexModel) {
|
|
|
36
42
|
type="submit"
|
|
37
43
|
className="border border-border px-2 py-1 font-mono text-xs text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
38
44
|
>
|
|
39
|
-
|
|
45
|
+
{c('markAllRead')}
|
|
40
46
|
</button>
|
|
41
47
|
</form>
|
|
42
48
|
)}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { BoardStatsModel } from '@meith/theme-kit'
|
|
1
|
+
import type { BoardStatsModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
2
3
|
|
|
3
4
|
import { UserRef } from '../shared'
|
|
4
5
|
|
|
@@ -8,30 +9,33 @@ export function BoardStats({
|
|
|
8
9
|
memberCount,
|
|
9
10
|
newestMember,
|
|
10
11
|
computedAt,
|
|
11
|
-
|
|
12
|
+
copy,
|
|
13
|
+
}: BoardStatsModel & { copy: SlotCopy }) {
|
|
14
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.boardStats.${key}`)
|
|
15
|
+
|
|
12
16
|
return (
|
|
13
17
|
<section aria-labelledby="board-stats-heading" className="border border-border">
|
|
14
18
|
<h2
|
|
15
19
|
id="board-stats-heading"
|
|
16
20
|
className="border-b border-border bg-secondary px-3 py-1 font-mono text-xs uppercase tracking-wide"
|
|
17
21
|
>
|
|
18
|
-
|
|
22
|
+
{c('heading')}
|
|
19
23
|
</h2>
|
|
20
24
|
<dl className="grid grid-cols-2 gap-x-4 gap-y-1 px-3 py-2 font-mono text-xs md:grid-cols-4 lg:grid-cols-2">
|
|
21
25
|
<div>
|
|
22
|
-
<dt className="text-muted-foreground">threads</dt>
|
|
23
|
-
<dd>{threadCount}</dd>
|
|
26
|
+
<dt className="text-muted-foreground">{c('threads')}</dt>
|
|
27
|
+
<dd>{threadCount.label}</dd>
|
|
24
28
|
</div>
|
|
25
29
|
<div>
|
|
26
|
-
<dt className="text-muted-foreground">posts</dt>
|
|
27
|
-
<dd>{postCount}</dd>
|
|
30
|
+
<dt className="text-muted-foreground">{c('posts')}</dt>
|
|
31
|
+
<dd>{postCount.label}</dd>
|
|
28
32
|
</div>
|
|
29
33
|
<div>
|
|
30
|
-
<dt className="text-muted-foreground">members</dt>
|
|
31
|
-
<dd>{memberCount}</dd>
|
|
34
|
+
<dt className="text-muted-foreground">{c('members')}</dt>
|
|
35
|
+
<dd>{memberCount.label}</dd>
|
|
32
36
|
</div>
|
|
33
37
|
<div>
|
|
34
|
-
<dt className="text-muted-foreground">newest</dt>
|
|
38
|
+
<dt className="text-muted-foreground">{c('newest')}</dt>
|
|
35
39
|
<dd>
|
|
36
40
|
{newestMember === null ? (
|
|
37
41
|
'—'
|
|
@@ -43,10 +47,10 @@ export function BoardStats({
|
|
|
43
47
|
</dl>
|
|
44
48
|
<p className="border-t border-border px-3 py-1 font-mono text-xs text-muted-foreground">
|
|
45
49
|
{computedAt === null ? (
|
|
46
|
-
'
|
|
50
|
+
c('notCounted')
|
|
47
51
|
) : (
|
|
48
52
|
<>
|
|
49
|
-
counted <time dateTime={computedAt.iso}>{computedAt.label}</time>
|
|
53
|
+
{c('counted')} <time dateTime={computedAt.iso}>{computedAt.label}</time>
|
|
50
54
|
</>
|
|
51
55
|
)}
|
|
52
56
|
</p>
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import type { CategoryBlockModel } from '@meith/theme-kit'
|
|
1
|
+
import type { CategoryBlockModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
|
+
|
|
4
|
+
export function CategoryBlock({
|
|
5
|
+
category,
|
|
6
|
+
children,
|
|
7
|
+
copy,
|
|
8
|
+
}: CategoryBlockModel & { copy: SlotCopy }) {
|
|
9
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.categoryBlock.${key}`)
|
|
2
10
|
|
|
3
|
-
export function CategoryBlock({ category, children }: CategoryBlockModel) {
|
|
4
11
|
return (
|
|
5
12
|
<section className="border border-border">
|
|
6
13
|
<h2 className="border-b border-border bg-secondary px-3 py-1.5 font-mono text-sm font-semibold uppercase tracking-wide">
|
|
@@ -22,10 +29,10 @@ export function CategoryBlock({ category, children }: CategoryBlockModel) {
|
|
|
22
29
|
</colgroup>
|
|
23
30
|
<thead className="sr-only">
|
|
24
31
|
<tr>
|
|
25
|
-
<th scope="col">
|
|
26
|
-
<th scope="col">
|
|
27
|
-
<th scope="col">
|
|
28
|
-
<th scope="col">
|
|
32
|
+
<th scope="col">{c('forumHeader')}</th>
|
|
33
|
+
<th scope="col">{c('threadsHeader')}</th>
|
|
34
|
+
<th scope="col">{c('postsHeader')}</th>
|
|
35
|
+
<th scope="col">{c('lastPostHeader')}</th>
|
|
29
36
|
</tr>
|
|
30
37
|
</thead>
|
|
31
38
|
<tbody>{children}</tbody>
|
package/src/slots/footer.tsx
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import type { FooterModel } from '@meith/theme-kit'
|
|
1
|
+
import type { FooterModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
|
+
|
|
4
|
+
export function Footer({
|
|
5
|
+
boardTitle,
|
|
6
|
+
links,
|
|
7
|
+
timezoneLabel,
|
|
8
|
+
poweredBy,
|
|
9
|
+
copy,
|
|
10
|
+
}: FooterModel & { copy: SlotCopy }) {
|
|
11
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.footer.${key}`)
|
|
2
12
|
|
|
3
|
-
export function Footer({ boardTitle, links, timezoneLabel, poweredBy }: FooterModel) {
|
|
4
13
|
return (
|
|
5
14
|
<footer className="mt-auto flex flex-wrap items-center justify-between gap-2 border-t border-border bg-secondary px-4 py-2 font-mono text-xs text-muted-foreground">
|
|
6
15
|
<span>{boardTitle}</span>
|
|
@@ -12,7 +21,9 @@ export function Footer({ boardTitle, links, timezoneLabel, poweredBy }: FooterMo
|
|
|
12
21
|
))}
|
|
13
22
|
</div>
|
|
14
23
|
<span className="flex flex-wrap gap-3">
|
|
15
|
-
<span>
|
|
24
|
+
<span>
|
|
25
|
+
{c('allTimes')} {timezoneLabel}
|
|
26
|
+
</span>
|
|
16
27
|
{poweredBy && (
|
|
17
28
|
<a href={poweredBy.href} className="hover:text-foreground">
|
|
18
29
|
{poweredBy.label}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import type { ForumDisplayModel } from '@meith/theme-kit'
|
|
1
|
+
import type { ForumDisplayModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
|
+
|
|
4
|
+
export function ForumDisplay({
|
|
5
|
+
forum,
|
|
6
|
+
newThreadHref,
|
|
7
|
+
markReadAction,
|
|
8
|
+
regions,
|
|
9
|
+
copy,
|
|
10
|
+
}: ForumDisplayModel & { copy: SlotCopy }) {
|
|
11
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.forumDisplay.${key}`)
|
|
2
12
|
|
|
3
|
-
export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }: ForumDisplayModel) {
|
|
4
13
|
return (
|
|
5
14
|
<div className="flex flex-col gap-3 p-3">
|
|
6
15
|
<div className="flex flex-wrap items-baseline justify-between gap-2">
|
|
@@ -10,7 +19,7 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
10
19
|
href={newThreadHref}
|
|
11
20
|
className="border border-primary bg-primary px-3 py-1 font-mono text-xs text-primary-foreground hover:opacity-90"
|
|
12
21
|
>
|
|
13
|
-
|
|
22
|
+
{c('newThread')}
|
|
14
23
|
</a>
|
|
15
24
|
)}
|
|
16
25
|
</div>
|
|
@@ -32,10 +41,18 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
32
41
|
<table className="w-full border-collapse border border-border text-sm">
|
|
33
42
|
<thead>
|
|
34
43
|
<tr className="bg-secondary text-left font-mono text-xs uppercase tracking-wide text-muted-foreground">
|
|
35
|
-
<th scope="col" className="px-3 py-1.5">
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<th scope="col" className="w-
|
|
44
|
+
<th scope="col" className="px-3 py-1.5">
|
|
45
|
+
{c('threadHeader')}
|
|
46
|
+
</th>
|
|
47
|
+
<th scope="col" className="w-16 px-2 py-1.5 text-right">
|
|
48
|
+
{c('repliesHeader')}
|
|
49
|
+
</th>
|
|
50
|
+
<th scope="col" className="w-16 px-2 py-1.5 text-right">
|
|
51
|
+
{c('viewsHeader')}
|
|
52
|
+
</th>
|
|
53
|
+
<th scope="col" className="w-56 px-3 py-1.5">
|
|
54
|
+
{c('lastPostHeader')}
|
|
55
|
+
</th>
|
|
39
56
|
</tr>
|
|
40
57
|
</thead>
|
|
41
58
|
<tbody>{regions.threads}</tbody>
|
|
@@ -53,7 +70,7 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
53
70
|
type="submit"
|
|
54
71
|
className="border border-border px-2 py-1 font-mono text-xs text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
55
72
|
>
|
|
56
|
-
|
|
73
|
+
{c('markForumRead')}
|
|
57
74
|
</button>
|
|
58
75
|
</form>
|
|
59
76
|
)}
|
package/src/slots/forum-row.tsx
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { ForumRowSlotModel } from '@meith/theme-kit'
|
|
1
|
+
import type { ForumRowSlotModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
2
3
|
|
|
3
4
|
import { UserRef } from '../shared'
|
|
4
5
|
|
|
5
|
-
export function ForumRow({ forum }: ForumRowSlotModel) {
|
|
6
|
+
export function ForumRow({ forum, copy }: ForumRowSlotModel & { copy: SlotCopy }) {
|
|
6
7
|
const isLink = forum.type === 'link'
|
|
8
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.forumRow.${key}`)
|
|
7
9
|
|
|
8
10
|
return (
|
|
9
11
|
<tr className="border-t border-border align-top odd:bg-card even:bg-muted/40">
|
|
@@ -15,7 +17,7 @@ export function ForumRow({ forum }: ForumRowSlotModel) {
|
|
|
15
17
|
<a href={forum.href} className="font-medium hover:text-primary">
|
|
16
18
|
{forum.title}
|
|
17
19
|
</a>
|
|
18
|
-
{forum.isUnread && <span className="sr-only">(
|
|
20
|
+
{forum.isUnread && <span className="sr-only">{c('newPosts')}</span>}
|
|
19
21
|
</div>
|
|
20
22
|
{forum.description !== null && (
|
|
21
23
|
<p className="mt-0.5 text-xs text-muted-foreground">{forum.description}</p>
|
|
@@ -32,14 +34,14 @@ export function ForumRow({ forum }: ForumRowSlotModel) {
|
|
|
32
34
|
</td>
|
|
33
35
|
|
|
34
36
|
<td className="w-16 px-2 py-2 text-right font-mono text-xs text-muted-foreground">
|
|
35
|
-
{isLink ? '' : forum.threadCount}
|
|
37
|
+
{isLink ? '' : forum.threadCount.label}
|
|
36
38
|
</td>
|
|
37
39
|
<td className="w-16 px-2 py-2 text-right font-mono text-xs text-muted-foreground">
|
|
38
|
-
{isLink ? '' : forum.postCount}
|
|
40
|
+
{isLink ? '' : forum.postCount.label}
|
|
39
41
|
</td>
|
|
40
42
|
<td className="w-64 px-3 py-2 text-xs text-muted-foreground">
|
|
41
43
|
{isLink || forum.lastPost === null ? (
|
|
42
|
-
<span className="text-forum-read">{isLink ? '' : '
|
|
44
|
+
<span className="text-forum-read">{isLink ? '' : c('noPosts')}</span>
|
|
43
45
|
) : (
|
|
44
46
|
<>
|
|
45
47
|
<a href={forum.lastPost.href} className="block truncate hover:text-primary">
|
package/src/slots/header.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { HeaderModel } from '@meith/theme-kit'
|
|
1
|
+
import type { HeaderModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
2
3
|
|
|
3
4
|
function BoardMark({ boardTitle, logo }: Pick<HeaderModel, 'boardTitle' | 'logo'>) {
|
|
4
5
|
if (logo === undefined) return <>{boardTitle}</>
|
|
@@ -22,7 +23,16 @@ function BoardMark({ boardTitle, logo }: Pick<HeaderModel, 'boardTitle' | 'logo'
|
|
|
22
23
|
)
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
export function Header({
|
|
26
|
+
export function Header({
|
|
27
|
+
boardTitle,
|
|
28
|
+
homeHref,
|
|
29
|
+
navigation,
|
|
30
|
+
logo,
|
|
31
|
+
children,
|
|
32
|
+
copy,
|
|
33
|
+
}: HeaderModel & { copy: SlotCopy }) {
|
|
34
|
+
const c = (key: string) => fromSlotCopy(copy, `midnight.header.${key}`)
|
|
35
|
+
|
|
26
36
|
return (
|
|
27
37
|
<header>
|
|
28
38
|
<div className="flex flex-wrap items-baseline justify-between gap-3 border-b border-border bg-secondary px-4 py-3">
|
|
@@ -36,7 +46,7 @@ export function Header({ boardTitle, homeHref, navigation, logo, children }: Hea
|
|
|
36
46
|
</div>
|
|
37
47
|
|
|
38
48
|
{navigation.length > 0 && (
|
|
39
|
-
<nav aria-label=
|
|
49
|
+
<nav aria-label={c('sectionsLabel')} className="flex flex-wrap border-b border-border">
|
|
40
50
|
{navigation.map((item) => (
|
|
41
51
|
<a
|
|
42
52
|
key={item.href}
|