@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
@@ -1,20 +1,25 @@
1
- import {ActionExpression, FunctionUtils, ReflectUtils, Subject} from '@dooboostore/core';
2
- import {debounceTime, distinctUntilChanged, throttleTime} from '@dooboostore/core/message/operators';
3
- import {getAddEventListenerMetadata} from './addEventListener';
1
+ import {ActionExpression, FunctionUtils, ReflectUtils} from '@dooboostore/core';
2
+ import {EventListenerLifeCycler} from './addEventListener';
3
+ import {MutationObserverLifeCycler} from './mutationObserver';
4
+ import {ResizeObserverLifeCycler} from './resizeObserver';
5
+ import {IntersectionObserverLifeCycler} from './intersectionObserver';
4
6
  import {findAllLifecycleMetadata, findAllOnConnectedAfterMetadata, findAllOnConnectedBeforeMetadata, findAllOnConnectedMetadata, ON_AFTER_ADOPTED_METADATA_KEY, ON_AFTER_DISCONNECTED_METADATA_KEY, ON_BEFORE_ADOPTED_METADATA_KEY, ON_BEFORE_DISCONNECTED_METADATA_KEY, ON_CONNECTED_COMPLETED_METADATA_KEY, ON_INITIALIZE_METADATA_KEY} from './lifecycles';
5
- import {getEmitCustomEventMetadataList} from './emitCustomEvent';
6
- import {convertAttributeValue, findAllAttributeChangedMetadata} from './changedAttribute';
7
- import {findAllAttributeApplyMetadata, findAllAttributeMetadata, getAttributeValue} from './applyAttribute';
8
- import {getQueryMetadata} from './query';
9
- import {getQueryAllMetadata} from './queryAll';
7
+ import {EmitCustomEventLifeCycler} from './emitCustomEvent';
8
+ import {ChangedAttributeLifeCycler} from './changedAttribute';
9
+ import {findAllAttributeApplyMetadata, findAllAttributeMetadata} from './applyAttribute';
10
+ import {getQueryMetadata, getQueryAllMetadata} from './query';
10
11
  import {SwcUtils} from '../utils/Utils';
11
12
  import {DOM_EVENT_NAMES, HTML_TAG_ENTRIES} from '../config/config';
12
13
  import {SituationTypeContainer, SituationTypeContainers} from '@dooboostore/simple-boot/decorators/inject/Inject';
13
- import {HelperHostSet, HostSet, InjectSituationType} from '../types';
14
+ import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
14
15
  import {ConvertUtils, ElementApply} from '@dooboostore/core-web';
15
16
  import {isSSR} from "../elements/SwcAppMixin";
16
17
  import {findAllStateMetadata} from "./state";
17
18
  import {findAllPropertyMetadata} from "./applyProperty";
19
+ import {MessageSubscribeLifeCycler} from "./subscribeSwcAppMessageWhileConnected";
20
+ import {RouteSubscribeLifeCycler} from "./subscribeSwcAppRouteChangeWhileConnected";
21
+ import {SetIntervalLifeCycler} from "./setInterval";
22
+ import {SetTimeoutLifeCycler} from "./setTimeout";
18
23
 
19
24
  // --- Core Interfaces & Types ---
20
25
 
@@ -42,7 +47,6 @@ export const ensureInit = (inst: any) => { // HTMLElement
42
47
  if (!inst.__swc_initialized) {
43
48
  inst._swcId = inst?.getAttribute?.('swc-use-ssr') ?? 's' + Math.random().toString(36).substring(2, 11).toLowerCase();
44
49
  inst._emitHandlers = new Map();
45
- inst._boundListeners = [];
46
50
  inst.__swc_initialized = true;
47
51
  // const attributeList = findAllQ(inst);
48
52
  // attributeList.forEach(meta => {
@@ -318,8 +322,8 @@ const setupPrototype = (proto: any, win: Window) => {
318
322
  export const elementDefine =
319
323
  (name: string, config: Partial<ElementConfig> = {}): ClassDecorator =>
320
324
  (constructor: any) => {
321
- const {win, doc, builtInTagMap: BUILT_IN_TAG_MAP, domHelpers: SWC_DOM_HELPERS} = buildEnv(config.window);
322
- const metadata: ElementMetadata = {...config, name, window: win};
325
+ const { win, doc, builtInTagMap: BUILT_IN_TAG_MAP, domHelpers: SWC_DOM_HELPERS } = buildEnv(config.window);
326
+ const metadata: ElementMetadata = { ...config, name, window: win };
323
327
 
324
328
  if (!metadata.window) {
325
329
  throw new Error('window is required');
@@ -337,10 +341,6 @@ export const elementDefine =
337
341
  metadata.extends = extendsTagName;
338
342
  }
339
343
 
340
- const emitCustomEventList = getEmitCustomEventMetadataList(constructor) || [];
341
- const emitHostCustomEventList = emitCustomEventList.filter(meta => meta.selector === '$this' || meta.selector === '');
342
- const addEventListenerList = getAddEventListenerMetadata(constructor) || [];
343
- const attrChangeMap = findAllAttributeChangedMetadata(constructor);
344
344
  const attributeList = findAllAttributeMetadata(constructor);
345
345
  const applyAttributeMap = findAllAttributeApplyMetadata(constructor);
346
346
 
@@ -353,24 +353,256 @@ export const elementDefine =
353
353
  const hostAttributes = attributeList.filter(it => it.selector === '$this').map(it => it.propertyKey || String(it.propertyKey));
354
354
  // Get original static observedAttributes before they're overwritten by Object.defineProperty
355
355
  const originalStaticObservedAttributes = (constructor.observedAttributes ?? []) as string[];
356
- const mergedObservedAttributes = [...new Set([...(metadata.observedAttributes ?? []), ...originalStaticObservedAttributes, ...attrChangeMap.keys(), ...attributeApplyNames, ...hostAttributes, ...emitHostCustomEventList.map(it => (it.options as any).attributeName).filter(Boolean), ...swcLifecycleAttributes, ...swcOnEvents])];
357
- // const connectedInnerHtmlList = getOnConnectedInnerHtmlMetadata(constructor) || [];
358
356
 
359
357
  const proto = constructor.prototype;
360
358
  setupPrototype(proto, win);
361
359
 
362
360
  // 값을 기대하지않는다.
363
361
  let helperHostSet: HelperHostSet | null = null;
362
+ // ── cyclers: 데코레이터별 라이프사이클 위임 클래스 (elementDefine 시 1회 생성, 계속 재사용) ──
363
+ const eventCycler = new EventListenerLifeCycler();
364
+ const cyclers: ElementDefineLifeCycler[] = [
365
+ eventCycler,
366
+ new ChangedAttributeLifeCycler(),
367
+ new EmitCustomEventLifeCycler(),
368
+ new ResizeObserverLifeCycler(),
369
+ new IntersectionObserverLifeCycler(),
370
+ new MutationObserverLifeCycler(),
371
+ new MessageSubscribeLifeCycler(),
372
+ new RouteSubscribeLifeCycler(),
373
+ new SetIntervalLifeCycler(),
374
+ new SetTimeoutLifeCycler(),
375
+ ];
376
+
377
+ // observedAttributes 기여 cycler (define-time, constructor 기반)
378
+ const cyclerObservedAttributes = cyclers.flatMap(c => c.getObservedAttributeNames?.(constructor) ?? []);
379
+ const mergedObservedAttributes = [...new Set([...(metadata.observedAttributes ?? []), ...originalStaticObservedAttributes, ...cyclerObservedAttributes, ...attributeApplyNames, ...hostAttributes, ...swcLifecycleAttributes, ...swcOnEvents])];
380
+
381
+ // 렌더 후 모든 시클러 onConnected 호출 → Set 수집 후 observer 생성
382
+ const buildObservers = async (helperHostSet: HelperHostSet): Promise<void> => {
383
+ const inst = helperHostSet.$this;
384
+ const observers: Array<MutationObserver | ResizeObserver | IntersectionObserver> = [];
385
+ let resizeObserverSet: ResizeObserverSet = [];
386
+ let mutationObserverSet: MutationObserverSet = [];
387
+ let intersectionObserverSet: IntersectionObserverSet = [];
388
+
389
+ // delegate 추적 root 해석: shadow/light/auto/all → 실제 root 목록
390
+ const resolveDelegateRoots = (delegateRoot: SwcRootType | undefined): (HTMLElement | ShadowRoot)[] => {
391
+ const r = delegateRoot || 'auto';
392
+ const roots: (HTMLElement | ShadowRoot)[] = [];
393
+ if (r === 'auto') roots.push(inst.shadowRoot || inst);
394
+ else if (r === 'light') roots.push(inst);
395
+ else if (r === 'shadow' && inst.shadowRoot) roots.push(inst.shadowRoot);
396
+ else if (r === 'all') { roots.push(inst); if (inst.shadowRoot) roots.push(inst.shadowRoot); }
397
+ return roots;
398
+ };
399
+
400
+ // 1. 모든 시클러 onConnected → observer Set 수집 (여러 시클러가 반환한 Set 을 전부 누적)
401
+ for (const c of cyclers) {
402
+ const result = (await c.onConnected?.(helperHostSet, resizeObserverSet)) as OnConnectedResult | void;
403
+ if (!result) continue;
404
+ if (result.resizeObserverSet?.length) resizeObserverSet = [...resizeObserverSet, ...result.resizeObserverSet];
405
+ if (result.mutationObserverSet?.length) mutationObserverSet = [...mutationObserverSet, ...result.mutationObserverSet];
406
+ if (result.intersectionObserverSet?.length) intersectionObserverSet = [...intersectionObserverSet, ...result.intersectionObserverSet];
407
+ }
408
+
409
+ // 2. 타입별로 observer 생성
410
+ // ResizeObserver 먼저: mutation callback 이 observe/unobserve 를 주입받아야 함
411
+ // observeTargets 중 delegate 항목은 셀렉터 문자열이라 초기 observe 하지 않는다 (동적 추적 전용).
412
+ // 동적 observe/unobserve 는 중복 호출을 막기 위해 observe 대상 WeakSet 으로 추적한다.
413
+ const resizeDelegates = (resizeObserverSet ?? []).filter(t => t.delegate && typeof t.target === 'string') as Array<ResizeObserverSetEntry & { target: string }>;
414
+ let resizeObserve: ((el: Element, opts?: ResizeObserverOptions) => void) | undefined;
415
+ let resizeUnobserve: ((el: Element) => void) | undefined;
416
+ const resizeObserved = new WeakSet<Element>();
417
+ if (resizeObserverSet?.length) {
418
+ const roCallbacks = [...new Set(resizeObserverSet.map(e => e.callback))];
419
+ const ro = new (win as any).ResizeObserver((entries, obs) => {
420
+ for (const cb of roCallbacks) cb(entries, obs);
421
+ });
422
+ for (const e of resizeObserverSet) {
423
+ if (e.delegate) continue;
424
+ if (typeof e.target === 'string') {
425
+ // 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
426
+ const scope = resolveDelegateRoots(e.delegateRoot);
427
+ for (const sr of scope) {
428
+ sr.querySelectorAll(e.target).forEach(el => {
429
+ if (!resizeObserved.has(el)) { ro.observe(el, e.options); resizeObserved.add(el); }
430
+ });
431
+ }
432
+ continue;
433
+ }
434
+ ro.observe(e.target as Element, e.options);
435
+ if (e.target instanceof Element) resizeObserved.add(e.target);
436
+ }
437
+ resizeObserve = (el, opts) => {
438
+ if (resizeObserved.has(el)) return;
439
+ ro.observe(el, opts);
440
+ resizeObserved.add(el);
441
+ };
442
+ resizeUnobserve = (el) => {
443
+ if (!resizeObserved.has(el)) return;
444
+ ro.unobserve(el);
445
+ resizeObserved.delete(el);
446
+ };
447
+ observers.push(ro);
448
+ }
449
+
450
+ // IntersectionObserver: 옵션 그룹별로 1개씩 생성.
451
+ // delegate 항목은 동적 추적용 셀렉터이므로 observe/unobserve 함수를 MutationObserver 콜백에 넘긴다.
452
+ // 동적 observe/unobserve 중복 방지를 위해 그룹별 WeakSet 으로 추적한다.
453
+ const intersectionDelegates: Array<{ target: string; delegateRoot?: SwcRootType; observe: (el: Element) => void; unobserve: (el: Element) => void }> = [];
454
+ for (const group of intersectionObserverSet) {
455
+ const ioCallbacks = [...new Set(group.observeTargets.map(e => e.callback))];
456
+ const io = new (win as any).IntersectionObserver((entries, obs) => {
457
+ for (const cb of ioCallbacks) cb(entries, obs);
458
+ }, group.options);
459
+ const ioObserved = new WeakSet<Element>();
460
+ for (const e of group.observeTargets) {
461
+ if (e.delegate) {
462
+ const target = e.target as string;
463
+ const observe = (el: Element) => {
464
+ if (ioObserved.has(el)) return;
465
+ io.observe(el);
466
+ ioObserved.add(el);
467
+ };
468
+ const unobserve = (el: Element) => {
469
+ if (!ioObserved.has(el)) return;
470
+ io.unobserve(el);
471
+ ioObserved.delete(el);
472
+ };
473
+ intersectionDelegates.push({ target, delegateRoot: e.delegateRoot, observe, unobserve });
474
+ continue;
475
+ }
476
+ if (typeof e.target === 'string') {
477
+ // 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
478
+ const scope = resolveDelegateRoots(e.delegateRoot);
479
+ for (const sr of scope) {
480
+ sr.querySelectorAll(e.target).forEach(el => {
481
+ if (!ioObserved.has(el)) { io.observe(el); ioObserved.add(el); }
482
+ });
483
+ }
484
+ continue;
485
+ }
486
+ io.observe(e.target as Element);
487
+ if (e.target instanceof Element) ioObserved.add(e.target);
488
+ }
489
+ observers.push(io);
490
+ }
491
+
492
+ // resize/intersection delegate 동적 추적 — MutationObserver 콜백에서 추가/제거된 요소를 observe/unobserve 한다.
493
+ const handleResizeDelegate = (n: Node, observe: boolean) => {
494
+ if (!resizeObserve || !resizeUnobserve || n.nodeType !== 1) return;
495
+ const el = n as HTMLElement;
496
+ for (const d of resizeDelegates) {
497
+ const isThis = d.target === '$this' || d.target === '';
498
+ const matchesSel = (node: any) => {
499
+ if (!node || node.nodeType !== 1) return false;
500
+ if (isThis) return true;
501
+ return (node as HTMLElement).matches?.(d.target) || !!(node as HTMLElement).closest?.(d.target);
502
+ };
503
+ if (matchesSel(el)) {
504
+ observe ? resizeObserve(el, d.options)
505
+ : resizeUnobserve(el);
506
+ } else if (!isThis) {
507
+ el.querySelectorAll(d.target).forEach(sub => {
508
+ observe ? resizeObserve(sub as HTMLElement, d.options)
509
+ : resizeUnobserve(sub as HTMLElement);
510
+ });
511
+ }
512
+ }
513
+ };
514
+ const handleIntersectionDelegate = (n: Node, observe: boolean) => {
515
+ if (n.nodeType !== 1) return;
516
+ const el = n as HTMLElement;
517
+ for (const d of intersectionDelegates) {
518
+ const isThis = d.target === '$this' || d.target === '';
519
+ const matchesSel = (node: any) => {
520
+ if (!node || node.nodeType !== 1) return false;
521
+ if (isThis) return true;
522
+ return (node as HTMLElement).matches?.(d.target) || !!(node as HTMLElement).closest?.(d.target);
523
+ };
524
+ if (matchesSel(el)) {
525
+ observe ? d.observe(el) : d.unobserve(el);
526
+ } else if (!isThis) {
527
+ el.querySelectorAll(d.target).forEach(sub => {
528
+ observe ? d.observe(sub as HTMLElement) : d.unobserve(sub as HTMLElement);
529
+ });
530
+ }
531
+ }
532
+ };
533
+
534
+ if (mutationObserverSet?.length || resizeDelegates.length > 0 || intersectionDelegates.length > 0) {
535
+ // 하나의 MutationObserver 로 resize/intersection delegate 동적 추적 + mutation 콜백을 함께 처리
536
+ const callbacks = [...new Set((mutationObserverSet ?? []).map(e => e.callback))];
537
+ const mo = new (win as any).MutationObserver((mutations, obs) => {
538
+ for (const mut of mutations) {
539
+ Array.from(mut.addedNodes || []).forEach(n => { handleResizeDelegate(n as Node, true); handleIntersectionDelegate(n as Node, true); });
540
+ Array.from(mut.removedNodes || []).forEach(n => { handleResizeDelegate(n as Node, false); handleIntersectionDelegate(n as Node, false); });
541
+ }
542
+ for (const cb of callbacks) cb(mutations, obs);
543
+ });
544
+
545
+ // observe 대상 전체(엔트리 + delegate 추적 root)를 하나로 모아 중복 observe 를 제거한다.
546
+ // 같은 target 이 여러 소스에서 지정되면 MutationObserverInit 을 합집합(최대 감지범위)으로 병합한다.
547
+ const moObserveTargets = new Map<Element | ShadowRoot, MutationObserverInit>();
548
+ const mergeMutationInit = (existing: MutationObserverInit | undefined, next: MutationObserverInit): MutationObserverInit => {
549
+ if (!existing) return next;
550
+ const mergedFilter = existing.attributeFilter || next.attributeFilter
551
+ ? [...new Set([...(existing.attributeFilter ?? []), ...(next.attributeFilter ?? [])])]
552
+ : undefined;
553
+ return {
554
+ childList: existing.childList || next.childList,
555
+ attributes: existing.attributes || next.attributes,
556
+ characterData: existing.characterData || next.characterData,
557
+ subtree: existing.subtree || next.subtree,
558
+ attributeOldValue: existing.attributeOldValue || next.attributeOldValue,
559
+ characterDataOldValue: existing.characterDataOldValue || next.characterDataOldValue,
560
+ attributeFilter: mergedFilter,
561
+ };
562
+ };
563
+ const addObserveTarget = (target: Element | ShadowRoot, options: MutationObserverInit) => {
564
+ moObserveTargets.set(target, mergeMutationInit(moObserveTargets.get(target), options));
565
+ };
566
+
567
+ // 1) mutationObserverSet 엔트리
568
+ for (const e of mutationObserverSet ?? []) {
569
+ if (e.delegate) continue;
570
+ const init = e.options ?? { childList: true };
571
+ if (typeof e.target === 'string') {
572
+ // 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
573
+ const scope = resolveDelegateRoots(e.delegateRoot);
574
+ for (const sr of scope) {
575
+ sr.querySelectorAll(e.target).forEach(el => addObserveTarget(el, init));
576
+ }
577
+ continue;
578
+ }
579
+ addObserveTarget(e.target, init);
580
+ }
581
+
582
+ // 2) delegate 추적 root: shadow/light/all/auto 로 해석해 전부 observe
583
+ for (const d of resizeDelegates) {
584
+ for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
585
+ }
586
+ for (const d of intersectionDelegates) {
587
+ for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
588
+ }
589
+
590
+ for (const [target, options] of moObserveTargets) {
591
+ mo.observe(target, options);
592
+ }
593
+ observers.push(mo);
594
+ }
595
+
596
+ inst.__swc_observers = observers;
597
+ };
598
+
364
599
  const originalConnected = proto.connectedCallback;
365
600
  proto.connectedCallback = async function () {
366
601
  ensureInit(this);
367
- // connected 됐을시 최신화
602
+ // 재연결 시 누적 방지 — cycler 내부 리소스 초기화
368
603
  helperHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
369
604
  const appHost = helperHostSet.$appHost;
370
605
  const useSsr = isSSR(this);
371
- // console.log('isSsr', useSsr, this);
372
- // console.log('----helperHostSet--->', this, helperHostSet, useSsr)
373
- // let connectedApp: SwcAppInterface |undefined;
374
606
  try {
375
607
  if (appHost && typeof (appHost as any)._connected === 'function') {
376
608
  await (appHost as any)._connected(this);
@@ -379,56 +611,42 @@ export const elementDefine =
379
611
  (appHost as any)._connected_safari_and_standby.push(this);
380
612
  }
381
613
 
382
- // const hostSet = SwcUtils.getHostSet(this as any);
383
614
  const conf = getElementConfig(this);
384
615
  const currentWin = (this as any)._resolveWindow(conf);
385
616
 
386
- const bMethods = findAllOnConnectedBeforeMetadata(this);//.filter(it => useSsr ? !it.options.ssrFirst : true);
387
- // console.log('beforeConnected', bMethods)
388
- for (const m of bMethods) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
617
+ // before-connected 라이프사이클 메서드 (@onConnectedBefore)
618
+ for (const m of findAllOnConnectedBeforeMetadata(this)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
389
619
  (this as any)._executeSwcScript('swc-on-before-connected', helperHostSet);
390
620
 
391
- // console.log('vvvvvvvvvvvvvvvu-seSsr-vvvvvvvvvv>', useSsr, this.tagName, this.getAttribute('seq'))
392
- // // ssr 처리라서 이미 내려준거그대로 상요하면된다 따라서 호출안한다
393
- // const targetConnectedList = findAllOnConnectedMetadata(constructor).filter(it => useSsr ? !it.options.ssrFirst : true);
621
+ // ── 렌더 (@onConnected / @onConnectedBody*) — elementDefine 책임 ──
394
622
  const targetConnectedList = useSsr ? [] : findAllOnConnectedMetadata(constructor);
395
623
  const shadowMode = conf?.useShadow || targetConnectedList.find(it => it.options.useShadow)?.options.useShadow;
396
- // console.log('--------->', this.tagName, shadowMode && !this.shadowRoot)
397
624
  if (shadowMode && !this.shadowRoot) {
398
625
  const mode = shadowMode === true ? 'open' : shadowMode;
399
- console.log('tagName=', this.tagName, 'mode', mode, this.shadowRoot);
400
- // if (this.tagName!=='INDEX-ROUTER') {
401
- this.attachShadow({mode: mode as ShadowRootMode});
402
- // }
626
+ this.attachShadow({ mode: mode as ShadowRootMode });
403
627
  }
404
628
 
405
- const stateContext: any = {...helperHostSet};
629
+ const stateContext: any = { ...helperHostSet };
406
630
  findAllStateMetadata(this).forEach(it => {
407
- stateContext[it.name] = this[it.propertyKey]
408
- })
631
+ stateContext[it.name] = this[it.propertyKey];
632
+ });
409
633
 
410
634
  const shadowChildren: Node[] = [];
411
635
  const lightChildren: Node[] = [];
412
636
 
413
- console.log('targetConnectList', this.tagName, targetConnectedList.length)
414
637
  if (targetConnectedList.length > 0) {
415
- // let sContent = '',
416
- // lContent = '';
417
638
  for (const meta of targetConnectedList) {
418
- // const res = (this as any)[meta.propertyKey]();
419
639
  let res = await (this as any)._invokeLifecycleMethod(meta.propertyKey, helperHostSet);
420
- // res = SwcUtils.projectProcessHtml(this, res);
421
640
  if (typeof res === 'string') {
422
641
  const htmlTemplateElement = doc.createElement('template');
423
- htmlTemplateElement.innerHTML = res;//SwcUtils.projectProcessHtml(this._swcId, res, doc);
642
+ htmlTemplateElement.innerHTML = res;
424
643
  res = htmlTemplateElement.content;
425
644
  }
426
-
427
645
  if (res) {
428
646
  const nodes = Array.isArray(res) ? res : [res];
429
647
  if (meta.options.useShadow || conf?.useShadow) {
430
648
  shadowChildren.push(...nodes);
431
- }else {
649
+ } else {
432
650
  lightChildren.push(...nodes);
433
651
  }
434
652
  }
@@ -436,23 +654,22 @@ export const elementDefine =
436
654
  try {
437
655
  if (this.shadowRoot) {
438
656
  const applyShadowChildren = SwcUtils.projectProcessHtml(this._swcId, shadowChildren, doc);
439
- this.shadowRoot.replaceChildren(...applyShadowChildren)
657
+ this.shadowRoot.replaceChildren(...applyShadowChildren);
440
658
  }
441
659
  if (lightChildren.length) {
442
660
  const applyLightChildren = SwcUtils.projectProcessHtml(this._swcId, lightChildren, doc);
443
- this.replaceChildren(...applyLightChildren)
661
+ this.replaceChildren(...applyLightChildren);
444
662
  }
445
-
446
663
  } catch (e) {
447
664
  console.error('[ElementDefine] innerHTML setting error:', e);
448
665
  }
449
- new ElementApply(this, {id: this._swcId}).apply({context: stateContext, bind: this});
666
+ new ElementApply(this, { id: this._swcId }).apply({ context: stateContext, bind: this });
450
667
  } else {
451
- new ElementApply(this, {id: this._swcId}).apply({exclude: {html: true, text: true, attribute: true}, context: stateContext, bind: this});
668
+ new ElementApply(this, { id: this._swcId }).apply({ exclude: { html: true, text: true, attribute: true }, context: stateContext, bind: this });
452
669
  }
453
670
 
454
671
  // global delegate event
455
- const root = this.getRootNode()
672
+ const root = this.getRootNode();
456
673
  if (!globalDelegatedRoots.has(root)) {
457
674
  DOM_EVENT_NAMES.forEach(type => {
458
675
  root.addEventListener(type, handleGlobalSwcEvent);
@@ -460,271 +677,51 @@ export const elementDefine =
460
677
  globalDelegatedRoots.add(root);
461
678
  }
462
679
 
680
+ // ── cycler 실행 (렌더 후): 모든 시클러 onConnected → observer Set 수집 → observer 생성 ──
681
+ // helperHostSet 은 클로저 변수라 async 흐름에서 다른 인스턴스로 덮어써질 수 있다.
682
+ // 반드시 현재 인스턴스(this)로 재계산한 값을 사용한다.
683
+ await buildObservers(SwcUtils.getHelperAndHostSet(win, this as any));
463
684
 
464
- // Separate delegate and non-delegate listeners
465
- const delegateListeners: typeof addEventListenerList = [];
466
- const nonDelegateListeners: typeof addEventListenerList = [];
467
-
468
- addEventListenerList.forEach(meta => {
469
- // Function-based selectors cannot be used with delegate listeners
470
- const isStringSelector = typeof meta.selector === 'string';
471
- if (meta.options.delegate && isStringSelector && meta.selector !== '$window' && meta.selector !== '$document' && meta.selector !== '$host' && meta.selector !== '$appHost' && meta.selector !== '$firstHost' && meta.selector !== '$lastHost' && meta.selector !== '$firstAppHost' && meta.selector !== '$lastAppHost' && meta.selector !== '$hosts' && meta.selector !== '$appHosts' && meta.selector !== '$this' && meta.selector !== '') {
472
- delegateListeners.push(meta);
473
- } else {
474
- nonDelegateListeners.push(meta);
475
- }
476
- });
477
-
478
- const delegatesByTypeAndRoot = new Map<string, any[]>();
479
- delegateListeners.forEach(meta => {
480
- const r = meta.options.root || 'auto';
481
- const rootKey = `${meta.type}:${r}`;
482
- if (!delegatesByTypeAndRoot.has(rootKey)) {
483
- delegatesByTypeAndRoot.set(rootKey, []);
484
- }
485
- delegatesByTypeAndRoot.get(rootKey)!.push(meta);
486
- });
487
-
488
- delegatesByTypeAndRoot.forEach((metaList, typeRootKey) => {
489
- const [type, rStr] = typeRootKey.split(':');
490
- const r = rStr === 'auto' ? 'auto' : rStr;
491
- const opts = {capture: metaList[0].options.capture, once: metaList[0].options.once, passive: metaList[0].options.passive};
492
-
493
- const bindRoots: (HTMLElement | ShadowRoot)[] = [];
494
- if (r === 'auto') bindRoots.push(this.shadowRoot || (this as any));
495
- else if (r === 'light') bindRoots.push(this as any);
496
- else if (r === 'shadow' && this.shadowRoot) bindRoots.push(this.shadowRoot);
497
- else if (r === 'all') {
498
- bindRoots.push(this as any);
499
- if (this.shadowRoot) bindRoots.push(this.shadowRoot);
500
- }
501
-
502
- bindRoots.forEach(br => {
503
- const sortedMetaList = [...metaList].sort((a, b) => {
504
- const aStop = a.options.stopPropagation ? 1 : 0;
505
- const bStop = b.options.stopPropagation ? 1 : 0;
506
- return bStop - aStop;
507
- });
508
-
509
- const unifiedHandler = async (event: Event) => {
510
- for (const meta of sortedMetaList) {
511
- const {selector, options} = meta;
512
- const matchedEl = (event.target as HTMLElement)?.closest(selector);
513
- if (matchedEl && (br as any).contains(matchedEl)) {
514
- // Apply filter if specified
515
- if (options.filter) {
516
- const helper = SwcUtils.getHelperAndHostSet(currentWin, matchedEl);
517
- if (!options.filter(event, helper)) {
518
- continue; // Skip this listener if filter returns false
519
- }
520
- }
521
- if (options.stopPropagation) event.stopPropagation();
522
- if (options.stopImmediatePropagation) event.stopImmediatePropagation();
523
- if (options.preventDefault) event.preventDefault();
524
- const currentHostSet = SwcUtils.getHostSet(this as any);
525
- const args = {event, ...currentHostSet, $el: matchedEl, $root: br};
526
- await (this as any)[meta.propertyKey](event, {...currentHostSet, $matchedElement: matchedEl}, args);
527
- if ((event as any).cancelBubble) break;
528
- }
529
- }
530
- };
531
- br.addEventListener(type, unifiedHandler, opts);
532
- (this as any)._boundListeners.push({target: br, type, handler: unifiedHandler, options: opts});
533
- });
534
- });
685
+ if (originalConnected) await originalConnected.apply(this);
535
686
 
536
- nonDelegateListeners.forEach(meta => {
537
- const {selector, type, options} = meta;
538
- const opts = {capture: options.capture, once: options.once, passive: options.passive};
539
- const bindTargets: EventTarget[] = [];
540
- const r = options.root || 'auto';
541
-
542
- // Resolve selector if it's a function
543
- let resolvedSelector: string | null = null;
544
- if (typeof selector === 'function') {
545
- const hostSet = SwcUtils.getHelperAndHostSet(currentWin, this);
546
- const result = selector(this, hostSet);
547
- if (typeof result === 'string') {
548
- resolvedSelector = result;
549
- } else if (result instanceof currentWin.Element) {
550
- bindTargets.push(result as EventTarget);
551
- } else if (result instanceof currentWin.NodeList) {
552
- bindTargets.push(...Array.from(result as EventTarget[]));
553
- } else if (Array.isArray(result)) {
554
- bindTargets.push(...(result as EventTarget[]));
555
- }
556
- } else {
557
- resolvedSelector = selector;
558
- }
559
-
560
- const applyRootOption = (target: any) => {
561
- if (!target) return;
562
- if (r === 'auto') {
563
- bindTargets.push(target.shadowRoot || target);
564
- } else {
565
- if (r === 'light' || r === 'all') bindTargets.push(target);
566
- if ((r === 'shadow' || r === 'all') && target.shadowRoot) bindTargets.push(target.shadowRoot);
567
- }
568
- };
569
-
570
- if (resolvedSelector) {
571
- if (resolvedSelector === '$window') bindTargets.push(currentWin);
572
- else if (resolvedSelector === '$document') bindTargets.push(currentWin.document);
573
- else if (resolvedSelector === '$host') applyRootOption(helperHostSet.$host);
574
- else if (resolvedSelector === '$parentHost') applyRootOption(helperHostSet.$parentHost);
575
- else if (resolvedSelector === '$appHost') applyRootOption(helperHostSet.$appHost);
576
- else if (resolvedSelector === '$firstHost') applyRootOption(helperHostSet.$firstHost);
577
- else if (resolvedSelector === '$lastHost') applyRootOption(helperHostSet.$lastHost);
578
- else if (resolvedSelector === '$firstAppHost') applyRootOption(helperHostSet.$firstAppHost);
579
- else if (resolvedSelector === '$lastAppHost') applyRootOption(helperHostSet.$lastAppHost);
580
- else if (resolvedSelector === '$hosts') helperHostSet.$hosts.forEach(applyRootOption);
581
- else if (resolvedSelector === '$appHosts') helperHostSet.$appHosts.forEach(applyRootOption);
582
- else if (resolvedSelector === '$this' || !resolvedSelector) applyRootOption(this);
583
- else {
584
- const searchRoots: (HTMLElement | ShadowRoot)[] = [];
585
- if (r === 'auto') searchRoots.push(this.shadowRoot || (this as any));
586
- else if (r === 'light') searchRoots.push(this as any);
587
- else if (r === 'shadow' && this.shadowRoot) searchRoots.push(this.shadowRoot);
588
- else if (r === 'all') {
589
- searchRoots.push(this as any);
590
- if (this.shadowRoot) searchRoots.push(this.shadowRoot);
591
- }
592
-
593
- searchRoots.forEach(sr => {
594
- sr.querySelectorAll(resolvedSelector).forEach(el => bindTargets.push(el));
595
- });
596
- }
597
- }
598
-
599
- bindTargets.forEach(t => {
600
- const handler = async (event: Event) => {
601
- // Apply filter if specified
602
- if (options.filter) {
603
- const helper = SwcUtils.getHelperAndHostSet(currentWin, t as HTMLElement);
604
- if (!options.filter(event, {currentThis: this, helper})) {
605
- return; // Skip if filter returns false
606
- }
607
- }
608
- if (options.stopPropagation) event.stopPropagation();
609
- if (options.stopImmediatePropagation) event.stopImmediatePropagation();
610
- if (options.preventDefault) event.preventDefault();
611
- const currentHostSet = SwcUtils.getHostSet(this as any);
612
- const args = {event, ...currentHostSet, $el: t, $root: t};
613
- await (this as any)[meta.propertyKey](event, {currentHostSet, $matchedElement: event.currentTarget}, args);
614
- };
615
-
616
- // Create event stream with Observable-based operators
617
- const eventSubject = new Subject<Event>();
618
- let eventStream = eventSubject as any;
619
-
620
- // Apply operators from @dooboostore/core/operators
621
- if (options.debounceTime !== undefined && options.debounceTime > 0) {
622
- eventStream = eventStream.pipe(debounceTime(options.debounceTime));
623
- }
624
-
625
- if (options.throttleTime !== undefined && options.throttleTime > 0) {
626
- eventStream = eventStream.pipe(throttleTime(options.throttleTime));
627
- }
628
-
629
- if (options.distinctUntilChanged !== undefined && options.distinctUntilChanged !== false) {
630
- if (typeof options.distinctUntilChanged === 'function') {
631
- eventStream = eventStream.pipe(distinctUntilChanged(options.distinctUntilChanged));
632
- } else {
633
- eventStream = eventStream.pipe(distinctUntilChanged());
634
- }
635
- }
636
-
637
- // Subscribe to the event stream
638
- const subscription = eventStream.subscribe({
639
- next: (event: Event) => {
640
- handler(event).catch((err: any) => console.error('Event handler error:', err));
641
- },
642
- error: (err: any) => console.error('Event stream error:', err)
643
- });
644
-
645
- // Create wrapper handler that emits to the subject
646
- const wrappedHandler = (event: Event) => {
647
- eventSubject.next(event);
648
- };
649
-
650
- t.addEventListener(type, wrappedHandler, opts);
651
- (this as any)._boundListeners.push({target: t, type, handler: wrappedHandler, options: opts, subscription});
652
- });
653
- });
654
-
655
- if (originalConnected) await (originalConnected.apply(this));
656
-
657
- const aMethods = findAllOnConnectedAfterMetadata(this); //.filter(it => { return useSsr ? !it.options.ssrFirst : true; });
658
- // console.log('afterConnected', this.tagName, aMethods, getOnConnectedAfterMetadata(this))
659
- for (const m of aMethods) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
687
+ // after-connected 라이프사이클 메서드 (@onConnectedAfter)
688
+ for (const m of findAllOnConnectedAfterMetadata(this)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
660
689
  (this as any)._executeSwcScript('swc-on-connected', helperHostSet);
661
690
  (this as any)._executeSwcScript('swc-on-after-connected', helperHostSet);
662
691
  (this as any).__swc_connected = true;
663
-
664
- // Trigger @changedAttributeThis with while: 'connected'
665
- for (let [name, metaList] of Array.from(attrChangeMap)) {
666
- for (const meta of metaList) {
667
- if (meta.options.while === 'connected') {
668
- const val = getAttributeValue(this, name, {type: meta.options.type});
669
- if (val !== null) {
670
- await ((this as any)[meta.propertyKey](val, null, name, helperHostSet));
671
- }
672
- }
673
- }
674
- }
675
692
  } finally {
676
- for (let m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) {
677
- await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
678
- }
693
+ for (const m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
679
694
  if (appHost && typeof (appHost as any)._connectedDone === 'function') {
680
695
  await (appHost as any)._connectedDone(this);
681
696
  }
682
697
  }
683
698
  };
684
699
 
700
+ /////////////////////////////////////////////////
701
+ // disconnectedCallback
702
+ ////////////////////////////////////////////////
685
703
  const originalDisconnected = proto.disconnectedCallback;
686
704
  proto.disconnectedCallback = function () {
687
- // const helperHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
688
- // Remove from appHost LAST when disconnected
689
- // console.log('disconnnnnnnnnnnnnnnnnnn', helperHostSet, this)
690
- const appHost = helperHostSet.$appHost;
705
+ const appHost = helperHostSet?.$appHost;
691
706
  if (appHost && typeof (appHost as any)._disconnected === 'function') {
692
707
  (appHost as any)._disconnected(this);
693
708
  }
694
709
 
695
710
  (this as any)._executeSwcScript('swc-on-before-disconnected', helperHostSet);
696
- const bMethods = findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY);
697
- for (const m of bMethods) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
698
-
699
- if ((this as any)._boundListeners) {
700
- (this as any)._boundListeners.forEach((l: any) => {
701
- l.target.removeEventListener(l.type, l.handler, l.options);
702
- // Cleanup Observable subscription if present
703
- if (l.subscription && typeof l.subscription.unsubscribe === 'function') {
704
- l.subscription.unsubscribe();
705
- }
706
- });
707
- (this as any)._boundListeners = [];
708
- }
711
+ for (const m of findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
712
+ for (const c of cyclers) c.onDisconnected?.(helperHostSet!);
709
713
 
710
- new ElementApply(this, {id: this._swcId}).removeAllEventListener();
714
+ for (const o of (this as any).__swc_observers ?? []) {
715
+ try { (o as any)?.disconnect?.(); } catch (e) { console.error('[SWC] observer disconnect error:', e); }
716
+ }
717
+ (this as any).__swc_observers = [];
711
718
 
712
- // globalDelegatedRoots remove
713
- // const roots = [this, this.getRootNode()];
714
- // if (this.shadowRoot) {
715
- // roots.push(this.shadowRoot);
716
- // }
717
- // roots.forEach(root => {
718
- // DOM_EVENT_NAMES.forEach(type => {
719
- // root.removeEventListener(type, handleGlobalSwcEvent);
720
- // });
721
- // }
722
- // )
719
+ // elementApply event listener 정리
720
+ new ElementApply(this, { id: this._swcId }).removeAllEventListener();
723
721
 
724
722
  if (originalDisconnected) originalDisconnected.apply(this);
725
723
 
726
- const aMethods = findAllLifecycleMetadata(this, ON_AFTER_DISCONNECTED_METADATA_KEY);
727
- for (const m of aMethods) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
724
+ for (const m of findAllLifecycleMetadata(this, ON_AFTER_DISCONNECTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
728
725
  (this as any)._executeSwcScript('swc-on-disconnected', helperHostSet);
729
726
  (this as any)._executeSwcScript('swc-on-after-disconnected', helperHostSet);
730
727
  (this as any).__swc_connected = false;
@@ -734,13 +731,12 @@ export const elementDefine =
734
731
  proto.adoptedCallback = function () {
735
732
  const hostSet = SwcUtils.getHostSet(this as any);
736
733
  (this as any)._executeSwcScript('swc-on-before-adopted', hostSet);
737
- const bMethods = findAllLifecycleMetadata(this, ON_BEFORE_ADOPTED_METADATA_KEY);
738
- for (const m of bMethods) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
734
+ for (const m of findAllLifecycleMetadata(this, ON_BEFORE_ADOPTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
735
+ for (const c of cyclers) c.onAdopted?.(helperHostSet!);
739
736
 
740
737
  if (originalAdopted) originalAdopted.apply(this);
741
738
 
742
- const aMethods = findAllLifecycleMetadata(this, ON_AFTER_ADOPTED_METADATA_KEY);
743
- for (const m of aMethods) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
739
+ for (const m of findAllLifecycleMetadata(this, ON_AFTER_ADOPTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
744
740
  (this as any)._executeSwcScript('swc-on-adopted', hostSet);
745
741
  (this as any)._executeSwcScript('swc-on-after-adopted', hostSet);
746
742
  };
@@ -749,7 +745,9 @@ export const elementDefine =
749
745
  proto.attributeChangedCallback = function (name: string, old: string | null, newVal: string | null) {
750
746
  if (originalAttributeChanged) originalAttributeChanged.apply(this, [name, old, newVal]);
751
747
 
752
- const hSet = SwcUtils.getHostSet(this as any);
748
+ // attributeChangedCallback 은 connected 이전에도 호출될 수 있으므로
749
+ // 클로저의 helperHostSet(null 가능) 대신 이 시점에 새로 계산한다.
750
+ const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
753
751
 
754
752
  // Process expression directive before passing to handlers
755
753
  let processedVal: any = newVal;
@@ -758,18 +756,17 @@ export const elementDefine =
758
756
  const expr = ae.getFirstExpression('callReturn');
759
757
  if (expr) {
760
758
  const win = (this as any)._resolveWindow?.() || ((typeof window !== 'undefined' ? window : undefined) as any);
761
- const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
759
+ const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
762
760
  const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
763
761
  try {
764
762
  const result = FunctionUtils.executeReturn({
765
763
  script: script,
766
764
  context: this,
767
- args: helperAndHostSet
765
+ args: exprHelperAndHostSet
768
766
  });
769
- // Pass the result object directly without serialization
770
767
  processedVal = result;
771
768
  } catch (e) {
772
- console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}: ${helperAndHostSet}`, e);
769
+ console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}: ${exprHelperAndHostSet}`, e);
773
770
  processedVal = newVal;
774
771
  }
775
772
  }
@@ -782,21 +779,8 @@ export const elementDefine =
782
779
  }
783
780
  }
784
781
 
785
- const hostCustomEventMeta = emitHostCustomEventList.find(it => (it.options as any).attributeName === name);
786
- if (hostCustomEventMeta && newVal !== null) {
787
- (this as any)._bindAttributeEvent(this as any, name, newVal, hostCustomEventMeta.type);
788
- }
789
-
790
- const metaList = attrChangeMap.get(name);
791
- if (metaList && Array.isArray(metaList)) {
792
- for (const meta of metaList) {
793
- if (meta.options.while === 'connected' && !(this as any).__swc_connected) {
794
- continue;
795
- }
796
- const convertedVal = convertAttributeValue(processedVal, meta.options.type);
797
- (this as any)[meta.propertyKey](convertedVal, old, name, hSet);
798
- }
799
- }
782
+ // cycler 위임: attribute 옵저빙(@emitCustomEvent(attributeName) / @changedAttribute 등)
783
+ for (const c of cyclers) c.onAttributeChanged?.(helperAndHostSet, name, old, processedVal);
800
784
  };
801
785
 
802
786
  Object.defineProperty(constructor, 'observedAttributes', {
@@ -807,7 +791,7 @@ export const elementDefine =
807
791
  ReflectUtils.defineMetadata(ELEMENT_CONFIG_KEY, metadata, constructor);
808
792
  const registry = metadata.customElementRegistry || (win as any)?.customElements;
809
793
  if (registry && !registry.get(metadata.name)) {
810
- registry.define(metadata.name, constructor as any, metadata.extends ? {extends: metadata.extends} : undefined);
794
+ registry.define(metadata.name, constructor as any, metadata.extends ? { extends: metadata.extends } : undefined);
811
795
  }
812
796
  return constructor;
813
797
  };