@mantou/gem 2.0.0 → 2.1.1
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 +1172 -1188
- package/dist/gem.umd.js +25 -25
- package/elements/base/gesture.d.ts +1 -2
- package/elements/base/gesture.d.ts.map +1 -1
- package/elements/base/gesture.js +21 -28
- package/elements/base/gesture.js.map +1 -1
- package/elements/base/link.d.ts +1 -2
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +17 -10
- package/elements/base/link.js.map +1 -1
- package/elements/base/modal-factory.d.ts +6 -4
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/modal-factory.js +3 -3
- package/elements/base/modal-factory.js.map +1 -1
- package/elements/base/route.d.ts +8 -2
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +4 -4
- package/elements/base/route.js.map +1 -1
- package/elements/base/title.d.ts +0 -1
- package/elements/base/title.d.ts.map +1 -1
- package/elements/base/title.js +15 -5
- package/elements/base/title.js.map +1 -1
- package/elements/base/unsafe.d.ts +1 -1
- package/elements/base/unsafe.d.ts.map +1 -1
- package/elements/base/unsafe.js +10 -4
- package/elements/base/unsafe.js.map +1 -1
- package/elements/base/use.d.ts +0 -1
- package/elements/base/use.d.ts.map +1 -1
- package/elements/base/use.js +11 -2
- package/elements/base/use.js.map +1 -1
- package/helper/i18n.d.ts.map +1 -1
- package/helper/i18n.js +11 -11
- package/helper/i18n.js.map +1 -1
- package/helper/theme.d.ts +8 -7
- package/helper/theme.d.ts.map +1 -1
- package/helper/theme.js +21 -21
- package/helper/theme.js.map +1 -1
- package/lib/decorators.d.ts +1 -1
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +3 -3
- package/lib/decorators.js.map +1 -1
- package/lib/element.d.ts +5 -4
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js +14 -16
- package/lib/element.js.map +1 -1
- package/lib/history.d.ts +20 -8
- package/lib/history.d.ts.map +1 -1
- package/lib/history.js +12 -12
- package/lib/history.js.map +1 -1
- package/lib/store.d.ts +5 -10
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +7 -22
- package/lib/store.js.map +1 -1
- package/lib/utils.d.ts +1 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +20 -14
- 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 +1 -2
- package/lib/version.js.map +1 -1
- package/package.json +3 -4
- package/src/elements/base/gesture.ts +23 -25
- package/src/elements/base/link.ts +9 -9
- package/src/elements/base/modal-factory.ts +3 -3
- package/src/elements/base/route.ts +4 -4
- package/src/elements/base/title.ts +5 -4
- package/src/elements/base/unsafe.ts +6 -3
- package/src/elements/base/use.ts +4 -3
- package/src/helper/i18n.ts +11 -8
- package/src/helper/theme.ts +27 -26
- package/src/lib/decorators.ts +4 -4
- package/src/lib/element.ts +17 -19
- package/src/lib/history.ts +12 -12
- package/src/lib/store.ts +11 -31
- package/src/lib/utils.ts +22 -14
- package/src/lib/version.ts +1 -2
- package/src/test/gem-element/advance.test.ts +2 -2
- package/src/test/gem-element/basic.test.ts +3 -3
- package/src/test/gem-element/decorators.test.ts +4 -4
- package/src/test/store.test.ts +7 -7
- package/src/test/utils.test.ts +1 -1
- package/LICENSE +0 -21
- package/helper/react-shim.d.ts +0 -15
- package/helper/react-shim.d.ts.map +0 -1
- package/helper/react-shim.js +0 -56
- package/helper/react-shim.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createCSSSheet, GemElement } from '../../lib/element';
|
|
2
2
|
import type { Emitter } from '../../lib/decorators';
|
|
3
|
-
import { attribute, emitter, mounted
|
|
3
|
+
import { adoptedStyle, attribute, emitter, mounted } from '../../lib/decorators';
|
|
4
|
+
import { css } from '../../lib/utils';
|
|
4
5
|
|
|
5
6
|
export type PanEventDetail = {
|
|
6
7
|
// movement
|
|
@@ -44,6 +45,14 @@ function angleAB(
|
|
|
44
45
|
);
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
const style = createCSSSheet(css`
|
|
49
|
+
:scope:where(:not([hidden])) {
|
|
50
|
+
display: block;
|
|
51
|
+
user-select: none;
|
|
52
|
+
touch-action: attr(touch-action, none);
|
|
53
|
+
}
|
|
54
|
+
`);
|
|
55
|
+
|
|
47
56
|
/**
|
|
48
57
|
* 块级元素,如果要设置成 `contents`,则内容块要设置 `touch-action: none`
|
|
49
58
|
* https://javascript.info/pointer-events#event-pointercancel
|
|
@@ -59,7 +68,7 @@ function angleAB(
|
|
|
59
68
|
*
|
|
60
69
|
* @attr touch-action
|
|
61
70
|
*/
|
|
62
|
-
@
|
|
71
|
+
@adoptedStyle(style)
|
|
63
72
|
export class GemGestureElement extends GemElement {
|
|
64
73
|
@emitter pan: Emitter<PanEventDetail>;
|
|
65
74
|
@emitter pinch: Emitter<PinchEventDetail>;
|
|
@@ -75,11 +84,8 @@ export class GemGestureElement extends GemElement {
|
|
|
75
84
|
|
|
76
85
|
#startEventMap: Map<number, PointerEvent> = new Map();
|
|
77
86
|
|
|
78
|
-
/** 指针移动事件记录 */
|
|
79
|
-
movesMap: Map<number, PanEventDetail[]> = new Map();
|
|
80
|
-
|
|
81
87
|
#getMoves = (pointerId: number) => {
|
|
82
|
-
return this.
|
|
88
|
+
return this._movesMap.get(pointerId) || [];
|
|
83
89
|
};
|
|
84
90
|
|
|
85
91
|
#getStartEvent = (pointerId: number) => {
|
|
@@ -87,7 +93,7 @@ export class GemGestureElement extends GemElement {
|
|
|
87
93
|
};
|
|
88
94
|
|
|
89
95
|
#getOtherLastMove = (pointerId: number) => {
|
|
90
|
-
for (const id of this.
|
|
96
|
+
for (const id of this._movesMap.keys()) {
|
|
91
97
|
if (id !== pointerId) {
|
|
92
98
|
const moves = this.#getMoves(id);
|
|
93
99
|
return moves[moves.length - 1] || this.#getStartEvent(id);
|
|
@@ -130,7 +136,7 @@ export class GemGestureElement extends GemElement {
|
|
|
130
136
|
#onStart = (evt: PointerEvent) => {
|
|
131
137
|
evt.stopPropagation();
|
|
132
138
|
this.setPointerCapture(evt.pointerId);
|
|
133
|
-
this.
|
|
139
|
+
this._movesMap.set(evt.pointerId, []);
|
|
134
140
|
this.#startEventMap.set(evt.pointerId, evt);
|
|
135
141
|
if (evt.isPrimary) {
|
|
136
142
|
this.#pressed = false;
|
|
@@ -163,7 +169,7 @@ export class GemGestureElement extends GemElement {
|
|
|
163
169
|
moves.push(move);
|
|
164
170
|
this.pan(move);
|
|
165
171
|
|
|
166
|
-
if (this.
|
|
172
|
+
if (this._movesMap.size !== 1) {
|
|
167
173
|
const secondaryPoint = this.#getOtherLastMove(pointerId) as PanEventDetail;
|
|
168
174
|
const moveLen = Math.sqrt(movementX ** 2 + movementY ** 2);
|
|
169
175
|
const distanceLen = Math.sqrt(
|
|
@@ -205,7 +211,7 @@ export class GemGestureElement extends GemElement {
|
|
|
205
211
|
const { pointerId } = evt;
|
|
206
212
|
window.clearTimeout(this.#pressTimer);
|
|
207
213
|
|
|
208
|
-
if (this.
|
|
214
|
+
if (this._movesMap.size === 1) {
|
|
209
215
|
this.end(null);
|
|
210
216
|
}
|
|
211
217
|
|
|
@@ -239,10 +245,12 @@ export class GemGestureElement extends GemElement {
|
|
|
239
245
|
}
|
|
240
246
|
}
|
|
241
247
|
|
|
242
|
-
this.
|
|
248
|
+
this._movesMap.delete(pointerId);
|
|
243
249
|
this.#startEventMap.delete(pointerId);
|
|
244
250
|
};
|
|
245
251
|
|
|
252
|
+
#preventDefault = (evt: Event) => evt.preventDefault();
|
|
253
|
+
|
|
246
254
|
@mounted()
|
|
247
255
|
#init = () => {
|
|
248
256
|
this.addEventListener('pointerdown', this.#onStart);
|
|
@@ -252,19 +260,9 @@ export class GemGestureElement extends GemElement {
|
|
|
252
260
|
// 为什么空白区域会自动触发 `pointercancel`?
|
|
253
261
|
this.addEventListener('pointercancel', this.#onEnd);
|
|
254
262
|
|
|
255
|
-
this.addEventListener('dragstart',
|
|
263
|
+
this.addEventListener('dragstart', this.#preventDefault);
|
|
256
264
|
};
|
|
257
265
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
<style>
|
|
261
|
-
:host(:where(:not([hidden]))) {
|
|
262
|
-
display: block;
|
|
263
|
-
user-select: none;
|
|
264
|
-
touch-action: ${this.touchAction || 'none'};
|
|
265
|
-
}
|
|
266
|
-
</style>
|
|
267
|
-
<slot></slot>
|
|
268
|
-
`;
|
|
269
|
-
}
|
|
266
|
+
/** 指针移动事件记录 */
|
|
267
|
+
_movesMap: Map<number, PanEventDetail[]> = new Map();
|
|
270
268
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
mounted,
|
|
12
12
|
effect,
|
|
13
13
|
boolattribute,
|
|
14
|
+
template,
|
|
14
15
|
} from '../../lib/decorators';
|
|
15
16
|
import { history, basePathStore } from '../../lib/history';
|
|
16
17
|
import { absoluteLocation, css } from '../../lib/utils';
|
|
@@ -85,7 +86,7 @@ export class GemLinkElement extends GemElement {
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
#onClick = async () => {
|
|
88
|
-
const locationString = this.
|
|
89
|
+
const locationString = this._getLocationString();
|
|
89
90
|
|
|
90
91
|
if (!locationString) return;
|
|
91
92
|
|
|
@@ -133,12 +134,10 @@ export class GemLinkElement extends GemElement {
|
|
|
133
134
|
}
|
|
134
135
|
};
|
|
135
136
|
|
|
136
|
-
#preventDefault = (e: MouseEvent) =>
|
|
137
|
-
e.preventDefault();
|
|
138
|
-
};
|
|
137
|
+
#preventDefault = (e: MouseEvent) => e.preventDefault();
|
|
139
138
|
|
|
140
139
|
#getHint = () => {
|
|
141
|
-
const locationString = this.
|
|
140
|
+
const locationString = this._getLocationString();
|
|
142
141
|
return isExternal(locationString)
|
|
143
142
|
? locationString
|
|
144
143
|
: new URL(history.basePath + locationString, location.origin).toString();
|
|
@@ -157,7 +156,8 @@ export class GemLinkElement extends GemElement {
|
|
|
157
156
|
this.addEventListener('pointerenter', this.#preload);
|
|
158
157
|
}
|
|
159
158
|
|
|
160
|
-
|
|
159
|
+
@template()
|
|
160
|
+
#content = () => {
|
|
161
161
|
return html`
|
|
162
162
|
<a
|
|
163
163
|
part=${this.link}
|
|
@@ -168,12 +168,12 @@ export class GemLinkElement extends GemElement {
|
|
|
168
168
|
<slot></slot>
|
|
169
169
|
</a>
|
|
170
170
|
`;
|
|
171
|
-
}
|
|
171
|
+
};
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
174
|
* 如果该元素是外部链接返回 URL,否则返回路径(不包含 basePath)
|
|
175
175
|
*/
|
|
176
|
-
|
|
176
|
+
_getLocationString() {
|
|
177
177
|
if (this.route) {
|
|
178
178
|
const queryProp = this.#routeOptions?.query || '';
|
|
179
179
|
const hashProp = this.#routeOptions?.hash || '';
|
|
@@ -208,6 +208,6 @@ export class GemActiveLinkElement extends GemLinkElement {
|
|
|
208
208
|
#updateState = () => {
|
|
209
209
|
const { path, query, hash } = history.getParams();
|
|
210
210
|
const isMatchPattern = this.pattern && matchPath(this.pattern, path);
|
|
211
|
-
this.active = !!isMatchPattern || path + query + hash === this.
|
|
211
|
+
this.active = !!isMatchPattern || path + query + hash === this._getLocationString();
|
|
212
212
|
};
|
|
213
213
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GemElement } from '../../lib/element';
|
|
2
|
-
import { connect, createStore
|
|
2
|
+
import { connect, createStore } from '../../lib/store';
|
|
3
3
|
import { history } from '../../lib/history';
|
|
4
4
|
import { aria, mounted, shadow } from '../../lib/decorators';
|
|
5
5
|
|
|
@@ -46,7 +46,7 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
|
|
|
46
46
|
this.inertStore = ([...document.body.children] as HTMLElement[]).filter((e) => !e.inert);
|
|
47
47
|
this.inertStore.forEach((e) => (e.inert = true));
|
|
48
48
|
document.body.append(instance);
|
|
49
|
-
const changeStore = () =>
|
|
49
|
+
const changeStore = () => this.store({ [open]: true, ...opts });
|
|
50
50
|
setTimeout(() => {
|
|
51
51
|
changeStore();
|
|
52
52
|
history.push({
|
|
@@ -75,7 +75,7 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
|
|
|
75
75
|
static closeHandle() {
|
|
76
76
|
this.inertStore.forEach((e) => (e.inert = false));
|
|
77
77
|
this.instance?.remove();
|
|
78
|
-
|
|
78
|
+
this.store({ [open]: false, ...options });
|
|
79
79
|
return final;
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -3,7 +3,7 @@ import { createState, GemElement, html } from '../../lib/element';
|
|
|
3
3
|
import type { Emitter } from '../../lib/decorators';
|
|
4
4
|
import { property, emitter, boolattribute, shadow, effect, template, light } from '../../lib/decorators';
|
|
5
5
|
import type { Store } from '../../lib/store';
|
|
6
|
-
import { createStore,
|
|
6
|
+
import { createStore, connect } from '../../lib/store';
|
|
7
7
|
import type { UpdateHistoryParams } from '../../lib/history';
|
|
8
8
|
import { titleStore, history } from '../../lib/history';
|
|
9
9
|
import type { QueryString } from '../../lib/utils';
|
|
@@ -205,7 +205,7 @@ export class GemLightRouteElement extends GemElement {
|
|
|
205
205
|
#updateLocationStore = () => {
|
|
206
206
|
if (!this.locationStore) return;
|
|
207
207
|
const { path, query, hash, data } = history.getParams();
|
|
208
|
-
|
|
208
|
+
this.locationStore({
|
|
209
209
|
path,
|
|
210
210
|
params: this.currentParams,
|
|
211
211
|
query,
|
|
@@ -256,7 +256,7 @@ export class GemLightRouteElement extends GemElement {
|
|
|
256
256
|
|
|
257
257
|
// history or i18n
|
|
258
258
|
@effect((i) => [i.scrollContainer])
|
|
259
|
-
#connectTriggerStore = () => connect(this.trigger.store, () => this.#state(
|
|
259
|
+
#connectTriggerStore = () => this.trigger.store && connect(this.trigger.store, () => this.#state());
|
|
260
260
|
|
|
261
261
|
#updateContentDep = () => {
|
|
262
262
|
const { path, query, hash } = this.trigger.getParams();
|
|
@@ -310,7 +310,7 @@ export class GemLightRouteElement extends GemElement {
|
|
|
310
310
|
if (this.trigger === history) {
|
|
311
311
|
// 嵌套路由执行顺序也是从父到子
|
|
312
312
|
if (location.href !== titleStore.url || route?.title) {
|
|
313
|
-
|
|
313
|
+
titleStore({ url: location.href, title: route?.title });
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
const contentOrLoader = content || getContent?.(params, this.shadowRoot || this);
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { GemElement, html } from '../../lib/element';
|
|
16
|
-
import { attribute, boolattribute, effect, mounted, shadow } from '../../lib/decorators';
|
|
17
|
-
import {
|
|
16
|
+
import { attribute, boolattribute, effect, mounted, shadow, template } from '../../lib/decorators';
|
|
17
|
+
import { connect } from '../../lib/store';
|
|
18
18
|
import { titleStore } from '../../lib/history';
|
|
19
19
|
|
|
20
20
|
// 避免重定向时的中间状态标题
|
|
@@ -50,7 +50,7 @@ export class GemTitleElement extends GemElement {
|
|
|
50
50
|
static title = document.title;
|
|
51
51
|
|
|
52
52
|
static setTitle(title: string) {
|
|
53
|
-
|
|
53
|
+
titleStore({ title });
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
@effect((i) => [i.inert])
|
|
@@ -63,7 +63,8 @@ export class GemTitleElement extends GemElement {
|
|
|
63
63
|
this.#ob.observe(this, { characterData: true, childList: true, subtree: true });
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
@template()
|
|
67
|
+
#content = () => {
|
|
67
68
|
// 多个 <gem-title> 时,最终 document.title 按渲染顺序决定
|
|
68
69
|
setDocumentTitle(this.textContent?.trim(), this.prefix, this.suffix);
|
|
69
70
|
return html`${GemTitleElement.title}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GemElement } from '../../lib/element';
|
|
2
|
-
import { attribute, shadow } from '../../lib/decorators';
|
|
2
|
+
import { attribute, shadow, template } from '../../lib/decorators';
|
|
3
3
|
import { raw } from '../../lib/utils';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -12,7 +12,8 @@ export class GemUnsafeElement extends GemElement {
|
|
|
12
12
|
@attribute content: string;
|
|
13
13
|
@attribute contentcss: string;
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
@template()
|
|
16
|
+
#content = () => {
|
|
16
17
|
this.shadowRoot!.innerHTML = raw`
|
|
17
18
|
<style>
|
|
18
19
|
:host(:where(:not([hidden]))) {
|
|
@@ -22,6 +23,8 @@ export class GemUnsafeElement extends GemElement {
|
|
|
22
23
|
</style>
|
|
23
24
|
${this.content}
|
|
24
25
|
`;
|
|
26
|
+
|
|
27
|
+
// 不要更新内容
|
|
25
28
|
return undefined;
|
|
26
|
-
}
|
|
29
|
+
};
|
|
27
30
|
}
|
package/src/elements/base/use.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createCSSSheet, GemElement, html } from '../../lib/element';
|
|
2
|
-
import { adoptedStyle, attribute, property, shadow, state } from '../../lib/decorators';
|
|
2
|
+
import { adoptedStyle, attribute, property, shadow, state, template } from '../../lib/decorators';
|
|
3
3
|
import { css } from '../../lib/utils';
|
|
4
4
|
|
|
5
5
|
const styles = createCSSSheet(css`
|
|
@@ -50,10 +50,11 @@ export class GemUseElement extends GemElement {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
@template()
|
|
54
|
+
#content = () => {
|
|
54
55
|
return html`
|
|
55
56
|
${this.#getContent()}
|
|
56
57
|
<slot></slot>
|
|
57
58
|
`;
|
|
58
|
-
}
|
|
59
|
+
};
|
|
59
60
|
}
|
package/src/helper/i18n.ts
CHANGED
|
@@ -113,6 +113,7 @@ export class I18n<T = Record<string, Msg>> implements RouteTrigger {
|
|
|
113
113
|
this.onChange?.(lang);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
#inConstructor = true;
|
|
116
117
|
constructor(options: I18nOptions<T>) {
|
|
117
118
|
if (!options.resources[options.fallbackLanguage]) {
|
|
118
119
|
throw new GemError('i18n: fallbackLanguage invalid');
|
|
@@ -139,6 +140,7 @@ export class I18n<T = Record<string, Msg>> implements RouteTrigger {
|
|
|
139
140
|
} else {
|
|
140
141
|
this.resetLanguage();
|
|
141
142
|
}
|
|
143
|
+
this.#inConstructor = false;
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
get(s: keyof T): string;
|
|
@@ -207,14 +209,15 @@ export class I18n<T = Record<string, Msg>> implements RouteTrigger {
|
|
|
207
209
|
}
|
|
208
210
|
this.resources[lang] = pack;
|
|
209
211
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
212
|
+
if (!this.#inConstructor) {
|
|
213
|
+
// 更新所有元素,可能会更新许多不必要的元素
|
|
214
|
+
const temp: Element[] = [document.documentElement];
|
|
215
|
+
while (!!temp.length) {
|
|
216
|
+
const element = temp.pop()!;
|
|
217
|
+
(element as any)[UpdateToken]?.();
|
|
218
|
+
// 和 gem 更新机制一样使用深度优先
|
|
219
|
+
temp.push(...[...element.children, ...(element.shadowRoot?.children || [])].reverse());
|
|
220
|
+
}
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
if (lang !== this.currentLanguage) {
|
package/src/helper/theme.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { Store } from '../lib/store';
|
|
2
|
-
import { connect,
|
|
3
|
-
import { camelToKebabCase, randomStr } from '../lib/utils';
|
|
2
|
+
import { connect, createStore } from '../lib/store';
|
|
3
|
+
import { camelToKebabCase, createUpdater, randomStr } from '../lib/utils';
|
|
4
4
|
import type { GemElement } from '../lib/element';
|
|
5
5
|
import { SheetToken, createCSSSheet } from '../lib/element';
|
|
6
6
|
|
|
7
|
-
export type Theme<T> = ReturnType<typeof
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
export type Theme<T> = ReturnType<typeof createUpdater<T>> &
|
|
8
|
+
ReturnType<typeof createCSSSheet> & {
|
|
9
|
+
[K in keyof T as K extends `${string}Color`
|
|
10
|
+
? `${string & K}${'' | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900}`
|
|
11
|
+
: K]: T[K];
|
|
12
|
+
};
|
|
12
13
|
|
|
13
14
|
const themeStoreMap = new WeakMap();
|
|
14
15
|
const themePropsMap = new WeakMap();
|
|
@@ -23,12 +24,12 @@ export function getThemeProps<T>(theme: Theme<T>) {
|
|
|
23
24
|
return themePropsMap.get(theme) as Theme<T>;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function
|
|
27
|
+
function createThemeFromProps<T extends Record<string, unknown>>(themeObj: T, props: Record<string, string> = {}) {
|
|
27
28
|
const salt = randomStr();
|
|
28
29
|
const styleSheet = createCSSSheet({})[SheetToken];
|
|
29
|
-
const
|
|
30
|
+
const store = createStore<T>(themeObj);
|
|
30
31
|
const theme: any = new Proxy(
|
|
31
|
-
{ [SheetToken]: styleSheet },
|
|
32
|
+
createUpdater({ [SheetToken]: styleSheet }, (payload: Partial<T>) => store(payload)),
|
|
32
33
|
{
|
|
33
34
|
get(target: any, key: string) {
|
|
34
35
|
if (!target[key]) {
|
|
@@ -58,41 +59,41 @@ function useThemeFromProps<T extends Record<string, unknown>>(themeObj: T, props
|
|
|
58
59
|
updateContent();
|
|
59
60
|
styleSheet.updateStyle();
|
|
60
61
|
});
|
|
61
|
-
return
|
|
62
|
+
return theme as Theme<T>;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* 用于 `@adoptedStyle(theme)`,类似 `createCSSSheet`
|
|
66
67
|
*/
|
|
67
|
-
export function
|
|
68
|
-
return
|
|
68
|
+
export function createScopedTheme<T extends Record<string, unknown>>(themeObj: T) {
|
|
69
|
+
return createThemeFromProps(themeObj);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
export function
|
|
73
|
-
const result =
|
|
74
|
-
document.adoptedStyleSheets.push(result[
|
|
72
|
+
/** 全局主题 */
|
|
73
|
+
export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
|
|
74
|
+
const result = createScopedTheme(themeObj);
|
|
75
|
+
document.adoptedStyleSheets.push(result[SheetToken].getStyle());
|
|
75
76
|
return result;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
export function
|
|
80
|
-
return
|
|
79
|
+
/** 用来覆盖全局主题 */
|
|
80
|
+
export function createOverrideTheme<T extends Record<string, unknown>>(theme: Theme<T>, themeObj: Partial<T>) {
|
|
81
|
+
return createThemeFromProps(themeObj, getThemeProps(theme));
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
export function
|
|
84
|
-
const
|
|
84
|
+
export function createDecoratorTheme<T extends Record<string, unknown>>(themeObj: T) {
|
|
85
|
+
const themeAsKeys = createThemeFromProps<T>(themeObj);
|
|
85
86
|
const props = getThemeProps(themeAsKeys);
|
|
86
|
-
const
|
|
87
|
+
const update =
|
|
87
88
|
<E extends GemElement, V extends () => T, K = any[] | undefined>(
|
|
88
89
|
getDep?: K extends readonly any[] ? (instance: E) => K : undefined,
|
|
89
90
|
) =>
|
|
90
91
|
(_: any, { addInitializer, access }: ClassFieldDecoratorContext<E, V> | ClassMethodDecoratorContext<E, V>) => {
|
|
91
92
|
addInitializer(function (this: E) {
|
|
92
|
-
const
|
|
93
|
+
const theme = createThemeFromProps({ ...themeObj }, props);
|
|
93
94
|
this.internals.sheets.push(theme[SheetToken].getStyle(this));
|
|
94
|
-
this.memo(() =>
|
|
95
|
+
this.memo(() => theme(access.get(this).apply(this)), getDep && (() => getDep(this) as any));
|
|
95
96
|
});
|
|
96
97
|
};
|
|
97
|
-
return
|
|
98
|
+
return createUpdater(themeAsKeys, update) as ReturnType<typeof createUpdater<typeof themeAsKeys, typeof update>>;
|
|
98
99
|
}
|
package/src/lib/decorators.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { createCSSSheet, Metadata, TemplateResult } from './element';
|
|
2
|
-
import { GemElement, UpdateToken,
|
|
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';
|
|
@@ -273,7 +273,7 @@ export function template<T extends GemElement, V extends () => TemplateResult |
|
|
|
273
273
|
{ addInitializer, access }: ClassFieldDecoratorContext<T, V> | ClassMethodDecoratorContext<T, V>,
|
|
274
274
|
) {
|
|
275
275
|
addInitializer(function (this: T) {
|
|
276
|
-
|
|
276
|
+
_createTemplate(this, {
|
|
277
277
|
render: access.get(this).bind(this),
|
|
278
278
|
condition: condition && (() => condition(this) as any),
|
|
279
279
|
});
|
|
@@ -287,7 +287,7 @@ export function fallback<T extends GemElement, V extends (err: any) => TemplateR
|
|
|
287
287
|
{ addInitializer, access }: ClassFieldDecoratorContext<T, V> | ClassMethodDecoratorContext<T, V>,
|
|
288
288
|
) {
|
|
289
289
|
addInitializer(function (this: T) {
|
|
290
|
-
this.addEventListener(
|
|
290
|
+
this.addEventListener(_RenderErrorEvent, ({ detail }: CustomEvent) => {
|
|
291
291
|
render(access.get(this).apply(this, [detail]), this.internals.shadowRoot || this);
|
|
292
292
|
});
|
|
293
293
|
});
|
|
@@ -456,7 +456,7 @@ export function adoptedStyle(sheet: ReturnType<typeof createCSSSheet>) {
|
|
|
456
456
|
* class App extends GemElement {}
|
|
457
457
|
* ```
|
|
458
458
|
*/
|
|
459
|
-
export function connectStore(store: Store<
|
|
459
|
+
export function connectStore(store: Store<any>) {
|
|
460
460
|
return function (_: unknown, context: ClassDecoratorContext) {
|
|
461
461
|
pushStaticField(context, 'observedStores', store);
|
|
462
462
|
};
|
package/src/lib/element.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { GemReflectElement } from '../elements/reflect';
|
|
|
5
5
|
|
|
6
6
|
import type { Store } from './store';
|
|
7
7
|
import { connect } from './store';
|
|
8
|
-
import { LinkedList, addMicrotask, isArrayChange, addListener, randomStr } from './utils';
|
|
8
|
+
import { LinkedList, addMicrotask, isArrayChange, addListener, randomStr, createUpdater } from './utils';
|
|
9
9
|
|
|
10
10
|
export { html, svg, render, directive, TemplateResult, SVGTemplateResult } from 'lit-html';
|
|
11
11
|
|
|
@@ -29,19 +29,19 @@ declare global {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const { assign, defineProperty
|
|
32
|
+
const { assign, defineProperty } = Object;
|
|
33
33
|
|
|
34
34
|
defineProperty(Symbol, 'metadata', { value: Symbol.for('Symbol.metadata') });
|
|
35
35
|
|
|
36
36
|
const execCallback = (fun: any) => typeof fun === 'function' && fun();
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// 读取构造样式表,跨多个 gem 工作
|
|
39
39
|
export const SheetToken = Symbol.for('gem@sheetToken');
|
|
40
|
-
//
|
|
40
|
+
// 更新元素,跨多个 gem 工作
|
|
41
41
|
export const UpdateToken = Symbol.for('gem@update');
|
|
42
42
|
|
|
43
43
|
const BoundaryCSSState = 'gem-style-boundary';
|
|
44
|
-
export const
|
|
44
|
+
export const _RenderErrorEvent = 'gem@render-error';
|
|
45
45
|
|
|
46
46
|
// fix modal-factory type error
|
|
47
47
|
const updateTokenAlias = UpdateToken;
|
|
@@ -177,8 +177,9 @@ export function createCSSSheet<T extends Record<string, string>>(
|
|
|
177
177
|
Object.keys(rules).forEach((key) => {
|
|
178
178
|
const isScope = key === '$';
|
|
179
179
|
// 对于已经有 `-` 的保留原始 key,支持覆盖修改
|
|
180
|
+
// :scope 下可以写嵌套样式 &:xxx,:host() 下不行(子内容可以)
|
|
180
181
|
sheet[key] = isScope || key.includes('-') ? key : `${key}-${randomStr()}`;
|
|
181
|
-
style += `${isScope ? ':scope,:host' : `.${sheet[key]}`} {${rules[key]}}`;
|
|
182
|
+
style += `${isScope ? ':where(:scope:not([hidden])),:host(:where(:not([hidden])))' : `.${sheet[key]}`} {${rules[key]}}`;
|
|
182
183
|
});
|
|
183
184
|
}
|
|
184
185
|
styleSheet.setContent(style);
|
|
@@ -221,7 +222,7 @@ const appleCSSStyleSheet = (ele: HTMLElement, sheets: CSSStyleSheet[]) => {
|
|
|
221
222
|
};
|
|
222
223
|
|
|
223
224
|
/**必须使用在字段中,否则会读取到错误的实例 */
|
|
224
|
-
export let createState: <T>(initState: T) =>
|
|
225
|
+
export let createState: <T>(initState: T) => ReturnType<typeof createUpdater<T>>;
|
|
225
226
|
|
|
226
227
|
type GetDepFun<T> = () => T;
|
|
227
228
|
type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
|
|
@@ -260,7 +261,7 @@ function clearEffect(list: EffectItem<any>[]) {
|
|
|
260
261
|
type Render = () => TemplateResult | null | undefined;
|
|
261
262
|
type RenderItem = { render: Render; condition?: () => boolean };
|
|
262
263
|
|
|
263
|
-
export let
|
|
264
|
+
export let _createTemplate: (ele: GemElement, item: RenderItem) => void;
|
|
264
265
|
|
|
265
266
|
export type Metadata = Partial<ShadowRootInit> & {
|
|
266
267
|
/** 内容可被外部样式化 */
|
|
@@ -273,7 +274,7 @@ export type Metadata = Partial<ShadowRootInit> & {
|
|
|
273
274
|
}
|
|
274
275
|
>;
|
|
275
276
|
// 实例化时使用到,DevTools 需要读取
|
|
276
|
-
observedStores: Store<
|
|
277
|
+
observedStores: Store<any>[];
|
|
277
278
|
adoptedStyleSheets?: Sheet<unknown>[];
|
|
278
279
|
// 以下静态字段仅供外部读取,没有实际作用
|
|
279
280
|
observedProperties?: string[];
|
|
@@ -331,20 +332,16 @@ export abstract class GemElement extends HTMLElement {
|
|
|
331
332
|
static {
|
|
332
333
|
createState = <T>(initState: T) => {
|
|
333
334
|
const ele = currentConstructGemElement;
|
|
334
|
-
const state
|
|
335
|
+
const state = createUpdater(initState, (payload) => {
|
|
335
336
|
assign(state, payload);
|
|
336
337
|
// 避免无限刷新
|
|
337
338
|
if (!ele.#rendering) addMicrotask(ele.#update);
|
|
338
|
-
};
|
|
339
|
-
setPrototypeOf(state, null);
|
|
340
|
-
delete state.name;
|
|
341
|
-
delete state.length;
|
|
339
|
+
});
|
|
342
340
|
ele.#internals.stateList.push(state);
|
|
343
|
-
|
|
344
|
-
return state as T & ((this: GemElement, state: Partial<T>) => void);
|
|
341
|
+
return state;
|
|
345
342
|
};
|
|
346
343
|
createRef = () => new RefObject(currentConstructGemElement);
|
|
347
|
-
|
|
344
|
+
_createTemplate = (ele, item) => ele.#renderList.push(item);
|
|
348
345
|
}
|
|
349
346
|
|
|
350
347
|
constructor() {
|
|
@@ -388,7 +385,8 @@ export abstract class GemElement extends HTMLElement {
|
|
|
388
385
|
}
|
|
389
386
|
|
|
390
387
|
get #renderItem() {
|
|
391
|
-
|
|
388
|
+
// 从末尾开始寻找,允许被覆盖
|
|
389
|
+
return this.#renderList.findLast((e) => !e.condition || e.condition());
|
|
392
390
|
}
|
|
393
391
|
|
|
394
392
|
get internals() {
|
|
@@ -422,7 +420,7 @@ export abstract class GemElement extends HTMLElement {
|
|
|
422
420
|
if (temp === undefined) return;
|
|
423
421
|
render(temp, this.#renderRoot);
|
|
424
422
|
} catch (err) {
|
|
425
|
-
this.dispatchEvent(new CustomEvent(
|
|
423
|
+
this.dispatchEvent(new CustomEvent(_RenderErrorEvent, { bubbles: true, composed: true, detail: err }));
|
|
426
424
|
}
|
|
427
425
|
};
|
|
428
426
|
|