@mantou/gem 2.2.1 → 2.2.2
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 +1015 -1014
- package/dist/gem.umd.js +12 -15
- package/elements/base/gesture.js +1 -1
- package/elements/base/link.d.ts +1 -1
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +13 -9
- package/elements/base/link.js.map +1 -1
- package/elements/base/modal-factory.d.ts +73 -48
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/modal-factory.js +12 -11
- package/elements/base/modal-factory.js.map +1 -1
- package/elements/base/route.d.ts +1 -1
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +11 -15
- package/elements/base/route.js.map +1 -1
- package/elements/base/title.d.ts.map +1 -1
- package/elements/base/title.js +2 -4
- package/elements/base/title.js.map +1 -1
- package/elements/base/unsafe.d.ts +3 -2
- package/elements/base/unsafe.d.ts.map +1 -1
- package/elements/base/unsafe.js +45 -24
- package/elements/base/unsafe.js.map +1 -1
- package/helper/i18n.d.ts.map +1 -1
- package/helper/logger.d.ts.map +1 -1
- package/helper/react-utils.d.ts.map +1 -1
- package/helper/react-utils.js +2 -6
- package/helper/react-utils.js.map +1 -1
- package/helper/ssr-shim.d.ts.map +1 -1
- package/helper/ssr-shim.js +0 -1
- package/helper/ssr-shim.js.map +1 -1
- package/helper/theme.d.ts +15 -3
- package/helper/theme.d.ts.map +1 -1
- package/helper/theme.js +22 -7
- package/helper/theme.js.map +1 -1
- package/lib/decorators.d.ts +49 -49
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +50 -49
- package/lib/decorators.js.map +1 -1
- package/lib/directive.d.ts.map +1 -1
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js.map +1 -1
- package/lib/history.d.ts +1 -1
- package/lib/history.d.ts.map +1 -1
- package/lib/history.js +3 -3
- package/lib/history.js.map +1 -1
- package/lib/lit-html.d.ts +2 -5
- package/lib/lit-html.d.ts.map +1 -1
- package/lib/lit-html.js +2 -5
- package/lib/lit-html.js.map +1 -1
- package/lib/reactive.d.ts +12 -5
- package/lib/reactive.d.ts.map +1 -1
- package/lib/reactive.js +14 -18
- package/lib/reactive.js.map +1 -1
- package/lib/repeat.d.ts.map +1 -1
- package/lib/store.d.ts +2 -2
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +1 -0
- package/lib/store.js.map +1 -1
- package/lib/utils.d.ts +4 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +11 -9
- package/lib/utils.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.d.ts.map +1 -1
- package/lib/version.js +2 -1
- package/lib/version.js.map +1 -1
- package/package.json +2 -2
- package/src/elements/base/gesture.ts +1 -1
- package/src/elements/base/link.ts +3 -3
- package/src/elements/base/modal-factory.ts +12 -11
- package/src/elements/base/route.ts +9 -15
- package/src/elements/base/title.ts +2 -4
- package/src/elements/base/unsafe.ts +33 -14
- package/src/helper/react-utils.ts +4 -12
- package/src/helper/ssr-shim.ts +0 -1
- package/src/helper/theme.ts +25 -10
- package/src/lib/decorators.ts +51 -49
- package/src/lib/element.ts +0 -1
- package/src/lib/history.ts +3 -4
- package/src/lib/lit-html.ts +3 -0
- package/src/lib/reactive.ts +45 -21
- package/src/lib/store.ts +3 -2
- package/src/lib/utils.ts +15 -8
- package/src/lib/version.ts +2 -1
- package/src/test/gem-element/advance.test.ts +1 -1
- package/src/test/gem-element/basic.test.ts +1 -2
- package/src/test/utils.test.ts +1 -1
package/src/helper/ssr-shim.ts
CHANGED
package/src/helper/theme.ts
CHANGED
|
@@ -2,9 +2,10 @@ import type { GemElement, Sheet } from '../lib/reactive';
|
|
|
2
2
|
import { css, SheetToken } from '../lib/reactive';
|
|
3
3
|
import type { Store } from '../lib/store';
|
|
4
4
|
import { connect, createStore } from '../lib/store';
|
|
5
|
+
import type { Updater } from '../lib/utils';
|
|
5
6
|
import { camelToKebabCase, createUpdater, randomStr } from '../lib/utils';
|
|
6
7
|
|
|
7
|
-
export type Theme<T> =
|
|
8
|
+
export type Theme<T> = Updater<T> &
|
|
8
9
|
Sheet<unknown> & {
|
|
9
10
|
[K in keyof T as K extends `${string}Color`
|
|
10
11
|
? `${string & K}${'' | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900}`
|
|
@@ -52,7 +53,7 @@ function createThemeFromProps<T extends Record<string, unknown>>(themeObj: T, pr
|
|
|
52
53
|
}
|
|
53
54
|
rules += `${props[key]}:${store[key]};`;
|
|
54
55
|
});
|
|
55
|
-
styleSheet.setContent(
|
|
56
|
+
styleSheet.setContent(`&,:host{${rules}}`);
|
|
56
57
|
};
|
|
57
58
|
updateContent();
|
|
58
59
|
connect(store, () => {
|
|
@@ -81,19 +82,33 @@ export function createOverrideTheme<T extends Record<string, unknown>>(theme: Th
|
|
|
81
82
|
return createThemeFromProps(themeObj, getThemeProps(theme));
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* 返回一个主题对象,但同时又是一个装饰器函数,用来装饰一个主题更新函数
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* const elementTheme = createDecoratorTheme({ color: '' });
|
|
91
|
+
* class MyElement {
|
|
92
|
+
* @elementTheme()
|
|
93
|
+
* #theme = () => ({ color: this.color });
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
* */
|
|
84
97
|
export function createDecoratorTheme<T extends Record<string, unknown>>(themeObj: T) {
|
|
85
|
-
const themeAsKeys = createThemeFromProps
|
|
98
|
+
const themeAsKeys = createThemeFromProps(themeObj);
|
|
86
99
|
const props = getThemeProps(themeAsKeys);
|
|
87
|
-
const update =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
(_: any, { addInitializer, access }:
|
|
100
|
+
const update = <E extends GemElement, V extends () => T, K = any[] | undefined>(
|
|
101
|
+
getDep?: K extends readonly any[] ? (instance: E) => K : undefined,
|
|
102
|
+
) => {
|
|
103
|
+
type Ctx = ClassFieldDecoratorContext<E, V> | ClassMethodDecoratorContext<E, V>;
|
|
104
|
+
return (_: any, { addInitializer, access }: Ctx) => {
|
|
92
105
|
addInitializer(function (this: E) {
|
|
93
106
|
const theme = createThemeFromProps({ ...themeObj }, props);
|
|
94
|
-
this.internals.sheets.push(theme[SheetToken].getStyle(this));
|
|
107
|
+
this.internals.sheets.push(theme[SheetToken].getStyle(this, true));
|
|
95
108
|
this.memo(() => theme(access.get(this).apply(this)), getDep && (() => getDep(this) as any));
|
|
96
109
|
});
|
|
97
110
|
};
|
|
98
|
-
|
|
111
|
+
};
|
|
112
|
+
// 不知道为什么 `as` 能解决外部 `SheetToken` 不能命名的问题
|
|
113
|
+
return createUpdater(themeAsKeys, update) as Updater<Theme<T>, typeof update>;
|
|
99
114
|
}
|
package/src/lib/decorators.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// biome-ignore-all lint/plugin/no_this_effect: 定义装饰器
|
|
2
|
+
|
|
1
3
|
import type { TemplateResult } from './lit-html';
|
|
2
4
|
import type { Metadata, Sheet } from './reactive';
|
|
3
5
|
import { _createTemplate, _RenderErrorEvent, GemElement, render, UpdateToken } from './reactive';
|
|
@@ -150,11 +152,11 @@ function decoratorAttr<T extends GemElement>(context: ClassFieldDecoratorContext
|
|
|
150
152
|
/**
|
|
151
153
|
* 定义一个响应式的 attribute,驼峰字段名将自动映射到烤串 attribute,默认值为空字符串;不要覆盖定义
|
|
152
154
|
*
|
|
153
|
-
*
|
|
155
|
+
* @example
|
|
154
156
|
* ```ts
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
157
|
+
* class App extends GemElement {
|
|
158
|
+
* @attribute attr: string;
|
|
159
|
+
* }
|
|
158
160
|
* ```
|
|
159
161
|
*/
|
|
160
162
|
export function attribute<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, string>) {
|
|
@@ -170,11 +172,11 @@ export function numattribute<T extends GemElement>(_: undefined, context: ClassF
|
|
|
170
172
|
/**
|
|
171
173
|
* 定义一个响应式的 property,注意值可能为 `undefined`;不要覆盖定义
|
|
172
174
|
*
|
|
173
|
-
*
|
|
175
|
+
* @example
|
|
174
176
|
* ```ts
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
177
|
+
* class App extends GemElement {
|
|
178
|
+
* @property prop: Data | undefined;
|
|
179
|
+
* }
|
|
178
180
|
* ```
|
|
179
181
|
*/
|
|
180
182
|
export function property<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T>) {
|
|
@@ -196,14 +198,14 @@ export function property<T extends GemElement>(_: undefined, context: ClassField
|
|
|
196
198
|
/**
|
|
197
199
|
* 依赖 `GemElement.memo`
|
|
198
200
|
*
|
|
199
|
-
*
|
|
201
|
+
* @example
|
|
200
202
|
* ```ts
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
203
|
+
* class App extends GemElement {
|
|
204
|
+
* @memo(() => [])
|
|
205
|
+
* get data() {
|
|
206
|
+
* return 1;
|
|
207
|
+
* }
|
|
208
|
+
* }
|
|
207
209
|
* ```
|
|
208
210
|
*/
|
|
209
211
|
export function memo<T extends GemElement, V = any, K = any[] | undefined>(
|
|
@@ -240,14 +242,14 @@ export function memo<T extends GemElement, V = any, K = any[] | undefined>(
|
|
|
240
242
|
* - 方法执行在字段前面
|
|
241
243
|
* - 如果没有 oldValue,在为首次执行
|
|
242
244
|
*
|
|
243
|
-
*
|
|
245
|
+
* @example
|
|
244
246
|
* ```ts
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
247
|
+
* class App extends GemElement {
|
|
248
|
+
* @effect(() => [])
|
|
249
|
+
* #fetchData(newValue, oldValue) {
|
|
250
|
+
* console.log('fetch')
|
|
251
|
+
* }
|
|
252
|
+
* }
|
|
251
253
|
* ```
|
|
252
254
|
*/
|
|
253
255
|
export function effect<T extends GemElement, V extends (depValues: K, oldDepValues?: K) => any, K = any[] | undefined>(
|
|
@@ -331,11 +333,11 @@ function defineCSSState(target: GemElementPrototype, prop: string, stateStr: str
|
|
|
331
333
|
* 重新赋值转换 css 伪类
|
|
332
334
|
* 将来也可能用于 IDE 识别
|
|
333
335
|
*
|
|
334
|
-
*
|
|
336
|
+
* @example
|
|
335
337
|
* ```ts
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
338
|
+
* class App extends GemElement {
|
|
339
|
+
* @state state: boolean;
|
|
340
|
+
* }
|
|
339
341
|
* ```
|
|
340
342
|
*/
|
|
341
343
|
export function state<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, boolean>) {
|
|
@@ -374,12 +376,12 @@ function defineStaticField(
|
|
|
374
376
|
/**
|
|
375
377
|
* 定义一个内部 slot,默认值即为字段名,不能使用全局属性 `slot`
|
|
376
378
|
*
|
|
377
|
-
*
|
|
379
|
+
* @example
|
|
378
380
|
* ```ts
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
381
|
+
* class App extends GemElement {
|
|
382
|
+
* @slot slot: string;
|
|
383
|
+
* @slot static slot: string;
|
|
384
|
+
* }
|
|
383
385
|
* ```
|
|
384
386
|
*/
|
|
385
387
|
export function slot(_: undefined, context: ClassFieldDecoratorContext<any, string>) {
|
|
@@ -391,12 +393,12 @@ export function slot(_: undefined, context: ClassFieldDecoratorContext<any, stri
|
|
|
391
393
|
/**
|
|
392
394
|
* 定义一个内部 part,默认值即为字段名,不能使用全局属性 `part`
|
|
393
395
|
*
|
|
394
|
-
*
|
|
396
|
+
* @example
|
|
395
397
|
* ```ts
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
398
|
+
* class App extends GemElement {
|
|
399
|
+
* @part part: string;
|
|
400
|
+
* @part static part: string;
|
|
401
|
+
* }
|
|
400
402
|
* ```
|
|
401
403
|
*/
|
|
402
404
|
export function part(_: undefined, context: ClassFieldDecoratorContext<any, string>) {
|
|
@@ -421,11 +423,11 @@ export type Emitter<T = any> = ((
|
|
|
421
423
|
* 定义一个事件发射器,类似 `HTMLElement.click`,
|
|
422
424
|
* 调用时将同步发送一个和字段名称的烤串式格式的自定义事件
|
|
423
425
|
*
|
|
424
|
-
*
|
|
426
|
+
* @example
|
|
425
427
|
* ```ts
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
428
|
+
* class App extends GemElement {
|
|
429
|
+
* @emitter load: Function;
|
|
430
|
+
* }
|
|
429
431
|
* ```
|
|
430
432
|
*/
|
|
431
433
|
export function emitter<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, Emitter>) {
|
|
@@ -453,10 +455,10 @@ function defineEmitter<T extends GemElement>(
|
|
|
453
455
|
/**
|
|
454
456
|
* 分配一个构造的样式表,前面的优先级越高(因为装饰器是越近越早执行)
|
|
455
457
|
*
|
|
456
|
-
*
|
|
458
|
+
* @example
|
|
457
459
|
* ```ts
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
+
* @adoptedStyle(stylesheet)
|
|
461
|
+
* class App extends GemElement {}
|
|
460
462
|
* ```
|
|
461
463
|
*/
|
|
462
464
|
export function adoptedStyle(sheet: Sheet<unknown>) {
|
|
@@ -468,10 +470,10 @@ export function adoptedStyle(sheet: Sheet<unknown>) {
|
|
|
468
470
|
/**
|
|
469
471
|
* 链接一个 store,当 store 更新时更新元素
|
|
470
472
|
*
|
|
471
|
-
*
|
|
473
|
+
* @example
|
|
472
474
|
* ```ts
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
+
* @connectStore(store)
|
|
476
|
+
* class App extends GemElement {}
|
|
475
477
|
* ```
|
|
476
478
|
*/
|
|
477
479
|
export function connectStore(store: Store<any>) {
|
|
@@ -523,10 +525,10 @@ export function aria(info: Metadata['aria']) {
|
|
|
523
525
|
/**
|
|
524
526
|
* 定义自定义元素
|
|
525
527
|
*
|
|
526
|
-
*
|
|
528
|
+
* @example
|
|
527
529
|
* ```ts
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
+
* @customElement('my-element')
|
|
531
|
+
* class App extends GemElement {}
|
|
530
532
|
* ```
|
|
531
533
|
*/
|
|
532
534
|
export function customElement(name: string) {
|
package/src/lib/element.ts
CHANGED
|
@@ -14,7 +14,6 @@ export * from './reactive';
|
|
|
14
14
|
export { repeat } from './repeat';
|
|
15
15
|
|
|
16
16
|
declare global {
|
|
17
|
-
// eslint-disable-next-line no-var
|
|
18
17
|
var __GEM_DEVTOOLS__HOOK__:
|
|
19
18
|
| (typeof reactiveExports & typeof decoratorsExports & typeof storeExports & typeof versionExports)
|
|
20
19
|
| Record<string, never>
|
package/src/lib/history.ts
CHANGED
|
@@ -188,7 +188,6 @@ const gemTitleStore = createStore({ defaultTitle: document.title, url: '', title
|
|
|
188
188
|
const HISTORY = { history: gemHistory, titleStore: gemTitleStore, basePathStore: gemBasePathStore };
|
|
189
189
|
|
|
190
190
|
declare global {
|
|
191
|
-
// eslint-disable-next-line no-var
|
|
192
191
|
var _GEMHISTORY: typeof HISTORY | undefined;
|
|
193
192
|
}
|
|
194
193
|
|
|
@@ -269,10 +268,10 @@ if (!globalThis._GEMHISTORY) {
|
|
|
269
268
|
if (!paramsMap.has(newState.$key)) {
|
|
270
269
|
const { pathname, search, hash } = location;
|
|
271
270
|
paramsMap.set(newState.$key, {
|
|
272
|
-
path: pathname,
|
|
271
|
+
path: getInternalPath(pathname),
|
|
273
272
|
query: new QueryString(search),
|
|
274
273
|
hash: decodeURIComponent(hash),
|
|
275
|
-
title: newState.$title, // document.title 是导航前的
|
|
274
|
+
title: gemTitleStore.title || newState.$title, // document.title 是导航前的
|
|
276
275
|
data: newState,
|
|
277
276
|
});
|
|
278
277
|
}
|
|
@@ -316,4 +315,4 @@ if (!globalThis._GEMHISTORY) {
|
|
|
316
315
|
}
|
|
317
316
|
|
|
318
317
|
const { history, titleStore, basePathStore } = globalThis._GEMHISTORY;
|
|
319
|
-
export { history, titleStore
|
|
318
|
+
export { basePathStore, history, titleStore };
|
package/src/lib/lit-html.ts
CHANGED
package/src/lib/reactive.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
// biome-ignore-all lint/plugin/no_this_effect: 兼容生命周期
|
|
2
|
+
|
|
1
3
|
import type * as Lit from './lit-html';
|
|
2
4
|
import type { Store } from './store';
|
|
3
5
|
import { connect } from './store';
|
|
4
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
addListener,
|
|
8
|
+
addMicrotask,
|
|
9
|
+
createUpdater,
|
|
10
|
+
GemError,
|
|
11
|
+
isArrayChange,
|
|
12
|
+
LinkedList,
|
|
13
|
+
randomStr,
|
|
14
|
+
raw,
|
|
15
|
+
type Updater,
|
|
16
|
+
} from './utils';
|
|
5
17
|
|
|
6
18
|
export let html = ((arr: TemplateStringsArray, ...args: any[]) =>
|
|
7
19
|
raw(arr, ...args.map((e) => (e instanceof Element ? e.outerHTML : e)))) as unknown as typeof Lit.html;
|
|
@@ -20,8 +32,11 @@ defineProperty(Symbol, 'metadata', { value: Symbol.for('Symbol.metadata') });
|
|
|
20
32
|
|
|
21
33
|
const execCallback = (fun: any) => typeof fun === 'function' && fun();
|
|
22
34
|
|
|
35
|
+
const ssrNativeQueueMicrotask = (globalThis as any).nQM;
|
|
36
|
+
|
|
23
37
|
// 读取构造样式表,跨多个 gem 工作
|
|
24
38
|
export const SheetToken = Symbol.for('gem@sheetToken');
|
|
39
|
+
export type SheetTokenType = typeof SheetToken;
|
|
25
40
|
// 更新元素,跨多个 gem 工作
|
|
26
41
|
export const UpdateToken = Symbol.for('gem@update');
|
|
27
42
|
|
|
@@ -67,7 +82,7 @@ class GemCSSSheet {
|
|
|
67
82
|
// 不需要 GC
|
|
68
83
|
#record = new Map<any, GemCSSStyleSheet>();
|
|
69
84
|
#used = new Map<GemCSSStyleSheet, string>();
|
|
70
|
-
getStyle(host?: HTMLElement) {
|
|
85
|
+
getStyle(host?: HTMLElement, isEleTheme?: boolean) {
|
|
71
86
|
const isLight = host && !(host as GemElement).internals?.shadowRoot;
|
|
72
87
|
|
|
73
88
|
// 对同一类 dom 只使用同一个样式表
|
|
@@ -86,7 +101,7 @@ class GemCSSSheet {
|
|
|
86
101
|
if (isLight) {
|
|
87
102
|
// light dom 嵌套时需要选择子元素
|
|
88
103
|
// `> *` 实际上是多范围?是否存在性能问题
|
|
89
|
-
scope = `@scope (${host.tagName}) to (:state(${BoundaryCSSState}) > *)`;
|
|
104
|
+
scope = isEleTheme ? '@scope' : `@scope (${host.tagName}) to (:state(${BoundaryCSSState}) > *)`;
|
|
90
105
|
// 不能使用 @layer,两个 @layer 不能覆盖,只有顺序起作用
|
|
91
106
|
// 所以外部不能通过元素名称选择器来覆盖样式,除非样式在之前插入(会自动反转应用到尾部, see `appleCSSStyleSheet`)
|
|
92
107
|
style = `${scope}{ ${style} }`;
|
|
@@ -143,16 +158,13 @@ export function css<T extends Record<string, string>>(
|
|
|
143
158
|
// 对于已经有 `-` 的保留原始 key,支持覆盖修改
|
|
144
159
|
// :scope 下可以写嵌套样式 &:xxx,:host() 下不行(子内容可以)
|
|
145
160
|
sheet[key] = isScope || key.includes('-') ? key : `${key}-${randomStr()}`;
|
|
146
|
-
style += `${isScope ? ':where(
|
|
161
|
+
style += `${isScope ? ':where(&:not([hidden])),:host(:where(:not([hidden])))' : `.${sheet[key]}`} {${rules[key]}}`;
|
|
147
162
|
});
|
|
148
163
|
}
|
|
149
164
|
styleSheet.setContent(style);
|
|
150
165
|
return sheet as Sheet<T>;
|
|
151
166
|
}
|
|
152
167
|
|
|
153
|
-
// TODO: move to transform?
|
|
154
|
-
export const isSSR = typeof process !== 'undefined';
|
|
155
|
-
|
|
156
168
|
const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleSheet[], value: number) => {
|
|
157
169
|
let needUpdate = false;
|
|
158
170
|
sheets.forEach((e) => {
|
|
@@ -164,7 +176,8 @@ const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleShee
|
|
|
164
176
|
});
|
|
165
177
|
if (needUpdate) {
|
|
166
178
|
// 避免重复更新,例如 list 元素增删,全 light dom 时 document 更新
|
|
167
|
-
|
|
179
|
+
// 在 SSR 中应该区别 effect 强制执行
|
|
180
|
+
addMicrotask(rootUpdateFnMap.get(map)!, ssrNativeQueueMicrotask);
|
|
168
181
|
}
|
|
169
182
|
};
|
|
170
183
|
|
|
@@ -189,7 +202,7 @@ const appleCSSStyleSheet = (ele: HTMLElement, sheets: CSSStyleSheet[]) => {
|
|
|
189
202
|
};
|
|
190
203
|
|
|
191
204
|
/**必须使用在字段中,否则会读取到错误的实例 */
|
|
192
|
-
export let createState: <T>(initState: T) =>
|
|
205
|
+
export let createState: <T>(initState: T) => Updater<T>;
|
|
193
206
|
|
|
194
207
|
type GetDepFun<T> = () => T;
|
|
195
208
|
type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
|
|
@@ -269,12 +282,28 @@ noBlockingTaskList.addEventListener('start', () => addMicrotask(tick));
|
|
|
269
282
|
|
|
270
283
|
let currentConstructGemElement: GemElement;
|
|
271
284
|
|
|
285
|
+
type KebabCase<S extends string> = S extends `${infer Head}${infer Tail}`
|
|
286
|
+
? Tail extends Uncapitalize<Tail>
|
|
287
|
+
? `${Lowercase<Head>}${KebabCase<Tail>}`
|
|
288
|
+
: `${Lowercase<Head>}-${KebabCase<Tail>}`
|
|
289
|
+
: S;
|
|
290
|
+
|
|
272
291
|
export abstract class GemElement extends HTMLElement {
|
|
292
|
+
/** 用来判断 attribute 是否存在,但是 length/item 将读取到 NamedNodeMap */
|
|
293
|
+
declare readonly attributes: NamedNodeMap & {
|
|
294
|
+
[K in keyof this as K extends keyof NamedNodeMap
|
|
295
|
+
? never
|
|
296
|
+
: K extends keyof GemElement
|
|
297
|
+
? never
|
|
298
|
+
: K extends Uppercase<string & K>
|
|
299
|
+
? never
|
|
300
|
+
: `${KebabCase<string & K>}`]?: Attr;
|
|
301
|
+
};
|
|
273
302
|
#renderRoot: HTMLElement | ShadowRoot;
|
|
274
303
|
#internals: ElementInternals & {
|
|
275
304
|
// 有别于 adoptedStyleSheets,用来定义单个实例样式
|
|
276
305
|
sheets: GemCSSStyleSheet[];
|
|
277
|
-
stateList:
|
|
306
|
+
stateList: Updater<any>[];
|
|
278
307
|
};
|
|
279
308
|
#effectList: EffectItem<any>[] = [];
|
|
280
309
|
#memoList: EffectItem<any>[] = [];
|
|
@@ -297,13 +326,15 @@ export abstract class GemElement extends HTMLElement {
|
|
|
297
326
|
const state = createUpdater(initState, (payload) => {
|
|
298
327
|
const effect = ele.#effectList.at(0);
|
|
299
328
|
// https://github.com/mantou132/gem/issues/203
|
|
300
|
-
|
|
301
|
-
|
|
329
|
+
// SSR 不会执行 initEffect,应该跳过
|
|
330
|
+
if (!ssrNativeQueueMicrotask && ele.#isMounted && effect && !effect.initialized) {
|
|
331
|
+
throw new GemError(`Don't set state sync before insert the DOM`);
|
|
302
332
|
}
|
|
303
333
|
assign(state, payload);
|
|
304
334
|
// 避免无限刷新
|
|
305
335
|
// 挂载前 set state 不应该触发更新
|
|
306
|
-
|
|
336
|
+
// SSR 中渲染 <gem-route>,其他情况的 SSR 应该也可以执行
|
|
337
|
+
if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update, ssrNativeQueueMicrotask);
|
|
307
338
|
});
|
|
308
339
|
ele.#internals.stateList.push(state);
|
|
309
340
|
return state;
|
|
@@ -314,7 +345,6 @@ export abstract class GemElement extends HTMLElement {
|
|
|
314
345
|
constructor() {
|
|
315
346
|
super();
|
|
316
347
|
|
|
317
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
318
348
|
currentConstructGemElement = this;
|
|
319
349
|
|
|
320
350
|
const { mode, serializable, delegatesFocus, slotAssignment, aria } = this.#metadata;
|
|
@@ -386,13 +416,7 @@ export abstract class GemElement extends HTMLElement {
|
|
|
386
416
|
const temp = item ? item.render() : isLight ? undefined : html`<slot></slot>`;
|
|
387
417
|
if (temp === undefined) return;
|
|
388
418
|
render(
|
|
389
|
-
temp !== null
|
|
390
|
-
? temp
|
|
391
|
-
: isLight
|
|
392
|
-
? // prettier-ignore
|
|
393
|
-
html`<style>@scope{:scope{display:none !important}}</style>`
|
|
394
|
-
: // prettier-ignore
|
|
395
|
-
html`<style>:host{display:none !important}</style>`,
|
|
419
|
+
temp !== null ? temp : html`<style>${isLight ? '@scope' : ':host'}{&{display:none!important;}}</style>`,
|
|
396
420
|
this.#renderRoot,
|
|
397
421
|
);
|
|
398
422
|
} catch (err) {
|
package/src/lib/store.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { addMicrotask, createUpdater } from './utils';
|
|
1
|
+
import { addMicrotask, createUpdater, type Updater } from './utils';
|
|
2
2
|
|
|
3
|
-
export type Store<T = any> =
|
|
3
|
+
export type Store<T = any> = Updater<T>;
|
|
4
4
|
|
|
5
5
|
/**@internal */
|
|
6
6
|
export const _StoreListenerMap = new WeakMap<Store<any>, Set<() => void>>();
|
|
7
7
|
|
|
8
8
|
export function createStore<T = any>(originStore: T) {
|
|
9
9
|
const store = createUpdater(originStore, (value?: Partial<T>) => {
|
|
10
|
+
// biome-ignore lint/plugin/assign: 未知类型
|
|
10
11
|
Object.assign(store, value);
|
|
11
12
|
_StoreListenerMap.get(store)?.forEach((func) => addMicrotask(func));
|
|
12
13
|
});
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
const { assign, fromEntries, entries, keys } = Object;
|
|
2
2
|
|
|
3
|
+
// 用原生 Chrome 找不到调用栈
|
|
4
|
+
function queueMicrotask(cb: () => void) {
|
|
5
|
+
Promise.resolve().then(cb);
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
const microtaskSet = new Set<() => void>();
|
|
4
|
-
export function addMicrotask(func: () => void,
|
|
9
|
+
export function addMicrotask(func: () => void, method = queueMicrotask) {
|
|
5
10
|
if (microtaskSet.has(func)) return;
|
|
6
|
-
|
|
11
|
+
method(() => {
|
|
7
12
|
microtaskSet.delete(func);
|
|
8
13
|
func();
|
|
9
|
-
};
|
|
10
|
-
if (isNodeForceMicrotask) {
|
|
11
|
-
process.nextTick(next);
|
|
12
|
-
} else {
|
|
13
|
-
Promise.resolve().then(next);
|
|
14
|
-
}
|
|
14
|
+
});
|
|
15
15
|
microtaskSet.add(func);
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -42,6 +42,8 @@ export function createUpdater<T, Fn = (payload?: Partial<T>) => any>(initState:
|
|
|
42
42
|
return state as Fn & Omit<OverrideFn, keyof T> & T;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
export type Updater<T, Fn = (payload?: Partial<T>) => any> = ReturnType<typeof createUpdater<T, Fn>>;
|
|
46
|
+
|
|
45
47
|
// 不编码 hash 用于比较
|
|
46
48
|
export function absoluteLocation(currentPath = '', relativePath = '') {
|
|
47
49
|
const { pathname, search, hash } = new URL(relativePath, location.origin + currentPath);
|
|
@@ -157,6 +159,11 @@ export class LinkedList<T = any> extends EventTarget {
|
|
|
157
159
|
return deleteItem;
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
clear() {
|
|
163
|
+
this.#map.clear();
|
|
164
|
+
this.#firstItem = this.#lastItem = undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
160
167
|
/** 获取头部元素,会从链表删除 */
|
|
161
168
|
get(): T | undefined {
|
|
162
169
|
const firstItem = this.#firstItem;
|
package/src/lib/version.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
// Do not modify manually
|
|
2
|
+
export const version = '0.0.0';
|
|
@@ -108,8 +108,7 @@ describe('基本 gem element 测试', () => {
|
|
|
108
108
|
expect(el.disabled).to.equal(true);
|
|
109
109
|
expect(el.count).to.equal(2);
|
|
110
110
|
expect(el.shadowRoot?.textContent).to.equal('attr: value, disabled: true, count: 2, prop: , state: ');
|
|
111
|
-
//
|
|
112
|
-
// @ts-ignore
|
|
111
|
+
// @ts-expect-error
|
|
113
112
|
el.attr = null;
|
|
114
113
|
await Promise.resolve();
|
|
115
114
|
expect(el.hasAttribute('attr')).to.equal(false);
|
package/src/test/utils.test.ts
CHANGED
|
@@ -110,7 +110,7 @@ describe('utils 测试', () => {
|
|
|
110
110
|
});
|
|
111
111
|
expect(cssSheet.scroll.startsWith('scroll')).to.true;
|
|
112
112
|
const rules = cssSheet[SheetToken].getStyle().cssRules;
|
|
113
|
-
expect(rules.item(0).selectorText).to.equal(':where(
|
|
113
|
+
expect(rules.item(0).selectorText).to.equal(':where(&:not([hidden])), :host(:where(:not([hidden])))');
|
|
114
114
|
expect(rules.item(1).selectorText.startsWith('.scroll')).to.true;
|
|
115
115
|
expect(rules.item(1).style.background).to.equal('red');
|
|
116
116
|
expect(rules.item(1).cssRules.item(0).selectorText).to.equal('&:hover *');
|