@mantou/gem 1.6.18 → 1.7.0

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 (42) hide show
  1. package/dist/gem.es.js +54 -44
  2. package/dist/gem.umd.js +22 -22
  3. package/elements/base/modal-factory.d.ts +18 -15
  4. package/elements/base/modal-factory.d.ts.map +1 -1
  5. package/elements/base/reflect.d.ts.map +1 -1
  6. package/elements/base/reflect.js +1 -3
  7. package/elements/base/reflect.js.map +1 -1
  8. package/elements/base/route.d.ts +1 -0
  9. package/elements/base/route.d.ts.map +1 -1
  10. package/elements/base/route.js +16 -4
  11. package/elements/base/route.js.map +1 -1
  12. package/elements/base/title.d.ts +1 -1
  13. package/elements/base/title.js +1 -1
  14. package/helper/i18n.js.map +1 -1
  15. package/lib/decorators.d.ts +1 -1
  16. package/lib/decorators.js +3 -3
  17. package/lib/decorators.js.map +1 -1
  18. package/lib/element.d.ts +4 -5
  19. package/lib/element.d.ts.map +1 -1
  20. package/lib/element.js +31 -33
  21. package/lib/element.js.map +1 -1
  22. package/lib/history.js +8 -7
  23. package/lib/history.js.map +1 -1
  24. package/lib/utils.js.map +1 -1
  25. package/lib/version.d.ts +1 -1
  26. package/lib/version.d.ts.map +1 -1
  27. package/lib/version.js +1 -1
  28. package/lib/version.js.map +1 -1
  29. package/package.json +2 -2
  30. package/src/elements/base/reflect.ts +1 -4
  31. package/src/elements/base/route.ts +16 -7
  32. package/src/elements/base/title.ts +1 -1
  33. package/src/helper/i18n.ts +1 -1
  34. package/src/lib/decorators.ts +3 -3
  35. package/src/lib/element.ts +35 -41
  36. package/src/lib/history.ts +10 -10
  37. package/src/lib/utils.ts +1 -1
  38. package/src/lib/version.ts +1 -1
  39. package/src/test/gem-element/advance.test.ts +8 -8
  40. package/src/test/gem-element/basic.test.ts +1 -1
  41. package/src/test/gem-element/decorators.test.ts +1 -1
  42. package/src/test/history.test.ts +1 -1
@@ -1,16 +1,13 @@
1
1
  import { GemElement } from '../../lib/element';
2
2
  import { property } from '../../lib/decorators';
3
3
 
4
- // support prerender
5
- document.querySelectorAll('[data-reflect]').forEach((e) => e.remove());
6
-
7
4
  export class GemReflectElement extends GemElement {
8
5
  @property target: HTMLElement = document.head;
9
6
 
10
7
  constructor() {
11
8
  super({ isLight: true });
12
9
  this.hidden = true;
13
- this.dataset.gemreflect = '';
10
+ this.dataset.gemReflect = '';
14
11
  this.effect(
15
12
  () => {
16
13
  const childNodes = [...this.childNodes];
@@ -1,18 +1,18 @@
1
1
  import { GemElement, html, TemplateResult } from '../../lib/element';
2
- import { property, connectStore, emitter, Emitter } from '../../lib/decorators';
2
+ import { property, connectStore, emitter, Emitter, boolattribute } from '../../lib/decorators';
3
3
  import { createStore, updateStore, Store } from '../../lib/store';
4
4
  import { titleStore, history, UpdateHistoryParams } from '../../lib/history';
5
5
  import { QueryString } from '../../lib/utils';
6
6
 
7
- interface NamePostition {
7
+ interface NamePosition {
8
8
  [index: string]: number;
9
9
  }
10
10
 
11
11
  // TODO: use `URLPattern`
12
12
  class ParamsRegExp extends RegExp {
13
- namePosition: NamePostition;
13
+ namePosition: NamePosition;
14
14
  constructor(pattern: string) {
15
- const namePosition: NamePostition = {};
15
+ const namePosition: NamePosition = {};
16
16
  let i = 0;
17
17
  super(
18
18
  `^${pattern
@@ -103,6 +103,7 @@ type State = {
103
103
  */
104
104
  @connectStore(history.store)
105
105
  export class GemRouteElement extends GemElement<State> {
106
+ @boolattribute transition: boolean;
106
107
  @property routes?: RouteItem[] | RoutesObject;
107
108
  /**
108
109
  * @example
@@ -172,9 +173,17 @@ export class GemRouteElement extends GemElement<State> {
172
173
  this.#lastLoader = undefined;
173
174
  this.currentRoute = route;
174
175
  this.currentParams = params;
175
- this.setState({ content });
176
- this.routechange(this.currentRoute);
177
- this.#updateLocationStore();
176
+ const changeContent = () => {
177
+ this.setState({ content });
178
+ this.routechange(this.currentRoute);
179
+ this.#updateLocationStore();
180
+ return Promise.resolve();
181
+ };
182
+ if (this.transition && 'startViewTransition' in document) {
183
+ (document as any).startViewTransition(changeContent);
184
+ } else {
185
+ changeContent();
186
+ }
178
187
  };
179
188
 
180
189
  mounted() {
@@ -2,7 +2,7 @@
2
2
  * 标题显示在:
3
3
  *
4
4
  * - 桌面端 Tab 的 title
5
- * - 移动端 Appbar 的 title
5
+ * - 移动端 AppBar 的 title
6
6
  *
7
7
  * 修改标题:
8
8
  *
@@ -142,7 +142,7 @@ export class I18n<T = Record<string, Msg>> {
142
142
  const currentLanguagePack = this.resources[this.currentLanguage] as any;
143
143
  const fallbackLanguagePack = this.resources[this.fallbackLanguage] as any;
144
144
  const msg = currentLanguagePack[s] || fallbackLanguagePack[s];
145
- const rawValue: string = msg?.message || msg || `[${s}]`;
145
+ const rawValue: string = msg?.message || msg || `[${s as string}]`;
146
146
  if (!rest.length) return rawValue;
147
147
  return splice(rawValue, ...rest);
148
148
  }
@@ -83,13 +83,13 @@ export function numattribute(target: GemElementPrototype, prop: string) {
83
83
  * ```
84
84
  */
85
85
  export function property(target: GemElementPrototype, prop: string) {
86
- pushStaticField(target, 'observedPropertys', prop);
86
+ pushStaticField(target, 'observedProperties', prop);
87
87
  defineProperty(target, prop);
88
88
  }
89
89
 
90
90
  /**
91
91
  * 定义一个元素[内部](https://html.spec.whatwg.org/multipage/custom-elements.html#elementinternals) state,
92
- * 类似 `:checked`,用于自定义 css 伪类(:--xxx),默认值即为字段名。
92
+ * 类似 `:checked`,用于自定义 css 伪类(:state(xxx)),默认值即为字段名。
93
93
  * 重新赋值转换 css 伪类
94
94
  * 将来也可能用于 IDE 识别
95
95
  *
@@ -103,7 +103,7 @@ export function property(target: GemElementPrototype, prop: string) {
103
103
  export function state(target: GemElementPrototype, prop: string) {
104
104
  const attr = camelToKebabCase(prop);
105
105
  pushStaticField(target, 'defineCSSStates', attr);
106
- defineCSSState(target, prop, `--${attr}`);
106
+ defineCSSState(target, prop, attr);
107
107
  }
108
108
 
109
109
  /**
@@ -24,11 +24,10 @@ export { guard } from 'lit-html/directives/guard';
24
24
 
25
25
  export { ifDefined } from 'lit-html/directives/if-defined';
26
26
 
27
- type CustomStateSet = Set<string>;
28
-
29
27
  declare global {
30
28
  interface ElementInternals extends ARIAMixin {
31
- states: CustomStateSet;
29
+ // https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet
30
+ states?: Set<string>;
32
31
  // https://w3c.github.io/aria/#role_definitions
33
32
  role?: string;
34
33
  }
@@ -68,12 +67,14 @@ type GetDepFun<T> = () => T;
68
67
  type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
69
68
  type EffectItem<T> = {
70
69
  callback: EffectCallback<T>;
71
- initialized: boolean;
70
+ initialized?: boolean;
71
+ inConstructor?: boolean;
72
72
  values?: T;
73
73
  getDep?: GetDepFun<T>;
74
74
  preCallback?: () => void;
75
75
  };
76
76
 
77
+ const constructorSymbol = Symbol('constructor');
77
78
  const initSymbol = Symbol('init');
78
79
  const updateSymbol = Symbol('update');
79
80
 
@@ -88,7 +89,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
88
89
  static observedAttributes?: string[]; // WebAPI 中是实时检查这个列表
89
90
  static booleanAttributes?: Set<string>;
90
91
  static numberAttributes?: Set<string>;
91
- static observedPropertys?: string[];
92
+ static observedProperties?: string[];
92
93
  static observedStores?: Store<unknown>[];
93
94
  static adoptedStyleSheets?: Sheet<unknown>[];
94
95
  static defineEvents?: string[];
@@ -115,6 +116,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
115
116
 
116
117
  constructor({ isAsync, isLight, delegatesFocus }: GemElementOptions = {}) {
117
118
  super();
119
+ (this as any)[constructorSymbol] = true;
118
120
 
119
121
  // 外部不可见,但允许类外面使用
120
122
  addMicrotask(() => ((this as any)[initSymbol] = false));
@@ -174,27 +176,19 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
174
176
 
175
177
  get internals() {
176
178
  if (!this.#internals) {
177
- const getCustomStateSet = () => {
178
- // https://wicg.github.io/custom-state-pseudo-class/
179
- const getV = (v: string) => v.replace(/-/g, '');
180
- return {
181
- has: (v) => getV(v) in this.dataset,
182
- add: (v) => {
183
- this.dataset[getV(v)] = '';
184
- },
185
- delete: (v) => delete this.dataset[getV(v)],
186
- } as CustomStateSet;
187
- };
188
- if (!this.attachInternals) {
189
- // https://bugs.webkit.org/show_bug.cgi?id=197960
190
- this.attachInternals = () => {
191
- return { states: getCustomStateSet() } as any;
192
- };
193
- }
194
179
  this.#internals = this.attachInternals();
195
- if (!this.#internals.states) {
196
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1588763
197
- this.#internals.states = getCustomStateSet();
180
+ // https://groups.google.com/a/chromium.org/g/blink-dev/c/JvpHoUfhJYE?pli=1
181
+ try {
182
+ this.#internals.states?.add('foo');
183
+ this.#internals.states?.delete('foo');
184
+ } catch {
185
+ Reflect.defineProperty(this.#internals, 'states', {
186
+ value: new Proxy(this.#internals.states!, {
187
+ get(target: any, p) {
188
+ return (str: string) => target[p](`--${str}`);
189
+ },
190
+ }),
191
+ });
198
192
  }
199
193
  }
200
194
  return this.#internals;
@@ -254,16 +248,14 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
254
248
  * }
255
249
  * ```
256
250
  * */
257
- effect = <T = any[] | undefined>(
258
- callback: EffectCallback<T>,
259
- getDep?: T extends any[] ? () => [...T] : undefined,
260
- ) => {
251
+ effect = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => T : undefined) => {
261
252
  if (!this.#effectList) this.#effectList = [];
262
253
  const effectItem: EffectItem<T> = {
263
254
  callback,
264
255
  getDep,
265
256
  values: undefined,
266
- initialized: !!this.#isMounted,
257
+ initialized: this.#isMounted,
258
+ inConstructor: (this as any)[constructorSymbol],
267
259
  };
268
260
  // 以挂载时立即执行副作用,未挂载时等挂载后执行
269
261
  if (this.#isMounted) {
@@ -287,14 +279,13 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
287
279
  * }
288
280
  * ```
289
281
  * */
290
- memo = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => [...T] : undefined) => {
282
+ memo = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => T : undefined) => {
291
283
  if (!this.#memoList) this.#memoList = [];
292
284
  this.#memoList.push({
293
285
  callback,
294
286
  getDep,
295
287
  values: [Symbol()],
296
- // 是否是 `willMount` 中定义
297
- initialized: !!this.#isMounted,
288
+ inConstructor: (this as any)[constructorSymbol],
298
289
  });
299
290
  };
300
291
 
@@ -400,6 +391,8 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
400
391
  }
401
392
 
402
393
  #connectedCallback = () => {
394
+ // 似乎这是最早的判断不在 `constructor` 中的地方
395
+ (this as any)[constructorSymbol] = false;
403
396
  if (this.#isAppendReason) {
404
397
  this.#isAppendReason = false;
405
398
  return;
@@ -478,7 +471,8 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
478
471
  this.#effectList?.forEach((effectItem) => {
479
472
  execCallback(effectItem.preCallback);
480
473
  });
481
- this.#memoList = this.#memoList?.filter(({ initialized }) => !initialized);
474
+ this.#effectList = this.#effectList?.filter(({ inConstructor }) => inConstructor);
475
+ this.#memoList = this.#memoList?.filter(({ inConstructor }) => inConstructor);
482
476
  }
483
477
  }
484
478
 
@@ -581,7 +575,7 @@ export function defineRef(target: GemElement, prop: string, ref: string) {
581
575
  return ref;
582
576
  },
583
577
  get element() {
584
- const gemReflects = ([...ele.querySelectorAll('[data-gemreflect]')] as GemReflectElement[]).map(
578
+ const gemReflects = ([...ele.querySelectorAll('[data-gem-reflect]')] as GemReflectElement[]).map(
585
579
  (e) => e.target,
586
580
  );
587
581
  for (const e of [ele, ...gemReflects]) {
@@ -603,15 +597,15 @@ export function defineCSSState(target: GemElement, prop: string, state: string)
603
597
  get() {
604
598
  const that = this as GemElement;
605
599
  const { states } = that.internals;
606
- return states.has?.(state);
600
+ return states?.has(state);
607
601
  },
608
602
  set(v: boolean) {
609
603
  const that = this as GemElement;
610
604
  const { states } = that.internals;
611
605
  if (v) {
612
- states.add(state);
606
+ states?.add(state);
613
607
  } else {
614
- states.delete(state);
608
+ states?.delete(state);
615
609
  }
616
610
  },
617
611
  });
@@ -620,12 +614,12 @@ export function defineCSSState(target: GemElement, prop: string, state: string)
620
614
  export const nativeDefineElement = customElements.define.bind(customElements);
621
615
  customElements.define = (name: string, cls: CustomElementConstructor, options?: ElementDefinitionOptions) => {
622
616
  if (cls.prototype instanceof GemElement) {
623
- const { observedAttributes, observedPropertys, defineEvents, defineCSSStates, defineRefs } =
617
+ const { observedAttributes, observedProperties, defineEvents, defineCSSStates, defineRefs } =
624
618
  cls as unknown as typeof GemElement;
625
619
  observedAttributes?.forEach((attr) => defineAttribute(cls.prototype, kebabToCamelCase(attr), attr));
626
- observedPropertys?.forEach((prop) => defineProperty(cls.prototype, prop));
620
+ observedProperties?.forEach((prop) => defineProperty(cls.prototype, prop));
627
621
  defineEvents?.forEach((event) => defineProperty(cls.prototype, kebabToCamelCase(event), event));
628
- defineCSSStates?.forEach((state) => defineCSSState(cls.prototype, kebabToCamelCase(state), `--${state}`));
622
+ defineCSSStates?.forEach((state) => defineCSSState(cls.prototype, kebabToCamelCase(state), state));
629
623
  defineRefs?.forEach((ref) => defineRef(cls.prototype, kebabToCamelCase(ref), ref));
630
624
  }
631
625
 
@@ -50,13 +50,13 @@ function validData(data: any) {
50
50
  if ($hasShouldCloseHandle) throw new GemError('`$hasShouldCloseHandle` is not allowed');
51
51
  }
52
52
 
53
- function getUrlbarPath(internalPath: string) {
53
+ function getUrlBarPath(internalPath: string) {
54
54
  return gemHistory.basePath ? gemHistory.basePath + internalPath : internalPath;
55
55
  }
56
56
 
57
- function getInternalPath(urlbarPath: string) {
58
- if (urlbarPath === gemHistory.basePath) return '/';
59
- return urlbarPath.replace(new RegExp(`^${gemHistory.basePath}/`), '/');
57
+ function getInternalPath(urlBarPath: string) {
58
+ if (urlBarPath === gemHistory.basePath) return '/';
59
+ return urlBarPath.replace(new RegExp(`^${gemHistory.basePath}/`), '/');
60
60
  }
61
61
 
62
62
  function normalizeParams(params: UpdateHistoryParams): HistoryParams {
@@ -88,7 +88,7 @@ function updateHistory(p: UpdateHistoryParams, native: typeof nativeHistory.push
88
88
  };
89
89
  paramsMap.set(state.$key, params);
90
90
  updateStore(cleanObject(store), state);
91
- const url = getUrlbarPath(path) + new QueryString(query) + hash;
91
+ const url = getUrlBarPath(path) + new QueryString(query) + hash;
92
92
  const prevHash = decodeURIComponent(location.hash);
93
93
  native(state, title, url);
94
94
  if (prevHash !== hash) window.dispatchEvent(new CustomEvent('hashchange'));
@@ -105,7 +105,7 @@ function updateHistoryByNative(data: any, title: string, originUrl: string, nati
105
105
  const params = normalizeParams({ path: pathname, query: new QueryString(search), hash, title, data });
106
106
  paramsMap.set(state.$key, params);
107
107
  updateStore(cleanObject(store), state);
108
- const url = getUrlbarPath(pathname) + params.query + hash;
108
+ const url = getUrlBarPath(pathname) + params.query + hash;
109
109
  const prevHash = location.hash;
110
110
  native(state, title, url);
111
111
  // `location.hash` 和 `hash` 都已经进行了 url 编码,可以直接进行相等判断
@@ -127,13 +127,13 @@ class GemHistory {
127
127
  // 应用初始化的时候设置
128
128
  updateStore(gemBasePathStore, { basePath });
129
129
  // paramsMap 更新后 ui 才会更新
130
- Object.assign(paramsMap.get(store.$key), { path: getInternalPath(location.pathname) });
130
+ Object.assign(paramsMap.get(store.$key)!, { path: getInternalPath(location.pathname) });
131
131
  }
132
132
  getParams() {
133
133
  return paramsMap.get(store.$key)!;
134
134
  }
135
135
  updateParams(params: UpdateHistoryParams) {
136
- Object.assign(paramsMap.get(store.$key), params);
136
+ Object.assign(paramsMap.get(store.$key)!, params);
137
137
  updateStore(store, {});
138
138
  }
139
139
  push(params: UpdateHistoryParams) {
@@ -257,8 +257,8 @@ if (!window._GEMHISTORY) {
257
257
 
258
258
  // url 变化前 historyItem
259
259
  const prevState = store;
260
-
261
- if (parseFloat(newState.$key) > parseFloat(prevState.$key) && newState.$hasOpenHandle) {
260
+ const isForward = parseFloat(newState.$key) > parseFloat(prevState.$key);
261
+ if (isForward && newState.$hasOpenHandle) {
262
262
  // 返回键关闭的 modal 能前进键重新打开
263
263
  // 刷新后不能工作:刷新后 historyItem 中只有 url
264
264
  // 能在子 app 中工作
package/src/lib/utils.ts CHANGED
@@ -272,7 +272,7 @@ export function createCSSSheet<T extends StyledKeyValuePair>(rules: T | string,
272
272
  style = rules;
273
273
  } else {
274
274
  Object.keys(rules).forEach((key: keyof T) => {
275
- sheet[key] = `${key}-${randomStr()}`;
275
+ sheet[key] = `${key as string}-${randomStr()}`;
276
276
  style += rules[key].styledContent.replace(/&/g, sheet[key]);
277
277
  });
278
278
  }
@@ -1 +1 @@
1
- export const version = '1.6.18';
1
+ export const version = '1.7.0';
@@ -222,19 +222,19 @@ class MemoGemDemo extends GemElement {
222
222
  constructor() {
223
223
  super();
224
224
  this.memo(
225
- () => this.memoCount++,
225
+ () => (this.memoCount += 1),
226
226
  () => [this.attr, this.prop],
227
227
  );
228
228
  }
229
229
  willMount() {
230
230
  this.memo(
231
231
  (_arr) => {
232
- this.memoCount++;
232
+ this.memoCount += 2;
233
233
  },
234
234
  () => [],
235
235
  );
236
236
  this.memo(() => {
237
- this.memoCount++;
237
+ this.memoCount += 4;
238
238
  });
239
239
  }
240
240
  }
@@ -242,23 +242,23 @@ customElements.define('memo-gem-demo', MemoGemDemo);
242
242
  describe('gem element Memo', () => {
243
243
  it('依赖当前值', async () => {
244
244
  const el: MemoGemDemo = await fixture(html`<memo-gem-demo></memo-gem-demo>`);
245
- expect(el.memoCount).to.equal(3);
245
+ expect(el.memoCount).to.equal(7);
246
246
  el.attr = 'b';
247
247
  el.prop = {};
248
248
  await nextFrame();
249
- expect(el.memoCount).to.equal(5);
249
+ expect(el.memoCount).to.equal(12);
250
250
 
251
251
  el.update();
252
252
  await Promise.resolve();
253
- expect(el.memoCount).to.equal(6);
253
+ expect(el.memoCount).to.equal(16);
254
254
 
255
255
  document.body.append(el);
256
- expect(el.memoCount).to.equal(6);
256
+ expect(el.memoCount).to.equal(16);
257
257
 
258
258
  el.remove();
259
259
  await Promise.resolve();
260
260
  document.body.append(el);
261
- expect(el.memoCount).to.equal(9);
261
+ expect(el.memoCount).to.equal(22);
262
262
  });
263
263
  });
264
264
 
@@ -26,7 +26,7 @@ class GemDemo extends GemElement {
26
26
  static observedStores = [store];
27
27
 
28
28
  prop = { value: '' };
29
- static observedPropertys = ['prop'];
29
+ static observedProperties = ['prop'];
30
30
 
31
31
  static adoptedStyleSheets = [styles];
32
32
  state = { value: '' };
@@ -87,7 +87,7 @@ describe('装饰器', () => {
87
87
  expect(DecoratorGemElement.defineParts).to.eql(['header-part']);
88
88
  expect(DecoratorGemElement.defineSlots).to.eql(['body-slot']);
89
89
  expect(DecoratorGemElement.defineRefs?.[0].startsWith('input-ref-')).to.equal(true);
90
- expect(DecoratorGemElement.observedPropertys).to.eql(['dataProp']);
90
+ expect(DecoratorGemElement.observedProperties).to.eql(['dataProp']);
91
91
  expect(el.rankAttr).to.equal('attr');
92
92
  expect(el.rankDisabled).to.equal(true);
93
93
  expect(el.rankCount).to.equal(2);
@@ -10,7 +10,7 @@ describe('history 测试', () => {
10
10
  await aTimeout(10);
11
11
  expect(window.location.hash).to.equal('#a');
12
12
  history.back();
13
- await aTimeout(10);
13
+ await aTimeout(600);
14
14
  expect(window.location.hash).to.equal(hash);
15
15
  history.push({ path: '/a', hash: '#b' });
16
16
  await aTimeout(10);