@mantou/gem 1.6.17 → 1.6.19
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/dist/gem.es.js +48 -27
- package/dist/gem.umd.js +22 -22
- package/elements/base/modal-factory.d.ts +10 -6
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/route.d.ts +1 -0
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +16 -4
- package/elements/base/route.js.map +1 -1
- package/helper/i18n.js.map +1 -1
- package/helper/theme.js +1 -1
- package/helper/theme.js.map +1 -1
- package/lib/element.d.ts +2 -2
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js +25 -16
- package/lib/element.js.map +1 -1
- package/lib/history.js +8 -7
- package/lib/history.js.map +1 -1
- package/lib/utils.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
- package/src/elements/base/route.ts +16 -7
- package/src/helper/i18n.ts +1 -1
- package/src/helper/theme.ts +1 -1
- package/src/lib/element.ts +25 -18
- package/src/lib/history.ts +10 -10
- package/src/lib/utils.ts +1 -1
- package/src/lib/version.ts +1 -1
- package/src/test/gem-element/advance.test.ts +24 -12
- package/src/test/history.test.ts +1 -1
package/src/lib/element.ts
CHANGED
|
@@ -68,12 +68,14 @@ type GetDepFun<T> = () => T;
|
|
|
68
68
|
type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
|
|
69
69
|
type EffectItem<T> = {
|
|
70
70
|
callback: EffectCallback<T>;
|
|
71
|
-
initialized
|
|
71
|
+
initialized?: boolean;
|
|
72
|
+
inConstructor?: boolean;
|
|
72
73
|
values?: T;
|
|
73
74
|
getDep?: GetDepFun<T>;
|
|
74
75
|
preCallback?: () => void;
|
|
75
76
|
};
|
|
76
77
|
|
|
78
|
+
const constructorSymbol = Symbol('constructor');
|
|
77
79
|
const initSymbol = Symbol('init');
|
|
78
80
|
const updateSymbol = Symbol('update');
|
|
79
81
|
|
|
@@ -106,19 +108,22 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
106
108
|
#renderRoot: HTMLElement | ShadowRoot;
|
|
107
109
|
#internals?: ElementInternals;
|
|
108
110
|
#isAppendReason?: boolean;
|
|
111
|
+
// 和 isConnected 有区别
|
|
112
|
+
#isMounted?: boolean;
|
|
109
113
|
#isAsync?: boolean;
|
|
110
114
|
#effectList?: EffectItem<any>[];
|
|
111
|
-
#
|
|
115
|
+
#memoList?: EffectItem<any>[];
|
|
112
116
|
#unmountCallback?: any;
|
|
113
117
|
|
|
114
118
|
constructor({ isAsync, isLight, delegatesFocus }: GemElementOptions = {}) {
|
|
115
119
|
super();
|
|
120
|
+
(this as any)[constructorSymbol] = true;
|
|
116
121
|
|
|
117
122
|
// 外部不可见,但允许类外面使用
|
|
118
123
|
addMicrotask(() => ((this as any)[initSymbol] = false));
|
|
119
124
|
(this as any)[initSymbol] = true;
|
|
120
125
|
(this as any)[updateSymbol] = () => {
|
|
121
|
-
if (this
|
|
126
|
+
if (this.#isMounted) {
|
|
122
127
|
addMicrotask(this.#update);
|
|
123
128
|
}
|
|
124
129
|
};
|
|
@@ -236,7 +241,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
236
241
|
};
|
|
237
242
|
|
|
238
243
|
#execMemo = () => {
|
|
239
|
-
this.#exec(this.#
|
|
244
|
+
this.#exec(this.#memoList);
|
|
240
245
|
};
|
|
241
246
|
|
|
242
247
|
/**
|
|
@@ -252,19 +257,17 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
252
257
|
* }
|
|
253
258
|
* ```
|
|
254
259
|
* */
|
|
255
|
-
effect = <T = any[] | undefined>(
|
|
256
|
-
callback: EffectCallback<T>,
|
|
257
|
-
getDep?: T extends any[] ? () => [...T] : undefined,
|
|
258
|
-
) => {
|
|
260
|
+
effect = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => T : undefined) => {
|
|
259
261
|
if (!this.#effectList) this.#effectList = [];
|
|
260
262
|
const effectItem: EffectItem<T> = {
|
|
261
263
|
callback,
|
|
262
264
|
getDep,
|
|
263
265
|
values: undefined,
|
|
264
|
-
initialized:
|
|
266
|
+
initialized: this.#isMounted,
|
|
267
|
+
inConstructor: (this as any)[constructorSymbol],
|
|
265
268
|
};
|
|
266
269
|
// 以挂载时立即执行副作用,未挂载时等挂载后执行
|
|
267
|
-
if (this
|
|
270
|
+
if (this.#isMounted) {
|
|
268
271
|
effectItem.values = getDep?.() as T;
|
|
269
272
|
effectItem.preCallback = callback(effectItem.values);
|
|
270
273
|
}
|
|
@@ -285,14 +288,13 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
285
288
|
* }
|
|
286
289
|
* ```
|
|
287
290
|
* */
|
|
288
|
-
memo = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () =>
|
|
289
|
-
if (!this.#
|
|
290
|
-
this.#
|
|
291
|
+
memo = <T = any[] | undefined>(callback: EffectCallback<T>, getDep?: T extends any[] ? () => T : undefined) => {
|
|
292
|
+
if (!this.#memoList) this.#memoList = [];
|
|
293
|
+
this.#memoList.push({
|
|
291
294
|
callback,
|
|
292
295
|
getDep,
|
|
293
296
|
values: [Symbol()],
|
|
294
|
-
|
|
295
|
-
initialized: this.isConnected,
|
|
297
|
+
inConstructor: (this as any)[constructorSymbol],
|
|
296
298
|
});
|
|
297
299
|
};
|
|
298
300
|
|
|
@@ -349,7 +351,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
349
351
|
};
|
|
350
352
|
|
|
351
353
|
#updateCallback = () => {
|
|
352
|
-
if (this
|
|
354
|
+
if (this.#isMounted && this.#shouldUpdate()) {
|
|
353
355
|
const temp = this.#render();
|
|
354
356
|
temp !== undefined && render(temp, this.#renderRoot);
|
|
355
357
|
addMicrotask(this.#updated);
|
|
@@ -392,12 +394,14 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
392
394
|
* use `effect`
|
|
393
395
|
*/
|
|
394
396
|
attributeChangedCallback() {
|
|
395
|
-
if (this
|
|
397
|
+
if (this.#isMounted) {
|
|
396
398
|
addMicrotask(this.#update);
|
|
397
399
|
}
|
|
398
400
|
}
|
|
399
401
|
|
|
400
402
|
#connectedCallback = () => {
|
|
403
|
+
// 似乎这是最早的判断不在 `constructor` 中的地方
|
|
404
|
+
(this as any)[constructorSymbol] = false;
|
|
401
405
|
if (this.#isAppendReason) {
|
|
402
406
|
this.#isAppendReason = false;
|
|
403
407
|
return;
|
|
@@ -409,6 +413,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
409
413
|
});
|
|
410
414
|
const temp = this.#render();
|
|
411
415
|
temp !== undefined && render(temp, this.#renderRoot);
|
|
416
|
+
this.#isMounted = true;
|
|
412
417
|
this.#unmountCallback = this.mounted?.();
|
|
413
418
|
this.#initEffect();
|
|
414
419
|
if (rootElement && (this.getRootNode() as ShadowRoot).host?.tagName !== rootElement.toUpperCase()) {
|
|
@@ -465,6 +470,7 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
465
470
|
this.#isAppendReason = true;
|
|
466
471
|
return;
|
|
467
472
|
}
|
|
473
|
+
this.#isMounted = false;
|
|
468
474
|
const { observedStores } = this.constructor as typeof GemElement;
|
|
469
475
|
observedStores?.forEach((store) => {
|
|
470
476
|
disconnect(store, this.#update);
|
|
@@ -474,7 +480,8 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
474
480
|
this.#effectList?.forEach((effectItem) => {
|
|
475
481
|
execCallback(effectItem.preCallback);
|
|
476
482
|
});
|
|
477
|
-
this.#
|
|
483
|
+
this.#effectList = this.#effectList?.filter(({ inConstructor }) => inConstructor);
|
|
484
|
+
this.#memoList = this.#memoList?.filter(({ inConstructor }) => inConstructor);
|
|
478
485
|
}
|
|
479
486
|
}
|
|
480
487
|
|
package/src/lib/history.ts
CHANGED
|
@@ -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
|
|
53
|
+
function getUrlBarPath(internalPath: string) {
|
|
54
54
|
return gemHistory.basePath ? gemHistory.basePath + internalPath : internalPath;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function getInternalPath(
|
|
58
|
-
if (
|
|
59
|
-
return
|
|
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 =
|
|
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 =
|
|
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)
|
|
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)
|
|
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 (
|
|
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
|
}
|
package/src/lib/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.6.
|
|
1
|
+
export const version = '1.6.19';
|
|
@@ -2,7 +2,7 @@ import { fixture, expect, nextFrame } from '@open-wc/testing';
|
|
|
2
2
|
|
|
3
3
|
import { GemElement, html } from '../../lib/element';
|
|
4
4
|
import { createStore, updateStore } from '../../lib/store';
|
|
5
|
-
import { attribute, property, customElement, emitter, adoptedStyle } from '../../lib/decorators';
|
|
5
|
+
import { attribute, property, customElement, emitter, adoptedStyle, refobject, RefObject } from '../../lib/decorators';
|
|
6
6
|
import { createCSSSheet, css } from '../../lib/utils';
|
|
7
7
|
|
|
8
8
|
const store = createStore({
|
|
@@ -66,14 +66,22 @@ describe('没有 Shadow DOM 的 gem 元素', () => {
|
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
@customElement('lifecycle-gem-demo')
|
|
70
69
|
class LifecycleGemElement extends GemElement {
|
|
71
70
|
@attribute appTitle = 'default';
|
|
72
71
|
@attribute appTitle2 = 'default2';
|
|
72
|
+
@refobject divRef: RefObject<HTMLElement>;
|
|
73
|
+
refInConstructor?: HTMLElement;
|
|
74
|
+
|
|
73
75
|
constructor(appTitle: string, appTitle2: string) {
|
|
74
76
|
super();
|
|
75
77
|
this.appTitle = appTitle;
|
|
76
78
|
this.appTitle2 = appTitle2;
|
|
79
|
+
this.effect(
|
|
80
|
+
() => {
|
|
81
|
+
this.refInConstructor = this.divRef.element;
|
|
82
|
+
},
|
|
83
|
+
() => [],
|
|
84
|
+
);
|
|
77
85
|
}
|
|
78
86
|
renderCount = 0;
|
|
79
87
|
mountedCount = 0;
|
|
@@ -92,12 +100,15 @@ class LifecycleGemElement extends GemElement {
|
|
|
92
100
|
|
|
93
101
|
render() {
|
|
94
102
|
this.renderCount++;
|
|
95
|
-
return html
|
|
103
|
+
return html`<div ref=${this.divRef.ref}></div>`;
|
|
96
104
|
}
|
|
97
105
|
}
|
|
98
106
|
describe('gem element 生命周期', () => {
|
|
99
107
|
it('mounted/unmounted', async () => {
|
|
100
108
|
const el: LifecycleGemElement = await fixture(html`<lifecycle-gem-demo></lifecycle-gem-demo>`);
|
|
109
|
+
// test #isMounted
|
|
110
|
+
customElements.define('lifecycle-gem-demo', LifecycleGemElement);
|
|
111
|
+
expect(!!el.refInConstructor).to.equal(true);
|
|
101
112
|
expect(el.renderCount).to.equal(1);
|
|
102
113
|
el.remove();
|
|
103
114
|
expect(el.renderCount).to.equal(0);
|
|
@@ -211,19 +222,19 @@ class MemoGemDemo extends GemElement {
|
|
|
211
222
|
constructor() {
|
|
212
223
|
super();
|
|
213
224
|
this.memo(
|
|
214
|
-
() => this.memoCount
|
|
225
|
+
() => (this.memoCount += 1),
|
|
215
226
|
() => [this.attr, this.prop],
|
|
216
227
|
);
|
|
217
228
|
}
|
|
218
229
|
willMount() {
|
|
219
230
|
this.memo(
|
|
220
231
|
(_arr) => {
|
|
221
|
-
this.memoCount
|
|
232
|
+
this.memoCount += 2;
|
|
222
233
|
},
|
|
223
234
|
() => [],
|
|
224
235
|
);
|
|
225
236
|
this.memo(() => {
|
|
226
|
-
this.memoCount
|
|
237
|
+
this.memoCount += 4;
|
|
227
238
|
});
|
|
228
239
|
}
|
|
229
240
|
}
|
|
@@ -231,23 +242,23 @@ customElements.define('memo-gem-demo', MemoGemDemo);
|
|
|
231
242
|
describe('gem element Memo', () => {
|
|
232
243
|
it('依赖当前值', async () => {
|
|
233
244
|
const el: MemoGemDemo = await fixture(html`<memo-gem-demo></memo-gem-demo>`);
|
|
234
|
-
expect(el.memoCount).to.equal(
|
|
245
|
+
expect(el.memoCount).to.equal(7);
|
|
235
246
|
el.attr = 'b';
|
|
236
247
|
el.prop = {};
|
|
237
248
|
await nextFrame();
|
|
238
|
-
expect(el.memoCount).to.equal(
|
|
249
|
+
expect(el.memoCount).to.equal(12);
|
|
239
250
|
|
|
240
251
|
el.update();
|
|
241
252
|
await Promise.resolve();
|
|
242
|
-
expect(el.memoCount).to.equal(
|
|
253
|
+
expect(el.memoCount).to.equal(16);
|
|
243
254
|
|
|
244
255
|
document.body.append(el);
|
|
245
|
-
expect(el.memoCount).to.equal(
|
|
256
|
+
expect(el.memoCount).to.equal(16);
|
|
246
257
|
|
|
247
258
|
el.remove();
|
|
248
259
|
await Promise.resolve();
|
|
249
260
|
document.body.append(el);
|
|
250
|
-
expect(el.memoCount).to.equal(
|
|
261
|
+
expect(el.memoCount).to.equal(22);
|
|
251
262
|
});
|
|
252
263
|
});
|
|
253
264
|
|
|
@@ -269,6 +280,7 @@ describe('gem element 继承', () => {
|
|
|
269
280
|
expect(InheritGem.observedAttributes).to.eql(['app-title', 'app-title', 'app-title2']);
|
|
270
281
|
});
|
|
271
282
|
it('attr/prop/emitter 继承', async () => {
|
|
283
|
+
const name = window.name;
|
|
272
284
|
const el: InheritGem = await fixture(html`<inherit-gem></inherit-gem>`);
|
|
273
285
|
expect(el.appTitle).to.equal('1');
|
|
274
286
|
expect(el.appData.a).to.equal(1);
|
|
@@ -280,7 +292,7 @@ describe('gem element 继承', () => {
|
|
|
280
292
|
window.name += '2';
|
|
281
293
|
});
|
|
282
294
|
el.sayHi();
|
|
283
|
-
expect(window.name).to.equal('21');
|
|
295
|
+
expect(window.name).to.equal(name + '21');
|
|
284
296
|
});
|
|
285
297
|
});
|
|
286
298
|
@customElement('render-empty')
|
package/src/test/history.test.ts
CHANGED
|
@@ -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(
|
|
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);
|