diff --git a/README.md b/README.md
index 9179f3a..ef8372c 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@

-Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [MYW](https://immyw.com/)
+Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://crazywong.com/)
Docs: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
@@ -79,7 +79,7 @@ npm i hexo-theme-butterfly
- [x] Related articles
- [x] Displays outdated notice for a post
- [x] Share (AddThis/Sharejs/Addtoany)
-- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo)
+- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus)
- [x] Multiple Comment System Support
- [x] Online Chats (Chatra/Tidio/Daovoice/Gitter/Crisp)
- [x] Web analytics
diff --git a/README_CN.md b/README_CN.md
index e680844..5d7e0bb 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -14,7 +14,7 @@

-預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [MYW](https://immyw.com/)
+預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://crazywong.com/)
文檔: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
@@ -79,7 +79,7 @@ theme: butterfly
- [x] 顯示相關文章
- [x] 過期文章提醒
- [x] 多種分享系統(AddThis/Sharejs/Addtoany)
-- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo)
+- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus)
- [x] 支持雙評論部署
- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Gitter/Crisp)
- [x] 多種分析系統
diff --git a/_config.yml b/_config.yml
index cb4e7ea..33580e0 100644
--- a/_config.yml
+++ b/_config.yml
@@ -171,6 +171,7 @@ toc:
post: true
page: false
number: true
+ expand: false
style_simple: false # for post
post_copyright:
@@ -253,10 +254,7 @@ addtoany:
comments:
# Up to two comments system, the first will be shown as default
- # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo
- use:
- # - Valine
- # - Disqus
+ use: # Valine,Disqus
text: true # Display the comment name next to the button
# lazyload: The comment system will be load when comment element enters the browser's viewport.
# If you set it to true, the comment count will be invalid
@@ -308,8 +306,6 @@ valine:
# https://waline.js.org/
waline:
serverURL: # Waline server address url
- avatar: monsterid # gravatar style https://zh-tw.gravatar.com/site/implement/images/#default-image
- avatarCDN: # Gravatar CDN baseURL
bg: # waline background
visitor: false
option:
@@ -341,6 +337,17 @@ twikoo:
visitor: false
option:
+# Giscus
+# https://giscus.app/
+giscus:
+ repo:
+ repo_id:
+ category_id:
+ theme:
+ light: light
+ dark: dark
+ option:
+
# Chat Services
# --------------------------------------
@@ -583,12 +590,11 @@ subtitle:
effect: true
# loop (循環打字)
loop: true
- # source調用第三方服務
+ # source 調用第三方服務
# source: false 關閉調用
- # source: 1 調用搏天 api 的隨機語錄(簡體)
- # source: 2 調用一言網的一句話(簡體)
- # source: 3 調用一句網(簡體)
- # source: 4 調用今日詩詞(簡體)
+ # source: 1 調用一言網的一句話(簡體) https://hitokoto.cn/
+ # source: 2 調用一句網(簡體) http://yijuzhan.com/
+ # source: 3 調用今日詩詞(簡體) https://www.jinrishici.com/
# subtitle 會先顯示 source , 再顯示 sub 的內容
source: false
# 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字
@@ -836,6 +842,7 @@ CDN:
utterances:
twikoo:
waline:
+ giscus:
# share
addtoany:
diff --git a/layout/includes/head/config.pug b/layout/includes/head/config.pug
index 5e15f0e..49fe4fe 100644
--- a/layout/includes/head/config.pug
+++ b/layout/includes/head/config.pug
@@ -119,5 +119,5 @@ script.
},
isPhotoFigcaption: !{theme.photofigcaption},
islazyload: !{theme.lazyload.enable},
- isanchor: !{theme.anchor}
+ isAnchor: !{theme.anchor}
}
diff --git a/layout/includes/page/flink.pug b/layout/includes/page/flink.pug
index f6c1063..e82186d 100644
--- a/layout/includes/page/flink.pug
+++ b/layout/includes/page/flink.pug
@@ -1,18 +1,57 @@
#article-container
.flink
- if site.data.link
- each i in site.data.link
- if i.class_name
- h2!= i.class_name
- if i.class_desc
- .flink-desc!=i.class_desc
- .flink-list
- each item in i.link_list
- .flink-list-item
- a(href=url_for(item.link) title=item.name target="_blank")
- .flink-item-icon
- img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
- .flink-item-name= item.name
- .flink-item-desc(title=item.descr)= item.descr
+ if page.flink_url
+ script.
+ (()=>{
+ const replaceSymbol = (str) => {
+ return str.replace(/[\p{P}\p{S}]/gu, '-')
+ }
+
+ let result = ''
+ fetch('!{url_for(page.flink_url)}')
+ .then(response => response.json())
+ .then(str => {
+ for(let i = 0; i < str.length; i++){
+ const replaceClassName = replaceSymbol(str[i].class_name)
+ const className = str[i].class_name ? `
${str[i].class_name}
` : ''
+ const classDesc = str[i].class_desc ? `${str[i].class_desc}
` : ''
+
+ let listResult = ''
+ const lists = str[i].link_list
+ for(let j = 0; j < lists.length; j++){
+ listResult += `
+ `
+ }
+
+ result += `${className}${classDesc} ${listResult}
`
+ }
+
+ document.querySelector('.flink').insertAdjacentHTML('afterbegin', result)
+ })
+ })()
+
+ else
+ if site.data.link
+ each i in site.data.link
+ if i.class_name
+ !=markdown(`## ${i.class_name}`)
+ if i.class_desc
+ .flink-desc!=i.class_desc
+ .flink-list
+ each item in i.link_list
+ .flink-list-item
+ a(href=url_for(item.link) title=item.name target="_blank")
+ .flink-item-icon
+ img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
+ .flink-item-name= item.name
+ .flink-item-desc(title=item.descr)= item.descr
!= page.content
diff --git a/layout/includes/third-party/comments/giscus.pug b/layout/includes/third-party/comments/giscus.pug
new file mode 100644
index 0000000..b09bf5a
--- /dev/null
+++ b/layout/includes/third-party/comments/giscus.pug
@@ -0,0 +1,49 @@
+- const { repo, repo_id, category_id, option } = theme.giscus
+- const themes = theme.giscus.theme
+script.
+ function loadGiscus () {
+ let nowTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{themes.dark}' : '!{themes.light}'
+
+ const config = Object.assign({
+ src: '!{theme.CDN.giscus}',
+ 'data-repo': '!{repo}',
+ 'data-repo-id': '!{repo_id}',
+ 'data-category-id': '!{category_id}',
+ 'data-mapping': 'pathname',
+ 'data-theme': nowTheme,
+ 'data-reactions-enabled': '1',
+ crossorigin: 'anonymous',
+ async: true
+ },!{JSON.stringify(option)})
+
+ let ele = document.createElement('script')
+ for (let key in config) {
+ ele.setAttribute(key, config[key])
+ }
+ document.getElementById('giscus-wrap').insertAdjacentElement('afterbegin',ele)
+ }
+
+ function changeGiscusTheme () {
+ const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{themes.dark}' : '!{themes.light}'
+
+ function sendMessage(message) {
+ const iframe = document.querySelector('iframe.giscus-frame');
+ if (!iframe) return;
+ iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
+ }
+
+ sendMessage({
+ setConfig: {
+ theme: theme
+ }
+ });
+ }
+
+ if ('!{theme.comments.use[0]}' === 'Giscus' || !!{theme.comments.lazyload}) {
+ if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('giscus-wrap'), loadGiscus)
+ else loadGiscus()
+ } else {
+ function loadOtherComment () {
+ loadGiscus()
+ }
+ }
diff --git a/layout/includes/third-party/comments/index.pug b/layout/includes/third-party/comments/index.pug
index 3bab3bb..6c725d6 100644
--- a/layout/includes/third-party/comments/index.pug
+++ b/layout/includes/third-party/comments/index.pug
@@ -33,6 +33,8 @@ hr
#twikoo-wrap
when 'Waline'
#waline-wrap
+ when 'Giscus'
+ #giscus-wrap
when 'Facebook Comments'
.fb-comments(data-colorscheme = theme.display_mode === 'dark' ? 'dark' : 'light'
data-numposts= theme.facebook_comments.pageSize || 10
diff --git a/layout/includes/third-party/comments/js.pug b/layout/includes/third-party/comments/js.pug
index 0b04451..1acd8d3 100644
--- a/layout/includes/third-party/comments/js.pug
+++ b/layout/includes/third-party/comments/js.pug
@@ -16,5 +16,7 @@ each name in theme.comments.use
!=partial('includes/third-party/comments/twikoo', {}, {cache: true})
when 'Waline'
!=partial('includes/third-party/comments/waline', {}, {cache: true})
+ when 'Giscus'
+ !=partial('includes/third-party/comments/giscus', {}, {cache: true})
when 'Facebook Comments'
!=partial('includes/third-party/comments/facebook_comments', {}, {cache: true})
diff --git a/layout/includes/third-party/comments/waline.pug b/layout/includes/third-party/comments/waline.pug
index 2407e4a..cfd05c2 100644
--- a/layout/includes/third-party/comments/waline.pug
+++ b/layout/includes/third-party/comments/waline.pug
@@ -1,4 +1,4 @@
-- const { serverURL, avatar, avatarCDN, visitor, option } = theme.waline
+- const { serverURL, visitor, option } = theme.waline
script.
function loadWaline () {
@@ -6,15 +6,13 @@ script.
const waline = new Waline(Object.assign({
el: '#waline-wrap',
serverURL: '!{serverURL}',
- avatar: '#{avatar}',
- avatarCDN: '!{avatarCDN || "https://sdn.geekzu.org/avatar/"}',
path: location.pathname,
visitor: !{visitor},
dark: 'html[data-theme="dark"]'
}, !{JSON.stringify(option)}))
}
- if (typeof Waline === 'function') initWaline()
+ if (typeof Waline === 'function') initWaline()
else getScript('!{url_for(theme.CDN.waline)}').then(initWaline)
}
diff --git a/layout/includes/third-party/math/mermaid.pug b/layout/includes/third-party/math/mermaid.pug
index 5716b30..31dda5e 100644
--- a/layout/includes/third-party/math/mermaid.pug
+++ b/layout/includes/third-party/math/mermaid.pug
@@ -3,6 +3,7 @@ script.
const $mermaidWrap = document.querySelectorAll('#article-container .mermaid-wrap')
if ($mermaidWrap.length) {
window.runMermaid = () => {
+ window.loadMermaid = true
const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{theme.mermaid.theme.dark}' : '!{theme.mermaid.theme.light}'
Array.from($mermaidWrap).forEach((item, index) => {
@@ -17,7 +18,7 @@ script.
}
const loadMermaid = () => {
- window.mermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid)
+ window.loadMermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid)
}
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
diff --git a/layout/includes/third-party/newest-comments/disqus-comment.pug b/layout/includes/third-party/newest-comments/disqus-comment.pug
index 896faab..5b95e74 100644
--- a/layout/includes/third-party/newest-comments/disqus-comment.pug
+++ b/layout/includes/third-party/newest-comments/disqus-comment.pug
@@ -49,7 +49,7 @@ script.
}
result += `
-
+
${array[i].nick}
`
}
diff --git a/layout/includes/third-party/newest-comments/github-issues.pug b/layout/includes/third-party/newest-comments/github-issues.pug
index 04fa27c..2153b11 100644
--- a/layout/includes/third-party/newest-comments/github-issues.pug
+++ b/layout/includes/third-party/newest-comments/github-issues.pug
@@ -75,7 +75,7 @@ script.
}
result += `
-
+
${array[i].nick} /
`
}
diff --git a/layout/includes/third-party/newest-comments/twikoo-comment.pug b/layout/includes/third-party/newest-comments/twikoo-comment.pug
index d8cea8c..71b2803 100644
--- a/layout/includes/third-party/newest-comments/twikoo-comment.pug
+++ b/layout/includes/third-party/newest-comments/twikoo-comment.pug
@@ -60,7 +60,7 @@ script.
}
result += `
-
+
${array[i].nick} /
`
}
diff --git a/layout/includes/third-party/newest-comments/valine.pug b/layout/includes/third-party/newest-comments/valine.pug
index 809da74..2aa758d 100644
--- a/layout/includes/third-party/newest-comments/valine.pug
+++ b/layout/includes/third-party/newest-comments/valine.pug
@@ -37,7 +37,7 @@ script.
}
result += `
-
+
${array[i].nick} /
`
}
diff --git a/layout/includes/third-party/newest-comments/waline.pug b/layout/includes/third-party/newest-comments/waline.pug
index 2b9b630..e412c37 100644
--- a/layout/includes/third-party/newest-comments/waline.pug
+++ b/layout/includes/third-party/newest-comments/waline.pug
@@ -16,13 +16,6 @@ script.
return content
}
- const getIcon = (ava,mail) => {
- if (ava) return ava
- let defaultIcon = '!{ avatar ? `?d=${avatar}` : ''}'
- let iconUrl = "!{avatarCDN ? avatarCDN : 'https://gravatar.loli.net/avatar/'}" + mail + defaultIcon
- return iconUrl
- }
-
const generateHtml = array => {
let result = ''
@@ -36,7 +29,7 @@ script.
}
result += `
-
+
${array[i].nick} /
`
}
@@ -59,7 +52,7 @@ script.
const walineArray = res.comments.map(e => {
return {
'content': changeContent(e.comment),
- 'avatar': getIcon(e.QQAvatar,e.mail),
+ 'avatar': e.avatar,
'nick': e.nick,
'url': e.url + '#' + e.objectId,
'date': e.updatedAt,
diff --git a/layout/includes/third-party/subtitle.pug b/layout/includes/third-party/subtitle.pug
index 1b2720c..21d705a 100644
--- a/layout/includes/third-party/subtitle.pug
+++ b/layout/includes/third-party/subtitle.pug
@@ -3,38 +3,6 @@
case source
when 1
- script.
- function subtitleType () {
- fetch('https://api.btstu.cn/yan/api.php?charset=utf-8&encode=json')
- .then(response => response.json())
- .then(data => {
- if (!{effect}) {
- const sub = !{JSON.stringify(subContent)}
- sub.unshift(data.text)
- window.typed = new Typed('#subtitle', {
- strings: sub,
- startDelay: 300,
- typeSpeed: 150,
- loop: !{loop},
- backSpeed: 50,
- })
- } else {
- document.getElementById('subtitle').innerHTML = data.text
- }
- })
- }
-
- if (!{effect}) {
- if (typeof Typed === 'function') {
- subtitleType()
- } else {
- getScript('!{url_for(theme.CDN.typed)}').then(subtitleType)
- }
- } else {
- subtitleType()
- }
-
- when 2
script.
function subtitleType () {
fetch('https://v1.hitokoto.cn')
@@ -67,7 +35,7 @@ case source
subtitleType()
}
- when 3
+ when 2
script.
function subtitleType () {
getScript('https://yijuzhan.com/api/word.php?m=js').then(() => {
@@ -99,7 +67,7 @@ case source
subtitleType()
}
- when 4
+ when 3
script.
function subtitleType () {
getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js').then(() => {
diff --git a/layout/includes/widget/card_post_toc.pug b/layout/includes/widget/card_post_toc.pug
index ec80335..4154221 100644
--- a/layout/includes/widget/card_post_toc.pug
+++ b/layout/includes/widget/card_post_toc.pug
@@ -1,4 +1,6 @@
- let tocNumber = page.toc_number !== undefined ? page.toc_number : theme.toc.number
+- let tocExpand = page.toc_expand !== undefined ? page.toc_expand : theme.toc.expand
+- let tocExpandClass = tocExpand ? 'is-expand' : ''
#card-toc.card-widget
.item-headline
@@ -7,7 +9,7 @@
span.toc-percentage
if (page.encrypt == true)
- .toc-content.toc-div-class(style="display:none")!=toc(page.origin, {list_number: tocNumber})
+ .toc-content.toc-div-class(class=tocExpandClass style="display:none")!=toc(page.origin, {list_number: tocNumber})
else
- .toc-content!=toc(page.content, {list_number: tocNumber})
+ .toc-content(class=tocExpandClass)!=toc(page.content, {list_number: tocNumber})
\ No newline at end of file
diff --git a/package.json b/package.json
index 1a32584..7e5536e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hexo-theme-butterfly",
- "version": "4.0.0-b9",
+ "version": "4.0.0-b10",
"description": "A Simple and Card UI Design theme for Hexo",
"main": "package.json",
"scripts": {
@@ -20,13 +20,13 @@
},
"bugs": {
"url": "https://github.com/jerryc127/hexo-theme-butterfly/issues",
- "email": "i@immyw.com"
+ "email": "my@crazywong.com"
},
"dependencies": {
"hexo-renderer-stylus": "^2.0.1",
"hexo-renderer-pug": "^2.0.0"
},
"homepage": "https://butterfly.js.org/",
- "author": "Jerry ",
+ "author": "Jerry ",
"license": "Apache-2.0"
}
diff --git a/scripts/events/cdn.js b/scripts/events/config.js
similarity index 91%
rename from scripts/events/cdn.js
rename to scripts/events/config.js
index ea564b2..8d4d074 100644
--- a/scripts/events/cdn.js
+++ b/scripts/events/config.js
@@ -1,12 +1,18 @@
/**
* Butterfly
- * CDN
+ * 1. Merge CDN
+ * 2. Capitalize the first letter of comment name
*/
'use strict'
hexo.extend.filter.register('before_generate', () => {
const themeConfig = hexo.theme.config
+
+ /**
+ * Merge CDN
+ */
+
const defaultCDN = {
main_css: '/css/index.css',
main: '/js/main.js',
@@ -24,6 +30,7 @@ hexo.extend.filter.register('before_generate', () => {
utterances: 'https://utteranc.es/client.js',
twikoo: 'https://cdn.jsdelivr.net/npm/twikoo/dist/twikoo.all.min.js',
waline: 'https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js',
+ giscus: 'https://giscus.app/client.js',
// share
addtoany: 'https://static.addtoany.com/menu/page.js',
@@ -101,4 +108,18 @@ hexo.extend.filter.register('before_generate', () => {
}
themeConfig.CDN = Object.assign(defaultCDN, deleteNullValue(themeConfig.CDN))
+
+ /**
+ * Capitalize the first letter of comment name
+ */
+
+ let { use } = themeConfig.comments
+
+ if (typeof use === 'string') {
+ use = use.split(',')
+ }
+
+ const newArray = use.map(item => item.toLowerCase().replace(/^\S/, s => s.toUpperCase()))
+
+ themeConfig.comments.use = newArray
})
diff --git a/scripts/tag/timeline.js b/scripts/tag/timeline.js
index 1653e6f..43c1333 100644
--- a/scripts/tag/timeline.js
+++ b/scripts/tag/timeline.js
@@ -22,8 +22,6 @@ function timeLineFn (args, content) {
matches.push(match[2])
}
- console.log(matches)
-
for (let i = 0; i < matches.length; i += 2) {
const tlChildTitle = hexo.render.renderSync({ text: matches[i], engine: 'markdown' })
const tlChildContent = hexo.render.renderSync({ text: matches[i + 1], engine: 'markdown' })
diff --git a/source/css/_global/function.styl b/source/css/_global/function.styl
index 741b35f..771196d 100644
--- a/source/css/_global/function.styl
+++ b/source/css/_global/function.styl
@@ -117,7 +117,7 @@ if hexo-config('enter_transitions')
#site-title,
#site-subtitle
- animation: titlescale 1s
+ animation: titleScale 1s
#nav.show
animation: headerNoOpacity 1s
@@ -187,7 +187,7 @@ if hexo-config('avatar.effect') == true
margin-top: 0
opacity: 1
-@keyframes titlescale
+@keyframes titleScale
0%
opacity: 0
transform: scale(.7)
diff --git a/source/css/_global/index.styl b/source/css/_global/index.styl
index 141ee9c..0d3c415 100644
--- a/source/css/_global/index.styl
+++ b/source/css/_global/index.styl
@@ -32,6 +32,7 @@
--timeline-bg: $timeline-content-bg
--timeline-border-color: $timeline-border-color
--pseudo-hover: $pseudo-hover
+ --headline-presudo: #a0a0a0
body
position: relative
diff --git a/source/css/_layout/aside.styl b/source/css/_layout/aside.styl
index ba6e0ff..0696274 100644
--- a/source/css/_layout/aside.styl
+++ b/source/css/_layout/aside.styl
@@ -265,16 +265,17 @@
+maxWidth900()
max-height: calc(100vh - 140px)
- .toc-child
- display: none
+ &:not(.is-expand)
+ .toc-child
+ display: none
- +maxWidth900()
- display: block !important
+ +maxWidth900()
+ display: block !important
- .toc-item
- &.active
- .toc-child
- display: block
+ .toc-item
+ &.active
+ .toc-child
+ display: block
ol,
li
diff --git a/source/css/_layout/post.styl b/source/css/_layout/post.styl
index a082f33..83c6cec 100644
--- a/source/css/_layout/post.styl
+++ b/source/css/_layout/post.styl
@@ -7,7 +7,7 @@ beautify()
font-size: unit(fontsize, 'px')
&:hover
- padding-left: unit(fontsize + 12, 'px')
+ padding-left: unit(fontsize + 18, 'px')
h1,
h2,
@@ -102,6 +102,32 @@ beautify()
font-family: Monaco, 'Ubuntu Mono', monospace
line-height: 1em
+ if hexo-config('anchor')
+ a.headerlink
+ &:after
+ @extend .fontawesomeIcon
+ float: right
+ color: var(--headline-presudo)
+ content: '\f0c1'
+ font-size: .95em
+ opacity: 0
+ transition: all .3s
+
+ &:hover
+ &:after
+ color: var(--pseudo-hover)
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6
+ &:hover
+ a.headerlink
+ &:after
+ opacity: 1
+
ol,
ul
ol,
diff --git a/source/css/_layout/rightside.styl b/source/css/_layout/rightside.styl
index d9e338c..bb3406b 100644
--- a/source/css/_layout/rightside.styl
+++ b/source/css/_layout/rightside.styl
@@ -7,11 +7,7 @@
transition: all .5s
#rightside-config-hide
- transition: transform .4s
- transform: translate(48px, 0)
-
- &.show
- transform: translate(0, 0) !important
+ display: none
& > div
& > button,
@@ -39,3 +35,19 @@
+maxWidth900()
#hide-aside-btn
display: none
+
+@keyframes rightside-item-in
+ 0%
+ transform: translate(48px, 0)
+
+ 100%
+ transform: translate(0, 0)
+
+@keyframes rightside-item-out
+ 0%
+ display: block
+ transform: translate(0, 0)
+
+ 100%
+ display: none
+ transform: translate(48px, 0)
diff --git a/source/css/_search/index.styl b/source/css/_search/index.styl
index cac6278..2e4e874 100644
--- a/source/css/_search/index.styl
+++ b/source/css/_search/index.styl
@@ -9,7 +9,6 @@
width: 600px
border-radius: 8px
background: var(--search-bg)
- animation: titlescale .5s
+maxWidth768()
top: 0
diff --git a/source/js/main.js b/source/js/main.js
index e0625b4..e292a12 100644
--- a/source/js/main.js
+++ b/source/js/main.js
@@ -34,7 +34,7 @@ document.addEventListener('DOMContentLoaded', function () {
open: () => {
btf.sidebarPaddingR()
document.body.style.overflow = 'hidden'
- btf.fadeIn(document.getElementById('menu-mask'), 0.5)
+ btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')
document.getElementById('sidebar-menus').classList.add('open')
mobileSidebarOpen = true
},
@@ -42,7 +42,7 @@ document.addEventListener('DOMContentLoaded', function () {
const $body = document.body
$body.style.overflow = ''
$body.style.paddingRight = ''
- btf.fadeOut(document.getElementById('menu-mask'), 0.5)
+ btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')
document.getElementById('sidebar-menus').classList.remove('open')
mobileSidebarOpen = false
}
@@ -315,82 +315,69 @@ document.addEventListener('DOMContentLoaded', function () {
}
/**
- * toc
+ * toc,anchor
*/
- const tocFn = function () {
- const $cardTocLayout = document.getElementById('card-toc')
- const $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
- const $tocLink = $cardToc.querySelectorAll('.toc-link')
+ const scrollFnToDo = function () {
+ const isToc = GLOBAL_CONFIG_SITE.isToc
+ const isAnchor = GLOBAL_CONFIG.isAnchor
const $article = document.getElementById('article-container')
- const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
- // main of scroll
- window.tocScrollFn = function () {
- return btf.throttle(function () {
- const currentTop = window.scrollY || document.documentElement.scrollTop
- scrollPercent(currentTop)
- findHeadPosition(currentTop)
- }, 100)()
- }
- window.addEventListener('scroll', tocScrollFn)
+ if (!($article && (isToc || isAnchor))) return
- const scrollPercent = function (currentTop) {
- const docHeight = $article.clientHeight
- const winHeight = document.documentElement.clientHeight
- const headerHeight = $article.offsetTop
- const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
- const scrollPercent = (currentTop - headerHeight) / (contentMath)
- const scrollPercentRounded = Math.round(scrollPercent * 100)
- const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
- $tocPercentage.textContent = percentage
- }
+ let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand
- // anchor
- const isAnchor = GLOBAL_CONFIG.isanchor
- const updateAnchor = function (anchor) {
- if (window.history.replaceState && anchor !== window.location.hash) {
- if (!anchor) anchor = location.pathname
- const title = GLOBAL_CONFIG_SITE.title
- window.history.replaceState({
- url: location.href,
- title: title
- }, title, anchor)
+ if (isToc) {
+ const $cardTocLayout = document.getElementById('card-toc')
+ $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
+ $tocLink = $cardToc.querySelectorAll('.toc-link')
+ const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
+ isExpand = $cardToc.classList.contains('is-expand')
+
+ scrollPercent = currentTop => {
+ const docHeight = $article.clientHeight
+ const winHeight = document.documentElement.clientHeight
+ const headerHeight = $article.offsetTop
+ const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
+ const scrollPercent = (currentTop - headerHeight) / (contentMath)
+ const scrollPercentRounded = Math.round(scrollPercent * 100)
+ const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
+ $tocPercentage.textContent = percentage
}
- }
- window.mobileToc = {
- open: () => {
- $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
- },
+ window.mobileToc = {
+ open: () => {
+ $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
+ },
- close: () => {
- $cardTocLayout.style.animation = 'toc-close .2s'
- setTimeout(() => {
- $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
- }, 100)
+ close: () => {
+ $cardTocLayout.style.animation = 'toc-close .2s'
+ setTimeout(() => {
+ $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
+ }, 100)
+ }
}
- }
- // toc元素點擊
- $cardToc.addEventListener('click', (e) => {
- e.preventDefault()
- const $target = e.target.classList.contains('toc-link')
- ? e.target
- : e.target.parentElement
- btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
- if (window.innerWidth < 900) {
- window.mobileToc.close()
- }
- })
+ // toc元素點擊
+ $cardToc.addEventListener('click', e => {
+ e.preventDefault()
+ const $target = e.target.classList.contains('toc-link')
+ ? e.target
+ : e.target.parentElement
+ btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
+ if (window.innerWidth < 900) {
+ window.mobileToc.close()
+ }
+ })
- const autoScrollToc = function (item) {
- const activePosition = item.getBoundingClientRect().top
- const sidebarScrollTop = $cardToc.scrollTop
- if (activePosition > (document.documentElement.clientHeight - 100)) {
- $cardToc.scrollTop = sidebarScrollTop + 150
- }
- if (activePosition < 100) {
- $cardToc.scrollTop = sidebarScrollTop - 150
+ autoScrollToc = item => {
+ const activePosition = item.getBoundingClientRect().top
+ const sidebarScrollTop = $cardToc.scrollTop
+ if (activePosition > (document.documentElement.clientHeight - 100)) {
+ $cardToc.scrollTop = sidebarScrollTop + 150
+ }
+ if (activePosition < 100) {
+ $cardToc.scrollTop = sidebarScrollTop - 150
+ }
}
}
@@ -398,7 +385,7 @@ document.addEventListener('DOMContentLoaded', function () {
const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
let detectItem = ''
const findHeadPosition = function (top) {
- if ($tocLink.length === 0 || top === 0) {
+ if (top === 0) {
return false
}
@@ -407,37 +394,50 @@ document.addEventListener('DOMContentLoaded', function () {
list.forEach(function (ele, index) {
if (top > btf.getEleTop(ele) - 80) {
- currentId = '#' + encodeURI(ele.getAttribute('id'))
+ const id = ele.id
+ currentId = id ? '#' + encodeURI(id) : ''
currentIndex = index
}
})
if (detectItem === currentIndex) return
- if (isAnchor) updateAnchor(currentId)
-
- if (currentId === '') {
- $cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
- detectItem = currentIndex
- return
- }
+ if (isAnchor) btf.updateAnchor(currentId)
detectItem = currentIndex
- $cardToc.querySelectorAll('.active').forEach(item => { item.classList.remove('active') })
- const currentActive = $tocLink[currentIndex]
- currentActive.classList.add('active')
+ if (isToc) {
+ $cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
- setTimeout(() => {
- autoScrollToc(currentActive)
- }, 0)
+ if (currentId === '') {
+ return
+ }
- let parent = currentActive.parentNode
+ const currentActive = $tocLink[currentIndex]
+ currentActive.classList.add('active')
- for (; !parent.matches('.toc'); parent = parent.parentNode) {
- if (parent.matches('li')) parent.classList.add('active')
+ setTimeout(() => {
+ autoScrollToc(currentActive)
+ }, 0)
+
+ if (isExpand) return
+ let parent = currentActive.parentNode
+
+ for (; !parent.matches('.toc'); parent = parent.parentNode) {
+ if (parent.matches('li')) parent.classList.add('active')
+ }
}
}
+
+ // main of scroll
+ window.tocScrollFn = function () {
+ return btf.throttle(function () {
+ const currentTop = window.scrollY || document.documentElement.scrollTop
+ isToc && scrollPercent(currentTop)
+ findHeadPosition(currentTop)
+ }, 100)()
+ }
+ window.addEventListener('scroll', tocScrollFn)
}
/**
@@ -473,12 +473,20 @@ document.addEventListener('DOMContentLoaded', function () {
}
// handle some cases
typeof utterancesTheme === 'function' && utterancesTheme()
+ typeof changeGiscusTheme === 'function' && changeGiscusTheme()
typeof FB === 'object' && window.loadFBComment()
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
typeof runMermaid === 'function' && window.runMermaid()
},
showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開
- document.getElementById('rightside-config-hide').classList.toggle('show')
+ const target = document.getElementById('rightside-config-hide')
+ if (window.rightSideIn) {
+ window.rightSideIn = false
+ btf.animateOut(target, 'rightside-item-out 0.5s')
+ } else {
+ window.rightSideIn = true
+ btf.animateIn(target, 'rightside-item-in 0.5s')
+ }
},
scrollToTop: () => { // Back to top
btf.scrollToDest(0, 500)
@@ -749,7 +757,7 @@ document.addEventListener('DOMContentLoaded', function () {
toggleCardCategory()
}
- GLOBAL_CONFIG_SITE.isToc && tocFn()
+ scrollFnToDo()
GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
addHighlightTool()
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
diff --git a/source/js/search/algolia.js b/source/js/search/algolia.js
index 83353a7..13373e9 100644
--- a/source/js/search/algolia.js
+++ b/source/js/search/algolia.js
@@ -3,9 +3,10 @@ window.addEventListener('load', () => {
const bodyStyle = document.body.style
bodyStyle.width = '100%'
bodyStyle.overflow = 'hidden'
- document.querySelector('#algolia-search .search-dialog').style.display = 'block'
- document.querySelector('#algolia-search .ais-SearchBox-input').focus()
- btf.fadeIn(document.getElementById('search-mask'), 0.5)
+ btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
+ btf.animateIn(document.querySelector('#algolia-search .search-dialog'), 'titleScale 0.5s')
+ setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100)
+
// shortcut: ESC
document.addEventListener('keydown', function f (event) {
if (event.code === 'Escape') {
@@ -19,10 +20,8 @@ window.addEventListener('load', () => {
const bodyStyle = document.body.style
bodyStyle.width = ''
bodyStyle.overflow = ''
- const $searchDialog = document.querySelector('#algolia-search .search-dialog')
- $searchDialog.style.animation = 'search_close .5s'
- setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
- btf.fadeOut(document.getElementById('search-mask'), 0.5)
+ btf.animateOut(document.querySelector('#algolia-search .search-dialog'), 'search_close .5s')
+ btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
}
const searchClickFn = () => {
diff --git a/source/js/search/local-search.js b/source/js/search/local-search.js
index d1df43d..de2db62 100644
--- a/source/js/search/local-search.js
+++ b/source/js/search/local-search.js
@@ -1,10 +1,12 @@
window.addEventListener('load', () => {
let loadFlag = false
- const openSearch = function () {
- document.body.style.cssText = 'width: 100%;overflow: hidden'
- document.querySelector('#local-search .search-dialog').style.display = 'block'
- document.querySelector('#local-search-input input').focus()
- btf.fadeIn(document.getElementById('search-mask'), 0.5)
+ const openSearch = () => {
+ const bodyStyle = document.body.style
+ bodyStyle.width = '100%'
+ bodyStyle.overflow = 'hidden'
+ btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
+ btf.animateIn(document.querySelector('#local-search .search-dialog'), 'titleScale 0.5s')
+ setTimeout(() => { document.querySelector('#local-search-input input').focus() }, 100)
if (!loadFlag) {
search(GLOBAL_CONFIG.localSearch.path)
loadFlag = true
@@ -18,12 +20,12 @@ window.addEventListener('load', () => {
})
}
- const closeSearch = function () {
- document.body.style.cssText = "width: '';overflow: ''"
- const $searchDialog = document.querySelector('#local-search .search-dialog')
- $searchDialog.style.animation = 'search_close .5s'
- setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
- btf.fadeOut(document.getElementById('search-mask'), 0.5)
+ const closeSearch = () => {
+ const bodyStyle = document.body.style
+ bodyStyle.width = ''
+ bodyStyle.overflow = ''
+ btf.animateOut(document.querySelector('#local-search .search-dialog'), 'search_close .5s')
+ btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
}
// click function
diff --git a/source/js/utils.js b/source/js/utils.js
index 5b38b5f..b3e5df0 100644
--- a/source/js/utils.js
+++ b/source/js/utils.js
@@ -58,16 +58,14 @@ const btf = {
}
},
- snackbarShow: (text, showAction, duration) => {
- const sa = (typeof showAction !== 'undefined') ? showAction : false
- const dur = (typeof duration !== 'undefined') ? duration : 2000
- const position = GLOBAL_CONFIG.Snackbar.position
- const bg = document.documentElement.getAttribute('data-theme') === 'light' ? GLOBAL_CONFIG.Snackbar.bgLight : GLOBAL_CONFIG.Snackbar.bgDark
+ snackbarShow: (text, showAction = false, duration = 2000) => {
+ const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
+ const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark
Snackbar.show({
text: text,
backgroundColor: bg,
- showAction: sa,
- duration: dur,
+ showAction: showAction,
+ duration: duration,
pos: position
})
},
@@ -151,16 +149,18 @@ const btf = {
})
},
- fadeIn: (ele, time) => {
- ele.style.cssText = `display:block;animation: to_show ${time}s`
+ animateIn: (ele, text) => {
+ ele.style.display = 'block'
+ ele.style.animation = text
},
- fadeOut: (ele, time) => {
+ animateOut: (ele, text) => {
ele.addEventListener('animationend', function f () {
- ele.style.cssText = "display: none; animation: '' "
+ ele.style.display = ''
+ ele.style.animation = ''
ele.removeEventListener('animationend', f)
})
- ele.style.animation = `to_hide ${time}s`
+ ele.style.animation = text
},
getParents: (elem, selector) => {
@@ -180,7 +180,6 @@ const btf = {
},
/**
- *
* @param {*} selector
* @param {*} eleType the type of create element
* @param {*} options object key: value
@@ -263,5 +262,16 @@ const btf = {
})
}
})
+ },
+
+ updateAnchor: (anchor) => {
+ if (anchor !== window.location.hash) {
+ if (!anchor) anchor = location.pathname
+ const title = GLOBAL_CONFIG_SITE.title
+ window.history.replaceState({
+ url: location.href,
+ title: title
+ }, title, anchor)
+ }
}
}