diff --git a/packages/island/package.json b/packages/island/package.json index f9e940a3..b3fe22f4 100644 --- a/packages/island/package.json +++ b/packages/island/package.json @@ -77,6 +77,7 @@ "lodash-es": "4.17.21", "mdast-util-mdxjs-esm": "1.3.0", "medium-zoom": "1.0.6", + "nprogress": "^0.2.0", "ora": "6.1.2", "picocolors": "1.0.0", "polka": "0.5.2", @@ -117,6 +118,7 @@ "@types/koa": "^2.13.5", "@types/koa-router": "^7.4.4", "@types/lodash-es": "^4.17.6", + "@types/nprogress": "^0.2.0", "@types/polka": "^0.5.4", "@types/react": "^18.0.17", "@types/react-dom": "^18.0.6", diff --git a/packages/island/src/runtime/Content.tsx b/packages/island/src/runtime/Content.tsx index ef8634e6..9f577e2d 100644 --- a/packages/island/src/runtime/Content.tsx +++ b/packages/island/src/runtime/Content.tsx @@ -1,19 +1,8 @@ -import { useRoutes, useLocation } from 'react-router-dom'; +import { useRoutes } from 'react-router-dom'; import { routes } from 'virtual:routes'; import { Suspense } from 'react'; -interface LocationState { - from?: string; -} - export const Content = () => { const routesElement = useRoutes(routes); - const location = useLocation(); - let prevRouteElement: React.ReactNode = null; - if (import.meta.env.ENABLE_SPA) { - const prevRoute = (location.state as LocationState)?.from; - // eslint-disable-next-line react-hooks/rules-of-hooks - prevRouteElement = prevRoute ? useRoutes(routes, prevRoute) : null; - } - return {routesElement}; + return {routesElement}; }; diff --git a/packages/island/src/runtime/utils.ts b/packages/island/src/runtime/utils.ts index 902b5a83..70b6a6f2 100644 --- a/packages/island/src/runtime/utils.ts +++ b/packages/island/src/runtime/utils.ts @@ -49,4 +49,8 @@ export function normalizeHref(url?: string) { return addLeadingSlash(`${url}${suffix}`); } +export function normalizeRoutePath(routePath: string) { + return routePath.replace(/\.html$/, '').replace(/\/index$/, '/'); +} + export { addLeadingSlash, removeTrailingSlash, normalizeSlash }; diff --git a/packages/island/src/theme-default/components/Aside/index.tsx b/packages/island/src/theme-default/components/Aside/index.tsx index b671300c..28cd4c87 100644 --- a/packages/island/src/theme-default/components/Aside/index.tsx +++ b/packages/island/src/theme-default/components/Aside/index.tsx @@ -32,7 +32,6 @@ export function Aside( }, [headers]); } useEffect(() => { - console.log(props.headers); setHeaders(props.headers); }, [props.headers, setHeaders, props.pagePath]); diff --git a/packages/island/src/theme-default/components/Link/index.tsx b/packages/island/src/theme-default/components/Link/index.tsx index 69899496..dd5c66f3 100644 --- a/packages/island/src/theme-default/components/Link/index.tsx +++ b/packages/island/src/theme-default/components/Link/index.tsx @@ -1,10 +1,16 @@ import React from 'react'; import styles from './index.module.scss'; -import { Link as RouterLink } from 'react-router-dom'; -import { withBase } from '@runtime'; +import { + matchRoutes, + normalizeRoutePath, + useNavigate, + withBase +} from '@runtime'; import { TARGET_BLANK_WHITE_LIST } from '@shared/constants'; -import { inBrowser } from '@shared/utils'; import { EXTERNAL_URL_RE } from '@shared/constants'; +import nprogress from 'nprogress'; +import { routes } from 'virtual:routes'; +import { Route } from 'node/plugin-routes'; export interface LinkProps { href?: string; @@ -12,6 +18,8 @@ export interface LinkProps { className?: string; } +nprogress.configure({ showSpinner: false }); + export function Link(props: LinkProps) { const { href = '/', children, className = '' } = props; const isExternal = EXTERNAL_URL_RE.test(href); @@ -20,19 +28,35 @@ export function Link(props: LinkProps) { ); const target = isExternal && !isWhiteList ? '_blank' : ''; const rel = isExternal ? 'noopener noreferrer' : undefined; - const pathname = inBrowser() ? window.location.pathname : ''; const withBaseUrl = isExternal ? href : withBase(href); + const navigate = useNavigate(); + + const handleNavigate = async ( + e: React.MouseEvent + ) => { + e.preventDefault(); + const matchedRoutes = matchRoutes(routes, normalizeRoutePath(withBaseUrl)); + if (matchedRoutes?.length) { + const timer = setTimeout(() => { + nprogress.start(); + }, 200); + await (matchedRoutes[0].route as Route).preload(); + clearTimeout(timer); + nprogress.done(); + } + navigate(withBaseUrl, { replace: false }); + }; if (import.meta.env.ENABLE_SPA && !isExternal) { return ( - {children} - + ); } else { return ( diff --git a/packages/island/src/theme-default/layout/Layout/index.tsx b/packages/island/src/theme-default/layout/Layout/index.tsx index 1550fad1..44d13b3d 100644 --- a/packages/island/src/theme-default/layout/Layout/index.tsx +++ b/packages/island/src/theme-default/layout/Layout/index.tsx @@ -9,6 +9,7 @@ import { DocLayoutProps } from '../DocLayout/index'; import { HomeLayoutProps } from '../HomeLayout/index'; import type { NavProps } from '../../components/Nav/index'; import { BackTop } from '@back-top'; +import 'nprogress/nprogress.css'; import 'virtual:custom-styles'; export type LayoutProps = { diff --git a/packages/island/src/theme-default/styles/base.css b/packages/island/src/theme-default/styles/base.css index bdf1b2d9..cf9ebd84 100644 --- a/packages/island/src/theme-default/styles/base.css +++ b/packages/island/src/theme-default/styles/base.css @@ -223,3 +223,12 @@ fieldset { .medium-zoom-image--opened { z-index: 999; } + +#nprogress .bar { + background: var(--island-c-brand); + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 2px; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f19d7cb5..28fec581 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,7 @@ importers: '@types/koa': ^2.13.5 '@types/koa-router': ^7.4.4 '@types/lodash-es': ^4.17.6 + '@types/nprogress': ^0.2.0 '@types/polka': ^0.5.4 '@types/react': ^18.0.17 '@types/react-dom': ^18.0.6 @@ -157,6 +158,7 @@ importers: lodash-es: 4.17.21 mdast-util-mdxjs-esm: 1.3.0 medium-zoom: 1.0.6 + nprogress: ^0.2.0 ora: 6.1.2 picocolors: 1.0.0 polka: 0.5.2 @@ -218,6 +220,7 @@ importers: lodash-es: 4.17.21 mdast-util-mdxjs-esm: 1.3.0 medium-zoom: 1.0.6 + nprogress: 0.2.0 ora: 6.1.2 picocolors: 1.0.0 polka: 0.5.2 @@ -257,6 +260,7 @@ importers: '@types/koa': 2.13.5 '@types/koa-router': 7.4.4 '@types/lodash-es': 4.17.6 + '@types/nprogress': 0.2.0 '@types/polka': 0.5.4 '@types/react': 18.0.17 '@types/react-dom': 18.0.6 @@ -1451,6 +1455,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/nprogress/0.2.0: + resolution: {integrity: sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==} + dev: true + /@types/parse-json/4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} @@ -5613,6 +5621,10 @@ packages: path-key: 4.0.0 dev: true + /nprogress/0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + dev: false + /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'}