diff --git a/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx b/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx
index 556cb32dc..e9076e4ad 100644
--- a/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx
+++ b/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx
@@ -5,15 +5,13 @@ import React from 'react';
import { Footer } from '@/components/Footer';
import { Header, HeaderLogo } from '@/components/Header';
import { SearchButton, SearchModal } from '@/components/Search';
-import { TableOfContents } from '@/components/TableOfContents';
+import { TOCScrollContent, TableOfContents } from '@/components/TableOfContents';
import { CONTAINER_STYLE } from '@/components/layout';
import { getSpaceLanguage } from '@/intl/server';
import { t } from '@/intl/translate';
+import type { VisitorAuthClaims } from '@/lib/adaptive';
import { tcls } from '@/lib/tailwind';
-import { MobileMenuSheet } from '@/components/MobileMenu';
-import { TOCScrollContent } from '@/components/TableOfContents/TOCScrollContent';
-import type { VisitorAuthClaims } from '@/lib/adaptive';
import { GITBOOK_API_PUBLIC_URL, GITBOOK_APP_URL } from '@v2/lib/env';
import { Announcement } from '../Announcement';
import { SpacesDropdown } from '../Header/SpacesDropdown';
@@ -68,27 +66,6 @@ export function SpaceLayout(props: {
>
-
-
- ) : null
- }
- context={context}
- />
-
)
}
- innerHeader={
- // displays the search button and/or the space dropdown in the ToC according to the header/variant settings. E.g if there is no header, the search button will be displayed in the ToC.
- <>
- {!withTopHeader && (
-
-
-
-
- {t(
- getSpaceLanguage(customization),
- customization.aiSearch.enabled
- ? 'search_or_ask'
- : 'search'
- )}
- ...
-
-
-
-
- )}
- {!withTopHeader && withSections && sections && (
-
- )}
- {isMultiVariants && (
-
+
+ {!withTopHeader && (
+
+
+
+
+ {t(
+ getSpaceLanguage(customization),
+ customization.aiSearch.enabled
+ ? 'search_or_ask'
+ : 'search'
+ )}
+ ...
+
+
+
+
)}
- />
- )}
- >
- }
- />
+ {!withTopHeader && withSections && sections && (
+
+ )}
+ {isMultiVariants && (
+
+ )}
+ >
+ ) : null
+ }
+ />
+
{children}
diff --git a/packages/gitbook/src/components/TableOfContents/TOCScrollContent.tsx b/packages/gitbook/src/components/TableOfContents/TOCScrollContent.tsx
index aeddd1f56..a30bf24b3 100644
--- a/packages/gitbook/src/components/TableOfContents/TOCScrollContent.tsx
+++ b/packages/gitbook/src/components/TableOfContents/TOCScrollContent.tsx
@@ -40,8 +40,8 @@ export function TOCScrollContent(props: {
{children};
+ }
return (
<>
@@ -65,7 +72,7 @@ export function TableOfContents(props: {
)}
>
{header && header}
-
+ {children}
>
diff --git a/packages/gitbook/src/components/TableOfContents/index.ts b/packages/gitbook/src/components/TableOfContents/index.ts
index 6eeff9269..d52040bae 100644
--- a/packages/gitbook/src/components/TableOfContents/index.ts
+++ b/packages/gitbook/src/components/TableOfContents/index.ts
@@ -2,3 +2,4 @@ export { TableOfContents } from './TableOfContents';
export { PagesList } from './PagesList';
export { TOCScrollContainer } from './TOCScroller';
export { Trademark } from './Trademark';
+export { TOCScrollContent } from './TOCScrollContent';
diff --git a/packages/gitbook/src/hooks/useIsMobile.ts b/packages/gitbook/src/hooks/useIsMobile.ts
new file mode 100644
index 000000000..f2625c997
--- /dev/null
+++ b/packages/gitbook/src/hooks/useIsMobile.ts
@@ -0,0 +1,61 @@
+import { useSyncExternalStore } from 'react';
+
+/**
+ * Hook to check if the screen is mobile
+ * @default breakpoint => 1024
+ */
+export function useIsMobile(breakpoint = 1024) {
+ const store = getMediaQueryStore(breakpoint);
+
+ return useSyncExternalStore(
+ (cb) => {
+ store.subscribers.add(cb);
+ return () => store.subscribers.delete(cb);
+ },
+ () => store.isMatch,
+ () => false
+ );
+}
+
+type MediaQueryStore = {
+ /** Latest match result */
+ isMatch: boolean;
+ /** The native MediaQueryList object */
+ mediaQueryList: MediaQueryList;
+ /** React subscribers that need re-rendering on change */
+ subscribers: Set<() => void>;
+};
+
+const mediaQueryStores: Record = {};
+
+/**
+ * getMediaQueryStore("(max-width: 1024px)")
+ * Returns a singleton store for that query,
+ * creating it (and its listener) the first time.
+ */
+function getMediaQueryStore(breakpoint: number): MediaQueryStore {
+ // If the store already exists, return it
+ if (mediaQueryStores[breakpoint]) return mediaQueryStores[breakpoint];
+
+ const queryString = `(max-width: ${breakpoint - 0.1}px)`;
+ const mqList =
+ typeof window !== 'undefined' ? window.matchMedia(queryString) : ({} as MediaQueryList);
+
+ const store: MediaQueryStore = {
+ isMatch: typeof window !== 'undefined' ? mqList.matches : false,
+ mediaQueryList: mqList,
+ subscribers: new Set(),
+ };
+
+ const update = () => {
+ store.isMatch = mqList.matches;
+ store.subscribers.forEach((cb) => cb());
+ };
+
+ if (mqList.addEventListener) mqList.addEventListener('change', update);
+ // For Safari < 14
+ else if (mqList.addListener) mqList.addListener(update);
+
+ mediaQueryStores[breakpoint] = store;
+ return store;
+}