@mantou/gem 1.7.11 → 2.0.0-alpha.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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/gem.es.js +1962 -1689
- package/dist/gem.umd.js +52 -54
- package/elements/base/dialog.d.ts +0 -11
- package/elements/base/dialog.d.ts.map +1 -1
- package/elements/base/dialog.js +93 -66
- package/elements/base/dialog.js.map +1 -1
- package/elements/base/gesture.d.ts +1 -2
- package/elements/base/gesture.d.ts.map +1 -1
- package/elements/base/gesture.js +157 -96
- package/elements/base/gesture.js.map +1 -1
- package/elements/base/link.d.ts +5 -5
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +249 -145
- package/elements/base/link.js.map +1 -1
- package/elements/base/modal-factory.d.ts +147 -139
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/modal-factory.js +84 -25
- package/elements/base/modal-factory.js.map +1 -1
- package/elements/base/reflect.d.ts +0 -1
- package/elements/base/reflect.d.ts.map +1 -1
- package/elements/base/reflect.js +83 -38
- package/elements/base/reflect.js.map +1 -1
- package/elements/base/route.d.ts +12 -24
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +263 -164
- package/elements/base/route.js.map +1 -1
- package/elements/base/title.d.ts +3 -11
- package/elements/base/title.d.ts.map +1 -1
- package/elements/base/title.js +110 -59
- package/elements/base/title.js.map +1 -1
- package/elements/base/unsafe.d.ts +0 -1
- package/elements/base/unsafe.d.ts.map +1 -1
- package/elements/base/unsafe.js +82 -33
- package/elements/base/unsafe.js.map +1 -1
- package/elements/base/use.d.ts.map +1 -1
- package/elements/base/use.js +95 -47
- package/elements/base/use.js.map +1 -1
- package/helper/i18n.d.ts +4 -9
- package/helper/i18n.d.ts.map +1 -1
- package/helper/i18n.js +69 -65
- package/helper/i18n.js.map +1 -1
- package/helper/logger.js +10 -9
- package/helper/logger.js.map +1 -1
- package/helper/react-shim.d.ts +15 -0
- package/helper/react-shim.d.ts.map +1 -0
- package/helper/react-shim.js +56 -0
- package/helper/react-shim.js.map +1 -0
- package/helper/react-utils.d.ts +15 -0
- package/helper/react-utils.d.ts.map +1 -0
- package/helper/react-utils.js +50 -0
- package/helper/react-utils.js.map +1 -0
- package/helper/request.d.ts +17 -7
- package/helper/request.d.ts.map +1 -1
- package/helper/request.js +103 -53
- package/helper/request.js.map +1 -1
- package/helper/ssr-shim.d.ts.map +1 -1
- package/helper/ssr-shim.js +8 -1
- package/helper/ssr-shim.js.map +1 -1
- package/helper/theme.d.ts +14 -21
- package/helper/theme.d.ts.map +1 -1
- package/helper/theme.js +55 -38
- package/helper/theme.js.map +1 -1
- package/index.d.ts +4 -3
- package/index.d.ts.map +1 -1
- package/index.js +5 -3
- package/index.js.map +1 -1
- package/lib/decorators.d.ts +74 -41
- package/lib/decorators.d.ts.map +1 -1
- package/lib/decorators.js +324 -84
- package/lib/decorators.js.map +1 -1
- package/lib/directives.d.ts +4 -0
- package/lib/directives.d.ts.map +1 -0
- package/lib/directives.js +5 -0
- package/lib/directives.js.map +1 -0
- package/lib/element.d.ts +112 -110
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js +431 -470
- package/lib/element.js.map +1 -1
- package/lib/history.d.ts +4 -0
- package/lib/history.d.ts.map +1 -1
- package/lib/history.js +12 -13
- package/lib/history.js.map +1 -1
- package/lib/store.d.ts +1 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +4 -5
- package/lib/store.js.map +1 -1
- package/lib/utils.d.ts +3 -43
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +61 -246
- 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 +12 -11
- package/src/dist.ts +3 -2
- package/src/elements/base/dialog.ts +10 -20
- package/src/elements/base/gesture.ts +16 -14
- package/src/elements/base/link.ts +65 -44
- package/src/elements/base/modal-factory.ts +14 -14
- package/src/elements/base/reflect.ts +24 -32
- package/src/elements/base/route.ts +55 -68
- package/src/elements/base/title.ts +25 -42
- package/src/elements/base/unsafe.ts +12 -21
- package/src/elements/base/use.ts +17 -13
- package/src/helper/i18n.ts +20 -14
- package/src/helper/react-utils.ts +66 -0
- package/src/helper/request.ts +122 -53
- package/src/helper/ssr-shim.ts +8 -1
- package/src/helper/theme.ts +72 -45
- package/src/index.ts +6 -4
- package/src/lib/decorators.ts +394 -93
- package/src/lib/directives.ts +6 -0
- package/src/lib/element.ts +473 -499
- package/src/lib/history.ts +4 -3
- package/src/lib/store.ts +2 -1
- package/src/lib/utils.ts +19 -205
- package/src/lib/version.ts +2 -1
- package/src/test/elements/link.test.ts +1 -1
- package/src/test/elements/title.test.ts +1 -0
- package/src/test/gem-element/advance.test.ts +72 -49
- package/src/test/gem-element/basic.test.ts +37 -50
- package/src/test/gem-element/decorators.test.ts +22 -21
- package/src/test/gem-element/multi.test.ts +5 -5
- package/src/test/utils.test.ts +16 -38
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GemElement, html } from '../../lib/element';
|
|
2
|
-
import {
|
|
2
|
+
import type { Emitter } from '../../lib/decorators';
|
|
3
|
+
import { attribute, emitter, mounted, shadow } from '../../lib/decorators';
|
|
3
4
|
|
|
4
5
|
export type PanEventDetail = {
|
|
5
6
|
// movement
|
|
@@ -58,6 +59,7 @@ function angleAB(
|
|
|
58
59
|
*
|
|
59
60
|
* @attr touch-action
|
|
60
61
|
*/
|
|
62
|
+
@shadow()
|
|
61
63
|
export class GemGestureElement extends GemElement {
|
|
62
64
|
@emitter pan: Emitter<PanEventDetail>;
|
|
63
65
|
@emitter pinch: Emitter<PinchEventDetail>;
|
|
@@ -68,18 +70,6 @@ export class GemGestureElement extends GemElement {
|
|
|
68
70
|
|
|
69
71
|
@attribute touchAction: string;
|
|
70
72
|
|
|
71
|
-
constructor() {
|
|
72
|
-
super();
|
|
73
|
-
this.addEventListener('pointerdown', this.#onStart);
|
|
74
|
-
this.addEventListener('pointermove', this.#onMoveSet);
|
|
75
|
-
this.addEventListener('pointerup', this.#onEnd);
|
|
76
|
-
this.addEventListener('pointerleave', this.#onEnd); // 有时候 up 没有触发?
|
|
77
|
-
// 为什么空白区域会自动触发 `pointercancel`?
|
|
78
|
-
this.addEventListener('pointercancel', this.#onEnd);
|
|
79
|
-
|
|
80
|
-
this.addEventListener('dragstart', (evt) => evt.preventDefault());
|
|
81
|
-
}
|
|
82
|
-
|
|
83
73
|
#pressed = false; // 触发 press 之后不触发其他事件
|
|
84
74
|
#pressTimer = 0;
|
|
85
75
|
|
|
@@ -204,7 +194,7 @@ export class GemGestureElement extends GemElement {
|
|
|
204
194
|
evt.stopPropagation();
|
|
205
195
|
// https://bugs.webkit.org/show_bug.cgi?id=210454
|
|
206
196
|
if ('getCoalescedEvents' in evt) {
|
|
207
|
-
evt.getCoalescedEvents().forEach((
|
|
197
|
+
evt.getCoalescedEvents().forEach((event) => this.#onMove(event));
|
|
208
198
|
} else {
|
|
209
199
|
this.#onMove(evt);
|
|
210
200
|
}
|
|
@@ -253,6 +243,18 @@ export class GemGestureElement extends GemElement {
|
|
|
253
243
|
this.#startEventMap.delete(pointerId);
|
|
254
244
|
};
|
|
255
245
|
|
|
246
|
+
@mounted()
|
|
247
|
+
#init = () => {
|
|
248
|
+
this.addEventListener('pointerdown', this.#onStart);
|
|
249
|
+
this.addEventListener('pointermove', this.#onMoveSet);
|
|
250
|
+
this.addEventListener('pointerup', this.#onEnd);
|
|
251
|
+
this.addEventListener('pointerleave', this.#onEnd); // 有时候 up 没有触发?
|
|
252
|
+
// 为什么空白区域会自动触发 `pointercancel`?
|
|
253
|
+
this.addEventListener('pointercancel', this.#onEnd);
|
|
254
|
+
|
|
255
|
+
this.addEventListener('dragstart', (evt) => evt.preventDefault());
|
|
256
|
+
};
|
|
257
|
+
|
|
256
258
|
render() {
|
|
257
259
|
return html`
|
|
258
260
|
<style>
|
|
@@ -1,14 +1,44 @@
|
|
|
1
|
-
import { GemElement, html
|
|
2
|
-
import {
|
|
1
|
+
import { createCSSSheet, GemElement, html } from '../../lib/element';
|
|
2
|
+
import {
|
|
3
|
+
attribute,
|
|
4
|
+
property,
|
|
5
|
+
state,
|
|
6
|
+
part,
|
|
7
|
+
connectStore,
|
|
8
|
+
shadow,
|
|
9
|
+
aria,
|
|
10
|
+
adoptedStyle,
|
|
11
|
+
mounted,
|
|
12
|
+
effect,
|
|
13
|
+
boolattribute,
|
|
14
|
+
} from '../../lib/decorators';
|
|
3
15
|
import { history, basePathStore } from '../../lib/history';
|
|
4
|
-
import { absoluteLocation } from '../../lib/utils';
|
|
16
|
+
import { absoluteLocation, css } from '../../lib/utils';
|
|
17
|
+
import { ifDefined } from '../../lib/directives';
|
|
5
18
|
|
|
6
|
-
import {
|
|
19
|
+
import type { RouteItem, RouteOptions } from './route';
|
|
20
|
+
import { matchPath, createHistoryParams, createPath } from './route';
|
|
7
21
|
|
|
8
22
|
function isExternal(path: string) {
|
|
9
23
|
return !path.startsWith('/');
|
|
10
24
|
}
|
|
11
25
|
|
|
26
|
+
const styles = createCSSSheet(css`
|
|
27
|
+
:host {
|
|
28
|
+
/* link default style */
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
color: blue;
|
|
31
|
+
text-decoration: underline;
|
|
32
|
+
}
|
|
33
|
+
a {
|
|
34
|
+
all: unset;
|
|
35
|
+
display: contents;
|
|
36
|
+
}
|
|
37
|
+
a:visited {
|
|
38
|
+
color: unset;
|
|
39
|
+
}
|
|
40
|
+
`);
|
|
41
|
+
|
|
12
42
|
/**
|
|
13
43
|
* @customElement gem-link
|
|
14
44
|
* @attr href
|
|
@@ -21,17 +51,21 @@ function isExternal(path: string) {
|
|
|
21
51
|
* @attr hint
|
|
22
52
|
* @part link
|
|
23
53
|
*
|
|
24
|
-
* print
|
|
54
|
+
* Bug: print `<link>` https://github.com/mantou132/gem/issues/36
|
|
25
55
|
*/
|
|
26
56
|
@connectStore(basePathStore)
|
|
57
|
+
@adoptedStyle(styles)
|
|
58
|
+
@shadow()
|
|
59
|
+
@aria({ focusable: true })
|
|
27
60
|
export class GemLinkElement extends GemElement {
|
|
28
61
|
@attribute href: string;
|
|
29
62
|
@attribute target: string;
|
|
30
63
|
@attribute path: string;
|
|
31
64
|
@attribute query: string;
|
|
32
65
|
@attribute hash: string;
|
|
33
|
-
@attribute docTitle: string;
|
|
34
66
|
@attribute hint: 'on' | 'off';
|
|
67
|
+
/** Preload route content when point over */
|
|
68
|
+
@boolattribute preload: boolean;
|
|
35
69
|
|
|
36
70
|
// 动态路由,根据 route.pattern 和 options.params 计算出 path
|
|
37
71
|
@property route?: RouteItem;
|
|
@@ -50,11 +84,6 @@ export class GemLinkElement extends GemElement {
|
|
|
50
84
|
return this.hint || 'on';
|
|
51
85
|
}
|
|
52
86
|
|
|
53
|
-
constructor() {
|
|
54
|
-
super({ focusable: true });
|
|
55
|
-
this.addEventListener('click', this.#onClick);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
87
|
#onClick = async () => {
|
|
59
88
|
const locationString = this.getLocationString();
|
|
60
89
|
|
|
@@ -87,24 +116,19 @@ export class GemLinkElement extends GemElement {
|
|
|
87
116
|
await this.prepare?.();
|
|
88
117
|
|
|
89
118
|
if (this.route) {
|
|
90
|
-
history.pushIgnoreCloseHandle(
|
|
91
|
-
...createHistoryParams(this.route, this.#routeOptions),
|
|
92
|
-
title: this.route.title || this.docTitle,
|
|
93
|
-
});
|
|
119
|
+
history.pushIgnoreCloseHandle(createHistoryParams(this.route, this.#routeOptions));
|
|
94
120
|
} else if (this.href) {
|
|
95
|
-
const
|
|
121
|
+
const url = new URL(locationString, location.origin);
|
|
96
122
|
history.pushIgnoreCloseHandle({
|
|
97
|
-
path: pathname,
|
|
98
|
-
query: search,
|
|
99
|
-
hash,
|
|
100
|
-
title: this.docTitle,
|
|
123
|
+
path: url.pathname,
|
|
124
|
+
query: url.search,
|
|
125
|
+
hash: url.hash,
|
|
101
126
|
});
|
|
102
127
|
} else {
|
|
103
128
|
history.pushIgnoreCloseHandle({
|
|
104
129
|
path: this.path,
|
|
105
130
|
query: this.query,
|
|
106
131
|
hash: this.hash,
|
|
107
|
-
title: this.docTitle,
|
|
108
132
|
});
|
|
109
133
|
}
|
|
110
134
|
};
|
|
@@ -120,27 +144,26 @@ export class GemLinkElement extends GemElement {
|
|
|
120
144
|
: new URL(history.basePath + locationString, location.origin).toString();
|
|
121
145
|
};
|
|
122
146
|
|
|
147
|
+
#preload = () => {
|
|
148
|
+
// 也许是 `getter`
|
|
149
|
+
this.route?.content;
|
|
150
|
+
// 只触发 `getContent` 调用,参数不使用
|
|
151
|
+
this.route?.getContent?.({}, this.shadowRoot!);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
@mounted()
|
|
155
|
+
#init() {
|
|
156
|
+
this.addEventListener('click', this.#onClick);
|
|
157
|
+
this.addEventListener('pointerenter', this.#preload);
|
|
158
|
+
}
|
|
159
|
+
|
|
123
160
|
render() {
|
|
124
161
|
return html`
|
|
125
|
-
<style>
|
|
126
|
-
:host {
|
|
127
|
-
/* link default style */
|
|
128
|
-
cursor: pointer;
|
|
129
|
-
color: blue;
|
|
130
|
-
text-decoration: underline;
|
|
131
|
-
}
|
|
132
|
-
a {
|
|
133
|
-
all: unset;
|
|
134
|
-
display: contents;
|
|
135
|
-
}
|
|
136
|
-
a:visited {
|
|
137
|
-
color: unset;
|
|
138
|
-
}
|
|
139
|
-
</style>
|
|
140
162
|
<a
|
|
141
163
|
part=${this.link}
|
|
142
164
|
@click=${this.#preventDefault}
|
|
143
165
|
href=${ifDefined(this.#hint === 'off' ? undefined : this.#getHint())}
|
|
166
|
+
tabindex="-1"
|
|
144
167
|
>
|
|
145
168
|
<slot></slot>
|
|
146
169
|
</a>
|
|
@@ -181,12 +204,10 @@ export class GemActiveLinkElement extends GemLinkElement {
|
|
|
181
204
|
@attribute pattern: string; // 使用匹配模式设定 active
|
|
182
205
|
@state active: boolean;
|
|
183
206
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
});
|
|
191
|
-
}
|
|
207
|
+
@effect()
|
|
208
|
+
#updateState = () => {
|
|
209
|
+
const { path, query, hash } = history.getParams();
|
|
210
|
+
const isMatchPattern = this.pattern && matchPath(this.pattern, path);
|
|
211
|
+
this.active = !!isMatchPattern || path + query + hash === this.getLocationString();
|
|
212
|
+
};
|
|
192
213
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GemElement } from '../../lib/element';
|
|
2
|
-
import { createStore, updateStore } from '../../lib/store';
|
|
2
|
+
import { connect, createStore, updateStore } from '../../lib/store';
|
|
3
3
|
import { history } from '../../lib/history';
|
|
4
|
+
import { aria, mounted, shadow } from '../../lib/decorators';
|
|
4
5
|
|
|
5
6
|
const open = Symbol('open mark');
|
|
6
7
|
|
|
@@ -15,7 +16,9 @@ const open = Symbol('open mark');
|
|
|
15
16
|
export function createModalClass<T extends Record<string, unknown>>(options: T) {
|
|
16
17
|
const final = Symbol();
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
@shadow()
|
|
20
|
+
@aria({ role: 'alertdialog', ariaModal: 'true' })
|
|
21
|
+
class ModalElement extends GemElement {
|
|
19
22
|
static inertStore: HTMLElement[] = [];
|
|
20
23
|
static instance: GemElement | null = null;
|
|
21
24
|
/**
|
|
@@ -32,11 +35,6 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
|
|
|
32
35
|
return this.store[open];
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
/**@final */
|
|
36
|
-
static get observedStores() {
|
|
37
|
-
return [history.store, this.store];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
38
|
/**
|
|
41
39
|
* @final
|
|
42
40
|
* 自带 100ms 延迟,以允许在其他 Dialog 的 `shouldClose` 中调用此方法;
|
|
@@ -88,12 +86,14 @@ export function createModalClass<T extends Record<string, unknown>>(options: T)
|
|
|
88
86
|
return true;
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
constructor() {
|
|
92
|
-
super();
|
|
93
|
-
this.internals.role = 'alertdialog';
|
|
94
|
-
this.internals.ariaModal = 'true';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
89
|
label = '';
|
|
98
|
-
|
|
90
|
+
|
|
91
|
+
@mounted()
|
|
92
|
+
#connectHistory = () => connect(history.store, this.update);
|
|
93
|
+
|
|
94
|
+
@mounted()
|
|
95
|
+
#connectStore = () => connect(ModalElement.store, this.update);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return ModalElement;
|
|
99
99
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 比如 duoyun-ui 文档站加载示例元素时把 GemReflectElement 渲染的元素清除
|
|
8
8
|
*/
|
|
9
9
|
import { GemElement } from '../../lib/element';
|
|
10
|
-
import { attribute, property } from '../../lib/decorators';
|
|
10
|
+
import { attribute, effect, property, willMount } from '../../lib/decorators';
|
|
11
11
|
|
|
12
12
|
const START = 'gem-reflect-start';
|
|
13
13
|
const END = 'gem-reflect-end';
|
|
@@ -37,37 +37,29 @@ export class GemReflectElement extends GemElement {
|
|
|
37
37
|
return this.method || 'append';
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
@willMount()
|
|
41
|
+
#init = () => {
|
|
42
|
+
this.hidden = true;
|
|
43
|
+
this.dataset.gemReflect = '';
|
|
44
|
+
};
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.dataset.gemReflect = '';
|
|
47
|
-
},
|
|
48
|
-
() => [],
|
|
49
|
-
);
|
|
46
|
+
#startNode = createReflectFragmentTag(START);
|
|
47
|
+
#endNode = createReflectFragmentTag(END);
|
|
48
|
+
#childNodes: undefined | ChildNode[];
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
childNodes = getReflectFragment(startNode);
|
|
67
|
-
childNodes.forEach((node) => node.remove());
|
|
68
|
-
};
|
|
69
|
-
},
|
|
70
|
-
() => [this.target],
|
|
71
|
-
);
|
|
72
|
-
}
|
|
50
|
+
@effect((i) => [i.target])
|
|
51
|
+
#changeTarget = () => {
|
|
52
|
+
(this.#childNodes || [this.#startNode, ...this.childNodes, this.#endNode]).forEach((node, index, arr) => {
|
|
53
|
+
const prev = arr[index - 1];
|
|
54
|
+
if (prev) {
|
|
55
|
+
prev.after(node);
|
|
56
|
+
} else {
|
|
57
|
+
this.target[this.#method](node);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
this.#childNodes = getReflectFragment(this.#startNode);
|
|
62
|
+
this.#childNodes.forEach((node) => node.remove());
|
|
63
|
+
};
|
|
64
|
+
};
|
|
73
65
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import type { TemplateResult } from '../../lib/element';
|
|
2
|
+
import { BoundaryCSSState, createState, GemElement, html } from '../../lib/element';
|
|
3
|
+
import type { Emitter } from '../../lib/decorators';
|
|
4
|
+
import { property, emitter, boolattribute, shadow, effect, template, willMount } from '../../lib/decorators';
|
|
5
|
+
import type { Store } from '../../lib/store';
|
|
6
|
+
import { createStore, updateStore, connect } from '../../lib/store';
|
|
7
|
+
import type { UpdateHistoryParams } from '../../lib/history';
|
|
8
|
+
import { titleStore, history } from '../../lib/history';
|
|
9
|
+
import type { QueryString } from '../../lib/utils';
|
|
10
|
+
import { addListener } from '../../lib/utils';
|
|
6
11
|
|
|
7
12
|
const UNNAMED_PARAM_PREFIX = '_';
|
|
8
13
|
|
|
@@ -113,11 +118,6 @@ export function createHistoryParams(route: RouteItem, options?: RouteOptions): U
|
|
|
113
118
|
};
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
interface ConstructorOptions {
|
|
117
|
-
isLight?: boolean;
|
|
118
|
-
routes?: RouteItem[] | RoutesObject;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
121
|
const INIT_CONTENT = html``;
|
|
122
122
|
|
|
123
123
|
type State = {
|
|
@@ -125,7 +125,7 @@ type State = {
|
|
|
125
125
|
};
|
|
126
126
|
|
|
127
127
|
export type RouteTrigger = {
|
|
128
|
-
store
|
|
128
|
+
store?: Store<any>;
|
|
129
129
|
replace: (arg: UpdateHistoryParams) => void;
|
|
130
130
|
getParams: () => { path: string; query?: string | QueryString; hash?: string };
|
|
131
131
|
};
|
|
@@ -133,13 +133,13 @@ export type RouteTrigger = {
|
|
|
133
133
|
const scrollPositionMap = new Map<string, number>();
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* @customElement gem-route
|
|
136
|
+
* @customElement gem-light-route
|
|
137
137
|
* @attr inert 暂停路由更新
|
|
138
138
|
* @fires routechange
|
|
139
139
|
* @fires error
|
|
140
140
|
* @fires loading
|
|
141
141
|
*/
|
|
142
|
-
export class
|
|
142
|
+
export class GemLightRouteElement extends GemElement {
|
|
143
143
|
@boolattribute transition: boolean;
|
|
144
144
|
@property routes?: RouteItem[] | RoutesObject;
|
|
145
145
|
/**
|
|
@@ -168,8 +168,6 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
168
168
|
/**当前匹配的路由的 params */
|
|
169
169
|
currentParams: Params = {};
|
|
170
170
|
|
|
171
|
-
#lastLoader?: Promise<TemplateResult | undefined>;
|
|
172
|
-
|
|
173
171
|
/**不要多个 `<gem-route>` 共享,因为那样会导致后面的元素卸载前触发更新 */
|
|
174
172
|
static createLocationStore = () => {
|
|
175
173
|
const { path, query, hash, data } = history.getParams();
|
|
@@ -201,14 +199,7 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
201
199
|
return typeof this.scrollContainer === 'function' ? this.scrollContainer() : this.scrollContainer;
|
|
202
200
|
}
|
|
203
201
|
|
|
204
|
-
|
|
205
|
-
super({ isLight });
|
|
206
|
-
this.routes = this.routes || routes;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
state: State = {
|
|
210
|
-
content: INIT_CONTENT,
|
|
211
|
-
};
|
|
202
|
+
#state = createState<State>({ content: INIT_CONTENT });
|
|
212
203
|
|
|
213
204
|
#updateLocationStore = () => {
|
|
214
205
|
if (!this.locationStore) return;
|
|
@@ -232,13 +223,14 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
232
223
|
}
|
|
233
224
|
};
|
|
234
225
|
|
|
226
|
+
#lastLoader?: Promise<TemplateResult | undefined>;
|
|
235
227
|
#setContent = (route: RouteItem | null, params: Params, content?: TemplateResult) => {
|
|
236
228
|
this.beforeroutechange(route);
|
|
237
229
|
this.#lastLoader = undefined;
|
|
238
230
|
this.currentRoute = route;
|
|
239
231
|
this.currentParams = params;
|
|
240
232
|
const changeContent = async () => {
|
|
241
|
-
this
|
|
233
|
+
this.#state({ content });
|
|
242
234
|
// 要确保新页面能读取到新路由数据
|
|
243
235
|
this.#updateLocationStore();
|
|
244
236
|
// 等待页面渲染完成
|
|
@@ -253,45 +245,39 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
253
245
|
}
|
|
254
246
|
};
|
|
255
247
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
() => {
|
|
259
|
-
if (!this.#scrollContainer) return;
|
|
260
|
-
return addListener(history, 'beforechange', () =>
|
|
261
|
-
scrollPositionMap.set(history.currentKey, this.#scrollContainer!.scrollTop),
|
|
262
|
-
);
|
|
263
|
-
},
|
|
264
|
-
() => [this.scrollContainer],
|
|
265
|
-
);
|
|
248
|
+
@willMount()
|
|
249
|
+
#unBoundary = () => this.internals.states.delete(BoundaryCSSState);
|
|
266
250
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
251
|
+
@effect((i) => [i.scrollContainer])
|
|
252
|
+
#rememberScrollPosition = () => {
|
|
253
|
+
if (!this.#scrollContainer) return;
|
|
254
|
+
return addListener(history, 'beforechange', () =>
|
|
255
|
+
scrollPositionMap.set(history.currentKey, this.#scrollContainer!.scrollTop),
|
|
271
256
|
);
|
|
257
|
+
};
|
|
272
258
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (old && key === old[0] && path === old[1]) {
|
|
277
|
-
this.#updateLocationStore();
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
this.update();
|
|
281
|
-
},
|
|
282
|
-
() => {
|
|
283
|
-
const { path, query, hash } = this.trigger.getParams();
|
|
284
|
-
return [this.key, path, query, hash] as const;
|
|
285
|
-
},
|
|
286
|
-
);
|
|
287
|
-
}
|
|
259
|
+
// history or i18n
|
|
260
|
+
@effect((i) => [i.scrollContainer])
|
|
261
|
+
#connectTriggerStore = () => connect(this.trigger.store, () => this.#state({}));
|
|
288
262
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
263
|
+
#updateContentDep = () => {
|
|
264
|
+
const { path, query, hash } = this.trigger.getParams();
|
|
265
|
+
return [this.key, path, query, hash] as const;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
@effect((i) => i.#updateContentDep())
|
|
269
|
+
#updateContent = ([key, path]: any, old?: any) => {
|
|
270
|
+
// 只有查询参数改变
|
|
271
|
+
if (old && key === old[0] && path === old[1]) {
|
|
272
|
+
this.#updateLocationStore();
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.update();
|
|
276
|
+
};
|
|
292
277
|
|
|
293
|
-
|
|
294
|
-
|
|
278
|
+
@template((i) => !i.inert)
|
|
279
|
+
#content = () => {
|
|
280
|
+
const { content } = this.#state;
|
|
295
281
|
if (content === undefined) return;
|
|
296
282
|
|
|
297
283
|
if (!this.shadowRoot) {
|
|
@@ -308,8 +294,9 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
308
294
|
</style>
|
|
309
295
|
${content === INIT_CONTENT ? html`<slot></slot>` : content}
|
|
310
296
|
`;
|
|
311
|
-
}
|
|
297
|
+
};
|
|
312
298
|
|
|
299
|
+
// 重写 `update`,并暴露为公共字段
|
|
313
300
|
update = () => {
|
|
314
301
|
const { path, hash, query } = this.trigger.getParams();
|
|
315
302
|
const { route, params = {} } = GemRouteElement.findRoute(this.routes, path);
|
|
@@ -323,7 +310,10 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
323
310
|
return;
|
|
324
311
|
}
|
|
325
312
|
if (this.trigger === history) {
|
|
326
|
-
|
|
313
|
+
// 嵌套路由执行顺序也是从父到子
|
|
314
|
+
if (location.href !== titleStore.url || route?.title) {
|
|
315
|
+
updateStore(titleStore, { url: location.href, title: route?.title });
|
|
316
|
+
}
|
|
327
317
|
}
|
|
328
318
|
const contentOrLoader = content || getContent?.(params, this.shadowRoot || this);
|
|
329
319
|
this.loading(route);
|
|
@@ -331,8 +321,8 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
331
321
|
this.#lastLoader = contentOrLoader;
|
|
332
322
|
const isSomeLoader = () => this.#lastLoader === contentOrLoader;
|
|
333
323
|
contentOrLoader
|
|
334
|
-
.then((
|
|
335
|
-
if (isSomeLoader()) this.#setContent(route, params,
|
|
324
|
+
.then((newContent) => {
|
|
325
|
+
if (isSomeLoader()) this.#setContent(route, params, newContent);
|
|
336
326
|
})
|
|
337
327
|
.catch((err) => {
|
|
338
328
|
if (isSomeLoader()) this.error(err);
|
|
@@ -344,10 +334,7 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
344
334
|
}
|
|
345
335
|
|
|
346
336
|
/**
|
|
347
|
-
* @customElement gem-
|
|
337
|
+
* @customElement gem-route
|
|
348
338
|
*/
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
super({ ...options, isLight: true });
|
|
352
|
-
}
|
|
353
|
-
}
|
|
339
|
+
@shadow()
|
|
340
|
+
export class GemRouteElement extends GemLightRouteElement {}
|
|
@@ -7,46 +7,40 @@
|
|
|
7
7
|
* 修改标题:titleStore 的 title 优先级高,比如 history 添加的 dialog Title
|
|
8
8
|
*
|
|
9
9
|
* - `<gem-route>` 匹配路由时自动设置 `route.title`
|
|
10
|
-
* -
|
|
11
|
-
* - 修改路径时,`history` 默认设置 `title` 为空
|
|
10
|
+
* - 修改路径(查询参数)时,`history` 默认设置 `title` 为空,运行 Modal 设置临时标题
|
|
12
11
|
* - 数据获取后,手动调用 `Title.setTitle`
|
|
13
12
|
* - `<gem-title>` 作为默认值设置
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import { GemElement, html } from '../../lib/element';
|
|
17
|
-
import { attribute, boolattribute } from '../../lib/decorators';
|
|
16
|
+
import { attribute, boolattribute, effect, mounted, shadow } from '../../lib/decorators';
|
|
18
17
|
import { updateStore, connect } from '../../lib/store';
|
|
19
18
|
import { titleStore } from '../../lib/history';
|
|
20
|
-
import { addMicrotask } from '../../lib/utils';
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
// 避免重定向时的中间状态标题
|
|
21
|
+
let timer = 0;
|
|
22
|
+
const setTitle = (documentTitle: string) => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
timer = window.setTimeout(() => (document.title = documentTitle));
|
|
25
|
+
};
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
function setDocumentTitle(str?: string | null, prefix = '', suffix = '') {
|
|
28
|
-
const title = titleStore.title || str;
|
|
29
|
-
if (title && title !== defaultTitle) {
|
|
27
|
+
function setDocumentTitle(defaultTitle?: string | null, prefix = '', suffix = '') {
|
|
28
|
+
const title = titleStore.title || defaultTitle;
|
|
29
|
+
if (title && title !== titleStore.defaultTitle) {
|
|
30
30
|
GemTitleElement.title = title;
|
|
31
|
-
|
|
31
|
+
setTitle(prefix + GemTitleElement.title + suffix);
|
|
32
32
|
} else {
|
|
33
|
-
GemTitleElement.title = defaultTitle;
|
|
34
|
-
|
|
33
|
+
GemTitleElement.title = titleStore.defaultTitle;
|
|
34
|
+
setTitle(GemTitleElement.title);
|
|
35
35
|
}
|
|
36
|
-
// 避免重定向时的中间状态
|
|
37
|
-
addMicrotask(setTitle);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
connect(titleStore, setDocumentTitle);
|
|
41
|
-
|
|
42
|
-
export const PREFIX = `${defaultTitle} | `;
|
|
43
|
-
export const SUFFIX = ` - ${defaultTitle}`;
|
|
44
|
-
|
|
45
38
|
/**
|
|
46
39
|
* @customElement gem-title
|
|
47
40
|
* @attr prefix
|
|
48
41
|
* @attr suffix
|
|
49
42
|
*/
|
|
43
|
+
@shadow()
|
|
50
44
|
export class GemTitleElement extends GemElement {
|
|
51
45
|
@attribute prefix: string;
|
|
52
46
|
@attribute suffix: string;
|
|
@@ -55,34 +49,23 @@ export class GemTitleElement extends GemElement {
|
|
|
55
49
|
// 没有后缀的当前标题
|
|
56
50
|
static title = document.title;
|
|
57
51
|
|
|
58
|
-
/**@deprecated */
|
|
59
|
-
static defaultPrefix = PREFIX;
|
|
60
|
-
/**@deprecated */
|
|
61
|
-
static defaultSuffix = SUFFIX;
|
|
62
|
-
|
|
63
52
|
static setTitle(title: string) {
|
|
64
53
|
updateStore(titleStore, { title });
|
|
65
54
|
}
|
|
66
55
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
childList: true,
|
|
72
|
-
subtree: true,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
56
|
+
@effect((i) => [i.inert])
|
|
57
|
+
#connectStore = () => !this.inert && connect(titleStore, this.update);
|
|
58
|
+
|
|
59
|
+
#ob = new MutationObserver(() => this.update());
|
|
75
60
|
|
|
76
|
-
mounted
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
() => [this.inert],
|
|
80
|
-
);
|
|
61
|
+
@mounted()
|
|
62
|
+
#obTextContent = () => {
|
|
63
|
+
this.#ob.observe(this, { characterData: true, childList: true, subtree: true });
|
|
81
64
|
};
|
|
82
65
|
|
|
83
|
-
render() {
|
|
84
|
-
// 多个 <gem-title> 时,最终 document.title
|
|
66
|
+
render = () => {
|
|
67
|
+
// 多个 <gem-title> 时,最终 document.title 按渲染顺序决定
|
|
85
68
|
setDocumentTitle(this.textContent?.trim(), this.prefix, this.suffix);
|
|
86
69
|
return html`${GemTitleElement.title}`;
|
|
87
|
-
}
|
|
70
|
+
};
|
|
88
71
|
}
|