@dooboostore/simple-web-component 1.0.50 → 1.0.52
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 +735 -354
- package/dist/cjs/SwcAppEngine.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +64 -146
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/applyAttribute.js +36 -48
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +9 -12
- package/dist/cjs/decorators/applyClass.js.map +2 -2
- package/dist/cjs/decorators/applyNode.js +14 -22
- package/dist/cjs/decorators/applyNode.js.map +2 -2
- package/dist/cjs/decorators/applyProperty.js +12 -42
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applySlot.js +3 -3
- package/dist/cjs/decorators/applySlot.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +9 -12
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/around.js +124 -0
- package/dist/cjs/decorators/around.js.map +7 -0
- package/dist/cjs/decorators/changedAttribute.js +40 -12
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +224 -195
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +14 -12
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/eventMedia.js +36 -8
- package/dist/cjs/decorators/eventMedia.js.map +2 -2
- package/dist/cjs/decorators/fetch.js +278 -0
- package/dist/cjs/decorators/fetch.js.map +7 -0
- package/dist/cjs/decorators/index.js +5 -2
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +33 -13
- package/dist/cjs/decorators/intersectionObserver.js.map +2 -2
- package/dist/cjs/decorators/lifecycles.js +11 -11
- package/dist/cjs/decorators/lifecycles.js.map +2 -2
- package/dist/cjs/decorators/mutationObserver.js +55 -39
- package/dist/cjs/decorators/mutationObserver.js.map +3 -3
- package/dist/cjs/decorators/parameter.js +92 -9
- package/dist/cjs/decorators/parameter.js.map +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/cjs/decorators/query.js +11 -5
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/requestAnimationFrame.js +7 -16
- package/dist/cjs/decorators/requestAnimationFrame.js.map +2 -2
- package/dist/cjs/decorators/resizeObserver.js +41 -17
- package/dist/cjs/decorators/resizeObserver.js.map +3 -3
- package/dist/cjs/decorators/setInterval.js +36 -25
- package/dist/cjs/decorators/setInterval.js.map +2 -2
- package/dist/cjs/decorators/setTimeout.js +36 -25
- package/dist/cjs/decorators/setTimeout.js.map +2 -2
- package/dist/cjs/decorators/state.js +13 -10
- package/dist/cjs/decorators/state.js.map +2 -2
- package/dist/cjs/decorators/{subscribeSwcAppMessageWhileConnected.js → subscribeSwcAppMessage.js} +20 -21
- package/dist/cjs/decorators/subscribeSwcAppMessage.js.map +7 -0
- package/dist/cjs/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +23 -19
- package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/cjs/decorators/swcAppRoute.js +149 -0
- package/dist/cjs/decorators/swcAppRoute.js.map +7 -0
- package/dist/cjs/elements/SwcApp.js +70 -0
- package/dist/cjs/elements/SwcApp.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +262 -83
- package/dist/cjs/elements/SwcAppMixin.js.map +3 -3
- package/dist/cjs/types/index.js +5 -1
- package/dist/cjs/types/index.js.map +2 -2
- package/dist/cjs/utils/Utils.js +36 -15
- package/dist/cjs/utils/Utils.js.map +2 -2
- package/dist/esm/SwcAppEngine.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +64 -146
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/applyAttribute.js +36 -48
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +9 -12
- package/dist/esm/decorators/applyClass.js.map +2 -2
- package/dist/esm/decorators/applyNode.js +15 -23
- package/dist/esm/decorators/applyNode.js.map +2 -2
- package/dist/esm/decorators/applyProperty.js +12 -42
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applySlot.js +3 -3
- package/dist/esm/decorators/applySlot.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +9 -12
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/around.js +105 -0
- package/dist/esm/decorators/around.js.map +7 -0
- package/dist/esm/decorators/changedAttribute.js +40 -12
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +222 -193
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +15 -13
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/eventMedia.js +36 -8
- package/dist/esm/decorators/eventMedia.js.map +2 -2
- package/dist/esm/decorators/fetch.js +259 -0
- package/dist/esm/decorators/fetch.js.map +7 -0
- package/dist/esm/decorators/index.js +5 -2
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +33 -13
- package/dist/esm/decorators/intersectionObserver.js.map +2 -2
- package/dist/esm/decorators/lifecycles.js +11 -11
- package/dist/esm/decorators/lifecycles.js.map +2 -2
- package/dist/esm/decorators/mutationObserver.js +55 -39
- package/dist/esm/decorators/mutationObserver.js.map +3 -3
- package/dist/esm/decorators/parameter.js +92 -9
- package/dist/esm/decorators/parameter.js.map +2 -2
- package/dist/esm/decorators/publishSwcAppMessage.js +2 -2
- package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/esm/decorators/query.js +11 -5
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/requestAnimationFrame.js +8 -17
- package/dist/esm/decorators/requestAnimationFrame.js.map +2 -2
- package/dist/esm/decorators/resizeObserver.js +41 -17
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +37 -26
- package/dist/esm/decorators/setInterval.js.map +2 -2
- package/dist/esm/decorators/setTimeout.js +37 -26
- package/dist/esm/decorators/setTimeout.js.map +2 -2
- package/dist/esm/decorators/state.js +13 -10
- package/dist/esm/decorators/state.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppMessage.js +53 -0
- package/dist/esm/decorators/subscribeSwcAppMessage.js.map +7 -0
- package/dist/esm/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +20 -16
- package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/esm/decorators/swcAppRoute.js +130 -0
- package/dist/esm/decorators/swcAppRoute.js.map +7 -0
- package/dist/esm/elements/SwcApp.js +70 -0
- package/dist/esm/elements/SwcApp.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +264 -85
- package/dist/esm/elements/SwcAppMixin.js.map +3 -3
- package/dist/esm/types/index.js +5 -1
- package/dist/esm/types/index.js.map +2 -2
- package/dist/esm/utils/Utils.js +36 -15
- package/dist/esm/utils/Utils.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +1893 -1000
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/SwcAppEngine.d.ts +1 -0
- package/dist/types/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/decorators/addEventListener.d.ts +923 -960
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +26 -38
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +1 -3
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +15 -17
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +25 -45
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +1 -3
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/around.d.ts +87 -0
- package/dist/types/decorators/around.d.ts.map +1 -0
- package/dist/types/decorators/changedAttribute.d.ts +21 -1
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +0 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/eventMedia.d.ts +24 -1
- package/dist/types/decorators/eventMedia.d.ts.map +1 -1
- package/dist/types/decorators/fetch.d.ts +139 -0
- package/dist/types/decorators/fetch.d.ts.map +1 -0
- package/dist/types/decorators/index.d.ts +5 -2
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/intersectionObserver.d.ts +24 -11
- package/dist/types/decorators/intersectionObserver.d.ts.map +1 -1
- package/dist/types/decorators/lifecycles.d.ts +8 -7
- package/dist/types/decorators/lifecycles.d.ts.map +1 -1
- package/dist/types/decorators/mutationObserver.d.ts +26 -13
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/parameter.d.ts +84 -6
- package/dist/types/decorators/parameter.d.ts.map +1 -1
- package/dist/types/decorators/publishSwcAppMessage.d.ts +0 -1
- package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +7 -7
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/requestAnimationFrame.d.ts +1 -1
- package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -1
- package/dist/types/decorators/resizeObserver.d.ts +26 -13
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts +10 -0
- package/dist/types/decorators/setInterval.d.ts.map +1 -1
- package/dist/types/decorators/setTimeout.d.ts +10 -0
- package/dist/types/decorators/setTimeout.d.ts.map +1 -1
- package/dist/types/decorators/state.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts +44 -0
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +60 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
- package/dist/types/decorators/swcAppRoute.d.ts +94 -0
- package/dist/types/decorators/swcAppRoute.d.ts.map +1 -0
- package/dist/types/elements/SwcApp.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts +423 -388
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +43 -4
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +15 -5
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +1893 -1000
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +8 -6
- package/src/SwcAppEngine.ts +1 -0
- package/src/decorators/addEventListener.ts +102 -96
- package/src/decorators/applyAttribute.ts +76 -138
- package/src/decorators/applyClass.ts +8 -14
- package/src/decorators/applyNode.ts +33 -41
- package/src/decorators/applyProperty.ts +51 -99
- package/src/decorators/applySlot.ts +3 -3
- package/src/decorators/applyStyle.ts +8 -14
- package/src/decorators/around.ts +196 -0
- package/src/decorators/changedAttribute.ts +39 -15
- package/src/decorators/elementDefine.ts +270 -219
- package/src/decorators/emitCustomEvent.ts +17 -13
- package/src/decorators/eventMedia.ts +50 -9
- package/src/decorators/fetch.ts +454 -0
- package/src/decorators/index.ts +5 -2
- package/src/decorators/intersectionObserver.ts +38 -24
- package/src/decorators/lifecycles.ts +24 -22
- package/src/decorators/mutationObserver.ts +71 -52
- package/src/decorators/parameter.ts +145 -14
- package/src/decorators/publishSwcAppMessage.ts +3 -4
- package/src/decorators/query.ts +22 -14
- package/src/decorators/requestAnimationFrame.ts +10 -19
- package/src/decorators/resizeObserver.ts +50 -29
- package/src/decorators/setInterval.ts +37 -26
- package/src/decorators/setTimeout.ts +37 -26
- package/src/decorators/state.ts +19 -13
- package/src/decorators/subscribeSwcAppMessage.ts +106 -0
- package/src/decorators/subscribeSwcAppRouteChange.ts +131 -0
- package/src/decorators/swcAppRoute.ts +231 -0
- package/src/elements/SwcApp.ts +40 -10
- package/src/elements/SwcAppMixin.ts +340 -101
- package/src/types/index.ts +38 -5
- package/src/utils/Utils.ts +50 -18
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +0 -54
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +0 -23
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +0 -1
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +0 -31
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +0 -1
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +0 -91
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +0 -107
|
@@ -13,17 +13,19 @@ import {DOM_EVENT_NAMES, HTML_TAG_ENTRIES} from '../config/config';
|
|
|
13
13
|
import {SituationTypeContainer, SituationTypeContainers} from '@dooboostore/simple-boot/decorators/inject/Inject';
|
|
14
14
|
import {FirstCheckMaker} from '@dooboostore/simple-boot';
|
|
15
15
|
import {buildSwcParameterArgs, getParameterMetadata} from './parameter';
|
|
16
|
-
import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
|
|
16
|
+
import {ElementDefineLifeCycler, HelperHostSet, HostSet, InjectSituationType, IntersectionObserverSet, MutationObserverSet, MutationObserverSetEntry, ObserverScope, OnConnectedResult, ResizeObserverSet, ResizeObserverSetEntry, SwcRootType} from '../types';
|
|
17
17
|
import {ConvertUtils, ElementApply} from '@dooboostore/core-web';
|
|
18
18
|
import {isSSR} from "../elements/SwcAppMixin";
|
|
19
19
|
import {findAllStateMetadata} from "./state";
|
|
20
|
+
import {findAllAroundMetadata} from "./around";
|
|
20
21
|
import {findAllPropertyMetadata} from "./applyProperty";
|
|
21
|
-
import {MessageSubscribeLifeCycler} from "./
|
|
22
|
-
import {RouteSubscribeLifeCycler} from "./
|
|
22
|
+
import {MessageSubscribeLifeCycler} from "./subscribeSwcAppMessage";
|
|
23
|
+
import {RouteSubscribeLifeCycler} from "./subscribeSwcAppRouteChange";
|
|
23
24
|
import {SetIntervalLifeCycler} from "./setInterval";
|
|
24
25
|
import {SetTimeoutLifeCycler} from "./setTimeout";
|
|
25
26
|
import {RequestAnimationFrameLifeCycler} from "./requestAnimationFrame";
|
|
26
27
|
import {EventMediaLifeCycler} from "./eventMedia";
|
|
28
|
+
import {FetchLifeCycler} from "./fetch";
|
|
27
29
|
|
|
28
30
|
// --- Core Interfaces & Types ---
|
|
29
31
|
|
|
@@ -43,7 +45,7 @@ export interface ElementMetadata extends Omit<ElementConfig, 'window'> {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export const getElementConfig = (target: any): ElementMetadata | undefined => {
|
|
46
|
-
const constructor = target
|
|
48
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
47
49
|
return ReflectUtils.getMetadata(ELEMENT_CONFIG_KEY, constructor);
|
|
48
50
|
};
|
|
49
51
|
|
|
@@ -57,7 +59,7 @@ export const ensureInit = (inst: any) => { // HTMLElement
|
|
|
57
59
|
// delete (inst as any)[meta.propertyKey];
|
|
58
60
|
// });
|
|
59
61
|
|
|
60
|
-
const target = inst
|
|
62
|
+
const target = typeof inst === 'function' ? inst : inst.constructor;
|
|
61
63
|
|
|
62
64
|
// ╔════════════════════════════════════════════════════════════════════════════════╗
|
|
63
65
|
// ║ decorator 필드의 own property 삭제 ║
|
|
@@ -116,6 +118,19 @@ export const ensureInit = (inst: any) => { // HTMLElement
|
|
|
116
118
|
const propertyAllList = findAllPropertyMetadata(target).filter(it => it.type === 'property');
|
|
117
119
|
if (propertyAllList) {
|
|
118
120
|
propertyAllList.forEach(meta => {
|
|
121
|
+
// getter가 없으면(순수 필드: declare·하이드레이션 값 등) 지우지 않음.
|
|
122
|
+
// getter가 있을 때만 own 값을 지워 getter가 DOM을 새로 읽게 한다.
|
|
123
|
+
let proto = Object.getPrototypeOf(inst);
|
|
124
|
+
let hasGetter = false;
|
|
125
|
+
while (proto && proto !== Object.prototype) {
|
|
126
|
+
const d = Object.getOwnPropertyDescriptor(proto, meta.propertyKey);
|
|
127
|
+
if (d) {
|
|
128
|
+
hasGetter = typeof d.get === 'function';
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
proto = Object.getPrototypeOf(proto);
|
|
132
|
+
}
|
|
133
|
+
if (!hasGetter) return;
|
|
119
134
|
delete (inst as any)[meta.propertyKey];
|
|
120
135
|
});
|
|
121
136
|
}
|
|
@@ -123,9 +138,31 @@ export const ensureInit = (inst: any) => { // HTMLElement
|
|
|
123
138
|
const stateAllList = findAllStateMetadata(target);
|
|
124
139
|
if (stateAllList) {
|
|
125
140
|
stateAllList.forEach(meta => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
(
|
|
141
|
+
// getter로 읽으면 안 됨 — own이 남아있을 때 getter가 이주까지 해버려
|
|
142
|
+
// setter 재대입과 합치면 DOM 동기화가 중복 실행됨. descriptor에서 raw를 꺼낸다.
|
|
143
|
+
// own이 없으면(이미 정리됨) 스킵.
|
|
144
|
+
const own = Object.getOwnPropertyDescriptor(inst, meta.propertyKey);
|
|
145
|
+
if (own && 'value' in own) {
|
|
146
|
+
const initUserData = own.value;
|
|
147
|
+
delete (inst as any)[meta.propertyKey];
|
|
148
|
+
(inst as any)[meta.propertyKey] = initUserData;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// @around 필드의 own property 정리 → setter 경유로 재대입 (초기값에 set() 적용).
|
|
154
|
+
// attribute/query처럼 plain delete하면 초기값이 날아간다 — 반드시 살려서 setter로 넣는다.
|
|
155
|
+
// getter로 읽으면 안 됨(before 변환값이 섞임) — descriptor에서 raw를 꺼낸다.
|
|
156
|
+
// own이 없으면(이미 정리됨) 스킵 — 그래야 중복 변환이 안 생긴다.
|
|
157
|
+
const aroundAllList = findAllAroundMetadata(target);
|
|
158
|
+
if (aroundAllList) {
|
|
159
|
+
aroundAllList.forEach(meta => {
|
|
160
|
+
const own = Object.getOwnPropertyDescriptor(inst, meta.propertyKey);
|
|
161
|
+
if (own && 'value' in own) {
|
|
162
|
+
const initUserData = own.value;
|
|
163
|
+
delete (inst as any)[meta.propertyKey];
|
|
164
|
+
(inst as any)[meta.propertyKey] = initUserData;
|
|
165
|
+
}
|
|
129
166
|
});
|
|
130
167
|
}
|
|
131
168
|
|
|
@@ -155,7 +192,7 @@ const handleGlobalSwcEvent = async (event: Event) => {
|
|
|
155
192
|
if (!(node instanceof HTMLElement)) continue;
|
|
156
193
|
const script = node.getAttribute?.(attrName);
|
|
157
194
|
if (script && !getElementConfig(node)) {
|
|
158
|
-
const host: any = SwcUtils.
|
|
195
|
+
const host: any = SwcUtils.findNearestSwcAncestorHost(node);
|
|
159
196
|
if (host && host.isConnected && typeof host.__swc_executeAttributeEvent === 'function') {
|
|
160
197
|
await host.__swc_executeAttributeEvent(node, attrName, script, event);
|
|
161
198
|
(event as any).__swc_handled = true;
|
|
@@ -213,8 +250,7 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
213
250
|
const script = this.getAttribute(attrName);
|
|
214
251
|
if (script) {
|
|
215
252
|
try {
|
|
216
|
-
const
|
|
217
|
-
const currentWin = (this as any)._resolveWindow(conf);
|
|
253
|
+
const currentWin = SwcUtils.resolveWindow(this);
|
|
218
254
|
const helpers = SwcUtils.getHelperSet(currentWin);
|
|
219
255
|
const args = {...hostSet, ...helpers, ...extraArgs, $el: this, $root: this.getRootNode()};
|
|
220
256
|
FunctionUtils.execute({script, context: this, args});
|
|
@@ -224,21 +260,10 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
224
260
|
}
|
|
225
261
|
};
|
|
226
262
|
|
|
227
|
-
proto._resolveWindow = function (localConfig?: ElementConfig): Window {
|
|
228
|
-
ensureInit(this);
|
|
229
|
-
if (localConfig?.window) return localConfig.window;
|
|
230
|
-
const ancestors = SwcUtils.findAllSwcHostsIncludingSelf(this as any);
|
|
231
|
-
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
232
|
-
const aConf = getElementConfig(ancestors[i]);
|
|
233
|
-
if (aConf?.window) return aConf.window;
|
|
234
|
-
}
|
|
235
|
-
return getElementConfig(this).window ?? ((typeof window !== 'undefined' ? window : undefined) as Window);
|
|
236
|
-
};
|
|
237
|
-
|
|
238
263
|
proto._invokeLifecycleMethod = function (methodName: string | symbol, hostSet?: HostSet, extraArgs: any[] = []) {
|
|
239
264
|
ensureInit(this);
|
|
240
265
|
if (typeof (this as any)[methodName] !== 'function') return;
|
|
241
|
-
const useHostSet = hostSet ?? SwcUtils.getHelperAndHostSet(
|
|
266
|
+
const useHostSet = hostSet ?? SwcUtils.getHelperAndHostSet(this);
|
|
242
267
|
const app = useHostSet?.$appHost?.simpleApplication;
|
|
243
268
|
|
|
244
269
|
// @hostSet/@helperHostSet/@helperSet(parameter.ts)를 위한 값. lifecycle 메서드는
|
|
@@ -262,10 +287,13 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
262
287
|
// @hostSet/@helperHostSet/@helperSet가 붙은 파라미터는 @Inject/situationType 기반
|
|
263
288
|
// 해석보다 먼저 처리되고(firstCheckMaker), 그 외 파라미터는 기존처럼 @Inject/타입
|
|
264
289
|
// 기반으로 정상 해석된다 — 같은 메서드에서 두 방식을 섞어 써도 된다.
|
|
290
|
+
// swc 파라미터 데코레이터가 붙은 자리는 DI 로 넘기지 않는다. lifecycle 이 못 채우는 kind
|
|
291
|
+
// (예: @fetchSettled — @fetch 가 나중에 채움)는 null. undefined 를 돌려주면
|
|
292
|
+
// "처리 안 함"으로 보고 DI 가 파라미터 타입(Object 등)을 resolve 하려다 SimNoSuch 로 죽는다.
|
|
265
293
|
const firstCheckMaker: FirstCheckMaker = ({target, targetKey}, token, idx) => {
|
|
266
294
|
const saves = getParameterMetadata(target, targetKey!);
|
|
267
295
|
const found = saves.find(s => s.index === idx);
|
|
268
|
-
return found ? (kindValues as any)[found.kind] : undefined;
|
|
296
|
+
return found ? ((kindValues as any)[found.kind] ?? null) : undefined;
|
|
269
297
|
};
|
|
270
298
|
|
|
271
299
|
return app.simstanceManager.executeBindParameterSim(
|
|
@@ -304,8 +332,7 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
304
332
|
|
|
305
333
|
const handler = async (event: any) => {
|
|
306
334
|
const hostSet = SwcUtils.getHostSet(el);
|
|
307
|
-
const
|
|
308
|
-
const currentWin = (this as any)._resolveWindow(conf);
|
|
335
|
+
const currentWin = SwcUtils.resolveWindow(this);
|
|
309
336
|
const helpers = SwcUtils.getHelperSet(currentWin);
|
|
310
337
|
const args = {
|
|
311
338
|
event,
|
|
@@ -326,8 +353,7 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
326
353
|
ensureInit(this);
|
|
327
354
|
|
|
328
355
|
const hostSet = SwcUtils.getHostSet(el);
|
|
329
|
-
const
|
|
330
|
-
const currentWin = (this as any)._resolveWindow(conf);
|
|
356
|
+
const currentWin = SwcUtils.resolveWindow(this);
|
|
331
357
|
const currentHelpers = SwcUtils.getHelperSet(currentWin);
|
|
332
358
|
const detail = (event as CustomEvent).detail;
|
|
333
359
|
const args = {
|
|
@@ -344,6 +370,11 @@ const setupPrototype = (proto: any, win: Window) => {
|
|
|
344
370
|
};
|
|
345
371
|
};
|
|
346
372
|
|
|
373
|
+
// 부모 클래스도 elementDefine 됐으면 proto.xxxCallback 은 부모의 swc 래퍼다 — 그걸 원래 함수로 부르면 라이프사이클이 두 번 돈다.
|
|
374
|
+
// 래퍼에 기억해 둔 사용자 원래 함수(없으면 undefined)를 쓴다.
|
|
375
|
+
const SWC_ORIGINAL_CALLBACK = Symbol.for('simple-web-component:original-callback');
|
|
376
|
+
const userCallback = (fn: any) => (typeof fn === 'function' && SWC_ORIGINAL_CALLBACK in fn ? fn[SWC_ORIGINAL_CALLBACK] : fn);
|
|
377
|
+
|
|
347
378
|
export const elementDefine =
|
|
348
379
|
(name: string, config: Partial<ElementConfig> = {}): ClassDecorator =>
|
|
349
380
|
(constructor: any) => {
|
|
@@ -382,36 +413,87 @@ export const elementDefine =
|
|
|
382
413
|
const proto = constructor.prototype;
|
|
383
414
|
setupPrototype(proto, win);
|
|
384
415
|
|
|
385
|
-
//
|
|
386
|
-
|
|
416
|
+
// helperHostSet 은 인스턴스마다 다르다 — 클래스 단위 변수로 두면 다른 인스턴스의 connect 가 덮어써서
|
|
417
|
+
// disconnect 때 엉뚱한 인스턴스가 정리된다. connect 시점 값을 인스턴스에 저장해 disconnect/adopt 에서 쓴다.
|
|
418
|
+
const connectedHelperHostSet = (inst: any): HelperHostSet => inst.__swc_helperHostSet ?? SwcUtils.getHelperAndHostSet(inst);
|
|
387
419
|
// ── cyclers: 데코레이터별 라이프사이클 위임 클래스 (elementDefine 시 1회 생성, 계속 재사용) ──
|
|
388
420
|
const eventCycler = new EventListenerLifeCycler();
|
|
421
|
+
const resizeCycler = new ResizeObserverLifeCycler();
|
|
422
|
+
const intersectionCycler = new IntersectionObserverLifeCycler();
|
|
423
|
+
const mutationCycler = new MutationObserverLifeCycler();
|
|
389
424
|
const cyclers: ElementDefineLifeCycler[] = [
|
|
390
425
|
eventCycler,
|
|
391
426
|
new ChangedAttributeLifeCycler(),
|
|
392
427
|
new EmitCustomEventLifeCycler(),
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
428
|
+
resizeCycler,
|
|
429
|
+
intersectionCycler,
|
|
430
|
+
mutationCycler,
|
|
396
431
|
new MessageSubscribeLifeCycler(),
|
|
397
432
|
new RouteSubscribeLifeCycler(),
|
|
398
433
|
new SetIntervalLifeCycler(),
|
|
399
434
|
new SetTimeoutLifeCycler(),
|
|
400
435
|
new RequestAnimationFrameLifeCycler(),
|
|
401
436
|
new EventMediaLifeCycler(),
|
|
437
|
+
new FetchLifeCycler(),
|
|
402
438
|
];
|
|
403
439
|
|
|
404
440
|
// observedAttributes 기여 cycler (define-time, constructor 기반)
|
|
405
441
|
const cyclerObservedAttributes = cyclers.flatMap(c => c.getObservedAttributeNames?.(constructor) ?? []);
|
|
406
442
|
const mergedObservedAttributes = [...new Set([...(metadata.observedAttributes ?? []), ...originalStaticObservedAttributes, ...cyclerObservedAttributes, ...attributeApplyNames, ...hostAttributes, ...swcLifecycleAttributes, ...swcOnEvents])];
|
|
407
443
|
|
|
408
|
-
//
|
|
409
|
-
|
|
444
|
+
// ── observer 빌드 (connect 1회당 상태 1개) ──
|
|
445
|
+
// MutationObserver·ResizeObserver 는 인스턴스당 1개씩만 만들고 모든 데코레이터가 공유한다 (delegate 추적 포함).
|
|
446
|
+
// 'self'(렌더 전): 자기 자신 대상 mutation/resize 만 붙여 첫 렌더부터 본다.
|
|
447
|
+
// 'rest'(렌더 후): 나머지 전부를 같은 옵저버에 덧붙인다.
|
|
448
|
+
// 시클러가 scope 별로 자기 메타만 담은 콜백을 주고, 메타는 자기 대상 레코드/엔트리만 받으므로 따로 나눌 필요가 없다.
|
|
449
|
+
// IntersectionObserver 는 옵션 그룹별로 1개 ('rest' 전용).
|
|
450
|
+
type IntersectionDelegate = { target: string; delegateRoot?: SwcRootType; observe: (el: Element) => void; unobserve: (el: Element) => void };
|
|
451
|
+
interface ObserverState {
|
|
452
|
+
observers: Array<MutationObserver | ResizeObserver | IntersectionObserver>;
|
|
453
|
+
ro?: ResizeObserver;
|
|
454
|
+
roCallbacks: Set<ResizeObserverSetEntry['callback']>;
|
|
455
|
+
roObserved: WeakSet<Element>;
|
|
456
|
+
resizeDelegates: Array<ResizeObserverSetEntry & { target: string }>;
|
|
457
|
+
intersectionDelegates: IntersectionDelegate[];
|
|
458
|
+
mo?: MutationObserver;
|
|
459
|
+
moCallbacks: Set<MutationObserverSetEntry['callback']>;
|
|
460
|
+
moTargets: Map<Element | ShadowRoot, MutationObserverInit>;
|
|
461
|
+
}
|
|
462
|
+
const newObserverState = (): ObserverState => ({
|
|
463
|
+
observers: [], roCallbacks: new Set(), roObserved: new WeakSet(), resizeDelegates: [], intersectionDelegates: [],
|
|
464
|
+
moCallbacks: new Set(), moTargets: new Map(),
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// 같은 target 이 여러 소스에서 지정되면 MutationObserverInit 을 합집합(최대 감지범위)으로 병합한다.
|
|
468
|
+
const mergeMutationInit = (existing: MutationObserverInit | undefined, next: MutationObserverInit): MutationObserverInit => {
|
|
469
|
+
if (!existing) return next;
|
|
470
|
+
const mergedFilter = existing.attributeFilter || next.attributeFilter
|
|
471
|
+
? [...new Set([...(existing.attributeFilter ?? []), ...(next.attributeFilter ?? [])])]
|
|
472
|
+
: undefined;
|
|
473
|
+
return {
|
|
474
|
+
childList: existing.childList || next.childList,
|
|
475
|
+
attributes: existing.attributes || next.attributes,
|
|
476
|
+
characterData: existing.characterData || next.characterData,
|
|
477
|
+
subtree: existing.subtree || next.subtree,
|
|
478
|
+
attributeOldValue: existing.attributeOldValue || next.attributeOldValue,
|
|
479
|
+
characterDataOldValue: existing.characterDataOldValue || next.characterDataOldValue,
|
|
480
|
+
attributeFilter: mergedFilter,
|
|
481
|
+
};
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// delegate 셀렉터에 맞는 요소(자신 또는 하위)를 찾아 observe/unobserve — MutationObserver 콜백에서 동적 추적용
|
|
485
|
+
const forDelegateMatches = (n: Node, selector: string, fn: (el: Element) => void) => {
|
|
486
|
+
if (n.nodeType !== 1) return;
|
|
487
|
+
const el = n as HTMLElement;
|
|
488
|
+
const isThis = selector === '$this' || selector === '';
|
|
489
|
+
if (isThis || el.matches?.(selector) || el.closest?.(selector)) fn(el);
|
|
490
|
+
else el.querySelectorAll(selector).forEach(fn);
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const buildObservers = async (helperHostSet: HelperHostSet, scope: ObserverScope, st: ObserverState, alive: () => boolean): Promise<void> => {
|
|
410
494
|
const inst = helperHostSet.$this;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
let mutationObserverSet: MutationObserverSet = [];
|
|
414
|
-
let intersectionObserverSet: IntersectionObserverSet = [];
|
|
495
|
+
// 'self' 는 순수 수집기(observer 시클러)만 돌린다 — 리스너·타이머 등 부작용 시클러는 'rest' 에서 한 번만.
|
|
496
|
+
const activeCyclers = scope === 'self' ? [resizeCycler, mutationCycler] : cyclers;
|
|
415
497
|
|
|
416
498
|
// delegate 추적 root 해석: shadow/light/auto/all → 실제 root 목록
|
|
417
499
|
const resolveDelegateRoots = (delegateRoot: SwcRootType | undefined): (HTMLElement | ShadowRoot)[] => {
|
|
@@ -424,225 +506,143 @@ export const elementDefine =
|
|
|
424
506
|
return roots;
|
|
425
507
|
};
|
|
426
508
|
|
|
427
|
-
// 1.
|
|
428
|
-
|
|
429
|
-
|
|
509
|
+
// 1. 시클러 onConnected → observer Set 수집 (여러 시클러가 반환한 Set 을 전부 누적)
|
|
510
|
+
let resizeObserverSet: ResizeObserverSet = [];
|
|
511
|
+
let mutationObserverSet: MutationObserverSet = [];
|
|
512
|
+
let intersectionObserverSet: IntersectionObserverSet = [];
|
|
513
|
+
for (const c of activeCyclers) {
|
|
514
|
+
const result = (await c.onConnected?.(helperHostSet, resizeObserverSet, scope)) as OnConnectedResult | void;
|
|
515
|
+
// await 사이에 떼어졌으면(새 연결이 차례를 가져갔으면) 여기서 멈춘다 — 옵저버를 만들면 아무도 disconnect 하지 않아 샌다.
|
|
516
|
+
if (!alive()) return;
|
|
430
517
|
if (!result) continue;
|
|
431
518
|
if (result.resizeObserverSet?.length) resizeObserverSet = [...resizeObserverSet, ...result.resizeObserverSet];
|
|
432
519
|
if (result.mutationObserverSet?.length) mutationObserverSet = [...mutationObserverSet, ...result.mutationObserverSet];
|
|
433
520
|
if (result.intersectionObserverSet?.length) intersectionObserverSet = [...intersectionObserverSet, ...result.intersectionObserverSet];
|
|
434
521
|
}
|
|
435
522
|
|
|
436
|
-
// 2.
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
523
|
+
// 2. ResizeObserver (1개). delegate 항목은 셀렉터 문자열이라 초기 observe 하지 않고 MutationObserver 로 동적 추적한다.
|
|
524
|
+
const resizeObserve = (el: Element, opts?: ResizeObserverOptions) => {
|
|
525
|
+
if (!st.ro || st.roObserved.has(el)) return;
|
|
526
|
+
st.ro.observe(el, opts);
|
|
527
|
+
st.roObserved.add(el);
|
|
528
|
+
};
|
|
529
|
+
const resizeUnobserve = (el: Element) => {
|
|
530
|
+
if (!st.ro || !st.roObserved.has(el)) return;
|
|
531
|
+
st.ro.unobserve(el);
|
|
532
|
+
st.roObserved.delete(el);
|
|
533
|
+
};
|
|
534
|
+
const newResizeDelegates: ObserverState['resizeDelegates'] = [];
|
|
535
|
+
if (resizeObserverSet.length) {
|
|
536
|
+
if (!st.ro) {
|
|
537
|
+
st.ro = new win.ResizeObserver((entries, obs) => { for (const cb of st.roCallbacks) cb(entries, obs); });
|
|
538
|
+
st.observers.push(st.ro);
|
|
539
|
+
}
|
|
449
540
|
for (const e of resizeObserverSet) {
|
|
450
|
-
|
|
541
|
+
st.roCallbacks.add(e.callback);
|
|
542
|
+
if (e.delegate) {
|
|
543
|
+
if (typeof e.target === 'string') newResizeDelegates.push(e as ResizeObserverSetEntry & { target: string });
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
451
546
|
if (typeof e.target === 'string') {
|
|
452
547
|
// 문자열(non-delegate)이면 root 에서 검색된 요소들을 observe 대상으로 추가한다.
|
|
453
|
-
const
|
|
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
|
-
}
|
|
548
|
+
for (const r of resolveDelegateRoots(e.delegateRoot)) r.querySelectorAll(e.target).forEach(el => resizeObserve(el, e.options));
|
|
459
549
|
continue;
|
|
460
550
|
}
|
|
461
|
-
ro.observe(e.target as Element, e.options);
|
|
462
|
-
if (e.target instanceof Element)
|
|
551
|
+
st.ro.observe(e.target as Element, e.options);
|
|
552
|
+
if (e.target instanceof Element) st.roObserved.add(e.target);
|
|
463
553
|
}
|
|
464
|
-
|
|
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);
|
|
554
|
+
st.resizeDelegates.push(...newResizeDelegates);
|
|
475
555
|
}
|
|
476
556
|
|
|
477
|
-
// IntersectionObserver: 옵션 그룹별로 1
|
|
478
|
-
|
|
479
|
-
// 동적 observe/unobserve 중복 방지를 위해 그룹별 WeakSet 으로 추적한다.
|
|
480
|
-
const intersectionDelegates: Array<{ target: string; delegateRoot?: SwcRootType; observe: (el: Element) => void; unobserve: (el: Element) => void }> = [];
|
|
557
|
+
// 3. IntersectionObserver: 옵션 그룹별로 1개. delegate 항목은 MutationObserver 로 동적 추적한다.
|
|
558
|
+
const newIntersectionDelegates: IntersectionDelegate[] = [];
|
|
481
559
|
for (const group of intersectionObserverSet) {
|
|
482
560
|
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);
|
|
561
|
+
const io = new win.IntersectionObserver((entries, obs) => { for (const cb of ioCallbacks) cb(entries, obs); }, group.options);
|
|
486
562
|
const ioObserved = new WeakSet<Element>();
|
|
563
|
+
const observe = (el: Element) => { if (ioObserved.has(el)) return; io.observe(el); ioObserved.add(el); };
|
|
564
|
+
const unobserve = (el: Element) => { if (!ioObserved.has(el)) return; io.unobserve(el); ioObserved.delete(el); };
|
|
487
565
|
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
|
-
}
|
|
566
|
+
if (e.delegate) { newIntersectionDelegates.push({ target: e.target as string, delegateRoot: e.delegateRoot, observe, unobserve }); continue; }
|
|
503
567
|
if (typeof e.target === 'string') {
|
|
504
|
-
|
|
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
|
-
}
|
|
568
|
+
for (const r of resolveDelegateRoots(e.delegateRoot)) r.querySelectorAll(e.target).forEach(observe);
|
|
511
569
|
continue;
|
|
512
570
|
}
|
|
513
|
-
|
|
514
|
-
if (e.target instanceof Element) ioObserved.add(e.target);
|
|
571
|
+
observe(e.target as Element);
|
|
515
572
|
}
|
|
516
|
-
observers.push(io);
|
|
573
|
+
st.observers.push(io);
|
|
517
574
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
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
|
-
}
|
|
575
|
+
st.intersectionDelegates.push(...newIntersectionDelegates);
|
|
576
|
+
|
|
577
|
+
// 4. MutationObserver (1개): mutation 콜백 + resize/intersection delegate 동적 추적을 함께 처리한다.
|
|
578
|
+
if (mutationObserverSet.length || newResizeDelegates.length || newIntersectionDelegates.length) {
|
|
579
|
+
if (!st.mo) {
|
|
580
|
+
st.mo = new win.MutationObserver((mutations, obs) => {
|
|
581
|
+
for (const mut of mutations) {
|
|
582
|
+
for (const [nodes, on] of [[mut.addedNodes, true], [mut.removedNodes, false]] as const) {
|
|
583
|
+
for (const n of Array.from(nodes)) {
|
|
584
|
+
for (const d of st.resizeDelegates) forDelegateMatches(n, d.target, el => on ? resizeObserve(el, d.options) : resizeUnobserve(el));
|
|
585
|
+
for (const d of st.intersectionDelegates) forDelegateMatches(n, d.target, el => on ? d.observe(el) : d.unobserve(el));
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
for (const cb of st.moCallbacks) cb(mutations, obs);
|
|
590
|
+
});
|
|
591
|
+
st.observers.push(st.mo);
|
|
558
592
|
}
|
|
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
593
|
const addObserveTarget = (target: Element | ShadowRoot, options: MutationObserverInit) => {
|
|
591
|
-
|
|
594
|
+
st.moTargets.set(target, mergeMutationInit(st.moTargets.get(target), options));
|
|
592
595
|
};
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
for (const e of mutationObserverSet ?? []) {
|
|
596
|
+
for (const e of mutationObserverSet) {
|
|
597
|
+
st.moCallbacks.add(e.callback);
|
|
596
598
|
if (e.delegate) continue;
|
|
597
599
|
const init = e.options ?? { childList: true };
|
|
598
600
|
if (typeof e.target === 'string') {
|
|
599
|
-
|
|
600
|
-
const scope = resolveDelegateRoots(e.delegateRoot);
|
|
601
|
-
for (const sr of scope) {
|
|
602
|
-
sr.querySelectorAll(e.target).forEach(el => addObserveTarget(el, init));
|
|
603
|
-
}
|
|
601
|
+
for (const r of resolveDelegateRoots(e.delegateRoot)) r.querySelectorAll(e.target).forEach(el => addObserveTarget(el, init));
|
|
604
602
|
continue;
|
|
605
603
|
}
|
|
606
604
|
addObserveTarget(e.target, init);
|
|
607
605
|
}
|
|
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) {
|
|
606
|
+
for (const d of [...newResizeDelegates, ...newIntersectionDelegates]) {
|
|
614
607
|
for (const r of resolveDelegateRoots(d.delegateRoot)) addObserveTarget(r, { childList: true, subtree: true });
|
|
615
608
|
}
|
|
616
|
-
|
|
617
|
-
for (const [target, options] of
|
|
618
|
-
mo.observe(target, options);
|
|
619
|
-
}
|
|
620
|
-
observers.push(mo);
|
|
609
|
+
// 같은 노드를 다시 observe 하면 옵션만 교체된다 (등록·전달 중복 없음) → 'self' 분과 합쳐진 옵션으로 갱신
|
|
610
|
+
for (const [target, options] of st.moTargets) st.mo.observe(target, options);
|
|
621
611
|
}
|
|
622
612
|
|
|
623
|
-
inst.__swc_observers = observers;
|
|
613
|
+
inst.__swc_observers = st.observers;
|
|
624
614
|
};
|
|
625
615
|
|
|
626
|
-
const originalConnected = proto.connectedCallback;
|
|
616
|
+
const originalConnected = userCallback(proto.connectedCallback);
|
|
627
617
|
proto.connectedCallback = async function () {
|
|
628
618
|
ensureInit(this);
|
|
629
619
|
// 재연결 시 누적 방지 — cycler 내부 리소스 초기화
|
|
630
|
-
helperHostSet = SwcUtils.getHelperAndHostSet(
|
|
620
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(this as any);
|
|
621
|
+
(this as any).__swc_helperHostSet = helperHostSet;
|
|
631
622
|
const appHost = helperHostSet.$appHost;
|
|
632
623
|
const useSsr = isSSR(this);
|
|
624
|
+
// 연결 처리는 await 를 거친다. 그 사이 떨어지거나 다시 붙으면(disconnect / 새 connect 가 차례 번호를 올리면) 이 연결은 멈춘다 —
|
|
625
|
+
// 안 그러면 떼어진 뒤에 리스너·타이머가 등록돼 새고, 다시 붙은 연결과 겹쳐 두 번씩 돈다.
|
|
626
|
+
// (isConnected 는 보지 않는다 — 직접 호출이나 SSR 처럼 붙어 있지 않아도 라이프사이클은 돌아야 한다)
|
|
627
|
+
const gen = ((this as any).__swc_connectGen = ((this as any).__swc_connectGen ?? 0) + 1);
|
|
628
|
+
const alive = () => (this as any).__swc_connectGen === gen;
|
|
629
|
+
const obsState = newObserverState();
|
|
633
630
|
try {
|
|
634
631
|
if (appHost && typeof (appHost as any)._connected === 'function') {
|
|
635
632
|
await (appHost as any)._connected(this);
|
|
633
|
+
if (!alive()) return;
|
|
636
634
|
} else if (appHost && typeof (appHost as any)) {
|
|
637
635
|
(appHost as any)._connected_safari_and_standby ??= [];
|
|
638
636
|
(appHost as any)._connected_safari_and_standby.push(this);
|
|
639
637
|
}
|
|
640
638
|
|
|
641
639
|
const conf = getElementConfig(this);
|
|
642
|
-
const currentWin = (this as any)._resolveWindow(conf);
|
|
643
640
|
|
|
644
641
|
// before-connected 라이프사이클 메서드 (@onConnectedBefore)
|
|
645
|
-
for (const m of findAllOnConnectedBeforeMetadata(this))
|
|
642
|
+
for (const m of findAllOnConnectedBeforeMetadata(this)) {
|
|
643
|
+
await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
644
|
+
if (!alive()) return;
|
|
645
|
+
}
|
|
646
646
|
(this as any)._executeSwcScript('swc-on-before-connected', helperHostSet);
|
|
647
647
|
|
|
648
648
|
// ── 렌더 (@onConnected / @onConnectedBody*) — elementDefine 책임 ──
|
|
@@ -653,6 +653,10 @@ export const elementDefine =
|
|
|
653
653
|
this.attachShadow({ mode: mode as ShadowRootMode });
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
+
// 자기 자신 대상 mutation/resize 는 렌더 전에 붙여 첫 렌더부터 본다 (shadow root 가 생긴 뒤라 $this 가 맞는 root 를 잡는다)
|
|
657
|
+
await buildObservers(helperHostSet, 'self', obsState, alive);
|
|
658
|
+
if (!alive()) return;
|
|
659
|
+
|
|
656
660
|
const stateContext: any = { ...helperHostSet };
|
|
657
661
|
findAllStateMetadata(this).forEach(it => {
|
|
658
662
|
stateContext[it.name] = this[it.propertyKey];
|
|
@@ -662,8 +666,40 @@ export const elementDefine =
|
|
|
662
666
|
const lightChildren: Node[] = [];
|
|
663
667
|
|
|
664
668
|
if (targetConnectedList.length > 0) {
|
|
669
|
+
const toNodes = (value: any): Node[] => {
|
|
670
|
+
if (!value) return [];
|
|
671
|
+
if (typeof value === 'string') {
|
|
672
|
+
const htmlTemplateElement = doc.createElement('template');
|
|
673
|
+
htmlTemplateElement.innerHTML = value;
|
|
674
|
+
return Array.from(htmlTemplateElement.content.childNodes);
|
|
675
|
+
}
|
|
676
|
+
if (Array.isArray(value)) return value;
|
|
677
|
+
return [value];
|
|
678
|
+
};
|
|
679
|
+
// fallback: 비동기 렌더 대기 중에 먼저 보여준다. 결과는 모든 렌더 메서드가 끝난 뒤 한 번에 들어가므로
|
|
680
|
+
// 그때 같이 걷어낸다 (메서드별로 걷으면 다음 메서드를 기다리는 동안 빈 화면이 된다).
|
|
681
|
+
const shownFallbacks: Node[] = [];
|
|
682
|
+
const removeFallbacks = () => shownFallbacks.splice(0).forEach(n => (n as ChildNode).remove());
|
|
665
683
|
for (const meta of targetConnectedList) {
|
|
684
|
+
const useShadowRoot = meta.options.useShadow || conf?.useShadow;
|
|
685
|
+
if (meta.options.fallback) {
|
|
686
|
+
const fb = typeof meta.options.fallback === 'function'
|
|
687
|
+
? (meta.options.fallback as any)(helperHostSet)
|
|
688
|
+
: meta.options.fallback;
|
|
689
|
+
const fallbackNodes = toNodes(fb);
|
|
690
|
+
const parent = useShadowRoot ? this.shadowRoot : this;
|
|
691
|
+
if (fallbackNodes.length && parent) {
|
|
692
|
+
try {
|
|
693
|
+
const processed = SwcUtils.projectProcessHtml(this._swcId, fallbackNodes, doc);
|
|
694
|
+
parent.append(...processed);
|
|
695
|
+
shownFallbacks.push(...processed);
|
|
696
|
+
} catch (e) {
|
|
697
|
+
console.error('[ElementDefine] fallback setting error:', e);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
666
701
|
let res = await (this as any)._invokeLifecycleMethod(meta.propertyKey, helperHostSet);
|
|
702
|
+
if (!alive()) { removeFallbacks(); return; }
|
|
667
703
|
if (typeof res === 'string') {
|
|
668
704
|
const htmlTemplateElement = doc.createElement('template');
|
|
669
705
|
htmlTemplateElement.innerHTML = res;
|
|
@@ -678,6 +714,7 @@ export const elementDefine =
|
|
|
678
714
|
}
|
|
679
715
|
}
|
|
680
716
|
}
|
|
717
|
+
removeFallbacks();
|
|
681
718
|
try {
|
|
682
719
|
if (this.shadowRoot) {
|
|
683
720
|
const applyShadowChildren = SwcUtils.projectProcessHtml(this._swcId, shadowChildren, doc);
|
|
@@ -704,22 +741,27 @@ export const elementDefine =
|
|
|
704
741
|
globalDelegatedRoots.add(root);
|
|
705
742
|
}
|
|
706
743
|
|
|
707
|
-
// ── cycler 실행 (렌더 후):
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
await buildObservers(SwcUtils.getHelperAndHostSet(win, this as any));
|
|
744
|
+
// ── cycler 실행 (렌더 후): 나머지 시클러 onConnected → observer Set 수집 → 같은 옵저버에 덧붙임 ──
|
|
745
|
+
await buildObservers(helperHostSet, 'rest', obsState, alive);
|
|
746
|
+
if (!alive()) return;
|
|
711
747
|
|
|
712
748
|
if (originalConnected) await originalConnected.apply(this);
|
|
749
|
+
if (!alive()) return;
|
|
713
750
|
|
|
714
751
|
// after-connected 라이프사이클 메서드 (@onConnectedAfter)
|
|
715
|
-
for (const m of findAllOnConnectedAfterMetadata(this))
|
|
752
|
+
for (const m of findAllOnConnectedAfterMetadata(this)) {
|
|
753
|
+
await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
754
|
+
if (!alive()) return;
|
|
755
|
+
}
|
|
716
756
|
(this as any)._executeSwcScript('swc-on-connected', helperHostSet);
|
|
717
757
|
(this as any)._executeSwcScript('swc-on-after-connected', helperHostSet);
|
|
718
758
|
(this as any).__swc_connected = true;
|
|
719
759
|
} finally {
|
|
720
|
-
|
|
760
|
+
// 멈춘 연결은 완료 훅을 부르지 않고, 앱 host 에는 중단만 알린다 (자식 연결 수만 맞추고 재생은 안 함)
|
|
761
|
+
const aborted = !alive();
|
|
762
|
+
if (!aborted) for (const m of findAllLifecycleMetadata(this, ON_CONNECTED_COMPLETED_METADATA_KEY)) await (this as any)._invokeLifecycleMethod(m.propertyKey, helperHostSet);
|
|
721
763
|
if (appHost && typeof (appHost as any)._connectedDone === 'function') {
|
|
722
|
-
await (appHost as any)._connectedDone(this);
|
|
764
|
+
await (appHost as any)._connectedDone(this, { aborted });
|
|
723
765
|
}
|
|
724
766
|
}
|
|
725
767
|
};
|
|
@@ -727,16 +769,19 @@ export const elementDefine =
|
|
|
727
769
|
/////////////////////////////////////////////////
|
|
728
770
|
// disconnectedCallback
|
|
729
771
|
////////////////////////////////////////////////
|
|
730
|
-
const originalDisconnected = proto.disconnectedCallback;
|
|
772
|
+
const originalDisconnected = userCallback(proto.disconnectedCallback);
|
|
731
773
|
proto.disconnectedCallback = function () {
|
|
732
|
-
|
|
774
|
+
(this as any).__swc_connectGen = ((this as any).__swc_connectGen ?? 0) + 1; // 진행 중인 연결은 멈춘다
|
|
775
|
+
// 떨어진 뒤라 DOM 으로는 $appHost 를 다시 못 찾는다 → connect 때 저장한 이 인스턴스의 값
|
|
776
|
+
const helperHostSet = connectedHelperHostSet(this);
|
|
777
|
+
const appHost = helperHostSet.$appHost;
|
|
733
778
|
if (appHost && typeof (appHost as any)._disconnected === 'function') {
|
|
734
779
|
(appHost as any)._disconnected(this);
|
|
735
780
|
}
|
|
736
781
|
|
|
737
782
|
(this as any)._executeSwcScript('swc-on-before-disconnected', helperHostSet);
|
|
738
783
|
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
|
|
784
|
+
for (const c of cyclers) c.onDisconnected?.(helperHostSet);
|
|
740
785
|
|
|
741
786
|
for (const o of (this as any).__swc_observers ?? []) {
|
|
742
787
|
try { (o as any)?.disconnect?.(); } catch (e) { console.error('[SWC] observer disconnect error:', e); }
|
|
@@ -754,12 +799,12 @@ export const elementDefine =
|
|
|
754
799
|
(this as any).__swc_connected = false;
|
|
755
800
|
};
|
|
756
801
|
|
|
757
|
-
const originalAdopted = proto.adoptedCallback;
|
|
802
|
+
const originalAdopted = userCallback(proto.adoptedCallback);
|
|
758
803
|
proto.adoptedCallback = function () {
|
|
759
804
|
const hostSet = SwcUtils.getHostSet(this as any);
|
|
760
805
|
(this as any)._executeSwcScript('swc-on-before-adopted', hostSet);
|
|
761
806
|
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?.(
|
|
807
|
+
for (const c of cyclers) c.onAdopted?.(connectedHelperHostSet(this));
|
|
763
808
|
|
|
764
809
|
if (originalAdopted) originalAdopted.apply(this);
|
|
765
810
|
|
|
@@ -768,13 +813,13 @@ export const elementDefine =
|
|
|
768
813
|
(this as any)._executeSwcScript('swc-on-after-adopted', hostSet);
|
|
769
814
|
};
|
|
770
815
|
|
|
771
|
-
const originalAttributeChanged = proto.attributeChangedCallback;
|
|
816
|
+
const originalAttributeChanged = userCallback(proto.attributeChangedCallback);
|
|
772
817
|
proto.attributeChangedCallback = function (name: string, old: string | null, newVal: string | null) {
|
|
773
818
|
if (originalAttributeChanged) originalAttributeChanged.apply(this, [name, old, newVal]);
|
|
774
819
|
|
|
775
820
|
// attributeChangedCallback 은 connected 이전에도 호출될 수 있으므로
|
|
776
821
|
// 클로저의 helperHostSet(null 가능) 대신 이 시점에 새로 계산한다.
|
|
777
|
-
const helperAndHostSet = SwcUtils.getHelperAndHostSet(
|
|
822
|
+
const helperAndHostSet = SwcUtils.getHelperAndHostSet(this as any);
|
|
778
823
|
|
|
779
824
|
// Process expression directive before passing to handlers
|
|
780
825
|
let processedVal: any = newVal;
|
|
@@ -782,8 +827,8 @@ export const elementDefine =
|
|
|
782
827
|
const ae = new ActionExpression(newVal);
|
|
783
828
|
const expr = ae.getFirstExpression('callReturn');
|
|
784
829
|
if (expr) {
|
|
785
|
-
const win = (this
|
|
786
|
-
const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(
|
|
830
|
+
const win = SwcUtils.resolveWindow(this);
|
|
831
|
+
const exprHelperAndHostSet = SwcUtils.getHelperAndHostSet(this as any);
|
|
787
832
|
const script = ConvertUtils.decodeHtmlEntity(expr.script, win.document);
|
|
788
833
|
try {
|
|
789
834
|
const result = FunctionUtils.executeReturn({
|
|
@@ -815,6 +860,12 @@ export const elementDefine =
|
|
|
815
860
|
configurable: true
|
|
816
861
|
});
|
|
817
862
|
|
|
863
|
+
// 이 클래스를 다시 상속해 elementDefine 하면 자식이 이 래퍼 대신 원래 사용자 함수를 부르도록 기억해 둔다
|
|
864
|
+
proto.connectedCallback[SWC_ORIGINAL_CALLBACK] = originalConnected;
|
|
865
|
+
proto.disconnectedCallback[SWC_ORIGINAL_CALLBACK] = originalDisconnected;
|
|
866
|
+
proto.adoptedCallback[SWC_ORIGINAL_CALLBACK] = originalAdopted;
|
|
867
|
+
proto.attributeChangedCallback[SWC_ORIGINAL_CALLBACK] = originalAttributeChanged;
|
|
868
|
+
|
|
818
869
|
ReflectUtils.defineMetadata(ELEMENT_CONFIG_KEY, metadata, constructor);
|
|
819
870
|
const registry = metadata.customElementRegistry || (win as any)?.customElements;
|
|
820
871
|
if (registry && !registry.get(metadata.name)) {
|