11// https://vitepress.dev/guide/custom-theme
2- import { render , h } from "vue" ;
2+ import { render , h , onMounted } from "vue" ;
33import type { Theme } from "vitepress" ;
44import DefaultTheme from "vitepress/theme" ;
55import BackToTop from "../../../components/internal/BackToTop.vue" ;
@@ -24,6 +24,10 @@ import "@nolebase/vitepress-plugin-inline-link-preview/client/style.css";
2424import "@nolebase/vitepress-plugin-highlight-targeted-heading/client/style.css" ;
2525import "@nolebase/vitepress-plugin-enhanced-mark/client/style.css" ;
2626
27+ import { OverlayScrollbars } from "overlayscrollbars" ;
28+ import "overlayscrollbars/overlayscrollbars.css" ;
29+ import "./overlayscrollbars.css" ;
30+
2731function addBackTotop ( ) {
2832 render (
2933 h ( BackToTop , {
@@ -33,6 +37,78 @@ function addBackTotop() {
3337 ) ;
3438}
3539
40+ function initCustomScrollbar ( ) {
41+ const bodyElement = document . querySelector ( "body" ) ;
42+ if ( ! bodyElement ) return ;
43+
44+ OverlayScrollbars (
45+ { target : bodyElement , cancel : { nativeScrollbarsOverlaid : true } } ,
46+ {
47+ scrollbars : {
48+ theme : "os-theme-dark" ,
49+ autoHide : "move" ,
50+ autoHideDelay : 500 ,
51+ autoHideSuspend : false ,
52+ } ,
53+ } ,
54+ ) ;
55+
56+ const markInitialized = ( el : HTMLElement ) =>
57+ el . setAttribute ( "data-scrollbar-initialized" , "true" ) ;
58+ const isInitialized = ( el : Element ) =>
59+ ( el as HTMLElement ) . hasAttribute ( "data-scrollbar-initialized" ) ;
60+
61+ const initOnElement = ( el : HTMLElement ) => {
62+ if ( isInitialized ( el ) ) return ;
63+
64+ if ( el . classList . contains ( "VPSidebar" ) ) {
65+ OverlayScrollbars (
66+ { target : el , cancel : { nativeScrollbarsOverlaid : true } } ,
67+ {
68+ scrollbars : {
69+ theme : "os-theme-dark" ,
70+ autoHide : "leave" ,
71+ autoHideDelay : 500 ,
72+ autoHideSuspend : false ,
73+ } ,
74+ } ,
75+ ) ;
76+ markInitialized ( el ) ;
77+ }
78+ } ;
79+
80+ const observer = new IntersectionObserver (
81+ ( entries , obs ) => {
82+ entries . forEach ( ( entry ) => {
83+ if ( entry . isIntersecting ) {
84+ initOnElement ( entry . target as HTMLElement ) ;
85+ obs . unobserve ( entry . target ) ;
86+ }
87+ } ) ;
88+ } ,
89+ { root : null , rootMargin : "100px" , threshold : 0.1 } ,
90+ ) ;
91+
92+ document
93+ . querySelectorAll < HTMLElement > ( ".VPSidebar" )
94+ . forEach ( ( el ) => observer . observe ( el ) ) ;
95+
96+ const mo = new MutationObserver ( ( mutations ) => {
97+ for ( const m of mutations ) {
98+ m . addedNodes . forEach ( ( n ) => {
99+ if ( ! ( n instanceof HTMLElement ) ) return ;
100+ if ( n . matches ( ".VPSidebar" ) && ! isInitialized ( n ) ) {
101+ observer . observe ( n ) ;
102+ }
103+ n . querySelectorAll < HTMLElement > ( ".VPSidebar" ) . forEach ( ( el ) => {
104+ if ( ! isInitialized ( el ) ) observer . observe ( el ) ;
105+ } ) ;
106+ } ) ;
107+ }
108+ } ) ;
109+ mo . observe ( document . documentElement , { childList : true , subtree : true } ) ;
110+ }
111+
36112function addDetailsAnimation ( ) {
37113 document
38114 . querySelectorAll ( "details" )
@@ -42,6 +118,9 @@ function addDetailsAnimation() {
42118export default {
43119 extends : DefaultTheme ,
44120 Layout : ( ) => {
121+ onMounted ( ( ) => {
122+ initCustomScrollbar ( ) ;
123+ } ) ;
45124 return h ( DefaultTheme . Layout , null , {
46125 // https://vitepress.dev/guide/extending-default-theme#layout-slots
47126 "nav-bar-content-after" : ( ) => h ( NolebaseEnhancedReadabilitiesMenu ) ,
0 commit comments