@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.
Files changed (53) hide show
  1. package/README.md +183 -92
  2. package/dist/cjs/decorators/addEventListener.js +2 -3
  3. package/dist/cjs/decorators/addEventListener.js.map +2 -2
  4. package/dist/cjs/decorators/elementDefine.js +89 -33
  5. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  6. package/dist/cjs/decorators/emitCustomEvent.js +2 -14
  7. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  8. package/dist/cjs/decorators/query.js +25 -36
  9. package/dist/cjs/decorators/query.js.map +2 -2
  10. package/dist/cjs/decorators/queryAll.js +28 -36
  11. package/dist/cjs/decorators/queryAll.js.map +2 -2
  12. package/dist/cjs/types.js +1 -0
  13. package/dist/cjs/types.js.map +2 -2
  14. package/dist/cjs/utils/Utils.js +36 -143
  15. package/dist/cjs/utils/Utils.js.map +2 -2
  16. package/dist/esm/decorators/addEventListener.js +2 -3
  17. package/dist/esm/decorators/addEventListener.js.map +2 -2
  18. package/dist/esm/decorators/elementDefine.js +89 -33
  19. package/dist/esm/decorators/elementDefine.js.map +2 -2
  20. package/dist/esm/decorators/emitCustomEvent.js +2 -14
  21. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  22. package/dist/esm/decorators/query.js +25 -36
  23. package/dist/esm/decorators/query.js.map +2 -2
  24. package/dist/esm/decorators/queryAll.js +28 -36
  25. package/dist/esm/decorators/queryAll.js.map +2 -2
  26. package/dist/esm/types.js +1 -0
  27. package/dist/esm/types.js.map +2 -2
  28. package/dist/esm/utils/Utils.js +36 -143
  29. package/dist/esm/utils/Utils.js.map +2 -2
  30. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +183 -266
  31. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +3 -3
  32. package/dist/types/decorators/addEventListener.d.ts +9 -4
  33. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  34. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  35. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  36. package/dist/types/decorators/query.d.ts +6 -5
  37. package/dist/types/decorators/query.d.ts.map +1 -1
  38. package/dist/types/decorators/queryAll.d.ts +6 -5
  39. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  40. package/dist/types/types.d.ts +3 -1
  41. package/dist/types/types.d.ts.map +1 -1
  42. package/dist/types/utils/Utils.d.ts +6 -14
  43. package/dist/types/utils/Utils.d.ts.map +1 -1
  44. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +183 -266
  45. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +3 -3
  46. package/package.json +3 -3
  47. package/src/decorators/addEventListener.ts +10 -7
  48. package/src/decorators/elementDefine.ts +86 -31
  49. package/src/decorators/emitCustomEvent.ts +5 -21
  50. package/src/decorators/query.ts +33 -46
  51. package/src/decorators/queryAll.ts +36 -46
  52. package/src/types.ts +6 -4
  53. 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.15",
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
- export function addEventListener(type: string, query?: string): MethodDecorator;
23
- export function addEventListener(options: AddEventListenerOptions): MethodDecorator;
24
- export function addEventListener(arg1: string | AddEventListenerOptions, arg2?: string): MethodDecorator {
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 (!attrName.startsWith('swc-on-')) return;
128
- const eventName = attrName.substring(7);
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 { type, query, root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault } = meta.options;
194
- const targetEls: (HTMLElement | Element | Document | ShadowRoot | Window)[] = [];
195
+ const { selector, type, options } = meta;
196
+ const { root, capture, once, passive, stopPropagation, stopImmediatePropagation, preventDefault, delegate } = options;
195
197
 
196
- if (!query) {
197
- targetEls.push(this as any);
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
- searchRoots.push((this.shadowRoot || (this as any)) as any);
202
+ bindRoots.push(this.shadowRoot || (this as any));
203
203
  } else if (r === 'light') {
204
- searchRoots.push(this as any);
204
+ bindRoots.push(this as any);
205
205
  } else if (r === 'shadow') {
206
- if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
206
+ if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
207
207
  } else if (r === 'all') {
208
- searchRoots.push(this as any);
209
- if (this.shadowRoot) searchRoots.push(this.shadowRoot as any);
208
+ bindRoots.push(this as any);
209
+ if (this.shadowRoot) bindRoots.push(this.shadowRoot as any);
210
210
  }
211
211
 
212
- searchRoots.forEach(sr => {
213
- const found = sr.querySelectorAll(query);
214
- found.forEach(el => targetEls.push(el as any));
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
- targetEls.forEach(el => {
219
- const handler = (event: Event) => {
220
- if (stopPropagation) event.stopPropagation();
221
- if (stopImmediatePropagation) event.stopImmediatePropagation();
222
- if (preventDefault) event.preventDefault();
223
-
224
- const hostSet = SwcUtils.getHostSet(this as any);
225
- this._invokeLifecycleMethod(meta.propertyKey, hostSet, [event, hostSet]);
226
- };
227
- const options = { capture, once, passive };
228
- el.addEventListener(type, handler, options);
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
- this._bindAttributeEvent(this as any, name, newVal);
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; // Default: on + type
6
- bubbles?: boolean; // Default: true
7
- composed?: boolean; // Default: true
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, descriptor: PropertyDescriptor) => {
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${options.type}`;
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
 
@@ -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
- export function query(selector: string): any;
18
- export function query(options: QueryOptions): any;
19
- export function query(target: Object, propertyKey: string | symbol): void;
20
- export function query(arg1?: string | QueryOptions | Object, arg2?: string | symbol): any {
21
- const decorator = (selector: string, options: QueryOptions, target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
22
- const isMethod = !!descriptor;
23
- const constructor = target.constructor;
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, isMethod });
31
-
32
- if (!isMethod) {
33
- Object.defineProperty(target, propertyKey, {
34
- get(this: HTMLElement) {
35
- if (!selector) return this;
36
- let searchRoot: Node = this;
37
- if (options.root === 'shadow') searchRoot = this.shadowRoot || this;
38
- else if (options.root === 'light') searchRoot = this;
39
- else searchRoot = this.shadowRoot || this; // Default: Auto (Shadow-First)
40
-
41
- return (searchRoot as HTMLElement).querySelector(selector);
42
- },
43
- enumerable: true,
44
- configurable: true
45
- });
46
- }
47
- };
48
-
49
- // Case: @query (parameterless decorator)
50
- if (arg1 && typeof arg2 === 'string') {
51
- return decorator('', {}, arg1 as Object, arg2 as string | symbol);
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
- export function queryAll(selector: string): any;
18
- export function queryAll(options: QueryAllOptions): any;
19
- export function queryAll(target: Object, propertyKey: string | symbol): void;
20
- export function queryAll(arg1?: string | QueryAllOptions | Object, arg2?: string | symbol): any {
21
- const decorator = (selector: string, options: QueryAllOptions, target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
22
- const isMethod = !!descriptor;
23
- const constructor = target.constructor;
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, isMethod });
31
-
32
- if (!isMethod) {
33
- Object.defineProperty(target, propertyKey, {
34
- get(this: HTMLElement) {
35
- if (!selector) return [this];
36
- let searchRoot: Node = this;
37
- if (options.root === 'shadow') searchRoot = this.shadowRoot || this;
38
- else if (options.root === 'light') searchRoot = this;
39
- else searchRoot = this.shadowRoot || this; // Default: Auto (Shadow-First)
40
-
41
- return (searchRoot as HTMLElement).querySelectorAll(selector);
42
- },
43
- enumerable: true,
44
- configurable: true
45
- });
46
- }
47
- };
48
-
49
- // Case: @queryAll (parameterless decorator)
50
- if (arg1 && typeof arg2 === 'string') {
51
- return decorator('', {}, arg1 as Object, arg2 as string | symbol);
52
- }
53
-
54
- // Case: @queryAll('selector') or @queryAll({ selector: '...', ... })
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 | null;
18
- $hosts: HTMLElement[];
19
- $firstHost: HTMLElement | null;
20
- $lastHost: HTMLElement | null;
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;