@dooboostore/simple-web-component 1.0.15 → 1.0.16
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/README.md +183 -92
- package/dist/cjs/decorators/addEventListener.js +2 -3
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +89 -33
- package/dist/cjs/decorators/elementDefine.js.map +2 -2
- package/dist/cjs/decorators/emitCustomEvent.js +2 -14
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/query.js +25 -36
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +28 -36
- package/dist/cjs/decorators/queryAll.js.map +2 -2
- package/dist/cjs/types.js +1 -0
- package/dist/cjs/types.js.map +2 -2
- package/dist/cjs/utils/Utils.js +36 -143
- package/dist/cjs/utils/Utils.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +2 -3
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +89 -33
- package/dist/esm/decorators/elementDefine.js.map +2 -2
- package/dist/esm/decorators/emitCustomEvent.js +2 -14
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/query.js +25 -36
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +28 -36
- package/dist/esm/decorators/queryAll.js.map +2 -2
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +2 -2
- package/dist/esm/utils/Utils.js +36 -143
- package/dist/esm/utils/Utils.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +183 -266
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +3 -3
- package/dist/types/decorators/addEventListener.d.ts +9 -4
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +6 -5
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts +6 -5
- package/dist/types/decorators/queryAll.d.ts.map +1 -1
- package/dist/types/types.d.ts +3 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +6 -14
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +183 -266
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +3 -3
- package/package.json +3 -3
- package/src/decorators/addEventListener.ts +10 -7
- package/src/decorators/elementDefine.ts +86 -31
- package/src/decorators/emitCustomEvent.ts +5 -21
- package/src/decorators/query.ts +33 -46
- package/src/decorators/queryAll.ts +36 -46
- package/src/types.ts +6 -4
- package/src/utils/Utils.ts +40 -176
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooboostore/simple-web-component",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -81,9 +81,9 @@
|
|
|
81
81
|
"webpack-cli": "^5.1.4",
|
|
82
82
|
"webpack-dev-server": "^5.2.0",
|
|
83
83
|
"@dooboostore/core": "1.0.28",
|
|
84
|
-
"@dooboostore/core-node": "1.0.10",
|
|
85
84
|
"@dooboostore/simple-boot": "1.0.53",
|
|
86
|
-
"@dooboostore/core-web": "1.0.9"
|
|
85
|
+
"@dooboostore/core-web": "1.0.9",
|
|
86
|
+
"@dooboostore/core-node": "1.0.10"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
89
|
"clean": "rm -rf dist",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
2
|
|
|
3
3
|
export interface AddEventListenerOptions extends EventListenerOptions {
|
|
4
|
-
query?: string;
|
|
5
|
-
type: string;
|
|
6
4
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
7
5
|
capture?: boolean;
|
|
8
6
|
once?: boolean;
|
|
@@ -10,28 +8,33 @@ export interface AddEventListenerOptions extends EventListenerOptions {
|
|
|
10
8
|
stopPropagation?: boolean;
|
|
11
9
|
stopImmediatePropagation?: boolean;
|
|
12
10
|
preventDefault?: boolean;
|
|
11
|
+
delegate?: boolean;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
export interface AddEventListenerMetadata {
|
|
15
|
+
selector: string;
|
|
16
|
+
type: string;
|
|
16
17
|
options: AddEventListenerOptions;
|
|
17
18
|
propertyKey: string | symbol;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export const ADD_EVENT_LISTENER_METADATA_KEY = Symbol('simple-web-component:add-event-listener');
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* @addEventListener decorator to bind events to elements.
|
|
25
|
+
* Usage: @addEventListener('.btn', 'click') onClick() { ... }
|
|
26
|
+
* Usage: @addEventListener('', 'scroll', { passive: true }) onScroll() { ... }
|
|
27
|
+
*/
|
|
28
|
+
export function addEventListener(selector: string, type: string, options: AddEventListenerOptions = {}): MethodDecorator {
|
|
25
29
|
return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
|
26
30
|
const constructor = target.constructor;
|
|
27
|
-
const options: AddEventListenerOptions = typeof arg1 === 'string' ? { type: arg1, query: arg2 } : arg1;
|
|
28
31
|
|
|
29
32
|
let listeners = ReflectUtils.getMetadata<AddEventListenerMetadata[]>(ADD_EVENT_LISTENER_METADATA_KEY, constructor);
|
|
30
33
|
if (!listeners) {
|
|
31
34
|
listeners = [];
|
|
32
35
|
ReflectUtils.defineMetadata(ADD_EVENT_LISTENER_METADATA_KEY, listeners, constructor);
|
|
33
36
|
}
|
|
34
|
-
listeners.push({ options, propertyKey });
|
|
37
|
+
listeners.push({ selector, type, options, propertyKey });
|
|
35
38
|
};
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -123,9 +123,11 @@ export const elementDefine =
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
private _bindAttributeEvent(el: HTMLElement, attrName: string, script: string) {
|
|
127
|
-
if (!
|
|
128
|
-
|
|
126
|
+
private _bindAttributeEvent(el: HTMLElement, attrName: string, script: string, eventName?: string) {
|
|
127
|
+
if (!eventName) {
|
|
128
|
+
if (!attrName.startsWith('swc-on-')) return;
|
|
129
|
+
eventName = attrName.substring(7);
|
|
130
|
+
}
|
|
129
131
|
if (swcLifecycleAttributes.includes(attrName)) return;
|
|
130
132
|
|
|
131
133
|
const elHandlers = getHandlers(this);
|
|
@@ -190,43 +192,75 @@ export const elementDefine =
|
|
|
190
192
|
|
|
191
193
|
// --- @addEventListener binding ---
|
|
192
194
|
addEventListenerList.forEach(meta => {
|
|
193
|
-
const {
|
|
194
|
-
const
|
|
195
|
+
const { selector, type, options } = meta;
|
|
196
|
+
const { root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault, delegate } = options;
|
|
195
197
|
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
} else {
|
|
199
|
-
const searchRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
198
|
+
if (delegate && selector && selector !== ':host') {
|
|
199
|
+
const bindRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
200
200
|
const r = root || 'auto';
|
|
201
201
|
if (r === 'auto') {
|
|
202
|
-
|
|
202
|
+
bindRoots.push(this.shadowRoot || (this as any));
|
|
203
203
|
} else if (r === 'light') {
|
|
204
|
-
|
|
204
|
+
bindRoots.push(this as any);
|
|
205
205
|
} else if (r === 'shadow') {
|
|
206
|
-
if (this.shadowRoot)
|
|
206
|
+
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
207
207
|
} else if (r === 'all') {
|
|
208
|
-
|
|
209
|
-
if (this.shadowRoot)
|
|
208
|
+
bindRoots.push(this as any);
|
|
209
|
+
if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
212
|
+
bindRoots.forEach(bindRoot => {
|
|
213
|
+
const handler = (event: Event) => {
|
|
214
|
+
const target = event.target as HTMLElement;
|
|
215
|
+
const matchedEl = target.closest(selector);
|
|
216
|
+
if (matchedEl && (bindRoot as any).contains(matchedEl)) {
|
|
217
|
+
if (stopPropagation) event.stopPropagation();
|
|
218
|
+
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
219
|
+
if (preventDefault) event.preventDefault();
|
|
220
|
+
|
|
221
|
+
const hostSet = SwcUtils.getHostSet(this as any);
|
|
222
|
+
this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet, matchedEl]);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
bindRoot.addEventListener(type, handler, { capture, once, passive });
|
|
215
226
|
});
|
|
216
|
-
}
|
|
227
|
+
} else {
|
|
228
|
+
const targetEls: (HTMLElement | Element | Document | ShadowRoot | Window)[] = [];
|
|
229
|
+
if (!selector || selector === ':host') {
|
|
230
|
+
targetEls.push(this as any);
|
|
231
|
+
} else {
|
|
232
|
+
const searchRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
233
|
+
const r = root || 'auto';
|
|
234
|
+
if (r === 'auto') {
|
|
235
|
+
searchRoots.push((this.shadowRoot || (this as any)) as any);
|
|
236
|
+
} else if (r === 'light') {
|
|
237
|
+
searchRoots.push(this as any);
|
|
238
|
+
} else if (r === 'shadow') {
|
|
239
|
+
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
240
|
+
} else if (r === 'all') {
|
|
241
|
+
searchRoots.push(this as any);
|
|
242
|
+
if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
searchRoots.forEach(sr => {
|
|
246
|
+
const found = sr.querySelectorAll(selector);
|
|
247
|
+
found.forEach(el => targetEls.push(el as any));
|
|
248
|
+
});
|
|
249
|
+
}
|
|
217
250
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
251
|
+
targetEls.forEach(el => {
|
|
252
|
+
const handler = (event: Event) => {
|
|
253
|
+
if (stopPropagation) event.stopPropagation();
|
|
254
|
+
if (stopImmediatePropagation) event.stopImmediatePropagation();
|
|
255
|
+
if (preventDefault) event.preventDefault();
|
|
256
|
+
|
|
257
|
+
const hostSet = SwcUtils.getHostSet(this as any);
|
|
258
|
+
this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet]);
|
|
259
|
+
};
|
|
260
|
+
const opts = { capture, once, passive };
|
|
261
|
+
el.addEventListener(type, handler, opts);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
230
264
|
});
|
|
231
265
|
|
|
232
266
|
if (super.connectedCallback) await super.connectedCallback();
|
|
@@ -272,7 +306,9 @@ export const elementDefine =
|
|
|
272
306
|
|
|
273
307
|
if (name.startsWith('swc-on-') && !swcLifecycleAttributes.includes(name)) {
|
|
274
308
|
if (newVal !== null) {
|
|
275
|
-
|
|
309
|
+
const customEventMeta = emitCustomEventList.find(it => it.options.attributeName === name);
|
|
310
|
+
const eventName = customEventMeta ? customEventMeta.options.type : name.substring(7);
|
|
311
|
+
this._bindAttributeEvent(this as any, name, newVal, eventName);
|
|
276
312
|
}
|
|
277
313
|
}
|
|
278
314
|
|
|
@@ -299,6 +335,25 @@ export const elementDefine =
|
|
|
299
335
|
}
|
|
300
336
|
});
|
|
301
337
|
|
|
338
|
+
// --- @emitCustomEvent wrapping logic ---
|
|
339
|
+
emitCustomEventList.forEach(meta => {
|
|
340
|
+
const { propertyKey, options } = meta;
|
|
341
|
+
const original = NewClass.prototype[propertyKey as any];
|
|
342
|
+
if (typeof original === 'function') {
|
|
343
|
+
NewClass.prototype[propertyKey as any] = function (...args: any[]) {
|
|
344
|
+
const detail = original.apply(this, args);
|
|
345
|
+
const event = new CustomEvent(options.type, {
|
|
346
|
+
detail,
|
|
347
|
+
bubbles: options.bubbles,
|
|
348
|
+
composed: options.composed,
|
|
349
|
+
cancelable: options.cancelable
|
|
350
|
+
});
|
|
351
|
+
this.dispatchEvent(event);
|
|
352
|
+
return detail;
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
302
357
|
const originalProto = constructor.prototype;
|
|
303
358
|
const newProto = NewClass.prototype;
|
|
304
359
|
Object.getOwnPropertyNames(originalProto).forEach(name => {
|
|
@@ -2,9 +2,9 @@ import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
|
2
2
|
|
|
3
3
|
export interface EmitCustomEventOptions {
|
|
4
4
|
type: string;
|
|
5
|
-
attributeName?: string;
|
|
6
|
-
bubbles?: boolean;
|
|
7
|
-
composed?: boolean;
|
|
5
|
+
attributeName?: string;
|
|
6
|
+
bubbles?: boolean;
|
|
7
|
+
composed?: boolean;
|
|
8
8
|
cancelable?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -16,12 +16,12 @@ export interface EmitCustomEventMetadata {
|
|
|
16
16
|
export const EMIT_CUSTOM_EVENT_METADATA_KEY = Symbol('simple-web-component:emit-custom-event');
|
|
17
17
|
|
|
18
18
|
export function emitCustomEvent(typeOrOptions: string | EmitCustomEventOptions): MethodDecorator {
|
|
19
|
-
return (target: Object, propertyKey: string | symbol
|
|
19
|
+
return (target: Object, propertyKey: string | symbol) => {
|
|
20
20
|
const constructor = target.constructor;
|
|
21
21
|
const options: EmitCustomEventOptions = typeof typeOrOptions === 'string' ? { type: typeOrOptions } : typeOrOptions;
|
|
22
22
|
|
|
23
23
|
if (!options.attributeName) {
|
|
24
|
-
options.attributeName = `on
|
|
24
|
+
options.attributeName = `swc-on-${options.type}`;
|
|
25
25
|
}
|
|
26
26
|
if (options.bubbles === undefined) options.bubbles = true;
|
|
27
27
|
if (options.composed === undefined) options.composed = true;
|
|
@@ -32,22 +32,6 @@ export function emitCustomEvent(typeOrOptions: string | EmitCustomEventOptions):
|
|
|
32
32
|
ReflectUtils.defineMetadata(EMIT_CUSTOM_EVENT_METADATA_KEY, list, constructor);
|
|
33
33
|
}
|
|
34
34
|
list.push({ propertyKey, options });
|
|
35
|
-
|
|
36
|
-
// Wrap the original method to dispatch event after execution
|
|
37
|
-
const originalMethod = descriptor.value;
|
|
38
|
-
descriptor.value = function (this: HTMLElement, ...args: any[]) {
|
|
39
|
-
const detail = originalMethod.apply(this, args);
|
|
40
|
-
|
|
41
|
-
const event = new CustomEvent(options.type, {
|
|
42
|
-
detail,
|
|
43
|
-
bubbles: options.bubbles,
|
|
44
|
-
composed: options.composed,
|
|
45
|
-
cancelable: options.cancelable
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
this.dispatchEvent(event);
|
|
49
|
-
return detail;
|
|
50
|
-
};
|
|
51
35
|
};
|
|
52
36
|
}
|
|
53
37
|
|
package/src/decorators/query.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
2
|
|
|
3
3
|
export interface QueryOptions {
|
|
4
|
-
selector?: string;
|
|
5
4
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
6
5
|
}
|
|
7
6
|
|
|
@@ -9,61 +8,49 @@ export interface QueryMetadata {
|
|
|
9
8
|
selector: string;
|
|
10
9
|
options: QueryOptions;
|
|
11
10
|
propertyKey: string | symbol;
|
|
12
|
-
isMethod: boolean;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
export const QUERY_METADATA_KEY = Symbol('simple-web-component:query');
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
/**
|
|
16
|
+
* @query decorator to inject a single element into a class property.
|
|
17
|
+
* Usage: @query('.my-class') myEl!: HTMLElement;
|
|
18
|
+
* Usage: @query('#my-id', { root: 'shadow' }) myEl!: HTMLElement;
|
|
19
|
+
*/
|
|
20
|
+
export function query(selector: string, options: QueryOptions = {}): PropertyDecorator {
|
|
21
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
|
|
22
|
+
if (descriptor !== undefined) {
|
|
23
|
+
throw new Error(`@query decorator cannot be used on methods. (Method: ${String(propertyKey)})`);
|
|
24
|
+
}
|
|
24
25
|
|
|
26
|
+
const constructor = target.constructor;
|
|
25
27
|
let queries = ReflectUtils.getMetadata<QueryMetadata[]>(QUERY_METADATA_KEY, constructor);
|
|
26
28
|
if (!queries) {
|
|
27
29
|
queries = [];
|
|
28
30
|
ReflectUtils.defineMetadata(QUERY_METADATA_KEY, queries, constructor);
|
|
29
31
|
}
|
|
30
|
-
queries.push({ selector, options, propertyKey
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Case: @query('selector') or @query({ selector: '...', ... })
|
|
55
|
-
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
56
|
-
let selector = '';
|
|
57
|
-
let options: QueryOptions = {};
|
|
58
|
-
|
|
59
|
-
if (typeof arg1 === 'string') {
|
|
60
|
-
selector = arg1;
|
|
61
|
-
} else if (typeof arg1 === 'object') {
|
|
62
|
-
options = arg1 as QueryOptions;
|
|
63
|
-
selector = options.selector || '';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
decorator(selector, options, target, propertyKey, descriptor);
|
|
32
|
+
queries.push({ selector, options, propertyKey });
|
|
33
|
+
|
|
34
|
+
Object.defineProperty(target, propertyKey, {
|
|
35
|
+
get(this: HTMLElement) {
|
|
36
|
+
if (!selector || selector === ':host') return this;
|
|
37
|
+
const r = options.root || 'auto';
|
|
38
|
+
|
|
39
|
+
if (r === 'shadow') {
|
|
40
|
+
return this.shadowRoot ? this.shadowRoot.querySelector(selector) : null;
|
|
41
|
+
}
|
|
42
|
+
if (r === 'light') {
|
|
43
|
+
return this.querySelector(selector);
|
|
44
|
+
}
|
|
45
|
+
if (r === 'all') {
|
|
46
|
+
return this.shadowRoot?.querySelector(selector) || this.querySelector(selector);
|
|
47
|
+
}
|
|
48
|
+
// auto
|
|
49
|
+
return (this.shadowRoot || this).querySelector(selector);
|
|
50
|
+
},
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true
|
|
53
|
+
});
|
|
67
54
|
};
|
|
68
55
|
}
|
|
69
56
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
2
2
|
|
|
3
3
|
export interface QueryAllOptions {
|
|
4
|
-
selector?: string;
|
|
5
4
|
root?: 'light' | 'shadow' | 'all' | 'auto';
|
|
6
5
|
}
|
|
7
6
|
|
|
@@ -9,61 +8,52 @@ export interface QueryAllMetadata {
|
|
|
9
8
|
selector: string;
|
|
10
9
|
options: QueryAllOptions;
|
|
11
10
|
propertyKey: string | symbol;
|
|
12
|
-
isMethod: boolean;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
export const QUERY_ALL_METADATA_KEY = Symbol('simple-web-component:query-all');
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
/**
|
|
16
|
+
* @queryAll decorator to inject a list of elements into a class property.
|
|
17
|
+
* Usage: @queryAll('.items') list!: NodeListOf<HTMLElement>;
|
|
18
|
+
* Usage: @queryAll('li', { root: 'shadow' }) list!: HTMLElement[];
|
|
19
|
+
*/
|
|
20
|
+
export function queryAll(selector: string, options: QueryAllOptions = {}): PropertyDecorator {
|
|
21
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: never): void => {
|
|
22
|
+
if (descriptor !== undefined) {
|
|
23
|
+
throw new Error(`@queryAll decorator cannot be used on methods. (Method: ${String(propertyKey)})`);
|
|
24
|
+
}
|
|
24
25
|
|
|
26
|
+
const constructor = target.constructor;
|
|
25
27
|
let queries = ReflectUtils.getMetadata<QueryAllMetadata[]>(QUERY_ALL_METADATA_KEY, constructor);
|
|
26
28
|
if (!queries) {
|
|
27
29
|
queries = [];
|
|
28
30
|
ReflectUtils.defineMetadata(QUERY_ALL_METADATA_KEY, queries, constructor);
|
|
29
31
|
}
|
|
30
|
-
queries.push({ selector, options, propertyKey
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
56
|
-
let selector = '';
|
|
57
|
-
let options: QueryAllOptions = {};
|
|
58
|
-
|
|
59
|
-
if (typeof arg1 === 'string') {
|
|
60
|
-
selector = arg1;
|
|
61
|
-
} else if (typeof arg1 === 'object') {
|
|
62
|
-
options = arg1 as QueryAllOptions;
|
|
63
|
-
selector = options.selector || '';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
decorator(selector, options, target, propertyKey, descriptor);
|
|
32
|
+
queries.push({ selector, options, propertyKey });
|
|
33
|
+
|
|
34
|
+
Object.defineProperty(target, propertyKey, {
|
|
35
|
+
get(this: HTMLElement) {
|
|
36
|
+
if (!selector || selector === ':host') return [this];
|
|
37
|
+
const r = options.root || 'auto';
|
|
38
|
+
|
|
39
|
+
if (r === 'shadow') {
|
|
40
|
+
return this.shadowRoot ? this.shadowRoot.querySelectorAll(selector) : [];
|
|
41
|
+
}
|
|
42
|
+
if (r === 'light') {
|
|
43
|
+
return this.querySelectorAll(selector);
|
|
44
|
+
}
|
|
45
|
+
if (r === 'all') {
|
|
46
|
+
const sNodes = this.shadowRoot ? Array.from(this.shadowRoot.querySelectorAll(selector)) : [];
|
|
47
|
+
const lNodes = Array.from(this.querySelectorAll(selector));
|
|
48
|
+
return [...sNodes, ...lNodes];
|
|
49
|
+
}
|
|
50
|
+
// auto
|
|
51
|
+
const root = this.shadowRoot || this;
|
|
52
|
+
return root.querySelectorAll(selector);
|
|
53
|
+
},
|
|
54
|
+
enumerable: true,
|
|
55
|
+
configurable: true
|
|
56
|
+
});
|
|
67
57
|
};
|
|
68
58
|
}
|
|
69
59
|
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,7 @@ export enum InjectSituationType {
|
|
|
6
6
|
APP_HOST = 'SIMPLE_WEB_COMPONENT://APPHOST',
|
|
7
7
|
APP_HOSTS = 'SIMPLE_WEB_COMPONENT://APPHOSTS',
|
|
8
8
|
HOST = 'SIMPLE_WEB_COMPONENT://HOST',
|
|
9
|
+
PARENT_HOST = 'SIMPLE_WEB_COMPONENT://PARENTHOST',
|
|
9
10
|
HOSTS = 'SIMPLE_WEB_COMPONENT://HOSTS',
|
|
10
11
|
FIRST_HOST = 'SIMPLE_WEB_COMPONENT://FIRSTHOST',
|
|
11
12
|
LAST_HOST = 'SIMPLE_WEB_COMPONENT://LASTHOST',
|
|
@@ -14,10 +15,11 @@ export enum InjectSituationType {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export type HostSet = {
|
|
17
|
-
$host: HTMLElement
|
|
18
|
-
$
|
|
19
|
-
$
|
|
20
|
-
$
|
|
18
|
+
$host: HTMLElement; // Current component itself
|
|
19
|
+
$parentHost: HTMLElement | null; // Nearest parent SWC component
|
|
20
|
+
$hosts: HTMLElement[]; // [self, parent, ..., top]
|
|
21
|
+
$firstHost: HTMLElement | null; // Top-most SWC ancestor
|
|
22
|
+
$lastHost: HTMLElement | null; // Same as $parentHost (for backward compatibility or clarity)
|
|
21
23
|
$appHost: SwcAppInterface | null;
|
|
22
24
|
$appHosts: SwcAppInterface[];
|
|
23
25
|
$firstAppHost: SwcAppInterface | null;
|