@jant/core 0.3.24 → 0.3.25
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/dist/app.js +50 -25
- package/dist/db/schema.js +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.js +3 -9
- package/dist/lib/constants.js +1 -0
- package/dist/lib/nav-reorder.js +1 -1
- package/dist/lib/navigation.js +26 -1
- package/dist/lib/pagination.js +44 -0
- package/dist/lib/render.js +7 -11
- package/dist/lib/schemas.js +3 -3
- package/dist/lib/theme.js +4 -4
- package/dist/lib/timeline.js +24 -48
- package/dist/lib/view.js +2 -2
- package/dist/routes/api/collections.js +124 -0
- package/dist/routes/api/nav-items.js +104 -0
- package/dist/routes/api/pages.js +91 -0
- package/dist/routes/api/posts.js +2 -2
- package/dist/routes/api/search.js +2 -2
- package/dist/routes/api/settings.js +68 -0
- package/dist/routes/compose.js +48 -0
- package/dist/routes/dash/collections.js +2 -2
- package/dist/routes/dash/index.js +1 -1
- package/dist/routes/dash/media.js +2 -2
- package/dist/routes/dash/pages.js +411 -62
- package/dist/routes/dash/posts.js +3 -5
- package/dist/routes/dash/redirects.js +2 -2
- package/dist/routes/dash/settings.js +79 -5
- package/dist/routes/feed/rss.js +2 -2
- package/dist/routes/feed/sitemap.js +1 -1
- package/dist/routes/pages/archive.js +3 -6
- package/dist/routes/pages/collection.js +3 -6
- package/dist/routes/pages/collections.js +28 -0
- package/dist/routes/pages/featured.js +32 -0
- package/dist/routes/pages/home.js +9 -50
- package/dist/routes/pages/page.js +29 -32
- package/dist/routes/pages/post.js +3 -6
- package/dist/routes/pages/search.js +3 -6
- package/dist/services/page.js +5 -1
- package/dist/services/post.js +40 -6
- package/dist/services/search.js +1 -1
- package/dist/ui/compose/ComposeDialog.js +452 -0
- package/dist/ui/compose/ComposePrompt.js +55 -0
- package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +1 -2
- package/dist/{theme/components → ui/dash}/PostForm.js +0 -27
- package/dist/{theme/components → ui/dash}/PostList.js +6 -6
- package/dist/{theme/components/VisibilityBadge.js → ui/dash/StatusBadge.js} +1 -2
- package/dist/{theme/components → ui/dash}/index.js +3 -6
- package/dist/{themes/threads/timeline → ui/feed}/LinkCard.js +6 -2
- package/dist/{themes/threads/timeline → ui/feed}/NoteCard.js +11 -6
- package/dist/{themes/threads/timeline → ui/feed}/QuoteCard.js +10 -6
- package/dist/{themes/threads/timeline → ui/feed}/ThreadPreview.js +7 -9
- package/dist/ui/feed/TimelineFeed.js +41 -0
- package/dist/ui/feed/TimelineItem.js +27 -0
- package/dist/{theme → ui}/layouts/BaseLayout.js +10 -0
- package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
- package/dist/ui/layouts/SiteLayout.js +141 -0
- package/dist/{themes/threads → ui}/pages/ArchivePage.js +16 -14
- package/dist/{themes/threads → ui}/pages/CollectionPage.js +6 -1
- package/dist/ui/pages/CollectionsPage.js +76 -0
- package/dist/ui/pages/FeaturedPage.js +24 -0
- package/dist/ui/pages/HomePage.js +24 -0
- package/dist/{themes/threads → ui}/pages/PostPage.js +13 -8
- package/dist/{themes/threads → ui}/pages/SearchPage.js +9 -7
- package/dist/{themes/threads → ui}/pages/SinglePage.js +3 -2
- package/dist/{theme/components → ui/shared}/MediaGallery.js +1 -1
- package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
- package/dist/{theme/components → ui/shared}/ThreadView.js +2 -2
- package/dist/ui/shared/index.js +5 -0
- package/package.json +1 -9
- package/src/__tests__/helpers/db.ts +3 -0
- package/src/app.tsx +57 -27
- package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/src/db/schema.ts +1 -1
- package/src/i18n/locales/en.po +332 -181
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +332 -181
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +332 -181
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +7 -36
- package/src/lib/__tests__/schemas.test.ts +60 -19
- package/src/lib/__tests__/timeline.test.ts +45 -81
- package/src/lib/__tests__/view.test.ts +13 -7
- package/src/lib/constants.ts +1 -0
- package/src/lib/nav-reorder.ts +1 -1
- package/src/lib/navigation.ts +40 -2
- package/src/lib/pagination.ts +50 -0
- package/src/lib/render.tsx +7 -14
- package/src/lib/schemas.ts +8 -6
- package/src/lib/theme.ts +5 -5
- package/src/lib/timeline.ts +28 -57
- package/src/lib/view.ts +2 -2
- package/src/preset.css +2 -1
- package/src/routes/__tests__/compose.test.ts +199 -0
- package/src/routes/api/__tests__/collections.test.ts +249 -0
- package/src/routes/api/__tests__/nav-items.test.ts +222 -0
- package/src/routes/api/__tests__/pages.test.ts +218 -0
- package/src/routes/api/__tests__/settings.test.ts +132 -0
- package/src/routes/api/collections.ts +143 -0
- package/src/routes/api/nav-items.ts +115 -0
- package/src/routes/api/pages.ts +101 -0
- package/src/routes/api/posts.ts +2 -2
- package/src/routes/api/search.ts +2 -2
- package/src/routes/api/settings.ts +91 -0
- package/src/routes/compose.ts +63 -0
- package/src/routes/dash/__tests__/pages.test.ts +225 -0
- package/src/routes/dash/collections.tsx +2 -2
- package/src/routes/dash/index.tsx +1 -1
- package/src/routes/dash/media.tsx +2 -2
- package/src/routes/dash/pages.tsx +443 -70
- package/src/routes/dash/posts.tsx +3 -7
- package/src/routes/dash/redirects.tsx +2 -2
- package/src/routes/dash/settings.tsx +83 -5
- package/src/routes/feed/rss.ts +2 -2
- package/src/routes/feed/sitemap.ts +1 -1
- package/src/routes/pages/__tests__/collections.test.ts +94 -0
- package/src/routes/pages/__tests__/featured.test.ts +94 -0
- package/src/routes/pages/archive.tsx +2 -6
- package/src/routes/pages/collection.tsx +2 -6
- package/src/routes/pages/collections.tsx +36 -0
- package/src/routes/pages/featured.tsx +38 -0
- package/src/routes/pages/home.tsx +9 -55
- package/src/routes/pages/page.tsx +28 -30
- package/src/routes/pages/post.tsx +2 -5
- package/src/routes/pages/search.tsx +2 -6
- package/src/services/__tests__/page.test.ts +106 -0
- package/src/services/__tests__/post.test.ts +114 -15
- package/src/services/page.ts +13 -1
- package/src/services/post.ts +57 -7
- package/src/services/search.ts +2 -2
- package/src/styles/tokens.css +47 -0
- package/src/styles/ui.css +491 -0
- package/src/types.ts +29 -159
- package/src/ui/compose/ComposeDialog.tsx +395 -0
- package/src/ui/compose/ComposePrompt.tsx +55 -0
- package/src/{theme/components/TypeBadge.tsx → ui/dash/FormatBadge.tsx} +2 -3
- package/src/{theme/components → ui/dash}/PostForm.tsx +0 -25
- package/src/{theme/components → ui/dash}/PostList.tsx +7 -7
- package/src/{theme/components/VisibilityBadge.tsx → ui/dash/StatusBadge.tsx} +2 -3
- package/src/ui/dash/index.ts +10 -0
- package/src/{themes/threads/timeline → ui/feed}/LinkCard.tsx +9 -4
- package/src/{themes/threads/timeline → ui/feed}/NoteCard.tsx +13 -8
- package/src/{themes/threads/timeline → ui/feed}/QuoteCard.tsx +13 -8
- package/src/{themes/threads/timeline → ui/feed}/ThreadPreview.tsx +7 -8
- package/src/ui/feed/TimelineFeed.tsx +49 -0
- package/src/ui/feed/TimelineItem.tsx +45 -0
- package/src/{theme → ui}/layouts/BaseLayout.tsx +11 -1
- package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
- package/src/ui/layouts/SiteLayout.tsx +150 -0
- package/src/{themes/threads → ui}/pages/ArchivePage.tsx +22 -17
- package/src/{themes/threads → ui}/pages/CollectionPage.tsx +14 -5
- package/src/ui/pages/CollectionsPage.tsx +73 -0
- package/src/ui/pages/FeaturedPage.tsx +31 -0
- package/src/{themes/threads → ui}/pages/HomePage.tsx +11 -15
- package/src/{themes/threads → ui}/pages/PostPage.tsx +23 -14
- package/src/{themes/threads → ui}/pages/SearchPage.tsx +13 -11
- package/src/{themes/threads → ui}/pages/SinglePage.tsx +4 -4
- package/src/{theme/components → ui/shared}/MediaGallery.tsx +1 -1
- package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
- package/src/{theme/components → ui/shared}/ThreadView.tsx +2 -2
- package/src/ui/shared/__tests__/pagination.test.ts +46 -0
- package/src/ui/shared/index.ts +12 -0
- package/bin/jant.js +0 -185
- package/dist/lib/theme-components.js +0 -46
- package/dist/routes/dash/navigation.js +0 -289
- package/dist/theme/index.js +0 -18
- package/dist/theme/layouts/index.js +0 -2
- package/dist/themes/threads/ThreadsSiteLayout.js +0 -172
- package/dist/themes/threads/index.js +0 -81
- package/dist/themes/threads/pages/HomePage.js +0 -25
- package/dist/themes/threads/timeline/TimelineFeed.js +0 -58
- package/dist/themes/threads/timeline/TimelineItem.js +0 -36
- package/dist/themes/threads/timeline/TimelineLoadMore.js +0 -23
- package/dist/themes/threads/timeline/groupByDate.js +0 -22
- package/dist/themes/threads/timeline/timelineMore.js +0 -107
- package/src/lib/__tests__/theme-components.test.ts +0 -105
- package/src/lib/theme-components.ts +0 -65
- package/src/routes/dash/navigation.tsx +0 -317
- package/src/theme/components/index.ts +0 -23
- package/src/theme/index.ts +0 -22
- package/src/theme/layouts/index.ts +0 -7
- package/src/themes/threads/ThreadsSiteLayout.tsx +0 -194
- package/src/themes/threads/index.ts +0 -100
- package/src/themes/threads/style.css +0 -336
- package/src/themes/threads/timeline/TimelineFeed.tsx +0 -62
- package/src/themes/threads/timeline/TimelineItem.tsx +0 -67
- package/src/themes/threads/timeline/TimelineLoadMore.tsx +0 -35
- package/src/themes/threads/timeline/groupByDate.ts +0 -30
- package/src/themes/threads/timeline/timelineMore.tsx +0 -130
- /package/dist/{theme → ui}/color-themes.js +0 -0
- /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
- /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
- /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
- /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
- /package/dist/{theme/components → ui/dash}/PageForm.js +0 -0
- /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
- /package/src/{theme → ui}/color-themes.ts +0 -0
- /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
- /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
- /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
- /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
- /package/src/{theme/components → ui/dash}/PageForm.tsx +0 -0
- /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme - Site Layout
|
|
3
|
-
*
|
|
4
|
-
* Left icon sidebar (76px) on desktop, bottom tab bar (60px) on mobile.
|
|
5
|
-
* Gray page background (#fafafa) with white rounded content container.
|
|
6
|
-
* All dimensions match threads.com's --barcelona-* design tokens.
|
|
7
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
-
/** Map known URL paths to SVG icons. Size 26x26 matching Threads' nav icons. */ function NavIcon({ url, isActive }) {
|
|
9
|
-
const stroke = "currentColor";
|
|
10
|
-
const sw = isActive ? "2.25" : "1.75";
|
|
11
|
-
const cls = "size-[26px]";
|
|
12
|
-
// Home
|
|
13
|
-
if (url === "/") {
|
|
14
|
-
return /*#__PURE__*/ _jsx("svg", {
|
|
15
|
-
class: cls,
|
|
16
|
-
fill: "none",
|
|
17
|
-
viewBox: "0 0 24 24",
|
|
18
|
-
"stroke-width": sw,
|
|
19
|
-
stroke: stroke,
|
|
20
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
21
|
-
"stroke-linecap": "round",
|
|
22
|
-
"stroke-linejoin": "round",
|
|
23
|
-
d: "m2.25 12 8.954-8.955a1.126 1.126 0 0 1 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
|
|
24
|
-
})
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
// Search
|
|
28
|
-
if (url === "/search") {
|
|
29
|
-
return /*#__PURE__*/ _jsx("svg", {
|
|
30
|
-
class: cls,
|
|
31
|
-
fill: "none",
|
|
32
|
-
viewBox: "0 0 24 24",
|
|
33
|
-
"stroke-width": sw,
|
|
34
|
-
stroke: stroke,
|
|
35
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
36
|
-
"stroke-linecap": "round",
|
|
37
|
-
"stroke-linejoin": "round",
|
|
38
|
-
d: "m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
|
|
39
|
-
})
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
// Archive
|
|
43
|
-
if (url === "/archive") {
|
|
44
|
-
return /*#__PURE__*/ _jsx("svg", {
|
|
45
|
-
class: cls,
|
|
46
|
-
fill: "none",
|
|
47
|
-
viewBox: "0 0 24 24",
|
|
48
|
-
"stroke-width": sw,
|
|
49
|
-
stroke: stroke,
|
|
50
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
51
|
-
"stroke-linecap": "round",
|
|
52
|
-
"stroke-linejoin": "round",
|
|
53
|
-
d: "M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"
|
|
54
|
-
})
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
// RSS — common for /feed, /rss, /atom
|
|
58
|
-
if (url.match(/\/(feed|rss|atom)/)) {
|
|
59
|
-
return /*#__PURE__*/ _jsx("svg", {
|
|
60
|
-
class: cls,
|
|
61
|
-
fill: "none",
|
|
62
|
-
viewBox: "0 0 24 24",
|
|
63
|
-
"stroke-width": sw,
|
|
64
|
-
stroke: stroke,
|
|
65
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
66
|
-
"stroke-linecap": "round",
|
|
67
|
-
"stroke-linejoin": "round",
|
|
68
|
-
d: "M12.75 19.5v-.75a7.5 7.5 0 0 0-7.5-7.5H4.5m0-6.75h.75c7.87 0 14.25 6.38 14.25 14.25v.75M4.5 19.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
|
|
69
|
-
})
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
// External link
|
|
73
|
-
if (url.startsWith("http")) {
|
|
74
|
-
return /*#__PURE__*/ _jsx("svg", {
|
|
75
|
-
class: cls,
|
|
76
|
-
fill: "none",
|
|
77
|
-
viewBox: "0 0 24 24",
|
|
78
|
-
"stroke-width": sw,
|
|
79
|
-
stroke: stroke,
|
|
80
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
81
|
-
"stroke-linecap": "round",
|
|
82
|
-
"stroke-linejoin": "round",
|
|
83
|
-
d: "M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
|
|
84
|
-
})
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
// Default: generic page icon
|
|
88
|
-
return /*#__PURE__*/ _jsx("svg", {
|
|
89
|
-
class: cls,
|
|
90
|
-
fill: "none",
|
|
91
|
-
viewBox: "0 0 24 24",
|
|
92
|
-
"stroke-width": sw,
|
|
93
|
-
stroke: stroke,
|
|
94
|
-
children: /*#__PURE__*/ _jsx("path", {
|
|
95
|
-
"stroke-linecap": "round",
|
|
96
|
-
"stroke-linejoin": "round",
|
|
97
|
-
d: "M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
|
|
98
|
-
})
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
function SidebarLink({ link }) {
|
|
102
|
-
return /*#__PURE__*/ _jsx("a", {
|
|
103
|
-
href: link.url,
|
|
104
|
-
class: `threads-sidebar-link ${link.isActive ? "threads-sidebar-link-active" : ""}`,
|
|
105
|
-
title: link.label,
|
|
106
|
-
...link.isExternal ? {
|
|
107
|
-
target: "_blank",
|
|
108
|
-
rel: "noopener noreferrer"
|
|
109
|
-
} : {},
|
|
110
|
-
children: /*#__PURE__*/ _jsx(NavIcon, {
|
|
111
|
-
url: link.url,
|
|
112
|
-
isActive: link.isActive
|
|
113
|
-
})
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
function MobileTabLink({ link }) {
|
|
117
|
-
return /*#__PURE__*/ _jsx("a", {
|
|
118
|
-
href: link.url,
|
|
119
|
-
class: `threads-mobile-tab ${link.isActive ? "threads-mobile-tab-active" : ""}`,
|
|
120
|
-
...link.isExternal ? {
|
|
121
|
-
target: "_blank",
|
|
122
|
-
rel: "noopener noreferrer"
|
|
123
|
-
} : {},
|
|
124
|
-
children: /*#__PURE__*/ _jsx(NavIcon, {
|
|
125
|
-
url: link.url,
|
|
126
|
-
isActive: link.isActive
|
|
127
|
-
})
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
export const ThreadsSiteLayout = ({ siteName, links, children })=>{
|
|
131
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
132
|
-
class: "threads-page",
|
|
133
|
-
children: [
|
|
134
|
-
/*#__PURE__*/ _jsxs("aside", {
|
|
135
|
-
class: "threads-sidebar",
|
|
136
|
-
children: [
|
|
137
|
-
/*#__PURE__*/ _jsx("a", {
|
|
138
|
-
href: "/",
|
|
139
|
-
class: "threads-logo",
|
|
140
|
-
title: siteName,
|
|
141
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
142
|
-
class: "text-2xl font-black leading-none",
|
|
143
|
-
children: "@"
|
|
144
|
-
})
|
|
145
|
-
}),
|
|
146
|
-
/*#__PURE__*/ _jsx("nav", {
|
|
147
|
-
class: "flex flex-1 flex-col items-center gap-1",
|
|
148
|
-
children: links.map((link)=>/*#__PURE__*/ _jsx(SidebarLink, {
|
|
149
|
-
link: link
|
|
150
|
-
}, link.id))
|
|
151
|
-
})
|
|
152
|
-
]
|
|
153
|
-
}),
|
|
154
|
-
/*#__PURE__*/ _jsx("main", {
|
|
155
|
-
class: "threads-main",
|
|
156
|
-
children: /*#__PURE__*/ _jsx("div", {
|
|
157
|
-
class: "threads-container",
|
|
158
|
-
children: /*#__PURE__*/ _jsx("div", {
|
|
159
|
-
class: "threads-content",
|
|
160
|
-
children: children
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
}),
|
|
164
|
-
/*#__PURE__*/ _jsx("nav", {
|
|
165
|
-
class: "threads-mobile-tabs",
|
|
166
|
-
children: links.map((link)=>/*#__PURE__*/ _jsx(MobileTabLink, {
|
|
167
|
-
link: link
|
|
168
|
-
}, link.id))
|
|
169
|
-
})
|
|
170
|
-
]
|
|
171
|
-
});
|
|
172
|
-
};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme
|
|
3
|
-
*
|
|
4
|
-
* A clean, centered timeline theme inspired by Threads.net.
|
|
5
|
-
* Posts separated by thin dividers, no cards, with thread connector lines.
|
|
6
|
-
*
|
|
7
|
-
* This is the default theme for Jant.
|
|
8
|
-
*/ // Layout
|
|
9
|
-
import { ThreadsSiteLayout } from "./ThreadsSiteLayout.js";
|
|
10
|
-
// Pages
|
|
11
|
-
import { HomePage } from "./pages/HomePage.js";
|
|
12
|
-
import { PostPage } from "./pages/PostPage.js";
|
|
13
|
-
import { SinglePage } from "./pages/SinglePage.js";
|
|
14
|
-
import { ArchivePage } from "./pages/ArchivePage.js";
|
|
15
|
-
import { SearchPage } from "./pages/SearchPage.js";
|
|
16
|
-
import { CollectionPage } from "./pages/CollectionPage.js";
|
|
17
|
-
// Timeline
|
|
18
|
-
import { NoteCard } from "./timeline/NoteCard.js";
|
|
19
|
-
import { LinkCard } from "./timeline/LinkCard.js";
|
|
20
|
-
import { QuoteCard } from "./timeline/QuoteCard.js";
|
|
21
|
-
import { ThreadPreview } from "./timeline/ThreadPreview.js";
|
|
22
|
-
import { TimelineFeed } from "./timeline/TimelineFeed.js";
|
|
23
|
-
import { TimelineLoadMore } from "./timeline/TimelineLoadMore.js";
|
|
24
|
-
import { timelineMore } from "./timeline/timelineMore.js";
|
|
25
|
-
/**
|
|
26
|
-
* Create the threads theme configuration.
|
|
27
|
-
*
|
|
28
|
-
* @param options - Optional overrides for components, CSS variables, or color themes
|
|
29
|
-
* @returns A JantTheme configuration object
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```typescript
|
|
33
|
-
* import { createApp } from "@jant/core";
|
|
34
|
-
* import { threadsTheme } from "@jant/core";
|
|
35
|
-
*
|
|
36
|
-
* export default createApp({
|
|
37
|
-
* theme: threadsTheme(),
|
|
38
|
-
* });
|
|
39
|
-
* ```
|
|
40
|
-
*/ export function theme(options) {
|
|
41
|
-
return {
|
|
42
|
-
name: "threads",
|
|
43
|
-
components: {
|
|
44
|
-
SiteLayout: ThreadsSiteLayout,
|
|
45
|
-
HomePage,
|
|
46
|
-
PostPage,
|
|
47
|
-
SinglePage,
|
|
48
|
-
ArchivePage,
|
|
49
|
-
SearchPage,
|
|
50
|
-
CollectionPage,
|
|
51
|
-
NoteCard,
|
|
52
|
-
LinkCard,
|
|
53
|
-
QuoteCard,
|
|
54
|
-
ThreadPreview,
|
|
55
|
-
TimelineFeed,
|
|
56
|
-
TimelineLoadMore,
|
|
57
|
-
...options?.components
|
|
58
|
-
},
|
|
59
|
-
timelineMore,
|
|
60
|
-
cssVariables: {
|
|
61
|
-
...options?.cssVariables
|
|
62
|
-
},
|
|
63
|
-
colorThemes: options?.colorThemes
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
// Re-export individual components for wrapping/extending
|
|
67
|
-
export { ThreadsSiteLayout } from "./ThreadsSiteLayout.js";
|
|
68
|
-
export { HomePage } from "./pages/HomePage.js";
|
|
69
|
-
export { PostPage } from "./pages/PostPage.js";
|
|
70
|
-
export { SinglePage } from "./pages/SinglePage.js";
|
|
71
|
-
export { ArchivePage } from "./pages/ArchivePage.js";
|
|
72
|
-
export { SearchPage } from "./pages/SearchPage.js";
|
|
73
|
-
export { CollectionPage } from "./pages/CollectionPage.js";
|
|
74
|
-
export { NoteCard } from "./timeline/NoteCard.js";
|
|
75
|
-
export { LinkCard } from "./timeline/LinkCard.js";
|
|
76
|
-
export { QuoteCard } from "./timeline/QuoteCard.js";
|
|
77
|
-
export { ThreadPreview } from "./timeline/ThreadPreview.js";
|
|
78
|
-
export { TimelineFeed } from "./timeline/TimelineFeed.js";
|
|
79
|
-
export { TimelineLoadMore } from "./timeline/TimelineLoadMore.js";
|
|
80
|
-
export { TimelineItem, TimelineItemFromPost } from "./timeline/TimelineItem.js";
|
|
81
|
-
export { timelineMore } from "./timeline/timelineMore.js";
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme - Home Page
|
|
3
|
-
*
|
|
4
|
-
* Clean feed of posts separated by dividers.
|
|
5
|
-
*/ import { jsx as _jsx, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { TimelineFeed as DefaultTimelineFeed } from "../timeline/TimelineFeed.js";
|
|
8
|
-
export const HomePage = ({ items, hasMore, nextCursor, theme })=>{
|
|
9
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
10
|
-
const Feed = theme?.TimelineFeed ?? DefaultTimelineFeed;
|
|
11
|
-
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
12
|
-
children: items.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
13
|
-
class: "py-12 text-center text-muted-foreground",
|
|
14
|
-
children: $__i18n._({
|
|
15
|
-
id: "ODiSoW",
|
|
16
|
-
message: "No posts yet."
|
|
17
|
-
})
|
|
18
|
-
}) : /*#__PURE__*/ _jsx(Feed, {
|
|
19
|
-
items: items,
|
|
20
|
-
hasMore: hasMore,
|
|
21
|
-
nextCursor: nextCursor,
|
|
22
|
-
theme: theme
|
|
23
|
-
})
|
|
24
|
-
});
|
|
25
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme - Timeline Feed
|
|
3
|
-
*
|
|
4
|
-
* Date-grouped posts separated by thin dividers.
|
|
5
|
-
* A centered date header appears above each group.
|
|
6
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
7
|
-
import { TimelineItem } from "./TimelineItem.js";
|
|
8
|
-
import { ThreadPreview as DefaultThreadPreview } from "./ThreadPreview.js";
|
|
9
|
-
import { TimelineLoadMore as DefaultTimelineLoadMore } from "./TimelineLoadMore.js";
|
|
10
|
-
import { groupByDate } from "./groupByDate.js";
|
|
11
|
-
export const TimelineFeed = ({ items, hasMore, nextCursor, theme })=>{
|
|
12
|
-
const ResolvedThreadPreview = theme?.ThreadPreview ?? DefaultThreadPreview;
|
|
13
|
-
const ResolvedLoadMore = theme?.TimelineLoadMore ?? DefaultTimelineLoadMore;
|
|
14
|
-
const groups = groupByDate(items);
|
|
15
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
16
|
-
children: [
|
|
17
|
-
/*#__PURE__*/ _jsx("div", {
|
|
18
|
-
id: "timeline-feed",
|
|
19
|
-
children: groups.map((group)=>/*#__PURE__*/ _jsxs("div", {
|
|
20
|
-
class: "threads-card",
|
|
21
|
-
children: [
|
|
22
|
-
/*#__PURE__*/ _jsx("div", {
|
|
23
|
-
class: "threads-date-header",
|
|
24
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
25
|
-
children: group.label
|
|
26
|
-
})
|
|
27
|
-
}),
|
|
28
|
-
/*#__PURE__*/ _jsx("div", {
|
|
29
|
-
id: `date-items-${group.dateKey}`,
|
|
30
|
-
class: "flex flex-col",
|
|
31
|
-
children: group.items.map((item, i)=>/*#__PURE__*/ _jsxs("div", {
|
|
32
|
-
children: [
|
|
33
|
-
i > 0 && /*#__PURE__*/ _jsx("hr", {
|
|
34
|
-
class: "border-border my-5"
|
|
35
|
-
}),
|
|
36
|
-
item.threadPreview ? /*#__PURE__*/ _jsx(ResolvedThreadPreview, {
|
|
37
|
-
rootPost: item.post,
|
|
38
|
-
previewReplies: item.threadPreview.replies,
|
|
39
|
-
totalReplyCount: item.threadPreview.totalReplyCount,
|
|
40
|
-
theme: theme
|
|
41
|
-
}) : /*#__PURE__*/ _jsx(TimelineItem, {
|
|
42
|
-
item: item,
|
|
43
|
-
theme: theme
|
|
44
|
-
})
|
|
45
|
-
]
|
|
46
|
-
}, item.post.id))
|
|
47
|
-
})
|
|
48
|
-
]
|
|
49
|
-
}, group.dateKey))
|
|
50
|
-
}),
|
|
51
|
-
hasMore && nextCursor && /*#__PURE__*/ _jsx(ResolvedLoadMore, {
|
|
52
|
-
nextCursor: nextCursor,
|
|
53
|
-
lastDate: groups.at(-1)?.dateKey,
|
|
54
|
-
theme: theme
|
|
55
|
-
})
|
|
56
|
-
]
|
|
57
|
-
});
|
|
58
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme - Timeline Item
|
|
3
|
-
*
|
|
4
|
-
* Dispatches to the correct card component based on post format.
|
|
5
|
-
*/ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
6
|
-
import { NoteCard } from "./NoteCard.js";
|
|
7
|
-
import { LinkCard } from "./LinkCard.js";
|
|
8
|
-
import { QuoteCard } from "./QuoteCard.js";
|
|
9
|
-
const CARD_MAP = {
|
|
10
|
-
note: NoteCard,
|
|
11
|
-
link: LinkCard,
|
|
12
|
-
quote: QuoteCard
|
|
13
|
-
};
|
|
14
|
-
const THEME_KEY_MAP = {
|
|
15
|
-
note: "NoteCard",
|
|
16
|
-
link: "LinkCard",
|
|
17
|
-
quote: "QuoteCard"
|
|
18
|
-
};
|
|
19
|
-
export const TimelineItem = ({ item, compact, cardOverride, theme })=>{
|
|
20
|
-
const themeKey = THEME_KEY_MAP[item.post.format];
|
|
21
|
-
const themeCard = theme?.[themeKey];
|
|
22
|
-
const Card = cardOverride ?? themeCard ?? CARD_MAP[item.post.format];
|
|
23
|
-
return /*#__PURE__*/ _jsx(Card, {
|
|
24
|
-
post: item.post,
|
|
25
|
-
compact: compact
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
export const TimelineItemFromPost = ({ post, compact, cardOverride, theme })=>{
|
|
29
|
-
const themeKey = THEME_KEY_MAP[post.format];
|
|
30
|
-
const themeCard = theme?.[themeKey];
|
|
31
|
-
const Card = cardOverride ?? themeCard ?? CARD_MAP[post.format];
|
|
32
|
-
return /*#__PURE__*/ _jsx(Card, {
|
|
33
|
-
post: post,
|
|
34
|
-
compact: compact
|
|
35
|
-
});
|
|
36
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme - Timeline Load More
|
|
3
|
-
*
|
|
4
|
-
* Auto-loads more posts when scrolled into view.
|
|
5
|
-
* Passes `lastDate` to the server so it can merge date groups across pages.
|
|
6
|
-
*/ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
7
|
-
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
8
|
-
export const TimelineLoadMore = ({ nextCursor, lastDate })=>{
|
|
9
|
-
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
10
|
-
const url = lastDate ? `/?cursor=${nextCursor}&lastDate=${lastDate}` : `/?cursor=${nextCursor}`;
|
|
11
|
-
return /*#__PURE__*/ _jsx("div", {
|
|
12
|
-
id: "load-more-container",
|
|
13
|
-
class: "py-6 text-center",
|
|
14
|
-
"data-on-intersect__once": `@get('${url}')`,
|
|
15
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
16
|
-
class: "text-sm text-muted-foreground",
|
|
17
|
-
children: $__i18n._({
|
|
18
|
-
id: "Z3FXyt",
|
|
19
|
-
message: "Loading..."
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
});
|
|
23
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Groups timeline items by their publication date (YYYY-MM-DD).
|
|
3
|
-
*
|
|
4
|
-
* Shared between TimelineFeed (initial render) and timelineMore (SSE patches)
|
|
5
|
-
* so that both produce identical date group structure.
|
|
6
|
-
*/ export function groupByDate(items) {
|
|
7
|
-
const groups = [];
|
|
8
|
-
let current = null;
|
|
9
|
-
for (const item of items){
|
|
10
|
-
const dateKey = item.post.publishedAt.slice(0, 10);
|
|
11
|
-
if (!current || current.dateKey !== dateKey) {
|
|
12
|
-
current = {
|
|
13
|
-
dateKey,
|
|
14
|
-
label: item.post.publishedAtFormatted,
|
|
15
|
-
items: []
|
|
16
|
-
};
|
|
17
|
-
groups.push(current);
|
|
18
|
-
}
|
|
19
|
-
current.items.push(item);
|
|
20
|
-
}
|
|
21
|
-
return groups;
|
|
22
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threads Theme - Timeline Load-More SSE Renderer
|
|
3
|
-
*
|
|
4
|
-
* Produces SSE DOM patches for incremental timeline loading.
|
|
5
|
-
* Uses date-grouped layout with threads-specific HTML (threads-card, threads-date-header)
|
|
6
|
-
* matching TimelineFeed's initial render exactly.
|
|
7
|
-
*/ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
-
import { groupByDate } from "./groupByDate.js";
|
|
9
|
-
import { TimelineItem } from "./TimelineItem.js";
|
|
10
|
-
import { ThreadPreview as DefaultThreadPreview } from "./ThreadPreview.js";
|
|
11
|
-
import { TimelineLoadMore as DefaultTimelineLoadMore } from "./TimelineLoadMore.js";
|
|
12
|
-
function renderItem(item, props) {
|
|
13
|
-
const theme = props.theme;
|
|
14
|
-
const ResolvedThreadPreview = theme?.ThreadPreview ?? DefaultThreadPreview;
|
|
15
|
-
if (item.threadPreview) {
|
|
16
|
-
return /*#__PURE__*/ _jsx(ResolvedThreadPreview, {
|
|
17
|
-
rootPost: item.post,
|
|
18
|
-
previewReplies: item.threadPreview.replies,
|
|
19
|
-
totalReplyCount: item.threadPreview.totalReplyCount,
|
|
20
|
-
theme: theme
|
|
21
|
-
}).toString();
|
|
22
|
-
}
|
|
23
|
-
return /*#__PURE__*/ _jsx(TimelineItem, {
|
|
24
|
-
item: item,
|
|
25
|
-
theme: theme
|
|
26
|
-
}).toString();
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Renders SSE patches for the threads theme's load-more response.
|
|
30
|
-
*
|
|
31
|
-
* @param props - Timeline more props with items, pagination, and theme
|
|
32
|
-
* @returns Array of DOM patch instructions for the SSE stream
|
|
33
|
-
*/ export function timelineMore(props) {
|
|
34
|
-
const { items, lastDate, hasMore, nextCursor, theme } = props;
|
|
35
|
-
const patches = [];
|
|
36
|
-
const groups = groupByDate(items);
|
|
37
|
-
if (groups.length === 0) return patches;
|
|
38
|
-
const firstGroup = groups[0];
|
|
39
|
-
const isContinuation = lastDate === firstGroup.dateKey;
|
|
40
|
-
// Continuation items: append into the existing date group's container
|
|
41
|
-
if (isContinuation) {
|
|
42
|
-
const continuationHtml = firstGroup.items.map((item)=>{
|
|
43
|
-
const content = renderItem(item, props);
|
|
44
|
-
return `<div><hr class="border-border my-5"/>${content}</div>`;
|
|
45
|
-
}).join("");
|
|
46
|
-
if (continuationHtml) {
|
|
47
|
-
patches.push({
|
|
48
|
-
selector: `#date-items-${lastDate}`,
|
|
49
|
-
content: continuationHtml,
|
|
50
|
-
mode: "append"
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// New date groups: append to #timeline-feed
|
|
55
|
-
const newGroups = isContinuation ? groups.slice(1) : groups;
|
|
56
|
-
if (newGroups.length > 0) {
|
|
57
|
-
const newGroupsHtml = newGroups.map((group)=>{
|
|
58
|
-
const itemsHtml = group.items.map((item, i)=>{
|
|
59
|
-
const content = renderItem(item, props);
|
|
60
|
-
return i > 0 ? `<div><hr class="border-border my-5"/>${content}</div>` : `<div>${content}</div>`;
|
|
61
|
-
}).join("");
|
|
62
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
63
|
-
class: "threads-card",
|
|
64
|
-
children: [
|
|
65
|
-
/*#__PURE__*/ _jsx("div", {
|
|
66
|
-
class: "threads-date-header",
|
|
67
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
68
|
-
children: group.label
|
|
69
|
-
})
|
|
70
|
-
}),
|
|
71
|
-
/*#__PURE__*/ _jsx("div", {
|
|
72
|
-
id: `date-items-${group.dateKey}`,
|
|
73
|
-
class: "flex flex-col",
|
|
74
|
-
dangerouslySetInnerHTML: {
|
|
75
|
-
__html: itemsHtml
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
]
|
|
79
|
-
}).toString();
|
|
80
|
-
}).join("");
|
|
81
|
-
patches.push({
|
|
82
|
-
selector: "#timeline-feed",
|
|
83
|
-
content: newGroupsHtml,
|
|
84
|
-
mode: "append"
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
// Load-more button
|
|
88
|
-
const ResolvedLoadMore = theme?.TimelineLoadMore ?? DefaultTimelineLoadMore;
|
|
89
|
-
const lastGroupDate = groups.at(-1)?.dateKey;
|
|
90
|
-
if (hasMore && nextCursor) {
|
|
91
|
-
patches.push({
|
|
92
|
-
selector: "#load-more-container",
|
|
93
|
-
content: /*#__PURE__*/ _jsx(ResolvedLoadMore, {
|
|
94
|
-
nextCursor: nextCursor,
|
|
95
|
-
lastDate: lastGroupDate,
|
|
96
|
-
theme: theme
|
|
97
|
-
}).toString()
|
|
98
|
-
});
|
|
99
|
-
} else {
|
|
100
|
-
patches.push({
|
|
101
|
-
selector: "#load-more-container",
|
|
102
|
-
content: "",
|
|
103
|
-
mode: "remove"
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
return patches;
|
|
107
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { resolveCardComponent, resolveComponent } from "../theme-components.js";
|
|
3
|
-
import type {
|
|
4
|
-
ThemeComponents,
|
|
5
|
-
TimelineCardProps,
|
|
6
|
-
ThreadPreviewProps,
|
|
7
|
-
TimelineFeedProps,
|
|
8
|
-
Format,
|
|
9
|
-
HomePageProps,
|
|
10
|
-
} from "../../types.js";
|
|
11
|
-
import type { FC } from "hono/jsx";
|
|
12
|
-
|
|
13
|
-
// Create simple mock components for testing (avoids importing .tsx files with i18n)
|
|
14
|
-
const MockNoteCard: FC<TimelineCardProps> = () => null;
|
|
15
|
-
const MockLinkCard: FC<TimelineCardProps> = () => null;
|
|
16
|
-
const MockQuoteCard: FC<TimelineCardProps> = () => null;
|
|
17
|
-
const MockThreadPreview: FC<ThreadPreviewProps> = () => null;
|
|
18
|
-
const MockTimelineFeed: FC<TimelineFeedProps> = () => null;
|
|
19
|
-
const MockHomePage: FC<HomePageProps> = () => null;
|
|
20
|
-
|
|
21
|
-
const DEFAULT_CARD_MAP: Record<Format, FC<TimelineCardProps>> = {
|
|
22
|
-
note: MockNoteCard,
|
|
23
|
-
link: MockLinkCard,
|
|
24
|
-
quote: MockQuoteCard,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
describe("theme-components", () => {
|
|
28
|
-
describe("resolveCardComponent", () => {
|
|
29
|
-
it("returns default NoteCard for note type", () => {
|
|
30
|
-
expect(resolveCardComponent("note", DEFAULT_CARD_MAP)).toBe(MockNoteCard);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("returns default LinkCard for link type", () => {
|
|
34
|
-
expect(resolveCardComponent("link", DEFAULT_CARD_MAP)).toBe(MockLinkCard);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("returns default QuoteCard for quote type", () => {
|
|
38
|
-
expect(resolveCardComponent("quote", DEFAULT_CARD_MAP)).toBe(
|
|
39
|
-
MockQuoteCard,
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("returns theme override when provided", () => {
|
|
44
|
-
const CustomNote: FC<TimelineCardProps> = () => null;
|
|
45
|
-
const overrides: ThemeComponents = { NoteCard: CustomNote };
|
|
46
|
-
expect(resolveCardComponent("note", DEFAULT_CARD_MAP, overrides)).toBe(
|
|
47
|
-
CustomNote,
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("returns default when theme has no override for type", () => {
|
|
52
|
-
const overrides: ThemeComponents = {};
|
|
53
|
-
expect(resolveCardComponent("link", DEFAULT_CARD_MAP, overrides)).toBe(
|
|
54
|
-
MockLinkCard,
|
|
55
|
-
);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe("resolveComponent", () => {
|
|
60
|
-
it("returns default ThreadPreview when no override", () => {
|
|
61
|
-
expect(resolveComponent("ThreadPreview", MockThreadPreview)).toBe(
|
|
62
|
-
MockThreadPreview,
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("returns theme override for ThreadPreview when provided", () => {
|
|
67
|
-
const Custom: FC<ThreadPreviewProps> = () => null;
|
|
68
|
-
expect(
|
|
69
|
-
resolveComponent("ThreadPreview", MockThreadPreview, {
|
|
70
|
-
ThreadPreview: Custom,
|
|
71
|
-
}),
|
|
72
|
-
).toBe(Custom);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("returns default TimelineFeed when no override", () => {
|
|
76
|
-
expect(resolveComponent("TimelineFeed", MockTimelineFeed)).toBe(
|
|
77
|
-
MockTimelineFeed,
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("returns theme override for TimelineFeed when provided", () => {
|
|
82
|
-
const Custom: FC<TimelineFeedProps> = () => null;
|
|
83
|
-
expect(
|
|
84
|
-
resolveComponent("TimelineFeed", MockTimelineFeed, {
|
|
85
|
-
TimelineFeed: Custom,
|
|
86
|
-
}),
|
|
87
|
-
).toBe(Custom);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("returns default HomePage when no override", () => {
|
|
91
|
-
expect(resolveComponent("HomePage", MockHomePage)).toBe(MockHomePage);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("returns theme override for HomePage when provided", () => {
|
|
95
|
-
const Custom: FC<HomePageProps> = () => null;
|
|
96
|
-
expect(
|
|
97
|
-
resolveComponent("HomePage", MockHomePage, { HomePage: Custom }),
|
|
98
|
-
).toBe(Custom);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("returns default when theme has empty overrides", () => {
|
|
102
|
-
expect(resolveComponent("HomePage", MockHomePage, {})).toBe(MockHomePage);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|