@dooboostore/simple-web-component 1.0.48 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -19
- package/dist/cjs/config/config.js +1 -1
- package/dist/cjs/config/config.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +2373 -30
- package/dist/cjs/decorators/addEventListener.js.map +3 -3
- package/dist/cjs/decorators/applyAttribute.js +31 -0
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +55 -18
- package/dist/cjs/decorators/applyClass.js.map +3 -3
- package/dist/cjs/decorators/applyNode.js +73 -1
- package/dist/cjs/decorators/applyNode.js.map +3 -3
- package/dist/cjs/decorators/applyProperty.js +107 -3
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applySlot.js +6 -5
- package/dist/cjs/decorators/applySlot.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +42 -5
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/changedAttribute.js +61 -13
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +254 -439
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +55 -0
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/eventMedia.js +84 -0
- package/dist/cjs/decorators/eventMedia.js.map +7 -0
- package/dist/cjs/decorators/index.js +6 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +200 -0
- package/dist/cjs/decorators/intersectionObserver.js.map +7 -0
- package/dist/cjs/decorators/mutationObserver.js +186 -55
- package/dist/cjs/decorators/mutationObserver.js.map +3 -3
- package/dist/cjs/decorators/parameter.js +73 -0
- package/dist/cjs/decorators/parameter.js.map +7 -0
- package/dist/cjs/decorators/query.js +117 -102
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/requestAnimationFrame.js +124 -0
- package/dist/cjs/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/cjs/decorators/resizeObserver.js +133 -55
- package/dist/cjs/decorators/resizeObserver.js.map +2 -2
- package/dist/cjs/decorators/setInterval.js +114 -0
- package/dist/cjs/decorators/setInterval.js.map +7 -0
- package/dist/cjs/decorators/setTimeout.js +117 -0
- package/dist/cjs/decorators/setTimeout.js.map +7 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js +9 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js +9 -0
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +19 -3
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/window.d.js.map +1 -1
- package/dist/esm/config/config.js +1 -1
- package/dist/esm/config/config.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +2374 -30
- package/dist/esm/decorators/addEventListener.js.map +3 -3
- package/dist/esm/decorators/applyAttribute.js +31 -0
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +55 -18
- package/dist/esm/decorators/applyClass.js.map +3 -3
- package/dist/esm/decorators/applyNode.js +73 -1
- package/dist/esm/decorators/applyNode.js.map +3 -3
- package/dist/esm/decorators/applyProperty.js +107 -3
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applySlot.js +6 -5
- package/dist/esm/decorators/applySlot.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +42 -5
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/changedAttribute.js +62 -13
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +261 -446
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +55 -0
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/eventMedia.js +66 -0
- package/dist/esm/decorators/eventMedia.js.map +7 -0
- package/dist/esm/decorators/index.js +6 -1
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +182 -0
- package/dist/esm/decorators/intersectionObserver.js.map +7 -0
- package/dist/esm/decorators/mutationObserver.js +187 -55
- package/dist/esm/decorators/mutationObserver.js.map +3 -3
- package/dist/esm/decorators/parameter.js +54 -0
- package/dist/esm/decorators/parameter.js.map +7 -0
- package/dist/esm/decorators/query.js +117 -102
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/requestAnimationFrame.js +105 -0
- package/dist/esm/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/esm/decorators/resizeObserver.js +134 -55
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +95 -0
- package/dist/esm/decorators/setInterval.js.map +7 -0
- package/dist/esm/decorators/setTimeout.js +98 -0
- package/dist/esm/decorators/setTimeout.js.map +7 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +9 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js +9 -0
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +20 -4
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +4961 -1628
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/config/config.d.ts.map +1 -1
- package/dist/types/decorators/addEventListener.d.ts +1046 -33
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +24 -34
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +36 -34
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +32 -20
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +61 -34
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applySlot.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +27 -12
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/changedAttribute.d.ts +12 -3
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts +0 -14
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +28 -4
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/eventMedia.d.ts +25 -0
- package/dist/types/decorators/eventMedia.d.ts.map +1 -0
- package/dist/types/decorators/index.d.ts +6 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/intersectionObserver.d.ts +58 -0
- package/dist/types/decorators/intersectionObserver.d.ts.map +1 -0
- package/dist/types/decorators/mutationObserver.d.ts +44 -25
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/parameter.d.ts +35 -0
- package/dist/types/decorators/parameter.d.ts.map +1 -0
- package/dist/types/decorators/query.d.ts +57 -14
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/requestAnimationFrame.d.ts +47 -0
- package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -0
- package/dist/types/decorators/resizeObserver.d.ts +43 -23
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts +41 -0
- package/dist/types/decorators/setInterval.d.ts.map +1 -0
- package/dist/types/decorators/setTimeout.d.ts +41 -0
- package/dist/types/decorators/setTimeout.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +7 -1
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +6 -0
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +104 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +4961 -1628
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +7 -5
- package/src/config/config.ts +1 -1
- package/src/decorators/addEventListener.ts +1446 -34
- package/src/decorators/applyAttribute.ts +60 -36
- package/src/decorators/applyClass.ts +86 -26
- package/src/decorators/applyNode.ts +124 -36
- package/src/decorators/applyProperty.ts +172 -40
- package/src/decorators/applySlot.ts +6 -5
- package/src/decorators/applyStyle.ts +74 -17
- package/src/decorators/changedAttribute.ts +76 -15
- package/src/decorators/elementDefine.ts +310 -582
- package/src/decorators/emitCustomEvent.ts +73 -5
- package/src/decorators/eventMedia.ts +78 -0
- package/src/decorators/index.ts +6 -1
- package/src/decorators/intersectionObserver.ts +275 -0
- package/src/decorators/mutationObserver.ts +282 -89
- package/src/decorators/parameter.ts +81 -0
- package/src/decorators/query.ts +168 -126
- package/src/decorators/requestAnimationFrame.ts +158 -0
- package/src/decorators/resizeObserver.ts +211 -89
- package/src/decorators/setInterval.ts +136 -0
- package/src/decorators/setTimeout.ts +139 -0
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +17 -0
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +19 -0
- package/src/elements/SwcAppMixin.ts +20 -5
- package/src/types/index.ts +141 -0
- package/src/types/window.d.ts +2 -18
- package/dist/cjs/decorators/queryAll.js +0 -181
- package/dist/cjs/decorators/queryAll.js.map +0 -7
- package/dist/cjs/examples/RxJSOperatorsExample.js +0 -285
- package/dist/cjs/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/esm/decorators/queryAll.js +0 -162
- package/dist/esm/decorators/queryAll.js.map +0 -7
- package/dist/esm/examples/RxJSOperatorsExample.js +0 -266
- package/dist/esm/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/types/decorators/queryAll.d.ts +0 -22
- package/dist/types/decorators/queryAll.d.ts.map +0 -1
- package/dist/types/examples/RxJSOperatorsExample.d.ts +0 -94
- package/dist/types/examples/RxJSOperatorsExample.d.ts.map +0 -1
- package/src/decorators/queryAll.ts +0 -186
- package/src/examples/RxJSOperatorsExample.ts +0 -279
|
@@ -1,39 +1,34 @@
|
|
|
1
|
-
import {ActionExpression, FunctionUtils, ReflectUtils
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {getResizeObserverMetadata} from './resizeObserver';
|
|
7
|
-
import type {ResizeObserverMetadata} from './resizeObserver';
|
|
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';
|
|
8
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';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {findAllAttributeApplyMetadata, findAllAttributeMetadata
|
|
12
|
-
import {getQueryMetadata} from './query';
|
|
13
|
-
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';
|
|
14
11
|
import {SwcUtils} from '../utils/Utils';
|
|
15
12
|
import {DOM_EVENT_NAMES, HTML_TAG_ENTRIES} from '../config/config';
|
|
16
13
|
import {SituationTypeContainer, SituationTypeContainers} from '@dooboostore/simple-boot/decorators/inject/Inject';
|
|
17
|
-
import {
|
|
14
|
+
import {FirstCheckMaker} from '@dooboostore/simple-boot';
|
|
15
|
+
import {buildSwcParameterArgs, getParameterMetadata} from './parameter';
|
|
16
|
+
import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
|
|
18
17
|
import {ConvertUtils, ElementApply} from '@dooboostore/core-web';
|
|
19
18
|
import {isSSR} from "../elements/SwcAppMixin";
|
|
20
19
|
import {findAllStateMetadata} from "./state";
|
|
21
20
|
import {findAllPropertyMetadata} from "./applyProperty";
|
|
21
|
+
import {MessageSubscribeLifeCycler} from "./subscribeSwcAppMessageWhileConnected";
|
|
22
|
+
import {RouteSubscribeLifeCycler} from "./subscribeSwcAppRouteChangeWhileConnected";
|
|
23
|
+
import {SetIntervalLifeCycler} from "./setInterval";
|
|
24
|
+
import {SetTimeoutLifeCycler} from "./setTimeout";
|
|
25
|
+
import {RequestAnimationFrameLifeCycler} from "./requestAnimationFrame";
|
|
26
|
+
import {EventMediaLifeCycler} from "./eventMedia";
|
|
22
27
|
|
|
23
28
|
// --- Core Interfaces & Types ---
|
|
24
29
|
|
|
25
30
|
export const ELEMENT_CONFIG_KEY = Symbol.for('simple-web-component:element-config');
|
|
26
31
|
|
|
27
|
-
/** 바인딩된 이벤트 리스너 한 건 */
|
|
28
|
-
export interface BoundListener {
|
|
29
|
-
target: EventTarget;
|
|
30
|
-
type: string;
|
|
31
|
-
handler: EventListener;
|
|
32
|
-
options: AddEventListenerOptions;
|
|
33
|
-
onRemoves?: Array<{ fn: (target: EventTarget, optionValue: unknown) => void; opts: unknown }>;
|
|
34
|
-
subscription?: { unsubscribe: () => void };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
32
|
export interface ElementConfig {
|
|
38
33
|
extends?: string;
|
|
39
34
|
observedAttributes?: string[];
|
|
@@ -245,6 +240,13 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
245
240
|
if (typeof (this as any)[methodName] !== 'function') return;
|
|
246
241
|
const useHostSet = hostSet ?? SwcUtils.getHelperAndHostSet(win, this);
|
|
247
242
|
const app = useHostSet?.$appHost?.simpleApplication;
|
|
243
|
+
|
|
244
|
+
// @hostSet/@helperHostSet/@helperSet(parameter.ts)를 위한 값. lifecycle 메서드는
|
|
245
|
+
// event/matched 개념이 없으므로 이 세 가지만 채운다.
|
|
246
|
+
const helperSetValue = SwcUtils.getHelperSet(win);
|
|
247
|
+
const helperHostSetValue = {...helperSetValue, ...useHostSet, $this: this};
|
|
248
|
+
const kindValues = {hostSet: useHostSet, helperHostSet: helperHostSetValue, helperSet: helperSetValue};
|
|
249
|
+
|
|
248
250
|
// console.log('---->hh',app, this, methodName);
|
|
249
251
|
if (app) {
|
|
250
252
|
const otherStorage = new Map<any, any>();
|
|
@@ -257,16 +259,30 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
257
259
|
}), new SituationTypeContainer({situationType: InjectSituationType.LAST_APP_HOST, data: useHostSet.$lastAppHost})]);
|
|
258
260
|
otherStorage.set(SituationTypeContainers, situations);
|
|
259
261
|
|
|
262
|
+
// @hostSet/@helperHostSet/@helperSet가 붙은 파라미터는 @Inject/situationType 기반
|
|
263
|
+
// 해석보다 먼저 처리되고(firstCheckMaker), 그 외 파라미터는 기존처럼 @Inject/타입
|
|
264
|
+
// 기반으로 정상 해석된다 — 같은 메서드에서 두 방식을 섞어 써도 된다.
|
|
265
|
+
const firstCheckMaker: FirstCheckMaker = ({target, targetKey}, token, idx) => {
|
|
266
|
+
const saves = getParameterMetadata(target, targetKey!);
|
|
267
|
+
const found = saves.find(s => s.index === idx);
|
|
268
|
+
return found ? (kindValues as any)[found.kind] : undefined;
|
|
269
|
+
};
|
|
270
|
+
|
|
260
271
|
return app.simstanceManager.executeBindParameterSim(
|
|
261
272
|
{
|
|
262
273
|
target: this,
|
|
263
274
|
targetKey: methodName,
|
|
264
|
-
inputParameters: extraArgs
|
|
275
|
+
inputParameters: extraArgs,
|
|
276
|
+
firstCheckMaker: [firstCheckMaker]
|
|
265
277
|
},
|
|
266
278
|
otherStorage
|
|
267
279
|
);
|
|
268
280
|
} else {
|
|
269
|
-
|
|
281
|
+
// SimpleApplication(DI 컨테이너)이 없는 standalone 사용 시에도 @hostSet/@helperHostSet/
|
|
282
|
+
// @helperSet만큼은 buildSwcParameterArgs로 주입해준다 — 해당 데코레이터가 하나도 없으면
|
|
283
|
+
// extraArgs를 그대로 반환하므로(기존 동작 그대로) 하위호환 깨지지 않는다.
|
|
284
|
+
const args = buildSwcParameterArgs(this, methodName, kindValues, extraArgs);
|
|
285
|
+
return (this as any)[methodName](...args);
|
|
270
286
|
}
|
|
271
287
|
};
|
|
272
288
|
|
|
@@ -350,12 +366,6 @@ export const elementDefine =
|
|
|
350
366
|
metadata.extends = extendsTagName;
|
|
351
367
|
}
|
|
352
368
|
|
|
353
|
-
const emitCustomEventList = getEmitCustomEventMetadataList(constructor) || [];
|
|
354
|
-
const emitHostCustomEventList = emitCustomEventList.filter(meta => meta.selector === '$this' || meta.selector === '');
|
|
355
|
-
const addEventListenerList = getAddEventListenerMetadata(constructor) || [];
|
|
356
|
-
const mutationObserverList = getMutationObserverMetadata(constructor) || [];
|
|
357
|
-
const resizeObserverList = getResizeObserverMetadata(constructor) || [];
|
|
358
|
-
const attrChangeMap = findAllAttributeChangedMetadata(constructor);
|
|
359
369
|
const attributeList = findAllAttributeMetadata(constructor);
|
|
360
370
|
const applyAttributeMap = findAllAttributeApplyMetadata(constructor);
|
|
361
371
|
|
|
@@ -368,37 +378,258 @@ export const elementDefine =
|
|
|
368
378
|
const hostAttributes = attributeList.filter(it => it.selector === '$this').map(it => it.propertyKey || String(it.propertyKey));
|
|
369
379
|
// Get original static observedAttributes before they're overwritten by Object.defineProperty
|
|
370
380
|
const originalStaticObservedAttributes = (constructor.observedAttributes ?? []) as string[];
|
|
371
|
-
const mergedObservedAttributes = [...new Set([...(metadata.observedAttributes ?? []), ...originalStaticObservedAttributes, ...attrChangeMap.keys(), ...attributeApplyNames, ...hostAttributes, ...emitHostCustomEventList.map(it => (it.options as any).attributeName).filter(Boolean), ...swcLifecycleAttributes, ...swcOnEvents])];
|
|
372
|
-
// const connectedInnerHtmlList = getOnConnectedInnerHtmlMetadata(constructor) || [];
|
|
373
381
|
|
|
374
382
|
const proto = constructor.prototype;
|
|
375
383
|
setupPrototype(proto, win);
|
|
376
384
|
|
|
377
385
|
// 값을 기대하지않는다.
|
|
378
386
|
let helperHostSet: HelperHostSet | null = null;
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
387
|
+
// ── cyclers: 데코레이터별 라이프사이클 위임 클래스 (elementDefine 시 1회 생성, 계속 재사용) ──
|
|
388
|
+
const eventCycler = new EventListenerLifeCycler();
|
|
389
|
+
const cyclers: ElementDefineLifeCycler[] = [
|
|
390
|
+
eventCycler,
|
|
391
|
+
new ChangedAttributeLifeCycler(),
|
|
392
|
+
new EmitCustomEventLifeCycler(),
|
|
393
|
+
new ResizeObserverLifeCycler(),
|
|
394
|
+
new IntersectionObserverLifeCycler(),
|
|
395
|
+
new MutationObserverLifeCycler(),
|
|
396
|
+
new MessageSubscribeLifeCycler(),
|
|
397
|
+
new RouteSubscribeLifeCycler(),
|
|
398
|
+
new SetIntervalLifeCycler(),
|
|
399
|
+
new SetTimeoutLifeCycler(),
|
|
400
|
+
new RequestAnimationFrameLifeCycler(),
|
|
401
|
+
new EventMediaLifeCycler(),
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
// observedAttributes 기여 cycler (define-time, constructor 기반)
|
|
405
|
+
const cyclerObservedAttributes = cyclers.flatMap(c => c.getObservedAttributeNames?.(constructor) ?? []);
|
|
406
|
+
const mergedObservedAttributes = [...new Set([...(metadata.observedAttributes ?? []), ...originalStaticObservedAttributes, ...cyclerObservedAttributes, ...attributeApplyNames, ...hostAttributes, ...swcLifecycleAttributes, ...swcOnEvents])];
|
|
407
|
+
|
|
408
|
+
// 렌더 후 모든 시클러 onConnected 호출 → Set 수집 후 observer 생성
|
|
409
|
+
const buildObservers = async (helperHostSet: HelperHostSet): Promise<void> => {
|
|
410
|
+
const inst = helperHostSet.$this;
|
|
411
|
+
const observers: Array<MutationObserver | ResizeObserver | IntersectionObserver> = [];
|
|
412
|
+
let resizeObserverSet: ResizeObserverSet = [];
|
|
413
|
+
let mutationObserverSet: MutationObserverSet = [];
|
|
414
|
+
let intersectionObserverSet: IntersectionObserverSet = [];
|
|
415
|
+
|
|
416
|
+
// delegate 추적 root 해석: shadow/light/auto/all → 실제 root 목록
|
|
417
|
+
const resolveDelegateRoots = (delegateRoot: SwcRootType | undefined): (HTMLElement | ShadowRoot)[] => {
|
|
418
|
+
const r = delegateRoot || 'auto';
|
|
419
|
+
const roots: (HTMLElement | ShadowRoot)[] = [];
|
|
420
|
+
if (r === 'auto') roots.push(inst.shadowRoot || inst);
|
|
421
|
+
else if (r === 'light') roots.push(inst);
|
|
422
|
+
else if (r === 'shadow' && inst.shadowRoot) roots.push(inst.shadowRoot);
|
|
423
|
+
else if (r === 'all') { roots.push(inst); if (inst.shadowRoot) roots.push(inst.shadowRoot); }
|
|
424
|
+
return roots;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// 1. 모든 시클러 onConnected → observer Set 수집 (여러 시클러가 반환한 Set 을 전부 누적)
|
|
428
|
+
for (const c of cyclers) {
|
|
429
|
+
const result = (await c.onConnected?.(helperHostSet, resizeObserverSet)) as OnConnectedResult | void;
|
|
430
|
+
if (!result) continue;
|
|
431
|
+
if (result.resizeObserverSet?.length) resizeObserverSet = [...resizeObserverSet, ...result.resizeObserverSet];
|
|
432
|
+
if (result.mutationObserverSet?.length) mutationObserverSet = [...mutationObserverSet, ...result.mutationObserverSet];
|
|
433
|
+
if (result.intersectionObserverSet?.length) intersectionObserverSet = [...intersectionObserverSet, ...result.intersectionObserverSet];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// 2. 타입별로 observer 생성
|
|
437
|
+
// ResizeObserver 먼저: mutation callback 이 observe/unobserve 를 주입받아야 함
|
|
438
|
+
// observeTargets 중 delegate 항목은 셀렉터 문자열이라 초기 observe 하지 않는다 (동적 추적 전용).
|
|
439
|
+
// 동적 observe/unobserve 는 중복 호출을 막기 위해 observe 대상 WeakSet 으로 추적한다.
|
|
440
|
+
const resizeDelegates = (resizeObserverSet ?? []).filter(t => t.delegate && typeof t.target === 'string') as Array<ResizeObserverSetEntry & { target: string }>;
|
|
441
|
+
let resizeObserve: ((el: Element, opts?: ResizeObserverOptions) => void) | undefined;
|
|
442
|
+
let resizeUnobserve: ((el: Element) => void) | undefined;
|
|
443
|
+
const resizeObserved = new WeakSet<Element>();
|
|
444
|
+
if (resizeObserverSet?.length) {
|
|
445
|
+
const roCallbacks = [...new Set(resizeObserverSet.map(e => e.callback))];
|
|
446
|
+
const ro = new win.ResizeObserver((entries, obs) => {
|
|
447
|
+
for (const cb of roCallbacks) cb(entries, obs);
|
|
448
|
+
});
|
|
449
|
+
for (const e of resizeObserverSet) {
|
|
450
|
+
if (e.delegate) continue;
|
|
451
|
+
if (typeof e.target === 'string') {
|
|
452
|
+
// 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
|
|
453
|
+
const scope = resolveDelegateRoots(e.delegateRoot);
|
|
454
|
+
for (const sr of scope) {
|
|
455
|
+
sr.querySelectorAll(e.target).forEach(el => {
|
|
456
|
+
if (!resizeObserved.has(el)) { ro.observe(el, e.options); resizeObserved.add(el); }
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
ro.observe(e.target as Element, e.options);
|
|
462
|
+
if (e.target instanceof Element) resizeObserved.add(e.target);
|
|
463
|
+
}
|
|
464
|
+
resizeObserve = (el, opts) => {
|
|
465
|
+
if (resizeObserved.has(el)) return;
|
|
466
|
+
ro.observe(el, opts);
|
|
467
|
+
resizeObserved.add(el);
|
|
468
|
+
};
|
|
469
|
+
resizeUnobserve = (el) => {
|
|
470
|
+
if (!resizeObserved.has(el)) return;
|
|
471
|
+
ro.unobserve(el);
|
|
472
|
+
resizeObserved.delete(el);
|
|
473
|
+
};
|
|
474
|
+
observers.push(ro);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// IntersectionObserver: 옵션 그룹별로 1개씩 생성.
|
|
478
|
+
// delegate 항목은 동적 추적용 셀렉터이므로 observe/unobserve 함수를 MutationObserver 콜백에 넘긴다.
|
|
479
|
+
// 동적 observe/unobserve 중복 방지를 위해 그룹별 WeakSet 으로 추적한다.
|
|
480
|
+
const intersectionDelegates: Array<{ target: string; delegateRoot?: SwcRootType; observe: (el: Element) => void; unobserve: (el: Element) => void }> = [];
|
|
481
|
+
for (const group of intersectionObserverSet) {
|
|
482
|
+
const ioCallbacks = [...new Set(group.observeTargets.map(e => e.callback))];
|
|
483
|
+
const io = new win.IntersectionObserver((entries, obs) => {
|
|
484
|
+
for (const cb of ioCallbacks) cb(entries, obs);
|
|
485
|
+
}, group.options);
|
|
486
|
+
const ioObserved = new WeakSet<Element>();
|
|
487
|
+
for (const e of group.observeTargets) {
|
|
488
|
+
if (e.delegate) {
|
|
489
|
+
const target = e.target as string;
|
|
490
|
+
const observe = (el: Element) => {
|
|
491
|
+
if (ioObserved.has(el)) return;
|
|
492
|
+
io.observe(el);
|
|
493
|
+
ioObserved.add(el);
|
|
494
|
+
};
|
|
495
|
+
const unobserve = (el: Element) => {
|
|
496
|
+
if (!ioObserved.has(el)) return;
|
|
497
|
+
io.unobserve(el);
|
|
498
|
+
ioObserved.delete(el);
|
|
499
|
+
};
|
|
500
|
+
intersectionDelegates.push({ target, delegateRoot: e.delegateRoot, observe, unobserve });
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (typeof e.target === 'string') {
|
|
504
|
+
// 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
|
|
505
|
+
const scope = resolveDelegateRoots(e.delegateRoot);
|
|
506
|
+
for (const sr of scope) {
|
|
507
|
+
sr.querySelectorAll(e.target).forEach(el => {
|
|
508
|
+
if (!ioObserved.has(el)) { io.observe(el); ioObserved.add(el); }
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
io.observe(e.target as Element);
|
|
514
|
+
if (e.target instanceof Element) ioObserved.add(e.target);
|
|
515
|
+
}
|
|
516
|
+
observers.push(io);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// resize/intersection delegate 동적 추적 — MutationObserver 콜백에서 추가/제거된 요소를 observe/unobserve 한다.
|
|
520
|
+
const handleResizeDelegate = (n: Node, observe: boolean) => {
|
|
521
|
+
if (!resizeObserve || !resizeUnobserve || n.nodeType !== 1) return;
|
|
522
|
+
const el = n as HTMLElement;
|
|
523
|
+
for (const d of resizeDelegates) {
|
|
524
|
+
const isThis = d.target === '$this' || d.target === '';
|
|
525
|
+
const matchesSel = (node: any) => {
|
|
526
|
+
if (!node || node.nodeType !== 1) return false;
|
|
527
|
+
if (isThis) return true;
|
|
528
|
+
return (node as HTMLElement).matches?.(d.target) || !!(node as HTMLElement).closest?.(d.target);
|
|
529
|
+
};
|
|
530
|
+
if (matchesSel(el)) {
|
|
531
|
+
observe ? resizeObserve(el, d.options)
|
|
532
|
+
: resizeUnobserve(el);
|
|
533
|
+
} else if (!isThis) {
|
|
534
|
+
el.querySelectorAll(d.target).forEach(sub => {
|
|
535
|
+
observe ? resizeObserve(sub as HTMLElement, d.options)
|
|
536
|
+
: resizeUnobserve(sub as HTMLElement);
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const handleIntersectionDelegate = (n: Node, observe: boolean) => {
|
|
542
|
+
if (n.nodeType !== 1) return;
|
|
543
|
+
const el = n as HTMLElement;
|
|
544
|
+
for (const d of intersectionDelegates) {
|
|
545
|
+
const isThis = d.target === '$this' || d.target === '';
|
|
546
|
+
const matchesSel = (node: any) => {
|
|
547
|
+
if (!node || node.nodeType !== 1) return false;
|
|
548
|
+
if (isThis) return true;
|
|
549
|
+
return (node as HTMLElement).matches?.(d.target) || !!(node as HTMLElement).closest?.(d.target);
|
|
550
|
+
};
|
|
551
|
+
if (matchesSel(el)) {
|
|
552
|
+
observe ? d.observe(el) : d.unobserve(el);
|
|
553
|
+
} else if (!isThis) {
|
|
554
|
+
el.querySelectorAll(d.target).forEach(sub => {
|
|
555
|
+
observe ? d.observe(sub as HTMLElement) : d.unobserve(sub as HTMLElement);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
if (mutationObserverSet?.length || resizeDelegates.length > 0 || intersectionDelegates.length > 0) {
|
|
562
|
+
// 하나의 MutationObserver 로 resize/intersection delegate 동적 추적 + mutation 콜백을 함께 처리
|
|
563
|
+
const callbacks = [...new Set((mutationObserverSet ?? []).map(e => e.callback))];
|
|
564
|
+
const mo = new win.MutationObserver((mutations, obs) => {
|
|
565
|
+
for (const mut of mutations) {
|
|
566
|
+
Array.from(mut.addedNodes || []).forEach(n => { handleResizeDelegate(n as Node, true); handleIntersectionDelegate(n as Node, true); });
|
|
567
|
+
Array.from(mut.removedNodes || []).forEach(n => { handleResizeDelegate(n as Node, false); handleIntersectionDelegate(n as Node, false); });
|
|
568
|
+
}
|
|
569
|
+
for (const cb of callbacks) cb(mutations, obs);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// observe 대상 전체(엔트리 + delegate 추적 root)를 하나로 모아 중복 observe 를 제거한다.
|
|
573
|
+
// 같은 target 이 여러 소스에서 지정되면 MutationObserverInit 을 합집합(최대 감지범위)으로 병합한다.
|
|
574
|
+
const moObserveTargets = new Map<Element | ShadowRoot, MutationObserverInit>();
|
|
575
|
+
const mergeMutationInit = (existing: MutationObserverInit | undefined, next: MutationObserverInit): MutationObserverInit => {
|
|
576
|
+
if (!existing) return next;
|
|
577
|
+
const mergedFilter = existing.attributeFilter || next.attributeFilter
|
|
578
|
+
? [...new Set([...(existing.attributeFilter ?? []), ...(next.attributeFilter ?? [])])]
|
|
579
|
+
: undefined;
|
|
580
|
+
return {
|
|
581
|
+
childList: existing.childList || next.childList,
|
|
582
|
+
attributes: existing.attributes || next.attributes,
|
|
583
|
+
characterData: existing.characterData || next.characterData,
|
|
584
|
+
subtree: existing.subtree || next.subtree,
|
|
585
|
+
attributeOldValue: existing.attributeOldValue || next.attributeOldValue,
|
|
586
|
+
characterDataOldValue: existing.characterDataOldValue || next.characterDataOldValue,
|
|
587
|
+
attributeFilter: mergedFilter,
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
const addObserveTarget = (target: Element | ShadowRoot, options: MutationObserverInit) => {
|
|
591
|
+
moObserveTargets.set(target, mergeMutationInit(moObserveTargets.get(target), options));
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
// 1) mutationObserverSet 엔트리
|
|
595
|
+
for (const e of mutationObserverSet ?? []) {
|
|
596
|
+
if (e.delegate) continue;
|
|
597
|
+
const init = e.options ?? { childList: true };
|
|
598
|
+
if (typeof e.target === 'string') {
|
|
599
|
+
// 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
|
|
600
|
+
const scope = resolveDelegateRoots(e.delegateRoot);
|
|
601
|
+
for (const sr of scope) {
|
|
602
|
+
sr.querySelectorAll(e.target).forEach(el => addObserveTarget(el, init));
|
|
603
|
+
}
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
addObserveTarget(e.target, init);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// 2) delegate 추적 root: shadow/light/all/auto 로 해석해 전부 observe
|
|
610
|
+
for (const d of resizeDelegates) {
|
|
611
|
+
for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
|
|
612
|
+
}
|
|
613
|
+
for (const d of intersectionDelegates) {
|
|
614
|
+
for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
for (const [target, options] of moObserveTargets) {
|
|
618
|
+
mo.observe(target, options);
|
|
619
|
+
}
|
|
620
|
+
observers.push(mo);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
inst.__swc_observers = observers;
|
|
624
|
+
};
|
|
625
|
+
|
|
388
626
|
const originalConnected = proto.connectedCallback;
|
|
389
627
|
proto.connectedCallback = async function () {
|
|
390
628
|
ensureInit(this);
|
|
391
|
-
// 재연결 시 누적 방지 —
|
|
392
|
-
boundListeners = [];
|
|
393
|
-
resizeObservers = [];
|
|
394
|
-
removeObserverCallbacks = [];
|
|
395
|
-
// connected 됐을시 최신화
|
|
629
|
+
// 재연결 시 누적 방지 — cycler 내부 리소스 초기화
|
|
396
630
|
helperHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
|
|
397
631
|
const appHost = helperHostSet.$appHost;
|
|
398
632
|
const useSsr = isSSR(this);
|
|
399
|
-
// console.log('isSsr', useSsr, this);
|
|
400
|
-
// console.log('----helperHostSet--->', this, helperHostSet, useSsr)
|
|
401
|
-
// let connectedApp: SwcAppInterface |undefined;
|
|
402
633
|
try {
|
|
403
634
|
if (appHost && typeof (appHost as any)._connected === 'function') {
|
|
404
635
|
await (appHost as any)._connected(this);
|
|
@@ -407,27 +638,19 @@ export const elementDefine =
|
|
|
407
638
|
(appHost as any)._connected_safari_and_standby.push(this);
|
|
408
639
|
}
|
|
409
640
|
|
|
410
|
-
// const hostSet = SwcUtils.getHostSet(this as any);
|
|
411
641
|
const conf = getElementConfig(this);
|
|
412
642
|
const currentWin = (this as any)._resolveWindow(conf);
|
|
413
643
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
for (const m of bMethods) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
644
|
+
// before-connected 라이프사이클 메서드 (@onConnectedBefore)
|
|
645
|
+
for (const m of findAllOnConnectedBeforeMetadata(this)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
417
646
|
(this as any)._executeSwcScript('swc-on-before-connected', helperHostSet);
|
|
418
647
|
|
|
419
|
-
//
|
|
420
|
-
// // ssr 처리라서 이미 내려준거그대로 상요하면된다 따라서 호출안한다
|
|
421
|
-
// const targetConnectedList = findAllOnConnectedMetadata(constructor).filter(it => useSsr ? !it.options.ssrFirst : true);
|
|
648
|
+
// ── 렌더 (@onConnected / @onConnectedBody*) — elementDefine 책임 ──
|
|
422
649
|
const targetConnectedList = useSsr ? [] : findAllOnConnectedMetadata(constructor);
|
|
423
650
|
const shadowMode = conf?.useShadow || targetConnectedList.find(it => it.options.useShadow)?.options.useShadow;
|
|
424
|
-
// console.log('--------->', this.tagName, shadowMode && !this.shadowRoot)
|
|
425
651
|
if (shadowMode && !this.shadowRoot) {
|
|
426
652
|
const mode = shadowMode === true ? 'open' : shadowMode;
|
|
427
|
-
console.log('tagName=', this.tagName, 'mode', mode, this.shadowRoot);
|
|
428
|
-
// if (this.tagName!=='INDEX-ROUTER') {
|
|
429
653
|
this.attachShadow({ mode: mode as ShadowRootMode });
|
|
430
|
-
// }
|
|
431
654
|
}
|
|
432
655
|
|
|
433
656
|
const stateContext: any = { ...helperHostSet };
|
|
@@ -438,20 +661,14 @@ export const elementDefine =
|
|
|
438
661
|
const shadowChildren: Node[] = [];
|
|
439
662
|
const lightChildren: Node[] = [];
|
|
440
663
|
|
|
441
|
-
console.log('targetConnectList', this.tagName, targetConnectedList.length);
|
|
442
664
|
if (targetConnectedList.length > 0) {
|
|
443
|
-
// let sContent = '',
|
|
444
|
-
// lContent = '';
|
|
445
665
|
for (const meta of targetConnectedList) {
|
|
446
|
-
// const res = (this as any)[meta.propertyKey]();
|
|
447
666
|
let res = await (this as any)._invokeLifecycleMethod(meta.propertyKey, helperHostSet);
|
|
448
|
-
// res = SwcUtils.projectProcessHtml(this, res);
|
|
449
667
|
if (typeof res === 'string') {
|
|
450
668
|
const htmlTemplateElement = doc.createElement('template');
|
|
451
|
-
htmlTemplateElement.innerHTML = res;
|
|
669
|
+
htmlTemplateElement.innerHTML = res;
|
|
452
670
|
res = htmlTemplateElement.content;
|
|
453
671
|
}
|
|
454
|
-
|
|
455
672
|
if (res) {
|
|
456
673
|
const nodes = Array.isArray(res) ? res : [res];
|
|
457
674
|
if (meta.options.useShadow || conf?.useShadow) {
|
|
@@ -487,440 +704,20 @@ export const elementDefine =
|
|
|
487
704
|
globalDelegatedRoots.add(root);
|
|
488
705
|
}
|
|
489
706
|
|
|
490
|
-
//
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
addEventListenerList.forEach(meta => {
|
|
495
|
-
// Function-based selectors cannot be used with delegate listeners
|
|
496
|
-
const isStringSelector = typeof meta.selector === 'string';
|
|
497
|
-
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 !== '') {
|
|
498
|
-
delegateListeners.push(meta);
|
|
499
|
-
} else {
|
|
500
|
-
nonDelegateListeners.push(meta);
|
|
501
|
-
}
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
const delegatesByTypeAndRoot = new Map<string, any[]>();
|
|
505
|
-
delegateListeners.forEach(meta => {
|
|
506
|
-
const r = meta.options.root || 'auto';
|
|
507
|
-
const rootKey = `${meta.type}:${r}`;
|
|
508
|
-
if (!delegatesByTypeAndRoot.has(rootKey)) {
|
|
509
|
-
delegatesByTypeAndRoot.set(rootKey, []);
|
|
510
|
-
}
|
|
511
|
-
delegatesByTypeAndRoot.get(rootKey)!.push(meta);
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
delegatesByTypeAndRoot.forEach((metaList, typeRootKey) => {
|
|
515
|
-
const [type, rStr] = typeRootKey.split(':');
|
|
516
|
-
const r = rStr === 'auto' ? 'auto' : rStr;
|
|
517
|
-
const opts = { capture: metaList[0].options.capture, once: metaList[0].options.once, passive: metaList[0].options.passive };
|
|
518
|
-
|
|
519
|
-
const bindRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
520
|
-
if (r === 'auto') bindRoots.push(this.shadowRoot || (this as any));
|
|
521
|
-
else if (r === 'light') bindRoots.push(this as any);
|
|
522
|
-
else if (r === 'shadow' && this.shadowRoot) bindRoots.push(this.shadowRoot);
|
|
523
|
-
else if (r === 'all') {
|
|
524
|
-
bindRoots.push(this as any);
|
|
525
|
-
if (this.shadowRoot) bindRoots.push(this.shadowRoot);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
bindRoots.forEach(br => {
|
|
529
|
-
const sortedMetaList = [...metaList].sort((a, b) => {
|
|
530
|
-
const aStop = a.options.stopPropagation ? 1 : 0;
|
|
531
|
-
const bStop = b.options.stopPropagation ? 1 : 0;
|
|
532
|
-
return bStop - aStop;
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
const unifiedHandler = async (event: Event) => {
|
|
536
|
-
for (const meta of sortedMetaList) {
|
|
537
|
-
const { selector, options } = meta;
|
|
538
|
-
const matchedEl = (event.target as HTMLElement)?.closest(selector);
|
|
539
|
-
if (matchedEl && (br as any).contains(matchedEl)) {
|
|
540
|
-
// Apply filter if specified
|
|
541
|
-
if (options.filter) {
|
|
542
|
-
const helper = SwcUtils.getHelperAndHostSet(currentWin, matchedEl);
|
|
543
|
-
if (!options.filter(event, helper)) {
|
|
544
|
-
continue; // Skip this listener if filter returns false
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
if (options.stopPropagation) event.stopPropagation();
|
|
548
|
-
if (options.stopImmediatePropagation) event.stopImmediatePropagation();
|
|
549
|
-
if (options.preventDefault) event.preventDefault();
|
|
550
|
-
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
551
|
-
const args = { event, ...currentHostSet, $el: matchedEl, $root: br };
|
|
552
|
-
await (this as any)[meta.propertyKey](event, { ...currentHostSet, $matchedElement: matchedEl }, args);
|
|
553
|
-
if ((event as any).cancelBubble) break;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
br.addEventListener(type, unifiedHandler, opts);
|
|
558
|
-
// 각 메타의 removeListener를 메타 옵션과 함께 보관 (delegate 그룹은 여러 메타 가능)
|
|
559
|
-
const onRemoves: Array<{ fn: (target: Element, optionValue: any) => void; opts: any }> = [];
|
|
560
|
-
for (const m of sortedMetaList) {
|
|
561
|
-
if (m.options.removeListener) {
|
|
562
|
-
onRemoves.push({ fn: m.options.removeListener, opts: m.options });
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
boundListeners.push({ target: br, type, handler: unifiedHandler, options: opts, onRemoves });
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
nonDelegateListeners.forEach(meta => {
|
|
570
|
-
const { selector, type, options } = meta;
|
|
571
|
-
const opts = { capture: options.capture, once: options.once, passive: options.passive };
|
|
572
|
-
const bindTargets: EventTarget[] = [];
|
|
573
|
-
const r = options.root || 'auto';
|
|
574
|
-
|
|
575
|
-
// Resolve selector if it's a function
|
|
576
|
-
let resolvedSelector: string | null = null;
|
|
577
|
-
if (typeof selector === 'function') {
|
|
578
|
-
const hostSet = SwcUtils.getHelperAndHostSet(currentWin, this);
|
|
579
|
-
const result = selector(this, hostSet);
|
|
580
|
-
if (typeof result === 'string') {
|
|
581
|
-
resolvedSelector = result;
|
|
582
|
-
} else if (result instanceof currentWin.Element) {
|
|
583
|
-
bindTargets.push(result as EventTarget);
|
|
584
|
-
} else if (result instanceof currentWin.NodeList) {
|
|
585
|
-
bindTargets.push(...Array.from(result as EventTarget[]));
|
|
586
|
-
} else if (Array.isArray(result)) {
|
|
587
|
-
bindTargets.push(...(result as EventTarget[]));
|
|
588
|
-
}
|
|
589
|
-
} else {
|
|
590
|
-
resolvedSelector = selector;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
const applyRootOption = (target: any) => {
|
|
594
|
-
if (!target) return;
|
|
595
|
-
if (r === 'auto') {
|
|
596
|
-
bindTargets.push(target.shadowRoot || target);
|
|
597
|
-
} else {
|
|
598
|
-
if (r === 'light' || r === 'all') bindTargets.push(target);
|
|
599
|
-
if ((r === 'shadow' || r === 'all') && target.shadowRoot) bindTargets.push(target.shadowRoot);
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
if (resolvedSelector) {
|
|
604
|
-
if (resolvedSelector === '$window') bindTargets.push(currentWin);
|
|
605
|
-
else if (resolvedSelector === '$document') bindTargets.push(currentWin.document);
|
|
606
|
-
else if (resolvedSelector === '$host') applyRootOption(helperHostSet.$host);
|
|
607
|
-
else if (resolvedSelector === '$parentHost') applyRootOption(helperHostSet.$parentHost);
|
|
608
|
-
else if (resolvedSelector === '$appHost') applyRootOption(helperHostSet.$appHost);
|
|
609
|
-
else if (resolvedSelector === '$firstHost') applyRootOption(helperHostSet.$firstHost);
|
|
610
|
-
else if (resolvedSelector === '$lastHost') applyRootOption(helperHostSet.$lastHost);
|
|
611
|
-
else if (resolvedSelector === '$firstAppHost') applyRootOption(helperHostSet.$firstAppHost);
|
|
612
|
-
else if (resolvedSelector === '$lastAppHost') applyRootOption(helperHostSet.$lastAppHost);
|
|
613
|
-
else if (resolvedSelector === '$hosts') helperHostSet.$hosts.forEach(applyRootOption);
|
|
614
|
-
else if (resolvedSelector === '$appHosts') helperHostSet.$appHosts.forEach(applyRootOption);
|
|
615
|
-
else if (resolvedSelector === '$this' || !resolvedSelector) applyRootOption(this);
|
|
616
|
-
else {
|
|
617
|
-
const searchRoots: (HTMLElement | ShadowRoot)[] = [];
|
|
618
|
-
if (r === 'auto') searchRoots.push(this.shadowRoot || (this as any));
|
|
619
|
-
else if (r === 'light') searchRoots.push(this as any);
|
|
620
|
-
else if (r === 'shadow' && this.shadowRoot) searchRoots.push(this.shadowRoot);
|
|
621
|
-
else if (r === 'all') {
|
|
622
|
-
searchRoots.push(this as any);
|
|
623
|
-
if (this.shadowRoot) searchRoots.push(this.shadowRoot);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
searchRoots.forEach(sr => {
|
|
627
|
-
sr.querySelectorAll(resolvedSelector).forEach(el => bindTargets.push(el));
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
bindTargets.forEach(t => {
|
|
633
|
-
const handler = async (event: Event) => {
|
|
634
|
-
// Apply filter if specified
|
|
635
|
-
if (options.filter) {
|
|
636
|
-
const helper = SwcUtils.getHelperAndHostSet(currentWin, t as HTMLElement);
|
|
637
|
-
if (!options.filter(event, { currentThis: this, helper })) {
|
|
638
|
-
return; // Skip if filter returns false
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (options.stopPropagation) event.stopPropagation();
|
|
642
|
-
if (options.stopImmediatePropagation) event.stopImmediatePropagation();
|
|
643
|
-
if (options.preventDefault) event.preventDefault();
|
|
644
|
-
const currentHostSet = SwcUtils.getHostSet(this as any);
|
|
645
|
-
const args = { event, ...currentHostSet, $el: t, $root: t };
|
|
646
|
-
await (this as any)[meta.propertyKey](event, { currentHostSet, $matchedElement: event.currentTarget }, args);
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
// Create event stream with Observable-based operators
|
|
650
|
-
const eventSubject = new Subject<Event>();
|
|
651
|
-
let eventStream = eventSubject as any;
|
|
652
|
-
|
|
653
|
-
// Apply operators from @dooboostore/core/operators
|
|
654
|
-
if (options.debounceTime !== undefined && options.debounceTime > 0) {
|
|
655
|
-
eventStream = eventStream.pipe(debounceTime(options.debounceTime));
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
if (options.throttleTime !== undefined && options.throttleTime > 0) {
|
|
659
|
-
eventStream = eventStream.pipe(throttleTime(options.throttleTime));
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
if (options.distinctUntilChanged !== undefined && options.distinctUntilChanged !== false) {
|
|
663
|
-
if (typeof options.distinctUntilChanged === 'function') {
|
|
664
|
-
eventStream = eventStream.pipe(distinctUntilChanged(options.distinctUntilChanged));
|
|
665
|
-
} else {
|
|
666
|
-
eventStream = eventStream.pipe(distinctUntilChanged());
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Subscribe to the event stream
|
|
671
|
-
const subscription = eventStream.subscribe({
|
|
672
|
-
next: (event: Event) => {
|
|
673
|
-
handler(event).catch((err: any) => console.error('Event handler error:', err));
|
|
674
|
-
},
|
|
675
|
-
error: (err: any) => console.error('Event stream error:', err)
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
// Create wrapper handler that emits to the subject
|
|
679
|
-
const wrappedHandler = (event: Event) => {
|
|
680
|
-
eventSubject.next(event);
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
t.addEventListener(type, wrappedHandler, opts);
|
|
684
|
-
const onRemoves: Array<{ fn: (target: Element, optionValue: any) => void; opts: any }> = options.removeListener ? [{ fn: options.removeListener, opts }] : [];
|
|
685
|
-
boundListeners.push({ target: t, type, handler: wrappedHandler, options: opts, subscription, onRemoves });
|
|
686
|
-
});
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
// ─── MutationObserver / ResizeObserver 처리 ───
|
|
690
|
-
// shadow/light 루트별로 각각 observer를 두고, 해당 루트에 데코레이터 메타가 있으면 observer를 생성한다.
|
|
691
|
-
// 두 루트 모두 있으면 observer 2개, 한쪽만 있으면 1개, 없으면 0개.
|
|
692
|
-
shadowMutationObserver = null;
|
|
693
|
-
lightMutationObserver = null;
|
|
694
|
-
resizeObserver = null;
|
|
695
|
-
|
|
696
|
-
// ResizeObserver: @resizeObserver 데코레이터가 하나라도 있으면 observer 하나 생성 (shadow 있으면 shadow 루트, 없으면 light)
|
|
697
|
-
if (resizeObserverList.length > 0) {
|
|
698
|
-
const root: HTMLElement | ShadowRoot = this.shadowRoot || (this as any);
|
|
699
|
-
resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[], obs: ResizeObserver) => {
|
|
700
|
-
for (const m of resizeObserverList) {
|
|
701
|
-
let matchedEls: HTMLElement[] = [];
|
|
702
|
-
if (m.options.delegate && typeof m.selector === 'string') {
|
|
703
|
-
const isThis = m.selector === '$this' || m.selector === '';
|
|
704
|
-
const matchesSel = (node: any): boolean => {
|
|
705
|
-
if (!node || node.nodeType !== 1) return false;
|
|
706
|
-
if (isThis) return true;
|
|
707
|
-
const el = node as HTMLElement;
|
|
708
|
-
return el.matches?.(m.selector as string) || !!el.closest?.(m.selector as string);
|
|
709
|
-
};
|
|
710
|
-
matchedEls = entries.map(e => e.target as HTMLElement).filter(matchesSel);
|
|
711
|
-
} else {
|
|
712
|
-
matchedEls = entries.map(e => e.target as HTMLElement).filter(t => t && t.nodeType === 1);
|
|
713
|
-
}
|
|
714
|
-
if (matchedEls.length === 0) continue;
|
|
715
|
-
if (m.options.filter) {
|
|
716
|
-
const helper = SwcUtils.getHelperAndHostSet(currentWin, this);
|
|
717
|
-
if (!m.options.filter(matchedEls, { currentThis: this, helper })) continue;
|
|
718
|
-
}
|
|
719
|
-
const hostSet = SwcUtils.getHostSet(this as any);
|
|
720
|
-
(this as any)[m.propertyKey](matchedEls, entries, obs, { ...hostSet, $root: root });
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
resizeObservers.push(resizeObserver);
|
|
724
|
-
// resizeObserver 메타별 removeObserver 수집
|
|
725
|
-
for (const m of resizeObserverList) {
|
|
726
|
-
if (m.options.removeObserver) {
|
|
727
|
-
removeObserverCallbacks.push({ fn: m.options.removeObserver, target: root, opts: m.options });
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
// MutationObserver: shadow/light 루트별 그룹핑
|
|
732
|
-
const shadowMetas = mutationObserverList.filter(m => {
|
|
733
|
-
const r = m.options.root || 'auto';
|
|
734
|
-
return this.shadowRoot && (r === 'shadow' || r === 'all' || r === 'auto');
|
|
735
|
-
});
|
|
736
|
-
const lightMetas = mutationObserverList.filter(m => {
|
|
737
|
-
const r = m.options.root || 'auto';
|
|
738
|
-
return r === 'light' || r === 'all' || (!this.shadowRoot && r === 'auto');
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
const setupMutationObserver = (root: HTMLElement | ShadowRoot, metas: MutationObserverMetadata[], resizeDelegates: ResizeObserverMetadata[] = []): MutationObserver | null => {
|
|
742
|
-
// mutation 메타가 없으면 observer 생성 안 함 (resize delegate는 보조)
|
|
743
|
-
if (metas.length === 0) return null;
|
|
744
|
-
|
|
745
|
-
// 사용자가 입력한 옵션 그대로 (아무것도 안 주면 childList 기본)
|
|
746
|
-
const initFrom = (o: MutationObserverBaseOptions): MutationObserverInit => {
|
|
747
|
-
const init: MutationObserverInit = {
|
|
748
|
-
childList: o.childList,
|
|
749
|
-
attributes: o.attributes,
|
|
750
|
-
characterData: o.characterData,
|
|
751
|
-
subtree: o.subtree,
|
|
752
|
-
attributeFilter: o.attributeFilter,
|
|
753
|
-
attributeOldValue: o.attributeOldValue,
|
|
754
|
-
characterDataOldValue: o.characterDataOldValue
|
|
755
|
-
};
|
|
756
|
-
// MutationObserver는 최소 1개는 true여야 함
|
|
757
|
-
if (!init.childList && !init.attributes && !init.characterData) init.childList = true;
|
|
758
|
-
return init;
|
|
759
|
-
};
|
|
760
|
-
|
|
761
|
-
const observer = new MutationObserver((mutations: MutationRecord[], obs: MutationObserver) => {
|
|
762
|
-
for (const meta of metas) {
|
|
763
|
-
const { selector, options } = meta;
|
|
764
|
-
let matchedEls: HTMLElement[] = [];
|
|
765
|
-
|
|
766
|
-
if (options.delegate && typeof selector === 'string') {
|
|
767
|
-
const isThis = selector === '$this' || selector === '';
|
|
768
|
-
const matchesSel = (node: any): boolean => {
|
|
769
|
-
if (!node || node.nodeType !== 1) return false;
|
|
770
|
-
if (isThis) return true;
|
|
771
|
-
const el = node as HTMLElement;
|
|
772
|
-
return el.matches?.(selector as string) || !!el.closest?.(selector as string);
|
|
773
|
-
};
|
|
774
|
-
matchedEls = mutations.flatMap(m => {
|
|
775
|
-
const els: HTMLElement[] = [];
|
|
776
|
-
if (matchesSel(m.target)) els.push(m.target as HTMLElement);
|
|
777
|
-
Array.from(m.addedNodes || [])
|
|
778
|
-
.filter(matchesSel)
|
|
779
|
-
.forEach(n => els.push(n as HTMLElement));
|
|
780
|
-
Array.from(m.removedNodes || [])
|
|
781
|
-
.filter(matchesSel)
|
|
782
|
-
.forEach(n => els.push(n as HTMLElement));
|
|
783
|
-
return els;
|
|
784
|
-
});
|
|
785
|
-
} else if (typeof selector === 'string') {
|
|
786
|
-
// non-delegate: observe 대상이 매칭 요소들이므로 그들의 mutation 대상 + 추가/제거 노드
|
|
787
|
-
const isThis = selector === '$this' || selector === '';
|
|
788
|
-
const matchesSel = (node: any): boolean => {
|
|
789
|
-
if (!node || node.nodeType !== 1) return false;
|
|
790
|
-
if (isThis) return true;
|
|
791
|
-
return (node as HTMLElement).matches?.(selector as string);
|
|
792
|
-
};
|
|
793
|
-
matchedEls = mutations.flatMap(m => {
|
|
794
|
-
const els: HTMLElement[] = [];
|
|
795
|
-
if (matchesSel(m.target)) els.push(m.target as HTMLElement);
|
|
796
|
-
Array.from(m.addedNodes || [])
|
|
797
|
-
.filter(matchesSel)
|
|
798
|
-
.forEach(n => els.push(n as HTMLElement));
|
|
799
|
-
Array.from(m.removedNodes || [])
|
|
800
|
-
.filter(matchesSel)
|
|
801
|
-
.forEach(n => els.push(n as HTMLElement));
|
|
802
|
-
return els;
|
|
803
|
-
});
|
|
804
|
-
} else {
|
|
805
|
-
matchedEls = mutations.map(m => m.target as HTMLElement).filter(t => t && t.nodeType === 1);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
if (matchedEls.length === 0) continue;
|
|
809
|
-
if (options.filter) {
|
|
810
|
-
const helper = SwcUtils.getHelperAndHostSet(currentWin, this);
|
|
811
|
-
if (!options.filter(matchedEls, { currentThis: this, helper })) continue;
|
|
812
|
-
}
|
|
813
|
-
const hostSet = SwcUtils.getHostSet(this as any);
|
|
814
|
-
// 1번째: 매치된 element 배열, 2번째: 원본 mutations, 3번째: observer
|
|
815
|
-
(this as any)[meta.propertyKey](matchedEls, mutations, obs, { ...hostSet, $root: root });
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// ─── resizeObserver delegate 동적 추적 ───
|
|
819
|
-
// 추가/삭제된 요소가 delegate 셀렉터와 매칭되면 resizeObserver에 observe/unobserve
|
|
820
|
-
if (resizeDelegates.length > 0 && resizeObserver) {
|
|
821
|
-
const handle = (n: Node, observe: boolean) => {
|
|
822
|
-
if (n.nodeType !== 1) return;
|
|
823
|
-
const el = n as HTMLElement;
|
|
824
|
-
for (const m of resizeDelegates) {
|
|
825
|
-
if (typeof m.selector !== 'string') continue;
|
|
826
|
-
const isThis = m.selector === '$this' || m.selector === '';
|
|
827
|
-
const matchesSel = (node: any): boolean => {
|
|
828
|
-
if (!node || node.nodeType !== 1) return false;
|
|
829
|
-
if (isThis) return true;
|
|
830
|
-
const e = node as HTMLElement;
|
|
831
|
-
return e.matches?.(m.selector as string) || !!e.closest?.(m.selector as string);
|
|
832
|
-
};
|
|
833
|
-
if (matchesSel(el)) {
|
|
834
|
-
if (observe) resizeObserver.observe(el, m.options.box ? { box: m.options.box } : undefined);
|
|
835
|
-
else resizeObserver.unobserve(el);
|
|
836
|
-
} else if (!isThis) {
|
|
837
|
-
el.querySelectorAll(m.selector).forEach(sub => {
|
|
838
|
-
if (observe) resizeObserver.observe(sub as HTMLElement, m.options.box ? { box: m.options.box } : undefined);
|
|
839
|
-
else resizeObserver.unobserve(sub as HTMLElement);
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
};
|
|
844
|
-
for (const m of mutations) {
|
|
845
|
-
Array.from(m.addedNodes || []).forEach(n => handle(n, true));
|
|
846
|
-
Array.from(m.removedNodes || []).forEach(n => handle(n, false));
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
|
|
851
|
-
// delegate 메타(mutation 또는 resize)가 하나라도 있으면 → 루트에 subtree observe 1개로 전부 감지 (콜백에서 메타별 매칭 처리)
|
|
852
|
-
const hasDelegate = metas.some(m => m.options.delegate) || resizeDelegates.some(m => m.options.delegate);
|
|
853
|
-
if (hasDelegate) {
|
|
854
|
-
observer.observe(root, { childList: true, subtree: true });
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// non-delegate 메타: 셀렉터 매칭 요소들을 사용자 옵션 그대로 observe
|
|
858
|
-
const nonDelegateMetas = metas.filter(m => !m.options.delegate);
|
|
859
|
-
for (const m of nonDelegateMetas) {
|
|
860
|
-
const { selector, options } = m;
|
|
861
|
-
const mInit = initFrom(options);
|
|
862
|
-
if (typeof selector === 'string') {
|
|
863
|
-
if (selector === '$this' || selector === '') {
|
|
864
|
-
observer.observe(root, mInit);
|
|
865
|
-
} else {
|
|
866
|
-
root.querySelectorAll(selector).forEach(el => observer.observe(el as HTMLElement, mInit));
|
|
867
|
-
}
|
|
868
|
-
} else if (typeof selector === 'function') {
|
|
869
|
-
// 함수 셀렉터는 루트 관찰로 대체 (매칭 평가 불가)
|
|
870
|
-
observer.observe(root, mInit);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
return observer;
|
|
875
|
-
};
|
|
876
|
-
|
|
877
|
-
// resizeObserver delegate 메타도 루트별로 필터해서 넘김
|
|
878
|
-
const shadowResizeDelegateMetas = resizeObserverList.filter(m => {
|
|
879
|
-
if (!m.options.delegate) return false;
|
|
880
|
-
const r = m.options.root || 'auto';
|
|
881
|
-
return this.shadowRoot && (r === 'shadow' || r === 'all' || r === 'auto');
|
|
882
|
-
});
|
|
883
|
-
const lightResizeDelegateMetas = resizeObserverList.filter(m => {
|
|
884
|
-
if (!m.options.delegate) return false;
|
|
885
|
-
const r = m.options.root || 'auto';
|
|
886
|
-
return r === 'light' || r === 'all' || (!this.shadowRoot && r === 'auto');
|
|
887
|
-
});
|
|
888
|
-
|
|
889
|
-
if (this.shadowRoot) shadowMutationObserver = setupMutationObserver(this.shadowRoot, shadowMetas, shadowResizeDelegateMetas);
|
|
890
|
-
lightMutationObserver = setupMutationObserver(this as any, lightMetas, lightResizeDelegateMetas);
|
|
891
|
-
|
|
892
|
-
// mutationObserver 메타별 removeObserver 수집
|
|
893
|
-
for (const m of mutationObserverList) {
|
|
894
|
-
if (m.options.removeObserver) {
|
|
895
|
-
const rootForM = this.shadowRoot && (m.options.root === 'shadow' || m.options.root === 'all' || (m.options.root === 'auto' && this.shadowRoot)) ? this.shadowRoot : (this as any);
|
|
896
|
-
removeObserverCallbacks.push({ fn: m.options.removeObserver, target: rootForM, opts: m.options });
|
|
897
|
-
}
|
|
898
|
-
}
|
|
707
|
+
// ── cycler 실행 (렌더 후): 모든 시클러 onConnected → observer Set 수집 → observer 생성 ──
|
|
708
|
+
// helperHostSet 은 클로저 변수라 async 흐름에서 다른 인스턴스로 덮어써질 수 있다.
|
|
709
|
+
// 반드시 현재 인스턴스(this)로 재계산한 값을 사용한다.
|
|
710
|
+
await buildObservers(SwcUtils.getHelperAndHostSet(win, this as any));
|
|
899
711
|
|
|
900
712
|
if (originalConnected) await originalConnected.apply(this);
|
|
901
713
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
for (const m of aMethods) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
714
|
+
// after-connected 라이프사이클 메서드 (@onConnectedAfter)
|
|
715
|
+
for (const m of findAllOnConnectedAfterMetadata(this)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
905
716
|
(this as any)._executeSwcScript('swc-on-connected', helperHostSet);
|
|
906
717
|
(this as any)._executeSwcScript('swc-on-after-connected', helperHostSet);
|
|
907
718
|
(this as any).__swc_connected = true;
|
|
908
|
-
|
|
909
|
-
// Trigger @changedAttributeThis with while: 'connected'
|
|
910
|
-
for (let [name, metaList] of Array.from(attrChangeMap)) {
|
|
911
|
-
for (const meta of metaList) {
|
|
912
|
-
if (meta.options.while === 'connected') {
|
|
913
|
-
const val = getAttributeValue(this, name, { type: meta.options.type });
|
|
914
|
-
if (val !== null) {
|
|
915
|
-
await (this as any)[meta.propertyKey](val, null, name, helperHostSet);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
719
|
} finally {
|
|
921
|
-
for (
|
|
922
|
-
await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
923
|
-
}
|
|
720
|
+
for (const m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
924
721
|
if (appHost && typeof (appHost as any)._connectedDone === 'function') {
|
|
925
722
|
await (appHost as any)._connectedDone(this);
|
|
926
723
|
}
|
|
@@ -932,82 +729,26 @@ export const elementDefine =
|
|
|
932
729
|
////////////////////////////////////////////////
|
|
933
730
|
const originalDisconnected = proto.disconnectedCallback;
|
|
934
731
|
proto.disconnectedCallback = function () {
|
|
935
|
-
|
|
936
|
-
// Remove from appHost LAST when disconnected
|
|
937
|
-
// console.log('disconnnnnnnnnnnnnnnnnnn', helperHostSet, this)
|
|
938
|
-
const appHost = helperHostSet.$appHost;
|
|
732
|
+
const appHost = helperHostSet?.$appHost;
|
|
939
733
|
if (appHost && typeof (appHost as any)._disconnected === 'function') {
|
|
940
734
|
(appHost as any)._disconnected(this);
|
|
941
735
|
}
|
|
942
736
|
|
|
943
737
|
(this as any)._executeSwcScript('swc-on-before-disconnected', helperHostSet);
|
|
944
|
-
const
|
|
945
|
-
for (const
|
|
946
|
-
// event listener 정리
|
|
947
|
-
if (boundListeners.length > 0) {
|
|
948
|
-
boundListeners.forEach((l: BoundListener) => {
|
|
949
|
-
l.target.removeEventListener(l.type, l.handler, l.options);
|
|
950
|
-
// Cleanup Observable subscription if present
|
|
951
|
-
if (l.subscription && typeof l.subscription.unsubscribe === 'function') {
|
|
952
|
-
l.subscription.unsubscribe();
|
|
953
|
-
}
|
|
954
|
-
// 사용자 정의 정리 콜백 호출
|
|
955
|
-
if (Array.isArray(l.onRemoves)) {
|
|
956
|
-
for (const r of l.onRemoves) {
|
|
957
|
-
try {
|
|
958
|
-
r.fn(l.target, r.opts);
|
|
959
|
-
} catch (e) {
|
|
960
|
-
console.error('[SWC] removeListener error:', e);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
});
|
|
965
|
-
boundListeners = [];
|
|
966
|
-
}
|
|
738
|
+
for (const m of findAllLifecycleMetadata(this, ON_BEFORE_DISCONNECTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
739
|
+
for (const c of cyclers) c.onDisconnected?.(helperHostSet!);
|
|
967
740
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
for (const o of _obs) {
|
|
971
|
-
try {
|
|
972
|
-
(o as any)?.disconnect?.();
|
|
973
|
-
} catch (e) {
|
|
974
|
-
console.error('[SWC] observer disconnect error:', e);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
shadowMutationObserver = null;
|
|
978
|
-
lightMutationObserver = null;
|
|
979
|
-
resizeObserver = null;
|
|
980
|
-
resizeObservers = [];
|
|
981
|
-
|
|
982
|
-
// 메타별 removeObserver 콜백 호출
|
|
983
|
-
for (const cb of removeObserverCallbacks) {
|
|
984
|
-
try {
|
|
985
|
-
cb.fn(cb.target, cb.opts);
|
|
986
|
-
} catch (e) {
|
|
987
|
-
console.error('[SWC] removeObserver error:', e);
|
|
988
|
-
}
|
|
741
|
+
for (const o of (this as any).__swc_observers ?? []) {
|
|
742
|
+
try { (o as any)?.disconnect?.(); } catch (e) { console.error('[SWC] observer disconnect error:', e); }
|
|
989
743
|
}
|
|
990
|
-
|
|
744
|
+
(this as any).__swc_observers = [];
|
|
991
745
|
|
|
992
746
|
// elementApply event listener 정리
|
|
993
747
|
new ElementApply(this, { id: this._swcId }).removeAllEventListener();
|
|
994
748
|
|
|
995
|
-
// globalDelegatedRoots remove
|
|
996
|
-
// const roots = [this, this.getRootNode()];
|
|
997
|
-
// if (this.shadowRoot) {
|
|
998
|
-
// roots.push(this.shadowRoot);
|
|
999
|
-
// }
|
|
1000
|
-
// roots.forEach(root => {
|
|
1001
|
-
// DOM_EVENT_NAMES.forEach(type => {
|
|
1002
|
-
// root.removeEventListener(type, handleGlobalSwcEvent);
|
|
1003
|
-
// });
|
|
1004
|
-
// }
|
|
1005
|
-
// )
|
|
1006
|
-
|
|
1007
749
|
if (originalDisconnected) originalDisconnected.apply(this);
|
|
1008
750
|
|
|
1009
|
-
const
|
|
1010
|
-
for (const m of aMethods) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
751
|
+
for (const m of findAllLifecycleMetadata(this, ON_AFTER_DISCONNECTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
1011
752
|
(this as any)._executeSwcScript('swc-on-disconnected', helperHostSet);
|
|
1012
753
|
(this as any)._executeSwcScript('swc-on-after-disconnected', helperHostSet);
|
|
1013
754
|
(this as any).__swc_connected = false;
|
|
@@ -1017,13 +758,12 @@ export const elementDefine =
|
|
|
1017
758
|
proto.adoptedCallback = function () {
|
|
1018
759
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
1019
760
|
(this as any)._executeSwcScript('swc-on-before-adopted', hostSet);
|
|
1020
|
-
const
|
|
1021
|
-
for (const
|
|
761
|
+
for (const m of findAllLifecycleMetadata(this, ON_BEFORE_ADOPTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
|
|
762
|
+
for (const c of cyclers) c.onAdopted?.(helperHostSet!);
|
|
1022
763
|
|
|
1023
764
|
if (originalAdopted) originalAdopted.apply(this);
|
|
1024
765
|
|
|
1025
|
-
const
|
|
1026
|
-
for (const m of aMethods) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
|
|
766
|
+
for (const m of findAllLifecycleMetadata(this, ON_AFTER_ADOPTED_METADATA_KEY)) (this as any)._invokeLifecycleMethod(m.propertyKey, hostSet);
|
|
1027
767
|
(this as any)._executeSwcScript('swc-on-adopted', hostSet);
|
|
1028
768
|
(this as any)._executeSwcScript('swc-on-after-adopted', hostSet);
|
|
1029
769
|
};
|
|
@@ -1032,7 +772,9 @@ export const elementDefine =
|
|
|
1032
772
|
proto.attributeChangedCallback = function (name: string, old: string | null, newVal: string | null) {
|
|
1033
773
|
if (originalAttributeChanged) originalAttributeChanged.apply(this, [name, old, newVal]);
|
|
1034
774
|
|
|
1035
|
-
|
|
775
|
+
// attributeChangedCallback 은 connected 이전에도 호출될 수 있으므로
|
|
776
|
+
// 클로저의 helperHostSet(null 가능) 대신 이 시점에 새로 계산한다.
|
|
777
|
+
const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
|
|
1036
778
|
|
|
1037
779
|
// Process expression directive before passing to handlers
|
|
1038
780
|
let processedVal: any = newVal;
|
|
@@ -1041,18 +783,17 @@ export const elementDefine =
|
|
|
1041
783
|
const expr = ae.getFirstExpression('callReturn');
|
|
1042
784
|
if (expr) {
|
|
1043
785
|
const win = (this as any)._resolveWindow?.() || ((typeof window !== 'undefined' ? window : undefined) as any);
|
|
1044
|
-
const
|
|
786
|
+
const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
|
|
1045
787
|
const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
|
|
1046
788
|
try {
|
|
1047
789
|
const result = FunctionUtils.executeReturn({
|
|
1048
790
|
script: script,
|
|
1049
791
|
context: this,
|
|
1050
|
-
args:
|
|
792
|
+
args: exprHelperAndHostSet
|
|
1051
793
|
});
|
|
1052
|
-
// Pass the result object directly without serialization
|
|
1053
794
|
processedVal = result;
|
|
1054
795
|
} catch (e) {
|
|
1055
|
-
console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}: ${
|
|
796
|
+
console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}: ${exprHelperAndHostSet}`, e);
|
|
1056
797
|
processedVal = newVal;
|
|
1057
798
|
}
|
|
1058
799
|
}
|
|
@@ -1065,21 +806,8 @@ export const elementDefine =
|
|
|
1065
806
|
}
|
|
1066
807
|
}
|
|
1067
808
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
(this as any)._bindAttributeEvent(this as any, name, newVal, hostCustomEventMeta.type);
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
const metaList = attrChangeMap.get(name);
|
|
1074
|
-
if (metaList && Array.isArray(metaList)) {
|
|
1075
|
-
for (const meta of metaList) {
|
|
1076
|
-
if (meta.options.while === 'connected' && !(this as any).__swc_connected) {
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
const convertedVal = convertAttributeValue(processedVal, meta.options.type);
|
|
1080
|
-
(this as any)[meta.propertyKey](convertedVal, old, name, hSet);
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
809
|
+
// cycler 위임: attribute 옵저빙(@emitCustomEvent(attributeName) / @changedAttribute 등)
|
|
810
|
+
for (const c of cyclers) c.onAttributeChanged?.(helperAndHostSet, name, old, processedVal);
|
|
1083
811
|
};
|
|
1084
812
|
|
|
1085
813
|
Object.defineProperty(constructor, 'observedAttributes', {
|