Skip to content

Commit

Permalink
[gem] Support scroll position restore
Browse files Browse the repository at this point in the history
Break change:

- `GemRouteElement.locationStore` update by location hash
  • Loading branch information
mantou132 committed Jan 5, 2024
1 parent 1291afb commit 7621db1
Show file tree
Hide file tree
Showing 24 changed files with 307 additions and 188 deletions.
2 changes: 2 additions & 0 deletions packages/duoyun-ui/src/elements/page-loadbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class DuoyunPageLoadbarElement extends GemElement {
static instance?: DuoyunPageLoadbarElement;
static timer = 0;

/**在延时时间内结束将不会显示加载条 */
static async start({ delay = 100 }: { delay?: number } = {}) {
clearInterval(Loadbar.timer);
Loadbar.timer = window.setTimeout(() => {
Expand All @@ -51,6 +52,7 @@ export class DuoyunPageLoadbarElement extends GemElement {
}

static async end() {
// 能同时取消 setTimeout ID
clearInterval(Loadbar.timer);
const instance = Loadbar.instance;
if (instance) {
Expand Down
14 changes: 11 additions & 3 deletions packages/duoyun-ui/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,15 @@ export function useCacheStore<T extends Record<string, any>>(
}

/**@deprecated use `useCacheStore` */
export const createCacheStore = (...args: Parameters<typeof useCacheStore>) => {
const [store, , save] = useCacheStore(...args);
export function createCacheStore<T extends Record<string, any>>(
storageKey: string,
initStore: T,
options?: {
cacheExcludeKeys?: (keyof T)[];
prefix?: string | (() => string | undefined);
depStore?: Store<NonPrimitive>;
},
) {
const [store, , save] = useCacheStore(storageKey, initStore, options);
return [store, save] as const;
};
}
2 changes: 1 addition & 1 deletion packages/gem-book/docs/zh/002-guide/003-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ npx gem-book -h

`gem-book` 命令会自动从项目根目录查找配置文件 `gem-book.cli.{js|json|mjs}`,支持大部分命令行选项(同时提供时合并命令行选项),例如:

<gbp-raw src="gem-book.cli.json" range="1,3-"><gbp-raw>
<gbp-raw src="gem-book.cli.json" range="1,3-"></gbp-raw>

> [!TIP]
> 如果用 `json` 格式,可以添加 `"$schema": "https://unpkg.com/gem-book/schema.json"` 以获取类型提示,
Expand Down
4 changes: 2 additions & 2 deletions packages/gem-book/docs/zh/003-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ yarn add gem-book

用于显示远端代码,如果提供的 `src` 只包含路径,则会从当前项目的 GitHub 上读取内容(受 [`sourceDir`](./002-guide/003-cli.md#--source-dir)[`sourceBranch`](./002-guide/003-cli.md#--source-branch) 影响),比如:

<gbp-raw src="package.json" range="2-3,-4--6" highlight="-5"><gbp-raw>
<gbp-raw src="package.json" range="2-3,-4--6" highlight="-5"></gbp-raw>

```md
<!-- `range` 指定显示的范围,支持使用负数 -->

<gbp-raw src="package.json" range="2-3,-4--6" highlight="-5"><gbp-raw>
<gbp-raw src="package.json" range="2-3,-4--6" highlight="-5"></gbp-raw>
```

## `<gbp-media>`
Expand Down
7 changes: 3 additions & 4 deletions packages/gem-book/src/element/elements/404.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { connectStore, customElement, GemElement, history, html } from '@mantou/gem';
import { connectStore, customElement, GemElement, html } from '@mantou/gem';

import { getGithubPath, getUserLink } from '../lib/utils';
import { bookStore } from '../store';
import { bookStore, locationStore } from '../store';
import { selfI18n } from '../helper/i18n';

import { icons } from './icons';
Expand All @@ -14,8 +14,7 @@ import '@mantou/gem/elements/title';
export class Meta extends GemElement {
#getMdFullPath = () => {
const { links = [] } = bookStore;
const { path } = history.getParams();
const parts = path.replace(/\/$/, '').split('/');
const parts = locationStore.path.replace(/\/$/, '').split('/');
const newFile = parts.pop();
const parentPath = parts.join('/');
const link = links.find(({ originLink }) => getUserLink(originLink).startsWith(parentPath));
Expand Down
69 changes: 40 additions & 29 deletions packages/gem-book/src/element/elements/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { html, GemElement, customElement, css, property, createCSSSheet, adoptedStyle, history } from '@mantou/gem';
import {
html,
GemElement,
customElement,
css,
property,
createCSSSheet,
adoptedStyle,
connectStore,
} from '@mantou/gem';
import { Renderer, parse } from 'marked';
import { mediaQuery } from '@mantou/gem/helper/mediaquery';

Expand Down Expand Up @@ -52,9 +61,10 @@ const linkStyle = css`

@customElement('gem-book-main')
@adoptedStyle(style)
@connectStore(locationStore)
export class Main extends GemElement {
@property content: string;
@property renderer: Renderer;
@property content?: string;
@property renderer?: Renderer;

// homepage/footer 等内置元素渲染在 main 前面,不能使用自定义渲染器
static instance?: Main;
Expand All @@ -75,29 +85,23 @@ export class Main extends GemElement {
constructor() {
super();
Main.instance = this;
this.memo(() => {
const [, , _sToken, _frontmatter, _eToken, mdBody] =
this.content.match(/^(([\r\n\s]*---\s*(?:\r\n|\n))(.*?)((?:\r\n|\n)---\s*(?:\r\n|\n)?))?(.*)$/s) || [];
this.#content = mdBody;
});
this.memo(
() => {
const [, , _sToken, _frontmatter, _eToken, mdBody = ''] =
this.content?.match(/^(([\r\n\s]*---\s*(?:\r\n|\n))(.*?)((?:\r\n|\n)---\s*(?:\r\n|\n)?))?(.*)$/s) || [];
this.#content = Main.parseMarkdown(mdBody);
},
() => [this.content],
);
}

#content = '';
#content: Element[] = [];

#hashChangeHandle = () => {
const { hash, path } = history.getParams();
// 确保是页内跳转或者新页(mounted)跳转
if (hash && path === locationStore.path) {
this.shadowRoot?.querySelector(`[id="${decodeURIComponent(hash.slice(1))}"]`)?.scrollIntoView({
block: 'start',
});
}
};

#updateToc = () =>
#updateToc = () => {
updateTocStore({
elements: [...this.shadowRoot!.querySelectorAll<HTMLHeadingElement>('h2,h3')],
});
};

render() {
return html`
Expand Down Expand Up @@ -315,7 +319,7 @@ export class Main extends GemElement {
}
}
</style>
${Main.parseMarkdown(this.#content)}
${this.#content}
<style>
${linkStyle}
</style>
Expand All @@ -325,23 +329,30 @@ export class Main extends GemElement {
mounted() {
this.effect(
() => {
checkBuiltInPlugin(this.shadowRoot!);

this.#updateToc();

const mo = new MutationObserver(this.#updateToc);
mo.observe(this.shadowRoot!, {
childList: true,
subtree: true,
});

checkBuiltInPlugin(this.shadowRoot!);
this.#hashChangeHandle();
window.addEventListener('hashchange', this.#hashChangeHandle);

return () => {
window.removeEventListener('hashchange', this.#hashChangeHandle);
mo.disconnect();
};
return () => mo.disconnect();
},
() => [this.content],
);

this.effect(
([hash]) => {
if (hash) {
this.shadowRoot?.querySelector(`[id="${hash.slice(1)}"]`)?.scrollIntoView({
block: 'start',
});
}
},
() => [locationStore.hash],
);
}
}
17 changes: 12 additions & 5 deletions packages/gem-book/src/element/elements/meta.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { connectStore, customElement, GemElement, html, history } from '@mantou/gem';
import { connectStore, customElement, GemElement, html } from '@mantou/gem';
import { mediaQuery } from '@mantou/gem/helper/mediaquery';

import { getAlternateUrl, getURL } from '../lib/utils';
import { bookStore } from '../store';
import { getRemotePath, getURL } from '../lib/utils';
import { bookStore, locationStore } from '../store';

import '@mantou/gem/elements/reflect';

function getAlternateUrl(lang: string, pathname?: string) {
const { origin } = location;
const { path, query, hash } = locationStore;
const fullPath = getRemotePath(pathname || path, lang);
if (pathname) return `${origin}${fullPath}`;
return `${origin}${fullPath}${query}${hash}`;
}

@customElement('gem-book-meta')
@connectStore(bookStore)
export class Meta extends GemElement {
render() {
const { langList, lang = '', routes, homePage, getCurrentLink, currentLinks } = bookStore;
const { path } = history.getParams();
const route = routes?.find((route) => route.pattern === path && route.redirect);
const route = routes?.find((route) => route.pattern === locationStore.path && route.redirect);
const canonicalLink = getAlternateUrl(
lang && langList && !location.pathname.startsWith(`/${lang}`) ? langList[0].code : lang,
route?.redirect,
Expand Down
18 changes: 17 additions & 1 deletion packages/gem-book/src/element/elements/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { mediaQuery } from '@mantou/gem/helper/mediaquery';
import { logger } from '@mantou/gem/helper/logger';

import { theme } from '../helper/theme';
import { bookStore } from '../store';
import { bookStore, locationStore } from '../store';
import { BookConfig } from '../../common/config';
import { icons } from '../elements/icons';

Expand All @@ -17,6 +17,7 @@ export class GemBookPluginElement<T = any> extends GemElement<T> {
static theme = theme;
static icons = icons;
static mediaQuery = mediaQuery;
static locationStore = locationStore;
static config = new Proxy<Partial<BookConfig>>(
{},
{
Expand Down Expand Up @@ -56,6 +57,21 @@ export class GemBookPluginElement<T = any> extends GemElement<T> {
return bookStore.getCurrentLink?.();
}

static caches?: Map<string, any>;

cacheState(getDeps: () => string[]) {
if (!this.state) throw new Error('Only cache state');
const cons = this.constructor as typeof GemBookPluginElement;
const caches = cons.caches || (cons.caches = new Map());
this.memo(
() => {
Object.assign(this.state!, caches.get(getDeps().join()));
return () => caches.set(getDeps().join(), this.state);
},
() => getDeps(),
);
}

@globalemitter error: Emitter<ErrorEvent | Event> = logger.error;

/**获取资源的远端 GitHub raw 地址,如果使用 `DEV_MODE`,则返回本机服务的 URL */
Expand Down
7 changes: 2 additions & 5 deletions packages/gem-book/src/element/elements/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import {
useStore,
refobject,
RefObject,
history,
} from '@mantou/gem';
import { mediaQuery } from '@mantou/gem/helper/mediaquery';

import { NavItem } from '../../common/config';
import { capitalize, isSameOrigin } from '../lib/utils';
import { theme } from '../helper/theme';
import { bookStore, locationStore } from '../store';
import { GemBookElement } from '..';

import { icons } from './icons';
import { tocStore } from './toc';
Expand Down Expand Up @@ -63,7 +61,6 @@ export class SideBar extends GemElement {
{ type = 'file', link, title, children, sidebarIgnore }: NavItem,
isTop = false,
): TemplateResult | null => {
const { path } = history.getParams();
const { homePage, config } = bookStore;
const { homeMode, onlyFile } = config || {};
if (sidebarIgnore || (homeMode && homePage === link)) {
Expand Down Expand Up @@ -91,7 +88,7 @@ export class SideBar extends GemElement {
>
${title ? capitalize(title) : 'No title'}
</gem-book-side-link>
${!onlyFile && path === link
${!onlyFile && locationStore.path === link
? html`<div class="links item hash">
${tocStore.elements
.filter((e) => e.tagName === 'H2')
Expand Down Expand Up @@ -272,7 +269,7 @@ export class SideBar extends GemElement {
}
</style>
<gem-book-nav-logo>
<slot name=${GemBookElement.logoAfter}></slot>
<slot name="logo-after"></slot>
</gem-book-nav-logo>
<div class="nav" ref=${this.navRef.ref}>
${mediaQuery.isPhone && topNavList?.length
Expand Down
10 changes: 4 additions & 6 deletions packages/gem-book/src/element/helper/theme.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createTheme, getThemeProps, getThemeStore, updateTheme } from '@mantou/gem/helper/theme';
import { getThemeProps, getThemeStore, useTheme } from '@mantou/gem/helper/theme';

import { defaultTheme } from './default-theme';

Expand Down Expand Up @@ -31,12 +31,10 @@ function generateTheme(theme: Theme) {
};
}

export const theme = createTheme(generateTheme(defaultTheme));
export const [theme, updateTheme] = useTheme(generateTheme(defaultTheme));
export const themeStore = getThemeStore(theme);
export const themeProps = getThemeProps(theme);

export function changeTheme(newTheme?: Partial<Theme>) {
if (newTheme) {
updateTheme(theme, generateTheme({ ...getThemeStore(theme), ...newTheme }));
}
export function changeTheme(newTheme: Partial<Theme> = {}) {
updateTheme(generateTheme({ ...getThemeStore(theme), ...newTheme }));
}
Loading

0 comments on commit 7621db1

Please sign in to comment.