nnbb 0.0.1
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/.dockerignore +1 -0
- package/.env.local +2 -0
- package/.eslintrc.json +15 -0
- package/.github/stale.yml +16 -0
- package/.github/workflows/codeql-analysis.yml +73 -0
- package/.github/workflows/docker-ghcr.yaml +59 -0
- package/.prettierrc +9 -0
- package/.vscode/launch.json +28 -0
- package/.vscode/settings.json +6 -0
- package/Dockerfile +38 -0
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/blog.config.js +454 -0
- package/components/Ackee.js +83 -0
- package/components/AdBlockDetect.js +40 -0
- package/components/AlgoliaSearchModal.js +250 -0
- package/components/Artalk.js +30 -0
- package/components/Busuanzi.js +26 -0
- package/components/ChatBase.js +19 -0
- package/components/Collapse.tsx +134 -0
- package/components/Comment.js +161 -0
- package/components/CommonHead.tsx +101 -0
- package/components/CommonScript.js +125 -0
- package/components/CusdisComponent.js +35 -0
- package/components/CustomContextMenu.js +221 -0
- package/components/DebugPanel.js +134 -0
- package/components/DisableCopy.js +21 -0
- package/components/Draggable.js +167 -0
- package/components/Equation.js +31 -0
- package/components/ExternalPlugins.js +75 -0
- package/components/ExternalScript.js +29 -0
- package/components/FacebookMessenger.js +255 -0
- package/components/FacebookPage.js +34 -0
- package/components/Fireworks.js +210 -0
- package/components/FlipCard.js +56 -0
- package/components/FlutteringRibbon.js +322 -0
- package/components/FullScreenButton.js +48 -0
- package/components/Giscus.js +33 -0
- package/components/Gitalk.js +42 -0
- package/components/GoogleAdsense.js +111 -0
- package/components/Gtag.js +18 -0
- package/components/HeroIcons.tsx +321 -0
- package/components/KatexReact.js +53 -0
- package/components/LazyImage.js +95 -0
- package/components/Live2D.js +52 -0
- package/components/Loading.js +20 -0
- package/components/Mark.js +28 -0
- package/components/NProgress.ts +8 -0
- package/components/Nest.js +124 -0
- package/components/NotionIcon.js +20 -0
- package/components/NotionPage.tsx +206 -0
- package/components/Player.js +54 -0
- package/components/PrismMac.js +236 -0
- package/components/QrCode.js +34 -0
- package/components/Ribbon.js +98 -0
- package/components/Sakura.js +192 -0
- package/components/Select.js +40 -0
- package/components/ShareBar.js +29 -0
- package/components/ShareButtons.js +403 -0
- package/components/SideBarDrawer.js +50 -0
- package/components/StarrySky.js +130 -0
- package/components/Tabs.js +64 -0
- package/components/ThemeSwitch.js +67 -0
- package/components/Twikoo.js +27 -0
- package/components/TwikooCommentCount.js +22 -0
- package/components/TwikooCommentCounter.js +78 -0
- package/components/TwikooRecentComments.js +11 -0
- package/components/Utterances.js +35 -0
- package/components/VConsole.js +76 -0
- package/components/ValineComponent.js +59 -0
- package/components/ValineCount.js +6 -0
- package/components/ValinePanel.js +3 -0
- package/components/Vercel.tsx +54 -0
- package/components/WWAds.js +18 -0
- package/components/WalineComponent.js +83 -0
- package/components/WebMention.js +173 -0
- package/components/Webwhiz.js +17 -0
- package/components/WordCount.js +73 -0
- package/hooks/useToggleClickOutSide.ts +32 -0
- package/hooks/useWindowSize.ts +30 -0
- package/lib/algolia.js +108 -0
- package/lib/busuanzi.js +99 -0
- package/lib/cache/cacheManager.ts +49 -0
- package/lib/cache/localFileCache.ts +56 -0
- package/lib/cache/memoryMache.ts +20 -0
- package/lib/cache/mongoDbCache.ts +70 -0
- package/lib/cache/types.ts +5 -0
- package/lib/font.js +46 -0
- package/lib/global.tsx +129 -0
- package/lib/gtag.js +17 -0
- package/lib/mailchimp.js +49 -0
- package/lib/memorize.js +0 -0
- package/lib/mhchem.js +1696 -0
- package/lib/notion/getAllCategories.ts +51 -0
- package/lib/notion/getAllPageIds.ts +51 -0
- package/lib/notion/getAllPosts.js +68 -0
- package/lib/notion/getAllTags.ts +43 -0
- package/lib/notion/getNotionData.ts +340 -0
- package/lib/notion/getPageInfoOfPostPage.ts +58 -0
- package/lib/notion/getPageProperties.ts +203 -0
- package/lib/notion/getPageTableOfContents.ts +107 -0
- package/lib/notion/getPostBlocks.ts +147 -0
- package/lib/notion/mapImage.ts +130 -0
- package/lib/notion/types.ts +125 -0
- package/lib/notion.js +2 -0
- package/lib/robots.txt.js +25 -0
- package/lib/rss.js +63 -0
- package/lib/sitemap.xml.js +67 -0
- package/lib/utils.js +212 -0
- package/next-env.d.ts +5 -0
- package/next-i18next.config.js +7 -0
- package/next-sitemap.config.js +11 -0
- package/next.config.js +124 -0
- package/package.json +92 -0
- package/pages/404.tsx +40 -0
- package/pages/[prefix]/[slug].tsx +123 -0
- package/pages/[prefix]/index.tsx +223 -0
- package/pages/_app.js +59 -0
- package/pages/_document.js +42 -0
- package/pages/api/subscribe.js +22 -0
- package/pages/archive/index.tsx +79 -0
- package/pages/category/[category]/index.tsx +87 -0
- package/pages/category/[category]/page/[page].tsx +103 -0
- package/pages/category/index.tsx +43 -0
- package/pages/index.tsx +88 -0
- package/pages/page/[page].tsx +93 -0
- package/pages/search/[keyword]/index.tsx +162 -0
- package/pages/search/[keyword]/page/[page].tsx +166 -0
- package/pages/search/index.tsx +69 -0
- package/pages/sitemap.xml.js +70 -0
- package/pages/tag/[tag]/index.tsx +73 -0
- package/pages/tag/[tag]/page/[page].tsx +87 -0
- package/pages/tag/index.tsx +42 -0
- package/postcss.config.js +6 -0
- package/public/ads.txt +1 -0
- package/public/avatar.png +0 -0
- package/public/avatar.svg +11 -0
- package/public/bg_image.jpg +0 -0
- package/public/css/all.min.css +9 -0
- package/public/css/custom.css +8 -0
- package/public/css/img-shadow.css +5 -0
- package/public/css/prism-mac-style.css +58 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +9 -0
- package/public/js/cusdis.es.js +107 -0
- package/public/js/custom.js +1 -0
- package/public/locales/en/common.json +44 -0
- package/public/locales/en/menu.json +9 -0
- package/public/locales/en/nav.json +11 -0
- package/public/locales/zh-CN/common.json +44 -0
- package/public/locales/zh-CN/menu.json +9 -0
- package/public/locales/zh-CN/nav.json +9 -0
- package/public/webfonts/fa-brands-400.ttf +0 -0
- package/public/webfonts/fa-brands-400.woff2 +0 -0
- package/public/webfonts/fa-regular-400.ttf +0 -0
- package/public/webfonts/fa-regular-400.woff2 +0 -0
- package/public/webfonts/fa-solid-900.ttf +0 -0
- package/public/webfonts/fa-solid-900.woff2 +0 -0
- package/public/webfonts/fa-v4compatibility.ttf +0 -0
- package/public/webfonts/fa-v4compatibility.woff2 +0 -0
- package/styles/animate.css +503 -0
- package/styles/globals.css +183 -0
- package/styles/notion.css +2064 -0
- package/styles/nprogress.css +84 -0
- package/styles/prism-theme.css +119 -0
- package/styles/utility-patterns.css +79 -0
- package/tailwind.config.js +37 -0
- package/theme/index.ts +6 -0
- package/theme/types/@theme-components.d.ts +29 -0
- package/theme/useLayout.ts +41 -0
- package/theme/utils.ts +108 -0
- package/themes/innocent/package.json +7 -0
- package/themes/innocent/theme.config.js +1 -0
- package/themes/nobelium/components/Announcement.tsx +27 -0
- package/themes/nobelium/components/ArticleFooter.tsx +39 -0
- package/themes/nobelium/components/ArticleInfo.tsx +58 -0
- package/themes/nobelium/components/ArticleLock.tsx +86 -0
- package/themes/nobelium/components/BlogArchiveItem.js +41 -0
- package/themes/nobelium/components/BlogListBar.js +39 -0
- package/themes/nobelium/components/BlogListPage.tsx +67 -0
- package/themes/nobelium/components/BlogListScroll.tsx +96 -0
- package/themes/nobelium/components/BlogPost.tsx +33 -0
- package/themes/nobelium/components/DarkModeButton.tsx +50 -0
- package/themes/nobelium/components/ExampleRecentComments.js +35 -0
- package/themes/nobelium/components/Footer.tsx +35 -0
- package/themes/nobelium/components/JumpToTopButton.tsx +39 -0
- package/themes/nobelium/components/LanguageSwitchButton.tsx +58 -0
- package/themes/nobelium/components/MenuItemCollapse.tsx +92 -0
- package/themes/nobelium/components/MenuItemDrop.tsx +83 -0
- package/themes/nobelium/components/Nav/Nav.module.css +50 -0
- package/themes/nobelium/components/Nav/Nav.tsx +187 -0
- package/themes/nobelium/components/RandomPostButton.tsx +31 -0
- package/themes/nobelium/components/SearchButton.tsx +31 -0
- package/themes/nobelium/components/SearchInput.tsx +94 -0
- package/themes/nobelium/components/SearchNavBar.js +19 -0
- package/themes/nobelium/components/SideBar.js +83 -0
- package/themes/nobelium/components/SvgIcon.js +29 -0
- package/themes/nobelium/components/TagItem.js +13 -0
- package/themes/nobelium/components/Tags.tsx +44 -0
- package/themes/nobelium/components/Title.js +19 -0
- package/themes/nobelium/index.tsx +28 -0
- package/themes/nobelium/layout/LayoutBase.tsx +79 -0
- package/themes/nobelium/pages/Archive.tsx +30 -0
- package/themes/nobelium/pages/Category.tsx +43 -0
- package/themes/nobelium/pages/Home.tsx +22 -0
- package/themes/nobelium/pages/PageNotFound.tsx +15 -0
- package/themes/nobelium/pages/Post.tsx +34 -0
- package/themes/nobelium/pages/PostList.tsx +74 -0
- package/themes/nobelium/pages/Search.tsx +65 -0
- package/themes/nobelium/pages/Tag.tsx +42 -0
- package/themes/nobelium/providers/index.tsx +60 -0
- package/themes/nobelium/stores/index.tsx +42 -0
- package/themes/nobelium/theme.config.ts +17 -0
- package/themes/nobelium/types/index.ts +10 -0
- package/tsconfig.json +29 -0
- package/types/index.ts +1 -0
- package/types/page.ts +102 -0
- package/vercel.json +5 -0
@@ -0,0 +1,250 @@
|
|
1
|
+
import { useState, useImperativeHandle, useRef } from 'react';
|
2
|
+
import BLOG from '@/blog.config';
|
3
|
+
import algoliasearch from 'algoliasearch';
|
4
|
+
import replaceSearchResult from '@/components/Mark';
|
5
|
+
import Link from 'next/link';
|
6
|
+
import { useGlobal } from '@/lib/global';
|
7
|
+
import throttle from 'lodash/throttle';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* 结合 Algolia 实现的弹出式搜索框
|
11
|
+
* 打开方式 cRef.current.openSearch()
|
12
|
+
* https://www.algolia.com/doc/api-reference/search-api-parameters/
|
13
|
+
*/
|
14
|
+
export default function AlgoliaSearchModal({ cRef }) {
|
15
|
+
const [searchResults, setSearchResults] = useState([]);
|
16
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
17
|
+
const [page, setPage] = useState(0);
|
18
|
+
const [keyword, setKeyword] = useState(null);
|
19
|
+
const [totalPage, setTotalPage] = useState(0);
|
20
|
+
const [totalHit, setTotalHit] = useState(0);
|
21
|
+
const [useTime, setUseTime] = useState(0);
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 对外暴露方法
|
25
|
+
*/
|
26
|
+
useImperativeHandle(cRef, () => {
|
27
|
+
return {
|
28
|
+
openSearch: () => {
|
29
|
+
setIsModalOpen(true);
|
30
|
+
},
|
31
|
+
};
|
32
|
+
});
|
33
|
+
|
34
|
+
const client = algoliasearch(
|
35
|
+
BLOG.ALGOLIA_APP_ID || '',
|
36
|
+
BLOG.ALGOLIA_SEARCH_ONLY_APP_KEY || '',
|
37
|
+
);
|
38
|
+
const index = client.initIndex(BLOG.ALGOLIA_INDEX);
|
39
|
+
|
40
|
+
/**
|
41
|
+
* 搜索
|
42
|
+
* @param {*} query
|
43
|
+
*/
|
44
|
+
const handleSearch = async (query, page) => {
|
45
|
+
setKeyword(query);
|
46
|
+
setPage(page);
|
47
|
+
setSearchResults([]);
|
48
|
+
setUseTime(0);
|
49
|
+
setTotalPage(0);
|
50
|
+
setTotalHit(0);
|
51
|
+
if (!query || query === '') {
|
52
|
+
return;
|
53
|
+
}
|
54
|
+
|
55
|
+
try {
|
56
|
+
const res = await index.search(query, { page, hitsPerPage: 10 });
|
57
|
+
const { hits, nbHits, nbPages, processingTimeMS } = res;
|
58
|
+
setUseTime(processingTimeMS);
|
59
|
+
setTotalPage(nbPages);
|
60
|
+
setTotalHit(nbHits);
|
61
|
+
setSearchResults(hits);
|
62
|
+
|
63
|
+
const doms = document
|
64
|
+
.getElementById('search-wrapper')
|
65
|
+
.getElementsByClassName('replace');
|
66
|
+
|
67
|
+
setTimeout(() => {
|
68
|
+
replaceSearchResult({
|
69
|
+
doms,
|
70
|
+
search: query,
|
71
|
+
target: {
|
72
|
+
element: 'span',
|
73
|
+
className: 'text-blue-600 border-b border-dashed',
|
74
|
+
},
|
75
|
+
});
|
76
|
+
}, 150);
|
77
|
+
} catch (error) {
|
78
|
+
console.error('Algolia search error:', error);
|
79
|
+
}
|
80
|
+
};
|
81
|
+
|
82
|
+
const throttledHandleSearch = useRef(throttle(handleSearch, 300)); // 设置节流延迟时间
|
83
|
+
|
84
|
+
// 修改input的onChange事件处理函数
|
85
|
+
const handleInputChange = (e) => {
|
86
|
+
const query = e.target.value;
|
87
|
+
throttledHandleSearch.current(query, 0);
|
88
|
+
};
|
89
|
+
|
90
|
+
/**
|
91
|
+
* 切换页码
|
92
|
+
* @param {*} page
|
93
|
+
*/
|
94
|
+
const switchPage = (page) => {
|
95
|
+
throttledHandleSearch.current(keyword, page);
|
96
|
+
};
|
97
|
+
|
98
|
+
/**
|
99
|
+
* 关闭弹窗
|
100
|
+
*/
|
101
|
+
const closeModal = () => {
|
102
|
+
setIsModalOpen(false);
|
103
|
+
};
|
104
|
+
|
105
|
+
if (!BLOG.ALGOLIA_APP_ID) {
|
106
|
+
return <></>;
|
107
|
+
}
|
108
|
+
|
109
|
+
return (
|
110
|
+
<div
|
111
|
+
id="search-wrapper"
|
112
|
+
className={`${isModalOpen ? 'opacity-100' : 'pointer-events-none invisible opacity-0'} fixed left-0 top-0 z-30 mt-12 flex h-screen w-screen items-start justify-center`}
|
113
|
+
>
|
114
|
+
{/* 模态框 */}
|
115
|
+
<div
|
116
|
+
className={`${isModalOpen ? 'opacity-100' : 'invisible translate-y-10 opacity-0'} dark:bg- z-50 flex min-h-[10rem] w-full max-w-xl flex-col justify-between rounded-lg border bg-white p-5 shadow transition-all duration-300 hover:border-blue-600 dark:border-gray-800 dark:bg-hexo-black-gray `}
|
117
|
+
>
|
118
|
+
<div className="flex items-center justify-between">
|
119
|
+
<div className="text-2xl font-bold text-blue-600">搜索</div>
|
120
|
+
<div>
|
121
|
+
<i
|
122
|
+
className="fa-solid fa-xmark cursor-pointer p-1 text-gray-600 hover:text-blue-600"
|
123
|
+
onClick={closeModal}
|
124
|
+
></i>
|
125
|
+
</div>
|
126
|
+
</div>
|
127
|
+
|
128
|
+
<input
|
129
|
+
type="text"
|
130
|
+
placeholder="在这里输入搜索关键词..."
|
131
|
+
onChange={(e) => handleInputChange(e)}
|
132
|
+
className="my-2 mb-4 w-full rounded-md border bg-gray-50 px-4 py-1 text-black outline-blue-500 dark:bg-gray-600 dark:text-gray-200"
|
133
|
+
/>
|
134
|
+
|
135
|
+
{/* 标签组 */}
|
136
|
+
<div className="mb-4">
|
137
|
+
<TagGroups />
|
138
|
+
</div>
|
139
|
+
|
140
|
+
<ul>
|
141
|
+
{searchResults.map((result) => (
|
142
|
+
<li key={result.objectID} className="replace my-2">
|
143
|
+
<a
|
144
|
+
href={`${BLOG.SUB_PATH}/${result.slug}`}
|
145
|
+
className="font-bold text-black hover:text-blue-600 dark:text-gray-200"
|
146
|
+
>
|
147
|
+
{result.title}
|
148
|
+
</a>
|
149
|
+
</li>
|
150
|
+
))}
|
151
|
+
</ul>
|
152
|
+
|
153
|
+
<Pagination totalPage={totalPage} page={page} switchPage={switchPage} />
|
154
|
+
<div>
|
155
|
+
{totalHit > 0 && (
|
156
|
+
<div>
|
157
|
+
共搜索到 {totalHit} 条结果,用时 {useTime} 毫秒
|
158
|
+
</div>
|
159
|
+
)}
|
160
|
+
</div>
|
161
|
+
<div className="mt-2 text-gray-600">
|
162
|
+
<span>
|
163
|
+
<i className="fa-brands fa-algolia"></i> Algolia 提供搜索服务
|
164
|
+
</span>{' '}
|
165
|
+
</div>
|
166
|
+
</div>
|
167
|
+
|
168
|
+
{/* 遮罩 */}
|
169
|
+
<div
|
170
|
+
onClick={closeModal}
|
171
|
+
className="glassmorphism fixed left-0 top-0 z-30 flex h-full w-full items-center justify-center"
|
172
|
+
/>
|
173
|
+
</div>
|
174
|
+
);
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* 标签组
|
179
|
+
*/
|
180
|
+
function TagGroups() {
|
181
|
+
const { tagOptions } = useGlobal();
|
182
|
+
// 获取tagOptions数组前十个
|
183
|
+
const firstTenTags = tagOptions?.slice(0, 10);
|
184
|
+
|
185
|
+
return (
|
186
|
+
<div id="tags-group" className="space-y-2 dark:border-gray-700">
|
187
|
+
{firstTenTags?.map((tag, index) => {
|
188
|
+
return (
|
189
|
+
<Link
|
190
|
+
passHref
|
191
|
+
key={index}
|
192
|
+
href={`/tag/${encodeURIComponent(tag.name)}`}
|
193
|
+
className={'inline-block cursor-pointer whitespace-nowrap'}
|
194
|
+
>
|
195
|
+
<div
|
196
|
+
className={
|
197
|
+
' flex items-center rounded-lg px-2 py-0.5 text-black transition-all duration-150 hover:scale-110 hover:bg-blue-600 hover:text-white dark:text-gray-300 dark:hover:bg-yellow-600'
|
198
|
+
}
|
199
|
+
>
|
200
|
+
<div className="text-lg">{tag.name} </div>
|
201
|
+
{tag.count ? (
|
202
|
+
<sup className="relative ml-1">{tag.count}</sup>
|
203
|
+
) : (
|
204
|
+
<></>
|
205
|
+
)}
|
206
|
+
</div>
|
207
|
+
</Link>
|
208
|
+
);
|
209
|
+
})}
|
210
|
+
</div>
|
211
|
+
);
|
212
|
+
}
|
213
|
+
|
214
|
+
/**
|
215
|
+
* 分页
|
216
|
+
* @param {*} param0
|
217
|
+
*/
|
218
|
+
function Pagination(props) {
|
219
|
+
const { totalPage, page, switchPage } = props;
|
220
|
+
if (totalPage <= 0) {
|
221
|
+
return <></>;
|
222
|
+
}
|
223
|
+
const pagesElement = [];
|
224
|
+
|
225
|
+
for (let i = 0; i < totalPage; i++) {
|
226
|
+
const selected = page === i;
|
227
|
+
pagesElement.push(getPageElement(i, selected, switchPage));
|
228
|
+
}
|
229
|
+
return (
|
230
|
+
<div className="flex w-full justify-center space-x-1 py-1">
|
231
|
+
{pagesElement.map((p) => p)}
|
232
|
+
</div>
|
233
|
+
);
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* 获取分页按钮
|
238
|
+
* @param {*} i
|
239
|
+
* @param {*} selected
|
240
|
+
*/
|
241
|
+
function getPageElement(i, selected, switchPage) {
|
242
|
+
return (
|
243
|
+
<div
|
244
|
+
onClick={() => switchPage(i)}
|
245
|
+
className={`${selected ? 'rounded bg-blue-600 font-bold text-white' : 'hover:font-bold hover:text-blue-600'} h-6 w-6 cursor-pointer text-center `}
|
246
|
+
>
|
247
|
+
{i + 1}
|
248
|
+
</div>
|
249
|
+
);
|
250
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import BLOG from '@/blog.config'
|
2
|
+
import { loadExternalResource } from '@/lib/utils'
|
3
|
+
// import { loadExternalResource } from '@/lib/utils'
|
4
|
+
import { useEffect } from 'react'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Giscus评论 @see https://giscus.app/zh-CN
|
8
|
+
* Contribute by @txs https://github.com/txs/NotionNext/commit/1bf7179d0af21fb433e4c7773504f244998678cb
|
9
|
+
* @returns {JSX.Element}
|
10
|
+
* @constructor
|
11
|
+
*/
|
12
|
+
|
13
|
+
const Artalk = ({ siteInfo }) => {
|
14
|
+
useEffect(() => {
|
15
|
+
loadExternalResource(BLOG.COMMENT_ARTALK_CSS, 'css')
|
16
|
+
window?.Artalk?.init({
|
17
|
+
server: BLOG.COMMENT_ARTALK_SERVER, // 后端地址
|
18
|
+
el: '#artalk', // 容器元素
|
19
|
+
locale: BLOG.LANG,
|
20
|
+
// pageKey: '/post/1', // 固定链接 (留空自动获取)
|
21
|
+
// pageTitle: '关于引入 Artalk 的这档子事', // 页面标题 (留空自动获取)
|
22
|
+
site: siteInfo?.title // 你的站点名
|
23
|
+
})
|
24
|
+
}, [])
|
25
|
+
return (
|
26
|
+
<div id="artalk"></div>
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
export default Artalk
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import busuanzi from '@/lib/busuanzi';
|
2
|
+
import { useRouter } from 'next/router';
|
3
|
+
import { useGlobal } from '@/lib/global';
|
4
|
+
// import { useRouter } from 'next/router'
|
5
|
+
import React from 'react';
|
6
|
+
|
7
|
+
let path = '';
|
8
|
+
|
9
|
+
export default function Busuanzi() {
|
10
|
+
const { theme } = useGlobal();
|
11
|
+
const Router = useRouter();
|
12
|
+
Router.events.on('routeChangeComplete', (url) => {
|
13
|
+
if (url !== path) {
|
14
|
+
path = url;
|
15
|
+
busuanzi.fetch();
|
16
|
+
}
|
17
|
+
});
|
18
|
+
|
19
|
+
// 更换主题时更新
|
20
|
+
React.useEffect(() => {
|
21
|
+
if (theme) {
|
22
|
+
busuanzi.fetch();
|
23
|
+
}
|
24
|
+
}, [theme]);
|
25
|
+
return null;
|
26
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import BLOG from '@/blog.config'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 这是一个嵌入组件,可以在任意位置全屏显示您的chat-base对话框
|
5
|
+
* 暂时没有页面引用
|
6
|
+
* 因为您可以直接用内嵌网页的方式放入您的notion中 https://www.chatbase.co/chatbot-iframe/${BLOG.CHATBASE_ID}
|
7
|
+
*/
|
8
|
+
export default function ChatBase() {
|
9
|
+
if (!BLOG.CHATBASE_ID) {
|
10
|
+
return <></>
|
11
|
+
}
|
12
|
+
|
13
|
+
return <iframe
|
14
|
+
src={`https://www.chatbase.co/chatbot-iframe/${BLOG.CHATBASE_ID}`}
|
15
|
+
width="100%"
|
16
|
+
style={{ height: '100%', minHeight: '700px' }}
|
17
|
+
frameborder="0"
|
18
|
+
></iframe>
|
19
|
+
}
|
@@ -0,0 +1,134 @@
|
|
1
|
+
import React, { useRef, useEffect, useImperativeHandle } from 'react';
|
2
|
+
import type { FC, ForwardedRef, ReactNode } from 'react';
|
3
|
+
|
4
|
+
const VerticalStyle = { height: '0px', willChange: 'height' };
|
5
|
+
const horizontalStyle = { width: '0px', willChange: 'width' };
|
6
|
+
|
7
|
+
export interface CollapseHandle {
|
8
|
+
updateCollapseHeight: () => void;
|
9
|
+
}
|
10
|
+
|
11
|
+
export interface CollapseProps {
|
12
|
+
type?: 'horizontal' | 'vertical';
|
13
|
+
isOpen: boolean;
|
14
|
+
className?: string;
|
15
|
+
onHeightChange?: (param: {
|
16
|
+
height: number;
|
17
|
+
width: number;
|
18
|
+
increase: boolean;
|
19
|
+
}) => void;
|
20
|
+
collapseRef?: ForwardedRef<CollapseHandle>;
|
21
|
+
children: ReactNode;
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* 折叠面板组件,支持水平折叠、垂直折叠
|
26
|
+
* @param {type:['horizontal','vertical'],isOpen} props
|
27
|
+
* @returns
|
28
|
+
*/
|
29
|
+
const Collapse: FC<CollapseProps> = (props) => {
|
30
|
+
const {
|
31
|
+
collapseRef,
|
32
|
+
type = 'vertical',
|
33
|
+
onHeightChange,
|
34
|
+
isOpen = false,
|
35
|
+
className,
|
36
|
+
children,
|
37
|
+
} = props;
|
38
|
+
const ref = useRef<HTMLDivElement>(null);
|
39
|
+
|
40
|
+
useImperativeHandle(collapseRef, () => {
|
41
|
+
return {
|
42
|
+
/**
|
43
|
+
* 当子元素高度变化时,可调用此方法更新折叠组件的高度
|
44
|
+
* @param {*} param0
|
45
|
+
*/
|
46
|
+
updateCollapseHeight: () => {
|
47
|
+
const style = ref.current?.style;
|
48
|
+
if (style) {
|
49
|
+
style.height = 'auto';
|
50
|
+
style.width = 'auto';
|
51
|
+
}
|
52
|
+
// ref.current.style.height = ref.current.scrollHeight;
|
53
|
+
// ref.current.style.height = 'auto';
|
54
|
+
},
|
55
|
+
};
|
56
|
+
});
|
57
|
+
|
58
|
+
/**
|
59
|
+
* 折叠
|
60
|
+
* @param {*} element
|
61
|
+
*/
|
62
|
+
const collapseSection = (element: HTMLDivElement) => {
|
63
|
+
const sectionHeight = element.scrollHeight;
|
64
|
+
const sectionWidth = element.scrollWidth;
|
65
|
+
|
66
|
+
requestAnimationFrame(function () {
|
67
|
+
switch (type) {
|
68
|
+
case 'horizontal':
|
69
|
+
element.style.width = sectionWidth + 'px';
|
70
|
+
requestAnimationFrame(function () {
|
71
|
+
element.style.width = 0 + 'px';
|
72
|
+
});
|
73
|
+
break;
|
74
|
+
case 'vertical':
|
75
|
+
element.style.height = sectionHeight + 'px';
|
76
|
+
requestAnimationFrame(function () {
|
77
|
+
element.style.height = 0 + 'px';
|
78
|
+
});
|
79
|
+
}
|
80
|
+
});
|
81
|
+
};
|
82
|
+
|
83
|
+
/**
|
84
|
+
* 展开
|
85
|
+
* @param {*} element
|
86
|
+
*/
|
87
|
+
const expandSection = (element: HTMLDivElement) => {
|
88
|
+
const sectionHeight = element.scrollHeight;
|
89
|
+
const sectionWidth = element.scrollWidth;
|
90
|
+
let clearTime: NodeJS.Timeout;
|
91
|
+
switch (type) {
|
92
|
+
case 'horizontal':
|
93
|
+
element.style.width = sectionWidth + 'px';
|
94
|
+
clearTime = setTimeout(() => {
|
95
|
+
element.style.width = 'auto';
|
96
|
+
}, 400);
|
97
|
+
break;
|
98
|
+
case 'vertical':
|
99
|
+
element.style.height = sectionHeight + 'px';
|
100
|
+
clearTime = setTimeout(() => {
|
101
|
+
element.style.height = 'auto';
|
102
|
+
}, 400);
|
103
|
+
}
|
104
|
+
|
105
|
+
clearTimeout(clearTime);
|
106
|
+
};
|
107
|
+
|
108
|
+
useEffect(() => {
|
109
|
+
if (isOpen) {
|
110
|
+
if (ref.current) expandSection(ref.current);
|
111
|
+
} else {
|
112
|
+
if (ref.current) collapseSection(ref.current);
|
113
|
+
}
|
114
|
+
// 通知父组件高度变化
|
115
|
+
onHeightChange &&
|
116
|
+
onHeightChange({
|
117
|
+
height: ref.current?.scrollHeight || 0,
|
118
|
+
width: ref.current?.scrollWidth || 0,
|
119
|
+
increase: isOpen,
|
120
|
+
});
|
121
|
+
}, [isOpen]);
|
122
|
+
|
123
|
+
return (
|
124
|
+
<div
|
125
|
+
ref={ref}
|
126
|
+
style={type === 'vertical' ? VerticalStyle : horizontalStyle}
|
127
|
+
className={`${className || ''} overflow-hidden duration-200 `}
|
128
|
+
>
|
129
|
+
{children}
|
130
|
+
</div>
|
131
|
+
);
|
132
|
+
};
|
133
|
+
|
134
|
+
export default Collapse;
|
@@ -0,0 +1,161 @@
|
|
1
|
+
import BLOG from '@/blog.config';
|
2
|
+
import dynamic from 'next/dynamic';
|
3
|
+
import Tabs from '@/components/Tabs';
|
4
|
+
import { isBrowser } from '@/lib/utils';
|
5
|
+
import { useRouter } from 'next/router';
|
6
|
+
import Artalk from './Artalk';
|
7
|
+
|
8
|
+
const WalineComponent = dynamic(
|
9
|
+
() => {
|
10
|
+
return import('@/components/WalineComponent');
|
11
|
+
},
|
12
|
+
{ ssr: false },
|
13
|
+
);
|
14
|
+
|
15
|
+
const CusdisComponent = dynamic(
|
16
|
+
() => {
|
17
|
+
return import('@/components/CusdisComponent');
|
18
|
+
},
|
19
|
+
{ ssr: false },
|
20
|
+
);
|
21
|
+
|
22
|
+
const TwikooCompenent = dynamic(
|
23
|
+
() => {
|
24
|
+
return import('@/components/Twikoo');
|
25
|
+
},
|
26
|
+
{ ssr: false },
|
27
|
+
);
|
28
|
+
|
29
|
+
const GitalkComponent = dynamic(
|
30
|
+
() => {
|
31
|
+
return import('@/components/Gitalk');
|
32
|
+
},
|
33
|
+
{ ssr: false },
|
34
|
+
);
|
35
|
+
const UtterancesComponent = dynamic(
|
36
|
+
() => {
|
37
|
+
return import('@/components/Utterances');
|
38
|
+
},
|
39
|
+
{ ssr: false },
|
40
|
+
);
|
41
|
+
const GiscusComponent = dynamic(
|
42
|
+
() => {
|
43
|
+
return import('@/components/Giscus');
|
44
|
+
},
|
45
|
+
{ ssr: false },
|
46
|
+
);
|
47
|
+
const WebMentionComponent = dynamic(
|
48
|
+
() => {
|
49
|
+
return import('@/components/WebMention');
|
50
|
+
},
|
51
|
+
{ ssr: false },
|
52
|
+
);
|
53
|
+
|
54
|
+
const ValineComponent = dynamic(() => import('@/components/ValineComponent'), {
|
55
|
+
ssr: false,
|
56
|
+
});
|
57
|
+
|
58
|
+
/**
|
59
|
+
* 是否有评论
|
60
|
+
*/
|
61
|
+
export const commentEnable =
|
62
|
+
BLOG.COMMENT_TWIKOO_ENV_ID ||
|
63
|
+
BLOG.COMMENT_WALINE_SERVER_URL ||
|
64
|
+
BLOG.COMMENT_VALINE_APP_ID ||
|
65
|
+
BLOG.COMMENT_GISCUS_REPO ||
|
66
|
+
BLOG.COMMENT_CUSDIS_APP_ID ||
|
67
|
+
BLOG.COMMENT_UTTERRANCES_REPO ||
|
68
|
+
BLOG.COMMENT_GITALK_CLIENT_ID ||
|
69
|
+
BLOG.COMMENT_WEBMENTION.ENABLE;
|
70
|
+
|
71
|
+
/**
|
72
|
+
* 评论组件
|
73
|
+
* @param {*} param0
|
74
|
+
* @returns
|
75
|
+
*/
|
76
|
+
const Comment = ({ siteInfo, frontMatter, className }) => {
|
77
|
+
const router = useRouter();
|
78
|
+
|
79
|
+
if (
|
80
|
+
isBrowser &&
|
81
|
+
('giscus' in router.query || router.query.target === 'comment')
|
82
|
+
) {
|
83
|
+
setTimeout(() => {
|
84
|
+
const url = router.asPath.replace('?target=comment', '');
|
85
|
+
history.replaceState({}, '', url);
|
86
|
+
document
|
87
|
+
?.getElementById('comment')
|
88
|
+
?.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
89
|
+
}, 1000);
|
90
|
+
}
|
91
|
+
|
92
|
+
if (!frontMatter) {
|
93
|
+
return <>Loading...</>;
|
94
|
+
}
|
95
|
+
|
96
|
+
return (
|
97
|
+
<div
|
98
|
+
key={frontMatter?.id}
|
99
|
+
id="comment"
|
100
|
+
className={`comment mt-5 text-gray-800 dark:text-gray-300 ${className || ''}`}
|
101
|
+
>
|
102
|
+
<Tabs>
|
103
|
+
{BLOG.COMMENT_ARTALK_SERVER && (
|
104
|
+
<div key="Artalk">
|
105
|
+
<Artalk siteInfo={siteInfo} />
|
106
|
+
</div>
|
107
|
+
)}
|
108
|
+
|
109
|
+
{BLOG.COMMENT_TWIKOO_ENV_ID && (
|
110
|
+
<div key="Twikoo">
|
111
|
+
<TwikooCompenent />
|
112
|
+
</div>
|
113
|
+
)}
|
114
|
+
|
115
|
+
{BLOG.COMMENT_WALINE_SERVER_URL && (
|
116
|
+
<div key="Waline">
|
117
|
+
<WalineComponent />
|
118
|
+
</div>
|
119
|
+
)}
|
120
|
+
|
121
|
+
{BLOG.COMMENT_VALINE_APP_ID && (
|
122
|
+
<div key="Valine" name="reply">
|
123
|
+
<ValineComponent path={frontMatter.id} />
|
124
|
+
</div>
|
125
|
+
)}
|
126
|
+
|
127
|
+
{BLOG.COMMENT_GISCUS_REPO && (
|
128
|
+
<div key="Giscus">
|
129
|
+
<GiscusComponent className="px-2" />
|
130
|
+
</div>
|
131
|
+
)}
|
132
|
+
|
133
|
+
{BLOG.COMMENT_CUSDIS_APP_ID && (
|
134
|
+
<div key="Cusdis">
|
135
|
+
<CusdisComponent frontMatter={frontMatter} />
|
136
|
+
</div>
|
137
|
+
)}
|
138
|
+
|
139
|
+
{BLOG.COMMENT_UTTERRANCES_REPO && (
|
140
|
+
<div key="Utterance">
|
141
|
+
<UtterancesComponent issueTerm={frontMatter.id} className="px-2" />
|
142
|
+
</div>
|
143
|
+
)}
|
144
|
+
|
145
|
+
{BLOG.COMMENT_GITALK_CLIENT_ID && (
|
146
|
+
<div key="GitTalk">
|
147
|
+
<GitalkComponent frontMatter={frontMatter} />
|
148
|
+
</div>
|
149
|
+
)}
|
150
|
+
|
151
|
+
{BLOG.COMMENT_WEBMENTION.ENABLE && (
|
152
|
+
<div key="WebMention">
|
153
|
+
<WebMentionComponent frontMatter={frontMatter} className="px-2" />
|
154
|
+
</div>
|
155
|
+
)}
|
156
|
+
</Tabs>
|
157
|
+
</div>
|
158
|
+
);
|
159
|
+
};
|
160
|
+
|
161
|
+
export default Comment;
|