Skip to content

Commit

Permalink
feat: support prev and next page in doc footer
Browse files Browse the repository at this point in the history
  • Loading branch information
sanyuan0704 committed Sep 19, 2022
1 parent bb779e6 commit 6150d39
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 16 deletions.
5 changes: 4 additions & 1 deletion docs/.island/config.ts
Expand Up @@ -12,7 +12,10 @@ export default defineConfig({
}
],
lastUpdatedText: 'Last Updated',
editLink: '',
editLink: {
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
text: '📝 Edit this page on GitHub'
},
nav: [
{
text: 'Guide',
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/islands-arch.md
Expand Up @@ -12,7 +12,7 @@ As the name suggests, we can image the whole page as a sea of static, and the in

Then the hydration process will only be executed on the islands, which will make the first page load performance and TTI(time to interactive) better because it only needs partial client script that is correspond to the interactive parts.

## How to implement it?
## Implement details in Island.js

The implementation of this architecture includes three parts: `server runtime``build time` and `client runtime`.

Expand All @@ -28,7 +28,7 @@ export function Layout() {
}
```

You only need to add a `__island` prop to the component anywhere you use it, and then the component will automatically be identified as a island component.Then, island.js will only inject the client script of this component and its props when it is rendered on the client.
You only need to add a `__island` prop to the component when you use it, and then the component will automatically be identified as a island component.Island.js will only inject the client script of island components as well as their props when they are rendered on the client.

### Internal Implement

Expand Down
5 changes: 5 additions & 0 deletions src/runtime/app.tsx
Expand Up @@ -15,15 +15,20 @@ export async function waitForApp(path: string): Promise<PageData> {
// Preload route component
const mod = await (matched[0].route as Route).preload();
const pagePath = cleanUrl((matched[0].route as Route).filePath);
const relativePagePath = pagePath
.replace(siteData.base, '')
.replace(/^\//, '');
return {
siteData,
pagePath,
relativePagePath,
...omit(mod, ['default'])
} as PageData;
} else {
return {
siteData,
pagePath: '',
relativePagePath: '',
pageType: '404'
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/shared/types/default-theme.ts
Expand Up @@ -167,14 +167,14 @@ export namespace DefaultTheme {
*
* @default 'Previous page'
*/
prev?: string;
prev?: SidebarItem;

/**
* Custom label for next page button.
*
* @default 'Next page'
*/
next?: string;
next?: SidebarItem;
}

// social link ---------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/shared/types/index.ts
Expand Up @@ -151,6 +151,7 @@ export interface PageModule<T extends ComponentType<any>> {
export interface PageData {
siteData: SiteData<DefaultTheme.Config>;
pagePath: string;
relativePagePath: string;
title?: string;
description?: string;
pageType: 'home' | 'doc' | 'custom' | '404';
Expand Down
7 changes: 5 additions & 2 deletions src/shared/types/type.d.ts
Expand Up @@ -16,13 +16,15 @@ declare module 'island/theme*' {
}

declare module 'island:site-data' {
export default any;
import { SiteData } from 'shared/types';
const siteData: SiteData;
export default siteData;
}


declare module 'island/client' {
import { ComponentType } from 'react';
import { PageData } from 'shared/types';
import { PageData, SiteData } from 'shared/types';

export const Content: ComponentType<any>;
export const usePageData: () => PageData;
Expand All @@ -41,6 +43,7 @@ declare module 'island/jsx-runtime' {

declare module 'virtual:routes' {
import { Route } from 'react-router-dom';
import { SiteData } from './index';

export const routes: Route[];
}
Expand Down
34 changes: 26 additions & 8 deletions src/theme-default/components/DocFooter/index.tsx
@@ -1,14 +1,18 @@
import styles from './index.module.scss';
import { usePageData } from 'island/client';
import { usePrevNextPage } from '../../logic';

export function DocFooter() {
const { siteData } = usePageData();
const { prevPage, nextPage } = usePrevNextPage(siteData);
const { editLink } = siteData?.themeConfig || {};
return (
<footer className={styles.footer}>
<div className={styles.editInfo}>
<div className={styles.editLink}>
<button className={styles.editLinkButton}>Edit Link</button>
<a className={styles.editLinkButton} href={editLink?.pattern}>
{editLink?.text}
</a>
</div>
<div className={styles.lastUpdated}>
{editLink ? (
Expand All @@ -24,14 +28,28 @@ export function DocFooter() {

<div className={styles.prevNext}>
<div className={styles.pager}>
<a href="/" className={`${styles.pagerLink} ${styles.prev}`}>
<span className={styles.desc}>Previous Page</span>
</a>
{prevPage ? (
<a
href={prevPage.link}
className={`${styles.pagerLink} ${styles.prev}`}
>
<span className={styles.desc}>Previous Page</span>
<span className={styles.title}>{prevPage.text}</span>
</a>
) : null}
</div>
<div className={`${styles.pager} ${styles.hasNext}`}>
<a href="/" className={`${styles.pagerLink} ${styles.next}`}>
<span className={styles.desc}>Next Page</span>
</a>
<div className={styles.pager}>
{nextPage ? (
<div className={`${styles.hasNext}`}>
<a
href={nextPage.link}
className={`${styles.pagerLink} ${styles.next}`}
>
<span className={styles.desc}>Next Page</span>
<span className={styles.title}>{nextPage.text}</span>
</a>
</div>
) : null}
</div>
</div>
</footer>
Expand Down
1 change: 1 addition & 0 deletions src/theme-default/logic/index.ts
Expand Up @@ -16,3 +16,4 @@ export function normalizeHref(url?: string) {
}

export { useAsideAnchor } from './useAsideAnchor';
export { usePrevNextPage } from './usePrevNextPage';
1 change: 0 additions & 1 deletion src/theme-default/logic/useAsideAnchor.ts
Expand Up @@ -35,7 +35,6 @@ export function useAsideAnchor(
}
// Util function to set dom ref after determining the active link
const activate = (links: NodeListOf<HTMLAnchorElement>, index: number) => {
console.log('current active: ', index);
if (prevActiveLinkRef.current) {
prevActiveLinkRef.current.classList.remove('aside-active');
}
Expand Down
35 changes: 35 additions & 0 deletions src/theme-default/logic/usePrevNextPage.ts
@@ -0,0 +1,35 @@
import { DefaultTheme, SiteData } from 'shared/types';
import { useLocation } from 'react-router-dom';

export function usePrevNextPage(siteData: SiteData<DefaultTheme.Config>) {
const themeConfig = siteData.themeConfig || {};
const sidebar = themeConfig.sidebar || [];
const flattenTitles: DefaultTheme.SidebarItem[] = [];
const { pathname } = useLocation();

const walkThroughSidebar = (sidebar: DefaultTheme.Sidebar) => {
if (Array.isArray(sidebar)) {
sidebar.forEach((sidebarGroup) => {
sidebarGroup.items.forEach((item) => {
flattenTitles.push(item);
});
});
} else {
Object.keys(sidebar).forEach((key) => {
walkThroughSidebar(sidebar[key]);
});
}
};

walkThroughSidebar(sidebar);

const pageIndex = flattenTitles.findIndex((item) => item.link === pathname);

const prevPage = flattenTitles[pageIndex - 1] || null;
const nextPage = flattenTitles[pageIndex + 1] || null;

return {
prevPage,
nextPage
};
}

0 comments on commit 6150d39

Please sign in to comment.