@dooboostore/simple-web-component 1.0.47 → 1.0.49

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 (233) hide show
  1. package/README.md +223 -27
  2. package/dist/cjs/config/config.js +1 -1
  3. package/dist/cjs/config/config.js.map +2 -2
  4. package/dist/cjs/decorators/addEventListener.js +412 -16
  5. package/dist/cjs/decorators/addEventListener.js.map +3 -3
  6. package/dist/cjs/decorators/applyAttribute.js +25 -0
  7. package/dist/cjs/decorators/applyAttribute.js.map +2 -2
  8. package/dist/cjs/decorators/applyClass.js +49 -18
  9. package/dist/cjs/decorators/applyClass.js.map +3 -3
  10. package/dist/cjs/decorators/applyNode.js +67 -1
  11. package/dist/cjs/decorators/applyNode.js.map +3 -3
  12. package/dist/cjs/decorators/applyProperty.js +101 -3
  13. package/dist/cjs/decorators/applyProperty.js.map +2 -2
  14. package/dist/cjs/decorators/applyStyle.js +36 -5
  15. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  16. package/dist/cjs/decorators/changedAttribute.js +39 -0
  17. package/dist/cjs/decorators/changedAttribute.js.map +2 -2
  18. package/dist/cjs/decorators/elementDefine.js +244 -225
  19. package/dist/cjs/decorators/elementDefine.js.map +3 -3
  20. package/dist/cjs/decorators/emitCustomEvent.js +55 -0
  21. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  22. package/dist/cjs/decorators/index.js +5 -1
  23. package/dist/cjs/decorators/index.js.map +2 -2
  24. package/dist/cjs/decorators/intersectionObserver.js +209 -0
  25. package/dist/cjs/decorators/intersectionObserver.js.map +7 -0
  26. package/dist/cjs/decorators/mutationObserver.js +244 -0
  27. package/dist/cjs/decorators/mutationObserver.js.map +7 -0
  28. package/dist/cjs/decorators/query.js +129 -102
  29. package/dist/cjs/decorators/query.js.map +2 -2
  30. package/dist/cjs/decorators/resizeObserver.js +191 -0
  31. package/dist/cjs/decorators/resizeObserver.js.map +7 -0
  32. package/dist/cjs/decorators/setInterval.js +82 -0
  33. package/dist/cjs/decorators/setInterval.js.map +7 -0
  34. package/dist/cjs/decorators/setTimeout.js +82 -0
  35. package/dist/cjs/decorators/setTimeout.js.map +7 -0
  36. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js +9 -0
  37. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
  38. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js +9 -0
  39. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  40. package/dist/cjs/examples/RxJSOperatorsExample.js +285 -0
  41. package/dist/cjs/examples/RxJSOperatorsExample.js.map +7 -0
  42. package/dist/cjs/types/index.js.map +1 -1
  43. package/dist/esm/config/config.js +1 -1
  44. package/dist/esm/config/config.js.map +2 -2
  45. package/dist/esm/decorators/addEventListener.js +413 -16
  46. package/dist/esm/decorators/addEventListener.js.map +3 -3
  47. package/dist/esm/decorators/applyAttribute.js +25 -0
  48. package/dist/esm/decorators/applyAttribute.js.map +2 -2
  49. package/dist/esm/decorators/applyClass.js +49 -18
  50. package/dist/esm/decorators/applyClass.js.map +3 -3
  51. package/dist/esm/decorators/applyNode.js +67 -1
  52. package/dist/esm/decorators/applyNode.js.map +3 -3
  53. package/dist/esm/decorators/applyProperty.js +101 -3
  54. package/dist/esm/decorators/applyProperty.js.map +2 -2
  55. package/dist/esm/decorators/applyStyle.js +36 -5
  56. package/dist/esm/decorators/applyStyle.js.map +2 -2
  57. package/dist/esm/decorators/changedAttribute.js +40 -0
  58. package/dist/esm/decorators/changedAttribute.js.map +2 -2
  59. package/dist/esm/decorators/elementDefine.js +249 -230
  60. package/dist/esm/decorators/elementDefine.js.map +3 -3
  61. package/dist/esm/decorators/emitCustomEvent.js +55 -0
  62. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  63. package/dist/esm/decorators/index.js +5 -1
  64. package/dist/esm/decorators/index.js.map +2 -2
  65. package/dist/esm/decorators/intersectionObserver.js +191 -0
  66. package/dist/esm/decorators/intersectionObserver.js.map +7 -0
  67. package/dist/esm/decorators/mutationObserver.js +226 -0
  68. package/dist/esm/decorators/mutationObserver.js.map +7 -0
  69. package/dist/esm/decorators/query.js +129 -102
  70. package/dist/esm/decorators/query.js.map +2 -2
  71. package/dist/esm/decorators/resizeObserver.js +173 -0
  72. package/dist/esm/decorators/resizeObserver.js.map +7 -0
  73. package/dist/esm/decorators/setInterval.js +64 -0
  74. package/dist/esm/decorators/setInterval.js.map +7 -0
  75. package/dist/esm/decorators/setTimeout.js +64 -0
  76. package/dist/esm/decorators/setTimeout.js.map +7 -0
  77. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +9 -0
  78. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
  79. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js +9 -0
  80. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
  81. package/dist/esm/examples/RxJSOperatorsExample.js +266 -0
  82. package/dist/esm/examples/RxJSOperatorsExample.js.map +7 -0
  83. package/dist/esm/types/index.js.map +1 -1
  84. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +2413 -889
  85. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  86. package/dist/types/config/config.d.ts.map +1 -1
  87. package/dist/types/decorators/addEventListener.d.ts +93 -23
  88. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  89. package/dist/types/decorators/applyAttribute.d.ts +22 -34
  90. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  91. package/dist/types/decorators/applyClass.d.ts +34 -34
  92. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  93. package/dist/types/decorators/applyNode.d.ts +30 -20
  94. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  95. package/dist/types/decorators/applyProperty.d.ts +57 -34
  96. package/dist/types/decorators/applyProperty.d.ts.map +1 -1
  97. package/dist/types/decorators/applyStyle.d.ts +25 -12
  98. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  99. package/dist/types/decorators/changedAttribute.d.ts +11 -0
  100. package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
  101. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  102. package/dist/types/decorators/emitCustomEvent.d.ts +28 -4
  103. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  104. package/dist/types/decorators/index.d.ts +5 -1
  105. package/dist/types/decorators/index.d.ts.map +1 -1
  106. package/dist/types/decorators/intersectionObserver.d.ts +49 -0
  107. package/dist/types/decorators/intersectionObserver.d.ts.map +1 -0
  108. package/dist/types/decorators/mutationObserver.d.ts +46 -0
  109. package/dist/types/decorators/mutationObserver.d.ts.map +1 -0
  110. package/dist/types/decorators/query.d.ts +63 -8
  111. package/dist/types/decorators/query.d.ts.map +1 -1
  112. package/dist/types/decorators/resizeObserver.d.ts +48 -0
  113. package/dist/types/decorators/resizeObserver.d.ts.map +1 -0
  114. package/dist/types/decorators/setInterval.d.ts +29 -0
  115. package/dist/types/decorators/setInterval.d.ts.map +1 -0
  116. package/dist/types/decorators/setTimeout.d.ts +29 -0
  117. package/dist/types/decorators/setTimeout.d.ts.map +1 -0
  118. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +7 -1
  119. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -1
  120. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +6 -0
  121. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
  122. package/dist/types/examples/RxJSOperatorsExample.d.ts +94 -0
  123. package/dist/types/examples/RxJSOperatorsExample.d.ts.map +1 -0
  124. package/dist/types/types/index.d.ts +104 -0
  125. package/dist/types/types/index.d.ts.map +1 -1
  126. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +2413 -889
  127. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  128. package/package.json +8 -6
  129. package/src/config/config.ts +1 -1
  130. package/src/decorators/addEventListener.ts +426 -23
  131. package/src/decorators/applyAttribute.ts +50 -36
  132. package/src/decorators/applyClass.ts +76 -26
  133. package/src/decorators/applyNode.ts +114 -36
  134. package/src/decorators/applyProperty.ts +152 -40
  135. package/src/decorators/applyStyle.ts +64 -17
  136. package/src/decorators/changedAttribute.ts +46 -0
  137. package/src/decorators/elementDefine.ts +301 -317
  138. package/src/decorators/emitCustomEvent.ts +73 -5
  139. package/src/decorators/index.ts +5 -1
  140. package/src/decorators/intersectionObserver.ts +259 -0
  141. package/src/decorators/mutationObserver.ts +304 -0
  142. package/src/decorators/query.ts +182 -117
  143. package/src/decorators/resizeObserver.ts +235 -0
  144. package/src/decorators/setInterval.ts +80 -0
  145. package/src/decorators/setTimeout.ts +80 -0
  146. package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +17 -0
  147. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +19 -0
  148. package/src/examples/RxJSOperatorsExample.ts +279 -0
  149. package/src/types/index.ts +141 -0
  150. package/dist/cjs/decorators/queryAll.js +0 -181
  151. package/dist/cjs/decorators/queryAll.js.map +0 -7
  152. package/dist/cjs/elements/tt/SwcAppMixin.js +0 -1
  153. package/dist/cjs/elements/tt/SwcAppMixin.js.map +0 -7
  154. package/dist/cjs/elements/tt/SwcAsync.js +0 -1
  155. package/dist/cjs/elements/tt/SwcAsync.js.map +0 -7
  156. package/dist/cjs/elements/tt/SwcAsyncTemplate.js +0 -1
  157. package/dist/cjs/elements/tt/SwcAsyncTemplate.js.map +0 -7
  158. package/dist/cjs/elements/tt/SwcChoose.js +0 -1
  159. package/dist/cjs/elements/tt/SwcChoose.js.map +0 -7
  160. package/dist/cjs/elements/tt/SwcChooseTemplate.js +0 -1
  161. package/dist/cjs/elements/tt/SwcChooseTemplate.js.map +0 -7
  162. package/dist/cjs/elements/tt/SwcIf.js +0 -1
  163. package/dist/cjs/elements/tt/SwcIf.js.map +0 -7
  164. package/dist/cjs/elements/tt/SwcIfTemplate.js +0 -1
  165. package/dist/cjs/elements/tt/SwcIfTemplate.js.map +0 -7
  166. package/dist/cjs/elements/tt/SwcLoop.js +0 -1
  167. package/dist/cjs/elements/tt/SwcLoop.js.map +0 -7
  168. package/dist/cjs/elements/tt/SwcLoopTemplate.js +0 -1
  169. package/dist/cjs/elements/tt/SwcLoopTemplate.js.map +0 -7
  170. package/dist/cjs/elements/tt/SwcSubTemplates.js +0 -1
  171. package/dist/cjs/elements/tt/SwcSubTemplates.js.map +0 -7
  172. package/dist/cjs/elements/tt/register.js +0 -1
  173. package/dist/cjs/elements/tt/register.js.map +0 -7
  174. package/dist/esm/decorators/queryAll.js +0 -162
  175. package/dist/esm/decorators/queryAll.js.map +0 -7
  176. package/dist/esm/elements/tt/SwcAppMixin.js +0 -1
  177. package/dist/esm/elements/tt/SwcAppMixin.js.map +0 -7
  178. package/dist/esm/elements/tt/SwcAsync.js +0 -1
  179. package/dist/esm/elements/tt/SwcAsync.js.map +0 -7
  180. package/dist/esm/elements/tt/SwcAsyncTemplate.js +0 -1
  181. package/dist/esm/elements/tt/SwcAsyncTemplate.js.map +0 -7
  182. package/dist/esm/elements/tt/SwcChoose.js +0 -1
  183. package/dist/esm/elements/tt/SwcChoose.js.map +0 -7
  184. package/dist/esm/elements/tt/SwcChooseTemplate.js +0 -1
  185. package/dist/esm/elements/tt/SwcChooseTemplate.js.map +0 -7
  186. package/dist/esm/elements/tt/SwcIf.js +0 -1
  187. package/dist/esm/elements/tt/SwcIf.js.map +0 -7
  188. package/dist/esm/elements/tt/SwcIfTemplate.js +0 -1
  189. package/dist/esm/elements/tt/SwcIfTemplate.js.map +0 -7
  190. package/dist/esm/elements/tt/SwcLoop.js +0 -1
  191. package/dist/esm/elements/tt/SwcLoop.js.map +0 -7
  192. package/dist/esm/elements/tt/SwcLoopTemplate.js +0 -1
  193. package/dist/esm/elements/tt/SwcLoopTemplate.js.map +0 -7
  194. package/dist/esm/elements/tt/SwcSubTemplates.js +0 -1
  195. package/dist/esm/elements/tt/SwcSubTemplates.js.map +0 -7
  196. package/dist/esm/elements/tt/register.js +0 -1
  197. package/dist/esm/elements/tt/register.js.map +0 -7
  198. package/dist/types/decorators/queryAll.d.ts +0 -22
  199. package/dist/types/decorators/queryAll.d.ts.map +0 -1
  200. package/dist/types/elements/tt/SwcAppMixin.d.ts +0 -1
  201. package/dist/types/elements/tt/SwcAppMixin.d.ts.map +0 -1
  202. package/dist/types/elements/tt/SwcAsync.d.ts +0 -1
  203. package/dist/types/elements/tt/SwcAsync.d.ts.map +0 -1
  204. package/dist/types/elements/tt/SwcAsyncTemplate.d.ts +0 -1
  205. package/dist/types/elements/tt/SwcAsyncTemplate.d.ts.map +0 -1
  206. package/dist/types/elements/tt/SwcChoose.d.ts +0 -1
  207. package/dist/types/elements/tt/SwcChoose.d.ts.map +0 -1
  208. package/dist/types/elements/tt/SwcChooseTemplate.d.ts +0 -1
  209. package/dist/types/elements/tt/SwcChooseTemplate.d.ts.map +0 -1
  210. package/dist/types/elements/tt/SwcIf.d.ts +0 -1
  211. package/dist/types/elements/tt/SwcIf.d.ts.map +0 -1
  212. package/dist/types/elements/tt/SwcIfTemplate.d.ts +0 -1
  213. package/dist/types/elements/tt/SwcIfTemplate.d.ts.map +0 -1
  214. package/dist/types/elements/tt/SwcLoop.d.ts +0 -1
  215. package/dist/types/elements/tt/SwcLoop.d.ts.map +0 -1
  216. package/dist/types/elements/tt/SwcLoopTemplate.d.ts +0 -1
  217. package/dist/types/elements/tt/SwcLoopTemplate.d.ts.map +0 -1
  218. package/dist/types/elements/tt/SwcSubTemplates.d.ts +0 -1
  219. package/dist/types/elements/tt/SwcSubTemplates.d.ts.map +0 -1
  220. package/dist/types/elements/tt/register.d.ts +0 -1
  221. package/dist/types/elements/tt/register.d.ts.map +0 -1
  222. package/src/decorators/queryAll.ts +0 -186
  223. package/src/elements/tt/SwcAppMixin.ts +0 -13
  224. package/src/elements/tt/SwcAsync.ts +0 -139
  225. package/src/elements/tt/SwcAsyncTemplate.ts +0 -126
  226. package/src/elements/tt/SwcChoose.ts +0 -182
  227. package/src/elements/tt/SwcChooseTemplate.ts +0 -229
  228. package/src/elements/tt/SwcIf.ts +0 -145
  229. package/src/elements/tt/SwcIfTemplate.ts +0 -158
  230. package/src/elements/tt/SwcLoop.ts +0 -222
  231. package/src/elements/tt/SwcLoopTemplate.ts +0 -255
  232. package/src/elements/tt/SwcSubTemplates.ts +0 -6
  233. package/src/elements/tt/register.ts +0 -720
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dooboostore/simple-web-component",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -13,7 +13,7 @@
13
13
  ]
14
14
  }
15
15
  },
16
- "description": "Simple Web Component library based on dom-render philosophy",
16
+ "description": "Simple Web Component library",
17
17
  "sideEffects": [
18
18
  "./dist/esm/elements/*.js",
19
19
  "./dist/esm/elements/is/*.js",
@@ -67,10 +67,7 @@
67
67
  },
68
68
  "author": "dooboostore <dooboostore@gmail.com> (https://github.com/dooboostore)",
69
69
  "peerDependencies": {
70
- "reflect-metadata": "^0.2.2",
71
- "@dooboostore/core": "1.0.32",
72
- "@dooboostore/core-web": "1.0.12",
73
- "@dooboostore/simple-boot": "1.0.56"
70
+ "reflect-metadata": "^0.2.2"
74
71
  },
75
72
  "devDependencies": {
76
73
  "@babel/core": "^7.24.0",
@@ -96,6 +93,11 @@
96
93
  "webpack-cli": "^5.1.4",
97
94
  "webpack-dev-server": "^5.2.0"
98
95
  },
96
+ "dependencies": {
97
+ "@dooboostore/core-web": "1.0.13",
98
+ "@dooboostore/core": "1.0.32",
99
+ "@dooboostore/simple-boot": "1.0.57"
100
+ },
99
101
  "scripts": {
100
102
  "clean": "rm -rf dist",
101
103
  "build:esm": "node build.mjs esm",
@@ -58,6 +58,6 @@ export const HTML_TAG_ENTRIES: [string, string][] = [
58
58
  ['HTMLBodyElement', 'body']
59
59
  ];
60
60
 
61
- export const DOM_EVENT_NAMES = ['click', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'contextmenu', 'keydown', 'keypress', 'keyup', 'blur', 'change', 'focus', 'focusin', 'focusout', 'input', 'invalid', 'reset', 'search', 'select', 'submit', 'drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop', 'copy', 'cut', 'paste', 'abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting', 'scroll', 'resize', 'load', 'unload', 'hashchange', 'connected'];
61
+ export const DOM_EVENT_NAMES = ['click', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'contextmenu', 'keydown', 'keypress', 'keyup', 'blur', 'change', 'focus', 'focusin', 'focusout', 'input', 'invalid', 'reset', 'search', 'select', 'submit', 'drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop', 'copy', 'cut', 'paste', 'abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting', 'scroll', 'resize', 'load', 'hashchange', 'connected'];
62
62
 
63
63
  export const BROWSER_GLOBALS = ['window', 'document', 'Node', 'Element', 'HTMLElement', 'DocumentFragment', 'CustomEvent', 'customElements', 'NodeFilter', ...HTML_TAG_ENTRIES.map(([className]) => className)];
@@ -1,5 +1,5 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
- import { SpecialSelector, SwcQueryOptions, HelperHostSet } from '../types';
2
+ import { SpecialSelector, SwcQueryOptions, HelperHostSet, SwcFnSelector, SwcSelector } from '../types';
3
3
 
4
4
  export interface AddEventListenerBaseOptions<TEvent extends Event = Event> extends EventListenerOptions {
5
5
  capture?: boolean;
@@ -9,32 +9,68 @@ export interface AddEventListenerBaseOptions<TEvent extends Event = Event> exten
9
9
  stopImmediatePropagation?: boolean;
10
10
  preventDefault?: boolean;
11
11
  // removeOnDisconnected?: boolean;
12
- delegate?: boolean;
12
+ // delegate는 문자열 셀렉터 전용 → AddEventListenerQueryOptions에만 존재
13
13
  filter?: (target: Event | CustomEvent, meta:{currentThis: any, helper: HelperHostSet}) => boolean;
14
+ // 리스너 제거(disconnected 또는 unmount) 시 호출되는 콜백. 첫 번째 인자는 바인딩된 타겟 element, 두 번째는 이 옵션이 속한 전체 옵션 객체(Base + SwcQuery + delegate).
15
+ removeListener?: (target: Element, optionValue: AddEventListenerQueryOptions<TEvent>) => void;
14
16
  // RxJS operator options
15
17
  debounceTime?: number;
16
18
  throttleTime?: number;
17
19
  distinctUntilChanged?: boolean | ((prev: TEvent, curr: TEvent) => boolean);
18
20
  }
19
21
 
20
- export type EventListenerSelector = string | ((currentThis: any, helper: HelperHostSet) => NodeList | Element | Element[] | null);
22
+ export type EventListenerFnSelector = SwcFnSelector;
23
+ export type EventListenerSelector = SwcSelector;
21
24
 
22
25
  export interface AddEventListenerMetadata<TEvent extends Event = Event> {
23
26
  propertyKey: string | symbol;
24
27
  selector: EventListenerSelector;
25
28
  type: string;
26
- options: AddEventListenerBaseOptions<TEvent> & SwcQueryOptions & { delegate?: boolean };
29
+ options: AddEventListenerQueryOptions<TEvent>;
27
30
  }
28
31
 
29
32
  export const ADD_EVENT_LISTENER_METADATA_KEY = Symbol.for('simple-web-component:add-event-listener');
30
33
 
31
- export function addEventListener<TEvent extends Event = Event>(target: SpecialSelector, type: string, options?: AddEventListenerBaseOptions<TEvent>): MethodDecorator;
32
- export function addEventListener<TEvent extends Event = Event>(selector: EventListenerSelector, type: string, options?: AddEventListenerBaseOptions<TEvent> & SwcQueryOptions & { delegate?: boolean }): MethodDecorator;
34
+ // root + delegate 허용 — 문자열 셀렉터는 컴포넌트 DOM 트리 안에서 탐색/델리게이션하므로 의미 있음
35
+ export type AddEventListenerQueryOptions<TEvent extends Event = Event> = AddEventListenerBaseOptions<TEvent> & SwcQueryOptions & { delegate?: boolean | 'this' | 'mutation' };
36
+ // root·delegate 비허용 — 함수 셀렉터는 이미 요소를 직접 반환하므로 root·delegate가 무의미함
37
+ export type AddEventListenerNonQueryOptions<TEvent extends Event = Event> = AddEventListenerBaseOptions<TEvent>;
38
+
39
+ // 셀렉터 종류에 따라 옵션 타입을 분기
40
+ export type AddEventListenerOptionsOf<S extends EventListenerSelector, TEvent extends Event = Event> =
41
+ S extends string ? AddEventListenerQueryOptions<TEvent> : AddEventListenerNonQueryOptions<TEvent>;
42
+
43
+ export function addEventListener<TEvent extends Event = Event>(target: SpecialSelector, type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator;
44
+ export function addEventListener<TEvent extends Event = Event>(selector: string, type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator;
45
+ export function addEventListener<TEvent extends Event = Event>(selector: string, type: string[], options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator;
46
+ export function addEventListener<TEvent extends Event = Event>(selector: EventListenerFnSelector, type: string, options?: AddEventListenerNonQueryOptions<TEvent>): MethodDecorator;
47
+ /**
48
+ * @addEventListener(type, options?) — 셀렉터 생략 시 $this(컴포넌트 자신)로 바인딩
49
+ */
50
+ export function addEventListener<TEvent extends Event = Event>(type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator;
51
+ export function addEventListener<TEvent extends Event = Event>(type: string[], options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator;
33
52
  /**
34
53
  * @addEventListener decorator to bind events to elements.
35
54
  */
36
- export function addEventListener<TEvent extends Event = Event>(selectorOrTarget: EventListenerSelector, type: string, options: any = {}): MethodDecorator {
55
+ export function addEventListener<TEvent extends Event = Event>(selectorOrType: EventListenerSelector | string | string[], typeOrOptions?: string | string[] | AddEventListenerQueryOptions<TEvent>, maybeOptions?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
37
56
  return (targetObj: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
57
+ let selector: EventListenerSelector;
58
+ let types: string[];
59
+ let opts: any = {};
60
+
61
+ if (typeof typeOrOptions === 'string' || Array.isArray(typeOrOptions)) {
62
+ // (selector, type|[types][, options]) form — 기존 호환
63
+ selector = selectorOrType as EventListenerSelector;
64
+ types = Array.isArray(typeOrOptions) ? typeOrOptions : [typeOrOptions];
65
+ opts = maybeOptions ?? {};
66
+ } else {
67
+ // (type|[types][, options]) form — selector 기본값 $this
68
+ selector = '$this';
69
+ if (Array.isArray(selectorOrType)) types = selectorOrType;
70
+ else types = [selectorOrType as string];
71
+ opts = typeOrOptions ?? {};
72
+ }
73
+
38
74
  const constructor = targetObj.constructor;
39
75
 
40
76
  let listeners = ReflectUtils.getMetadata<AddEventListenerMetadata<TEvent>[]>(ADD_EVENT_LISTENER_METADATA_KEY, constructor);
@@ -43,54 +79,421 @@ export function addEventListener<TEvent extends Event = Event>(selectorOrTarget:
43
79
  ReflectUtils.defineMetadata(ADD_EVENT_LISTENER_METADATA_KEY, listeners, constructor);
44
80
  }
45
81
 
46
- listeners.push({ propertyKey, selector: selectorOrTarget, type, options });
82
+ for (const type of types) listeners.push({ propertyKey, selector, type, options: opts });
47
83
  };
48
84
  }
49
85
 
50
86
 
51
- export function addEventListenerDelegateLightDom<TEvent extends Event = Event>(selector: EventListenerSelector, type: string, options?: Omit<AddEventListenerBaseOptions<TEvent>, 'delegate'>): MethodDecorator {
87
+ export function addEventListenerDelegateLight<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
52
88
  return addEventListener<TEvent>(selector, type, {...options??{}, root:'light', delegate: true});
53
89
  }
54
90
 
55
- export function addEventListenerDelegateShadowDom<TEvent extends Event = Event>(selector: EventListenerSelector, type: string, options?: Omit<AddEventListenerBaseOptions<TEvent>, 'delegate'>): MethodDecorator {
91
+ export function addEventListenerDelegateShadow<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
56
92
  return addEventListener<TEvent>(selector, type, {...options??{}, root:'shadow', delegate: true});
57
93
  }
58
- export function addEventListenerDelegateAllDom<TEvent extends Event = Event>(selector: EventListenerSelector, type: string, options?: Omit<AddEventListenerBaseOptions<TEvent>, 'delegate'>): MethodDecorator {
94
+ export function addEventListenerDelegateAll<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
59
95
  return addEventListener<TEvent>(selector, type, {...options??{}, root:'all', delegate: true});
60
96
  }
61
- export function addEventListenerDelegate<TEvent extends Event = Event>(selector: EventListenerSelector, type: string, options?: Omit<AddEventListenerBaseOptions<TEvent>, 'delegate'>): MethodDecorator {
97
+ export function addEventListenerDelegate<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
62
98
  return addEventListener<TEvent>(selector, type, {...options??{}, root:'auto', delegate: true});
63
99
  }
64
100
 
101
+ export function addEventListenerMutationLight<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
102
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'light', delegate: 'mutation'});
103
+ }
104
+
105
+ export function addEventListenerMutationShadow<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
106
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'shadow', delegate: 'mutation'});
107
+ }
108
+
109
+ export function addEventListenerMutationAll<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
110
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'all', delegate: 'mutation'});
111
+ }
112
+
113
+ export function addEventListenerMutation<TEvent extends Event = Event>(selector: string, type: string, options?: Omit<AddEventListenerQueryOptions<TEvent>, 'delegate'>): MethodDecorator {
114
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'auto', delegate: 'mutation'});
115
+ }
116
+
117
+ // ─── root별 일반 헬퍼 (delegate 없음, 문자열 셀렉터 전용) ───
118
+
119
+ export function addEventListenerLight<TEvent extends Event = Event>(selector: string, type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
120
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'light'});
121
+ }
122
+
123
+ export function addEventListenerShadow<TEvent extends Event = Event>(selector: string, type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
124
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'shadow'});
125
+ }
126
+
127
+ export function addEventListenerAll<TEvent extends Event = Event>(selector: string, type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
128
+ return addEventListener<TEvent>(selector, type, {...options??{}, root:'all'});
129
+ }
130
+
65
131
  /**
66
132
  * @addEventListenerThis decorator - simplified version of @addEventListener for $this selector
67
133
  */
68
-
69
- // --- Aliases: event... ---
70
- export const event = addEventListener;
71
- export const eventDelegateLightDom = addEventListenerDelegateLightDom;
72
- export const eventDelegateShadowDom = addEventListenerDelegateShadowDom;
73
- export const eventDelegateAllDom = addEventListenerDelegateAllDom;
74
- export const eventDelegate = addEventListenerDelegate;
75
- export function addEventListenerThis<TEvent extends Event = Event>(type: string, options?: AddEventListenerBaseOptions<TEvent> & SwcQueryOptions): MethodDecorator {
134
+ export function addEventListenerThis<TEvent extends Event = Event>(type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
76
135
  return addEventListener<TEvent>('$this', type, options);
77
136
  }
78
137
 
79
138
  /**
80
139
  * @addEventListenerAppHost decorator - simplified version of @addEventListener for $appHost selector
81
140
  */
82
- export function addEventListenerAppHost<TEvent extends Event = Event>(type: string, options?: AddEventListenerBaseOptions<TEvent> & SwcQueryOptions): MethodDecorator {
141
+ export function addEventListenerAppHost<TEvent extends Event = Event>(type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
83
142
  return addEventListener<TEvent>('$appHost', type, options);
84
143
  }
85
144
 
86
- export function addEventListenerWindow<TEvent extends Event = Event>(type: string, options?: AddEventListenerBaseOptions<TEvent> & SwcQueryOptions): MethodDecorator {
145
+ export function addEventListenerWindow<TEvent extends Event = Event>(type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
87
146
  return addEventListener<TEvent>('$window', type, options);
88
147
  }
89
- export function addEventListenerDocument<TEvent extends Event = Event>(type: string, options?: AddEventListenerBaseOptions<TEvent> & SwcQueryOptions): MethodDecorator {
148
+ export function addEventListenerDocument<TEvent extends Event = Event>(type: string, options?: AddEventListenerQueryOptions<TEvent>): MethodDecorator {
90
149
  return addEventListener<TEvent>('$document', type, options);
91
150
  }
92
151
 
152
+ // --- Aliases: event... ---
153
+ export const event = addEventListener;
154
+ export const eventDelegateLight = addEventListenerDelegateLight;
155
+ export const eventDelegateShadow = addEventListenerDelegateShadow;
156
+ export const eventDelegateAll = addEventListenerDelegateAll;
157
+ export const eventDelegate = addEventListenerDelegate;
158
+ export const eventMutationLight = addEventListenerMutationLight;
159
+ export const eventMutationShadow = addEventListenerMutationShadow;
160
+ export const eventMutationAll = addEventListenerMutationAll;
161
+ export const eventMutation = addEventListenerMutation;
162
+ export const eventLight = addEventListenerLight;
163
+ export const eventShadow = addEventListenerShadow;
164
+ export const eventAll = addEventListenerAll;
165
+ export const eventAppHost = addEventListenerAppHost;
166
+ export const eventWindow = addEventListenerWindow;
167
+ export const eventDocument = addEventListenerDocument;
168
+ export const eventThis = addEventListenerThis;
169
+
93
170
  export const getAddEventListenerMetadata = (target: any): AddEventListenerMetadata<Event>[] | undefined => {
94
171
  const constructor = target instanceof Function ? target : target.constructor;
95
172
  return ReflectUtils.getMetadata(ADD_EVENT_LISTENER_METADATA_KEY, constructor);
96
173
  };
174
+
175
+ // ─────────────────────────────────────────────────────────────────────────────
176
+ // EventListenerLifeCycler
177
+ // ─────────────────────────────────────────────────────────────────────────────
178
+ import { ElementDefineLifeCycler, MutationObserverSetEntry, OnConnectedResult } from '../types';
179
+ import { Subject } from '@dooboostore/core';
180
+ import { debounceTime, distinctUntilChanged, throttleTime } from '@dooboostore/core/message/operators';
181
+ import { SwcUtils } from '../utils/Utils';
182
+
183
+ export interface BoundListenerEntry {
184
+ target: EventTarget;
185
+ type: string;
186
+ handler: EventListener;
187
+ options: AddEventListenerOptions;
188
+ onRemoves?: Array<{ fn: (target: EventTarget, optionValue: unknown) => void; opts: unknown }>;
189
+ subscription?: { unsubscribe: () => void };
190
+ meta?: AddEventListenerMetadata<Event>;
191
+ }
192
+
193
+ export class EventListenerLifeCycler implements ElementDefineLifeCycler {
194
+ /** 인스턴스별 boundListeners 상태 (시클러는 elementDefine 시 1회 생성되어 공유되므로) */
195
+ private readonly boundListenersMap = new WeakMap<Element, BoundListenerEntry[]>();
196
+ /** 인스턴스별 delegate:'mutation' 바인딩 추적 WeakSet — MutationObserverLifeCycler 에서 참조 */
197
+ private readonly eventBoundSetsMap = new WeakMap<Element, Map<AddEventListenerMetadata, WeakSet<Element>>>();
198
+
199
+ // ── 외부(MutationObserverLifeCycler)에서 직접 사용하는 공개 헬퍼 ──
200
+
201
+ /** 인스턴스별 boundListeners 를 반환한다 (없으면 생성). */
202
+ getBoundListeners(inst: Element): BoundListenerEntry[] {
203
+ let list = this.boundListenersMap.get(inst);
204
+ if (!list) { list = []; this.boundListenersMap.set(inst, list); }
205
+ return list;
206
+ }
207
+
208
+ /** 인스턴스별 eventBoundSets 를 반환한다 (없으면 생성). */
209
+ getEventBoundSets(inst: Element): Map<AddEventListenerMetadata, WeakSet<Element>> {
210
+ let map = this.eventBoundSetsMap.get(inst);
211
+ if (!map) { map = new Map(); this.eventBoundSetsMap.set(inst, map); }
212
+ return map;
213
+ }
214
+
215
+ /**
216
+ * target 에 meta 이벤트를 직접 바인딩한다.
217
+ * delegate:'mutation' 초기/동적 바인딩에서 호출된다.
218
+ */
219
+ bindDirect(helperHostSet: HelperHostSet, target: EventTarget, meta: AddEventListenerMetadata): void {
220
+ const inst = helperHostSet.$this;
221
+ const currentWin = helperHostSet.$w;
222
+ const { type, options } = meta;
223
+ const opts = { capture: options.capture, once: options.once, passive: options.passive };
224
+
225
+ const handler = async (event: Event) => {
226
+ if (options.filter) {
227
+ const helper = SwcUtils.getHelperAndHostSet(currentWin, target as HTMLElement);
228
+ if (!options.filter(event, { currentThis: inst, helper })) return;
229
+ }
230
+ if (options.stopPropagation) event.stopPropagation();
231
+ if (options.stopImmediatePropagation) event.stopImmediatePropagation();
232
+ if (options.preventDefault) event.preventDefault();
233
+ const currentHostSet = SwcUtils.getHostSet(inst);
234
+ await inst[meta.propertyKey](event, { currentHostSet, $matchedElement: event.currentTarget }, { event, ...currentHostSet, $el: target, $root: target });
235
+ };
236
+
237
+ const eventSubject = new Subject<Event>();
238
+ let eventStream: any = eventSubject;
239
+ if (options.debounceTime && options.debounceTime > 0)
240
+ eventStream = eventStream.pipe(debounceTime(options.debounceTime));
241
+ if (options.throttleTime && options.throttleTime > 0)
242
+ eventStream = eventStream.pipe(throttleTime(options.throttleTime));
243
+ if (options.distinctUntilChanged !== undefined && options.distinctUntilChanged !== false) {
244
+ eventStream = typeof options.distinctUntilChanged === 'function'
245
+ ? eventStream.pipe(distinctUntilChanged(options.distinctUntilChanged))
246
+ : eventStream.pipe(distinctUntilChanged());
247
+ }
248
+
249
+ const subscription = eventStream.subscribe({
250
+ next: (e: Event) => handler(e).catch((err: any) => console.error('Event handler error:', err)),
251
+ error: (err: any) => console.error('Event stream error:', err),
252
+ });
253
+
254
+ const wrappedHandler = (event: Event) => eventSubject.next(event);
255
+ target.addEventListener(type, wrappedHandler, opts);
256
+
257
+ const onRemoves: Array<{ fn: any; opts: any }> =
258
+ options.removeListener ? [{ fn: options.removeListener, opts: options }] : [];
259
+ this.getBoundListeners(inst).push({ target, type, handler: wrappedHandler, options: opts, subscription, onRemoves, meta });
260
+ }
261
+
262
+ /** target + meta 조합 리스너를 제거한다. MutationObserver 추적 요소 제거 시 호출. */
263
+ unbindDirect(helperHostSet: HelperHostSet, target: EventTarget, meta: AddEventListenerMetadata): void {
264
+ const inst = helperHostSet.$this;
265
+ const list = this.getBoundListeners(inst);
266
+ for (let i = list.length - 1; i >= 0; i--) {
267
+ const l = list[i];
268
+ if (l.target === target && l.type === meta.type && l.meta === meta) {
269
+ try { l.target.removeEventListener(l.type, l.handler, l.options); l.subscription?.unsubscribe?.(); }
270
+ catch (e) { console.error('[SWC] unbindDirect error:', e); }
271
+ for (const r of l.onRemoves ?? []) {
272
+ try { r.fn(l.target, r.opts); } catch (e) { console.error('[SWC] unbindDirect removeListener error:', e); }
273
+ }
274
+ list.splice(i, 1);
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * 메타데이터 목록을 delegate / mutationDelegate / nonDelegate 로 분류한다.
281
+ * MutationObserverLifeCycler 에서 mutationDelegate 목록을 꺼낼 때 사용한다.
282
+ */
283
+ classifyListeners(metaList: AddEventListenerMetadata[]): {
284
+ delegateListeners: AddEventListenerMetadata[];
285
+ mutationDelegateListeners: AddEventListenerMetadata[];
286
+ nonDelegateListeners: AddEventListenerMetadata[];
287
+ } {
288
+ const isSpecial = (sel: any) =>
289
+ ['$window', '$document', '$host', '$appHost', '$firstHost', '$lastHost',
290
+ '$firstAppHost', '$lastAppHost', '$hosts', '$appHosts', '$this', ''].includes(sel);
291
+
292
+ const delegateListeners: AddEventListenerMetadata[] = [];
293
+ const mutationDelegateListeners: AddEventListenerMetadata[] = [];
294
+ const nonDelegateListeners: AddEventListenerMetadata[] = [];
295
+
296
+ for (const meta of metaList) {
297
+ const isStr = typeof meta.selector === 'string';
298
+ const mode = meta.options.delegate;
299
+ if (mode && isStr && !isSpecial(meta.selector)) {
300
+ mode === 'mutation' ? mutationDelegateListeners.push(meta) : delegateListeners.push(meta);
301
+ } else {
302
+ nonDelegateListeners.push(meta);
303
+ }
304
+ }
305
+ return { delegateListeners, mutationDelegateListeners, nonDelegateListeners };
306
+ }
307
+
308
+ // ── ElementDefineLifeCycler 구현 ──
309
+
310
+ onConnected(helperHostSet: HelperHostSet): OnConnectedResult | void {
311
+ const inst = helperHostSet.$this;
312
+ const currentWin = helperHostSet.$w;
313
+
314
+ this.boundListenersMap.delete(inst);
315
+ this.eventBoundSetsMap.delete(inst);
316
+
317
+ const allMeta = getAddEventListenerMetadata(inst) ?? [];
318
+ const { delegateListeners, nonDelegateListeners, mutationDelegateListeners } = this.classifyListeners(allMeta);
319
+
320
+ // ── delegate 리스너: type+root 키로 그룹핑 → unified handler ──
321
+ const groups = new Map<string, AddEventListenerMetadata[]>();
322
+ for (const meta of delegateListeners) {
323
+ const key = `${meta.type}:${meta.options.root || 'auto'}`;
324
+ if (!groups.has(key)) groups.set(key, []);
325
+ groups.get(key)!.push(meta);
326
+ }
327
+
328
+ groups.forEach((metaList, key) => {
329
+ const ci = key.indexOf(':');
330
+ const type = key.slice(0, ci);
331
+ const r = key.slice(ci + 1);
332
+ const opts = { capture: metaList[0].options.capture, once: metaList[0].options.once, passive: metaList[0].options.passive };
333
+
334
+ const roots: (HTMLElement | ShadowRoot)[] = [];
335
+ if (r === 'auto') roots.push(inst.shadowRoot || inst);
336
+ else if (r === 'light') roots.push(inst);
337
+ else if (r === 'shadow' && inst.shadowRoot) roots.push(inst.shadowRoot);
338
+ else if (r === 'all') { roots.push(inst); if (inst.shadowRoot) roots.push(inst.shadowRoot); }
339
+
340
+ for (const br of roots) {
341
+ const sorted = [...metaList].sort((a, b) => (b.options.stopPropagation ? 1 : 0) - (a.options.stopPropagation ? 1 : 0));
342
+ const handler = async (event: Event) => {
343
+ for (const m of sorted) {
344
+ const matchedEl = (event.target as HTMLElement)?.closest(m.selector as string);
345
+ console.log('[DBG][delegate] type=', event.type, '| evtTarget=', (event.target as any)?.tagName, '| sel=', m.selector, '| matchedEl=', matchedEl?.className || matchedEl?.tagName, '| brContains=', (br as any).contains?.(matchedEl), '| brTag=', (br as any).tagName || (br as any).constructor?.name);
346
+ if (matchedEl && (br as any).contains(matchedEl)) {
347
+ if (m.options.filter) {
348
+ const helper = SwcUtils.getHelperAndHostSet(currentWin, matchedEl as HTMLElement);
349
+ if (!m.options.filter(event, { currentThis: inst, helper })) continue;
350
+ }
351
+ if (m.options.stopPropagation) event.stopPropagation();
352
+ if (m.options.stopImmediatePropagation) event.stopImmediatePropagation();
353
+ if (m.options.preventDefault) event.preventDefault();
354
+ const hs = SwcUtils.getHostSet(inst);
355
+ await inst[m.propertyKey](event, { ...hs, $matchedElement: matchedEl }, { event, ...hs, $el: matchedEl, $root: br });
356
+ if ((event as any).cancelBubble) break;
357
+ }
358
+ }
359
+ };
360
+ br.addEventListener(type, handler, opts);
361
+ if (type === 'click') {
362
+ console.log('[DBG][eventCycler] bound click on br=', (br as any).tagName || (br as any).constructor?.name, '| inst=', (inst as any).tagName || (inst as any).constructor?.name, '| isSame=', br === inst);
363
+ }
364
+ const onRemoves: Array<{ fn: any; opts: any }> = sorted.filter(m => m.options.removeListener).map(m => ({ fn: m.options.removeListener, opts: m.options }));
365
+ this.getBoundListeners(inst).push({ target: br, type, handler, options: opts, onRemoves });
366
+ }
367
+ });
368
+
369
+ // ── non-delegate 리스너: 대상 요소 직접 탐색 → bindDirect ──
370
+ for (const meta of nonDelegateListeners) {
371
+ const { selector, options } = meta;
372
+ const r = options.root || 'auto';
373
+ const bindTargets: EventTarget[] = [];
374
+
375
+ let resolvedSel: string | null = null;
376
+ if (typeof selector === 'function') {
377
+ const result = selector(inst, helperHostSet);
378
+ if (typeof result === 'string') resolvedSel = result;
379
+ else if (result instanceof currentWin.Element) bindTargets.push(result as EventTarget);
380
+ else if (result instanceof currentWin.NodeList) bindTargets.push(...(Array.from(result) as EventTarget[]));
381
+ else if (Array.isArray(result)) bindTargets.push(...(result as EventTarget[]));
382
+ } else {
383
+ resolvedSel = selector;
384
+ }
385
+
386
+ const applyRoot = (t: any) => {
387
+ if (!t) return;
388
+ if (r === 'auto') bindTargets.push(t.shadowRoot || t);
389
+ else { if (r === 'light' || r === 'all') bindTargets.push(t); if ((r === 'shadow' || r === 'all') && t.shadowRoot) bindTargets.push(t.shadowRoot); }
390
+ };
391
+
392
+ if (resolvedSel) {
393
+ if (resolvedSel === '$window') bindTargets.push(currentWin);
394
+ else if (resolvedSel === '$document') bindTargets.push(currentWin.document);
395
+ else if (resolvedSel === '$host') applyRoot(helperHostSet.$host);
396
+ else if (resolvedSel === '$parentHost') applyRoot(helperHostSet.$parentHost);
397
+ else if (resolvedSel === '$appHost') applyRoot(helperHostSet.$appHost);
398
+ else if (resolvedSel === '$firstHost') applyRoot(helperHostSet.$firstHost);
399
+ else if (resolvedSel === '$lastHost') applyRoot(helperHostSet.$lastHost);
400
+ else if (resolvedSel === '$firstAppHost') applyRoot(helperHostSet.$firstAppHost);
401
+ else if (resolvedSel === '$lastAppHost') applyRoot(helperHostSet.$lastAppHost);
402
+ else if (resolvedSel === '$hosts') helperHostSet.$hosts.forEach(applyRoot);
403
+ else if (resolvedSel === '$appHosts') helperHostSet.$appHosts.forEach(applyRoot);
404
+ else if (resolvedSel === '$this' || !resolvedSel) applyRoot(inst);
405
+ else {
406
+ const searchRoots: (HTMLElement | ShadowRoot)[] = [];
407
+ if (r === 'auto') searchRoots.push(inst.shadowRoot || inst);
408
+ else if (r === 'light') searchRoots.push(inst);
409
+ else if (r === 'shadow' && inst.shadowRoot) searchRoots.push(inst.shadowRoot);
410
+ else if (r === 'all') { searchRoots.push(inst); if (inst.shadowRoot) searchRoots.push(inst.shadowRoot); }
411
+ for (const sr of searchRoots) sr.querySelectorAll(resolvedSel).forEach(el => bindTargets.push(el));
412
+ }
413
+ }
414
+
415
+ for (const t of bindTargets) this.bindDirect(helperHostSet, t, meta);
416
+ }
417
+
418
+ // ── delegate:'mutation' 리스너: MutationObserver 로 추가/제거된 요소를 추적해 직접 바인딩/해제 ──
419
+ // 이벤트 리스너의 책임이므로 EventListenerLifeCycler 가 직접 처리하고,
420
+ // mutationObserverSet 을 반환해 elementDefine 이 하나의 MutationObserver 로 생성하게 한다.
421
+ if (mutationDelegateListeners.length > 0) {
422
+ const shadowEventDelegates = mutationDelegateListeners.filter(m => {
423
+ const r = m.options.root || 'auto';
424
+ return inst.shadowRoot && (r === 'shadow' || r === 'all' || r === 'auto');
425
+ });
426
+ const lightEventDelegates = mutationDelegateListeners.filter(m => {
427
+ const r = m.options.root || 'auto';
428
+ return r === 'light' || r === 'all' || (!inst.shadowRoot && r === 'auto');
429
+ });
430
+
431
+ const buildMutationEntry = (
432
+ root: HTMLElement | ShadowRoot,
433
+ eventDelegates: AddEventListenerMetadata[],
434
+ ): MutationObserverSetEntry | null => {
435
+ if (eventDelegates.length === 0) return null;
436
+ const boundSets = this.getEventBoundSets(inst);
437
+
438
+ const callback = (mutations: MutationRecord[], obs: MutationObserver) => {
439
+ for (const m of eventDelegates) {
440
+ let boundSet = boundSets.get(m);
441
+ if (!boundSet) { boundSet = new WeakSet<Element>(); boundSets.set(m, boundSet); }
442
+
443
+ const isThis = m.selector === '$this' || m.selector === '';
444
+ const matchesSel = (n: any) => {
445
+ if (!n || n.nodeType !== 1) return false;
446
+ if (isThis) return true;
447
+ return typeof m.selector === 'string' && (n as HTMLElement).matches?.(m.selector);
448
+ };
449
+ const bindEl = (el: Element) => { if (!boundSet!.has(el)) { this.bindDirect(helperHostSet, el, m); boundSet!.add(el); } };
450
+ const unbindEl = (el: Element) => { if (boundSet!.has(el)) { this.unbindDirect(helperHostSet, el, m); boundSet!.delete(el); } };
451
+
452
+ for (const mut of mutations) {
453
+ Array.from(mut.addedNodes || []).forEach(n => { if (n.nodeType !== 1) return; if (matchesSel(n)) bindEl(n as Element); else if (!isThis && typeof m.selector === 'string') (n as Element).querySelectorAll?.(m.selector).forEach(bindEl); });
454
+ Array.from(mut.removedNodes || []).forEach(n => { if (n.nodeType !== 1) return; const el = n as Element; if (matchesSel(el)) unbindEl(el); else if (!isThis && typeof m.selector === 'string') el.querySelectorAll?.(m.selector).forEach(unbindEl); });
455
+ }
456
+ }
457
+ };
458
+
459
+ // delegate:'mutation' 초기 바인딩 (connected 시 이미 존재하는 요소)
460
+ for (const m of eventDelegates) {
461
+ let boundSet = boundSets.get(m);
462
+ if (!boundSet) { boundSet = new WeakSet<Element>(); boundSets.set(m, boundSet); }
463
+ if (typeof m.selector !== 'string') continue;
464
+ if (m.selector === '$this' || m.selector === '') {
465
+ this.bindDirect(helperHostSet, root as EventTarget, m);
466
+ boundSet.add(root as Element);
467
+ } else {
468
+ root.querySelectorAll(m.selector).forEach(el => {
469
+ if (!boundSet!.has(el)) { this.bindDirect(helperHostSet, el, m); boundSet!.add(el); }
470
+ });
471
+ }
472
+ }
473
+
474
+ return { target: root, options: { childList: true, subtree: true }, callback };
475
+ };
476
+
477
+ const shadowEntry = inst.shadowRoot ? buildMutationEntry(inst.shadowRoot, shadowEventDelegates) : null;
478
+ const lightEntry = buildMutationEntry(inst as any, lightEventDelegates);
479
+ const mutationObserverSet: MutationObserverSetEntry[] = [shadowEntry, lightEntry].filter(Boolean) as MutationObserverSetEntry[];
480
+ if (mutationObserverSet.length > 0) return { mutationObserverSet };
481
+ }
482
+
483
+ return undefined;
484
+ }
485
+
486
+ onDisconnected(helperHostSet: HelperHostSet): void {
487
+ const inst = helperHostSet.$this;
488
+ const list = this.boundListenersMap.get(inst) ?? [];
489
+ for (const l of list) {
490
+ try { l.target.removeEventListener(l.type, l.handler, l.options); l.subscription?.unsubscribe?.(); }
491
+ catch (e) { console.error('[SWC] EventListenerLifeCycler cleanup error:', e); }
492
+ for (const r of l.onRemoves ?? []) {
493
+ try { r.fn(l.target, r.opts); } catch (e) { console.error('[SWC] removeListener error:', e); }
494
+ }
495
+ }
496
+ this.boundListenersMap.delete(inst);
497
+ this.eventBoundSetsMap.delete(inst);
498
+ }
499
+ }