@mantou/gem 1.7.1 → 1.7.3

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 (86) hide show
  1. package/dist/gem.es.js +367 -369
  2. package/dist/gem.umd.js +24 -28
  3. package/elements/base/dialog.d.ts +7 -7
  4. package/elements/base/dialog.d.ts.map +1 -1
  5. package/elements/base/dialog.js +10 -3
  6. package/elements/base/dialog.js.map +1 -1
  7. package/elements/base/gesture.d.ts +4 -13
  8. package/elements/base/gesture.d.ts.map +1 -1
  9. package/elements/base/gesture.js +151 -134
  10. package/elements/base/gesture.js.map +1 -1
  11. package/elements/base/link.d.ts +10 -10
  12. package/elements/base/link.d.ts.map +1 -1
  13. package/elements/base/link.js +69 -63
  14. package/elements/base/link.js.map +1 -1
  15. package/elements/base/modal-factory.d.ts +117 -83
  16. package/elements/base/modal-factory.d.ts.map +1 -1
  17. package/elements/base/modal-factory.js +12 -8
  18. package/elements/base/modal-factory.js.map +1 -1
  19. package/elements/base/reflect.js.map +1 -1
  20. package/elements/base/route.d.ts +12 -5
  21. package/elements/base/route.d.ts.map +1 -1
  22. package/elements/base/route.js +16 -4
  23. package/elements/base/route.js.map +1 -1
  24. package/elements/base/title.d.ts +3 -1
  25. package/elements/base/title.d.ts.map +1 -1
  26. package/elements/base/title.js +14 -16
  27. package/elements/base/title.js.map +1 -1
  28. package/elements/base/unsafe.js.map +1 -1
  29. package/elements/base/use.d.ts +1 -1
  30. package/elements/base/use.d.ts.map +1 -1
  31. package/elements/base/use.js +27 -17
  32. package/elements/base/use.js.map +1 -1
  33. package/helper/i18n.d.ts +2 -2
  34. package/helper/i18n.d.ts.map +1 -1
  35. package/helper/i18n.js.map +1 -1
  36. package/helper/mediaquery.d.ts +16 -18
  37. package/helper/mediaquery.d.ts.map +1 -1
  38. package/helper/mediaquery.js +42 -41
  39. package/helper/mediaquery.js.map +1 -1
  40. package/helper/request.d.ts +1 -1
  41. package/helper/request.d.ts.map +1 -1
  42. package/helper/request.js.map +1 -1
  43. package/helper/ssr-shim.js.map +1 -1
  44. package/helper/theme.d.ts +2 -2
  45. package/helper/theme.d.ts.map +1 -1
  46. package/helper/theme.js +20 -24
  47. package/helper/theme.js.map +1 -1
  48. package/lib/decorators.d.ts +4 -4
  49. package/lib/decorators.d.ts.map +1 -1
  50. package/lib/decorators.js +1 -2
  51. package/lib/decorators.js.map +1 -1
  52. package/lib/element.d.ts +5 -7
  53. package/lib/element.d.ts.map +1 -1
  54. package/lib/element.js +46 -71
  55. package/lib/element.js.map +1 -1
  56. package/lib/history.d.ts +1 -1
  57. package/lib/history.d.ts.map +1 -1
  58. package/lib/history.js +2 -2
  59. package/lib/history.js.map +1 -1
  60. package/lib/store.d.ts +2 -2
  61. package/lib/store.d.ts.map +1 -1
  62. package/lib/store.js.map +1 -1
  63. package/lib/utils.d.ts +8 -32
  64. package/lib/utils.d.ts.map +1 -1
  65. package/lib/utils.js +7 -22
  66. package/lib/utils.js.map +1 -1
  67. package/lib/version.d.ts +1 -1
  68. package/lib/version.js +1 -1
  69. package/package.json +2 -2
  70. package/src/elements/base/dialog.ts +8 -3
  71. package/src/elements/base/gesture.ts +51 -45
  72. package/src/elements/base/link.ts +64 -62
  73. package/src/elements/base/modal-factory.ts +5 -0
  74. package/src/elements/base/route.ts +21 -4
  75. package/src/elements/base/title.ts +8 -11
  76. package/src/elements/base/use.ts +3 -3
  77. package/src/helper/mediaquery.ts +43 -42
  78. package/src/helper/theme.ts +22 -24
  79. package/src/lib/decorators.ts +1 -2
  80. package/src/lib/element.ts +38 -63
  81. package/src/lib/history.ts +2 -1
  82. package/src/lib/utils.ts +11 -43
  83. package/src/lib/version.ts +1 -1
  84. package/src/test/gem-element/advance.test.ts +16 -8
  85. package/src/test/history.test.ts +3 -1
  86. package/src/test/utils.test.ts +8 -1
@@ -8,7 +8,6 @@ interface NamePosition {
8
8
  [index: string]: number;
9
9
  }
10
10
 
11
- // TODO: use `URLPattern`
12
11
  class ParamsRegExp extends RegExp {
13
12
  namePosition: NamePosition;
14
13
  constructor(pattern: string) {
@@ -28,10 +27,27 @@ class ParamsRegExp extends RegExp {
28
27
  }
29
28
 
30
29
  type Params = Record<string, string>;
30
+ declare global {
31
+ interface Window {
32
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1731418
33
+ // https://github.com/WebKit/standards-positions/issues/61
34
+ URLPattern: any;
35
+ }
36
+ }
31
37
 
32
- // `/a/b/:c/:d` `/a/b/1/2`
38
+ // URLPattern 大小写敏感
33
39
  // 匹配成功时返回 params
40
+ // `/a/b/:c/:d` `/a/b/1/2`
41
+ // pattern 以 / 结尾时能匹配 2 中路径
42
+ // `/a/b/:c/:d/` `/a/b/1/2`
43
+ // `/a/b/:c/:d/` `/a/b/1/2/`
34
44
  export function matchPath(pattern: string, path: string) {
45
+ if (window.URLPattern) {
46
+ const urLPattern = new window.URLPattern({ pathname: pattern });
47
+ const matchResult = urLPattern.exec({ pathname: path }) || urLPattern.exec({ pathname: `${path}/` });
48
+ if (!matchResult) return null;
49
+ return matchResult.pathname.groups as Params;
50
+ }
35
51
  const reg = new ParamsRegExp(pattern);
36
52
  const matchResult = path.match(reg) || `${path}/`.match(reg);
37
53
  if (!matchResult) return null;
@@ -111,13 +127,14 @@ export class GemRouteElement extends GemElement<State> {
111
127
  * html`<gem-route .locationStore=${locationStore}>`
112
128
  */
113
129
  @property locationStore?: Store<{ path: string; params: Params; query: QueryString; data?: any }>;
114
- @property key: any; // 除了 href 提供另外一种方式来更新,比如语言更新也需要刷新 <gem-route>
130
+ @property key?: any; // 除了 href 提供另外一种方式来更新,比如语言更新也需要刷新 <gem-route>
115
131
  @emitter routechange: Emitter<RouteItem | null>; // path 改变或者 key 改变,包含初始渲染
116
132
  @emitter loading: Emitter<RouteItem>;
117
133
  @emitter error: Emitter<any>;
118
134
 
135
+ /**当前使用的路由对象 */
119
136
  currentRoute: RouteItem | null;
120
- // 当前匹配的路由的 params
137
+ /**当前匹配的路由的 params */
121
138
  currentParams: Params = {};
122
139
 
123
140
  #lastLoader?: Promise<TemplateResult>;
@@ -27,26 +27,23 @@ export class GemTitleElement extends GemElement {
27
27
  // 没有后缀的标题
28
28
  static title = document.title;
29
29
  static defaultTitle = document.title;
30
+ static defaultPrefix = `${document.title} | `;
30
31
  static defaultSuffix = ` - ${document.title}`;
31
32
 
32
33
  static setTitle(title: string) {
33
34
  updateStore(titleStore, { title });
34
35
  }
35
36
 
36
- static updateTitle(str = '', suffix = '') {
37
- const { title } = titleStore;
38
- if (title && title !== GemTitleElement.defaultTitle) {
39
- GemTitleElement.title = title;
40
- document.title = GemTitleElement.title + suffix;
41
- } else if (str) {
42
- GemTitleElement.title = str;
43
- document.title = GemTitleElement.title + suffix;
37
+ static updateTitle(title = titleStore.title, prefix = '', suffix = '') {
38
+ if (title === GemTitleElement.defaultTitle) {
39
+ document.title = GemTitleElement.title = title;
44
40
  } else {
45
- GemTitleElement.title = GemTitleElement.defaultTitle;
46
- document.title = GemTitleElement.title;
41
+ GemTitleElement.title = title;
42
+ document.title = prefix + GemTitleElement.title + suffix;
47
43
  }
48
44
  }
49
45
 
46
+ @attribute prefix: string;
50
47
  @attribute suffix: string;
51
48
 
52
49
  constructor() {
@@ -56,7 +53,7 @@ export class GemTitleElement extends GemElement {
56
53
 
57
54
  render() {
58
55
  // 多个 <gem-title> 时,最终 document.title 按执行顺序决定
59
- GemTitleElement.updateTitle(this.textContent || '', this.suffix);
56
+ GemTitleElement.updateTitle(this.textContent || undefined, this.prefix, this.suffix);
60
57
 
61
58
  if (this.hidden) {
62
59
  return html``;
@@ -18,7 +18,7 @@ export class GemUseElement extends GemElement {
18
18
 
19
19
  @state active: boolean;
20
20
 
21
- private getContent() {
21
+ #getContent = () => {
22
22
  const ele = this.element || (this.selector ? (this.root || document).querySelector(this.selector) : null);
23
23
  if (typeof ele === 'string') {
24
24
  let temp = eleCache.get(ele);
@@ -33,7 +33,7 @@ export class GemUseElement extends GemElement {
33
33
  } else {
34
34
  return ele?.cloneNode(true);
35
35
  }
36
- }
36
+ };
37
37
 
38
38
  render() {
39
39
  return html`
@@ -48,7 +48,7 @@ export class GemUseElement extends GemElement {
48
48
  height: 100%;
49
49
  }
50
50
  </style>
51
- ${this.getContent()}
51
+ ${this.#getContent()}
52
52
  <slot></slot>
53
53
  `;
54
54
  }
@@ -1,77 +1,78 @@
1
1
  // https://gist.github.com/gokulkrishh/242e68d1ee94ad05f488
2
2
  // https://mediag.com/news/popular-screen-resolutions-designing-for-all/
3
3
 
4
- const matche = (query: string) => window.matchMedia(query).matches;
4
+ const match = (query: string) => window.matchMedia(query).matches;
5
5
 
6
6
  export const mediaQuery = {
7
7
  PRINT: 'print',
8
8
  get isPrint() {
9
- return matche(this.PRINT);
9
+ return match(this.PRINT);
10
10
  },
11
+
11
12
  HOVER: '(hover: hover)',
12
13
  get isHover() {
13
- return matche(this.HOVER);
14
- },
15
-
16
- PHONE: '(min-width: 319px) and (max-width: 480px)',
17
- get isPhone() {
18
- return matche(this.PHONE);
14
+ return match(this.HOVER);
19
15
  },
20
16
 
21
- LAPTOP: '(min-width: 1023px) and (max-width: 1280px) and (orientation: landscape)',
22
- get isLaptop() {
23
- return matche(this.LAPTOP);
17
+ PWA: '(display-mode: standalone)',
18
+ // PWA: '(display-mode: browser)', // debugging
19
+ get isPWA() {
20
+ return match(this.PWA);
24
21
  },
25
22
 
26
- DESKTOP: '(min-width: 1279px) and (orientation: landscape)',
27
- get isDesktop() {
28
- return matche(this.DESKTOP);
23
+ MOTION_REDUCE: '(prefers-reduced-motion: reduce)',
24
+ get isMotionReduce() {
25
+ return match(this.MOTION_REDUCE);
29
26
  },
30
27
 
31
- WIDTHSCREEN: '(min-width: 1919px) and (orientation: landscape)',
32
- get isWidthScreen() {
33
- return matche(this.WIDTHSCREEN);
28
+ DATA_REDUCE: '(prefers-reduced-data: reduce)',
29
+ get isDataReduce() {
30
+ return match(this.DATA_REDUCE);
34
31
  },
35
32
 
36
- PHONE_LANDSCAPE: '(min-width: 479px) and (max-width: 959px) and (orientation: landscape)',
37
- // PHONE_LANDSCAPE: '(min-width: 481px) and (max-width: 959px)',
38
- get isPhoneLandscape() {
39
- return matche(this.PHONE_LANDSCAPE);
33
+ WATCH: '(width < 320px)',
34
+ get isWatch() {
35
+ return match(this.WATCH);
40
36
  },
41
37
 
42
- TABLET: '(min-width: 768px) and (max-width: 1024px)',
43
- get isTablet() {
44
- return matche(this.TABLET);
38
+ SMALL_PHONE: '(320px <= width <= 480px) and (height <= 640px)',
39
+ get isSmallPhone() {
40
+ return match(this.SMALL_PHONE);
45
41
  },
46
42
 
47
- SMALL_PHONE: '(min-width: 319px) and (max-width: 480px) and (max-height: 640px)',
48
- get isSmallPhone() {
49
- return matche(this.SMALL_PHONE);
43
+ PHONE: '(320px <= width <= 480px)',
44
+ get isPhone() {
45
+ return match(this.PHONE);
50
46
  },
51
47
 
52
- WATCH: '(max-width: 319px)',
53
- get isWatch() {
54
- return matche(this.WATCH);
48
+ PHONE_LANDSCAPE: '(480px < width < 960px) and (orientation: landscape)',
49
+ // PHONE_LANDSCAPE: '(480px < width < 960px)',
50
+ get isPhoneLandscape() {
51
+ return match(this.PHONE_LANDSCAPE);
55
52
  },
56
53
 
57
- SHORT: '(min-width: 479px) and (max-height: 320px)',
54
+ // Widget
55
+ SHORT: '(width >= 480px) and (height <= 320px)',
58
56
  get isShort() {
59
- return matche(this.SHORT);
57
+ return match(this.SHORT);
60
58
  },
61
59
 
62
- PWA: '(display-mode: standalone)',
63
- // PWA: '(display-mode: browser)', // debugging
64
- get isPWA() {
65
- return matche(this.PWA);
60
+ // 1280 x 850
61
+ // 1024 x 768
62
+ TABLET: '(480px < width <= 1280px)',
63
+ get isTablet() {
64
+ return match(this.TABLET);
66
65
  },
67
66
 
68
- MOTION_REDUCE: '(prefers-reduced-motion: reduce)',
69
- get isMotionReduce() {
70
- return matche(this.MOTION_REDUCE);
67
+ // 1366x768
68
+ DESKTOP: '(width > 1280px) and (orientation: landscape)',
69
+ get isDesktop() {
70
+ return match(this.DESKTOP);
71
71
  },
72
72
 
73
- DATA_REDUCE: '(prefers-reduced-data: reduce)',
74
- get isDataReduce() {
75
- return matche(this.DATA_REDUCE);
73
+ // 1920x1080
74
+ WIDTHSCREEN: '(width >= 1920px) and (orientation: landscape)',
75
+ get isWidthScreen() {
76
+ return match(this.WIDTHSCREEN);
76
77
  },
77
78
  };
@@ -11,21 +11,13 @@ export function getThemeStore<T>(theme: SomeType<T>) {
11
11
  return themeStoreMap.get(theme) as Store<T>;
12
12
  }
13
13
 
14
- const themePropMap = new WeakMap();
14
+ const themePropsMap = new WeakMap();
15
15
 
16
- export function getThemeProp<T>(theme: SomeType<T>) {
17
- return themePropMap.get(theme) as SomeType<T>;
16
+ export function getThemeProps<T>(theme: SomeType<T>) {
17
+ return themePropsMap.get(theme) as SomeType<T>;
18
18
  }
19
19
 
20
- function replaceStyle(style: HTMLStyleElement, theme: Record<string, unknown>) {
21
- const themeStore = themeStoreMap.get(theme);
22
- const themeProp = themePropMap.get(theme);
23
- const rules = Object.keys(themeStore).reduce((prev, key) => {
24
- if (!themeStore[key] || !themeProp[key]) return prev;
25
- return prev + `${themeProp[key]}:${themeStore[key]};`;
26
- }, '');
27
- style.textContent = `:root, :host {${rules}}`;
28
- }
20
+ const setThemeFnMap = new WeakMap();
29
21
 
30
22
  /**
31
23
  * 创建主题,插入 `document.head`
@@ -39,17 +31,23 @@ function replaceStyle(style: HTMLStyleElement, theme: Record<string, unknown>) {
39
31
  export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
40
32
  const salt = randomStr();
41
33
  const style = document.createElement('style');
42
- const themeStore = createStore<T>(themeObj);
34
+ const store = createStore<T>(themeObj);
43
35
  const theme: Record<string, string> = {};
44
- const themeProp: Record<string, string> = {};
45
- themePropMap.set(theme, themeProp);
46
- themeStoreMap.set(theme, themeStore);
47
- Object.keys(themeStore).forEach((key) => {
48
- themeProp[key] = `--${camelToKebabCase(key)}-${salt}`;
49
- theme[key] = `var(${themeProp[key]})`;
50
- });
51
- const replace = () => replaceStyle(style, theme);
52
- connect(themeStore, replace);
36
+ const props: Record<string, string> = {};
37
+ themePropsMap.set(theme, props);
38
+ themeStoreMap.set(theme, store);
39
+ const setTheme = () =>
40
+ Object.keys(store).forEach((key) => {
41
+ if (props[key]) return;
42
+ props[key] = `--${camelToKebabCase(key)}-${salt}`;
43
+ theme[key] = `var(${props[key]})`;
44
+ });
45
+ setThemeFnMap.set(theme, setTheme);
46
+ setTheme();
47
+ const getStyle = () =>
48
+ `:root, :host {${Object.keys(store).reduce((prev, key) => prev + `${props[key]}:${store[key]};`, '')}}`;
49
+ const replace = () => (style.textContent = getStyle());
50
+ connect(store, replace);
53
51
  replace();
54
52
  (document.head || document.documentElement).append(style);
55
53
  return theme as SomeType<T>;
@@ -61,6 +59,6 @@ export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
61
59
  * @param newThemeObj 新主题
62
60
  */
63
61
  export function updateTheme<T = Record<string, unknown>>(theme: SomeType<T>, newThemeObj: Partial<T>) {
64
- const store = getThemeStore(theme);
65
- updateStore(store, newThemeObj);
62
+ updateStore(getThemeStore(theme), newThemeObj);
63
+ setThemeFnMap.get(theme)();
66
64
  }
@@ -191,7 +191,6 @@ export function adoptedStyle(style: Sheet<unknown>) {
191
191
  * ```
192
192
  */
193
193
  export function connectStore(store: Store<unknown>) {
194
- // 这里的签名该怎么写?
195
194
  return function (cls: unknown) {
196
195
  const con = cls as GemElementConstructor;
197
196
  pushStaticField(con.prototype, 'observedStores', store);
@@ -203,7 +202,7 @@ export function connectStore(store: Store<unknown>) {
203
202
  *
204
203
  * For example
205
204
  * ```ts
206
- * @rootElement(MyElement)
205
+ * @rootElement('my-element')
207
206
  * class App extends GemElement {}
208
207
  * ```
209
208
  */
@@ -13,7 +13,6 @@ import {
13
13
  kebabToCamelCase,
14
14
  PropProxyMap,
15
15
  removeItems,
16
- useNativeCSSStyleSheet,
17
16
  } from './utils';
18
17
  import * as GemExports from './element';
19
18
  import * as VersionExports from './version';
@@ -30,16 +29,13 @@ declare global {
30
29
  interface ElementInternals extends ARIAMixin {
31
30
  // https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet
32
31
  states: Set<string>;
33
- role?: string | null;
34
32
  }
35
33
  // 用于 css 选择器选择元素,使用 @refobject 自动选择获取
36
34
  // 必须使用 attr 赋值
37
35
  /**
38
36
  * @attr ref
39
- * @attr inert
40
37
  */
41
38
  interface HTMLElement {
42
- inert: boolean;
43
39
  ref: string;
44
40
  }
45
41
  }
@@ -88,6 +84,10 @@ export interface GemElementOptions {
88
84
  }
89
85
 
90
86
  export abstract class GemElement<T = Record<string, unknown>> extends HTMLElement {
87
+ // 禁止覆盖自定义元素原生生命周期方法
88
+ // https://github.com/microsoft/TypeScript/issues/21388#issuecomment-934345226
89
+ static #final = Symbol();
90
+
91
91
  // 这里只是字段申明,不能赋值,否则子类会继承被共享该字段
92
92
  static observedAttributes?: string[]; // WebAPI 中是实时检查这个列表
93
93
  static booleanAttributes?: Set<string>;
@@ -119,16 +119,16 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
119
119
 
120
120
  constructor({ isAsync, isLight, delegatesFocus, slotAssignment }: GemElementOptions = {}) {
121
121
  super();
122
- (this as any)[constructorSymbol] = true;
123
122
 
124
123
  // 外部不可见,但允许类外面使用
125
- addMicrotask(() => ((this as any)[initSymbol] = false));
126
- (this as any)[initSymbol] = true;
127
- (this as any)[updateSymbol] = () => {
124
+ addMicrotask(() => Reflect.set(this, initSymbol, false));
125
+ Reflect.set(this, constructorSymbol, true);
126
+ Reflect.set(this, initSymbol, true);
127
+ Reflect.set(this, updateSymbol, () => {
128
128
  if (this.#isMounted) {
129
129
  addMicrotask(this.#update);
130
130
  }
131
- };
131
+ });
132
132
 
133
133
  this.#isAsync = isAsync;
134
134
  this.#renderRoot = isLight ? this : this.attachShadow({ mode: 'open', delegatesFocus, slotAssignment });
@@ -142,34 +142,10 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
142
142
  this.effect(
143
143
  () => {
144
144
  const root = this.getRootNode() as ShadowRoot | Document;
145
- if (!useNativeCSSStyleSheet) {
146
- const ele: any = root === document ? document.body : root;
147
- ele._sheets = ele._sheets || {};
148
- sheets.forEach(({ style, media }: any) => {
149
- if (ele._sheets[style]) {
150
- ele._sheets[style].count++;
151
- } else {
152
- const s = document.createElement('style');
153
- s.innerHTML = style;
154
- s.media = media.mediaText;
155
- ele.append(s);
156
- ele._sheets[style] = { ele: s, count: 1 };
157
- }
158
- });
159
- return () => {
160
- sheets.forEach(({ style }: any) => {
161
- ele._sheets[style].count--;
162
- if (!ele._sheets[style].count) {
163
- delete ele._sheets[style];
164
- }
165
- });
166
- };
167
- } else {
168
- root.adoptedStyleSheets = [...root.adoptedStyleSheets, ...sheets];
169
- return () => {
170
- root.adoptedStyleSheets = removeItems(root.adoptedStyleSheets, sheets);
171
- };
172
- }
145
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, ...sheets];
146
+ return () => {
147
+ root.adoptedStyleSheets = removeItems(root.adoptedStyleSheets, sheets);
148
+ };
173
149
  },
174
150
  () => [],
175
151
  );
@@ -289,6 +265,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
289
265
  this.#memoList.push({
290
266
  callback,
291
267
  getDep,
268
+ // 这里为什么跟 effect 不同???
292
269
  values: [Symbol()],
293
270
  inConstructor: (this as any)[constructorSymbol],
294
271
  });
@@ -320,20 +297,8 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
320
297
 
321
298
  #render = () => {
322
299
  this.#execMemo();
323
- const styles = useNativeCSSStyleSheet
324
- ? ''
325
- : html`${this.shadowRoot?.adoptedStyleSheets?.map(
326
- (e: any) => html`
327
- <style media=${e.media.mediaText}>
328
- ${e.style}
329
- </style>
330
- `,
331
- )}`;
332
- if (this.render) {
333
- const r = this.render();
334
- return r && html`${r}${styles}`;
335
- }
336
- return this.#renderRoot === this ? undefined : html`<slot></slot>${styles}`;
300
+ if (this.render) return this.render();
301
+ return this.#renderRoot === this ? undefined : html`<slot></slot>`;
337
302
  };
338
303
 
339
304
  /**@lifecycle */
@@ -392,15 +357,18 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
392
357
  if (this.#isMounted) {
393
358
  addMicrotask(this.#update);
394
359
  }
360
+ return GemElement.#final;
395
361
  }
396
362
 
397
363
  #connectedCallback = () => {
398
- // 似乎这是最早的判断不在 `constructor` 中的地方
399
- (this as any)[constructorSymbol] = false;
400
364
  if (this.#isAppendReason) {
401
365
  this.#isAppendReason = false;
402
366
  return;
403
367
  }
368
+
369
+ // 似乎这是最早的判断不在 `constructor` 中的地方
370
+ Reflect.set(this, constructorSymbol, false);
371
+
404
372
  this.willMount?.();
405
373
  const { observedStores, rootElement } = this.constructor as typeof GemElement;
406
374
  observedStores?.forEach((store) => {
@@ -447,13 +415,16 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
447
415
  } else {
448
416
  this.#connectedCallback();
449
417
  }
418
+ return GemElement.#final;
450
419
  }
451
420
 
452
421
  /**
453
422
  * @private
454
423
  * @final
455
424
  */
456
- // adoptedCallback() {}
425
+ adoptedCallback() {
426
+ return GemElement.#final;
427
+ }
457
428
 
458
429
  /**
459
430
  * @private
@@ -472,12 +443,16 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
472
443
  });
473
444
  execCallback(this.#unmountCallback);
474
445
  this.unmounted?.();
475
- this.#effectList?.forEach((effectItem) => {
476
- execCallback(effectItem.preCallback);
477
- });
478
- this.#effectList = this.#effectList?.filter(({ inConstructor }) => inConstructor);
479
- this.#memoList = this.#memoList?.filter(({ inConstructor }) => inConstructor);
446
+ this.#effectList?.forEach(({ preCallback }) => execCallback(preCallback));
447
+ this.#effectList = this.#effectList?.filter(this.#filterEffect);
448
+ this.#memoList = this.#memoList?.filter(this.#filterEffect);
449
+ return GemElement.#final;
480
450
  }
451
+
452
+ #filterEffect = (e: EffectItem<any>) => {
453
+ e.initialized = false;
454
+ return e.inConstructor;
455
+ };
481
456
  }
482
457
 
483
458
  const gemElementProxyMap = new PropProxyMap<GemElement>();
@@ -503,7 +478,7 @@ export function defineAttribute(target: GemElement, prop: string, attr: string)
503
478
  },
504
479
  set(v: string | null | undefined | number | boolean) {
505
480
  const that = this as GemElement;
506
- const proxy = gemElementProxyMap.get(this) as any;
481
+ const proxy = gemElementProxyMap.get(this);
507
482
  const hasSet = proxy[prop];
508
483
  const value = that.getAttribute(attr);
509
484
  // https://github.com/whatwg/dom/issues/922
@@ -544,7 +519,7 @@ export function defineProperty(
544
519
  },
545
520
  set(v) {
546
521
  const that = this as GemElement;
547
- const proxy = gemElementProxyMap.get(that) as any;
522
+ const proxy = gemElementProxyMap.get(that);
548
523
  if (v !== proxy[prop]) {
549
524
  if (event) {
550
525
  proxy[prop] = v?.[isEventHandleSymbol]
@@ -554,7 +529,7 @@ export function defineProperty(
554
529
  that.dispatchEvent(evt);
555
530
  v(detail, options);
556
531
  };
557
- proxy[prop][isEventHandleSymbol] = true;
532
+ Reflect.set(proxy[prop]!, isEventHandleSymbol, true);
558
533
  // emitter 不触发元素更新
559
534
  } else {
560
535
  proxy[prop] = v;
@@ -627,7 +602,7 @@ customElements.define = (name: string, cls: CustomElementConstructor, options?:
627
602
  defineRefs?.forEach((ref) => defineRef(cls.prototype, kebabToCamelCase(ref), ref));
628
603
  }
629
604
 
630
- nativeDefineElement(name, cls as CustomElementConstructor, options);
605
+ nativeDefineElement(name, cls, options);
631
606
  };
632
607
 
633
608
  declare global {
@@ -1,3 +1,5 @@
1
+ // https://github.com/WICG/navigation-api#stakeholder-feedback
2
+
1
3
  import { createStore, updateStore, connect } from './store';
2
4
  import { QueryString, cleanObject, GemError, absoluteLocation } from './utils';
3
5
 
@@ -38,7 +40,6 @@ export interface UpdateHistoryParams {
38
40
  // 实际应用值
39
41
  type HistoryParams = UpdateHistoryParams & { title: string; path: string; query: QueryString; hash: string };
40
42
 
41
- // TODO: WeakRef
42
43
  const paramsMap = new Map<string, HistoryParams>();
43
44
 
44
45
  function validData(data: any) {
package/src/lib/utils.ts CHANGED
@@ -23,7 +23,7 @@ function execMicrotaskStack() {
23
23
 
24
24
  /**
25
25
  * 添加回调函数到微任务队列栈;
26
- * 先进后执行;
26
+ * 先进后执行,使用这个函数可以改变嵌套的元素 `mounted` 的顺序;
27
27
  */
28
28
  export function addMicrotaskToStack(func: () => void) {
29
29
  if (!microtaskStack.length) {
@@ -50,8 +50,8 @@ interface LinkedListItem<T> {
50
50
  next?: LinkedListItem<T>;
51
51
  }
52
52
  // work on nodejs
53
- export class LinkedList<T = any> extends (globalThis.EventTarget || Object) {
54
- addEventListener: <K extends keyof LinkedListEventMap>(
53
+ export class LinkedList<T = any> extends EventTarget {
54
+ declare addEventListener: <K extends keyof LinkedListEventMap>(
55
55
  type: K,
56
56
  listener: (this: LinkedList<T>, ev: LinkedListEventMap[K]) => any,
57
57
  options?: boolean | AddEventListenerOptions,
@@ -115,7 +115,7 @@ export class LinkedList<T = any> extends (globalThis.EventTarget || Object) {
115
115
  get(): T | undefined {
116
116
  const firstItem = this.#firstItem;
117
117
  if (!firstItem) return;
118
- this.#delete(firstItem.value);
118
+ this.delete(firstItem.value);
119
119
  return firstItem.value;
120
120
  }
121
121
  }
@@ -131,11 +131,6 @@ export class PropProxyMap<T extends NonPrimitive, V = unknown> extends WeakMap<T
131
131
  }
132
132
  }
133
133
 
134
- declare global {
135
- interface URLSearchParams {
136
- entries(): Iterable<readonly [string | number | symbol, any]>;
137
- }
138
- }
139
134
  export class QueryString extends URLSearchParams {
140
135
  constructor(param?: any) {
141
136
  super(param);
@@ -216,22 +211,6 @@ export type Sheet<T> = {
216
211
  [P in keyof T]: P;
217
212
  } & { [SheetToken]: CSSStyleSheet };
218
213
 
219
- declare global {
220
- interface CSSStyleSheet {
221
- replaceSync: (str: string) => void;
222
- }
223
-
224
- interface CSSRuleList {
225
- item(index: number): CSSStyleRule;
226
- }
227
- interface ShadowRoot {
228
- adoptedStyleSheets: CSSStyleSheet[];
229
- }
230
- interface Document {
231
- adoptedStyleSheets: CSSStyleSheet[];
232
- }
233
- }
234
-
235
214
  export type StyledType = 'id' | 'class' | 'keyframes';
236
215
  export interface StyledValueObject {
237
216
  styledContent: string;
@@ -241,22 +220,6 @@ export interface StyledKeyValuePair {
241
220
  [key: string]: StyledValueObject;
242
221
  }
243
222
 
244
- // !!! 目前只有 Chrome/FirefoxNightly 支持
245
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1520690
246
- // https://bugs.webkit.org/show_bug.cgi?id=228684
247
- export let useNativeCSSStyleSheet = true;
248
- let CSSStyleSheet = globalThis.CSSStyleSheet;
249
- try {
250
- new (CSSStyleSheet as any)();
251
- } catch {
252
- useNativeCSSStyleSheet = false;
253
- CSSStyleSheet = class {
254
- media = { mediaText: '' };
255
- style = '';
256
- replaceSync = (style: string) => (this.style = style);
257
- } as any;
258
- }
259
-
260
223
  /**
261
224
  *
262
225
  * 创建 style sheet 用于 `adoptedStyleSheets`,不支持样式更新
@@ -293,6 +256,8 @@ export function randomStr(number = 5, origin = '0123456789abcdefghijklmnopqrstuv
293
256
  const nestingRuleRegExp = new RegExp('&.*{((.|\n)*)}', 'g');
294
257
  // 只支持一层嵌套
295
258
  // https://drafts.csswg.org/css-nesting-1/
259
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1648037
260
+ // https://bugs.webkit.org/show_bug.cgi?id=223497
296
261
  function flatStyled(style: string, type: StyledType): StyledValueObject {
297
262
  const nestingRules: string[] = [];
298
263
  let styledContent =
@@ -404,6 +369,9 @@ export function classMap(object: Record<string, boolean>) {
404
369
 
405
370
  export const partMap = classMap;
406
371
 
407
- export function exportPartsMap(object: Record<string, string | undefined>) {
408
- return objectMapToString(object, ',', (key, value) => (value !== undefined ? `${key}:${value}` : ''));
372
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts
373
+ export function exportPartsMap(object: Record<string, string | undefined | boolean>) {
374
+ return objectMapToString(object, ',', (key, value) =>
375
+ value === true || key === value ? key : value ? `${key}:${value}` : '',
376
+ );
409
377
  }