@mantou/gem 2.2.15 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/gem.js +7 -7
  2. package/dist/gem.mjs +7 -7
  3. package/elements/base/dialog.d.ts.map +1 -1
  4. package/elements/base/link.d.ts.map +1 -1
  5. package/elements/base/link.js +1 -1
  6. package/elements/base/link.js.map +1 -1
  7. package/elements/base/modal-factory.d.ts +20 -510
  8. package/elements/base/modal-factory.d.ts.map +1 -1
  9. package/elements/base/modal-factory.js +3 -2
  10. package/elements/base/modal-factory.js.map +1 -1
  11. package/elements/base/reflect.d.ts +7 -4
  12. package/elements/base/reflect.d.ts.map +1 -1
  13. package/elements/base/reflect.js +30 -4
  14. package/elements/base/reflect.js.map +1 -1
  15. package/elements/base/route.d.ts +11 -9
  16. package/elements/base/route.d.ts.map +1 -1
  17. package/elements/base/route.js +84 -44
  18. package/elements/base/route.js.map +1 -1
  19. package/elements/base/title.d.ts.map +1 -1
  20. package/elements/base/title.js +1 -1
  21. package/elements/base/title.js.map +1 -1
  22. package/helper/hmr.js.map +1 -1
  23. package/helper/i18n.d.ts +21 -17
  24. package/helper/i18n.d.ts.map +1 -1
  25. package/helper/i18n.js +172 -116
  26. package/helper/i18n.js.map +1 -1
  27. package/helper/logger.d.ts.map +1 -1
  28. package/helper/mediaquery.d.ts.map +1 -1
  29. package/helper/request.d.ts +7 -1
  30. package/helper/request.d.ts.map +1 -1
  31. package/helper/request.js.map +1 -1
  32. package/helper/theme.d.ts +8 -4
  33. package/helper/theme.d.ts.map +1 -1
  34. package/helper/theme.js +12 -9
  35. package/helper/theme.js.map +1 -1
  36. package/lib/decorators.d.ts +3 -2
  37. package/lib/decorators.d.ts.map +1 -1
  38. package/lib/decorators.js +29 -44
  39. package/lib/decorators.js.map +1 -1
  40. package/lib/directive.d.ts.map +1 -1
  41. package/lib/history.d.ts +46 -42
  42. package/lib/history.d.ts.map +1 -1
  43. package/lib/history.js +19 -37
  44. package/lib/history.js.map +1 -1
  45. package/lib/lit-html.d.ts.map +1 -1
  46. package/lib/lit-html.js.map +1 -1
  47. package/lib/reactive.d.ts +7 -9
  48. package/lib/reactive.d.ts.map +1 -1
  49. package/lib/reactive.js +86 -115
  50. package/lib/reactive.js.map +1 -1
  51. package/lib/repeat.d.ts +1 -1
  52. package/lib/repeat.d.ts.map +1 -1
  53. package/lib/repeat.js.map +1 -1
  54. package/lib/server.d.ts +12 -0
  55. package/lib/server.d.ts.map +1 -1
  56. package/lib/server.js +2 -0
  57. package/lib/server.js.map +1 -1
  58. package/lib/store.d.ts +0 -6
  59. package/lib/store.d.ts.map +1 -1
  60. package/lib/store.js +5 -17
  61. package/lib/store.js.map +1 -1
  62. package/lib/utils.d.ts.map +1 -1
  63. package/lib/version.d.ts +1 -1
  64. package/lib/version.d.ts.map +1 -1
  65. package/lib/version.js +1 -1
  66. package/lib/version.js.map +1 -1
  67. package/package.json +7 -8
  68. package/src/elements/base/modal-factory.ts +22 -6
  69. package/src/elements/base/reflect.ts +30 -4
  70. package/src/elements/base/route.ts +80 -50
  71. package/src/helper/i18n.ts +184 -127
  72. package/src/helper/theme.ts +16 -9
  73. package/src/lib/decorators.ts +36 -49
  74. package/src/lib/history.ts +19 -32
  75. package/src/lib/reactive.ts +90 -126
  76. package/src/lib/server.ts +14 -0
  77. package/src/lib/store.ts +3 -19
  78. package/src/lib/version.ts +1 -1
  79. package/src/test/elements/gesture.test.ts +13 -5
  80. package/src/test/elements/link.test.ts +12 -2
  81. package/src/test/elements/reflect.test.ts +20 -1
  82. package/src/test/elements/route.test.ts +102 -0
  83. package/src/test/elements/title.test.ts +7 -0
  84. package/src/test/elements/unsafe.test.ts +22 -0
  85. package/src/test/gem-element/advance.test.ts +106 -0
  86. package/src/test/gem-element/basic.test.ts +13 -0
  87. package/src/test/history.test.ts +19 -0
  88. package/src/test/theme.test.ts +8 -1
  89. package/src/test/utils.test.ts +10 -0
  90. package/src/test/utils.ts +56 -0
package/lib/store.d.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  import { type Updater } from './utils';
2
2
  export type Store<T = any> = Updater<T>;
3
- /** SSR 时当前请求中更新过的有 key 的 store,供 `@mantou/gem-ssr` 使用 */
4
- export declare const _getUpdatedStores: () => Map<any, any>;
5
- declare let restoreStore: ((key: string) => object | undefined) | undefined;
6
- /** 设置客户端恢复 store 的方法,供 `@mantou/gem-ssr` 使用 */
7
- export declare function _setStoreRestorer(restorer: typeof restoreStore): void;
8
3
  /**
9
4
  * 创建 store,`key` 用来在 SSR 时同步服务端的值,`swc-plugin-gem` 的 `ssr` 选项会为模块顶层的 store 自动添加
10
5
  */
@@ -22,5 +17,4 @@ export declare function createStore<T = any>(originStore: T, key?: string): ((va
22
17
  [Symbol.metadata]: never;
23
18
  }, keyof T> & T;
24
19
  export declare function connect(store: Store<any>, func: () => void): () => boolean | undefined;
25
- export {};
26
20
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/lib/store.ts"],"names":[],"mappings":"AACA,OAAO,EAA4C,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAEjF,MAAM,MAAM,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAUxC,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,qBAAwD,CAAC;AAGvF,QAAA,IAAI,YAAY,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;AAEpE,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,YAAY,QAE9D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,aAIzB,OAAO,CAAC,CAAC,CAAC;;;;;;;;;;;;gBAcjD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,6BAK1D"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/lib/store.ts"],"names":[],"mappings":"AACA,OAAO,EAA4C,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAEjF,MAAM,MAAM,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAOxC;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,aAIzB,OAAO,CAAC,CAAC,CAAC;;;;;;;;;;;;gBAYjD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,6BAK1D"}
package/lib/store.js CHANGED
@@ -1,18 +1,8 @@
1
- import { getRequestValue, isolate, isServer } from './server';
1
+ import { getRequestValue, isolate, isServer, ssrHooks } from './server';
2
2
  import { addMicrotask, assignState, createUpdater } from './utils';
3
3
  /**@internal */
4
4
  export const _StoreListenerMap = new WeakMap();
5
5
  const createListeners = () => new Set();
6
- // SSR 时当前请求中更新过的有 key 的 store,渲染后输出到页面中
7
- const updatedStores = new Map();
8
- /** SSR 时当前请求中更新过的有 key 的 store,供 `@mantou/gem-ssr` 使用 */
9
- export const _getUpdatedStores = () => getRequestValue(updatedStores, () => new Map());
10
- // 客户端读取服务端渲染时 store 的值
11
- let restoreStore;
12
- /** 设置客户端恢复 store 的方法,供 `@mantou/gem-ssr` 使用 */
13
- export function _setStoreRestorer(restorer) {
14
- restoreStore = restorer;
15
- }
16
6
  /**
17
7
  * 创建 store,`key` 用来在 SSR 时同步服务端的值,`swc-plugin-gem` 的 `ssr` 选项会为模块顶层的 store 自动添加
18
8
  */
@@ -26,13 +16,11 @@ export function createStore(originStore, key) {
26
16
  return;
27
17
  // SSR 时请求中添加的监听器只属于当前请求,其他请求更新 store 时不会触发
28
18
  getRequestValue(listeners, createListeners).forEach(addMicrotask);
29
- const updated = _getUpdatedStores();
30
- // 不在请求中时更新的是全局的值,客户端也一样
31
- if (key && updated !== updatedStores)
32
- updated.set(key, store);
19
+ if (key)
20
+ ssrHooks.updateStore?.(key, store);
33
21
  }));
34
- if (key && restoreStore)
35
- assignState(store, restoreStore(key));
22
+ if (key)
23
+ assignState(store, ssrHooks.createStore?.(key));
36
24
  _StoreListenerMap.set(store, listeners);
37
25
  return store;
38
26
  }
package/lib/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/lib/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAgB,MAAM,SAAS,CAAC;AAIjF,eAAe;AACf,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAA+B,CAAC;AAE5E,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,IAAI,GAAG,EAAc,CAAC;AAEpD,wCAAwC;AACxC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;AAEpD,yDAAyD;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,EAAE,CAAC,eAAe,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;AAEvF,uBAAuB;AACvB,IAAI,YAA+D,CAAC;AAEpE,+CAA+C;AAC/C,MAAM,UAAU,iBAAiB,CAAC,QAA6B;IAC7D,YAAY,GAAG,QAAQ,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAU,WAAc,EAAE,GAAY;IAC/D,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,kBAAkB;IAClB,MAAM,KAAK,GAAG,OAAO,CACnB,aAAa,CAAC,WAAW,EAAE,CAAC,KAAkB,EAAE,EAAE;QAChD,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1B,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,2CAA2C;QAC3C,eAAe,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;QACpC,wBAAwB;QACxB,IAAI,GAAG,IAAI,OAAO,KAAK,aAAa;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAChE,CAAC,CAAC,CACH,CAAC;IACF,IAAI,GAAG,IAAI,YAAY;QAAE,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAiB,EAAE,IAAgB;IACzD,MAAM,cAAc,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,cAAc,IAAI,eAAe,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IACrF,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/lib/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAgB,MAAM,SAAS,CAAC;AAIjF,eAAe;AACf,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAA+B,CAAC;AAE5E,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,IAAI,GAAG,EAAc,CAAC;AAEpD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAU,WAAc,EAAE,GAAY;IAC/D,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,kBAAkB;IAClB,MAAM,KAAK,GAAG,OAAO,CACnB,aAAa,CAAC,WAAW,EAAE,CAAC,KAAkB,EAAE,EAAE;QAChD,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1B,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,2CAA2C;QAC3C,eAAe,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAClE,IAAI,GAAG;YAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CACH,CAAC;IACF,IAAI,GAAG;QAAE,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAiB,EAAE,IAAgB;IACzD,MAAM,cAAc,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,cAAc,IAAI,eAAe,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IACrF,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/lib/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/lib/utils.ts"],"names":[],"mappings":"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"}
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version = "2.2.15";
1
+ export declare const version = "2.3.1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/lib/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/lib/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC"}
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '2.2.15';
1
+ export const version = '2.3.1';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/lib/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/lib/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mantou/gem",
3
- "version": "2.2.15",
4
- "description": "💎 使用自定义元素的轻量级 WebApp 开发框架",
3
+ "version": "2.3.1",
4
+ "description": "💎 Build reusable custom elements and web apps on Web Components",
5
5
  "keywords": [
6
6
  "frontend",
7
7
  "web components",
@@ -31,17 +31,16 @@
31
31
  ],
32
32
  "typings": "index.d.ts",
33
33
  "scripts": {
34
- "docs": "cross-env GEM_BOOK_REPLACE=true gem-book docs",
35
- "docs:remote": "pnpm run docs",
36
- "build:docs": "pnpm run docs --build",
37
- "build:module": "tsc -p tsconfig.build.json",
34
+ "docs": "gem-book docs",
35
+ "build:docs": "gem-book build docs",
36
+ "build:module": "tsrs -p tsconfig.build.json",
38
37
  "build:lib": "rslib build",
39
38
  "build": "pnpm build:module && pnpm build:lib",
40
39
  "start": "pnpm build:module --watch",
41
40
  "test": "cross-env wtr"
42
41
  },
43
42
  "devDependencies": {
44
- "@gemjs/config": "^2.1.3",
43
+ "@gemjs/config": "^2.1.4",
45
44
  "@rslib/core": "^0.23.0",
46
45
  "@swc/helpers": "^0.5.23",
47
46
  "@types/chai": "^5.0.0",
@@ -51,7 +50,7 @@
51
50
  "@web/test-runner": "^0.18.1",
52
51
  "chai": "^5.1.1",
53
52
  "rimraf": "^3.0.2",
54
- "typescript": "^5.6.2"
53
+ "tsrs": "^0.18.0-ts7.1.0-dev.20260929"
55
54
  },
56
55
  "author": "mantou132",
57
56
  "license": "MIT",
@@ -1,9 +1,27 @@
1
1
  import { aria, mounted, shadow } from '../../lib/decorators';
2
2
  import { history } from '../../lib/history';
3
3
  import { GemElement } from '../../lib/reactive';
4
+ import type { Store } from '../../lib/store';
4
5
  import { connect, createStore } from '../../lib/store';
5
6
 
6
7
  const open = Symbol('open mark');
8
+ // 静态方法返回不可伪造的值,防止子类覆盖
9
+ const final = Symbol();
10
+
11
+ type ModalElement = GemElement & { label: string };
12
+
13
+ export interface ModalClass<T> extends Omit<typeof GemElement, 'prototype'> {
14
+ new (): ModalElement;
15
+ prototype: ModalElement;
16
+ inertStore: HTMLElement[];
17
+ instance: GemElement | null;
18
+ store: Store<{ [open]: boolean } & T>;
19
+ readonly isOpen: boolean;
20
+ open(opts: T): typeof final;
21
+ close(): typeof final;
22
+ closeHandle(): typeof final;
23
+ shouldClose(): boolean;
24
+ }
7
25
 
8
26
  /**
9
27
  * 需要使用范型,所以导出一个函数,用来*创建*类似 Confirm 元素的基类;
@@ -13,9 +31,7 @@ const open = Symbol('open mark');
13
31
  * 使用 `mounted`/`unmounted` 定义 `open`/`close` 回调;
14
32
  * 模拟 top layer:https://github.com/whatwg/html/issues/4633.
15
33
  */
16
- export function createModalClass<T extends Record<string, unknown>>(options: T) {
17
- const final = Symbol();
18
-
34
+ export function createModalClass<T extends Record<string, unknown>>(options: T): ModalClass<T> {
19
35
  @shadow()
20
36
  @aria({ role: 'alertdialog', ariaModal: 'true' })
21
37
  class ModalElement extends GemElement {
@@ -40,7 +56,7 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
40
56
  * 自带 100ms 延迟,以允许在其他 Dialog 的 `shouldClose` 中调用此方法;
41
57
  * 浏览器 history 为异步 API,需要设置较长延迟;
42
58
  */
43
- static open(opts: T) {
59
+ static open(opts: T): typeof final {
44
60
  const instance = new this();
45
61
  this.instance = instance;
46
62
  this.inertStore = ([...document.body.children] as HTMLElement[]).filter((e) => !e.inert);
@@ -63,7 +79,7 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
63
79
  * @final
64
80
  * UI 中执行关闭
65
81
  */
66
- static close() {
82
+ static close(): typeof final {
67
83
  history.back();
68
84
  return final;
69
85
  }
@@ -72,7 +88,7 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
72
88
  * @final
73
89
  * history 中自动调用
74
90
  */
75
- static closeHandle() {
91
+ static closeHandle(): typeof final {
76
92
  this.inertStore.forEach((e) => (e.inert = false));
77
93
  this.instance?.remove();
78
94
  this.store({ [open]: false, ...options });
@@ -1,9 +1,8 @@
1
1
  /**
2
- * - 预渲染可能造成重复的元素,在序列化时可以用正则移除 GemReflectElement 渲染的从开始到结束连续的元素
3
- * - `replaceAll(/<!--gem-reflect-start-->.*?<!--gem-reflect-end-->/gs, '')`
2
+ * 把子节点移动到 `target`(默认 `document.head`)中,用开始和结束注释标记这些节点,
3
+ * 修改 `target` 或者卸载时一起移动或移除
4
4
  *
5
- * 不能在在顶级代码块中清除这些元素,因为重复加载该代码时会把正常 reflect 元素清除,
6
- * 比如 duoyun-ui 文档站加载示例元素时把 GemReflectElement 渲染的元素清除
5
+ * 服务端渲染时不执行 effect,客户端水合后才移动,使用 `getLogicalParent` 从被移动的节点找到 reflect 元素
7
6
  */
8
7
 
9
8
  import { attribute, effect, property, willMount } from '../../lib/decorators';
@@ -25,6 +24,31 @@ const getReflectFragment = (startNode: Comment) => {
25
24
  return result;
26
25
  };
27
26
 
27
+ // 移动到的目标元素和片段开始注释对应的 reflect 元素,向上查找元素时跨越 reflect
28
+ const targets = new WeakSet<Node>();
29
+ const startNodes = new WeakMap<Node, GemReflectElement>();
30
+
31
+ // 向前查找片段的开始注释,遇到其他片段的结束注释时不在片段中
32
+ const getReflectElement = (node: Node) => {
33
+ for (let n = node.previousSibling; n; n = n.previousSibling) {
34
+ const ele = startNodes.get(n);
35
+ if (ele) return ele;
36
+ if (n.nodeValue === END) return;
37
+ }
38
+ };
39
+
40
+ /**
41
+ * 节点的逻辑父元素:被 reflect 移动的节点是 reflect 元素,ShadowRoot 中顶层的节点是宿主,其他是父元素
42
+ */
43
+ export function getLogicalParent(node: Node): Element | null {
44
+ const parent = node.parentNode;
45
+ if (parent && targets.has(parent)) {
46
+ const ele = getReflectElement(node);
47
+ if (ele) return ele;
48
+ }
49
+ return parent instanceof ShadowRoot ? parent.host : node.parentElement;
50
+ }
51
+
28
52
  export class GemReflectElement extends GemElement {
29
53
  @attribute method: 'prepend' | 'append';
30
54
 
@@ -45,6 +69,8 @@ export class GemReflectElement extends GemElement {
45
69
 
46
70
  @effect((i) => [i.target])
47
71
  #changeTarget = () => {
72
+ targets.add(this.target);
73
+ startNodes.set(this.#startNode, this);
48
74
  (this.#childNodes || [this.#startNode, ...this.childNodes, this.#endNode]).forEach((node, index, arr) => {
49
75
  const prev = arr[index - 1];
50
76
  if (prev) {
@@ -9,32 +9,54 @@ import { connect, createStore } from '../../lib/store';
9
9
  import type { QueryString } from '../../lib/utils';
10
10
  import { addListener } from '../../lib/utils';
11
11
 
12
+ type Params = Record<string, string>;
13
+ type Matcher = (path: string) => Params | null | undefined;
14
+
12
15
  const UNNAMED_PARAM_PREFIX = '_';
13
16
 
14
- // '/test/:a' /^\/test\/(?<a>[^/]+)$/i
15
- // '/test/:a/b' /^\/test\/?<a>([^/]+)\/b$/i
16
- // '/test/*' /^\/test\/(?<_0>.*)$/i
17
- // '/test/:a/*' /^\/test\/(?<a>[^/]+)\/(?<_0>.*)$/i
18
- // '/test/*/:b /^\/test\/(?<_0>.*)\/(?<a>[^/]+)$/i
19
- class ParamsRegExp extends RegExp {
20
- constructor(pattern: string) {
21
- let i = 0;
22
- super(
23
- `^${pattern.replace(/((:[^/$]+)|(\*))/g, (_m, name: string) => {
24
- if (name === '*') return `(?<${UNNAMED_PARAM_PREFIX}${i++}>.*)`;
25
- return `(?<${name.slice(1)}>[^/]+)`;
26
- })}$`,
27
- );
28
- }
17
+ // 不支持 `URLPattern` 时转换成正则
18
+ // '/test/:a' /^\/test\/(?<a>[^/]+)$/
19
+ // '/test/:a/b' /^\/test\/(?<a>[^/]+)\/b$/
20
+ // '/test/*' /^\/test\/(?<_0>.*)$/
21
+ // '/test/:a/*' /^\/test\/(?<a>[^/]+)\/(?<_0>.*)$/
22
+ // '/test/*/:b' /^\/test\/(?<_0>.*)\/(?<b>[^/]+)$/
23
+ function createRegExpMatcher(pattern: string): Matcher {
24
+ let i = 0;
25
+ const source = pattern.replace(/:[^/$]+|\*/g, (name) =>
26
+ name === '*' ? `(?<${UNNAMED_PARAM_PREFIX}${i++}>.*)` : `(?<${name.slice(1)}>[^/]+)`,
27
+ );
28
+ const reg = new RegExp(`^${source}$`);
29
+ return (path) => {
30
+ const result = path.match(reg);
31
+ if (!result) return null;
32
+ const params: Params = {};
33
+ for (const key in result.groups) {
34
+ params[key.startsWith(UNNAMED_PARAM_PREFIX) ? key.slice(1) : key] = result.groups[key];
35
+ }
36
+ return params;
37
+ };
29
38
  }
30
39
 
31
- type Params = Record<string, string>;
32
- declare global {
33
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1731418
34
- // https://github.com/WebKit/standards-positions/issues/61
35
- var URLPattern: any;
40
+ // pattern 开头不含参数和特殊字符的部分
41
+ const LITERAL_PREFIX = /^[\w\-./~]*/;
42
+
43
+ // `URLPattern` 的创建和 `exec` 都比较慢,不含参数时直接比较,否则先检查固定的开头
44
+ function createMatcher(pattern: string): Matcher {
45
+ const literal = LITERAL_PREFIX.exec(pattern)![0];
46
+ if (literal === pattern) return (path) => (path === pattern ? {} : null);
47
+ // 根 layout 的 pattern
48
+ if (pattern === '*') return (path) => ({ 0: path });
49
+ if (!globalThis.URLPattern) return createRegExpMatcher(pattern);
50
+ const urlPattern = new URLPattern({ pathname: pattern });
51
+ // 参数前的 `/` 可能是可选的,例如 `/a/:b?` 匹配 `/a`
52
+ const prefix = literal.replace(/\/$/, '');
53
+ return (path) =>
54
+ path.startsWith(prefix) ? (urlPattern.exec({ pathname: path })?.pathname.groups as Params | undefined) : null;
36
55
  }
37
56
 
57
+ // pattern 来自路由定义,数量有限
58
+ const matchers = new Map<string, Matcher>();
59
+
38
60
  /**
39
61
  * - 大小写敏感
40
62
  * - 匹配成功时返回 params
@@ -49,22 +71,9 @@ declare global {
49
71
  * ```
50
72
  */
51
73
  export function matchPath(pattern: string, path: string) {
52
- if (globalThis.URLPattern) {
53
- const urLPattern = new URLPattern({ pathname: pattern });
54
- const matchResult = urLPattern.exec({ pathname: path }) || urLPattern.exec({ pathname: `${path}/` });
55
- if (!matchResult) return null;
56
- return matchResult.pathname.groups as Params;
57
- }
58
- const reg = new ParamsRegExp(pattern);
59
- const matchResult = path.match(reg) || `${path}/`.match(reg);
60
- if (!matchResult) return null;
61
- const params: Params = {};
62
- if (matchResult.groups) {
63
- Object.entries(matchResult.groups).forEach(([key, value]) => {
64
- params[key.startsWith(UNNAMED_PARAM_PREFIX) ? key.slice(1) : key] = value;
65
- });
66
- }
67
- return params;
74
+ let match = matchers.get(pattern);
75
+ if (!match) matchers.set(pattern, (match = createMatcher(pattern)));
76
+ return match(path) || match(`${path}/`) || null;
68
77
  }
69
78
 
70
79
  export interface RouteItem<T = unknown> {
@@ -79,7 +88,7 @@ export interface RouteItem<T = unknown> {
79
88
  getContent?: (
80
89
  params: Params,
81
90
  renderRoot: DocumentFragment | GemLightRouteElement,
82
- ) => TemplateResult | Promise<TemplateResult> | undefined | Promise<undefined>;
91
+ ) => TemplateResult | undefined | Promise<TemplateResult | undefined>;
83
92
  title?: string;
84
93
  // 用来传递数据
85
94
  data?: T;
@@ -130,6 +139,9 @@ export type RouteTrigger = {
130
139
 
131
140
  const scrollPositionMap = new Map<string, number>();
132
141
 
142
+ // 导航到了新的历史记录,前进、后退和页面加载时为 `false`
143
+ let pushed = false;
144
+
133
145
  @light({ penetrable: true })
134
146
  export class GemLightRouteElement extends GemElement {
135
147
  @boolattribute transition: boolean;
@@ -148,8 +160,13 @@ export class GemLightRouteElement extends GemElement {
148
160
  @emitter routechange: Emitter<RouteItem | null>; // path 改变或者 key 改变,包含初始渲染
149
161
  @emitter error: Emitter<any>;
150
162
 
151
- /**自动让滚动容器恢复滚动位置 */
152
- @property scrollContainer?: HTMLElement | (() => HTMLElement);
163
+ /**
164
+ * 自动让滚动容器恢复滚动位置,导航到新的历史记录时滚动到顶部,
165
+ * 函数返回空值时不处理当前路由,例如只有子路由渲染后才需要滚动
166
+ */
167
+ @property scrollContainer?: Element | (() => Element | null | undefined);
168
+ /**恢复滚动位置前等待,例如等待异步加载的内容撑开滚动容器 */
169
+ @property scrollWait?: () => Promise<unknown>;
153
170
  /**默认当有 `hash` 时不进行滚动 */
154
171
  @property scrollIgnoreHash?: boolean;
155
172
 
@@ -205,14 +222,24 @@ export class GemLightRouteElement extends GemElement {
205
222
  });
206
223
  };
207
224
 
208
- #updateScrollContainerPosition = () => {
209
- if (!this.#scrollContainer) return;
210
- const scrollTop = scrollPositionMap.get(history.currentKey);
211
- if (scrollTop) {
212
- this.#scrollContainer.scroll(0, scrollTop);
213
- } else if (this.scrollIgnoreHash || !history.getParams().hash) {
214
- this.#scrollContainer.scroll(0, 0);
225
+ // 已经处理过滚动的历史记录,同一条历史记录重新渲染时保持滚动位置
226
+ #scrolledKey?: string;
227
+
228
+ #updateScrollContainerPosition = async () => {
229
+ const container = this.#scrollContainer;
230
+ const key = history.currentKey;
231
+ if (!container || key === this.#scrolledKey) return;
232
+ this.#scrolledKey = key;
233
+ const scrollTop = scrollPositionMap.get(key);
234
+ if (!pushed && scrollTop !== undefined) {
235
+ await this.scrollWait?.();
236
+ // 等待时可能已经导航
237
+ if (key === history.currentKey) container.scroll(0, scrollTop);
238
+ return;
215
239
  }
240
+ // 前进、后退或者页面加载时位置未知(例如刷新后),保持浏览器恢复的位置,浏览器只恢复文档的滚动位置
241
+ if (!pushed && container === document.scrollingElement) return;
242
+ if (this.scrollIgnoreHash || !history.getParams().hash) container.scroll(0, 0);
216
243
  };
217
244
 
218
245
  #lastLoader?: TemplateResult | Promise<TemplateResult | undefined>;
@@ -239,10 +266,13 @@ export class GemLightRouteElement extends GemElement {
239
266
 
240
267
  @effect((i) => [i.scrollContainer])
241
268
  #rememberScrollPosition = () => {
242
- if (!this.#scrollContainer) return;
243
- return addListener(history, 'beforechange', () =>
244
- scrollPositionMap.set(history.currentKey, this.#scrollContainer!.scrollTop),
245
- );
269
+ if (!this.scrollContainer) return;
270
+ return addListener(history, 'beforechange', () => {
271
+ // 前进和后退时浏览器的历史记录已经改变,Gem 的还没有改变
272
+ pushed = globalThis.history.state?.$key === history.currentKey;
273
+ const container = this.#scrollContainer;
274
+ if (container) scrollPositionMap.set(history.currentKey, container.scrollTop);
275
+ });
246
276
  };
247
277
 
248
278
  // history or i18n