@mantou/gem 1.7.10 → 1.7.12
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 +1839 -2597
- package/dist/gem.umd.js +23 -24
- package/elements/base/gesture.d.ts +1 -3
- package/elements/base/gesture.d.ts.map +1 -1
- package/elements/base/gesture.js +2 -3
- package/elements/base/gesture.js.map +1 -1
- package/elements/base/link.d.ts +2 -0
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +2 -0
- package/elements/base/link.js.map +1 -1
- package/elements/base/modal-factory.d.ts +23 -21
- package/elements/base/modal-factory.d.ts.map +1 -1
- package/elements/base/reflect.d.ts +5 -0
- package/elements/base/reflect.d.ts.map +1 -1
- package/elements/base/reflect.js +26 -7
- package/elements/base/reflect.js.map +1 -1
- package/elements/base/route.d.ts +9 -7
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +23 -7
- package/elements/base/route.js.map +1 -1
- package/elements/base/title.d.ts +1 -1
- package/elements/base/title.js +1 -1
- package/elements/base/unsafe.d.ts +1 -2
- package/elements/base/unsafe.d.ts.map +1 -1
- package/elements/base/unsafe.js +1 -2
- package/elements/base/unsafe.js.map +1 -1
- package/elements/base/use.d.ts +1 -1
- package/elements/base/use.js +1 -1
- package/helper/i18n.d.ts +1 -1
- package/helper/i18n.d.ts.map +1 -1
- package/helper/i18n.js +2 -3
- package/helper/i18n.js.map +1 -1
- 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/lib/decorators.d.ts +1 -0
- package/lib/decorators.d.ts.map +1 -1
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js +16 -18
- package/lib/element.js.map +1 -1
- package/lib/store.d.ts +1 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +3 -1
- package/lib/store.js.map +1 -1
- package/lib/utils.d.ts +7 -7
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +19 -30
- package/lib/utils.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +6 -5
- package/src/elements/base/gesture.ts +3 -3
- package/src/elements/base/link.ts +2 -0
- package/src/elements/base/reflect.ts +18 -6
- package/src/elements/base/route.ts +32 -11
- package/src/elements/base/title.ts +1 -1
- package/src/elements/base/unsafe.ts +1 -2
- package/src/elements/base/use.ts +1 -1
- package/src/helper/i18n.ts +2 -4
- package/src/helper/request.ts +122 -53
- package/src/lib/decorators.ts +1 -1
- package/src/lib/element.ts +18 -17
- package/src/lib/store.ts +3 -1
- package/src/lib/utils.ts +30 -37
- package/src/lib/version.ts +1 -1
- package/src/test/utils.test.ts +12 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantou/gem",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.12",
|
|
4
4
|
"description": "💎 使用自定义元素的轻量级 WebApp 开发框架",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontend",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
],
|
|
31
31
|
"typings": "index.d.ts",
|
|
32
32
|
"scripts": {
|
|
33
|
-
"docs": "gem-book docs",
|
|
33
|
+
"docs": "GEM_BOOK_REPLACE=true gem-book docs",
|
|
34
|
+
"docs:remote": "yarn docs",
|
|
34
35
|
"build:docs": "yarn docs --build",
|
|
35
36
|
"build:module": "tsc -p tsconfig.build.json",
|
|
36
37
|
"build:lib": "vite build",
|
|
@@ -43,13 +44,13 @@
|
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@esm-bundle/chai": "^4.3.4-fix.0",
|
|
46
|
-
"@gemjs/config": "^1.6.
|
|
47
|
+
"@gemjs/config": "^1.6.14",
|
|
47
48
|
"@open-wc/testing": "^2.5.33",
|
|
48
49
|
"@web/dev-server-esbuild": "^0.2.16",
|
|
49
50
|
"@web/test-runner": "^0.13.22",
|
|
50
51
|
"rimraf": "^3.0.2",
|
|
51
|
-
"typescript": "^5.3
|
|
52
|
-
"vite": "^
|
|
52
|
+
"typescript": "^5.4.3",
|
|
53
|
+
"vite": "^4.5.2"
|
|
53
54
|
},
|
|
54
55
|
"author": "mantou132",
|
|
55
56
|
"license": "MIT",
|
|
@@ -46,11 +46,9 @@ function angleAB(
|
|
|
46
46
|
/**
|
|
47
47
|
* 块级元素,如果要设置成 `contents`,则内容块要设置 `touch-action: none`
|
|
48
48
|
* https://javascript.info/pointer-events#event-pointercancel
|
|
49
|
-
*
|
|
50
49
|
* 在移动端上,必须设置 `touch-action` 以允许滚动等原生动作
|
|
51
50
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
* @customElement gem-gesture
|
|
54
52
|
* @event pan
|
|
55
53
|
* @event pinch
|
|
56
54
|
* @event rotate
|
|
@@ -76,7 +74,9 @@ export class GemGestureElement extends GemElement {
|
|
|
76
74
|
this.addEventListener('pointermove', this.#onMoveSet);
|
|
77
75
|
this.addEventListener('pointerup', this.#onEnd);
|
|
78
76
|
this.addEventListener('pointerleave', this.#onEnd); // 有时候 up 没有触发?
|
|
77
|
+
// 为什么空白区域会自动触发 `pointercancel`?
|
|
79
78
|
this.addEventListener('pointercancel', this.#onEnd);
|
|
79
|
+
|
|
80
80
|
this.addEventListener('dragstart', (evt) => evt.preventDefault());
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -10,6 +10,7 @@ function isExternal(path: string) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
+
* @customElement gem-link
|
|
13
14
|
* @attr href
|
|
14
15
|
* @attr target
|
|
15
16
|
* @attr doc-title
|
|
@@ -171,6 +172,7 @@ export class GemLinkElement extends GemElement {
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
/**
|
|
175
|
+
* @customElement gem-active-link
|
|
174
176
|
* @attr pattern
|
|
175
177
|
* @state active
|
|
176
178
|
*/
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 比如 duoyun-ui 文档站加载示例元素时把 GemReflectElement 渲染的元素清除
|
|
8
8
|
*/
|
|
9
9
|
import { GemElement } from '../../lib/element';
|
|
10
|
-
import { property } from '../../lib/decorators';
|
|
10
|
+
import { attribute, property } from '../../lib/decorators';
|
|
11
11
|
|
|
12
12
|
const START = 'gem-reflect-start';
|
|
13
13
|
const END = 'gem-reflect-end';
|
|
@@ -25,9 +25,18 @@ const getReflectFragment = (startNode: Comment) => {
|
|
|
25
25
|
return result;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @customElement gem-reflect
|
|
30
|
+
*/
|
|
28
31
|
export class GemReflectElement extends GemElement {
|
|
32
|
+
@attribute method: 'prepend' | 'append';
|
|
33
|
+
|
|
29
34
|
@property target: HTMLElement = document.head;
|
|
30
35
|
|
|
36
|
+
get #method() {
|
|
37
|
+
return this.method || 'append';
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
constructor() {
|
|
32
41
|
super({ isLight: true });
|
|
33
42
|
|
|
@@ -45,11 +54,14 @@ export class GemReflectElement extends GemElement {
|
|
|
45
54
|
|
|
46
55
|
this.effect(
|
|
47
56
|
() => {
|
|
48
|
-
|
|
49
|
-
[
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
(childNodes || [startNode, ...this.childNodes, endNode]).forEach((node, index, arr) => {
|
|
58
|
+
const prev = arr[index - 1];
|
|
59
|
+
if (prev) {
|
|
60
|
+
prev.after(node);
|
|
61
|
+
} else {
|
|
62
|
+
this.target[this.#method](node);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
53
65
|
return () => {
|
|
54
66
|
childNodes = getReflectFragment(startNode);
|
|
55
67
|
childNodes.forEach((node) => node.remove());
|
|
@@ -72,13 +72,17 @@ export interface RouteItem<T = unknown> {
|
|
|
72
72
|
pattern: string;
|
|
73
73
|
redirect?: string;
|
|
74
74
|
content?: TemplateResult;
|
|
75
|
-
|
|
75
|
+
// 返回 `undefined` 意味着不会执行 `render`, 支持 React 等组件挂载到 `<gem-route>` 元素下
|
|
76
|
+
getContent?: (
|
|
77
|
+
params: Params,
|
|
78
|
+
renderRoot: DocumentFragment | GemLightRouteElement,
|
|
79
|
+
) => TemplateResult | Promise<TemplateResult> | undefined | Promise<undefined>;
|
|
76
80
|
title?: string;
|
|
77
81
|
// 用来传递数据
|
|
78
82
|
data?: T;
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
export type RoutesObject = Record<string, RouteItem
|
|
85
|
+
export type RoutesObject<T = unknown> = Record<string, RouteItem<T>>;
|
|
82
86
|
|
|
83
87
|
// params 中的成员不会验证
|
|
84
88
|
export interface RouteOptions extends Omit<UpdateHistoryParams, 'path'> {
|
|
@@ -88,7 +92,10 @@ export interface RouteOptions extends Omit<UpdateHistoryParams, 'path'> {
|
|
|
88
92
|
// 从路由创建路径
|
|
89
93
|
// 不包含 basePath
|
|
90
94
|
export function createPath(route: RouteItem, options?: RouteOptions) {
|
|
91
|
-
|
|
95
|
+
// /a/* /a/:0
|
|
96
|
+
// /a/*/:b/* /a/:0/:b/:1
|
|
97
|
+
let i = 0;
|
|
98
|
+
let path = route.pattern.replaceAll('*', () => `:${i++}`);
|
|
92
99
|
const params = options?.params;
|
|
93
100
|
if (params) {
|
|
94
101
|
Object.keys(params).forEach((key) => {
|
|
@@ -111,19 +118,22 @@ interface ConstructorOptions {
|
|
|
111
118
|
routes?: RouteItem[] | RoutesObject;
|
|
112
119
|
}
|
|
113
120
|
|
|
121
|
+
const INIT_CONTENT = html``;
|
|
122
|
+
|
|
114
123
|
type State = {
|
|
115
124
|
content?: TemplateResult;
|
|
116
125
|
};
|
|
117
126
|
|
|
118
127
|
export type RouteTrigger = {
|
|
119
128
|
store: Store<any>;
|
|
120
|
-
replace: (arg:
|
|
129
|
+
replace: (arg: UpdateHistoryParams) => void;
|
|
121
130
|
getParams: () => { path: string; query?: string | QueryString; hash?: string };
|
|
122
131
|
};
|
|
123
132
|
|
|
124
133
|
const scrollPositionMap = new Map<string, number>();
|
|
125
134
|
|
|
126
135
|
/**
|
|
136
|
+
* @customElement gem-route
|
|
127
137
|
* @attr inert 暂停路由更新
|
|
128
138
|
* @fires routechange
|
|
129
139
|
* @fires error
|
|
@@ -158,7 +168,7 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
158
168
|
/**当前匹配的路由的 params */
|
|
159
169
|
currentParams: Params = {};
|
|
160
170
|
|
|
161
|
-
#lastLoader?: Promise<TemplateResult>;
|
|
171
|
+
#lastLoader?: Promise<TemplateResult | undefined>;
|
|
162
172
|
|
|
163
173
|
/**不要多个 `<gem-route>` 共享,因为那样会导致后面的元素卸载前触发更新 */
|
|
164
174
|
static createLocationStore = () => {
|
|
@@ -193,11 +203,11 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
193
203
|
|
|
194
204
|
constructor({ isLight, routes }: ConstructorOptions = {}) {
|
|
195
205
|
super({ isLight });
|
|
196
|
-
this.routes = routes;
|
|
206
|
+
this.routes = this.routes || routes;
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
state: State = {
|
|
200
|
-
content:
|
|
210
|
+
content: INIT_CONTENT,
|
|
201
211
|
};
|
|
202
212
|
|
|
203
213
|
#updateLocationStore = () => {
|
|
@@ -282,7 +292,12 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
282
292
|
|
|
283
293
|
render() {
|
|
284
294
|
const { content } = this.state;
|
|
285
|
-
if (
|
|
295
|
+
if (content === undefined) return;
|
|
296
|
+
|
|
297
|
+
if (!this.shadowRoot) {
|
|
298
|
+
if (content === INIT_CONTENT) return;
|
|
299
|
+
return html`${content}`;
|
|
300
|
+
}
|
|
286
301
|
|
|
287
302
|
return html`
|
|
288
303
|
<style>
|
|
@@ -291,23 +306,26 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
291
306
|
display: contents;
|
|
292
307
|
}
|
|
293
308
|
</style>
|
|
294
|
-
${content
|
|
309
|
+
${content === INIT_CONTENT ? html`<slot></slot>` : content}
|
|
295
310
|
`;
|
|
296
311
|
}
|
|
297
312
|
|
|
298
313
|
update = () => {
|
|
299
|
-
const {
|
|
314
|
+
const { path, hash, query } = this.trigger.getParams();
|
|
315
|
+
const { route, params = {} } = GemRouteElement.findRoute(this.routes, path);
|
|
300
316
|
const { redirect, content, getContent } = route || {};
|
|
301
317
|
if (redirect) {
|
|
302
318
|
this.trigger.replace({
|
|
303
319
|
path: createPath({ pattern: redirect }, { params }),
|
|
320
|
+
query,
|
|
321
|
+
hash,
|
|
304
322
|
});
|
|
305
323
|
return;
|
|
306
324
|
}
|
|
307
325
|
if (this.trigger === history) {
|
|
308
326
|
updateStore(titleStore, { title: route?.title });
|
|
309
327
|
}
|
|
310
|
-
const contentOrLoader = content || getContent?.(params);
|
|
328
|
+
const contentOrLoader = content || getContent?.(params, this.shadowRoot || this);
|
|
311
329
|
this.loading(route);
|
|
312
330
|
if (contentOrLoader instanceof Promise) {
|
|
313
331
|
this.#lastLoader = contentOrLoader;
|
|
@@ -325,6 +343,9 @@ export class GemRouteElement extends GemElement<State> {
|
|
|
325
343
|
};
|
|
326
344
|
}
|
|
327
345
|
|
|
346
|
+
/**
|
|
347
|
+
* @customElement gem-light-route
|
|
348
|
+
*/
|
|
328
349
|
export class GemLightRouteElement extends GemRouteElement {
|
|
329
350
|
constructor(options: ConstructorOptions = {}) {
|
|
330
351
|
super({ ...options, isLight: true });
|
package/src/elements/base/use.ts
CHANGED
|
@@ -6,9 +6,9 @@ const eleCache = new Map<string, HTMLTemplateElement>();
|
|
|
6
6
|
* svg 中的 `<use>` 不能穿透 ShadowDOM,
|
|
7
7
|
* 此元素用来模拟 `<use>`,
|
|
8
8
|
* 由于是复制元素,所以不能像 `<use>` 一样自动更新
|
|
9
|
-
*
|
|
10
9
|
* slot 支持为 icon 添加效果,如涟漪,徽章
|
|
11
10
|
*
|
|
11
|
+
* @customElement gem-use
|
|
12
12
|
* @attr selector
|
|
13
13
|
*/
|
|
14
14
|
export class GemUseElement extends GemElement {
|
package/src/helper/i18n.ts
CHANGED
|
@@ -74,10 +74,8 @@ export class I18n<T = Record<string, Msg>> implements RouteTrigger {
|
|
|
74
74
|
|
|
75
75
|
store: Store<any>;
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
|
|
77
|
+
// 兼容 `<gem-route>`
|
|
78
|
+
replace = ({ path }: { path: string }) => this.setLanguage(path);
|
|
81
79
|
getParams = () => ({ path: this.currentLanguage });
|
|
82
80
|
|
|
83
81
|
get #cacheCurrentKey() {
|
package/src/helper/request.ts
CHANGED
|
@@ -1,60 +1,129 @@
|
|
|
1
|
-
|
|
2
|
-
headers: {
|
|
3
|
-
'Content-Type': 'application/json',
|
|
4
|
-
},
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export async function request<T>(uri: string, options: RequestInit = {}): Promise<T> {
|
|
8
|
-
const res = await fetch(uri, {
|
|
9
|
-
...defaultReqInit,
|
|
10
|
-
...options,
|
|
11
|
-
});
|
|
12
|
-
if (res.status === 0) throw new Error('Request fail');
|
|
13
|
-
if (res.status >= 500) throw new Error(res.statusText);
|
|
14
|
-
try {
|
|
15
|
-
const data = await res.json();
|
|
16
|
-
if (res.status >= 400) throw new (Error as any)(data?.message || res.statusText, { cause: data });
|
|
17
|
-
return data;
|
|
18
|
-
} catch {
|
|
19
|
-
throw new Error('Server error');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
1
|
+
import { GemError } from '../lib/utils';
|
|
22
2
|
|
|
23
3
|
type ReqBody = Record<string, unknown> | BodyInit;
|
|
4
|
+
type ReqParams = Record<string, string | number>;
|
|
24
5
|
const TypedArray = Object.getPrototypeOf(Object.getPrototypeOf(new Int8Array())).constructor;
|
|
25
6
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
) {
|
|
35
|
-
return body as BodyInit;
|
|
36
|
-
}
|
|
37
|
-
if (typeof body === 'object') {
|
|
38
|
-
return JSON.stringify(body);
|
|
39
|
-
}
|
|
40
|
-
return body;
|
|
41
|
-
}
|
|
7
|
+
type Options = {
|
|
8
|
+
origin?: string;
|
|
9
|
+
base?: string;
|
|
10
|
+
// only for get/del/put/post
|
|
11
|
+
appendHeaders?: (input?: any) => HeadersInit;
|
|
12
|
+
transformInput?: (input?: any) => any;
|
|
13
|
+
transformOutput?: (output: any, req: Request) => any;
|
|
14
|
+
} & RequestInit;
|
|
42
15
|
|
|
43
|
-
function
|
|
44
|
-
const {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
16
|
+
function initRequest(options: Options = {}) {
|
|
17
|
+
const {
|
|
18
|
+
origin = location.origin,
|
|
19
|
+
base = '',
|
|
20
|
+
appendHeaders = () => ({}),
|
|
21
|
+
transformInput = (p?: any) => p,
|
|
22
|
+
transformOutput = (p?: any) => p,
|
|
23
|
+
...reqInit
|
|
24
|
+
} = options;
|
|
48
25
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
26
|
+
const serializationBody = (body?: ReqBody): BodyInit | undefined => {
|
|
27
|
+
if (
|
|
28
|
+
body instanceof FormData ||
|
|
29
|
+
body instanceof Blob ||
|
|
30
|
+
body instanceof TypedArray ||
|
|
31
|
+
body instanceof URLSearchParams ||
|
|
32
|
+
body instanceof ReadableStream ||
|
|
33
|
+
body instanceof ArrayBuffer
|
|
34
|
+
) {
|
|
35
|
+
return body as BodyInit;
|
|
36
|
+
}
|
|
37
|
+
if (typeof body === 'object') {
|
|
38
|
+
return JSON.stringify(body);
|
|
39
|
+
}
|
|
40
|
+
return body;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const serializationUrl = (path: string, params?: ReqParams) => {
|
|
44
|
+
if (new URL(path, location.origin).pathname !== path) {
|
|
45
|
+
throw new GemError('`path` format is incorrect');
|
|
46
|
+
}
|
|
47
|
+
const query = new URLSearchParams(params as Record<string, string>).toString();
|
|
48
|
+
return `${origin}${base}${path}${query ? '?' : ''}${query}`;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const serializationHeaders = (headers?: HeadersInit) => {
|
|
52
|
+
if (!headers) return {};
|
|
53
|
+
// auto `toLowerCase`
|
|
54
|
+
if (headers instanceof Headers) return Object.fromEntries(headers);
|
|
55
|
+
const entries = Array.isArray(headers) ? headers : Object.entries(headers);
|
|
56
|
+
return Object.fromEntries(entries.map(([key, value]) => [key.toLowerCase(), value]));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const request = async <T>(uri: string, reqOptions: RequestInit = {}): Promise<T> => {
|
|
60
|
+
const req = new Request(uri, {
|
|
61
|
+
...reqInit,
|
|
62
|
+
...reqOptions,
|
|
63
|
+
headers: new Headers({
|
|
64
|
+
'content-type': 'application/json',
|
|
65
|
+
...serializationHeaders(reqInit.headers),
|
|
66
|
+
...serializationHeaders(reqOptions.headers),
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
const res = await fetch(req);
|
|
70
|
+
|
|
71
|
+
if (res.status === 0) throw new Error('Request fail');
|
|
72
|
+
if (res.status >= 500) throw new Error(res.statusText);
|
|
73
|
+
|
|
74
|
+
const contentType = req.headers.get('content-type')!;
|
|
75
|
+
const isClientError = res.status >= 400;
|
|
76
|
+
try {
|
|
77
|
+
let data: any;
|
|
78
|
+
if (contentType.includes('json')) {
|
|
79
|
+
data = await res.json();
|
|
80
|
+
} else if (contentType.includes('octet-stream')) {
|
|
81
|
+
data = await res.arrayBuffer();
|
|
82
|
+
} else {
|
|
83
|
+
data = await res.text();
|
|
84
|
+
}
|
|
85
|
+
if (isClientError) throw new (Error as any)(res.statusText, { cause: data });
|
|
86
|
+
return transformOutput(data, req);
|
|
87
|
+
} catch {
|
|
88
|
+
throw new Error('Parse error');
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
request,
|
|
94
|
+
get<T>(path: string, params?: ReqParams) {
|
|
95
|
+
const i = transformInput(params);
|
|
96
|
+
return request<T>(serializationUrl(path, i), {
|
|
97
|
+
method: 'GET',
|
|
98
|
+
headers: appendHeaders(i),
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
del<T>(path: string, params?: ReqParams) {
|
|
102
|
+
const i = transformInput(params);
|
|
103
|
+
return request<T>(serializationUrl(path, i), {
|
|
104
|
+
method: 'DELETE',
|
|
105
|
+
headers: appendHeaders(i),
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
put<T>(path: string, body?: ReqBody) {
|
|
109
|
+
const i = transformInput(body);
|
|
110
|
+
return request<T>(serializationUrl(path), {
|
|
111
|
+
method: 'PUT',
|
|
112
|
+
headers: appendHeaders(i),
|
|
113
|
+
body: serializationBody(i),
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
post<T>(path: string, body?: ReqBody) {
|
|
117
|
+
const i = transformInput(body);
|
|
118
|
+
return request<T>(serializationUrl(path), {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: appendHeaders(i),
|
|
121
|
+
body: serializationBody(i),
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
};
|
|
60
125
|
}
|
|
126
|
+
|
|
127
|
+
const { request, del, get, post, put } = initRequest();
|
|
128
|
+
|
|
129
|
+
export { initRequest, request, del, get, post, put };
|
package/src/lib/decorators.ts
CHANGED
|
@@ -19,7 +19,7 @@ function pushStaticField(target: GemElementPrototype, field: StaticField, member
|
|
|
19
19
|
(cls[field] as any)[isSet ? 'add' : 'push'](member);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export type RefObject<T = HTMLElement> = { ref: string; element: T | undefined };
|
|
22
|
+
export type RefObject<T = HTMLElement> = { ref: string; element: T | undefined; elements: T[] };
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* 引用元素,只有第一个标记 ref 的元素有效
|
package/src/lib/element.ts
CHANGED
|
@@ -419,22 +419,17 @@ export abstract class GemElement<T = Record<string, unknown>> extends HTMLElemen
|
|
|
419
419
|
|
|
420
420
|
closestElement<K extends keyof HTMLElementTagNameMap>(tag: K): HTMLElementTagNameMap[K] | null;
|
|
421
421
|
closestElement<K extends abstract new (...args: any) => any>(constructor: K): InstanceType<K> | null;
|
|
422
|
-
closestElement<K extends abstract new (...args: any) => any>(constructorOrTag: K | string)
|
|
422
|
+
closestElement<K extends abstract new (...args: any) => any>(constructorOrTag: K | string) {
|
|
423
423
|
const isConstructor = typeof constructorOrTag === 'function';
|
|
424
424
|
const tagName = typeof constructorOrTag === 'string' && constructorOrTag.toUpperCase();
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return rootEle;
|
|
434
|
-
}
|
|
435
|
-
return getRootElement(rootEle);
|
|
436
|
-
};
|
|
437
|
-
return getRootElement(this);
|
|
425
|
+
const is = (ele: Element) => (isConstructor ? ele.constructor === constructorOrTag : ele.tagName === tagName);
|
|
426
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
427
|
+
let node: Element | null = this;
|
|
428
|
+
while (node) {
|
|
429
|
+
if (is(node)) break;
|
|
430
|
+
node = node.parentElement || (node.getRootNode() as ShadowRoot).host;
|
|
431
|
+
}
|
|
432
|
+
return node;
|
|
438
433
|
}
|
|
439
434
|
|
|
440
435
|
/**
|
|
@@ -572,7 +567,11 @@ export function defineProperty(
|
|
|
572
567
|
});
|
|
573
568
|
}
|
|
574
569
|
|
|
570
|
+
const getReflectTargets = (ele: ShadowRoot | GemElement) =>
|
|
571
|
+
[...ele.querySelectorAll<GemReflectElement>('[data-gem-reflect]')].map((e) => e.target);
|
|
572
|
+
|
|
575
573
|
export function defineRef(target: GemElement, prop: string, ref: string) {
|
|
574
|
+
const refSelector = `[ref=${ref}]`;
|
|
576
575
|
Object.defineProperty(target, prop, {
|
|
577
576
|
configurable: true,
|
|
578
577
|
get() {
|
|
@@ -586,12 +585,14 @@ export function defineRef(target: GemElement, prop: string, ref: string) {
|
|
|
586
585
|
return ref;
|
|
587
586
|
},
|
|
588
587
|
get element() {
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
const result = e.querySelector(`[ref=${ref}]`);
|
|
588
|
+
for (const e of [ele, ...getReflectTargets(ele)]) {
|
|
589
|
+
const result = e.querySelector(refSelector);
|
|
592
590
|
if (result) return result;
|
|
593
591
|
}
|
|
594
592
|
},
|
|
593
|
+
get elements() {
|
|
594
|
+
return [ele, ...getReflectTargets(ele)].map((e) => [...e.querySelectorAll(refSelector)]).flat();
|
|
595
|
+
},
|
|
595
596
|
};
|
|
596
597
|
proxy[prop] = refobject;
|
|
597
598
|
}
|
package/src/lib/store.ts
CHANGED
|
@@ -28,7 +28,9 @@ export function useStore<T extends NonPrimitive>(originStore: T) {
|
|
|
28
28
|
export function connect<T extends NonPrimitive>(store: Store<T>, func: () => void) {
|
|
29
29
|
const listeners = StoreListenerMap.get(store);
|
|
30
30
|
listeners?.add(func);
|
|
31
|
-
return () =>
|
|
31
|
+
return () => {
|
|
32
|
+
listeners?.delete(func);
|
|
33
|
+
};
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export function disconnect<T extends NonPrimitive>(store: Store<T>, func: () => void) {
|