@mantou/gem 1.7.6 → 1.7.8

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.
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,YAAY,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAE/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAAiC,CAAC;AAK7E,MAAM,UAAU,WAAW,CAAyB,WAAc;IAChE,IAAI,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACvC,CAAC;IACD,gBAAgB,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,GAAG,EAAc,CAAC,CAAC;IACzD,OAAO,WAAuB,CAAC;AACjC,CAAC;AAQD,MAAM,UAAU,cAAc,CAA2B,cAAiB;IACxE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,OAAO,cAA6B,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,WAAW,CAAyB,KAAe,EAAE,KAAkB;IACrF,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC1B,YAAY,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,OAAO,CAAyB,KAAe,EAAE,IAAgB;IAC/E,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,GAAG,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,UAAU,CAAyB,KAAe,EAAE,IAAgB;IAClF,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/lib/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAE/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAAiC,CAAC;AAK7E,MAAM,UAAU,WAAW,CAAyB,WAAc;IAChE,IAAI,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACvC,CAAC;IACD,gBAAgB,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,GAAG,EAAc,CAAC,CAAC;IACzD,OAAO,WAAuB,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,WAAW,CAAyB,KAAe,EAAE,KAAkB;;IACrF,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5B,MAAA,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,0CAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5C,YAAY,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,QAAQ,CAAyB,WAAc;IAC7D,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACvC,OAAO,CAAC,KAAK,EAAE,CAAC,KAAkB,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAU,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,OAAO,CAAyB,KAAe,EAAE,IAAgB;IAC/E,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,GAAG,EAAE,CAAC,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,UAAU,CAAyB,KAAe,EAAE,IAAgB;IAClF,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version = "1.7.6";
1
+ export declare const version = "1.7.8";
2
2
  //# sourceMappingURL=version.d.ts.map
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '1.7.6';
1
+ export const version = '1.7.8';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantou/gem",
3
- "version": "1.7.6",
3
+ "version": "1.7.8",
4
4
  "description": "💎 使用自定义元素的轻量级 WebApp 开发框架",
5
5
  "keywords": [
6
6
  "frontend",
@@ -75,6 +75,7 @@ export class GemGestureElement extends GemElement {
75
75
  this.addEventListener('pointerdown', this.#onStart);
76
76
  this.addEventListener('pointermove', this.#onMoveSet);
77
77
  this.addEventListener('pointerup', this.#onEnd);
78
+ this.addEventListener('pointerleave', this.#onEnd); // 有时候 up 没有触发?
78
79
  this.addEventListener('pointercancel', this.#onEnd);
79
80
  this.addEventListener('dragstart', (evt) => evt.preventDefault());
80
81
  }
@@ -14,13 +14,13 @@
14
14
  */
15
15
 
16
16
  import { GemElement, html } from '../../lib/element';
17
- import { attribute, connectStore } from '../../lib/decorators';
17
+ import { attribute, boolattribute } from '../../lib/decorators';
18
18
  import { updateStore, connect } from '../../lib/store';
19
19
  import { titleStore } from '../../lib/history';
20
20
 
21
21
  const defaultTitle = document.title;
22
22
 
23
- function updateTitle(str?: string | null, prefix = '', suffix = '') {
23
+ function setDocumentTitle(str?: string | null, prefix = '', suffix = '') {
24
24
  const title = titleStore.title || str;
25
25
  if (title && title !== defaultTitle) {
26
26
  GemTitleElement.title = title;
@@ -31,18 +31,20 @@ function updateTitle(str?: string | null, prefix = '', suffix = '') {
31
31
  }
32
32
  }
33
33
 
34
+ connect(titleStore, setDocumentTitle);
35
+
34
36
  export const PREFIX = `${defaultTitle} | `;
35
37
  export const SUFFIX = ` - ${defaultTitle}`;
36
38
 
37
39
  /**
38
- * 允许声明式设置 `document.title`
40
+ * 映射到 `document.title`
39
41
  * @attr prefix
40
42
  * @attr suffix
41
43
  */
42
- @connectStore(titleStore)
43
44
  export class GemTitleElement extends GemElement {
44
45
  @attribute prefix: string;
45
46
  @attribute suffix: string;
47
+ @boolattribute inert: boolean;
46
48
 
47
49
  // 没有后缀的当前标题
48
50
  static title = document.title;
@@ -64,15 +66,16 @@ export class GemTitleElement extends GemElement {
64
66
  });
65
67
  }
66
68
 
69
+ mounted = () => {
70
+ this.effect(
71
+ () => !this.inert && connect(titleStore, this.update),
72
+ () => [this.inert],
73
+ );
74
+ };
75
+
67
76
  render() {
68
77
  // 多个 <gem-title> 时,最终 document.title 按执行顺序决定
69
- updateTitle(this.textContent, this.prefix, this.suffix);
70
-
71
- if (this.hidden) {
72
- return html``;
73
- }
78
+ setDocumentTitle(this.textContent?.trim(), this.prefix, this.suffix);
74
79
  return html`${GemTitleElement.title}`;
75
80
  }
76
81
  }
77
-
78
- connect(titleStore, updateTitle);
@@ -7,12 +7,14 @@ type SomeType<T> = {
7
7
 
8
8
  const themeStoreMap = new WeakMap();
9
9
 
10
+ /**获取主题原始值 */
10
11
  export function getThemeStore<T>(theme: SomeType<T>) {
11
12
  return themeStoreMap.get(theme) as Store<T>;
12
13
  }
13
14
 
14
15
  const themePropsMap = new WeakMap();
15
16
 
17
+ /**获取 css 变量名 */
16
18
  export function getThemeProps<T>(theme: SomeType<T>) {
17
19
  return themePropsMap.get(theme) as SomeType<T>;
18
20
  }
@@ -62,3 +64,8 @@ export function updateTheme<T = Record<string, unknown>>(theme: SomeType<T>, new
62
64
  updateStore(getThemeStore(theme), newThemeObj);
63
65
  setThemeFnMap.get(theme)();
64
66
  }
67
+
68
+ export function useTheme<T extends Record<string, unknown>>(themeObj: T) {
69
+ const theme = createTheme(themeObj);
70
+ return [theme, (newThemeObj: Partial<T>) => updateTheme(theme, newThemeObj)] as const;
71
+ }
@@ -2,7 +2,7 @@ import { html, render, TemplateResult } from 'lit-html';
2
2
 
3
3
  import type { GemReflectElement } from '../elements/reflect';
4
4
 
5
- import { connect, disconnect, Store } from './store';
5
+ import { connect, Store } from './store';
6
6
  import {
7
7
  LinkedList,
8
8
  addMicrotask,
@@ -289,15 +289,19 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
289
289
 
290
290
  /**
291
291
  * @lifecycle
292
- * 返回 null 时渲染 lit-html 定义的空内容
293
- * 返回 undefined 时不会调用 `render()`
292
+ *
293
+ * - 不提供 `render` 时显示子内容
294
+ * - 返回 `null` 时渲染空内容
295
+ * - 返回 `undefined` 时不会调用 `render()`, 也就是不会更新以前的内容
294
296
  * */
295
297
  render?(): TemplateResult | null | undefined;
296
298
 
297
299
  #render = () => {
298
300
  this.#execMemo();
299
- if (this.render) return this.render();
300
- return this.#renderRoot === this ? undefined : html`<slot></slot>`;
301
+ const isLight = this.#renderRoot === this;
302
+ const temp = this.render ? this.render() : isLight ? undefined : html`<slot></slot>`;
303
+ if (temp === undefined) return;
304
+ render(temp, this.#renderRoot);
301
305
  };
302
306
 
303
307
  /**@lifecycle */
@@ -311,8 +315,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
311
315
 
312
316
  #updateCallback = () => {
313
317
  if (this.#isMounted && this.#shouldUpdate()) {
314
- const temp = this.#render();
315
- temp !== undefined && render(temp, this.#renderRoot);
318
+ this.#render();
316
319
  addMicrotask(this.#updated);
317
320
  addMicrotask(this.#execEffect);
318
321
  }
@@ -359,6 +362,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
359
362
  return GemElement.#final;
360
363
  }
361
364
 
365
+ #disconnectStore?: (() => void)[];
362
366
  #connectedCallback = () => {
363
367
  if (this.#isAppendReason) {
364
368
  this.#isAppendReason = false;
@@ -370,15 +374,16 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
370
374
 
371
375
  this.willMount?.();
372
376
  const { observedStores, rootElement } = this.constructor as typeof GemElement;
373
- observedStores?.forEach((store) => {
374
- connect(store, this.#update);
375
- });
376
- const temp = this.#render();
377
- temp !== undefined && render(temp, this.#renderRoot);
377
+ this.#disconnectStore = observedStores?.map((store) => connect(store, this.#update));
378
+ this.#render();
378
379
  this.#isMounted = true;
379
380
  this.#unmountCallback = this.mounted?.();
380
381
  this.#initEffect();
381
- if (rootElement && (this.getRootNode() as ShadowRoot).host?.tagName !== rootElement.toUpperCase()) {
382
+ if (
383
+ rootElement &&
384
+ this.isConnected &&
385
+ (this.getRootNode() as ShadowRoot).host?.tagName !== rootElement.toUpperCase()
386
+ ) {
382
387
  throw new GemError(`not allow current root type`);
383
388
  }
384
389
  };
@@ -406,10 +411,10 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
406
411
  /**
407
412
  * @private
408
413
  * @final
409
- * use `mounted`
414
+ * use `mounted`; 允许手动调用 `connectedCallback` 以清除装饰器定义的字段
410
415
  */
411
416
  connectedCallback() {
412
- if (this.#isAsync) {
417
+ if (this.isConnected && this.#isAsync) {
413
418
  asyncRenderTaskList.add(this.#connectedCallback);
414
419
  } else {
415
420
  this.#connectedCallback();
@@ -436,10 +441,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
436
441
  return;
437
442
  }
438
443
  this.#isMounted = false;
439
- const { observedStores } = this.constructor as typeof GemElement;
440
- observedStores?.forEach((store) => {
441
- disconnect(store, this.#update);
442
- });
444
+ this.#disconnectStore?.forEach((disconnect) => disconnect());
443
445
  execCallback(this.#unmountCallback);
444
446
  this.unmounted?.();
445
447
  this.#effectList?.forEach(({ preCallback }) => execCallback(preCallback));
@@ -1,6 +1,6 @@
1
1
  // https://github.com/WICG/navigation-api#stakeholder-feedback
2
2
 
3
- import { createStore, updateStore, connect } from './store';
3
+ import { connect, useStore } from './store';
4
4
  import { QueryString, cleanObject, GemError, absoluteLocation } from './utils';
5
5
 
6
6
  const nativeHistory = window.history;
@@ -18,7 +18,7 @@ export interface HistoryState {
18
18
  [index: string]: any;
19
19
  }
20
20
 
21
- const store = createStore<HistoryState>({
21
+ const [store, updateHistoryStore] = useStore<HistoryState>({
22
22
  $hasCloseHandle: false,
23
23
  $hasOpenHandle: false,
24
24
  $hasShouldCloseHandle: false,
@@ -85,10 +85,12 @@ function updateHistory(p: UpdateHistoryParams, native: typeof nativeHistory.push
85
85
  $hasOpenHandle: !!open,
86
86
  $hasShouldCloseHandle: !!shouldClose,
87
87
  $key: getKey(),
88
+ $title: title,
88
89
  ...data,
89
90
  };
90
91
  paramsMap.set(state.$key, params);
91
- updateStore(cleanObject(store), state);
92
+ cleanObject(store);
93
+ updateHistoryStore(state);
92
94
  const url = getUrlBarPath(path) + new QueryString(query) + hash;
93
95
  const prevHash = decodeURIComponent(location.hash);
94
96
  native(state, title, url);
@@ -100,12 +102,14 @@ function updateHistoryByNative(data: any, title: string, originUrl: string, nati
100
102
  validData(data);
101
103
  const state = {
102
104
  $key: getKey(),
105
+ $title: title,
103
106
  ...(data || {}),
104
107
  };
105
108
  const { pathname, search, hash } = new URL(originUrl, location.origin + location.pathname);
106
109
  const params = normalizeParams({ path: pathname, query: new QueryString(search), hash, title, data });
107
110
  paramsMap.set(state.$key, params);
108
- updateStore(cleanObject(store), state);
111
+ cleanObject(store);
112
+ updateHistoryStore(state);
109
113
  const url = getUrlBarPath(pathname) + params.query + hash;
110
114
  const prevHash = location.hash;
111
115
  native(state, title, url);
@@ -113,7 +117,7 @@ function updateHistoryByNative(data: any, title: string, originUrl: string, nati
113
117
  if (prevHash !== hash) window.dispatchEvent(new CustomEvent('hashchange'));
114
118
  }
115
119
 
116
- const gemBasePathStore = createStore({
120
+ const [gemBasePathStore, updateBasePathStore] = useStore({
117
121
  basePath: '',
118
122
  });
119
123
 
@@ -126,7 +130,7 @@ class GemHistory {
126
130
  }
127
131
  set basePath(basePath: string) {
128
132
  // 应用初始化的时候设置
129
- updateStore(gemBasePathStore, { basePath });
133
+ updateBasePathStore({ basePath });
130
134
  // paramsMap 更新后 ui 才会更新
131
135
  Object.assign(paramsMap.get(store.$key)!, { path: getInternalPath(location.pathname) });
132
136
  }
@@ -135,7 +139,7 @@ class GemHistory {
135
139
  }
136
140
  updateParams(params: UpdateHistoryParams) {
137
141
  Object.assign(paramsMap.get(store.$key)!, params);
138
- updateStore(store, {});
142
+ updateHistoryStore();
139
143
  }
140
144
  push(params: UpdateHistoryParams) {
141
145
  updateHistory(params, nativePushState);
@@ -162,7 +166,7 @@ class GemHistory {
162
166
  // `<gem-**>` 只读写父应用 `basePath`
163
167
  const gemHistory = new GemHistory();
164
168
 
165
- const gemTitleStore = createStore({ title: '' });
169
+ const [gemTitleStore, updateTitleStore] = useStore({ title: '' });
166
170
 
167
171
  const _GEMHISTORY = { history: gemHistory, titleStore: gemTitleStore, basePathStore: gemBasePathStore };
168
172
 
@@ -195,7 +199,7 @@ if (!window._GEMHISTORY) {
195
199
  const { pathname, search, hash } = location;
196
200
  gemHistory.replace({ path: pathname, query: search, hash });
197
201
  } else if (nativeHistory.state.$hasCloseHandle) {
198
- updateStore(store, nativeHistory.state);
202
+ updateHistoryStore(nativeHistory.state);
199
203
  const params = normalizeParams({ title: document.title });
200
204
  paramsMap.set(store.$key, params);
201
205
  // 有 handle 返回键的页面刷新需要清除返回 handler
@@ -203,7 +207,7 @@ if (!window._GEMHISTORY) {
203
207
  } else {
204
208
  // 有 gem 历史的正常普通刷新, 储存 params
205
209
  const params = normalizeParams({ title: document.title, hash: location.hash });
206
- updateStore(store, {
210
+ updateHistoryStore({
207
211
  $key: getKey(),
208
212
  ...(nativeHistory.state || {}),
209
213
  });
@@ -220,7 +224,7 @@ if (!window._GEMHISTORY) {
220
224
  connect(store, () => {
221
225
  const { title } = gemHistory.getParams();
222
226
  if (title !== gemTitleStore.title) {
223
- updateStore(gemTitleStore, { title });
227
+ updateTitleStore({ title });
224
228
  }
225
229
  });
226
230
 
@@ -251,7 +255,7 @@ if (!window._GEMHISTORY) {
251
255
  path: pathname,
252
256
  query: new QueryString(search),
253
257
  hash,
254
- title: document.title, // 浏览器缓存的 title
258
+ title: newState.$title, // document.title 是导航前的
255
259
  data: newState,
256
260
  });
257
261
  }
@@ -288,7 +292,8 @@ if (!window._GEMHISTORY) {
288
292
  }
289
293
  }
290
294
 
291
- updateStore(cleanObject(store), newState);
295
+ cleanObject(store);
296
+ updateHistoryStore(newState);
292
297
  });
293
298
  }
294
299
 
package/src/lib/store.ts CHANGED
@@ -13,32 +13,22 @@ export function createStore<T extends NonPrimitive>(originStore: T) {
13
13
  return originStore as Store<T>;
14
14
  }
15
15
 
16
- interface StoreObjectSet {
17
- [store: string]: NonPrimitive;
18
- }
19
- export type StoreSet<T> = {
20
- [P in keyof T]: T[P] & Store<T[P]>;
21
- };
22
- export function createStoreSet<T extends StoreObjectSet>(originStoreSet: T) {
23
- const keys = Object.keys(originStoreSet);
24
- keys.forEach((key) => {
25
- createStore(originStoreSet[key]);
26
- });
27
-
28
- return originStoreSet as StoreSet<T>;
29
- }
30
-
31
16
  export function updateStore<T extends NonPrimitive>(store: Store<T>, value?: Partial<T>) {
32
17
  Object.assign(store, value);
33
- const listeners = StoreListenerMap.get(store);
34
- listeners?.forEach((func) => {
18
+ StoreListenerMap.get(store)?.forEach((func) => {
35
19
  addMicrotask(func);
36
20
  });
37
21
  }
38
22
 
23
+ export function useStore<T extends NonPrimitive>(originStore: T) {
24
+ const store = createStore(originStore);
25
+ return [store, (value?: Partial<T>) => updateStore(store, value)] as const;
26
+ }
27
+
39
28
  export function connect<T extends NonPrimitive>(store: Store<T>, func: () => void) {
40
29
  const listeners = StoreListenerMap.get(store);
41
30
  listeners?.add(func);
31
+ return () => listeners?.delete(func);
42
32
  }
43
33
 
44
34
  export function disconnect<T extends NonPrimitive>(store: Store<T>, func: () => void) {
@@ -1 +1 @@
1
- export const version = '1.7.6';
1
+ export const version = '1.7.8';
@@ -1,3 +1,11 @@
1
+ /**
2
+ * 测试 HTMLElement 没有的特性或者不常用的特性
3
+ * - 异步渲染
4
+ * - Light DOM 自定义元素
5
+ * - 生命周期以及 Effect/Memo
6
+ * - 派生元素(类扩展)
7
+ * - 无渲染内容 Gem 元素
8
+ */
1
9
  import { fixture, expect, nextFrame } from '@open-wc/testing';
2
10
 
3
11
  import { GemElement, html } from '../../lib/element';
@@ -120,7 +128,10 @@ describe('gem element 生命周期', () => {
120
128
  const el = container.firstElementChild as LifecycleGemElement;
121
129
  expect(el.appTitle).to.equal('title');
122
130
  expect(el.renderCount).to.equal(1);
123
- expect((el.cloneNode() as LifecycleGemElement).appTitle).to.equal('title');
131
+ const clone = el.cloneNode() as LifecycleGemElement;
132
+ // v2 BUG
133
+ clone.connectedCallback();
134
+ expect(clone.appTitle).to.equal('title');
124
135
 
125
136
  const el2 = new LifecycleGemElement('', '2');
126
137
  expect(el2.appTitle).to.equal('');
@@ -51,20 +51,24 @@ class DecoratorGemElement extends GemElement {
51
51
  }
52
52
  }
53
53
 
54
- @connectStore(store)
55
- @adoptedStyle(styles)
56
- @customElement('decorator-gem-demo2')
57
- class DecoratorGemElement2 extends GemElement {
58
- static observedStores = [];
59
- static adoptedStyleSheets = [];
60
- renderCount = 0;
61
- render() {
62
- this.renderCount++;
63
- return html``;
64
- }
65
- }
66
-
67
54
  describe('装饰器', () => {
55
+ it('使用装饰器定义的未插入文档元素', async () => {
56
+ const el = new DecoratorGemElement();
57
+ expect(el.propData).to.eql({ value: '' });
58
+ expect(el.getAttribute('rank-attr')).to.equal(null);
59
+ // v2 BUG
60
+ expect(el.rankAttr).to.equal('');
61
+ el.connectedCallback();
62
+ expect(el.getAttribute('rank-attr')).to.equal(null);
63
+ expect(el.rankAttr).to.equal('');
64
+
65
+ el.rankAttr = 'attr';
66
+ el.propData = { value: '1' };
67
+ const el2 = el.cloneNode() as DecoratorGemElement;
68
+ expect(el2.getAttribute('rank-attr')).to.equal('attr');
69
+ expect(el2.rankAttr).to.equal('attr');
70
+ expect(el2.propData).to.eql({ value: '' });
71
+ });
68
72
  it('装饰器定义的自定义元素', async () => {
69
73
  let a = 1;
70
74
  const el: DecoratorGemElement = await fixture(html`
@@ -104,12 +108,4 @@ describe('装饰器', () => {
104
108
  expect(el.renderCount).to.equal(3);
105
109
  expect(a).to.equal(2);
106
110
  });
107
-
108
- it('装饰器和静态属性共存', async () => {
109
- const el: DecoratorGemElement2 = await fixture(html`<decorator-gem-demo2></decorator-gem-demo2>`);
110
- updateStore(store, { a: 3 });
111
- await Promise.resolve();
112
- expect(el.renderCount).to.equal(2);
113
- expect(el.shadowRoot?.adoptedStyleSheets.length).to.equal(1);
114
- });
115
111
  });
@@ -1,12 +1,13 @@
1
1
  import { expect, fixture } from '@open-wc/testing';
2
2
 
3
- import { customElement, property, RefObject, refobject } from '../../lib/decorators';
3
+ import { attribute, customElement, property, RefObject, refobject } from '../../lib/decorators';
4
4
  import { GemElement, html } from '../../lib/element';
5
5
 
6
6
  @customElement('app-children')
7
7
  export class Children extends GemElement {
8
8
  @refobject inputRef: RefObject<HTMLInputElement>;
9
9
  @property value?: { value: number };
10
+ @attribute attr: string;
10
11
 
11
12
  render() {
12
13
  return html`<input ref=${this.inputRef.ref} />`;
@@ -19,13 +20,13 @@ export class App extends GemElement {
19
20
  @refobject childrenRef2: RefObject<Children>;
20
21
  render() {
21
22
  return html`
22
- <app-children ref=${this.childrenRef1.ref} .value=${{ value: 1 }}></app-children>
23
- <app-children ref=${this.childrenRef2.ref} .value=${{ value: 2 }}></app-children>
23
+ <app-children ref=${this.childrenRef1.ref} .value=${{ value: 1 }} attr="1"></app-children>
24
+ <app-children ref=${this.childrenRef2.ref} .value=${{ value: 2 }} attr="2"></app-children>
24
25
  `;
25
26
  }
26
27
  }
27
- describe('多个 gem element', () => {
28
- it('ref & prop', async () => {
28
+ describe('多个 gem element 一起工作', () => {
29
+ it('ref & prop & attr', async () => {
29
30
  const el: App = await fixture(html`<app-root></app-root>`);
30
31
  const children1 = el.childrenRef1.element!;
31
32
  const children2 = el.childrenRef2.element!;
@@ -34,5 +35,7 @@ describe('多个 gem element', () => {
34
35
  expect(input1 !== input2).to.equal(true);
35
36
  expect(children1.value).to.eql({ value: 1 });
36
37
  expect(children2.value).to.eql({ value: 2 });
38
+ expect(children1.attr).to.eql('1');
39
+ expect(children2.attr).to.eql('2');
37
40
  });
38
41
  });
@@ -1,6 +1,6 @@
1
1
  import { expect, aTimeout } from '@open-wc/testing';
2
2
 
3
- import { StoreListenerMap, createStore, createStoreSet, updateStore, connect, disconnect } from '../lib/store';
3
+ import { StoreListenerMap, createStore, updateStore, connect, disconnect } from '../lib/store';
4
4
 
5
5
  describe('store 测试', () => {
6
6
  it('create store', () => {
@@ -8,8 +8,6 @@ describe('store 测试', () => {
8
8
  const store = createStore(origin);
9
9
  expect(store).to.equal(origin);
10
10
  expect(!!StoreListenerMap.get(store)).to.equal(true);
11
- const stores = createStoreSet({ a: {} });
12
- expect(!!StoreListenerMap.get(stores.a)).to.equal(true);
13
11
  });
14
12
  it('update store', async () => {
15
13
  const store = createStore({ a: 1 });