From 5a98a4a60f6a671b3311faa64dcadd53377a5625 Mon Sep 17 00:00:00 2001 From: Alexander Fuks Date: Sun, 29 Sep 2024 20:53:17 +0000 Subject: [PATCH 01/17] feat: show toc on mobile screens --- _includes/overlay.html | 11 +++ _includes/post-toc-button.html | 12 +++ _includes/post-topbar.html | 14 +++ _includes/toc-status.html | 10 +++ _includes/toc.html | 7 +- _javascript/modules/components/post-topbar.js | 86 +++++++++++++++++++ _javascript/modules/components/toc.js | 2 +- _javascript/modules/layouts.js | 1 + _javascript/post.js | 10 ++- _layouts/post.html | 7 +- _sass/addon/commons.scss | 8 +- _sass/layout/post.scss | 78 +++++++++++++++++ 12 files changed, 233 insertions(+), 13 deletions(-) create mode 100644 _includes/overlay.html create mode 100644 _includes/post-toc-button.html create mode 100644 _includes/post-topbar.html create mode 100644 _includes/toc-status.html create mode 100644 _javascript/modules/components/post-topbar.js diff --git a/_includes/overlay.html b/_includes/overlay.html new file mode 100644 index 00000000000..8b7b85ffb48 --- /dev/null +++ b/_includes/overlay.html @@ -0,0 +1,11 @@ + +
+
+

{{- site.data.locales[include.lang].panel.toc -}}

+ +
+
+
+
diff --git a/_includes/post-toc-button.html b/_includes/post-toc-button.html new file mode 100644 index 00000000000..ab233405946 --- /dev/null +++ b/_includes/post-toc-button.html @@ -0,0 +1,12 @@ +{% comment %} + Post TOC button trigger +{% endcomment %} + +{% include toc-status.html %} + +{% if enable_toc %} + +{% endif %} diff --git a/_includes/post-topbar.html b/_includes/post-topbar.html new file mode 100644 index 00000000000..ef9f767f8a0 --- /dev/null +++ b/_includes/post-topbar.html @@ -0,0 +1,14 @@ +{% comment %} + Post secondary topbar +{% endcomment %} + +{% include toc-status.html %} + +{% if enable_toc %} +
+ {{ page.title }} + +
+{% endif %} diff --git a/_includes/toc-status.html b/_includes/toc-status.html new file mode 100644 index 00000000000..ff030be1b32 --- /dev/null +++ b/_includes/toc-status.html @@ -0,0 +1,10 @@ +{% comment %} + Determine TOC enable and return it through variable "toc_enable" +{% endcomment %} + +{% assign enable_toc = false %} +{% if site.toc and page.toc %} + {% if page.content contains ' diff --git a/_javascript/modules/components/post-topbar.js b/_javascript/modules/components/post-topbar.js new file mode 100644 index 00000000000..db28fea7f80 --- /dev/null +++ b/_javascript/modules/components/post-topbar.js @@ -0,0 +1,86 @@ +/* + * Post topbar functions + */ + +const postTopbar = document.getElementById('post-topbar'); + +const overlayTrigger = document.getElementById('overlay-trigger'); +const overlayInlineTrigger = document.getElementById('overlay-inline-trigger'); + +const overlay = document.getElementById('overlay'); +const overlayContent = document.getElementById('overlay-content'); +const overlayClose = document.getElementById('overlay-close'); + +const UNLOADED = 'd-none'; + +function initPostTopbar() { + window.addEventListener('scroll', () => { + if (window.scrollY >= overlayInlineTrigger?.offsetTop) { + postTopbar.classList.remove(UNLOADED); + } else { + postTopbar.classList.add(UNLOADED); + } + }); +} + +class Overlay { + static NOSCROLL = 'overflow-hidden'; + + static show() { + this.setScrollEnabled(false); + overlay.showModal(); + } + + static hide() { + this.setScrollEnabled(true); + overlay.close(); + } + + static setScrollEnabled(enabled) { + if (enabled) { + document.documentElement.classList.remove(this.NOSCROLL); + document.body.classList.remove(this.NOSCROLL); + } else { + document.documentElement.classList.add(this.NOSCROLL); + document.body.classList.add(this.NOSCROLL); + } + } + + static addTocToOverlay() { + const toc = document.getElementById('toc-wrapper'); + const clonedToc = toc.cloneNode(true); + this.removeContent(); + overlayContent.appendChild(clonedToc); + } + + static removeContent() { + while (overlayContent.firstChild) { + overlayContent.removeChild(overlayContent.firstChild); + } + } + + static init() { + [overlayTrigger, overlayInlineTrigger].forEach((e) => + e.addEventListener('click', () => { + this.addTocToOverlay(); + this.show(); + }) + ); + + overlay?.addEventListener('click', () => { + this.hide(); + this.removeContent(); + }); + + overlayClose?.addEventListener('click', () => { + this.hide(); + this.removeContent(); + }); + } +} + +export { initPostTopbar }; + +export function initOverlay() { + Overlay.init(); +} diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js index 56ce26fac50..5244823409e 100644 --- a/_javascript/modules/components/toc.js +++ b/_javascript/modules/components/toc.js @@ -10,6 +10,6 @@ export function toc() { scrollSmooth: false }); - document.getElementById('toc-wrapper').classList.remove('d-none'); + document.getElementById('toc-wrapper')?.classList.remove('d-none'); } } diff --git a/_javascript/modules/layouts.js b/_javascript/modules/layouts.js index 28f7962591b..7597784edbe 100644 --- a/_javascript/modules/layouts.js +++ b/_javascript/modules/layouts.js @@ -1,3 +1,4 @@ export { basic } from './layouts/basic'; export { initSidebar } from './layouts/sidebar'; export { initTopbar } from './layouts/topbar'; +export { initPostTopbar, initOverlay } from './components/post-topbar'; diff --git a/_javascript/post.js b/_javascript/post.js index 9340f05e70d..acf9c7eed0e 100644 --- a/_javascript/post.js +++ b/_javascript/post.js @@ -1,4 +1,10 @@ -import { basic, initSidebar, initTopbar } from './modules/layouts'; +import { + basic, + initOverlay, + initSidebar, + initTopbar, + initPostTopbar +} from './modules/layouts'; import { loadImg, imgPopup, @@ -14,4 +20,6 @@ initSidebar(); initLocaleDatetime(); initClipboard(); initTopbar(); +initPostTopbar(); +initOverlay(); basic(); diff --git a/_layouts/post.html b/_layouts/post.html index f17ceea8641..648e8c93b88 100644 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -8,9 +8,10 @@ - post-nav - comments --- - {% include lang.html %} +{% include post-topbar.html lang=lang %} +

{{ page.title }}

@@ -95,6 +96,8 @@

{{ page.title }}

+ {% include post-toc-button.html lang=lang %} +
{{ content }}
@@ -150,3 +153,5 @@

{{ page.title }}

+ +{% include overlay.html lang=lang %} diff --git a/_sass/addon/commons.scss b/_sass/addon/commons.scss index e2a0e6154b5..1415287b12a 100644 --- a/_sass/addon/commons.scss +++ b/_sass/addon/commons.scss @@ -604,6 +604,10 @@ main { } } +button i { + color: #999999; +} + /* --- Effects classes --- */ .flex-grow-1 { @@ -908,10 +912,6 @@ $btn-mb: 0.5rem; } #topbar { - button i { - color: #999999; - } - #breadcrumb { font-size: 1rem; color: var(--text-muted-color); diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 815db933198..38dcf93fda4 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -14,6 +14,47 @@ padding-right: $pr; } +#post-topbar { + position: sticky; + top: 0; + z-index: 1; + background: var(--sidebar-bg); + border-bottom: 1px solid var(--sidebar-border-color); + + @media all and (min-width: 992px) { + margin: 0 calc(var(--bs-gutter-x) * -0.5 - 2.5rem); + } + + @media all and (max-width: 991px) { + margin: 0 calc(var(--bs-gutter-x) * -0.5 - 1.75rem); + } + + @media all and (max-width: 849px) { + margin: 0 calc(var(--bs-gutter-x) * -0.5 - 1.5rem); + } + + @media all and (max-width: 768px) { + margin: 0 -1.5rem; + } + + @media all and (max-width: 767px) { + margin: 0 -0.75rem; + } + + span { + padding: 0 0.75rem; + text-wrap: nowrap; + overflow: hidden; + text-overflow: ellipsis; + word-break: keep-all; + white-space: nowrap; + } +} + +#overlay-inline-trigger { + margin-top: 2.5rem; +} + header { .post-desc { @extend %heading; @@ -342,6 +383,33 @@ header { } } +#overlay { + border-color: var(--btn-border-color); + border-width: 1px; + border-radius: 0.5rem; + height: 90%; + + h2 { + margin: unset; + } + + #toc-wrapper { + margin-top: 2rem; + top: unset; + transition: none; + animation: none; + + h2 { + margin-top: unset; + display: none; + } + } +} + +dialog::backdrop { + backdrop-filter: blur(5px); +} + @media all and (max-width: 576px) { .post-tail-bottom { flex-wrap: wrap-reverse !important; @@ -368,3 +436,13 @@ header { margin-right: -0.5rem; } } + +@media all and (min-width: 1200px) { + #post-topbar { + display: none !important; + } + + #overlay-inline-trigger { + display: none !important; + } +} From c971949a2d24b62a7968c7289b487c0f8e9eaabf Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:05:16 +0800 Subject: [PATCH 02/17] refactor: improve code --- _includes/overlay.html | 11 - _includes/post-toc-button.html | 12 -- _includes/post-topbar.html | 14 -- _includes/toc-status.html | 2 +- _includes/toc.html | 2 +- _javascript/modules/components/post-topbar.js | 86 -------- _javascript/modules/components/toc.js | 40 ++-- .../modules/components/toc/toc-desktop.js | 26 +++ .../modules/components/toc/toc-mobile.js | 85 ++++++++ _javascript/modules/layouts.js | 1 - _javascript/modules/plugins.js | 2 +- _javascript/post.js | 15 +- _layouts/post.html | 31 ++- _sass/addon/module.scss | 1 + _sass/layout/post.scss | 191 +++++++++++------- 15 files changed, 291 insertions(+), 228 deletions(-) delete mode 100644 _includes/overlay.html delete mode 100644 _includes/post-toc-button.html delete mode 100644 _includes/post-topbar.html delete mode 100644 _javascript/modules/components/post-topbar.js create mode 100644 _javascript/modules/components/toc/toc-desktop.js create mode 100644 _javascript/modules/components/toc/toc-mobile.js diff --git a/_includes/overlay.html b/_includes/overlay.html deleted file mode 100644 index 8b7b85ffb48..00000000000 --- a/_includes/overlay.html +++ /dev/null @@ -1,11 +0,0 @@ - -
-
-

{{- site.data.locales[include.lang].panel.toc -}}

- -
-
-
-
diff --git a/_includes/post-toc-button.html b/_includes/post-toc-button.html deleted file mode 100644 index ab233405946..00000000000 --- a/_includes/post-toc-button.html +++ /dev/null @@ -1,12 +0,0 @@ -{% comment %} - Post TOC button trigger -{% endcomment %} - -{% include toc-status.html %} - -{% if enable_toc %} - -{% endif %} diff --git a/_includes/post-topbar.html b/_includes/post-topbar.html deleted file mode 100644 index ef9f767f8a0..00000000000 --- a/_includes/post-topbar.html +++ /dev/null @@ -1,14 +0,0 @@ -{% comment %} - Post secondary topbar -{% endcomment %} - -{% include toc-status.html %} - -{% if enable_toc %} -
- {{ page.title }} - -
-{% endif %} diff --git a/_includes/toc-status.html b/_includes/toc-status.html index ff030be1b32..4b71caeefcc 100644 --- a/_includes/toc-status.html +++ b/_includes/toc-status.html @@ -1,5 +1,5 @@ {% comment %} - Determine TOC enable and return it through variable "toc_enable" + Determine TOC state and return it through variable "enable_toc" {% endcomment %} {% assign enable_toc = false %} diff --git a/_includes/toc.html b/_includes/toc.html index bac1c42f8fc..49c8f9052d3 100644 --- a/_includes/toc.html +++ b/_includes/toc.html @@ -1,7 +1,7 @@ {% include toc-status.html %} {% if enable_toc %} -
+

{{- site.data.locales[include.lang].panel.toc -}}

diff --git a/_javascript/modules/components/post-topbar.js b/_javascript/modules/components/post-topbar.js deleted file mode 100644 index db28fea7f80..00000000000 --- a/_javascript/modules/components/post-topbar.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Post topbar functions - */ - -const postTopbar = document.getElementById('post-topbar'); - -const overlayTrigger = document.getElementById('overlay-trigger'); -const overlayInlineTrigger = document.getElementById('overlay-inline-trigger'); - -const overlay = document.getElementById('overlay'); -const overlayContent = document.getElementById('overlay-content'); -const overlayClose = document.getElementById('overlay-close'); - -const UNLOADED = 'd-none'; - -function initPostTopbar() { - window.addEventListener('scroll', () => { - if (window.scrollY >= overlayInlineTrigger?.offsetTop) { - postTopbar.classList.remove(UNLOADED); - } else { - postTopbar.classList.add(UNLOADED); - } - }); -} - -class Overlay { - static NOSCROLL = 'overflow-hidden'; - - static show() { - this.setScrollEnabled(false); - overlay.showModal(); - } - - static hide() { - this.setScrollEnabled(true); - overlay.close(); - } - - static setScrollEnabled(enabled) { - if (enabled) { - document.documentElement.classList.remove(this.NOSCROLL); - document.body.classList.remove(this.NOSCROLL); - } else { - document.documentElement.classList.add(this.NOSCROLL); - document.body.classList.add(this.NOSCROLL); - } - } - - static addTocToOverlay() { - const toc = document.getElementById('toc-wrapper'); - const clonedToc = toc.cloneNode(true); - this.removeContent(); - overlayContent.appendChild(clonedToc); - } - - static removeContent() { - while (overlayContent.firstChild) { - overlayContent.removeChild(overlayContent.firstChild); - } - } - - static init() { - [overlayTrigger, overlayInlineTrigger].forEach((e) => - e.addEventListener('click', () => { - this.addTocToOverlay(); - this.show(); - }) - ); - - overlay?.addEventListener('click', () => { - this.hide(); - this.removeContent(); - }); - - overlayClose?.addEventListener('click', () => { - this.hide(); - this.removeContent(); - }); - } -} - -export { initPostTopbar }; - -export function initOverlay() { - Overlay.init(); -} diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js index 5244823409e..a33829fca47 100644 --- a/_javascript/modules/components/toc.js +++ b/_javascript/modules/components/toc.js @@ -1,15 +1,29 @@ -export function toc() { - if (document.querySelector('main h2, main h3')) { - // see: https://github.com/tscanlin/tocbot#usage - tocbot.init({ - tocSelector: '#toc', - contentSelector: '.content', - ignoreSelector: '[data-toc-skip]', - headingSelector: 'h2, h3, h4', - orderedList: false, - scrollSmooth: false - }); - - document.getElementById('toc-wrapper')?.classList.remove('d-none'); +import { TocMobile as mobile } from './toc/toc-mobile'; +import { TocDesktop as desktop } from './toc/toc-desktop'; + +const mediaQuery = matchMedia('(min-width: 1200px)'); + +function refresh() { + if (mediaQuery.matches) { + desktop.refresh(); + } else { + mobile.refresh(); } } + +function init() { + if (document.querySelector('main>article[data-toc="true"]') === null) { + return; + } + + // Avoid create multiple instances of Tocbot. Ref: + if (mediaQuery.matches) { + desktop.init(); + } else { + mobile.init(); + } + + mediaQuery.addEventListener('change', refresh); +} + +export { init as initToc }; diff --git a/_javascript/modules/components/toc/toc-desktop.js b/_javascript/modules/components/toc/toc-desktop.js new file mode 100644 index 00000000000..6fd636e295b --- /dev/null +++ b/_javascript/modules/components/toc/toc-desktop.js @@ -0,0 +1,26 @@ +export class TocDesktop { + /* Tocbot options Ref: https://github.com/tscanlin/tocbot#usage */ + static options = { + tocSelector: '#toc', + contentSelector: '.content', + ignoreSelector: '[data-toc-skip]', + headingSelector: 'h2, h3, h4', + orderedList: false, + scrollSmooth: false, + headingsOffset: 16 * 2 // 2rem + }; + + static refresh() { + tocbot.refresh(TocDesktop.options); + } + + static init() { + const wrapper = document.getElementById('toc-wrapper'); + + if (wrapper === null) { + return; + } + + tocbot.init(TocDesktop.options); + } +} diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js new file mode 100644 index 00000000000..0c0d2ed70eb --- /dev/null +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -0,0 +1,85 @@ +/* + TOC topbar and popup for mobile/tablet + */ + +const tocBar = document.getElementById('toc-bar'); +const soloTrigger = document.getElementById('toc-solo-trigger'); +const triggers = document.getElementsByClassName('toc-trigger'); + +const popup = document.getElementById('toc-popup'); +const btnClose = document.getElementById('toc-popup-close'); + +export class TocMobile { + static isVisible = false; + static FROZEN = 'overflow-hidden'; + static barHeight = 16 * 3; // 3rem + + static options = { + tocSelector: '#toc-popup-content', + contentSelector: '.content', + ignoreSelector: '[data-toc-skip]', + headingSelector: 'h2, h3, h4', + orderedList: false, + scrollSmooth: false, + collapseDepth: 4, + headingsOffset: TocMobile.barHeight + }; + + static initBar() { + if (tocBar === null) { + return; + } + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + tocBar.classList.toggle('invisible', entry.isIntersecting); + }); + }, + { rootMargin: `-${TocMobile.barHeight}px 0px 0px 0px` } + ); + + observer.observe(soloTrigger); + } + + static refresh() { + if (!TocMobile.isVisible) { + TocMobile.initComponents(); + } + + tocbot.refresh(this.options); + } + + static show() { + TocMobile.setScrollEnabled(false); + popup.showModal(); + } + + static hide() { + TocMobile.setScrollEnabled(true); + popup.close(); + } + + static setScrollEnabled(enabled) { + document.documentElement.classList.toggle(this.FROZEN, !enabled); + document.body.classList.toggle(this.FROZEN, !enabled); + } + + static initComponents() { + TocMobile.initBar(); + + [...triggers].forEach((trigger) => { + trigger.addEventListener('click', TocMobile.show); + }); + + popup?.addEventListener('click', TocMobile.hide); + btnClose?.addEventListener('click', TocMobile.hide); + + TocMobile.isVisible = true; + } + + static init() { + tocbot.init(this.options); + TocMobile.initComponents(); + } +} diff --git a/_javascript/modules/layouts.js b/_javascript/modules/layouts.js index 7597784edbe..28f7962591b 100644 --- a/_javascript/modules/layouts.js +++ b/_javascript/modules/layouts.js @@ -1,4 +1,3 @@ export { basic } from './layouts/basic'; export { initSidebar } from './layouts/sidebar'; export { initTopbar } from './layouts/topbar'; -export { initPostTopbar, initOverlay } from './components/post-topbar'; diff --git a/_javascript/modules/plugins.js b/_javascript/modules/plugins.js index fb892e25bf5..cc95c1bc393 100644 --- a/_javascript/modules/plugins.js +++ b/_javascript/modules/plugins.js @@ -3,4 +3,4 @@ export { initClipboard } from './components/clipboard'; export { loadImg } from './components/img-loading'; export { imgPopup } from './components/img-popup'; export { initLocaleDatetime } from './components/locale-datetime'; -export { toc } from './components/toc'; +export { initToc } from './components/toc'; diff --git a/_javascript/post.js b/_javascript/post.js index acf9c7eed0e..1c616ecd0a6 100644 --- a/_javascript/post.js +++ b/_javascript/post.js @@ -1,25 +1,18 @@ -import { - basic, - initOverlay, - initSidebar, - initTopbar, - initPostTopbar -} from './modules/layouts'; +import { basic, initTopbar, initSidebar } from './modules/layouts'; + import { loadImg, imgPopup, initLocaleDatetime, initClipboard, - toc + initToc } from './modules/plugins'; loadImg(); -toc(); +initToc(); imgPopup(); initSidebar(); initLocaleDatetime(); initClipboard(); initTopbar(); -initPostTopbar(); -initOverlay(); basic(); diff --git a/_layouts/post.html b/_layouts/post.html index 648e8c93b88..6042db13e95 100644 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -8,11 +8,12 @@ - post-nav - comments --- + {% include lang.html %} -{% include post-topbar.html lang=lang %} +{% include toc-status.html %} -
+

{{ page.title }}

{% if page.description %} @@ -96,7 +97,29 @@

{{ page.title }}

- {% include post-toc-button.html lang=lang %} + {% if enable_toc %} + + + + + +
+
{{- page.title -}}
+ +
+
+
+ {% endif %}
{{ content }} @@ -153,5 +176,3 @@

{{ page.title }}

- -{% include overlay.html lang=lang %} diff --git a/_sass/addon/module.scss b/_sass/addon/module.scss index 42db4e2d991..3573b86a397 100644 --- a/_sass/addon/module.scss +++ b/_sass/addon/module.scss @@ -8,6 +8,7 @@ color: var(--heading-color); font-weight: 400; font-family: $font-family-heading; + scroll-margin-top: 2rem; } %anchor { diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 38dcf93fda4..2447098fda2 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -14,47 +14,6 @@ padding-right: $pr; } -#post-topbar { - position: sticky; - top: 0; - z-index: 1; - background: var(--sidebar-bg); - border-bottom: 1px solid var(--sidebar-border-color); - - @media all and (min-width: 992px) { - margin: 0 calc(var(--bs-gutter-x) * -0.5 - 2.5rem); - } - - @media all and (max-width: 991px) { - margin: 0 calc(var(--bs-gutter-x) * -0.5 - 1.75rem); - } - - @media all and (max-width: 849px) { - margin: 0 calc(var(--bs-gutter-x) * -0.5 - 1.5rem); - } - - @media all and (max-width: 768px) { - margin: 0 -1.5rem; - } - - @media all and (max-width: 767px) { - margin: 0 -0.75rem; - } - - span { - padding: 0 0.75rem; - text-wrap: nowrap; - overflow: hidden; - text-overflow: ellipsis; - word-break: keep-all; - white-space: nowrap; - } -} - -#overlay-inline-trigger { - margin-top: 2.5rem; -} - header { .post-desc { @extend %heading; @@ -269,6 +228,7 @@ header { } } +/* TOC pannel */ #toc-wrapper { border-left: 1px solid rgba(158, 158, 158, 0.17); position: -webkit-sticky; @@ -331,6 +291,115 @@ header { } } +/* --- TOC button, bar and popup in mobile/tablet --- */ + +#toc-bar { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1; + margin: 0 -1rem; + height: $topbar-height; + background: var(--main-bg); + border-bottom: 1px solid var(--main-border-color); + transition: all 0.2s ease-in-out; + + .label { + @extend %heading; + + margin-left: 0.25rem; + padding: 0 0.75rem; + color: inherit; + } + + &.invisible { + top: -$topbar-height; + transition: none; + } +} + +#toc-solo-trigger { + color: var(--link-color); + border-radius: $radius-sm; + + .label { + font-size: 1rem; + font-family: $font-family-heading; + } + + &:hover { + box-shadow: none; + background: none; + } +} + +#toc-popup { + border-color: var(--main-border-color); + border-width: 1px; + border-radius: $radius-lg; + color: var(--text-color); + background: var(--main-bg); + margin-top: $topbar-height; + min-width: 20rem; + font-size: 1.05rem; + + @media all and (min-width: 850px) { + left: $sidebar-width; + } + + .header { + position: -webkit-sticky; + position: sticky; + top: 0; + background-color: inherit; + border-bottom: 1px solid var(--main-border-color); + + .label { + font-family: $font-family-heading; + color: var(--text-muted-color); + max-width: 25rem; + } + } + + button:focus-visible { + box-shadow: none; + } + + ul { + list-style-type: none; + padding-left: 1.25rem; + + li { + line-height: 1.5rem; + margin-top: 0.75rem; + } + } + + .toc-link::before { + display: none; + } + + .is-active-link { + color: var(--toc-highlight) !important; + font-weight: 600; + } + + &::-webkit-backdrop { + -webkit-backdrop-filter: blur(5px); + backdrop-filter: blur(5px); + } + + &::backdrop { + -webkit-backdrop-filter: blur(5px); + backdrop-filter: blur(5px); + } + + #toc-popup-content { + overflow: auto; + max-height: calc(100vh - 4 * $topbar-height); + } +} + /* --- Related Posts --- */ #related-posts { @@ -383,33 +452,6 @@ header { } } -#overlay { - border-color: var(--btn-border-color); - border-width: 1px; - border-radius: 0.5rem; - height: 90%; - - h2 { - margin: unset; - } - - #toc-wrapper { - margin-top: 2rem; - top: unset; - transition: none; - animation: none; - - h2 { - margin-top: unset; - display: none; - } - } -} - -dialog::backdrop { - backdrop-filter: blur(5px); -} - @media all and (max-width: 576px) { .post-tail-bottom { flex-wrap: wrap-reverse !important; @@ -437,12 +479,17 @@ dialog::backdrop { } } -@media all and (min-width: 1200px) { - #post-topbar { - display: none !important; +@media all and (max-width: 1199px) { + h2, + h3, + h4 { + scroll-margin-top: 3.5rem; } +} - #overlay-inline-trigger { +@media all and (min-width: 1200px) { + #toc-bar, + #toc-solo-trigger { display: none !important; } } From 846b73bd517afc856eb85e01719f06f91cc53b95 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Mon, 7 Oct 2024 19:03:11 +0800 Subject: [PATCH 03/17] chore: ensure invalid popup is closed Co-authored-by: Alexander Fuks Signed-off-by: Cotes Chung <11371340+cotes2020@users.noreply.github.com> --- _javascript/modules/components/toc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js index a33829fca47..d187f5bfb28 100644 --- a/_javascript/modules/components/toc.js +++ b/_javascript/modules/components/toc.js @@ -5,6 +5,7 @@ const mediaQuery = matchMedia('(min-width: 1200px)'); function refresh() { if (mediaQuery.matches) { + mobile.hide(); desktop.refresh(); } else { mobile.refresh(); From e04c0b26110aa06c9ecff33454da20736389c5dc Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Mon, 7 Oct 2024 19:25:55 +0800 Subject: [PATCH 04/17] refactor: code style --- _javascript/modules/components/toc.js | 2 +- .../modules/components/toc/toc-desktop.js | 10 +++----- .../modules/components/toc/toc-mobile.js | 24 +++++++++---------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js index d187f5bfb28..083e7158a38 100644 --- a/_javascript/modules/components/toc.js +++ b/_javascript/modules/components/toc.js @@ -5,7 +5,7 @@ const mediaQuery = matchMedia('(min-width: 1200px)'); function refresh() { if (mediaQuery.matches) { - mobile.hide(); + mobile.hidePopup(); desktop.refresh(); } else { mobile.refresh(); diff --git a/_javascript/modules/components/toc/toc-desktop.js b/_javascript/modules/components/toc/toc-desktop.js index 6fd636e295b..5021a72a082 100644 --- a/_javascript/modules/components/toc/toc-desktop.js +++ b/_javascript/modules/components/toc/toc-desktop.js @@ -11,16 +11,12 @@ export class TocDesktop { }; static refresh() { - tocbot.refresh(TocDesktop.options); + tocbot.refresh(this.options); } static init() { - const wrapper = document.getElementById('toc-wrapper'); - - if (wrapper === null) { - return; + if (document.getElementById('toc-wrapper')) { + tocbot.init(this.options); } - - tocbot.init(TocDesktop.options); } } diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index 0c0d2ed70eb..06b16ea0a54 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -22,7 +22,7 @@ export class TocMobile { orderedList: false, scrollSmooth: false, collapseDepth: 4, - headingsOffset: TocMobile.barHeight + headingsOffset: this.barHeight }; static initBar() { @@ -36,26 +36,26 @@ export class TocMobile { tocBar.classList.toggle('invisible', entry.isIntersecting); }); }, - { rootMargin: `-${TocMobile.barHeight}px 0px 0px 0px` } + { rootMargin: `-${this.barHeight}px 0px 0px 0px` } ); observer.observe(soloTrigger); } static refresh() { - if (!TocMobile.isVisible) { - TocMobile.initComponents(); + if (!this.isVisible) { + this.initComponents(); } tocbot.refresh(this.options); } - static show() { + static showPopup() { TocMobile.setScrollEnabled(false); popup.showModal(); } - static hide() { + static hidePopup() { TocMobile.setScrollEnabled(true); popup.close(); } @@ -66,20 +66,20 @@ export class TocMobile { } static initComponents() { - TocMobile.initBar(); + this.initBar(); [...triggers].forEach((trigger) => { - trigger.addEventListener('click', TocMobile.show); + trigger.addEventListener('click', this.showPopup); }); - popup?.addEventListener('click', TocMobile.hide); - btnClose?.addEventListener('click', TocMobile.hide); + popup?.addEventListener('click', this.hidePopup); + btnClose?.addEventListener('click', this.hidePopup); - TocMobile.isVisible = true; + this.isVisible = true; } static init() { tocbot.init(this.options); - TocMobile.initComponents(); + this.initComponents(); } } From c430a033c85424f4760fd03885aad99afdd775a3 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:16:56 +0800 Subject: [PATCH 05/17] chore: allow popup to be dismissed by the ESC key Co-authored-by: Alexander Fuks Signed-off-by: Cotes Chung <11371340+cotes2020@users.noreply.github.com> --- _javascript/modules/components/toc/toc-mobile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index 06b16ea0a54..ff50bfccdc2 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -73,6 +73,7 @@ export class TocMobile { }); popup?.addEventListener('click', this.hidePopup); + popup?.addEventListener('cancel', this.hidePopup); btnClose?.addEventListener('click', this.hidePopup); this.isVisible = true; From 95d58c42e1084eef66387fd38e77b335a2eb3dae Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:24:36 +0800 Subject: [PATCH 06/17] chore: center the active item in the popup --- _javascript/modules/components/toc/toc-mobile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index ff50bfccdc2..5b135e1025a 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -53,6 +53,8 @@ export class TocMobile { static showPopup() { TocMobile.setScrollEnabled(false); popup.showModal(); + const activeItem = popup.querySelector('li.is-active-li'); + activeItem.scrollIntoView({ block: 'center' }); } static hidePopup() { From fed32498998031e172905ac60461903f4f01b61c Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:28:16 +0800 Subject: [PATCH 07/17] refactor: simplify scss --- _sass/addon/module.scss | 2 +- _sass/layout/post.scss | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/_sass/addon/module.scss b/_sass/addon/module.scss index 3573b86a397..a225c78d24e 100644 --- a/_sass/addon/module.scss +++ b/_sass/addon/module.scss @@ -8,7 +8,7 @@ color: var(--heading-color); font-weight: 400; font-family: $font-family-heading; - scroll-margin-top: 2rem; + scroll-margin-top: 3.5rem; } %anchor { diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 2447098fda2..7ae4714e039 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -228,7 +228,7 @@ header { } } -/* TOC pannel */ +/* TOC panel */ #toc-wrapper { border-left: 1px solid rgba(158, 158, 158, 0.17); position: -webkit-sticky; @@ -479,15 +479,13 @@ header { } } -@media all and (max-width: 1199px) { +@media all and (min-width: 1200px) { h2, h3, h4 { - scroll-margin-top: 3.5rem; + scroll-margin-top: 2rem; } -} -@media all and (min-width: 1200px) { #toc-bar, #toc-solo-trigger { display: none !important; From 232dc513fe6b2c738db885e8fc24c5ff06434fcc Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:09:53 +0800 Subject: [PATCH 08/17] refactor: simplify JS --- _javascript/modules/components/toc.js | 10 ++--- .../modules/components/toc/toc-mobile.js | 41 +++++++++---------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/_javascript/modules/components/toc.js b/_javascript/modules/components/toc.js index 083e7158a38..765336ac2db 100644 --- a/_javascript/modules/components/toc.js +++ b/_javascript/modules/components/toc.js @@ -1,10 +1,10 @@ import { TocMobile as mobile } from './toc/toc-mobile'; import { TocDesktop as desktop } from './toc/toc-desktop'; -const mediaQuery = matchMedia('(min-width: 1200px)'); +const desktopMode = matchMedia('(min-width: 1200px)'); -function refresh() { - if (mediaQuery.matches) { +function refresh(e) { + if (e.matches) { mobile.hidePopup(); desktop.refresh(); } else { @@ -18,13 +18,13 @@ function init() { } // Avoid create multiple instances of Tocbot. Ref: - if (mediaQuery.matches) { + if (desktopMode.matches) { desktop.init(); } else { mobile.init(); } - mediaQuery.addEventListener('change', refresh); + desktopMode.onchange = refresh; } export { init as initToc }; diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index 5b135e1025a..344b6bdccbc 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -1,16 +1,16 @@ -/* - TOC topbar and popup for mobile/tablet +/** + * TOC button, topbar and popup for mobile devices */ -const tocBar = document.getElementById('toc-bar'); -const soloTrigger = document.getElementById('toc-solo-trigger'); -const triggers = document.getElementsByClassName('toc-trigger'); +const $tocBar = document.getElementById('toc-bar'); +const $soloTrigger = document.getElementById('toc-solo-trigger'); +const $triggers = document.getElementsByClassName('toc-trigger'); -const popup = document.getElementById('toc-popup'); -const btnClose = document.getElementById('toc-popup-close'); +const $popup = document.getElementById('toc-popup'); +const $btnClose = document.getElementById('toc-popup-close'); export class TocMobile { - static isVisible = false; + static invisible = true; static FROZEN = 'overflow-hidden'; static barHeight = 16 * 3; // 3rem @@ -26,24 +26,24 @@ export class TocMobile { }; static initBar() { - if (tocBar === null) { + if ($tocBar === null) { return; } const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { - tocBar.classList.toggle('invisible', entry.isIntersecting); + $tocBar.classList.toggle('invisible', entry.isIntersecting); }); }, { rootMargin: `-${this.barHeight}px 0px 0px 0px` } ); - observer.observe(soloTrigger); + observer.observe($soloTrigger); } static refresh() { - if (!this.isVisible) { + if (this.invisible) { this.initComponents(); } @@ -52,14 +52,14 @@ export class TocMobile { static showPopup() { TocMobile.setScrollEnabled(false); - popup.showModal(); - const activeItem = popup.querySelector('li.is-active-li'); + $popup.showModal(); + const activeItem = $popup.querySelector('li.is-active-li'); activeItem.scrollIntoView({ block: 'center' }); } static hidePopup() { TocMobile.setScrollEnabled(true); - popup.close(); + $popup.close(); } static setScrollEnabled(enabled) { @@ -70,15 +70,12 @@ export class TocMobile { static initComponents() { this.initBar(); - [...triggers].forEach((trigger) => { - trigger.addEventListener('click', this.showPopup); + [...$triggers].forEach((trigger) => { + trigger.onclick = this.showPopup; }); - popup?.addEventListener('click', this.hidePopup); - popup?.addEventListener('cancel', this.hidePopup); - btnClose?.addEventListener('click', this.hidePopup); - - this.isVisible = true; + $popup.onclick = $popup.oncancel = $btnClose.onclick = this.hidePopup; + this.invisible = !this.invisible; } static init() { From 9da53891e4dc210432b8e346cd3d8e7a342a6cb5 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:14:04 +0800 Subject: [PATCH 09/17] chore: improve popup layout --- _layouts/post.html | 2 +- _sass/colors/typography-dark.scss | 2 +- _sass/colors/typography-light.scss | 2 +- _sass/layout/post.scss | 14 ++++++++++---- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/_layouts/post.html b/_layouts/post.html index 6042db13e95..9d7d497cb48 100644 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -112,7 +112,7 @@

{{ page.title }}

-
{{- page.title -}}
+
{{- page.title -}}
diff --git a/_sass/colors/typography-dark.scss b/_sass/colors/typography-dark.scss index 12427ec494a..664c93653e6 100644 --- a/_sass/colors/typography-dark.scss +++ b/_sass/colors/typography-dark.scss @@ -22,7 +22,6 @@ --btn-border-color: #2e2f31; --btn-backtotop-color: var(--text-color); --btn-backtotop-border-color: #212122; - --btn-box-shadow: var(--main-bg); --card-header-bg: #292929; --checkbox-color: rgb(118, 120, 121); --checkbox-checked-color: var(--link-color); @@ -60,6 +59,7 @@ /* Posts */ --toc-highlight: rgb(116, 178, 243); + --toc-popup-border-color: #373737; --tag-hover: rgb(43, 56, 62); --tb-odd-bg: #252526; /* odd rows of the posts' table */ --tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */ diff --git a/_sass/colors/typography-light.scss b/_sass/colors/typography-light.scss index 78000746925..b6fc5618ad5 100644 --- a/_sass/colors/typography-light.scss +++ b/_sass/colors/typography-light.scss @@ -22,7 +22,6 @@ --btn-border-color: #e9ecef; --btn-backtotop-color: #686868; --btn-backtotop-border-color: #f1f1f1; - --btn-box-shadow: #eaeaea; --checkbox-color: #c5c5c5; --checkbox-checked-color: #07a8f7; --img-bg: radial-gradient( @@ -63,6 +62,7 @@ /* Posts */ --toc-highlight: #0550ae; + --toc-popup-border-color: lightgray; --btn-share-color: gray; --btn-share-hover-color: #0d6efd; --card-bg: white; diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 7ae4714e039..49dd55eb0b3 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -319,8 +319,9 @@ header { } #toc-solo-trigger { - color: var(--link-color); - border-radius: $radius-sm; + color: var(--text-muted-color); + border-color: var(--btn-border-color); + border-radius: $radius-lg; .label { font-size: 1rem; @@ -334,7 +335,7 @@ header { } #toc-popup { - border-color: var(--main-border-color); + border-color: var(--toc-popup-border-color); border-width: 1px; border-radius: $radius-lg; color: var(--text-color); @@ -356,7 +357,6 @@ header { .label { font-family: $font-family-heading; - color: var(--text-muted-color); max-width: 25rem; } } @@ -373,6 +373,12 @@ header { line-height: 1.5rem; margin-top: 0.75rem; } + + ul { + border-left: 1px solid var(--main-border-color); + margin-left: 2px; + padding-left: calc(1.25rem - 3px); + } } .toc-link::before { From 3a862b9ccc54528b1c088abfeddcc7aef6fad1eb Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:35:40 +0800 Subject: [PATCH 10/17] chore: fix incorrect use of btn color --- _sass/addon/commons.scss | 6 ++---- _sass/addon/module.scss | 6 ++++++ _sass/layout/post.scss | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/_sass/addon/commons.scss b/_sass/addon/commons.scss index 1415287b12a..e332f21336a 100644 --- a/_sass/addon/commons.scss +++ b/_sass/addon/commons.scss @@ -604,10 +604,6 @@ main { } } -button i { - color: #999999; -} - /* --- Effects classes --- */ .flex-grow-1 { @@ -912,6 +908,8 @@ $btn-mb: 0.5rem; } #topbar { + @extend %btn-color; + #breadcrumb { font-size: 1rem; color: var(--text-muted-color); diff --git a/_sass/addon/module.scss b/_sass/addon/module.scss index a225c78d24e..34ac67b95ea 100644 --- a/_sass/addon/module.scss +++ b/_sass/addon/module.scss @@ -135,6 +135,12 @@ } } +%btn-color { + button i { + color: #999999; + } +} + /* ---------- scss mixin --------- */ @mixin mt-mb($value) { diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 49dd55eb0b3..fa170ebec92 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -304,6 +304,8 @@ header { border-bottom: 1px solid var(--main-border-color); transition: all 0.2s ease-in-out; + @extend %btn-color; + .label { @extend %heading; @@ -349,6 +351,8 @@ header { } .header { + @extend %btn-color; + position: -webkit-sticky; position: sticky; top: 0; From d160027b1e3a60bbaa8f660fd8f9566be625e91d Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 07:25:24 +0800 Subject: [PATCH 11/17] fix: avoid overcalling function --- _javascript/modules/components/toc/toc-mobile.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index 344b6bdccbc..c9723707548 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -58,6 +58,9 @@ export class TocMobile { } static hidePopup() { + if (!$popup.open) { + return; + } TocMobile.setScrollEnabled(true); $popup.close(); } From 677cda8fe54f81aa43b4ad869657f51341c331c2 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 07:30:01 +0800 Subject: [PATCH 12/17] fix: close popup on backdrop click --- .../modules/components/toc/toc-mobile.js | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index c9723707548..58f7b9ce2c2 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -5,13 +5,13 @@ const $tocBar = document.getElementById('toc-bar'); const $soloTrigger = document.getElementById('toc-solo-trigger'); const $triggers = document.getElementsByClassName('toc-trigger'); - const $popup = document.getElementById('toc-popup'); const $btnClose = document.getElementById('toc-popup-close'); +const SCROLL_LOCK = 'overflow-hidden'; + export class TocMobile { static invisible = true; - static FROZEN = 'overflow-hidden'; static barHeight = 16 * 3; // 3rem static options = { @@ -26,10 +26,6 @@ export class TocMobile { }; static initBar() { - if ($tocBar === null) { - return; - } - const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { @@ -40,18 +36,26 @@ export class TocMobile { ); observer.observe($soloTrigger); + this.invisible = false; + } + + static listenAnchors() { + const $anchors = document.getElementsByClassName('toc-link'); + [...$anchors].forEach((anchor) => { + anchor.onclick = this.hidePopup; + }); } static refresh() { if (this.invisible) { this.initComponents(); } - tocbot.refresh(this.options); + this.listenAnchors(); } static showPopup() { - TocMobile.setScrollEnabled(false); + TocMobile.lockScroll(true); $popup.showModal(); const activeItem = $popup.querySelector('li.is-active-li'); activeItem.scrollIntoView({ block: 'center' }); @@ -61,13 +65,25 @@ export class TocMobile { if (!$popup.open) { return; } - TocMobile.setScrollEnabled(true); $popup.close(); + TocMobile.lockScroll(false); } - static setScrollEnabled(enabled) { - document.documentElement.classList.toggle(this.FROZEN, !enabled); - document.body.classList.toggle(this.FROZEN, !enabled); + static lockScroll(enable) { + document.documentElement.classList.toggle(SCROLL_LOCK, enable); + document.body.classList.toggle(SCROLL_LOCK, enable); + } + + static clickBackdrop(event) { + const rect = event.target.getBoundingClientRect(); + if ( + event.clientX < rect.left || + event.clientX > rect.right || + event.clientY < rect.top || + event.clientY > rect.bottom + ) { + TocMobile.hidePopup(); + } } static initComponents() { @@ -77,12 +93,13 @@ export class TocMobile { trigger.onclick = this.showPopup; }); - $popup.onclick = $popup.oncancel = $btnClose.onclick = this.hidePopup; - this.invisible = !this.invisible; + $popup.onclick = this.clickBackdrop; + $btnClose.onclick = $popup.oncancel = this.hidePopup; } static init() { tocbot.init(this.options); + this.listenAnchors(); this.initComponents(); } } From 2c747e7c2364b89184f1812b71bfa3a5de1853a2 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 07:43:49 +0800 Subject: [PATCH 13/17] feat: add animation to toc popup --- _sass/layout/post.scss | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index fa170ebec92..a3c9b35b1d8 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -228,6 +228,26 @@ header { } } +@mixin slide-down { + from { + opacity: 0.7; + transform: translateY(-$topbar-height); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@-webkit-keyframes slide-down { + @include slide-down; +} + +@keyframes slide-down { + @include slide-down; +} + /* TOC panel */ #toc-wrapper { border-left: 1px solid rgba(158, 158, 158, 0.17); @@ -337,6 +357,8 @@ header { } #toc-popup { + $anmimation: slide-down 0.3s ease-out; + border-color: var(--toc-popup-border-color); border-width: 1px; border-radius: $radius-lg; @@ -345,6 +367,8 @@ header { margin-top: $topbar-height; min-width: 20rem; font-size: 1.05rem; + -webkit-animation: $anmimation; + animation: $anmimation; @media all and (min-width: 850px) { left: $sidebar-width; From 80489e06d86b801fcfd0b518eb9b9e248d5de1ee Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:43:32 +0800 Subject: [PATCH 14/17] feat: increase anchors click area --- _layouts/post.html | 4 ++-- _sass/layout/post.scss | 29 +++++++++++++++++++---------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/_layouts/post.html b/_layouts/post.html index 9d7d497cb48..c34ba68815f 100644 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -111,13 +111,13 @@

{{ page.title }}

-
+
{{- page.title -}}
-
+
{% endif %} diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index a3c9b35b1d8..01f5094bfd0 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -395,22 +395,31 @@ header { ul { list-style-type: none; - padding-left: 1.25rem; + padding-left: 0; li { - line-height: 1.5rem; - margin-top: 0.75rem; - } + ul, + & + li { + margin-top: 0.25rem; + } - ul { - border-left: 1px solid var(--main-border-color); - margin-left: 2px; - padding-left: calc(1.25rem - 3px); + a { + display: flex; + line-height: 1.5; + padding: 0.375rem 0; + padding-right: 1.125rem; + + &.toc-link::before { + display: none; + } + } } } - .toc-link::before { - display: none; + @for $i from 2 through 4 { + .node-name--H#{$i} { + padding-left: 1.125rem * ($i - 1); + } } .is-active-link { From ec97b2dc8c6b9655e2f249b30801d36c21271b50 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 19:52:59 +0800 Subject: [PATCH 15/17] feat: add animation to closing popup --- .../modules/components/toc/toc-mobile.js | 14 +++- _sass/layout/post.scss | 75 +++++++++++++------ 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/_javascript/modules/components/toc/toc-mobile.js b/_javascript/modules/components/toc/toc-mobile.js index 58f7b9ce2c2..48b372df443 100644 --- a/_javascript/modules/components/toc/toc-mobile.js +++ b/_javascript/modules/components/toc/toc-mobile.js @@ -9,6 +9,7 @@ const $popup = document.getElementById('toc-popup'); const $btnClose = document.getElementById('toc-popup-close'); const SCROLL_LOCK = 'overflow-hidden'; +const CLOSING = 'closing'; export class TocMobile { static invisible = true; @@ -65,7 +66,18 @@ export class TocMobile { if (!$popup.open) { return; } - $popup.close(); + + $popup.toggleAttribute(CLOSING); + + $popup.addEventListener( + 'animationend', + () => { + $popup.toggleAttribute(CLOSING); + $popup.close(); + }, + { once: true } + ); + TocMobile.lockScroll(false); } diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 01f5094bfd0..37bc2274f61 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -228,26 +228,6 @@ header { } } -@mixin slide-down { - from { - opacity: 0.7; - transform: translateY(-$topbar-height); - } - - to { - opacity: 1; - transform: translateY(0); - } -} - -@-webkit-keyframes slide-down { - @include slide-down; -} - -@keyframes slide-down { - @include slide-down; -} - /* TOC panel */ #toc-wrapper { border-left: 1px solid rgba(158, 158, 158, 0.17); @@ -356,8 +336,49 @@ header { } } +@mixin slide-in { + from { + opacity: 0.7; + transform: translateY(-$topbar-height); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@mixin slide-out { + 0% { + transform: translateY(0); + opacity: 1; + } + + 100% { + transform: translateY(-$topbar-height); + opacity: 0; + } +} + +@-webkit-keyframes slide-in { + @include slide-in; +} + +@keyframes slide-in { + @include slide-in; +} + +@-webkit-keyframes slide-out { + @include slide-out; +} + +@keyframes slide-out { + @include slide-out; +} + #toc-popup { - $anmimation: slide-down 0.3s ease-out; + $slide-in: slide-in 0.3s ease-out; + $slide-out: slide-out 0.3s ease-out; border-color: var(--toc-popup-border-color); border-width: 1px; @@ -367,8 +388,16 @@ header { margin-top: $topbar-height; min-width: 20rem; font-size: 1.05rem; - -webkit-animation: $anmimation; - animation: $anmimation; + + &[open] { + -webkit-animation: $slide-in; + animation: $slide-in; + } + + &[closing] { + -webkit-animation: $slide-out; + animation: $slide-out; + } @media all and (min-width: 850px) { left: $sidebar-width; From 47d033b0651f1bd13f1b8409b12e0f0fdfc061b3 Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:39:11 +0800 Subject: [PATCH 16/17] chore: improve popup width --- _layouts/post.html | 4 ++-- _sass/layout/post.scss | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/_layouts/post.html b/_layouts/post.html index c34ba68815f..bcc133f8559 100644 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -111,8 +111,8 @@

{{ page.title }}

-
-
{{- page.title -}}
+
+
{{- page.title -}}
diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index 37bc2274f61..bb39fbe5134 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -389,6 +389,10 @@ header { min-width: 20rem; font-size: 1.05rem; + @media all and (min-width: 576px) { + max-width: 32rem; + } + &[open] { -webkit-animation: $slide-in; animation: $slide-in; @@ -414,7 +418,6 @@ header { .label { font-family: $font-family-heading; - max-width: 25rem; } } @@ -469,6 +472,7 @@ header { #toc-popup-content { overflow: auto; max-height: calc(100vh - 4 * $topbar-height); + font-family: $font-family-heading; } } From 8957862312a24ad3a5eb6d726a09ba152917846e Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Fri, 11 Oct 2024 22:19:40 +0800 Subject: [PATCH 17/17] feat: add curtain to toc popup --- _sass/layout/post.scss | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/_sass/layout/post.scss b/_sass/layout/post.scss index bb39fbe5134..be727006b62 100644 --- a/_sass/layout/post.scss +++ b/_sass/layout/post.scss @@ -379,6 +379,7 @@ header { #toc-popup { $slide-in: slide-in 0.3s ease-out; $slide-out: slide-out 0.3s ease-out; + $curtain-height: 2rem; border-color: var(--toc-popup-border-color); border-width: 1px; @@ -469,10 +470,19 @@ header { backdrop-filter: blur(5px); } + &::after { + display: flex; + content: ''; + position: relative; + background: linear-gradient(transparent, var(--main-bg) 70%); + height: $curtain-height; + } + #toc-popup-content { overflow: auto; max-height: calc(100vh - 4 * $topbar-height); font-family: $font-family-heading; + margin-bottom: -$curtain-height; } }