Skip to content

Commit 22943b1

Browse files
authored
Merge pull request #20 from sanyuan0704/feat/island-dev
feat: support prev and next page
2 parents f1574e9 + a96a3aa commit 22943b1

File tree

15 files changed

+158
-29
lines changed

15 files changed

+158
-29
lines changed

CHANGELOG.md

+19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11

22

3+
## [0.1.3](https://github.com/sanyuan0704/island.js/compare/0.1.2...0.1.3) (2022-09-19)
4+
5+
6+
### Bug Fixes
7+
8+
* aside marker opacity problem ([647cc47](https://github.com/sanyuan0704/island.js/commit/647cc47c6e159b36337ca999d6b8abc7ff448112))
9+
* normalize prev and next page link ([8b3f0bb](https://github.com/sanyuan0704/island.js/commit/8b3f0bbf304223130847eeb8218f57f1b265e816))
10+
* postfix in md link ([bb779e6](https://github.com/sanyuan0704/island.js/commit/bb779e63bc66dd26714d60f3f7573bf33c24aa58))
11+
* remove hard code image in hero component ([2bd354f](https://github.com/sanyuan0704/island.js/commit/2bd354f6abd0adb1de4e37764898de7a87940231))
12+
* sidebar active invalid ([4be2c90](https://github.com/sanyuan0704/island.js/commit/4be2c90d230967e534eaa1ffda7c1031246dd805))
13+
14+
15+
### Features
16+
17+
* add architecture doc ([d31999d](https://github.com/sanyuan0704/island.js/commit/d31999d3b24b6125f859d642dcd9c5bca5904683))
18+
* add configure-site doc ([f22c3a6](https://github.com/sanyuan0704/island.js/commit/f22c3a67c62bcf7053ba5abdd0aa459388c85416))
19+
* support edit link ([85248c6](https://github.com/sanyuan0704/island.js/commit/85248c62af7d3623f03e27cf53f78d272ced79e1))
20+
* support prev and next page in doc footer ([6150d39](https://github.com/sanyuan0704/island.js/commit/6150d39781539a838d0c346c7050bc373b4b6d8a))
21+
322
## [0.1.2](https://github.com/sanyuan0704/island.js/compare/0.1.1...0.1.2) (2022-09-18)
423

524

docs/.island/config.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ export default defineConfig({
1212
}
1313
],
1414
lastUpdatedText: 'Last Updated',
15-
editLink: '',
15+
editLink: {
16+
pattern:
17+
'https://github.com/sanyuan0704/island.js/tree/master/docs/:path',
18+
text: '📝 Edit this page on GitHub'
19+
},
1620
nav: [
1721
{
1822
text: 'Guide',

docs/guide/islands-arch.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ As the name suggests, we can image the whole page as a sea of static, and the in
1212

1313
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.
1414

15-
## How to implement it?
15+
## Implement details in Island.js
1616

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

@@ -28,7 +28,7 @@ export function Layout() {
2828
}
2929
```
3030

31-
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.
31+
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.
3232

3333
### Internal Implement
3434

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "islandjs",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"description": "Vite & Islands architecture SSG framework",
55
"main": "dist/index.js",
66
"packageManager": "[email protected]",

src/node/config.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ export async function resolveUserConfig(
4949
}
5050
}
5151

52-
export function resolveSiteData(userConfig: UserConfig): SiteData {
52+
export function resolveSiteData(
53+
userConfig: UserConfig,
54+
root: string
55+
): SiteData {
5356
return {
5457
lang: userConfig.lang || 'en-US',
5558
title: userConfig.title || 'Island',
@@ -59,7 +62,8 @@ export function resolveSiteData(userConfig: UserConfig): SiteData {
5962
base: userConfig.base || '/',
6063
scrollOffset: userConfig.scrollOffset || 90,
6164
locales: userConfig.locales || {},
62-
icon: userConfig.icon || ''
65+
icon: userConfig.icon || '',
66+
root
6367
};
6468
}
6569

@@ -89,7 +93,7 @@ export async function resolveConfig(
8993
tempDir: resolve(root, 'node_modules', '.island'),
9094
vite: userConfig.vite || {},
9195
allowDeadLinks: userConfig.allowDeadLinks || false,
92-
siteData: resolveSiteData(userConfig),
96+
siteData: resolveSiteData(userConfig, root),
9397
enableSpa: userConfig.enableSpa || false
9498
};
9599

src/runtime/app.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { routes } from 'virtual:routes';
33
import { matchRoutes, useLocation } from 'react-router-dom';
44
import siteData from 'island:site-data';
55
import { Route } from '../node/plugin-routes';
6-
import { cleanUrl, omit } from './utils';
6+
import { cleanUrl, getRelativePagePath, omit } from './utils';
77
import { PageData } from '../shared/types';
88
import { HelmetProvider } from 'react-helmet-async';
99
import { useContext, useLayoutEffect } from 'react';
@@ -15,15 +15,19 @@ export async function waitForApp(path: string): Promise<PageData> {
1515
// Preload route component
1616
const mod = await (matched[0].route as Route).preload();
1717
const pagePath = cleanUrl((matched[0].route as Route).filePath);
18+
const relativePagePath = getRelativePagePath(path, pagePath, siteData.base);
19+
console.log('relativePagePath', relativePagePath);
1820
return {
1921
siteData,
2022
pagePath,
23+
relativePagePath,
2124
...omit(mod, ['default'])
2225
} as PageData;
2326
} else {
2427
return {
2528
siteData,
2629
pagePath: '',
30+
relativePagePath: '',
2731
pageType: '404'
2832
};
2933
}

src/runtime/utils.ts

+16
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,19 @@ export const hashRE = /#.*$/s;
1313

1414
export const cleanUrl = (url: string): string =>
1515
url.replace(hashRE, '').replace(queryRE, '');
16+
17+
export const getRelativePagePath = (
18+
routePath: string,
19+
filePath: string,
20+
base: string
21+
) => {
22+
const extname = filePath.split('.').pop();
23+
let pagePath = cleanUrl(routePath);
24+
if (pagePath.startsWith(base)) {
25+
pagePath = pagePath.slice(base.length);
26+
}
27+
if (extname) {
28+
pagePath += `.${extname}`;
29+
}
30+
return pagePath.replace(/^\//, '');
31+
};

src/shared/types/default-theme.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,14 @@ export namespace DefaultTheme {
167167
*
168168
* @default 'Previous page'
169169
*/
170-
prev?: string;
170+
prev?: SidebarItem;
171171

172172
/**
173173
* Custom label for next page button.
174174
*
175175
* @default 'Next page'
176176
*/
177-
next?: string;
177+
next?: SidebarItem;
178178
}
179179

180180
// social link ---------------------------------------------------------------

src/shared/types/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export interface LocaleConfig {
9595
}
9696

9797
export interface SiteData<ThemeConfig = any> {
98+
root: string;
9899
base: string;
99100
lang: string;
100101
title: string;
@@ -151,6 +152,7 @@ export interface PageModule<T extends ComponentType<any>> {
151152
export interface PageData {
152153
siteData: SiteData<DefaultTheme.Config>;
153154
pagePath: string;
155+
relativePagePath: string;
154156
title?: string;
155157
description?: string;
156158
pageType: 'home' | 'doc' | 'custom' | '404';

src/shared/types/type.d.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ declare module 'island/theme*' {
1616
}
1717

1818
declare module 'island:site-data' {
19-
export default any;
19+
import { SiteData } from 'shared/types';
20+
const siteData: SiteData;
21+
export default siteData;
2022
}
2123

2224

2325
declare module 'island/client' {
2426
import { ComponentType } from 'react';
25-
import { PageData } from 'shared/types';
27+
import { PageData, SiteData } from 'shared/types';
2628

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

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

4548
export const routes: Route[];
4649
}

src/theme-default/components/DocFooter/index.tsx

+41-16
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,62 @@
11
import styles from './index.module.scss';
22
import { usePageData } from 'island/client';
3+
import { useEditLink, usePrevNextPage } from '../../logic';
4+
import { normalizeHref } from '../../logic/index';
35

46
export function DocFooter() {
5-
const { siteData } = usePageData();
6-
const { editLink } = siteData?.themeConfig || {};
7+
const { siteData, relativePagePath } = usePageData();
8+
const { prevPage, nextPage } = usePrevNextPage(siteData);
9+
const { editLink: rawEditLink } = siteData.themeConfig;
10+
const editLink = useEditLink(rawEditLink!, relativePagePath);
11+
712
return (
813
<footer className={styles.footer}>
914
<div className={styles.editInfo}>
10-
<div className={styles.editLink}>
11-
<button className={styles.editLinkButton}>Edit Link</button>
12-
</div>
13-
<div className={styles.lastUpdated}>
14-
{editLink ? (
15+
{editLink ? (
16+
<div className={styles.editLink}>
17+
<a className={styles.editLinkButton} href={editLink.link}>
18+
{editLink.text}
19+
</a>
20+
</div>
21+
) : null}
22+
23+
{/* TODO */}
24+
{/* <div className={styles.lastUpdated}>
25+
{lastUpdatedText ? (
1526
<>
1627
<p className={styles.lastUpdated}>
1728
{editLink?.text || 'Last Updated: '}
1829
</p>
19-
<span>{editLink?.pattern}</span>
30+
<span>{}</span>
2031
</>
2132
) : null}
22-
</div>
33+
</div> */}
2334
</div>
2435

2536
<div className={styles.prevNext}>
2637
<div className={styles.pager}>
27-
<a href="/" className={`${styles.pagerLink} ${styles.prev}`}>
28-
<span className={styles.desc}>Previous Page</span>
29-
</a>
38+
{prevPage ? (
39+
<a
40+
href={normalizeHref(prevPage.link)}
41+
className={`${styles.pagerLink} ${styles.prev}`}
42+
>
43+
<span className={styles.desc}>Previous Page</span>
44+
<span className={styles.title}>{prevPage.text}</span>
45+
</a>
46+
) : null}
3047
</div>
31-
<div className={`${styles.pager} ${styles.hasNext}`}>
32-
<a href="/" className={`${styles.pagerLink} ${styles.next}`}>
33-
<span className={styles.desc}>Next Page</span>
34-
</a>
48+
<div className={styles.pager}>
49+
{nextPage ? (
50+
<div className={`${styles.hasNext}`}>
51+
<a
52+
href={normalizeHref(nextPage.link)}
53+
className={`${styles.pagerLink} ${styles.next}`}
54+
>
55+
<span className={styles.desc}>Next Page</span>
56+
<span className={styles.title}>{nextPage.text}</span>
57+
</a>
58+
</div>
59+
) : null}
3560
</div>
3661
</div>
3762
</footer>

src/theme-default/logic/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ export function normalizeHref(url?: string) {
1616
}
1717

1818
export { useAsideAnchor } from './useAsideAnchor';
19+
export { usePrevNextPage } from './usePrevNextPage';
20+
export { useEditLink } from './useEditLink';

src/theme-default/logic/useAsideAnchor.ts

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export function useAsideAnchor(
3535
}
3636
// Util function to set dom ref after determining the active link
3737
const activate = (links: NodeListOf<HTMLAnchorElement>, index: number) => {
38-
console.log('current active: ', index);
3938
if (prevActiveLinkRef.current) {
4039
prevActiveLinkRef.current.classList.remove('aside-active');
4140
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { DefaultTheme } from 'shared/types';
2+
export function useEditLink(
3+
editLink: DefaultTheme.EditLink,
4+
relativePagePath: string
5+
) {
6+
if (!editLink) {
7+
return null;
8+
}
9+
const { text, pattern } = editLink;
10+
const link = pattern.replace(':path', relativePagePath);
11+
12+
return {
13+
text,
14+
link
15+
};
16+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { DefaultTheme, SiteData } from 'shared/types';
2+
import { useLocation } from 'react-router-dom';
3+
4+
export function usePrevNextPage(siteData: SiteData<DefaultTheme.Config>) {
5+
const themeConfig = siteData.themeConfig || {};
6+
const sidebar = themeConfig.sidebar || [];
7+
const flattenTitles: DefaultTheme.SidebarItem[] = [];
8+
const { pathname } = useLocation();
9+
10+
const walkThroughSidebar = (sidebar: DefaultTheme.Sidebar) => {
11+
if (Array.isArray(sidebar)) {
12+
sidebar.forEach((sidebarGroup) => {
13+
sidebarGroup.items.forEach((item) => {
14+
flattenTitles.push(item);
15+
});
16+
});
17+
} else {
18+
Object.keys(sidebar).forEach((key) => {
19+
walkThroughSidebar(sidebar[key]);
20+
});
21+
}
22+
};
23+
24+
walkThroughSidebar(sidebar);
25+
26+
const pageIndex = flattenTitles.findIndex((item) => item.link === pathname);
27+
28+
const prevPage = flattenTitles[pageIndex - 1] || null;
29+
const nextPage = flattenTitles[pageIndex + 1] || null;
30+
31+
return {
32+
prevPage,
33+
nextPage
34+
};
35+
}

0 commit comments

Comments
 (0)