@meith/theme-clubhouse 0.7.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 +4 -4
- package/src/copy.ts +229 -0
- package/src/index.ts +2 -2
- package/src/messages/en.json +126 -0
- package/src/messages/index.ts +5 -0
- package/src/shared.tsx +3 -3
- package/src/slots/announcement.tsx +15 -6
- package/src/slots/board-index.tsx +11 -4
- package/src/slots/board-stats.tsx +12 -10
- package/src/slots/category-block.tsx +9 -3
- package/src/slots/footer.tsx +15 -4
- package/src/slots/forum-display.tsx +24 -14
- package/src/slots/forum-row.tsx +10 -8
- package/src/slots/header.tsx +13 -3
- package/src/slots/latest-posts.tsx +10 -7
- package/src/slots/latest-threads.tsx +15 -8
- package/src/slots/member-profile.tsx +14 -12
- package/src/slots/navigation.tsx +6 -3
- package/src/slots/notice.tsx +13 -10
- package/src/slots/pagination.tsx +15 -9
- package/src/slots/post-actions.tsx +18 -11
- package/src/slots/post-bit.tsx +32 -17
- package/src/slots/shell.tsx +4 -3
- package/src/slots/subforum-list.tsx +7 -5
- package/src/slots/thread-row.tsx +15 -12
- package/src/slots/thread-view.tsx +18 -9
- package/src/slots/user-panel.tsx +17 -13
- package/src/slots/who-is-online.tsx +39 -20
- package/src/theme.ts +48 -0
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { ForumDisplayModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
|
+
import { Card, CardRows, cn, Empty, EmptyAction, EmptyDescription, EmptyTitle } from '@meith/ui'
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
BUTTON,
|
|
6
7
|
ColumnHeads,
|
|
7
8
|
HEADING,
|
|
9
|
+
isEmptyRegion,
|
|
8
10
|
MICRO,
|
|
9
11
|
NUMERIC,
|
|
10
12
|
PAGE_BODY,
|
|
11
13
|
PageHead,
|
|
12
|
-
isEmptyRegion,
|
|
13
14
|
} from '../shared'
|
|
14
15
|
|
|
15
|
-
export function ForumDisplay({
|
|
16
|
+
export function ForumDisplay({
|
|
17
|
+
forum,
|
|
18
|
+
newThreadHref,
|
|
19
|
+
markReadAction,
|
|
20
|
+
regions,
|
|
21
|
+
copy,
|
|
22
|
+
}: ForumDisplayModel & { copy: SlotCopy }) {
|
|
23
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.forumDisplay.${key}`)
|
|
24
|
+
|
|
16
25
|
return (
|
|
17
26
|
<div className={PAGE_BODY}>
|
|
18
27
|
<PageHead
|
|
@@ -27,7 +36,7 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
27
36
|
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
28
37
|
)}
|
|
29
38
|
>
|
|
30
|
-
|
|
39
|
+
{c('markRead')}
|
|
31
40
|
</button>
|
|
32
41
|
</form>
|
|
33
42
|
)}
|
|
@@ -36,7 +45,7 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
36
45
|
href={newThreadHref}
|
|
37
46
|
className={cn(BUTTON, 'bg-primary text-primary-foreground hover:bg-primary-hover')}
|
|
38
47
|
>
|
|
39
|
-
|
|
48
|
+
{c('newThread')}
|
|
40
49
|
</a>
|
|
41
50
|
)}
|
|
42
51
|
</>
|
|
@@ -46,8 +55,7 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
46
55
|
<p className="mt-0.5 flex flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground">
|
|
47
56
|
{forum.type !== 'link' && (
|
|
48
57
|
<span className={`${MICRO} ${NUMERIC}`}>
|
|
49
|
-
{forum.threadCount.
|
|
50
|
-
{forum.postCount.toLocaleString('en')} posts
|
|
58
|
+
{forum.threadCount.label} {c('threads')} · {forum.postCount.label} {c('posts')}
|
|
51
59
|
</span>
|
|
52
60
|
)}
|
|
53
61
|
{forum.description !== null && <span>{forum.description}</span>}
|
|
@@ -67,11 +75,9 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
67
75
|
<Card>
|
|
68
76
|
{isEmptyRegion(regions.threads) ? (
|
|
69
77
|
<Empty>
|
|
70
|
-
<EmptyTitle>
|
|
78
|
+
<EmptyTitle>{c('noThreadsYet')}</EmptyTitle>
|
|
71
79
|
<EmptyDescription>
|
|
72
|
-
{newThreadHref === null
|
|
73
|
-
? 'Nothing has been posted in this forum.'
|
|
74
|
-
: 'Nothing has been posted in this forum. Yours would be the first.'}
|
|
80
|
+
{newThreadHref === null ? c('emptyNoThread') : c('emptyNoThreadFirst')}
|
|
75
81
|
</EmptyDescription>
|
|
76
82
|
{newThreadHref !== null && (
|
|
77
83
|
<EmptyAction>
|
|
@@ -82,14 +88,18 @@ export function ForumDisplay({ forum, newThreadHref, markReadAction, regions }:
|
|
|
82
88
|
'bg-primary text-primary-foreground hover:bg-primary-hover',
|
|
83
89
|
)}
|
|
84
90
|
>
|
|
85
|
-
|
|
91
|
+
{c('startFirstThread')}
|
|
86
92
|
</a>
|
|
87
93
|
</EmptyAction>
|
|
88
94
|
)}
|
|
89
95
|
</Empty>
|
|
90
96
|
) : (
|
|
91
97
|
<>
|
|
92
|
-
<ColumnHeads
|
|
98
|
+
<ColumnHeads
|
|
99
|
+
first={c('thread')}
|
|
100
|
+
counts={[c('replies'), c('views')]}
|
|
101
|
+
last={c('latest')}
|
|
102
|
+
/>
|
|
93
103
|
<CardRows>{regions.threads}</CardRows>
|
|
94
104
|
</>
|
|
95
105
|
)}
|
package/src/slots/forum-row.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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 { CHIP, HEADING, LINK, MICRO, ReadMark, Stamp, TABLE_ROW, Tally, UserRef } from '../shared'
|
|
4
5
|
|
|
5
|
-
export function ForumRow({ forum }: ForumRowSlotModel) {
|
|
6
|
+
export function ForumRow({ forum, copy }: ForumRowSlotModel & { copy: SlotCopy }) {
|
|
7
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.forumRow.${key}`)
|
|
6
8
|
const isLink = forum.type === 'link'
|
|
7
9
|
|
|
8
10
|
return (
|
|
@@ -19,8 +21,8 @@ export function ForumRow({ forum }: ForumRowSlotModel) {
|
|
|
19
21
|
>
|
|
20
22
|
{forum.title}
|
|
21
23
|
</a>
|
|
22
|
-
{forum.isUnread && <span className="sr-only"> (
|
|
23
|
-
{isLink && <span className={`${MICRO} ml-2`}>
|
|
24
|
+
{forum.isUnread && <span className="sr-only"> {c('newPosts')}</span>}
|
|
25
|
+
{isLink && <span className={`${MICRO} ml-2`}>{c('link')}</span>}
|
|
24
26
|
|
|
25
27
|
{forum.description !== null && (
|
|
26
28
|
<p className="mt-0.5 text-xs text-muted-foreground">{forum.description}</p>
|
|
@@ -40,19 +42,19 @@ export function ForumRow({ forum }: ForumRowSlotModel) {
|
|
|
40
42
|
{!isLink && (
|
|
41
43
|
<>
|
|
42
44
|
<span className="col-start-2 md:col-start-3 md:block md:text-right">
|
|
43
|
-
<Tally value={forum.threadCount} label=
|
|
45
|
+
<Tally value={forum.threadCount} label={c('threads')} />
|
|
44
46
|
<span className="ms-3 md:hidden">
|
|
45
|
-
<Tally value={forum.postCount} label=
|
|
47
|
+
<Tally value={forum.postCount} label={c('posts')} />
|
|
46
48
|
</span>
|
|
47
49
|
</span>
|
|
48
50
|
|
|
49
51
|
<span className="hidden md:block md:text-right">
|
|
50
|
-
<Tally value={forum.postCount} label=
|
|
52
|
+
<Tally value={forum.postCount} label={c('posts')} />
|
|
51
53
|
</span>
|
|
52
54
|
|
|
53
55
|
<div className="col-start-2 min-w-0 text-xs text-muted-foreground md:col-start-5">
|
|
54
56
|
{forum.lastPost === null ? (
|
|
55
|
-
<span className={MICRO}>
|
|
57
|
+
<span className={MICRO}>{c('noPostsYet')}</span>
|
|
56
58
|
) : (
|
|
57
59
|
<>
|
|
58
60
|
<a
|
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
|
import { ClubRule, Crest, HEADING, PAGE, TAB } from '../shared'
|
|
4
5
|
|
|
@@ -33,7 +34,16 @@ function BoardMark({ boardTitle, logo }: Pick<HeaderModel, 'boardTitle' | 'logo'
|
|
|
33
34
|
)
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export function Header({
|
|
37
|
+
export function Header({
|
|
38
|
+
boardTitle,
|
|
39
|
+
homeHref,
|
|
40
|
+
navigation,
|
|
41
|
+
logo,
|
|
42
|
+
children,
|
|
43
|
+
copy,
|
|
44
|
+
}: HeaderModel & { copy: SlotCopy }) {
|
|
45
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.header.${key}`)
|
|
46
|
+
|
|
37
47
|
return (
|
|
38
48
|
<header className="bg-card">
|
|
39
49
|
<div className={`${PAGE} flex flex-wrap items-center justify-between gap-x-6 gap-y-3 py-3`}>
|
|
@@ -49,7 +59,7 @@ export function Header({ boardTitle, homeHref, navigation, logo, children }: Hea
|
|
|
49
59
|
<ClubRule />
|
|
50
60
|
|
|
51
61
|
{navigation.length > 0 && (
|
|
52
|
-
<nav aria-label=
|
|
62
|
+
<nav aria-label={c('boardSections')} className="border-b border-border bg-surface">
|
|
53
63
|
<div className={PAGE}>
|
|
54
64
|
<ul className="-mx-4 flex items-stretch overflow-x-auto px-4 sm:-mx-6 sm:px-6">
|
|
55
65
|
{navigation.map((item) => (
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
+
import type { LatestPostsModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
1
3
|
import { Card, CardContent, Empty, EmptyDescription, EmptyTitle } from '@meith/ui'
|
|
2
|
-
import type { LatestPostsModel } from '@meith/theme-kit'
|
|
3
4
|
|
|
4
5
|
import { LINK, MICRO, MUTED_LINK, PanelHead, Stamp, UserRef } from '../shared'
|
|
5
6
|
|
|
6
|
-
export function LatestPosts({ posts, capturedAt }: LatestPostsModel) {
|
|
7
|
+
export function LatestPosts({ posts, capturedAt, copy }: LatestPostsModel & { copy: SlotCopy }) {
|
|
8
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.latestPosts.${key}`)
|
|
9
|
+
|
|
7
10
|
return (
|
|
8
11
|
<Card aria-labelledby="latest-posts-heading">
|
|
9
12
|
<PanelHead
|
|
10
13
|
id="latest-posts-heading"
|
|
11
|
-
title=
|
|
14
|
+
title={c('title')}
|
|
12
15
|
aside={
|
|
13
16
|
<span className={MICRO}>
|
|
14
|
-
|
|
17
|
+
{c('asOf')} <Stamp at={capturedAt} />
|
|
15
18
|
</span>
|
|
16
19
|
}
|
|
17
20
|
/>
|
|
18
21
|
|
|
19
22
|
{posts.length === 0 ? (
|
|
20
23
|
<Empty className="py-5">
|
|
21
|
-
<EmptyTitle>
|
|
22
|
-
<EmptyDescription>
|
|
24
|
+
<EmptyTitle>{c('nothingSaidYet')}</EmptyTitle>
|
|
25
|
+
<EmptyDescription>{c('newestPostsEmpty')}</EmptyDescription>
|
|
23
26
|
</Empty>
|
|
24
27
|
) : (
|
|
25
28
|
<CardContent className="px-0 py-0">
|
|
@@ -39,7 +42,7 @@ export function LatestPosts({ posts, capturedAt }: LatestPostsModel) {
|
|
|
39
42
|
|
|
40
43
|
<p className="truncate text-xs text-muted-foreground">
|
|
41
44
|
<UserRef user={post.author} className="font-medium" />
|
|
42
|
-
{'
|
|
45
|
+
{` ${c('in')} `}
|
|
43
46
|
<a href={post.forum.href} className={MUTED_LINK}>
|
|
44
47
|
{post.forum.label}
|
|
45
48
|
</a>
|
|
@@ -1,25 +1,32 @@
|
|
|
1
|
+
import type { LatestThreadsModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
1
3
|
import { Card, CardContent, Empty, EmptyDescription, EmptyTitle } from '@meith/ui'
|
|
2
|
-
import type { LatestThreadsModel } from '@meith/theme-kit'
|
|
3
4
|
|
|
4
5
|
import { LINK, MICRO, MUTED_LINK, NUMERIC, PanelHead, Stamp, UserRef } from '../shared'
|
|
5
6
|
|
|
6
|
-
export function LatestThreads({
|
|
7
|
+
export function LatestThreads({
|
|
8
|
+
threads,
|
|
9
|
+
capturedAt,
|
|
10
|
+
copy,
|
|
11
|
+
}: LatestThreadsModel & { copy: SlotCopy }) {
|
|
12
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.latestThreads.${key}`)
|
|
13
|
+
|
|
7
14
|
return (
|
|
8
15
|
<Card aria-labelledby="latest-threads-heading">
|
|
9
16
|
<PanelHead
|
|
10
17
|
id="latest-threads-heading"
|
|
11
|
-
title=
|
|
18
|
+
title={c('title')}
|
|
12
19
|
aside={
|
|
13
20
|
<span className={MICRO}>
|
|
14
|
-
|
|
21
|
+
{c('asOf')} <Stamp at={capturedAt} />
|
|
15
22
|
</span>
|
|
16
23
|
}
|
|
17
24
|
/>
|
|
18
25
|
|
|
19
26
|
{threads.length === 0 ? (
|
|
20
27
|
<Empty className="py-5">
|
|
21
|
-
<EmptyTitle>
|
|
22
|
-
<EmptyDescription>
|
|
28
|
+
<EmptyTitle>{c('nothingStartedYet')}</EmptyTitle>
|
|
29
|
+
<EmptyDescription>{c('newestThreadsEmpty')}</EmptyDescription>
|
|
23
30
|
</Empty>
|
|
24
31
|
) : (
|
|
25
32
|
<CardContent className="px-0 py-0">
|
|
@@ -34,13 +41,13 @@ export function LatestThreads({ threads, capturedAt }: LatestThreadsModel) {
|
|
|
34
41
|
{thread.title}
|
|
35
42
|
</a>
|
|
36
43
|
<span className={`shrink-0 text-xs text-muted-foreground ${NUMERIC}`}>
|
|
37
|
-
{thread.replyCount.
|
|
44
|
+
{thread.replyCount.label}
|
|
38
45
|
</span>
|
|
39
46
|
</div>
|
|
40
47
|
|
|
41
48
|
<p className="truncate text-xs text-muted-foreground">
|
|
42
49
|
<UserRef user={thread.author} className="font-medium" />
|
|
43
|
-
{'
|
|
50
|
+
{` ${c('in')} `}
|
|
44
51
|
<a href={thread.forum.href} className={MUTED_LINK}>
|
|
45
52
|
{thread.forum.label}
|
|
46
53
|
</a>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { MemberProfileModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
1
3
|
import { Avatar, Card, CardContent, cn } from '@meith/ui'
|
|
2
|
-
import type { MemberProfileModel } from '@meith/theme-kit'
|
|
3
4
|
|
|
4
5
|
import { BUTTON, ClubBar, HEADING, MICRO, NUMERIC, PAGE_BODY, PanelHead, Stamp } from '../shared'
|
|
5
6
|
|
|
@@ -14,7 +15,10 @@ export function MemberProfile({
|
|
|
14
15
|
fields,
|
|
15
16
|
actions,
|
|
16
17
|
regions,
|
|
17
|
-
|
|
18
|
+
copy,
|
|
19
|
+
}: MemberProfileModel & { copy: SlotCopy }) {
|
|
20
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.memberProfile.${key}`)
|
|
21
|
+
|
|
18
22
|
return (
|
|
19
23
|
<div className={PAGE_BODY}>
|
|
20
24
|
<Card className="flex items-stretch">
|
|
@@ -40,7 +44,7 @@ export function MemberProfile({
|
|
|
40
44
|
|
|
41
45
|
{actions.length > 0 && (
|
|
42
46
|
<nav
|
|
43
|
-
aria-label=
|
|
47
|
+
aria-label={c('memberActions')}
|
|
44
48
|
className="flex shrink-0 flex-wrap items-center gap-2"
|
|
45
49
|
>
|
|
46
50
|
{actions.map((action, index) => (
|
|
@@ -63,21 +67,19 @@ export function MemberProfile({
|
|
|
63
67
|
|
|
64
68
|
<dl className="flex flex-wrap gap-x-6 gap-y-1 border-t-2 border-t-secondary bg-surface px-4 py-2.5 text-xs">
|
|
65
69
|
<div className="flex items-baseline gap-1.5">
|
|
66
|
-
<dt className={MICRO}>
|
|
67
|
-
<dd className={`${NUMERIC} font-semibold text-foreground`}>
|
|
68
|
-
{postCount.toLocaleString('en')}
|
|
69
|
-
</dd>
|
|
70
|
+
<dt className={MICRO}>{c('posts')}</dt>
|
|
71
|
+
<dd className={`${NUMERIC} font-semibold text-foreground`}>{postCount.label}</dd>
|
|
70
72
|
</div>
|
|
71
73
|
<div className="flex items-baseline gap-1.5">
|
|
72
|
-
<dt className={MICRO}>
|
|
74
|
+
<dt className={MICRO}>{c('joined')}</dt>
|
|
73
75
|
<dd className="font-semibold text-foreground">
|
|
74
76
|
<Stamp at={joinedAt} />
|
|
75
77
|
</dd>
|
|
76
78
|
</div>
|
|
77
79
|
<div className="flex items-baseline gap-1.5">
|
|
78
|
-
<dt className={MICRO}>
|
|
80
|
+
<dt className={MICRO}>{c('lastVisit')}</dt>
|
|
79
81
|
<dd className="font-semibold text-foreground">
|
|
80
|
-
{lastVisitAt === null ? '
|
|
82
|
+
{lastVisitAt === null ? c('never') : <Stamp at={lastVisitAt} />}
|
|
81
83
|
</dd>
|
|
82
84
|
</div>
|
|
83
85
|
</dl>
|
|
@@ -86,7 +88,7 @@ export function MemberProfile({
|
|
|
86
88
|
|
|
87
89
|
{fields.length > 0 && (
|
|
88
90
|
<Card aria-labelledby="profile-fields-heading">
|
|
89
|
-
<PanelHead id="profile-fields-heading" title=
|
|
91
|
+
<PanelHead id="profile-fields-heading" title={c('about')} />
|
|
90
92
|
<CardContent className="px-4 py-3">
|
|
91
93
|
<dl className="grid gap-x-6 gap-y-2.5 sm:grid-cols-2">
|
|
92
94
|
{fields.map((field) => (
|
|
@@ -102,7 +104,7 @@ export function MemberProfile({
|
|
|
102
104
|
|
|
103
105
|
{signatureHtml !== null && (
|
|
104
106
|
<Card aria-labelledby="profile-signature-heading">
|
|
105
|
-
<PanelHead id="profile-signature-heading" title=
|
|
107
|
+
<PanelHead id="profile-signature-heading" title={c('signature')} />
|
|
106
108
|
<CardContent className="px-4 py-3">
|
|
107
109
|
<div
|
|
108
110
|
className="prose-md text-sm text-muted-foreground"
|
package/src/slots/navigation.tsx
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import type { NavigationModel } from '@meith/theme-kit'
|
|
1
|
+
import type { NavigationModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
2
3
|
|
|
3
4
|
import { MICRO, MUTED_LINK, PAGE } from '../shared'
|
|
4
5
|
|
|
5
|
-
export function Navigation({ items }: NavigationModel) {
|
|
6
|
+
export function Navigation({ items, copy }: NavigationModel & { copy: SlotCopy }) {
|
|
6
7
|
if (items.length === 0) return null
|
|
7
8
|
|
|
9
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.navigation.${key}`)
|
|
10
|
+
|
|
8
11
|
return (
|
|
9
|
-
<nav aria-label=
|
|
12
|
+
<nav aria-label={c('breadcrumb')} className="border-b border-border bg-card">
|
|
10
13
|
<ol
|
|
11
14
|
className={`${PAGE} flex items-center gap-2 overflow-x-auto py-2 whitespace-nowrap ${MICRO}`}
|
|
12
15
|
>
|
package/src/slots/notice.tsx
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
+
import type { NoticeModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
1
3
|
import { Alert, AlertDescription, AlertTitle } from '@meith/ui'
|
|
2
|
-
import type { NoticeModel } from '@meith/theme-kit'
|
|
3
4
|
|
|
4
5
|
import { MICRO, MUTED_LINK } from '../shared'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
export function Notice({ kind, message, dismissHref, copy }: NoticeModel & { copy: SlotCopy }) {
|
|
8
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.notice.${key}`)
|
|
9
|
+
|
|
10
|
+
const kindLabels: Record<NoticeModel['kind'], string> = {
|
|
11
|
+
info: c('kindInfo'),
|
|
12
|
+
success: c('kindSuccess'),
|
|
13
|
+
warning: c('kindWarning'),
|
|
14
|
+
error: c('kindError'),
|
|
15
|
+
}
|
|
12
16
|
|
|
13
|
-
export function Notice({ kind, message, dismissHref }: NoticeModel) {
|
|
14
17
|
return (
|
|
15
18
|
<Alert tone={kind}>
|
|
16
19
|
<AlertDescription>
|
|
17
|
-
<AlertTitle className={MICRO}>{
|
|
20
|
+
<AlertTitle className={MICRO}>{kindLabels[kind]}</AlertTitle> {message}
|
|
18
21
|
</AlertDescription>
|
|
19
22
|
|
|
20
23
|
{dismissHref !== null && (
|
|
21
24
|
<a href={dismissHref} className={`shrink-0 text-xs ${MUTED_LINK}`}>
|
|
22
|
-
|
|
25
|
+
{c('dismiss')}
|
|
23
26
|
</a>
|
|
24
27
|
)}
|
|
25
28
|
</Alert>
|
package/src/slots/pagination.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { PaginationModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
1
3
|
import { cn } from '@meith/ui'
|
|
2
|
-
import type { PaginationModel } from '@meith/theme-kit'
|
|
3
4
|
|
|
4
5
|
import { BUTTON, MICRO, NUMERIC } from '../shared'
|
|
5
6
|
|
|
@@ -10,21 +11,24 @@ export function Pagination({
|
|
|
10
11
|
pages,
|
|
11
12
|
previousHref,
|
|
12
13
|
nextHref,
|
|
13
|
-
|
|
14
|
+
copy,
|
|
15
|
+
}: PaginationModel & { copy: SlotCopy }) {
|
|
14
16
|
if (pageCount <= 1 && previousHref === null && nextHref === null) return null
|
|
15
17
|
|
|
18
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.pagination.${key}`)
|
|
19
|
+
|
|
16
20
|
const step = cn(BUTTON, 'min-w-20 justify-center border border-border bg-card hover:bg-accent')
|
|
17
21
|
const stepDisabled = cn(step, 'pointer-events-none opacity-40')
|
|
18
22
|
|
|
19
23
|
return (
|
|
20
|
-
<nav aria-label=
|
|
24
|
+
<nav aria-label={c('pagination')} className="flex flex-wrap items-center justify-between gap-3">
|
|
21
25
|
{previousHref === null ? (
|
|
22
26
|
<span className={stepDisabled} aria-hidden="true">
|
|
23
|
-
|
|
27
|
+
{c('previous')}
|
|
24
28
|
</span>
|
|
25
29
|
) : (
|
|
26
30
|
<a href={previousHref} rel="prev" className={step}>
|
|
27
|
-
|
|
31
|
+
{c('previous')}
|
|
28
32
|
</a>
|
|
29
33
|
)}
|
|
30
34
|
|
|
@@ -43,7 +47,7 @@ export function Pagination({
|
|
|
43
47
|
<a
|
|
44
48
|
href={entry.href}
|
|
45
49
|
aria-current={entry.isCurrent ? 'page' : undefined}
|
|
46
|
-
aria-label={
|
|
50
|
+
aria-label={`${c('page')} ${entry.page}`}
|
|
47
51
|
className={cn(
|
|
48
52
|
BUTTON,
|
|
49
53
|
'min-w-8 justify-center px-2.5',
|
|
@@ -61,18 +65,20 @@ export function Pagination({
|
|
|
61
65
|
|
|
62
66
|
<li className={`ml-2 whitespace-nowrap ${MICRO}`}>
|
|
63
67
|
<span className={NUMERIC}>
|
|
64
|
-
{pageCountIsExact
|
|
68
|
+
{pageCountIsExact
|
|
69
|
+
? `${c('page')} ${page} ${c('of')} ${pageCount}`
|
|
70
|
+
: `${c('page')} ${page}`}
|
|
65
71
|
</span>
|
|
66
72
|
</li>
|
|
67
73
|
</ol>
|
|
68
74
|
|
|
69
75
|
{nextHref === null ? (
|
|
70
76
|
<span className={stepDisabled} aria-hidden="true">
|
|
71
|
-
|
|
77
|
+
{c('next')}
|
|
72
78
|
</span>
|
|
73
79
|
) : (
|
|
74
80
|
<a href={nextHref} rel="next" className={step}>
|
|
75
|
-
|
|
81
|
+
{c('next')}
|
|
76
82
|
</a>
|
|
77
83
|
)}
|
|
78
84
|
</nav>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { PostActionsSlotModel, SlotCopy } from '@meith/theme-kit'
|
|
2
|
+
import { fromSlotCopy } from '@meith/theme-kit'
|
|
3
|
+
import { cn, Separator } from '@meith/ui'
|
|
3
4
|
|
|
4
5
|
import { BUTTON, isEmptyRegion } from '../shared'
|
|
5
6
|
|
|
@@ -10,18 +11,24 @@ interface Action {
|
|
|
10
11
|
|
|
11
12
|
const ACTION = cn(BUTTON, 'h-7 px-2.5 text-muted-foreground hover:bg-accent hover:text-foreground')
|
|
12
13
|
|
|
13
|
-
export function PostActions({
|
|
14
|
+
export function PostActions({
|
|
15
|
+
actions,
|
|
16
|
+
children,
|
|
17
|
+
copy,
|
|
18
|
+
}: PostActionsSlotModel & { copy: SlotCopy }) {
|
|
19
|
+
const c = (key: string) => fromSlotCopy(copy, `clubhouse.postActions.${key}`)
|
|
20
|
+
|
|
14
21
|
const reader: Action[] = [
|
|
15
|
-
actions.quoteHref === null ? null : { href: actions.quoteHref, label: '
|
|
16
|
-
actions.editHref === null ? null : { href: actions.editHref, label: '
|
|
17
|
-
actions.rateHref === null ? null : { href: actions.rateHref, label: '
|
|
18
|
-
actions.reportHref === null ? null : { href: actions.reportHref, label: '
|
|
22
|
+
actions.quoteHref === null ? null : { href: actions.quoteHref, label: c('quote') },
|
|
23
|
+
actions.editHref === null ? null : { href: actions.editHref, label: c('edit') },
|
|
24
|
+
actions.rateHref === null ? null : { href: actions.rateHref, label: c('rate') },
|
|
25
|
+
actions.reportHref === null ? null : { href: actions.reportHref, label: c('report') },
|
|
19
26
|
].filter((action): action is Action => action !== null)
|
|
20
27
|
|
|
21
28
|
const staff: Action[] = [
|
|
22
|
-
actions.restoreHref === null ? null : { href: actions.restoreHref, label: '
|
|
23
|
-
actions.warnHref === null ? null : { href: actions.warnHref, label: '
|
|
24
|
-
actions.moderateHref === null ? null : { href: actions.moderateHref, label: '
|
|
29
|
+
actions.restoreHref === null ? null : { href: actions.restoreHref, label: c('restore') },
|
|
30
|
+
actions.warnHref === null ? null : { href: actions.warnHref, label: c('warn') },
|
|
31
|
+
actions.moderateHref === null ? null : { href: actions.moderateHref, label: c('moderate') },
|
|
25
32
|
].filter((action): action is Action => action !== null)
|
|
26
33
|
|
|
27
34
|
const extra = isEmptyRegion(children) ? null : children
|
|
@@ -30,7 +37,7 @@ export function PostActions({ actions, children }: PostActionsSlotModel) {
|
|
|
30
37
|
|
|
31
38
|
return (
|
|
32
39
|
<nav
|
|
33
|
-
aria-label=
|
|
40
|
+
aria-label={c('postActions')}
|
|
34
41
|
className="flex flex-wrap items-center gap-1 border-t border-border bg-surface px-3 py-1.5 empty:hidden"
|
|
35
42
|
>
|
|
36
43
|
{reader.map((action) => (
|