@dooboostore/simple-web-component 1.0.48 → 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.
- package/README.md +117 -19
- package/dist/cjs/config/config.js +1 -1
- package/dist/cjs/config/config.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +387 -29
- package/dist/cjs/decorators/addEventListener.js.map +3 -3
- package/dist/cjs/decorators/applyAttribute.js +25 -0
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +49 -18
- package/dist/cjs/decorators/applyClass.js.map +3 -3
- package/dist/cjs/decorators/applyNode.js +67 -1
- package/dist/cjs/decorators/applyNode.js.map +3 -3
- package/dist/cjs/decorators/applyProperty.js +101 -3
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +36 -5
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/changedAttribute.js +39 -0
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +237 -437
- 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/index.js +3 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +209 -0
- package/dist/cjs/decorators/intersectionObserver.js.map +7 -0
- package/dist/cjs/decorators/mutationObserver.js +140 -0
- package/dist/cjs/decorators/mutationObserver.js.map +3 -3
- package/dist/cjs/decorators/query.js +117 -102
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/resizeObserver.js +87 -0
- package/dist/cjs/decorators/resizeObserver.js.map +2 -2
- package/dist/cjs/decorators/setInterval.js +82 -0
- package/dist/cjs/decorators/setInterval.js.map +7 -0
- package/dist/cjs/decorators/setTimeout.js +82 -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/types/index.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 +388 -29
- package/dist/esm/decorators/addEventListener.js.map +3 -3
- package/dist/esm/decorators/applyAttribute.js +25 -0
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +49 -18
- package/dist/esm/decorators/applyClass.js.map +3 -3
- package/dist/esm/decorators/applyNode.js +67 -1
- package/dist/esm/decorators/applyNode.js.map +3 -3
- package/dist/esm/decorators/applyProperty.js +101 -3
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +36 -5
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/changedAttribute.js +40 -0
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +244 -444
- 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/index.js +3 -1
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +191 -0
- package/dist/esm/decorators/intersectionObserver.js.map +7 -0
- package/dist/esm/decorators/mutationObserver.js +141 -0
- package/dist/esm/decorators/mutationObserver.js.map +3 -3
- package/dist/esm/decorators/query.js +117 -102
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/resizeObserver.js +88 -0
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +64 -0
- package/dist/esm/decorators/setInterval.js.map +7 -0
- package/dist/esm/decorators/setTimeout.js +64 -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/types/index.js.map +1 -1
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +2565 -1474
- 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 +84 -33
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +22 -34
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +34 -34
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +30 -20
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +57 -34
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +25 -12
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/changedAttribute.d.ts +11 -0
- 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/index.d.ts +3 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/intersectionObserver.d.ts +49 -0
- package/dist/types/decorators/intersectionObserver.d.ts.map +1 -0
- package/dist/types/decorators/mutationObserver.d.ts +35 -25
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +57 -14
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/resizeObserver.d.ts +34 -23
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts +29 -0
- package/dist/types/decorators/setInterval.d.ts.map +1 -0
- package/dist/types/decorators/setTimeout.d.ts +29 -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/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 +2565 -1474
- 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 +394 -34
- package/src/decorators/applyAttribute.ts +50 -36
- package/src/decorators/applyClass.ts +76 -26
- package/src/decorators/applyNode.ts +114 -36
- package/src/decorators/applyProperty.ts +152 -40
- package/src/decorators/applyStyle.ts +64 -17
- package/src/decorators/changedAttribute.ts +46 -0
- package/src/decorators/elementDefine.ts +281 -580
- package/src/decorators/emitCustomEvent.ts +73 -5
- package/src/decorators/index.ts +3 -1
- package/src/decorators/intersectionObserver.ts +259 -0
- package/src/decorators/mutationObserver.ts +215 -39
- package/src/decorators/query.ts +168 -126
- package/src/decorators/resizeObserver.ts +152 -46
- package/src/decorators/setInterval.ts +80 -0
- package/src/decorators/setTimeout.ts +80 -0
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +17 -0
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +19 -0
- package/src/types/index.ts +141 -0
- package/dist/cjs/decorators/queryAll.js +0 -181
- package/dist/cjs/decorators/queryAll.js.map +0 -7
- package/dist/esm/decorators/queryAll.js +0 -162
- package/dist/esm/decorators/queryAll.js.map +0 -7
- package/dist/types/decorators/queryAll.d.ts +0 -22
- package/dist/types/decorators/queryAll.d.ts.map +0 -1
- package/src/decorators/queryAll.ts +0 -186
|
@@ -1,39 +1,30 @@
|
|
|
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 {HelperHostSet, HostSet, InjectSituationType} from '../types';
|
|
14
|
+
import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
|
|
18
15
|
import {ConvertUtils, ElementApply} from '@dooboostore/core-web';
|
|
19
16
|
import {isSSR} from "../elements/SwcAppMixin";
|
|
20
17
|
import {findAllStateMetadata} from "./state";
|
|
21
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";
|
|
22
23
|
|
|
23
24
|
// --- Core Interfaces & Types ---
|
|
24
25
|
|
|
25
26
|
export const ELEMENT_CONFIG_KEY = Symbol.for('simple-web-component:element-config');
|
|
26
27
|
|
|
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
28
|
export interface ElementConfig {
|
|
38
29
|
extends?: string;
|
|
39
30
|
observedAttributes?: string[];
|
|
@@ -350,12 +341,6 @@ export const elementDefine =
|
|
|
350
341
|
metadata.extends = extendsTagName;
|
|
351
342
|
}
|
|
352
343
|
|
|
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
344
|
const attributeList = findAllAttributeMetadata(constructor);
|
|
360
345
|
const applyAttributeMap = findAllAttributeApplyMetadata(constructor);
|
|
361
346
|
|
|
@@ -368,37 +353,256 @@ export const elementDefine =
|
|
|
368
353
|
const hostAttributes = attributeList.filter(it => it.selector === '$this').map(it => it.propertyKey || String(it.propertyKey));
|
|
369
354
|
// Get original static observedAttributes before they're overwritten by Object.defineProperty
|
|
370
355
|
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
356
|
|
|
374
357
|
const proto = constructor.prototype;
|
|
375
358
|
setupPrototype(proto, win);
|
|
376
359
|
|
|
377
360
|
// 값을 기대하지않는다.
|
|
378
361
|
let helperHostSet: HelperHostSet | null = null;
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
+
|
|
388
599
|
const originalConnected = proto.connectedCallback;
|
|
389
600
|
proto.connectedCallback = async function () {
|
|
390
601
|
ensureInit(this);
|
|
391
|
-
// 재연결 시 누적 방지 —
|
|
392
|
-
boundListeners = [];
|
|
393
|
-
resizeObservers = [];
|
|
394
|
-
removeObserverCallbacks = [];
|
|
395
|
-
// connected 됐을시 최신화
|
|
602
|
+
// 재연결 시 누적 방지 — cycler 내부 리소스 초기화
|
|
396
603
|
helperHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
|
|
397
604
|
const appHost = helperHostSet.$appHost;
|
|
398
605
|
const useSsr = isSSR(this);
|
|
399
|
-
// console.log('isSsr', useSsr, this);
|
|
400
|
-
// console.log('----helperHostSet--->', this, helperHostSet, useSsr)
|
|
401
|
-
// let connectedApp: SwcAppInterface |undefined;
|
|
402
606
|
try {
|
|
403
607
|
if (appHost && typeof (appHost as any)._connected === 'function') {
|
|
404
608
|
await (appHost as any)._connected(this);
|
|
@@ -407,27 +611,19 @@ export const elementDefine =
|
|
|
407
611
|
(appHost as any)._connected_safari_and_standby.push(this);
|
|
408
612
|
}
|
|
409
613
|
|
|
410
|
-
// const hostSet = SwcUtils.getHostSet(this as any);
|
|
411
614
|
const conf = getElementConfig(this);
|
|
412
615
|
const currentWin = (this as any)._resolveWindow(conf);
|
|
413
616
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
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);
|
|
417
619
|
(this as any)._executeSwcScript('swc-on-before-connected', helperHostSet);
|
|
418
620
|
|
|
419
|
-
//
|
|
420
|
-
// // ssr 처리라서 이미 내려준거그대로 상요하면된다 따라서 호출안한다
|
|
421
|
-
// const targetConnectedList = findAllOnConnectedMetadata(constructor).filter(it => useSsr ? !it.options.ssrFirst : true);
|
|
621
|
+
// ── 렌더 (@onConnected / @onConnectedBody*) — elementDefine 책임 ──
|
|
422
622
|
const targetConnectedList = useSsr ? [] : findAllOnConnectedMetadata(constructor);
|
|
423
623
|
const shadowMode = conf?.useShadow || targetConnectedList.find(it => it.options.useShadow)?.options.useShadow;
|
|
424
|
-
// console.log('--------->', this.tagName, shadowMode && !this.shadowRoot)
|
|
425
624
|
if (shadowMode && !this.shadowRoot) {
|
|
426
625
|
const mode = shadowMode === true ? 'open' : shadowMode;
|
|
427
|
-
console.log('tagName=', this.tagName, 'mode', mode, this.shadowRoot);
|
|
428
|
-
// if (this.tagName!=='INDEX-ROUTER') {
|
|
429
626
|
this.attachShadow({ mode: mode as ShadowRootMode });
|
|
430
|
-
// }
|
|
431
627
|
}
|
|
432
628
|
|
|
433
629
|
const stateContext: any = { ...helperHostSet };
|
|
@@ -438,20 +634,14 @@ export const elementDefine =
|
|
|
438
634
|
const shadowChildren: Node[] = [];
|
|
439
635
|
const lightChildren: Node[] = [];
|
|
440
636
|
|
|
441
|
-
console.log('targetConnectList', this.tagName, targetConnectedList.length);
|
|
442
637
|
if (targetConnectedList.length > 0) {
|
|
443
|
-
// let sContent = '',
|
|
444
|
-
// lContent = '';
|
|
445
638
|
for (const meta of targetConnectedList) {
|
|
446
|
-
// const res = (this as any)[meta.propertyKey]();
|
|
447
639
|
let res = await (this as any)._invokeLifecycleMethod(meta.propertyKey, helperHostSet);
|
|
448
|
-
// res = SwcUtils.projectProcessHtml(this, res);
|
|
449
640
|
if (typeof res === 'string') {
|
|
450
641
|
const htmlTemplateElement = doc.createElement('template');
|
|
451
|
-
htmlTemplateElement.innerHTML = res;
|
|
642
|
+
htmlTemplateElement.innerHTML = res;
|
|
452
643
|
res = htmlTemplateElement.content;
|
|
453
644
|
}
|
|
454
|
-
|
|
455
645
|
if (res) {
|
|
456
646
|
const nodes = Array.isArray(res) ? res : [res];
|
|
457
647
|
if (meta.options.useShadow || conf?.useShadow) {
|
|
@@ -487,440 +677,20 @@ export const elementDefine =
|
|
|
487
677
|
globalDelegatedRoots.add(root);
|
|
488
678
|
}
|
|
489
679
|
|
|
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
|
-
}
|
|
680
|
+
// ── cycler 실행 (렌더 후): 모든 시클러 onConnected → observer Set 수집 → observer 생성 ──
|
|
681
|
+
// helperHostSet 은 클로저 변수라 async 흐름에서 다른 인스턴스로 덮어써질 수 있다.
|
|
682
|
+
// 반드시 현재 인스턴스(this)로 재계산한 값을 사용한다.
|
|
683
|
+
await buildObservers(SwcUtils.getHelperAndHostSet(win, this as any));
|
|
899
684
|
|
|
900
685
|
if (originalConnected) await originalConnected.apply(this);
|
|
901
686
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
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);
|
|
905
689
|
(this as any)._executeSwcScript('swc-on-connected', helperHostSet);
|
|
906
690
|
(this as any)._executeSwcScript('swc-on-after-connected', helperHostSet);
|
|
907
691
|
(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
692
|
} finally {
|
|
921
|
-
for (
|
|
922
|
-
await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
923
|
-
}
|
|
693
|
+
for (const m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
924
694
|
if (appHost && typeof (appHost as any)._connectedDone === 'function') {
|
|
925
695
|
await (appHost as any)._connectedDone(this);
|
|
926
696
|
}
|
|
@@ -932,82 +702,26 @@ export const elementDefine =
|
|
|
932
702
|
////////////////////////////////////////////////
|
|
933
703
|
const originalDisconnected = proto.disconnectedCallback;
|
|
934
704
|
proto.disconnectedCallback = function () {
|
|
935
|
-
|
|
936
|
-
// Remove from appHost LAST when disconnected
|
|
937
|
-
// console.log('disconnnnnnnnnnnnnnnnnnn', helperHostSet, this)
|
|
938
|
-
const appHost = helperHostSet.$appHost;
|
|
705
|
+
const appHost = helperHostSet?.$appHost;
|
|
939
706
|
if (appHost && typeof (appHost as any)._disconnected === 'function') {
|
|
940
707
|
(appHost as any)._disconnected(this);
|
|
941
708
|
}
|
|
942
709
|
|
|
943
710
|
(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
|
-
}
|
|
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!);
|
|
967
713
|
|
|
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
|
-
}
|
|
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); }
|
|
989
716
|
}
|
|
990
|
-
|
|
717
|
+
(this as any).__swc_observers = [];
|
|
991
718
|
|
|
992
719
|
// elementApply event listener 정리
|
|
993
720
|
new ElementApply(this, { id: this._swcId }).removeAllEventListener();
|
|
994
721
|
|
|
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
722
|
if (originalDisconnected) originalDisconnected.apply(this);
|
|
1008
723
|
|
|
1009
|
-
const
|
|
1010
|
-
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);
|
|
1011
725
|
(this as any)._executeSwcScript('swc-on-disconnected', helperHostSet);
|
|
1012
726
|
(this as any)._executeSwcScript('swc-on-after-disconnected', helperHostSet);
|
|
1013
727
|
(this as any).__swc_connected = false;
|
|
@@ -1017,13 +731,12 @@ export const elementDefine =
|
|
|
1017
731
|
proto.adoptedCallback = function () {
|
|
1018
732
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
1019
733
|
(this as any)._executeSwcScript('swc-on-before-adopted', hostSet);
|
|
1020
|
-
const
|
|
1021
|
-
for (const
|
|
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!);
|
|
1022
736
|
|
|
1023
737
|
if (originalAdopted) originalAdopted.apply(this);
|
|
1024
738
|
|
|
1025
|
-
const
|
|
1026
|
-
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);
|
|
1027
740
|
(this as any)._executeSwcScript('swc-on-adopted', hostSet);
|
|
1028
741
|
(this as any)._executeSwcScript('swc-on-after-adopted', hostSet);
|
|
1029
742
|
};
|
|
@@ -1032,7 +745,9 @@ export const elementDefine =
|
|
|
1032
745
|
proto.attributeChangedCallback = function (name: string, old: string | null, newVal: string | null) {
|
|
1033
746
|
if (originalAttributeChanged) originalAttributeChanged.apply(this, [name, old, newVal]);
|
|
1034
747
|
|
|
1035
|
-
|
|
748
|
+
// attributeChangedCallback 은 connected 이전에도 호출될 수 있으므로
|
|
749
|
+
// 클로저의 helperHostSet(null 가능) 대신 이 시점에 새로 계산한다.
|
|
750
|
+
const helperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
|
|
1036
751
|
|
|
1037
752
|
// Process expression directive before passing to handlers
|
|
1038
753
|
let processedVal: any = newVal;
|
|
@@ -1041,18 +756,17 @@ export const elementDefine =
|
|
|
1041
756
|
const expr = ae.getFirstExpression('callReturn');
|
|
1042
757
|
if (expr) {
|
|
1043
758
|
const win = (this as any)._resolveWindow?.() || ((typeof window !== 'undefined' ? window : undefined) as any);
|
|
1044
|
-
const
|
|
759
|
+
const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(win, this as any);
|
|
1045
760
|
const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
|
|
1046
761
|
try {
|
|
1047
762
|
const result = FunctionUtils.executeReturn({
|
|
1048
763
|
script: script,
|
|
1049
764
|
context: this,
|
|
1050
|
-
args:
|
|
765
|
+
args: exprHelperAndHostSet
|
|
1051
766
|
});
|
|
1052
|
-
// Pass the result object directly without serialization
|
|
1053
767
|
processedVal = result;
|
|
1054
768
|
} catch (e) {
|
|
1055
|
-
console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}: ${
|
|
769
|
+
console.error(`[SWC] Failed to execute directive {{= ${expr.script} }} on attribute ${name}: ${exprHelperAndHostSet}`, e);
|
|
1056
770
|
processedVal = newVal;
|
|
1057
771
|
}
|
|
1058
772
|
}
|
|
@@ -1065,21 +779,8 @@ export const elementDefine =
|
|
|
1065
779
|
}
|
|
1066
780
|
}
|
|
1067
781
|
|
|
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
|
-
}
|
|
782
|
+
// cycler 위임: attribute 옵저빙(@emitCustomEvent(attributeName) / @changedAttribute 등)
|
|
783
|
+
for (const c of cyclers) c.onAttributeChanged?.(helperAndHostSet, name, old, processedVal);
|
|
1083
784
|
};
|
|
1084
785
|
|
|
1085
786
|
Object.defineProperty(constructor, 'observedAttributes', {
|