@mantou/gem 2.2.1 → 2.2.3
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 +1154 -1154
- package/dist/gem.umd.js +12 -15
- package/elements/base/gesture.d.ts +1 -0
- package/elements/base/gesture.d.ts.map +1 -1
- package/elements/base/gesture.js +3 -2
- package/elements/base/gesture.js.map +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/request.d.ts +4 -4
- package/helper/request.d.ts.map +1 -1
- package/helper/request.js +14 -14
- package/helper/request.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 +50 -50
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +52 -51
- 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 +17 -9
- package/lib/lit-html.d.ts.map +1 -1
- package/lib/lit-html.js +6 -9
- package/lib/lit-html.js.map +1 -1
- package/lib/reactive.d.ts +18 -5
- package/lib/reactive.d.ts.map +1 -1
- package/lib/reactive.js +29 -22
- 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.js +1 -1
- package/package.json +2 -2
- package/src/elements/base/gesture.ts +4 -2
- 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/request.ts +17 -17
- package/src/helper/ssr-shim.ts +0 -1
- package/src/helper/theme.ts +25 -10
- package/src/lib/decorators.ts +53 -56
- package/src/lib/element.ts +0 -1
- package/src/lib/history.ts +3 -4
- package/src/lib/lit-html.ts +13 -6
- package/src/lib/reactive.ts +66 -28
- package/src/lib/store.ts +3 -2
- package/src/lib/utils.ts +15 -8
- package/src/lib/version.ts +1 -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/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>) {
|
|
@@ -480,15 +482,10 @@ export function connectStore(store: Store<any>) {
|
|
|
480
482
|
};
|
|
481
483
|
}
|
|
482
484
|
|
|
483
|
-
export function shadow({
|
|
484
|
-
mode = 'open',
|
|
485
|
-
serializable = true,
|
|
486
|
-
delegatesFocus,
|
|
487
|
-
slotAssignment,
|
|
488
|
-
}: Partial<ShadowRootInit> = {}) {
|
|
485
|
+
export function shadow({ mode = 'open', serializable = true, ...rest }: Partial<ShadowRootInit> = {}) {
|
|
489
486
|
return (_: any, context: ClassDecoratorContext) => {
|
|
490
487
|
const metadata = context.metadata as Metadata;
|
|
491
|
-
assign(metadata, { mode, serializable,
|
|
488
|
+
assign(metadata, { mode, serializable, ...rest });
|
|
492
489
|
};
|
|
493
490
|
}
|
|
494
491
|
|
|
@@ -523,10 +520,10 @@ export function aria(info: Metadata['aria']) {
|
|
|
523
520
|
/**
|
|
524
521
|
* 定义自定义元素
|
|
525
522
|
*
|
|
526
|
-
*
|
|
523
|
+
* @example
|
|
527
524
|
* ```ts
|
|
528
|
-
*
|
|
529
|
-
*
|
|
525
|
+
* @customElement('my-element')
|
|
526
|
+
* class App extends GemElement {}
|
|
530
527
|
* ```
|
|
531
528
|
*/
|
|
532
529
|
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
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/** biome-ignore-all lint/complexity/noCommaOperator: lit */
|
|
2
|
+
/** biome-ignore-all lint/style/useTemplate: lit */
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* @license
|
|
3
6
|
* Copyright 2017 Google LLC
|
|
@@ -371,6 +374,7 @@ const getTemplateHtml = (strings: TemplateStringsArray, type: ResultType): [Trus
|
|
|
371
374
|
|
|
372
375
|
/** @internal */
|
|
373
376
|
export type { Template };
|
|
377
|
+
|
|
374
378
|
class Template {
|
|
375
379
|
/** @internal */
|
|
376
380
|
el!: HTMLTemplateElement;
|
|
@@ -544,15 +548,13 @@ function resolveDirective(
|
|
|
544
548
|
return value;
|
|
545
549
|
}
|
|
546
550
|
|
|
547
|
-
export type { TemplateInstance };
|
|
548
551
|
/**
|
|
549
552
|
* An updateable instance of a Template. Holds references to the Parts used to
|
|
550
553
|
* update the template instance.
|
|
551
554
|
*/
|
|
552
|
-
class TemplateInstance implements Disconnectable {
|
|
555
|
+
export class TemplateInstance implements Disconnectable {
|
|
553
556
|
_$template: Template;
|
|
554
557
|
_$parts: Array<Part | undefined> = [];
|
|
555
|
-
/** @internal */
|
|
556
558
|
_$conditions: ConditionChain[] = [];
|
|
557
559
|
|
|
558
560
|
/** @internal */
|
|
@@ -653,12 +655,13 @@ class TemplateInstance implements Disconnectable {
|
|
|
653
655
|
}
|
|
654
656
|
}
|
|
655
657
|
|
|
656
|
-
class ConditionChain {
|
|
658
|
+
export class ConditionChain {
|
|
657
659
|
readonly mark: Comment;
|
|
658
660
|
readonly list: { ele: Element; idx?: number }[];
|
|
659
661
|
|
|
660
662
|
constructor(ele: Element, idx: number) {
|
|
661
663
|
const mark = createMarker();
|
|
664
|
+
(mark as any)._$chain = this;
|
|
662
665
|
ele.before(mark);
|
|
663
666
|
this.mark = mark;
|
|
664
667
|
this.list = [{ ele, idx }];
|
|
@@ -1009,6 +1012,7 @@ export interface RootPart extends ChildPart {
|
|
|
1009
1012
|
}
|
|
1010
1013
|
|
|
1011
1014
|
export type { AttributePart };
|
|
1015
|
+
|
|
1012
1016
|
class AttributePart implements Disconnectable {
|
|
1013
1017
|
readonly type: typeof ATTRIBUTE_PART | typeof PROPERTY_PART | typeof BOOLEAN_ATTRIBUTE_PART | typeof EVENT_PART =
|
|
1014
1018
|
ATTRIBUTE_PART;
|
|
@@ -1132,6 +1136,7 @@ class AttributePart implements Disconnectable {
|
|
|
1132
1136
|
}
|
|
1133
1137
|
|
|
1134
1138
|
export type { PropertyPart };
|
|
1139
|
+
|
|
1135
1140
|
class PropertyPart extends AttributePart {
|
|
1136
1141
|
override readonly type = PROPERTY_PART;
|
|
1137
1142
|
|
|
@@ -1142,6 +1147,7 @@ class PropertyPart extends AttributePart {
|
|
|
1142
1147
|
}
|
|
1143
1148
|
|
|
1144
1149
|
export type { BooleanAttributePart };
|
|
1150
|
+
|
|
1145
1151
|
class BooleanAttributePart extends AttributePart {
|
|
1146
1152
|
override readonly type = BOOLEAN_ATTRIBUTE_PART;
|
|
1147
1153
|
|
|
@@ -1154,6 +1160,7 @@ class BooleanAttributePart extends AttributePart {
|
|
|
1154
1160
|
type EventListenerWithOptions = EventListenerOrEventListenerObject & Partial<AddEventListenerOptions>;
|
|
1155
1161
|
|
|
1156
1162
|
export type { EventPart };
|
|
1163
|
+
|
|
1157
1164
|
class EventPart extends AttributePart {
|
|
1158
1165
|
override readonly type = EVENT_PART;
|
|
1159
1166
|
|
|
@@ -1189,8 +1196,7 @@ class EventPart extends AttributePart {
|
|
|
1189
1196
|
}
|
|
1190
1197
|
}
|
|
1191
1198
|
|
|
1192
|
-
export
|
|
1193
|
-
class ElementPart implements Disconnectable {
|
|
1199
|
+
export class ElementPart implements Disconnectable {
|
|
1194
1200
|
readonly type = ELEMENT_PART;
|
|
1195
1201
|
|
|
1196
1202
|
/** @internal */
|
|
@@ -1244,6 +1250,7 @@ class ElementPart implements Disconnectable {
|
|
|
1244
1250
|
export const createRef = <T = Element>() => new Ref<T>();
|
|
1245
1251
|
|
|
1246
1252
|
export type { Ref };
|
|
1253
|
+
|
|
1247
1254
|
class Ref<T = Element> {
|
|
1248
1255
|
value?: T;
|
|
1249
1256
|
}
|
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;
|
|
@@ -13,6 +25,14 @@ export function setEngine(h: typeof Lit.html, r: typeof Lit.render) {
|
|
|
13
25
|
render = r;
|
|
14
26
|
}
|
|
15
27
|
|
|
28
|
+
let hydrationEngine = (_value: unknown, _container: HTMLElement | DocumentFragment): Lit.RootPart | undefined => {
|
|
29
|
+
return;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function setHydrationEngine(engine: typeof hydrationEngine) {
|
|
33
|
+
hydrationEngine = engine;
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
const { assign, defineProperty } = Object;
|
|
17
37
|
|
|
18
38
|
// https://github.com/tc39/proposal-decorator-metadata
|
|
@@ -20,12 +40,16 @@ defineProperty(Symbol, 'metadata', { value: Symbol.for('Symbol.metadata') });
|
|
|
20
40
|
|
|
21
41
|
const execCallback = (fun: any) => typeof fun === 'function' && fun();
|
|
22
42
|
|
|
43
|
+
const ssrNativeQueueMicrotask = (globalThis as any).nQM;
|
|
44
|
+
|
|
23
45
|
// 读取构造样式表,跨多个 gem 工作
|
|
24
46
|
export const SheetToken = Symbol.for('gem@sheetToken');
|
|
47
|
+
export type SheetTokenType = typeof SheetToken;
|
|
25
48
|
// 更新元素,跨多个 gem 工作
|
|
26
49
|
export const UpdateToken = Symbol.for('gem@update');
|
|
27
50
|
|
|
28
51
|
const BoundaryCSSState = 'gem-style-boundary';
|
|
52
|
+
const ElementCSSState = 'gem-element';
|
|
29
53
|
|
|
30
54
|
/**@internal */
|
|
31
55
|
export const _RenderErrorEvent = 'gem@render-error';
|
|
@@ -67,7 +91,7 @@ class GemCSSSheet {
|
|
|
67
91
|
// 不需要 GC
|
|
68
92
|
#record = new Map<any, GemCSSStyleSheet>();
|
|
69
93
|
#used = new Map<GemCSSStyleSheet, string>();
|
|
70
|
-
getStyle(host?: HTMLElement) {
|
|
94
|
+
getStyle(host?: HTMLElement, isEleTheme?: boolean) {
|
|
71
95
|
const isLight = host && !(host as GemElement).internals?.shadowRoot;
|
|
72
96
|
|
|
73
97
|
// 对同一类 dom 只使用同一个样式表
|
|
@@ -86,7 +110,7 @@ class GemCSSSheet {
|
|
|
86
110
|
if (isLight) {
|
|
87
111
|
// light dom 嵌套时需要选择子元素
|
|
88
112
|
// `> *` 实际上是多范围?是否存在性能问题
|
|
89
|
-
scope = `@scope (${host.tagName}) to (:state(${BoundaryCSSState}) > *)`;
|
|
113
|
+
scope = isEleTheme ? '@scope' : `@scope (${host.tagName}) to (:state(${BoundaryCSSState}) > *)`;
|
|
90
114
|
// 不能使用 @layer,两个 @layer 不能覆盖,只有顺序起作用
|
|
91
115
|
// 所以外部不能通过元素名称选择器来覆盖样式,除非样式在之前插入(会自动反转应用到尾部, see `appleCSSStyleSheet`)
|
|
92
116
|
style = `${scope}{ ${style} }`;
|
|
@@ -143,16 +167,13 @@ export function css<T extends Record<string, string>>(
|
|
|
143
167
|
// 对于已经有 `-` 的保留原始 key,支持覆盖修改
|
|
144
168
|
// :scope 下可以写嵌套样式 &:xxx,:host() 下不行(子内容可以)
|
|
145
169
|
sheet[key] = isScope || key.includes('-') ? key : `${key}-${randomStr()}`;
|
|
146
|
-
style += `${isScope ? ':where(
|
|
170
|
+
style += `${isScope ? ':where(&:not([hidden])),:host(:where(:not([hidden])))' : `.${sheet[key]}`} {${rules[key]}}`;
|
|
147
171
|
});
|
|
148
172
|
}
|
|
149
173
|
styleSheet.setContent(style);
|
|
150
174
|
return sheet as Sheet<T>;
|
|
151
175
|
}
|
|
152
176
|
|
|
153
|
-
// TODO: move to transform?
|
|
154
|
-
export const isSSR = typeof process !== 'undefined';
|
|
155
|
-
|
|
156
177
|
const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleSheet[], value: number) => {
|
|
157
178
|
let needUpdate = false;
|
|
158
179
|
sheets.forEach((e) => {
|
|
@@ -164,7 +185,8 @@ const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleShee
|
|
|
164
185
|
});
|
|
165
186
|
if (needUpdate) {
|
|
166
187
|
// 避免重复更新,例如 list 元素增删,全 light dom 时 document 更新
|
|
167
|
-
|
|
188
|
+
// 在 SSR 中应该区别 effect 强制执行
|
|
189
|
+
addMicrotask(rootUpdateFnMap.get(map)!, ssrNativeQueueMicrotask);
|
|
168
190
|
}
|
|
169
191
|
};
|
|
170
192
|
|
|
@@ -175,7 +197,7 @@ const appleCSSStyleSheet = (ele: HTMLElement, sheets: CSSStyleSheet[]) => {
|
|
|
175
197
|
rootStyleSheetInfo.set(root, map);
|
|
176
198
|
rootUpdateFnMap.set(map, () => {
|
|
177
199
|
// 先找到外部样式表
|
|
178
|
-
const newSheets = root.adoptedStyleSheets.filter((e) => !map.has(e));
|
|
200
|
+
const newSheets = (root.adoptedStyleSheets || []).filter((e) => !map.has(e));
|
|
179
201
|
map.forEach((count, sheet) => count && newSheets.push(sheet));
|
|
180
202
|
// 外层元素的样式要放到最后,以提升优先级,但是只考虑第一次出现样式表的位置
|
|
181
203
|
root.adoptedStyleSheets = newSheets.reverse();
|
|
@@ -189,7 +211,7 @@ const appleCSSStyleSheet = (ele: HTMLElement, sheets: CSSStyleSheet[]) => {
|
|
|
189
211
|
};
|
|
190
212
|
|
|
191
213
|
/**必须使用在字段中,否则会读取到错误的实例 */
|
|
192
|
-
export let createState: <T>(initState: T) =>
|
|
214
|
+
export let createState: <T>(initState: T) => Updater<T>;
|
|
193
215
|
|
|
194
216
|
type GetDepFun<T> = () => T;
|
|
195
217
|
type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
|
|
@@ -234,6 +256,10 @@ export let _createTemplate: (ele: GemElement, item: RenderItem) => void;
|
|
|
234
256
|
export type Metadata = Partial<ShadowRootInit> & {
|
|
235
257
|
/** 内容可被外部样式化 */
|
|
236
258
|
penetrable?: boolean;
|
|
259
|
+
/**
|
|
260
|
+
* 这个属性用来解决同步渲染长列表,且渲染过程有大量计算阻塞主线程
|
|
261
|
+
* 否则应该考虑 `content-visibility: auto` 和虚拟列表
|
|
262
|
+
*/
|
|
237
263
|
noBlocking?: boolean;
|
|
238
264
|
aria?: Partial<
|
|
239
265
|
ARIAMixin & {
|
|
@@ -258,7 +284,7 @@ export type Metadata = Partial<ShadowRootInit> & {
|
|
|
258
284
|
// global render task pool
|
|
259
285
|
const noBlockingTaskList = new LinkedList<() => void>();
|
|
260
286
|
const tick = (timeStamp = performance.now()) => {
|
|
261
|
-
if (performance.now() > timeStamp +
|
|
287
|
+
if (performance.now() > timeStamp + 50) return requestAnimationFrame(tick);
|
|
262
288
|
const task = noBlockingTaskList.get();
|
|
263
289
|
if (task) {
|
|
264
290
|
task();
|
|
@@ -269,12 +295,28 @@ noBlockingTaskList.addEventListener('start', () => addMicrotask(tick));
|
|
|
269
295
|
|
|
270
296
|
let currentConstructGemElement: GemElement;
|
|
271
297
|
|
|
298
|
+
type KebabCase<S extends string> = S extends `${infer Head}${infer Tail}`
|
|
299
|
+
? Tail extends Uncapitalize<Tail>
|
|
300
|
+
? `${Lowercase<Head>}${KebabCase<Tail>}`
|
|
301
|
+
: `${Lowercase<Head>}-${KebabCase<Tail>}`
|
|
302
|
+
: S;
|
|
303
|
+
|
|
272
304
|
export abstract class GemElement extends HTMLElement {
|
|
305
|
+
/** 用来判断 attribute 是否存在,但是 length/item 将读取到 NamedNodeMap */
|
|
306
|
+
declare readonly attributes: NamedNodeMap & {
|
|
307
|
+
[K in keyof this as K extends keyof NamedNodeMap
|
|
308
|
+
? never
|
|
309
|
+
: K extends keyof GemElement
|
|
310
|
+
? never
|
|
311
|
+
: K extends Uppercase<string & K>
|
|
312
|
+
? never
|
|
313
|
+
: `${KebabCase<string & K>}`]?: Attr;
|
|
314
|
+
};
|
|
273
315
|
#renderRoot: HTMLElement | ShadowRoot;
|
|
274
316
|
#internals: ElementInternals & {
|
|
275
317
|
// 有别于 adoptedStyleSheets,用来定义单个实例样式
|
|
276
318
|
sheets: GemCSSStyleSheet[];
|
|
277
|
-
stateList:
|
|
319
|
+
stateList: Updater<any>[];
|
|
278
320
|
};
|
|
279
321
|
#effectList: EffectItem<any>[] = [];
|
|
280
322
|
#memoList: EffectItem<any>[] = [];
|
|
@@ -297,13 +339,15 @@ export abstract class GemElement extends HTMLElement {
|
|
|
297
339
|
const state = createUpdater(initState, (payload) => {
|
|
298
340
|
const effect = ele.#effectList.at(0);
|
|
299
341
|
// https://github.com/mantou132/gem/issues/203
|
|
300
|
-
|
|
301
|
-
|
|
342
|
+
// SSR 不会执行 initEffect,应该跳过
|
|
343
|
+
if (!ssrNativeQueueMicrotask && ele.#isMounted && effect && !effect.initialized) {
|
|
344
|
+
throw new GemError(`Don't set state sync before insert the DOM`);
|
|
302
345
|
}
|
|
303
346
|
assign(state, payload);
|
|
304
347
|
// 避免无限刷新
|
|
305
348
|
// 挂载前 set state 不应该触发更新
|
|
306
|
-
|
|
349
|
+
// SSR 中渲染 <gem-route>,其他情况的 SSR 应该也可以执行
|
|
350
|
+
if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update, ssrNativeQueueMicrotask);
|
|
307
351
|
});
|
|
308
352
|
ele.#internals.stateList.push(state);
|
|
309
353
|
return state;
|
|
@@ -314,13 +358,13 @@ export abstract class GemElement extends HTMLElement {
|
|
|
314
358
|
constructor() {
|
|
315
359
|
super();
|
|
316
360
|
|
|
317
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
318
361
|
currentConstructGemElement = this;
|
|
319
362
|
|
|
320
|
-
const {
|
|
363
|
+
const { aria, mode, delegatesFocus, ...rest } = this.#metadata;
|
|
321
364
|
const { focusable, ...internalsAria } = aria || {};
|
|
322
365
|
|
|
323
|
-
|
|
366
|
+
// 这里认为 rest 不会有自定义字段和 ShadowRootInit 参数冲突
|
|
367
|
+
this.#renderRoot = !mode ? this : this.shadowRoot || this.attachShadow({ mode, delegatesFocus, ...rest });
|
|
324
368
|
this.#internals = this.attachInternals() as GemElement['internals'];
|
|
325
369
|
this.#internals.stateList = [];
|
|
326
370
|
this.#internals.sheets = [];
|
|
@@ -385,16 +429,8 @@ export abstract class GemElement extends HTMLElement {
|
|
|
385
429
|
const isLight = this.#renderRoot === this;
|
|
386
430
|
const temp = item ? item.render() : isLight ? undefined : html`<slot></slot>`;
|
|
387
431
|
if (temp === undefined) return;
|
|
388
|
-
|
|
389
|
-
|
|
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>`,
|
|
396
|
-
this.#renderRoot,
|
|
397
|
-
);
|
|
432
|
+
const r = temp !== null ? temp : html`<style>${isLight ? '@scope' : ':host'}{&{display:none!important;}}</style>`;
|
|
433
|
+
hydrationEngine(r, this.#renderRoot) || render(r, this.#renderRoot);
|
|
398
434
|
} catch (err) {
|
|
399
435
|
this.dispatchEvent(new CustomEvent(_RenderErrorEvent, { bubbles: true, composed: true, detail: err }));
|
|
400
436
|
throw err;
|
|
@@ -483,6 +519,8 @@ export abstract class GemElement extends HTMLElement {
|
|
|
483
519
|
|
|
484
520
|
const { observedStores, mode, penetrable } = this.#metadata;
|
|
485
521
|
|
|
522
|
+
// https://github.com/w3c/csswg-drafts/issues/10407#issuecomment-4418616596
|
|
523
|
+
this.#internals.states.add(ElementCSSState);
|
|
486
524
|
// 有渲染函数的 light dom 应该添加边界,防止内容被外部样式化
|
|
487
525
|
// 如果渲染内容需要应用外部样式,需要手动 `delete` 边界
|
|
488
526
|
if (!mode && this.#renderList.length && !penetrable) this.#internals.states.add(BoundaryCSSState);
|
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 @@
|
|
|
1
|
-
export const version = '2.2.
|
|
1
|
+
export const version = '2.2.3';
|
|
@@ -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 *');
|