@mantou/gem 2.1.0 → 2.2.0
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 +1845 -1904
- package/dist/gem.umd.js +34 -191
- package/elements/base/dialog.d.ts +0 -4
- package/elements/base/dialog.d.ts.map +1 -1
- package/elements/base/dialog.js +0 -4
- package/elements/base/dialog.js.map +1 -1
- package/elements/base/gesture.d.ts +4 -14
- package/elements/base/gesture.d.ts.map +1 -1
- package/elements/base/gesture.js +34 -37
- package/elements/base/gesture.js.map +1 -1
- package/elements/base/link.d.ts +2 -19
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +29 -42
- package/elements/base/link.js.map +1 -1
- package/elements/base/modal-factory.d.ts +32 -33
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/reflect.d.ts +0 -3
- package/elements/base/reflect.d.ts.map +1 -1
- package/elements/base/reflect.js +0 -3
- package/elements/base/reflect.js.map +1 -1
- package/elements/base/route.d.ts +0 -10
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +10 -22
- package/elements/base/route.js.map +1 -1
- package/elements/base/title.d.ts +0 -6
- package/elements/base/title.d.ts.map +1 -1
- package/elements/base/title.js +13 -8
- package/elements/base/title.js.map +1 -1
- package/elements/base/unsafe.d.ts +1 -6
- package/elements/base/unsafe.d.ts.map +1 -1
- package/elements/base/unsafe.js +12 -12
- package/elements/base/unsafe.js.map +1 -1
- package/elements/base/use.d.ts +0 -4
- package/elements/base/use.d.ts.map +1 -1
- package/elements/base/use.js +15 -13
- package/elements/base/use.js.map +1 -1
- package/helper/theme.d.ts +3 -3
- package/helper/theme.d.ts.map +1 -1
- package/helper/theme.js +3 -3
- package/helper/theme.js.map +1 -1
- package/index.d.ts +0 -1
- package/index.d.ts.map +1 -1
- package/index.js +0 -1
- package/index.js.map +1 -1
- package/lib/decorators.d.ts +7 -6
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +48 -33
- package/lib/decorators.js.map +1 -1
- package/lib/directive.d.ts +83 -0
- package/lib/directive.d.ts.map +1 -0
- package/lib/directive.js +59 -0
- package/lib/directive.js.map +1 -0
- package/lib/element.d.ts +9 -23
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js +61 -56
- package/lib/element.js.map +1 -1
- package/lib/lit-html.d.ts +331 -0
- package/lib/lit-html.d.ts.map +1 -0
- package/lib/lit-html.js +941 -0
- package/lib/lit-html.js.map +1 -0
- package/lib/ref.d.ts +1 -0
- package/lib/ref.d.ts.map +1 -0
- package/lib/ref.js +2 -0
- package/lib/ref.js.map +1 -0
- package/lib/repeat.d.ts +126 -0
- package/lib/repeat.d.ts.map +1 -0
- package/lib/repeat.js +533 -0
- package/lib/repeat.js.map +1 -0
- package/lib/store.d.ts +1 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +4 -4
- package/lib/store.js.map +1 -1
- package/lib/utils.d.ts +1 -4
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +6 -15
- package/lib/utils.js.map +1 -1
- package/package.json +7 -9
- package/src/elements/base/dialog.ts +0 -4
- package/src/elements/base/gesture.ts +32 -35
- package/src/elements/base/link.ts +21 -41
- package/src/elements/base/reflect.ts +0 -3
- package/src/elements/base/route.ts +11 -21
- package/src/elements/base/title.ts +3 -7
- package/src/elements/base/unsafe.ts +9 -11
- package/src/elements/base/use.ts +8 -14
- package/src/helper/theme.ts +4 -4
- package/src/index.ts +0 -1
- package/src/lib/decorators.ts +53 -40
- package/src/lib/directive.ts +139 -0
- package/src/lib/element.ts +70 -79
- package/src/lib/lit-html.ts +1217 -0
- package/src/lib/repeat.ts +580 -0
- package/src/lib/store.ts +4 -4
- package/src/lib/utils.ts +6 -16
- package/src/test/elements/link.test.ts +1 -2
- package/src/test/elements/reflect.test.ts +13 -3
- package/src/test/elements/title.test.ts +1 -2
- package/src/test/gem-element/advance.test.ts +15 -8
- package/src/test/gem-element/basic.test.ts +22 -10
- package/src/test/gem-element/decorators.test.ts +6 -9
- package/src/test/gem-element/multi.test.ts +20 -11
- package/src/test/history.test.ts +2 -2
- package/src/test/store.test.ts +3 -3
- package/src/test/utils.test.ts +9 -10
- package/src/test/utils.ts +22 -0
- package/LICENSE +0 -21
- package/src/lib/directives.ts +0 -6
package/src/lib/decorators.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { GemElement, UpdateToken,
|
|
1
|
+
import type { css, Metadata, TemplateResult } from './element';
|
|
2
|
+
import { GemElement, UpdateToken, _createTemplate, _RenderErrorEvent, render } from './element';
|
|
3
3
|
import { camelToKebabCase, PropProxyMap, GemError } from './utils';
|
|
4
4
|
import type { Store } from './store';
|
|
5
5
|
import * as elementExports from './element';
|
|
@@ -7,7 +7,7 @@ import * as decoratorsExports from './decorators';
|
|
|
7
7
|
import * as storeExports from './store';
|
|
8
8
|
import * as versionExports from './version';
|
|
9
9
|
|
|
10
|
-
type GemElementPrototype = GemElement;
|
|
10
|
+
type GemElementPrototype = GemElement & { '': never };
|
|
11
11
|
type StaticField = Exclude<keyof Metadata, keyof ShadowRootInit | 'aria' | 'noBlocking' | 'penetrable'>;
|
|
12
12
|
|
|
13
13
|
const { deleteProperty, getOwnPropertyDescriptor, defineProperty } = Reflect;
|
|
@@ -18,10 +18,8 @@ function pushStaticField(context: ClassFieldDecoratorContext | ClassDecoratorCon
|
|
|
18
18
|
const metadata = context.metadata as Metadata;
|
|
19
19
|
if (!getOwnPropertyDescriptor(metadata, field)) {
|
|
20
20
|
// 继承基类
|
|
21
|
-
const current = new Set<unknown>(metadata[field]);
|
|
22
|
-
current.delete(member);
|
|
23
21
|
defineProperty(metadata, field, {
|
|
24
|
-
value: [
|
|
22
|
+
value: metadata[field]?.filter((e) => e !== member) || [],
|
|
25
23
|
});
|
|
26
24
|
}
|
|
27
25
|
|
|
@@ -96,10 +94,13 @@ function defineProp(
|
|
|
96
94
|
if (event) {
|
|
97
95
|
proxy[prop] = v?.[isEventHandleSymbol]
|
|
98
96
|
? v
|
|
99
|
-
: (detail: any, options: any) => {
|
|
97
|
+
: async (detail: any, options: any) => {
|
|
98
|
+
// 事件如果同步触发,则祖先元素可能还没有挂载好,其中就不能更新 state
|
|
99
|
+
// https://github.com/mantou132/gem/issues/203
|
|
100
|
+
await Promise.resolve();
|
|
100
101
|
const evt = new CustomEvent(event, { ...options, ...eventOptions, detail });
|
|
101
102
|
that.dispatchEvent(evt);
|
|
102
|
-
v(detail, options);
|
|
103
|
+
await v(detail, options);
|
|
103
104
|
};
|
|
104
105
|
(proxy[prop] as any)[isEventHandleSymbol] = true;
|
|
105
106
|
// emitter 不触发元素更新
|
|
@@ -111,21 +112,35 @@ function defineProp(
|
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
// https://github.com/tc39/proposal-decorators/issues/517
|
|
116
|
+
const targetCache = new WeakMap<DecoratorMetadataObject, GemElementPrototype>();
|
|
117
|
+
function getDecoratorTarget(element: GemElement, { metadata }: ClassFieldDecoratorContext) {
|
|
118
|
+
const target = targetCache.get(metadata);
|
|
119
|
+
if (target) return target;
|
|
120
|
+
let result = element as GemElementPrototype;
|
|
121
|
+
do {
|
|
122
|
+
result = getPrototypeOf(result);
|
|
123
|
+
} while (result.constructor[Symbol.metadata] !== metadata);
|
|
124
|
+
targetCache.set(metadata, result);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
type AttrType = BooleanConstructor | NumberConstructor | StringConstructor;
|
|
115
129
|
function decoratorAttr<T extends GemElement>(context: ClassFieldDecoratorContext<T>, attrType: AttrType) {
|
|
116
130
|
const prop = context.name as string;
|
|
117
131
|
const attr = camelToKebabCase(prop);
|
|
118
132
|
context.addInitializer(function (this: T) {
|
|
119
|
-
const target =
|
|
133
|
+
const target = getDecoratorTarget(this, context);
|
|
120
134
|
if (!target.hasOwnProperty(prop)) {
|
|
121
135
|
pushStaticField(context, 'observedAttributes', attr); // 没有 observe 的效果
|
|
122
136
|
defineProp(target, prop, { attr, attrType });
|
|
123
|
-
// 记录观察的 attribute
|
|
124
|
-
const attrMap = observedTargetAttributes.get(target) || new Map<string, string>();
|
|
125
|
-
attrMap.set(attr, prop);
|
|
126
|
-
observedTargetAttributes.set(target, attrMap);
|
|
127
137
|
}
|
|
128
138
|
clearField(this, prop);
|
|
139
|
+
// 记录观察的 attribute, 用于 hack 的 setAttribute
|
|
140
|
+
const proto = getPrototypeOf(this);
|
|
141
|
+
const attrMap = observedTargetAttributes.get(proto) || new Map<string, string>();
|
|
142
|
+
attrMap.set(attr, prop);
|
|
143
|
+
observedTargetAttributes.set(proto, attrMap);
|
|
129
144
|
});
|
|
130
145
|
// 延时定义的元素需要继承原实例属性值
|
|
131
146
|
return function (this: any, initValue: any) {
|
|
@@ -166,7 +181,7 @@ export function numattribute<T extends GemElement>(_: undefined, context: ClassF
|
|
|
166
181
|
export function property<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T>) {
|
|
167
182
|
const prop = context.name as string;
|
|
168
183
|
context.addInitializer(function (this: T) {
|
|
169
|
-
const target =
|
|
184
|
+
const target = getDecoratorTarget(this, context);
|
|
170
185
|
if (!target.hasOwnProperty(prop)) {
|
|
171
186
|
pushStaticField(context, 'observedProperties', prop);
|
|
172
187
|
defineProp(target, prop);
|
|
@@ -214,14 +229,15 @@ export function memo<T extends GemElement, V = any, K = any[] | undefined>(
|
|
|
214
229
|
}
|
|
215
230
|
|
|
216
231
|
/**
|
|
217
|
-
* 依赖 `GemElement.effect`
|
|
218
|
-
* 方法执行在字段前面
|
|
232
|
+
* - 依赖 `GemElement.effect`
|
|
233
|
+
* - 方法执行在字段前面
|
|
234
|
+
* - 如果没有 oldValue,在为首次执行
|
|
219
235
|
*
|
|
220
236
|
* For example
|
|
221
237
|
* ```ts
|
|
222
238
|
* class App extends GemElement {
|
|
223
239
|
* @effect(() => [])
|
|
224
|
-
* #fetchData() {
|
|
240
|
+
* #fetchData(newValue, oldValue) {
|
|
225
241
|
* console.log('fetch')
|
|
226
242
|
* }
|
|
227
243
|
* }
|
|
@@ -273,7 +289,7 @@ export function template<T extends GemElement, V extends () => TemplateResult |
|
|
|
273
289
|
{ addInitializer, access }: ClassFieldDecoratorContext<T, V> | ClassMethodDecoratorContext<T, V>,
|
|
274
290
|
) {
|
|
275
291
|
addInitializer(function (this: T) {
|
|
276
|
-
|
|
292
|
+
_createTemplate(this, {
|
|
277
293
|
render: access.get(this).bind(this),
|
|
278
294
|
condition: condition && (() => condition(this) as any),
|
|
279
295
|
});
|
|
@@ -287,7 +303,7 @@ export function fallback<T extends GemElement, V extends (err: any) => TemplateR
|
|
|
287
303
|
{ addInitializer, access }: ClassFieldDecoratorContext<T, V> | ClassMethodDecoratorContext<T, V>,
|
|
288
304
|
) {
|
|
289
305
|
addInitializer(function (this: T) {
|
|
290
|
-
this.addEventListener(
|
|
306
|
+
this.addEventListener(_RenderErrorEvent, ({ detail }: CustomEvent) => {
|
|
291
307
|
render(access.get(this).apply(this, [detail]), this.internals.shadowRoot || this);
|
|
292
308
|
});
|
|
293
309
|
});
|
|
@@ -328,11 +344,11 @@ function defineCSSState(target: GemElementPrototype, prop: string, stateStr: str
|
|
|
328
344
|
* ```
|
|
329
345
|
*/
|
|
330
346
|
export function state<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, boolean>) {
|
|
347
|
+
const prop = context.name as string;
|
|
348
|
+
const attr = camelToKebabCase(prop);
|
|
331
349
|
context.addInitializer(function (this: T) {
|
|
332
|
-
const target =
|
|
333
|
-
const prop = context.name as string;
|
|
350
|
+
const target = getDecoratorTarget(this, context);
|
|
334
351
|
if (!target.hasOwnProperty(prop)) {
|
|
335
|
-
const attr = camelToKebabCase(prop);
|
|
336
352
|
pushStaticField(context, 'definedCSSStates', attr);
|
|
337
353
|
defineCSSState(target, prop, attr);
|
|
338
354
|
}
|
|
@@ -394,7 +410,7 @@ export function part(_: undefined, context: ClassFieldDecoratorContext<any, stri
|
|
|
394
410
|
};
|
|
395
411
|
}
|
|
396
412
|
|
|
397
|
-
export type Emitter<T = any> = (detail?: T, options?: Omit<CustomEventInit<unknown>, 'detail'>) => void;
|
|
413
|
+
export type Emitter<T = any> = (detail?: T, options?: Omit<CustomEventInit<unknown>, 'detail'>) => Promise<void> | void;
|
|
398
414
|
|
|
399
415
|
/**
|
|
400
416
|
* 定义一个事件发射器,类似 `HTMLElement.click`,
|
|
@@ -408,28 +424,25 @@ export type Emitter<T = any> = (detail?: T, options?: Omit<CustomEventInit<unkno
|
|
|
408
424
|
* ```
|
|
409
425
|
*/
|
|
410
426
|
export function emitter<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, Emitter>) {
|
|
411
|
-
context
|
|
412
|
-
defineEmitter(context, this, context.name as string);
|
|
413
|
-
});
|
|
427
|
+
defineEmitter(context);
|
|
414
428
|
}
|
|
415
429
|
export function globalemitter<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, Emitter>) {
|
|
416
|
-
context
|
|
417
|
-
defineEmitter(context, this, context.name as string, { bubbles: true, composed: true });
|
|
418
|
-
});
|
|
430
|
+
defineEmitter(context, { bubbles: true, composed: true });
|
|
419
431
|
}
|
|
420
|
-
function defineEmitter(
|
|
432
|
+
function defineEmitter<T extends GemElement>(
|
|
421
433
|
context: ClassFieldDecoratorContext,
|
|
422
|
-
t: GemElement,
|
|
423
|
-
prop: string,
|
|
424
434
|
eventOptions?: Omit<CustomEventInit<unknown>, 'detail'>,
|
|
425
435
|
) {
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
436
|
+
const prop = context.name as string;
|
|
437
|
+
const event = camelToKebabCase(prop);
|
|
438
|
+
context.addInitializer(function (this: T) {
|
|
439
|
+
const target = getDecoratorTarget(this, context);
|
|
440
|
+
if (!target.hasOwnProperty(prop)) {
|
|
441
|
+
pushStaticField(context, 'definedEvents', event);
|
|
442
|
+
defineProp(target, prop, { event, eventOptions });
|
|
443
|
+
}
|
|
444
|
+
clearField(this, prop);
|
|
445
|
+
});
|
|
433
446
|
}
|
|
434
447
|
|
|
435
448
|
/**
|
|
@@ -441,7 +454,7 @@ function defineEmitter(
|
|
|
441
454
|
* class App extends GemElement {}
|
|
442
455
|
* ```
|
|
443
456
|
*/
|
|
444
|
-
export function adoptedStyle(sheet: ReturnType<typeof
|
|
457
|
+
export function adoptedStyle(sheet: ReturnType<typeof css>) {
|
|
445
458
|
return function (_: unknown, context: ClassDecoratorContext) {
|
|
446
459
|
pushStaticField(context, 'adoptedStyleSheets', sheet);
|
|
447
460
|
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2017 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Disconnectable, Part } from './lit-html.js';
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
AttributePart,
|
|
11
|
+
BooleanAttributePart,
|
|
12
|
+
ChildPart,
|
|
13
|
+
ElementPart,
|
|
14
|
+
EventPart,
|
|
15
|
+
Part,
|
|
16
|
+
PropertyPart,
|
|
17
|
+
} from './lit-html.js';
|
|
18
|
+
|
|
19
|
+
export interface DirectiveClass {
|
|
20
|
+
new (part: PartInfo): Directive;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* This utility type extracts the signature of a directive class's render()
|
|
25
|
+
* method so we can use it for the type of the generated directive function.
|
|
26
|
+
*/
|
|
27
|
+
export type DirectiveParameters<C extends Directive> = Parameters<C['render']>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A generated directive function doesn't evaluate the directive, but just
|
|
31
|
+
* returns a DirectiveResult object that captures the arguments.
|
|
32
|
+
*/
|
|
33
|
+
export interface DirectiveResult<C extends DirectiveClass = DirectiveClass> {
|
|
34
|
+
/**
|
|
35
|
+
* This property needs to remain unminified.
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
['_$litDirective$']: C;
|
|
39
|
+
/** @internal */
|
|
40
|
+
values: DirectiveParameters<InstanceType<C>>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const PartType = {
|
|
44
|
+
ATTRIBUTE: 1,
|
|
45
|
+
CHILD: 2,
|
|
46
|
+
PROPERTY: 3,
|
|
47
|
+
BOOLEAN_ATTRIBUTE: 4,
|
|
48
|
+
EVENT: 5,
|
|
49
|
+
ELEMENT: 6,
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
export type PartType = (typeof PartType)[keyof typeof PartType];
|
|
53
|
+
|
|
54
|
+
export interface ChildPartInfo {
|
|
55
|
+
readonly type: typeof PartType.CHILD;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AttributePartInfo {
|
|
59
|
+
readonly type:
|
|
60
|
+
| typeof PartType.ATTRIBUTE
|
|
61
|
+
| typeof PartType.PROPERTY
|
|
62
|
+
| typeof PartType.BOOLEAN_ATTRIBUTE
|
|
63
|
+
| typeof PartType.EVENT;
|
|
64
|
+
readonly strings?: ReadonlyArray<string>;
|
|
65
|
+
readonly name: string;
|
|
66
|
+
readonly tagName: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ElementPartInfo {
|
|
70
|
+
readonly type: typeof PartType.ELEMENT;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Information about the part a directive is bound to.
|
|
75
|
+
*
|
|
76
|
+
* This is useful for checking that a directive is attached to a valid part,
|
|
77
|
+
* such as with directive that can only be used on attribute bindings.
|
|
78
|
+
*/
|
|
79
|
+
export type PartInfo = ChildPartInfo | AttributePartInfo | ElementPartInfo;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates a user-facing directive function from a Directive class. This
|
|
83
|
+
* function has the same parameters as the directive's render() method.
|
|
84
|
+
*/
|
|
85
|
+
export const directive =
|
|
86
|
+
<C extends DirectiveClass>(c: C) =>
|
|
87
|
+
(...values: DirectiveParameters<InstanceType<C>>): DirectiveResult<C> => ({
|
|
88
|
+
// This property needs to remain unminified.
|
|
89
|
+
['_$litDirective$']: c,
|
|
90
|
+
values,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Base class for creating custom directives. Users should extend this class,
|
|
95
|
+
* implement `render` and/or `update`, and then pass their subclass to
|
|
96
|
+
* `directive`.
|
|
97
|
+
*/
|
|
98
|
+
export abstract class Directive implements Disconnectable {
|
|
99
|
+
//@internal
|
|
100
|
+
__part!: Part;
|
|
101
|
+
//@internal
|
|
102
|
+
__attributeIndex: number | undefined;
|
|
103
|
+
//@internal
|
|
104
|
+
__directive?: Directive;
|
|
105
|
+
|
|
106
|
+
//@internal
|
|
107
|
+
_$parent!: Disconnectable;
|
|
108
|
+
|
|
109
|
+
// These will only exist on the AsyncDirective subclass
|
|
110
|
+
//@internal
|
|
111
|
+
_$disconnectableChildren?: Set<Disconnectable>;
|
|
112
|
+
// This property needs to remain unminified.
|
|
113
|
+
//@internal
|
|
114
|
+
['_$notifyDirectiveConnectionChanged']?(isConnected: boolean): void;
|
|
115
|
+
|
|
116
|
+
constructor(_partInfo: PartInfo) {}
|
|
117
|
+
|
|
118
|
+
// See comment in Disconnectable interface for why this is a getter
|
|
119
|
+
get _$isConnected() {
|
|
120
|
+
return this._$parent._$isConnected;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** @internal */
|
|
124
|
+
_$initialize(part: Part, parent: Disconnectable, attributeIndex: number | undefined) {
|
|
125
|
+
this.__part = part;
|
|
126
|
+
this._$parent = parent;
|
|
127
|
+
this.__attributeIndex = attributeIndex;
|
|
128
|
+
}
|
|
129
|
+
/** @internal */
|
|
130
|
+
_$resolve(part: Part, props: Array<unknown>): unknown {
|
|
131
|
+
return this.update(part, props);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
abstract render(...props: Array<unknown>): unknown;
|
|
135
|
+
|
|
136
|
+
update(_part: Part, props: Array<unknown>): unknown {
|
|
137
|
+
return this.render(...props);
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/lib/element.ts
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import type { GemReflectElement } from '../elements/reflect';
|
|
5
|
-
|
|
1
|
+
import { html, render } from './lit-html';
|
|
2
|
+
import type { TemplateResult } from './lit-html';
|
|
6
3
|
import type { Store } from './store';
|
|
7
4
|
import { connect } from './store';
|
|
8
|
-
import { LinkedList, addMicrotask, isArrayChange, addListener, randomStr, createUpdater } from './utils';
|
|
5
|
+
import { LinkedList, addMicrotask, isArrayChange, addListener, randomStr, createUpdater, GemError, raw } from './utils';
|
|
9
6
|
|
|
10
|
-
export {
|
|
7
|
+
export { directive } from './directive';
|
|
8
|
+
export { repeat } from './repeat';
|
|
9
|
+
export { html, svg, mathml, render, TemplateResult, createRef } from './lit-html';
|
|
11
10
|
|
|
12
11
|
declare global {
|
|
13
|
-
// 用于 css 选择器选择元素,使用 RefObject 自动选择获取
|
|
14
|
-
// 必须使用 attr 赋值
|
|
15
|
-
/**
|
|
16
|
-
* @attr ref
|
|
17
|
-
*/
|
|
18
|
-
interface HTMLElement {
|
|
19
|
-
ref: string;
|
|
20
|
-
}
|
|
21
12
|
interface DOMStringMap {
|
|
22
13
|
gemReflect?: '';
|
|
23
14
|
gemStyle?: '';
|
|
@@ -35,13 +26,13 @@ defineProperty(Symbol, 'metadata', { value: Symbol.for('Symbol.metadata') });
|
|
|
35
26
|
|
|
36
27
|
const execCallback = (fun: any) => typeof fun === 'function' && fun();
|
|
37
28
|
|
|
38
|
-
//
|
|
29
|
+
// 读取构造样式表,跨多个 gem 工作
|
|
39
30
|
export const SheetToken = Symbol.for('gem@sheetToken');
|
|
40
|
-
//
|
|
31
|
+
// 更新元素,跨多个 gem 工作
|
|
41
32
|
export const UpdateToken = Symbol.for('gem@update');
|
|
42
33
|
|
|
43
34
|
const BoundaryCSSState = 'gem-style-boundary';
|
|
44
|
-
export const
|
|
35
|
+
export const _RenderErrorEvent = 'gem@render-error';
|
|
45
36
|
|
|
46
37
|
// fix modal-factory type error
|
|
47
38
|
const updateTokenAlias = UpdateToken;
|
|
@@ -49,40 +40,6 @@ const updateTokenAlias = UpdateToken;
|
|
|
49
40
|
const rootStyleSheetInfo = new WeakMap<Document | ShadowRoot, Map<CSSStyleSheet, number>>();
|
|
50
41
|
const rootUpdateFnMap = new WeakMap<Map<CSSStyleSheet, number>, () => void>();
|
|
51
42
|
|
|
52
|
-
const getReflectTargets = (ele: ShadowRoot | GemElement) =>
|
|
53
|
-
[...ele.querySelectorAll<GemReflectElement>('[data-gem-reflect]')].map((e) => e.target);
|
|
54
|
-
|
|
55
|
-
class RefObject<T = HTMLElement> {
|
|
56
|
-
refSelector: string;
|
|
57
|
-
ele: GemElement | ShadowRoot;
|
|
58
|
-
ref: string;
|
|
59
|
-
|
|
60
|
-
constructor(current: GemElement) {
|
|
61
|
-
const ref = `ref-${randomStr()}`;
|
|
62
|
-
this.refSelector = `[ref=${ref}]`;
|
|
63
|
-
this.ele = current.internals.shadowRoot || current;
|
|
64
|
-
this.ref = ref;
|
|
65
|
-
}
|
|
66
|
-
get element() {
|
|
67
|
-
for (const e of [this.ele, ...getReflectTargets(this.ele)]) {
|
|
68
|
-
// 在 LightDOM 中可能工作很慢?
|
|
69
|
-
const result = e.querySelector(this.refSelector);
|
|
70
|
-
if (result) return result as T;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
get elements() {
|
|
74
|
-
return [this.ele, ...getReflectTargets(this.ele)]
|
|
75
|
-
.map((e) => [...e.querySelectorAll(this.refSelector)] as T[])
|
|
76
|
-
.flat();
|
|
77
|
-
}
|
|
78
|
-
toString() {
|
|
79
|
-
return this.ref;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**必须使用在字段中,否则会读取到错误的实例 */
|
|
84
|
-
export let createRef: <T>() => RefObject<T>;
|
|
85
|
-
|
|
86
43
|
class GemCSSStyleSheet extends CSSStyleSheet {
|
|
87
44
|
#ele?: HTMLStyleElement;
|
|
88
45
|
|
|
@@ -146,7 +103,7 @@ class GemCSSSheet {
|
|
|
146
103
|
return sheet;
|
|
147
104
|
}
|
|
148
105
|
|
|
149
|
-
//
|
|
106
|
+
// 一般用于主题更新
|
|
150
107
|
updateStyle() {
|
|
151
108
|
this.#used.forEach((scope, sheet) => {
|
|
152
109
|
sheet.replaceSync(scope ? `${scope}{${this.#content}}` : this.#content);
|
|
@@ -160,14 +117,26 @@ export type Sheet<T> = { [P in keyof T]: P } & { [SheetToken]: GemCSSSheet };
|
|
|
160
117
|
*
|
|
161
118
|
* 创建 style sheet 用于 `@adoptedStyle`,不支持样式更新,只支持自定义 CSS 属性
|
|
162
119
|
*/
|
|
163
|
-
export function
|
|
164
|
-
export function
|
|
165
|
-
export function
|
|
166
|
-
|
|
167
|
-
|
|
120
|
+
export function css<T extends Record<string, string>>(media: TemplateStringsArray, ...rest: any[]): Sheet<T>;
|
|
121
|
+
export function css<T extends Record<string, string>>(media: string, rules: T | string): Sheet<T>;
|
|
122
|
+
export function css<T extends Record<string, string>>(rules: T | string): Sheet<T>;
|
|
123
|
+
export function css<T extends Record<string, string>>(
|
|
124
|
+
mediaOrRules: T | string | TemplateStringsArray,
|
|
125
|
+
...rest: any[]
|
|
168
126
|
): Sheet<T> {
|
|
169
|
-
|
|
170
|
-
|
|
127
|
+
let media = '';
|
|
128
|
+
let rules: T | string = '';
|
|
129
|
+
|
|
130
|
+
const rulesValue = rest.at(0);
|
|
131
|
+
if (Array.isArray(mediaOrRules)) {
|
|
132
|
+
rules = raw(mediaOrRules as TemplateStringsArray, ...rest);
|
|
133
|
+
} else if (rulesValue !== undefined) {
|
|
134
|
+
media = mediaOrRules as string;
|
|
135
|
+
rules = rulesValue;
|
|
136
|
+
} else {
|
|
137
|
+
rules = mediaOrRules as string;
|
|
138
|
+
}
|
|
139
|
+
|
|
171
140
|
const styleSheet = new GemCSSSheet(media);
|
|
172
141
|
const sheet: any = { [SheetToken]: styleSheet };
|
|
173
142
|
let style = '';
|
|
@@ -232,7 +201,7 @@ type EffectItem<T> = {
|
|
|
232
201
|
fixed?: boolean; // 不需要清理,只会添加一次
|
|
233
202
|
values?: T;
|
|
234
203
|
getDep?: GetDepFun<T>;
|
|
235
|
-
preCallback?:
|
|
204
|
+
preCallback?: any;
|
|
236
205
|
};
|
|
237
206
|
|
|
238
207
|
function execEffectList(list?: EffectItem<any>[]) {
|
|
@@ -261,7 +230,20 @@ function clearEffect(list: EffectItem<any>[]) {
|
|
|
261
230
|
type Render = () => TemplateResult | null | undefined;
|
|
262
231
|
type RenderItem = { render: Render; condition?: () => boolean };
|
|
263
232
|
|
|
264
|
-
|
|
233
|
+
const nullTemplate = html`
|
|
234
|
+
<style>
|
|
235
|
+
:host {
|
|
236
|
+
display: none !important;
|
|
237
|
+
}
|
|
238
|
+
@scope {
|
|
239
|
+
:scope {
|
|
240
|
+
display: none !important;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
</style>
|
|
244
|
+
`;
|
|
245
|
+
|
|
246
|
+
export let _createTemplate: (ele: GemElement, item: RenderItem) => void;
|
|
265
247
|
|
|
266
248
|
export type Metadata = Partial<ShadowRootInit> & {
|
|
267
249
|
/** 内容可被外部样式化 */
|
|
@@ -318,30 +300,33 @@ export abstract class GemElement extends HTMLElement {
|
|
|
318
300
|
#isAppendReason?: boolean;
|
|
319
301
|
#isMounted?: boolean;
|
|
320
302
|
// not in constructor 的近似值
|
|
321
|
-
#
|
|
303
|
+
#notInCons?: boolean;
|
|
322
304
|
#rendering?: boolean;
|
|
323
|
-
#clearStyle?:
|
|
305
|
+
#clearStyle?: (() => void) | undefined;
|
|
324
306
|
|
|
325
307
|
[updateTokenAlias]() {
|
|
326
308
|
// 避免 `connectedCallback` 中的 property 赋值造成多余更新
|
|
327
|
-
if (this.#isMounted)
|
|
328
|
-
addMicrotask(this.#update);
|
|
329
|
-
}
|
|
309
|
+
if (this.#isMounted) addMicrotask(this.#update);
|
|
330
310
|
}
|
|
331
311
|
|
|
332
312
|
static {
|
|
333
313
|
createState = <T>(initState: T) => {
|
|
334
314
|
const ele = currentConstructGemElement;
|
|
335
315
|
const state = createUpdater(initState, (payload) => {
|
|
316
|
+
const effect = ele.#effectList.at(0);
|
|
317
|
+
// https://github.com/mantou132/gem/issues/203
|
|
318
|
+
if (ele.#isMounted && effect && !effect.initialized) {
|
|
319
|
+
throw new GemError(`Do't set state sync before insert the DOM`);
|
|
320
|
+
}
|
|
336
321
|
assign(state, payload);
|
|
337
322
|
// 避免无限刷新
|
|
338
|
-
|
|
323
|
+
// 挂载前 set state 不应该触发更新
|
|
324
|
+
if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update);
|
|
339
325
|
});
|
|
340
326
|
ele.#internals.stateList.push(state);
|
|
341
327
|
return state;
|
|
342
328
|
};
|
|
343
|
-
|
|
344
|
-
createTemplate = (ele, item) => ele.#renderList.push(item);
|
|
329
|
+
_createTemplate = (ele, item) => ele.#renderList.push(item);
|
|
345
330
|
}
|
|
346
331
|
|
|
347
332
|
constructor() {
|
|
@@ -385,7 +370,8 @@ export abstract class GemElement extends HTMLElement {
|
|
|
385
370
|
}
|
|
386
371
|
|
|
387
372
|
get #renderItem() {
|
|
388
|
-
|
|
373
|
+
// 从末尾开始寻找,允许被覆盖
|
|
374
|
+
return this.#renderList.findLast((e) => !e.condition || e.condition());
|
|
389
375
|
}
|
|
390
376
|
|
|
391
377
|
get internals() {
|
|
@@ -405,9 +391,10 @@ export abstract class GemElement extends HTMLElement {
|
|
|
405
391
|
|
|
406
392
|
/**
|
|
407
393
|
* - 条件渲染
|
|
408
|
-
* -
|
|
409
|
-
* - 返回 `null` 时渲染空的子内容
|
|
394
|
+
* - 没有 `render` 时显示子内容
|
|
410
395
|
* - 返回 `undefined` 时不会更新现有内容
|
|
396
|
+
* - 返回 `null` 时渲染空内容
|
|
397
|
+
* - 返回 html`` 时渲染空的子内容
|
|
411
398
|
* */
|
|
412
399
|
#render = (item?: RenderItem) => {
|
|
413
400
|
try {
|
|
@@ -417,9 +404,10 @@ export abstract class GemElement extends HTMLElement {
|
|
|
417
404
|
const temp = item ? item.render() : isLight ? undefined : html`<slot></slot>`;
|
|
418
405
|
this.#rendering = false;
|
|
419
406
|
if (temp === undefined) return;
|
|
420
|
-
render(temp, this.#renderRoot);
|
|
407
|
+
render(temp === null ? nullTemplate : temp, this.#renderRoot);
|
|
421
408
|
} catch (err) {
|
|
422
|
-
this.dispatchEvent(new CustomEvent(
|
|
409
|
+
this.dispatchEvent(new CustomEvent(_RenderErrorEvent, { bubbles: true, composed: true, detail: err }));
|
|
410
|
+
throw err;
|
|
423
411
|
}
|
|
424
412
|
};
|
|
425
413
|
|
|
@@ -493,8 +481,12 @@ export abstract class GemElement extends HTMLElement {
|
|
|
493
481
|
#connectedCallback = async () => {
|
|
494
482
|
if (this.#isAppendReason) {
|
|
495
483
|
this.#isAppendReason = false;
|
|
484
|
+
// https://github.com/mantou132/gem/issues/202
|
|
485
|
+
this.#clearStyle?.();
|
|
486
|
+
this.#clearStyle = this.#prepareStyle();
|
|
496
487
|
return;
|
|
497
488
|
}
|
|
489
|
+
this.#notInCons = true;
|
|
498
490
|
this.#compat();
|
|
499
491
|
|
|
500
492
|
const { observedStores, mode, penetrable } = this.#metadata;
|
|
@@ -503,7 +495,6 @@ export abstract class GemElement extends HTMLElement {
|
|
|
503
495
|
// 如果渲染内容需要应用外部样式,需要手动 `delete` 边界
|
|
504
496
|
if (!mode && this.#renderList.length && !penetrable) this.#internals.states.add(BoundaryCSSState);
|
|
505
497
|
|
|
506
|
-
this.#isConnected = true;
|
|
507
498
|
this.#disconnectStore = observedStores?.map((store) => connect(store, this.#update));
|
|
508
499
|
this.#render(this.#renderItem);
|
|
509
500
|
this.#isMounted = true;
|
|
@@ -553,7 +544,7 @@ export abstract class GemElement extends HTMLElement {
|
|
|
553
544
|
// 是否要异步执行回调?
|
|
554
545
|
this.#effectList = clearEffect(this.#effectList);
|
|
555
546
|
this.#memoList = clearEffect(this.#memoList);
|
|
556
|
-
|
|
547
|
+
this.#clearStyle?.();
|
|
557
548
|
return GemElement.#final;
|
|
558
549
|
}
|
|
559
550
|
|
|
@@ -576,7 +567,7 @@ export abstract class GemElement extends HTMLElement {
|
|
|
576
567
|
callback,
|
|
577
568
|
getDep,
|
|
578
569
|
initialized: this.#isMounted,
|
|
579
|
-
fixed: !this.#
|
|
570
|
+
fixed: !this.#notInCons,
|
|
580
571
|
};
|
|
581
572
|
// 已挂载时立即执行副作用,未挂载时等挂载后执行
|
|
582
573
|
if (this.#isMounted) {
|
|
@@ -607,7 +598,7 @@ export abstract class GemElement extends HTMLElement {
|
|
|
607
598
|
this.#memoList.push({
|
|
608
599
|
callback,
|
|
609
600
|
getDep,
|
|
610
|
-
fixed: !this.#
|
|
601
|
+
fixed: !this.#notInCons,
|
|
611
602
|
});
|
|
612
603
|
};
|
|
613
604
|
|