@mantou/gem 2.2.13 → 2.2.15
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.js +7 -7
- package/dist/gem.mjs +7 -7
- package/elements/base/gesture.d.ts +7 -1
- package/elements/base/gesture.d.ts.map +1 -1
- package/elements/base/gesture.js +23 -15
- package/elements/base/gesture.js.map +1 -1
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +6 -3
- package/elements/base/link.js.map +1 -1
- package/elements/base/modal-factory.d.ts +1 -1
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/use.d.ts.map +1 -1
- package/elements/base/use.js +11 -0
- package/elements/base/use.js.map +1 -1
- package/helper/hmr.d.ts +3 -2
- package/helper/hmr.d.ts.map +1 -1
- package/helper/hmr.js +95 -23
- package/helper/hmr.js.map +1 -1
- package/helper/i18n.d.ts +2 -1
- package/helper/i18n.d.ts.map +1 -1
- package/helper/i18n.js +9 -1
- package/helper/i18n.js.map +1 -1
- package/helper/theme.d.ts.map +1 -1
- package/helper/theme.js +5 -0
- package/helper/theme.js.map +1 -1
- package/lib/decorators.d.ts +9 -1
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +4 -3
- package/lib/decorators.js.map +1 -1
- package/lib/history.d.ts +10 -7
- package/lib/history.d.ts.map +1 -1
- package/lib/history.js +20 -16
- package/lib/history.js.map +1 -1
- package/lib/lit-html.d.ts +15 -0
- package/lib/lit-html.d.ts.map +1 -1
- package/lib/lit-html.js +3 -1
- package/lib/lit-html.js.map +1 -1
- package/lib/reactive.d.ts +4 -1
- package/lib/reactive.d.ts.map +1 -1
- package/lib/reactive.js +41 -18
- package/lib/reactive.js.map +1 -1
- package/lib/server.d.ts +14 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +43 -0
- package/lib/server.js.map +1 -0
- package/lib/store.d.ts +10 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +36 -9
- package/lib/store.js.map +1 -1
- package/lib/utils.d.ts +2 -2
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +20 -7
- package/lib/utils.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/elements/base/gesture.ts +26 -15
- package/src/elements/base/link.ts +5 -3
- package/src/elements/base/use.ts +11 -0
- package/src/helper/hmr.ts +105 -37
- package/src/helper/i18n.ts +12 -1
- package/src/helper/theme.ts +4 -0
- package/src/lib/decorators.ts +10 -10
- package/src/lib/history.ts +20 -16
- package/src/lib/lit-html.ts +3 -1
- package/src/lib/reactive.ts +40 -18
- package/src/lib/server.ts +45 -0
- package/src/lib/store.ts +40 -9
- package/src/lib/utils.ts +22 -7
- package/src/lib/version.ts +1 -1
- package/src/test/elements/gesture.test.ts +26 -1
- package/src/test/gem-element/basic.test.ts +20 -0
- package/src/test/store.test.ts +12 -0
package/src/lib/reactive.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// biome-ignore-all lint/plugin/no_this_effect: 兼容生命周期
|
|
2
2
|
|
|
3
3
|
import type * as Lit from './lit-html';
|
|
4
|
+
import { isServer } from './server';
|
|
4
5
|
import type { Store } from './store';
|
|
5
6
|
import { connect } from './store';
|
|
6
7
|
import {
|
|
7
8
|
addListener,
|
|
8
9
|
addMicrotask,
|
|
10
|
+
assignState,
|
|
9
11
|
createUpdater,
|
|
10
12
|
GemError,
|
|
11
13
|
isArrayChange,
|
|
@@ -29,10 +31,18 @@ let hydrationEngine = (_value: unknown, _container: HTMLElement | DocumentFragme
|
|
|
29
31
|
return;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
// 在元素首次渲染前恢复 SSR 的状态
|
|
35
|
+
let restoreState: ((ele: GemElement) => void) | undefined;
|
|
36
|
+
|
|
37
|
+
export function setHydrationEngine(engine: typeof hydrationEngine, restore?: typeof restoreState) {
|
|
33
38
|
hydrationEngine = engine;
|
|
39
|
+
restoreState = restore;
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
/** 渲染元素内容,SSR 时由水合引擎接管 */
|
|
43
|
+
export const _renderRoot = (value: unknown, container: HTMLElement | DocumentFragment) =>
|
|
44
|
+
hydrationEngine(value, container) || render(value, container);
|
|
45
|
+
|
|
36
46
|
const { assign, defineProperty } = Object;
|
|
37
47
|
|
|
38
48
|
// https://github.com/tc39/proposal-decorator-metadata
|
|
@@ -40,8 +50,6 @@ defineProperty(Symbol, 'metadata', { value: Symbol.for('Symbol.metadata') });
|
|
|
40
50
|
|
|
41
51
|
const execCallback = (fun: any) => typeof fun === 'function' && fun();
|
|
42
52
|
|
|
43
|
-
const ssrNativeQueueMicrotask = (globalThis as any).nQM;
|
|
44
|
-
|
|
45
53
|
// 读取构造样式表,跨多个 gem 工作
|
|
46
54
|
export const SheetToken = Symbol.for('gem@sheetToken');
|
|
47
55
|
export type SheetTokenType = typeof SheetToken;
|
|
@@ -186,7 +194,7 @@ const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleShee
|
|
|
186
194
|
if (needUpdate) {
|
|
187
195
|
// 避免重复更新,例如 list 元素增删,全 light dom 时 document 更新
|
|
188
196
|
// 在 SSR 中应该区别 effect 强制执行
|
|
189
|
-
addMicrotask(rootUpdateFnMap.get(map)
|
|
197
|
+
addMicrotask(rootUpdateFnMap.get(map)!);
|
|
190
198
|
}
|
|
191
199
|
};
|
|
192
200
|
|
|
@@ -286,10 +294,14 @@ const noBlockingTaskList = new LinkedList<() => void>();
|
|
|
286
294
|
const tick = (timeStamp = performance.now()) => {
|
|
287
295
|
if (performance.now() > timeStamp + 50) return requestAnimationFrame(tick);
|
|
288
296
|
const task = noBlockingTaskList.get();
|
|
289
|
-
if (task)
|
|
297
|
+
if (!task) return;
|
|
298
|
+
// 出错时继续执行后面的任务,否则剩下的任务不会再被调度
|
|
299
|
+
try {
|
|
290
300
|
task();
|
|
291
|
-
|
|
301
|
+
} catch (err) {
|
|
302
|
+
reportError(err);
|
|
292
303
|
}
|
|
304
|
+
tick(timeStamp);
|
|
293
305
|
};
|
|
294
306
|
noBlockingTaskList.addEventListener('start', () => addMicrotask(tick));
|
|
295
307
|
|
|
@@ -340,14 +352,14 @@ export abstract class GemElement extends HTMLElement {
|
|
|
340
352
|
const effect = ele.#effectList.at(0);
|
|
341
353
|
// https://github.com/mantou132/gem/issues/203
|
|
342
354
|
// SSR 不会执行 initEffect,应该跳过
|
|
343
|
-
if (!
|
|
355
|
+
if (!isServer && ele.#isMounted && effect && !effect.initialized) {
|
|
344
356
|
throw new GemError(`Don't set state sync before insert the DOM`);
|
|
345
357
|
}
|
|
346
|
-
|
|
358
|
+
assignState(state, payload);
|
|
347
359
|
// 避免无限刷新
|
|
348
360
|
// 挂载前 set state 不应该触发更新
|
|
349
361
|
// SSR 中渲染 <gem-route>,其他情况的 SSR 应该也可以执行
|
|
350
|
-
if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update
|
|
362
|
+
if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update);
|
|
351
363
|
});
|
|
352
364
|
ele.#internals.stateList.push(state);
|
|
353
365
|
return state;
|
|
@@ -429,11 +441,17 @@ export abstract class GemElement extends HTMLElement {
|
|
|
429
441
|
const isLight = this.#renderRoot === this;
|
|
430
442
|
const temp = item ? item.render() : isLight ? undefined : html`<slot></slot>`;
|
|
431
443
|
if (temp === undefined) return;
|
|
432
|
-
|
|
433
|
-
|
|
444
|
+
// `<style>` 中不使用绑定,SSR 水合不支持
|
|
445
|
+
const r =
|
|
446
|
+
temp ??
|
|
447
|
+
(isLight
|
|
448
|
+
? html`<style>@scope{&{display:none!important;}}</style>`
|
|
449
|
+
: html`<style>:host{&{display:none!important;}}</style>`);
|
|
450
|
+
_renderRoot(r, this.#renderRoot);
|
|
434
451
|
} catch (err) {
|
|
435
|
-
|
|
436
|
-
|
|
452
|
+
const evt = new CustomEvent(_RenderErrorEvent, { bubbles: true, composed: true, cancelable: true, detail: err });
|
|
453
|
+
// `@fallback` 处理了错误
|
|
454
|
+
if (this.dispatchEvent(evt)) throw err;
|
|
437
455
|
} finally {
|
|
438
456
|
this.#rendering = false;
|
|
439
457
|
}
|
|
@@ -447,12 +465,13 @@ export abstract class GemElement extends HTMLElement {
|
|
|
447
465
|
(item || !this.#renderList.length)
|
|
448
466
|
) {
|
|
449
467
|
this.#render(item);
|
|
450
|
-
addMicrotask(this.#execEffect);
|
|
468
|
+
if (!isServer) addMicrotask(this.#execEffect);
|
|
451
469
|
}
|
|
452
470
|
};
|
|
453
471
|
|
|
454
472
|
#update = () => {
|
|
455
|
-
|
|
473
|
+
// SSR 需要同步渲染
|
|
474
|
+
if (this.#metadata.noBlocking && !isServer) {
|
|
456
475
|
noBlockingTaskList.add(this.#updateCallback);
|
|
457
476
|
} else {
|
|
458
477
|
this.#updateCallback();
|
|
@@ -506,7 +525,8 @@ export abstract class GemElement extends HTMLElement {
|
|
|
506
525
|
};
|
|
507
526
|
|
|
508
527
|
#disconnectStore?: (() => void)[];
|
|
509
|
-
|
|
528
|
+
// 同步执行,SSR 需要捕获渲染错误
|
|
529
|
+
#connectedCallback = () => {
|
|
510
530
|
if (this.#isAppendReason) {
|
|
511
531
|
this.#isAppendReason = false;
|
|
512
532
|
// https://github.com/mantou132/gem/issues/202
|
|
@@ -526,12 +546,14 @@ export abstract class GemElement extends HTMLElement {
|
|
|
526
546
|
if (!mode && this.#renderList.length && !penetrable) this.#internals.states.add(BoundaryCSSState);
|
|
527
547
|
|
|
528
548
|
this.#disconnectStore = observedStores?.map((store) => connect(store, this.#update));
|
|
549
|
+
restoreState?.(this);
|
|
529
550
|
this.#render(this.#renderItem);
|
|
530
551
|
this.#isMounted = true;
|
|
531
552
|
this.#clearStyle = this.#prepareStyle();
|
|
532
553
|
// 等待所有元素的样式被应用,再执行回调
|
|
533
554
|
// 这让 mounted 和 effect 回调和其他回调一样保持一样的异步行为
|
|
534
|
-
|
|
555
|
+
// SSR 不执行副作用
|
|
556
|
+
if (!isServer) addMicrotask(this.#initEffect);
|
|
535
557
|
};
|
|
536
558
|
|
|
537
559
|
/**
|
|
@@ -540,7 +562,7 @@ export abstract class GemElement extends HTMLElement {
|
|
|
540
562
|
* use `@mounted`
|
|
541
563
|
*/
|
|
542
564
|
connectedCallback(): Unique {
|
|
543
|
-
if (this.#metadata.noBlocking) {
|
|
565
|
+
if (this.#metadata.noBlocking && !isServer) {
|
|
544
566
|
noBlockingTaskList.add(this.#connectedCallback);
|
|
545
567
|
} else {
|
|
546
568
|
this.#connectedCallback();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SSR 相关的功能都在这里
|
|
2
|
+
//
|
|
3
|
+
// `@mantou/gem-ssr` 在加载 Gem 前提供 `__GEM_SSR__`,返回当前请求的作用域,
|
|
4
|
+
// 客户端构建时 `unplugin-gem` 把 `globalThis.__GEM_SSR__` 替换成 `undefined`,SSR 代码会被压缩移除
|
|
5
|
+
|
|
6
|
+
const { get, set, has, deleteProperty, defineProperty, getOwnPropertyDescriptor, ownKeys } = Reflect;
|
|
7
|
+
|
|
8
|
+
const getRequestScope: (() => WeakMap<object, any> | undefined) | undefined = (globalThis as any).__GEM_SSR__;
|
|
9
|
+
|
|
10
|
+
/** 服务端渲染时不执行副作用,同步更新 */
|
|
11
|
+
export const isServer = !!getRequestScope;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* SSR 时返回当前请求中 `key` 对应的值,第一次访问时使用 `init` 创建,
|
|
15
|
+
* 不在请求中(包括客户端)时返回 `key` 本身
|
|
16
|
+
*/
|
|
17
|
+
export const getRequestValue: <T extends object>(key: T, init: () => T) => T = getRequestScope
|
|
18
|
+
? (key, init) => {
|
|
19
|
+
const scope = getRequestScope();
|
|
20
|
+
if (!scope) return key;
|
|
21
|
+
if (!scope.has(key)) scope.set(key, init());
|
|
22
|
+
return scope.get(key);
|
|
23
|
+
}
|
|
24
|
+
: (key) => key;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* SSR 时对象的读写转到当前请求的副本上,副本在第一次访问时从原对象浅复制
|
|
28
|
+
*
|
|
29
|
+
* 嵌套的对象没有复制,原地修改会影响所有请求,应该替换顶层属性,例如 `store({ list: [...store.list, item] })`
|
|
30
|
+
*/
|
|
31
|
+
export const isolate: <T extends object>(target: T) => T = getRequestScope
|
|
32
|
+
? (target) => {
|
|
33
|
+
const copy = () => ({ ...target });
|
|
34
|
+
const current = () => getRequestValue(target, copy);
|
|
35
|
+
return new Proxy(target, {
|
|
36
|
+
get: (_, key) => get(current(), key),
|
|
37
|
+
set: (_, key, value) => set(current(), key, value),
|
|
38
|
+
has: (_, key) => has(current(), key),
|
|
39
|
+
deleteProperty: (_, key) => deleteProperty(current(), key),
|
|
40
|
+
defineProperty: (_, key, descriptor) => defineProperty(current(), key, descriptor),
|
|
41
|
+
getOwnPropertyDescriptor: (_, key) => getOwnPropertyDescriptor(current(), key),
|
|
42
|
+
ownKeys: () => ownKeys(current()),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
: (target) => target;
|
package/src/lib/store.ts
CHANGED
|
@@ -1,22 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getRequestValue, isolate, isServer } from './server';
|
|
2
|
+
import { addMicrotask, assignState, createUpdater, type Updater } from './utils';
|
|
2
3
|
|
|
3
4
|
export type Store<T = any> = Updater<T>;
|
|
4
5
|
|
|
5
6
|
/**@internal */
|
|
6
7
|
export const _StoreListenerMap = new WeakMap<Store<any>, Set<() => void>>();
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const createListeners = () => new Set<() => void>();
|
|
10
|
+
|
|
11
|
+
// SSR 时当前请求中更新过的有 key 的 store,渲染后输出到页面中
|
|
12
|
+
const updatedStores = new Map<string, Store<any>>();
|
|
13
|
+
|
|
14
|
+
/** SSR 时当前请求中更新过的有 key 的 store,供 `@mantou/gem-ssr` 使用 */
|
|
15
|
+
export const _getUpdatedStores = () => getRequestValue(updatedStores, () => new Map());
|
|
16
|
+
|
|
17
|
+
// 客户端读取服务端渲染时 store 的值
|
|
18
|
+
let restoreStore: ((key: string) => object | undefined) | undefined;
|
|
19
|
+
|
|
20
|
+
/** 设置客户端恢复 store 的方法,供 `@mantou/gem-ssr` 使用 */
|
|
21
|
+
export function _setStoreRestorer(restorer: typeof restoreStore) {
|
|
22
|
+
restoreStore = restorer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 创建 store,`key` 用来在 SSR 时同步服务端的值,`swc-plugin-gem` 的 `ssr` 选项会为模块顶层的 store 自动添加
|
|
27
|
+
*/
|
|
28
|
+
export function createStore<T = any>(originStore: T, key?: string) {
|
|
29
|
+
const listeners = createListeners();
|
|
30
|
+
// SSR 时每个请求使用独立的值
|
|
31
|
+
const store = isolate(
|
|
32
|
+
createUpdater(originStore, (value?: Partial<T>) => {
|
|
33
|
+
assignState(store, value);
|
|
34
|
+
listeners.forEach(addMicrotask);
|
|
35
|
+
if (!isServer) return;
|
|
36
|
+
// SSR 时请求中添加的监听器只属于当前请求,其他请求更新 store 时不会触发
|
|
37
|
+
getRequestValue(listeners, createListeners).forEach(addMicrotask);
|
|
38
|
+
const updated = _getUpdatedStores();
|
|
39
|
+
// 不在请求中时更新的是全局的值,客户端也一样
|
|
40
|
+
if (key && updated !== updatedStores) updated.set(key, store);
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
if (key && restoreStore) assignState(store, restoreStore(key));
|
|
44
|
+
_StoreListenerMap.set(store, listeners);
|
|
15
45
|
return store;
|
|
16
46
|
}
|
|
17
47
|
|
|
18
48
|
export function connect(store: Store<any>, func: () => void) {
|
|
19
|
-
const
|
|
49
|
+
const storeListeners = _StoreListenerMap.get(store);
|
|
50
|
+
const listeners = storeListeners && getRequestValue(storeListeners, createListeners);
|
|
20
51
|
listeners?.add(func);
|
|
21
52
|
return () => listeners?.delete(func);
|
|
22
53
|
}
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { getRequestValue } from './server';
|
|
2
|
+
|
|
3
|
+
const { assign, fromEntries, entries, keys, defineProperty, hasOwn } = Object;
|
|
2
4
|
|
|
3
5
|
// 用原生 Chrome 找不到调用栈
|
|
4
6
|
function queueMicrotask(cb: () => void) {
|
|
@@ -6,13 +8,16 @@ function queueMicrotask(cb: () => void) {
|
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
const microtaskSet = new Set<() => void>();
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const createMicrotaskSet = () => new Set<() => void>();
|
|
12
|
+
export function addMicrotask(func: () => void) {
|
|
13
|
+
// SSR 时不同请求的同一个函数都需要执行
|
|
14
|
+
const taskSet = getRequestValue(microtaskSet, createMicrotaskSet);
|
|
15
|
+
if (taskSet.has(func)) return;
|
|
16
|
+
queueMicrotask(() => {
|
|
17
|
+
taskSet.delete(func);
|
|
13
18
|
func();
|
|
14
19
|
});
|
|
15
|
-
|
|
20
|
+
taskSet.add(func);
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
type OverrideFn = {
|
|
@@ -29,6 +34,16 @@ type OverrideFn = {
|
|
|
29
34
|
[Symbol.metadata]: never;
|
|
30
35
|
};
|
|
31
36
|
|
|
37
|
+
// 删除后继承的 `name` `length` 只读,直接赋值会报错,需要先定义成自有属性
|
|
38
|
+
export function assignState(state: any, payload: any) {
|
|
39
|
+
for (const key of ['name', 'length']) {
|
|
40
|
+
if (payload && hasOwn(payload, key)) {
|
|
41
|
+
defineProperty(state, key, { value: payload[key], writable: true, enumerable: true, configurable: true });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
assign(state, payload);
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
// 注意 typeof state === 'function' 但是没有 Function 的方法和属性
|
|
33
48
|
export function createUpdater<T, Fn = (payload?: Partial<T>) => any>(initState: T, fn: Fn) {
|
|
34
49
|
const state: any = fn;
|
|
@@ -36,7 +51,7 @@ export function createUpdater<T, Fn = (payload?: Partial<T>) => any>(initState:
|
|
|
36
51
|
// setPrototypeOf(state, null);
|
|
37
52
|
delete state.name;
|
|
38
53
|
delete state.length;
|
|
39
|
-
|
|
54
|
+
assignState(state, initState);
|
|
40
55
|
// ts 不允许直接 Omit 掉 Fn 上的属性
|
|
41
56
|
// 只能利用 ts plugin 移除多余的自动完成项
|
|
42
57
|
return state as Fn & Omit<OverrideFn, keyof T> & T;
|
package/src/lib/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.2.
|
|
1
|
+
export const version = '2.2.15';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndEventDetail, GemGestureElement, SwipeEventDetail } from '../../elements/gesture';
|
|
1
|
+
import type { EndEventDetail, GemGestureElement, PanEventDetail, SwipeEventDetail } from '../../elements/gesture';
|
|
2
2
|
import { html } from '../../lib/element';
|
|
3
3
|
import { aTimeout, expect, fixture } from '../utils';
|
|
4
4
|
|
|
@@ -240,6 +240,31 @@ describe('GemGestureElement swipe', () => {
|
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
it('locks the axis for the rest of a touch with touch-action=pan-y', async () => {
|
|
244
|
+
const { el, swipes, dispatch } = await setupGesture('pan-y');
|
|
245
|
+
const pans: number[] = [];
|
|
246
|
+
el.addEventListener('pan', (evt: CustomEvent<PanEventDetail>) => pans.push(evt.detail.x));
|
|
247
|
+
dispatch('pointerdown', [0, 0]);
|
|
248
|
+
dispatch('pointermove', [10, 0, 20]);
|
|
249
|
+
dispatch('pointermove', [20, 60, 20]);
|
|
250
|
+
dispatch('pointerup', [30, 120, 20]);
|
|
251
|
+
await Promise.resolve();
|
|
252
|
+
expect(pans).to.deep.equal([0, 0]);
|
|
253
|
+
expect(swipes).to.deep.equal([]);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('emits raw horizontal movement once locked horizontally with touch-action=pan-y', async () => {
|
|
257
|
+
const { el, dispatch } = await setupGesture('pan-y');
|
|
258
|
+
const pans: number[] = [];
|
|
259
|
+
el.addEventListener('pan', (evt: CustomEvent<PanEventDetail>) => pans.push(evt.detail.x));
|
|
260
|
+
dispatch('pointerdown', [0, 0]);
|
|
261
|
+
dispatch('pointermove', [10, 5, 0]);
|
|
262
|
+
dispatch('pointermove', [20, 15, 0]);
|
|
263
|
+
dispatch('pointermove', [30, 20, 10]);
|
|
264
|
+
await Promise.resolve();
|
|
265
|
+
expect(pans).to.deep.equal([0, 10, 5]);
|
|
266
|
+
});
|
|
267
|
+
|
|
243
268
|
it('allows horizontal flicks with touch-action=pan-y', async () => {
|
|
244
269
|
const { swipes, dispatch } = await setupGesture('pan-y');
|
|
245
270
|
dispatch('pointerdown', [0, 0]);
|
|
@@ -161,3 +161,23 @@ describe('基本 gem element 测试', () => {
|
|
|
161
161
|
expect(el.renderCount).to.equal(2);
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
|
+
|
|
165
|
+
@customElement('gem-name-state')
|
|
166
|
+
class GemNameStateElement extends GemElement {
|
|
167
|
+
state = createState({ name: 'guest' });
|
|
168
|
+
|
|
169
|
+
render() {
|
|
170
|
+
return html`${this.state.name}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
describe('state `name` 字段', () => {
|
|
175
|
+
it('create and update state', async () => {
|
|
176
|
+
const el: GemNameStateElement = await fixture(html`<gem-name-state></gem-name-state>`);
|
|
177
|
+
expect(el.textContent).to.equal('guest');
|
|
178
|
+
el.state({ name: 'admin' });
|
|
179
|
+
await Promise.resolve();
|
|
180
|
+
expect({ ...el.state }).to.deep.equal({ name: 'admin' });
|
|
181
|
+
expect(el.textContent).to.equal('admin');
|
|
182
|
+
});
|
|
183
|
+
});
|
package/src/test/store.test.ts
CHANGED
|
@@ -25,3 +25,15 @@ describe('store 测试', () => {
|
|
|
25
25
|
expect(flag).to.equal(true);
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
|
+
|
|
29
|
+
describe('store `name` `length` 字段', () => {
|
|
30
|
+
it('create and update store', () => {
|
|
31
|
+
const store = createStore({ name: 'guest', length: 1 });
|
|
32
|
+
expect({ ...store }).to.deep.equal({ name: 'guest', length: 1 });
|
|
33
|
+
store({ name: 'admin', length: 2 });
|
|
34
|
+
expect({ ...store }).to.deep.equal({ name: 'admin', length: 2 });
|
|
35
|
+
const store2 = createStore<{ name?: string }>({});
|
|
36
|
+
store2({ name: 'guest' });
|
|
37
|
+
expect(store2.name).to.equal('guest');
|
|
38
|
+
});
|
|
39
|
+
});
|