@dooboostore/simple-web-component 1.0.50 → 1.0.51
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 +256 -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 +258 -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 +1886 -999
- 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 +1886 -999
- 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 +330 -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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
-
import { ensureInit
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
3
|
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { buildSwcParameterArgs } from './parameter';
|
|
4
5
|
import { ElementDefineLifeCycler, HelperHostSet } from '../types';
|
|
5
6
|
|
|
6
7
|
export const SET_INTERVAL_METADATA_KEY = Symbol.for('simple-web-component:set-interval');
|
|
@@ -18,6 +19,12 @@ export interface SetIntervalOptions {
|
|
|
18
19
|
parameter?: (set: HelperHostSet) => any[];
|
|
19
20
|
/** 타이머 등록 직후 1회 호출 (디버깅/로깅용). id는 실제 setInterval 반환값 - cleanup은 자동으로 처리되므로 직접 clear할 필요 없음. */
|
|
20
21
|
created?: (set: HelperHostSet, id: number) => void;
|
|
22
|
+
/** [type:'onConnected' 전용] 매 tick 실행 여부 게이트. Promise<boolean>도 가능. false면 이 tick 스킵. */
|
|
23
|
+
filter?: (set: HelperHostSet) => boolean | Promise<boolean>;
|
|
24
|
+
/** [type:'onConnected' 전용] tick 핸들러 직전 훅. await되고, 리턴값은 @setIntervalBeforeReturn 으로 주입된다. */
|
|
25
|
+
before?: (set: HelperHostSet) => any | Promise<any>;
|
|
26
|
+
/** [type:'onConnected' 전용] tick 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 인자/결과/에러. */
|
|
27
|
+
finally?: (set: HelperHostSet, ctx: { args: any[]; result?: any; error?: any }) => any | Promise<any>;
|
|
21
28
|
/**
|
|
22
29
|
* type:'onConnected' - 매 tick마다 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
|
|
23
30
|
* type:'returnValue' - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을 "실제로 반복 실행될 함수"로 사용한다.
|
|
@@ -33,15 +40,7 @@ export interface SetIntervalMetadata {
|
|
|
33
40
|
options: SetIntervalOptions;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
|
-
const
|
|
37
|
-
const getActiveEntries = (inst: any): number[] => {
|
|
38
|
-
let entries = activeMap.get(inst);
|
|
39
|
-
if (!entries) {
|
|
40
|
-
entries = [];
|
|
41
|
-
activeMap.set(inst, entries);
|
|
42
|
-
}
|
|
43
|
-
return entries;
|
|
44
|
-
};
|
|
43
|
+
const getActiveEntries = (inst: any): number[] => (inst.__swc_intervalIds ??= []);
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* type:'onConnected' - connect 시 자동으로 setInterval을 걸고, disconnect 시 자동으로 clearInterval 한다.
|
|
@@ -76,8 +75,7 @@ export function setInterval(interval: number, options: SetIntervalOptions = {}):
|
|
|
76
75
|
|
|
77
76
|
if (tickFn) {
|
|
78
77
|
ensureInit(this);
|
|
79
|
-
const
|
|
80
|
-
const win = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
|
|
78
|
+
const win = SwcUtils.resolveWindow(this);
|
|
81
79
|
const id = win.setInterval(() => {
|
|
82
80
|
try {
|
|
83
81
|
tickFn(id);
|
|
@@ -85,7 +83,7 @@ export function setInterval(interval: number, options: SetIntervalOptions = {}):
|
|
|
85
83
|
console.error('[SWC] setInterval tick error:', e);
|
|
86
84
|
}
|
|
87
85
|
}, interval);
|
|
88
|
-
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(
|
|
86
|
+
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(this), id);
|
|
89
87
|
getActiveEntries(this).push(id);
|
|
90
88
|
}
|
|
91
89
|
return res;
|
|
@@ -95,27 +93,40 @@ export function setInterval(interval: number, options: SetIntervalOptions = {}):
|
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
export const findAllSetIntervalMetadata = (target: any): SetIntervalMetadata[] => {
|
|
98
|
-
const constructor = target
|
|
96
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
99
97
|
return (ReflectUtils.findAllMetadata<SetIntervalMetadata[]>(SET_INTERVAL_METADATA_KEY, constructor) || []).flat();
|
|
100
98
|
};
|
|
101
99
|
|
|
102
100
|
export class SetIntervalLifeCycler implements ElementDefineLifeCycler {
|
|
103
101
|
onConnected(helperHostSet: HelperHostSet): void {
|
|
104
102
|
const inst = helperHostSet.$this;
|
|
105
|
-
|
|
103
|
+
inst.__swc_intervalIds = []; // 재연결 대비 리셋
|
|
106
104
|
for (const meta of findAllSetIntervalMetadata(inst)) {
|
|
107
105
|
if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
|
|
108
106
|
const id = helperHostSet.$w.setInterval(() => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
void (async () => {
|
|
108
|
+
const helper = SwcUtils.getHelperAndHostSet(inst);
|
|
109
|
+
if (meta.options.filter && !(await meta.options.filter(helper))) return;
|
|
110
|
+
const paramArgs = meta.options.parameter?.(helperHostSet) ?? [];
|
|
111
|
+
const hostSet = SwcUtils.getHostSet(inst);
|
|
112
|
+
const helperSet = SwcUtils.getHelperSet(helperHostSet.$w);
|
|
113
|
+
const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, meta.propertyKey, {
|
|
114
|
+
hostSet, helperHostSet: helper, helperSet, setIntervalBeforeReturn: beforeReturn
|
|
115
|
+
}, [...paramArgs, beforeReturn]);
|
|
116
|
+
let args = buildArgs(undefined);
|
|
117
|
+
if (meta.options.before) { const br = await meta.options.before(helper); args = buildArgs(br); }
|
|
118
|
+
let result: any, error: any;
|
|
119
|
+
try {
|
|
120
|
+
result = await inst[meta.propertyKey](...args);
|
|
121
|
+
const vk = meta.options.valueKey ?? SET_INTERVAL_METADATA_KEY;
|
|
122
|
+
if (result && typeof result === 'object' && typeof result[vk] === 'function') result[vk](id);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
error = e;
|
|
125
|
+
} finally {
|
|
126
|
+
if (meta.options.finally) await meta.options.finally(helper, { args, result, error });
|
|
115
127
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
128
|
+
if (error) console.error('[SWC] setInterval tick error:', error);
|
|
129
|
+
})().catch(e => console.error('[SWC] setInterval tick error:', e));
|
|
119
130
|
}, meta.interval);
|
|
120
131
|
meta.options.created?.(helperHostSet, id);
|
|
121
132
|
getActiveEntries(inst).push(id);
|
|
@@ -124,13 +135,13 @@ export class SetIntervalLifeCycler implements ElementDefineLifeCycler {
|
|
|
124
135
|
|
|
125
136
|
onDisconnected(helperHostSet: HelperHostSet): void {
|
|
126
137
|
const inst = helperHostSet.$this;
|
|
127
|
-
for (const id of
|
|
138
|
+
for (const id of inst.__swc_intervalIds ?? []) {
|
|
128
139
|
try {
|
|
129
140
|
helperHostSet.$w.clearInterval(id);
|
|
130
141
|
} catch (e) {
|
|
131
142
|
console.error('[SWC] clearInterval error:', e);
|
|
132
143
|
}
|
|
133
144
|
}
|
|
134
|
-
|
|
145
|
+
inst.__swc_intervalIds = [];
|
|
135
146
|
}
|
|
136
147
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
-
import { ensureInit
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
3
|
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { buildSwcParameterArgs } from './parameter';
|
|
4
5
|
import { ElementDefineLifeCycler, HelperHostSet } from '../types';
|
|
5
6
|
|
|
6
7
|
export const SET_TIMEOUT_METADATA_KEY = Symbol.for('simple-web-component:set-timeout');
|
|
@@ -18,6 +19,12 @@ export interface SetTimeoutOptions {
|
|
|
18
19
|
parameter?: (set: HelperHostSet) => any[];
|
|
19
20
|
/** 타이머 등록 직후 1회 호출 (디버깅/로깅용). id는 실제 setTimeout 반환값 - cleanup은 자동으로 처리되므로 직접 clear할 필요 없음. */
|
|
20
21
|
created?: (set: HelperHostSet, id: number) => void;
|
|
22
|
+
/** [type:'onConnected' 전용] 발화 여부 게이트. Promise<boolean>도 가능. false면 스킵. */
|
|
23
|
+
filter?: (set: HelperHostSet) => boolean | Promise<boolean>;
|
|
24
|
+
/** [type:'onConnected' 전용] 핸들러 직전 훅. await되고, 리턴값은 @setTimeoutBeforeReturn 으로 주입된다. */
|
|
25
|
+
before?: (set: HelperHostSet) => any | Promise<any>;
|
|
26
|
+
/** [type:'onConnected' 전용] 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 인자/결과/에러. */
|
|
27
|
+
finally?: (set: HelperHostSet, ctx: { args: any[]; result?: any; error?: any }) => any | Promise<any>;
|
|
21
28
|
/**
|
|
22
29
|
* type:'onConnected' - fire 시 데코레이트된 메서드의 리턴값이 객체이고 이 키의 값이 함수이면 (id: number)로 호출한다.
|
|
23
30
|
* type:'returnValue' - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을 "실제로 실행될 함수"로 사용한다.
|
|
@@ -33,15 +40,7 @@ export interface SetTimeoutMetadata {
|
|
|
33
40
|
options: SetTimeoutOptions;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
|
-
const
|
|
37
|
-
const getActiveEntries = (inst: any): number[] => {
|
|
38
|
-
let entries = activeMap.get(inst);
|
|
39
|
-
if (!entries) {
|
|
40
|
-
entries = [];
|
|
41
|
-
activeMap.set(inst, entries);
|
|
42
|
-
}
|
|
43
|
-
return entries;
|
|
44
|
-
};
|
|
43
|
+
const getActiveEntries = (inst: any): number[] => (inst.__swc_timeoutIds ??= []);
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* type:'onConnected' - connect 시 자동으로 setTimeout을 걸고, disconnect 시 (아직 실행 전이라면) 자동으로 clearTimeout 한다.
|
|
@@ -76,8 +75,7 @@ export function setTimeout(delay: number, options: SetTimeoutOptions = {}): Meth
|
|
|
76
75
|
|
|
77
76
|
if (tickFn) {
|
|
78
77
|
ensureInit(this);
|
|
79
|
-
const
|
|
80
|
-
const win = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
|
|
78
|
+
const win = SwcUtils.resolveWindow(this);
|
|
81
79
|
const id = win.setTimeout(() => {
|
|
82
80
|
const entries = getActiveEntries(this);
|
|
83
81
|
const idx = entries.indexOf(id);
|
|
@@ -88,7 +86,7 @@ export function setTimeout(delay: number, options: SetTimeoutOptions = {}): Meth
|
|
|
88
86
|
console.error('[SWC] setTimeout fire error:', e);
|
|
89
87
|
}
|
|
90
88
|
}, delay);
|
|
91
|
-
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(
|
|
89
|
+
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(this), id);
|
|
92
90
|
getActiveEntries(this).push(id);
|
|
93
91
|
}
|
|
94
92
|
return res;
|
|
@@ -98,27 +96,40 @@ export function setTimeout(delay: number, options: SetTimeoutOptions = {}): Meth
|
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
export const findAllSetTimeoutMetadata = (target: any): SetTimeoutMetadata[] => {
|
|
101
|
-
const constructor = target
|
|
99
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
102
100
|
return (ReflectUtils.findAllMetadata<SetTimeoutMetadata[]>(SET_TIMEOUT_METADATA_KEY, constructor) || []).flat();
|
|
103
101
|
};
|
|
104
102
|
|
|
105
103
|
export class SetTimeoutLifeCycler implements ElementDefineLifeCycler {
|
|
106
104
|
onConnected(helperHostSet: HelperHostSet): void {
|
|
107
105
|
const inst = helperHostSet.$this;
|
|
108
|
-
|
|
106
|
+
inst.__swc_timeoutIds = []; // 재연결 대비 리셋
|
|
109
107
|
for (const meta of findAllSetTimeoutMetadata(inst)) {
|
|
110
108
|
if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
|
|
111
109
|
const id = helperHostSet.$w.setTimeout(() => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
void (async () => {
|
|
111
|
+
const helper = SwcUtils.getHelperAndHostSet(inst);
|
|
112
|
+
if (meta.options.filter && !(await meta.options.filter(helper))) return;
|
|
113
|
+
const paramArgs = meta.options.parameter?.(helperHostSet) ?? [];
|
|
114
|
+
const hostSet = SwcUtils.getHostSet(inst);
|
|
115
|
+
const helperSet = SwcUtils.getHelperSet(helperHostSet.$w);
|
|
116
|
+
const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, meta.propertyKey, {
|
|
117
|
+
hostSet, helperHostSet: helper, helperSet, setTimeoutBeforeReturn: beforeReturn
|
|
118
|
+
}, [...paramArgs, beforeReturn]);
|
|
119
|
+
let args = buildArgs(undefined);
|
|
120
|
+
if (meta.options.before) { const br = await meta.options.before(helper); args = buildArgs(br); }
|
|
121
|
+
let result: any, error: any;
|
|
122
|
+
try {
|
|
123
|
+
result = await inst[meta.propertyKey](...args);
|
|
124
|
+
const vk = meta.options.valueKey ?? SET_TIMEOUT_METADATA_KEY;
|
|
125
|
+
if (result && typeof result === 'object' && typeof result[vk] === 'function') result[vk](id);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
error = e;
|
|
128
|
+
} finally {
|
|
129
|
+
if (meta.options.finally) await meta.options.finally(helper, { args, result, error });
|
|
118
130
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
131
|
+
if (error) console.error('[SWC] setTimeout fire error:', error);
|
|
132
|
+
})().catch(e => console.error('[SWC] setTimeout fire error:', e));
|
|
122
133
|
}, meta.delay);
|
|
123
134
|
meta.options.created?.(helperHostSet, id);
|
|
124
135
|
getActiveEntries(inst).push(id);
|
|
@@ -127,13 +138,13 @@ export class SetTimeoutLifeCycler implements ElementDefineLifeCycler {
|
|
|
127
138
|
|
|
128
139
|
onDisconnected(helperHostSet: HelperHostSet): void {
|
|
129
140
|
const inst = helperHostSet.$this;
|
|
130
|
-
for (const id of
|
|
141
|
+
for (const id of inst.__swc_timeoutIds ?? []) {
|
|
131
142
|
try {
|
|
132
143
|
helperHostSet.$w.clearTimeout(id);
|
|
133
144
|
} catch (e) {
|
|
134
145
|
console.error('[SWC] clearTimeout error:', e);
|
|
135
146
|
}
|
|
136
147
|
}
|
|
137
|
-
|
|
148
|
+
inst.__swc_timeoutIds = [];
|
|
138
149
|
}
|
|
139
150
|
}
|
package/src/decorators/state.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {ReflectUtils} from '@dooboostore/core';
|
|
2
2
|
import {ElementApply} from "@dooboostore/core-web";
|
|
3
3
|
import {SwcUtils} from "../utils/Utils";
|
|
4
|
-
import {getElementConfig} from "./elementDefine";
|
|
5
4
|
|
|
6
5
|
export interface StateMetadata {
|
|
7
6
|
propertyKey: string | symbol;
|
|
@@ -15,23 +14,22 @@ export const STATE_METADATA_KEY = Symbol.for('simple-web-component:state');
|
|
|
15
14
|
export function state(target: Object, propertyKey: string | symbol): void;
|
|
16
15
|
export function state(name: string): PropertyDecorator;
|
|
17
16
|
/**
|
|
18
|
-
*
|
|
17
|
+
* Forms (빈 괄호 @state() 는 타입 에러):
|
|
19
18
|
* - @state
|
|
20
|
-
* - @state()
|
|
21
19
|
* - @state('selector')
|
|
22
20
|
*/
|
|
23
21
|
export function state(nameOrTarget?: string | Object, propertyKey?: string | symbol): any {
|
|
24
22
|
|
|
25
23
|
const stateDefine = (targetObj: any, stateMetadata: StateMetadata) => {
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
//
|
|
25
|
+
// around.ts와 동일: 클로저 변수에 두면 모든 인스턴스가 공유해버리므로
|
|
26
|
+
// 인스턴스 전용 Symbol 슬롯에 실제 값을 둔다.
|
|
27
|
+
const storageKey = Symbol(`state:${String(stateMetadata.propertyKey)}`);
|
|
29
28
|
Object.defineProperty(targetObj, stateMetadata.propertyKey, {
|
|
30
29
|
set(this: any, nv: string) {
|
|
31
|
-
|
|
32
|
-
const config = getElementConfig(this)
|
|
30
|
+
this[storageKey] = nv;
|
|
33
31
|
const ea = new ElementApply(this, {id: this._swcId});
|
|
34
|
-
const helperHostSet = SwcUtils.getHelperAndHostSet(
|
|
32
|
+
const helperHostSet = SwcUtils.getHelperAndHostSet(this);
|
|
35
33
|
const stateContext: any = {...helperHostSet};
|
|
36
34
|
findAllStateMetadata(this).forEach(it => {
|
|
37
35
|
stateContext[it.name] = this[it.propertyKey]
|
|
@@ -39,7 +37,15 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
|
|
|
39
37
|
ea.apply({context:stateContext, targetVariableName: stateMetadata.name, bind: this});
|
|
40
38
|
},
|
|
41
39
|
get(this: any) {
|
|
42
|
-
|
|
40
|
+
// 안전망: 필드 이니셜라이저의 own property가 남아있다면(ensureInit이 안 돈
|
|
41
|
+
// 일반 클래스 등) 슬롯으로 회수한다. setter 경유는 안 함 — setter의 DOM
|
|
42
|
+
// 동기화는 초기화 전에 돌면 안 되므로 직접 슬롯에 둔다.
|
|
43
|
+
const own = Object.getOwnPropertyDescriptor(this, stateMetadata.propertyKey);
|
|
44
|
+
if (own && 'value' in own) {
|
|
45
|
+
delete this[stateMetadata.propertyKey];
|
|
46
|
+
this[storageKey] = own.value;
|
|
47
|
+
}
|
|
48
|
+
return this[storageKey];
|
|
43
49
|
},
|
|
44
50
|
enumerable: true,
|
|
45
51
|
configurable: true
|
|
@@ -51,7 +57,7 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
|
|
|
51
57
|
const targetObj = nameOrTarget as Object;
|
|
52
58
|
const name = String(propertyKey);
|
|
53
59
|
const constructor = (targetObj as any).constructor;
|
|
54
|
-
let states = ReflectUtils.
|
|
60
|
+
let states = ReflectUtils.getOwnMetadata(STATE_METADATA_KEY, constructor) as StateMetadata[] | undefined;
|
|
55
61
|
if (!states) {
|
|
56
62
|
states = [];
|
|
57
63
|
ReflectUtils.defineMetadata(STATE_METADATA_KEY, states, constructor);
|
|
@@ -66,7 +72,7 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
|
|
|
66
72
|
return (targetObj: Object, propertyKey: string | symbol): void => {
|
|
67
73
|
const name = String(nameOrTarget);
|
|
68
74
|
const constructor = targetObj.constructor;
|
|
69
|
-
let states = ReflectUtils.
|
|
75
|
+
let states = ReflectUtils.getOwnMetadata(STATE_METADATA_KEY, constructor) as StateMetadata[] | undefined;
|
|
70
76
|
if (!states) {
|
|
71
77
|
states = [];
|
|
72
78
|
ReflectUtils.defineMetadata(STATE_METADATA_KEY, states, constructor);
|
|
@@ -78,6 +84,6 @@ export function state(nameOrTarget?: string | Object, propertyKey?: string | sym
|
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
export const findAllStateMetadata = (target: any): StateMetadata[] => {
|
|
81
|
-
const constructor = target
|
|
82
|
-
return ReflectUtils.
|
|
87
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
88
|
+
return ReflectUtils.findAllMetadata<any[]>(STATE_METADATA_KEY, constructor).flat();
|
|
83
89
|
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { SwcAppMessage, SwcAppMessageSubject, SpecialSelector, SwcQueryOptions, HelperHostSet } from '../types';
|
|
3
|
+
|
|
4
|
+
export const SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-message');
|
|
5
|
+
|
|
6
|
+
export interface SwcAppMessageOptions<Data = any, BeforeReturn = any, Result = any> {
|
|
7
|
+
/** 메시지 수신 시 핸들러 실행 여부 게이트. Promise<boolean>도 되어 async 판정 가능. false면 스킵. */
|
|
8
|
+
filter?: (message: SwcAppMessage<Data>, currentThis: any) => boolean | Promise<boolean>;
|
|
9
|
+
/** filter 통과 후 핸들러 직전 훅. await되고, 리턴값은 @appMessageBeforeReturn 으로 핸들러에 주입된다. */
|
|
10
|
+
before?: (message: SwcAppMessage<Data>, currentThis: any) => BeforeReturn | Promise<BeforeReturn>;
|
|
11
|
+
/** 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 핸들러 인자/결과/에러를 받는다. 에러는 삼키지 않고 전파. */
|
|
12
|
+
finally?: (message: SwcAppMessage<Data>, currentThis: any, ctx: { args: any[]; result?: Result; error?: any }) => any | Promise<any>;
|
|
13
|
+
/**
|
|
14
|
+
* 늦게 연결된 경우 과거 메시지 재생 방식. 이미 연결된 구독자에게는 항상 live로 쏨.
|
|
15
|
+
* - 미지정/'subject': live만. 지나간 건 못 받음 (기본값, 기존 동작)
|
|
16
|
+
* - 'behavior': 마지막 1개 재생
|
|
17
|
+
* - 'replay': 버퍼 전체 시간순 재생
|
|
18
|
+
*/
|
|
19
|
+
subject?: SwcAppMessageSubject;
|
|
20
|
+
/**
|
|
21
|
+
* 재생 시점. 기본 'connected' (연결 즉시).
|
|
22
|
+
* - 'connectedDone': 엘리먼트 render 완료 후 (_connectedDone). @innerHtml 등
|
|
23
|
+
* DOM 타겟이 필요한 핸들러는 이걸로 (replay가 render보다 먼저 와서 증발하는 레이스 방지).
|
|
24
|
+
*/
|
|
25
|
+
trigger?: 'connected' | 'connectedDone';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SwcAppMessageSubscriberMetadata extends SwcAppMessageOptions {
|
|
29
|
+
propertyKey: string | symbol;
|
|
30
|
+
messageType?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createSubscribeSwcAppMessage(options?: SwcAppMessageOptions, messageType?: string): MethodDecorator {
|
|
34
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
35
|
+
const constructor = target.constructor;
|
|
36
|
+
let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY, constructor) as SwcAppMessageSubscriberMetadata[];
|
|
37
|
+
if (!list) {
|
|
38
|
+
list = [];
|
|
39
|
+
ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY, list, constructor);
|
|
40
|
+
}
|
|
41
|
+
list.push({ propertyKey, messageType, filter: options?.filter, before: options?.before, finally: options?.finally, subject: options?.subject, trigger: options?.trigger });
|
|
42
|
+
|
|
43
|
+
return descriptor;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 오버로드 시그니처 - 직접 사용 (괄호 없음)
|
|
48
|
+
export function subscribeSwcAppMessage(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
49
|
+
// 오버로드 시그니처 - 함수 호출 (괄호 있음) - 메시지 타입만
|
|
50
|
+
export function subscribeSwcAppMessage(messageType: string): MethodDecorator;
|
|
51
|
+
// 오버로드 시그니처 - 함수 호출 (괄호 있음) - 옵션만
|
|
52
|
+
export function subscribeSwcAppMessage<Data = any, BeforeReturn = any, Result = any>(options: SwcAppMessageOptions<NoInfer<Data>, NoInfer<BeforeReturn>, NoInfer<Result>>): MethodDecorator;
|
|
53
|
+
// 오버로드 시그니처 - 함수 호출 (괄호 있음) - 메시지 타입 + 옵션
|
|
54
|
+
export function subscribeSwcAppMessage<Data = any, BeforeReturn = any, Result = any>(messageType: string, options: SwcAppMessageOptions<NoInfer<Data>, NoInfer<BeforeReturn>, NoInfer<Result>>): MethodDecorator;
|
|
55
|
+
|
|
56
|
+
export function subscribeSwcAppMessage(targetOrMessageTypeOrOptions?: any, optionsOrPropertyKey?: any, descriptor?: PropertyDescriptor): any {
|
|
57
|
+
// 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppMessage
|
|
58
|
+
if (targetOrMessageTypeOrOptions && typeof targetOrMessageTypeOrOptions === 'object' &&
|
|
59
|
+
optionsOrPropertyKey && (typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') &&
|
|
60
|
+
descriptor) {
|
|
61
|
+
const target = targetOrMessageTypeOrOptions;
|
|
62
|
+
const propertyKey = optionsOrPropertyKey;
|
|
63
|
+
return createSubscribeSwcAppMessage({})(target, propertyKey, descriptor);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 함수로 호출된 경우 (괄호 있음)
|
|
67
|
+
let messageType: string | undefined;
|
|
68
|
+
let options: SwcAppMessageOptions = {};
|
|
69
|
+
|
|
70
|
+
if (typeof targetOrMessageTypeOrOptions === 'string') {
|
|
71
|
+
// 메시지 타입 전달: @subscribeSwcAppMessage('messageType') 또는 @subscribeSwcAppMessage('messageType', options)
|
|
72
|
+
messageType = targetOrMessageTypeOrOptions;
|
|
73
|
+
if (optionsOrPropertyKey && typeof optionsOrPropertyKey === 'object') {
|
|
74
|
+
options = optionsOrPropertyKey;
|
|
75
|
+
}
|
|
76
|
+
} else if (targetOrMessageTypeOrOptions && typeof targetOrMessageTypeOrOptions === 'object') {
|
|
77
|
+
// 옵션만 전달: @subscribeSwcAppMessage({ filter: ... })
|
|
78
|
+
options = targetOrMessageTypeOrOptions;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return createSubscribeSwcAppMessage(options, messageType);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const receiveMessage = subscribeSwcAppMessage;
|
|
85
|
+
// Helper function to retrieve message subscribers metadata
|
|
86
|
+
export const getSubscribeSwcAppMessageMetadata = (target: any): SwcAppMessageSubscriberMetadata[] | undefined => {
|
|
87
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
88
|
+
return ReflectUtils.findAllMetadata<any[]>(SUBSCRIBE_SWC_APP_MESSAGE_METADATA_KEY, constructor).flat();
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
// MessageSubscribeLifeCycler
|
|
93
|
+
// NOTE: 실제 구독 로직은 SwcAppMixin._connected 에서 처리된다.
|
|
94
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
95
|
+
import { ElementDefineLifeCycler } from '../types';
|
|
96
|
+
|
|
97
|
+
export class MessageSubscribeLifeCycler implements ElementDefineLifeCycler {
|
|
98
|
+
onConnected(_helperHostSet: HelperHostSet): void {
|
|
99
|
+
// 실제 구독은 SwcAppMixin._connected 에서 처리
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** SwcAppMixin 이 구독 대상 메타를 꺼낼 때 사용 */
|
|
103
|
+
getMetadata(helperHostSet: HelperHostSet): SwcAppMessageSubscriberMetadata[] | undefined {
|
|
104
|
+
return getSubscribeSwcAppMessageMetadata(helperHostSet.$this);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {ReflectUtils} from '@dooboostore/core';
|
|
2
|
+
import {Router} from '@dooboostore/core-web';
|
|
3
|
+
import {HelperHostSet} from '../types';
|
|
4
|
+
|
|
5
|
+
export const SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-route-change');
|
|
6
|
+
|
|
7
|
+
export type RoutePathType = string | string[] | ((currentThis: any) => string | string[]);
|
|
8
|
+
|
|
9
|
+
export interface SwcAppRouteChangeSubscriberMetadata {
|
|
10
|
+
propertyKey: string | symbol;
|
|
11
|
+
options?: SwcAppRouteChangeOptions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SwcAppRouteChangeOptions<BeforeReturn = any, Result = any> {
|
|
15
|
+
path?: RoutePathType;
|
|
16
|
+
/** 라우트 변경 시 핸들러 실행 여부 게이트. Promise<boolean>도 되어 async 가드(인증 체크 등) 가능. false면 스킵. */
|
|
17
|
+
filter?: (router: Router, meta: { currentThis: any; helper: HelperHostSet }) => boolean | Promise<boolean>;
|
|
18
|
+
/** filter 통과 후 핸들러 직전 훅. await되고, 리턴값은 @routeChangeBeforeReturn 으로 핸들러에 주입된다. */
|
|
19
|
+
before?: (router: Router, meta: { currentThis: any; helper: HelperHostSet }) => BeforeReturn | Promise<BeforeReturn>;
|
|
20
|
+
/** 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 핸들러 인자/결과/에러를 받는다. 에러는 삼키지 않고 전파. */
|
|
21
|
+
finally?: (router: Router, meta: { currentThis: any; helper: HelperHostSet }, ctx: { args: any[]; result?: Result; error?: any }) => any | Promise<any>;
|
|
22
|
+
order?: number;
|
|
23
|
+
valueKey?: symbol | string;
|
|
24
|
+
/**
|
|
25
|
+
* 늦게 붙은 인스턴스에 마지막 라우트 재생 시점. 기본 'connected'.
|
|
26
|
+
* - 'connectedDone': render 완료 후 (_connectedDone). DOM 타겟 필요한 핸들러용.
|
|
27
|
+
* - live 라우트 변경(전체 방송) 때는 항상 실행. replay에만 적용.
|
|
28
|
+
*/
|
|
29
|
+
trigger?: 'connected' | 'connectedDone';
|
|
30
|
+
/**
|
|
31
|
+
* 언제 실행할지. 기본 'match'(경로가 맞을 때마다 — 기존 동작).
|
|
32
|
+
* - 'enter': 안 맞다가 맞게 됐을 때 (처음 붙을 때의 재생 포함)
|
|
33
|
+
* - 'update': 계속 맞는데 경로나 query 가 바뀌었을 때
|
|
34
|
+
* - 'leave': 맞다가 안 맞게 됐을 때 (요소가 붙어 있을 때만 — 교체되는 페이지는 onDisconnected 가 담당)
|
|
35
|
+
* - 'beforeLeave': 이 경로를 떠나기 전. false(또는 Promise<false>)를 리턴하면 이동 취소 (router.go / 뒤로·앞으로 가기)
|
|
36
|
+
*/
|
|
37
|
+
on?: 'match' | 'enter' | 'update' | 'leave' | 'beforeLeave';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createSubscribeSwcAppRouteChange(options?: SwcAppRouteChangeOptions): MethodDecorator {
|
|
41
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
42
|
+
const pathPattern = options?.path;
|
|
43
|
+
const filter = options?.filter;
|
|
44
|
+
|
|
45
|
+
const constructor = target.constructor;
|
|
46
|
+
let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, constructor) as SwcAppRouteChangeSubscriberMetadata[];
|
|
47
|
+
if (!list) {
|
|
48
|
+
list = [];
|
|
49
|
+
ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, list, constructor);
|
|
50
|
+
}
|
|
51
|
+
list.push({ propertyKey, options });
|
|
52
|
+
|
|
53
|
+
return descriptor;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 오버로드 시그니처 - 직접 사용 (괄호 없음)
|
|
58
|
+
export function subscribeSwcAppRouteChange(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
59
|
+
// 오버로드 시그니처 - 함수 호출 (괄호 있음)
|
|
60
|
+
export function subscribeSwcAppRouteChange(pathPattern: RoutePathType): MethodDecorator;
|
|
61
|
+
export function subscribeSwcAppRouteChange<BeforeReturn = any, Result = any>(pathPattern: RoutePathType, config: Omit<SwcAppRouteChangeOptions<NoInfer<BeforeReturn>, NoInfer<Result>>, 'path'>): MethodDecorator;
|
|
62
|
+
export function subscribeSwcAppRouteChange<BeforeReturn = any, Result = any>(options: SwcAppRouteChangeOptions<NoInfer<BeforeReturn>, NoInfer<Result>>): MethodDecorator;
|
|
63
|
+
|
|
64
|
+
// 실제 구현 (이중 모드)
|
|
65
|
+
export function subscribeSwcAppRouteChange(targetOrOptions?: any, propertyKeyOrConfig?: any, descriptor?: PropertyDescriptor): any {
|
|
66
|
+
// 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppRouteChange
|
|
67
|
+
// propertyKey가 string | symbol이고 descriptor가 있으면 직접 사용
|
|
68
|
+
if (propertyKeyOrConfig && (typeof propertyKeyOrConfig === 'string' || typeof propertyKeyOrConfig === 'symbol')) {
|
|
69
|
+
return createSubscribeSwcAppRouteChange({})(targetOrOptions, propertyKeyOrConfig, descriptor);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 함수로 호출된 경우 (인자 있음): @subscribeSwcAppRouteChange('/path')
|
|
73
|
+
let options: SwcAppRouteChangeOptions = {};
|
|
74
|
+
|
|
75
|
+
if (Array.isArray(targetOrOptions)) {
|
|
76
|
+
// 배열: @subscribeSwcAppRouteChange(['/path1', '/path2'])
|
|
77
|
+
// 또는: @subscribeSwcAppRouteChange(['/path1', '/path2'], { order: 1 })
|
|
78
|
+
options.path = targetOrOptions;
|
|
79
|
+
if (propertyKeyOrConfig && typeof propertyKeyOrConfig === 'object') {
|
|
80
|
+
options = { ...options, ...propertyKeyOrConfig };
|
|
81
|
+
}
|
|
82
|
+
} else if (typeof targetOrOptions === 'string' || typeof targetOrOptions === 'function') {
|
|
83
|
+
// 문자열 또는 함수: @subscribeSwcAppRouteChange('/path')
|
|
84
|
+
// 또는: @subscribeSwcAppRouteChange('/path', { order: 1 })
|
|
85
|
+
options.path = targetOrOptions;
|
|
86
|
+
if (propertyKeyOrConfig && typeof propertyKeyOrConfig === 'object') {
|
|
87
|
+
options = { ...options, ...propertyKeyOrConfig };
|
|
88
|
+
}
|
|
89
|
+
} else if (targetOrOptions && typeof targetOrOptions === 'object') {
|
|
90
|
+
// 객체: @subscribeSwcAppRouteChange({ path: '/path', filter: ..., order: 1 })
|
|
91
|
+
options = targetOrOptions;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return createSubscribeSwcAppRouteChange(options);
|
|
95
|
+
}
|
|
96
|
+
export const changedRoute = subscribeSwcAppRouteChange;
|
|
97
|
+
|
|
98
|
+
// 늦게 붙은 인스턴스도 렌더 완료 후 마지막 라우트를 재생 — DOM 타겟 핸들러용.
|
|
99
|
+
// @subscribeSwcAppRouteChange(path, { trigger: 'connectedDone' }) 의 단축형.
|
|
100
|
+
export function subscribeSwcAppRouteChangeConnectedDone<BeforeReturn = any, Result = any>(pathPattern: RoutePathType, config?: Omit<SwcAppRouteChangeOptions<NoInfer<BeforeReturn>, NoInfer<Result>>, 'path' | 'trigger'>): MethodDecorator {
|
|
101
|
+
return subscribeSwcAppRouteChange(pathPattern, { ...config, trigger: 'connectedDone' });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Helper function to retrieve route change subscribers metadata
|
|
105
|
+
export const getSubscribeSwcAppRouteChangeMetadata = (target: any): SwcAppRouteChangeSubscriberMetadata[] => {
|
|
106
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
107
|
+
const results = ReflectUtils.findAllMetadata<any[]>(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, constructor).flat();
|
|
108
|
+
if (!results) return [];
|
|
109
|
+
|
|
110
|
+
// Sort by order (default 0 if not specified)
|
|
111
|
+
return [...results].sort((a, b) => (a.options?.order ?? 0) - (b.options?.order ?? 0));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
+
// RouteSubscribeLifeCycler
|
|
116
|
+
// NOTE: 실제 구독 로직은 SwcAppMixin._connected 에서 처리된다.
|
|
117
|
+
// 이 cycler 는 메타데이터 접근 진입점 역할만 하며,
|
|
118
|
+
// 향후 SwcAppMixin 에서 로직을 분리할 때 이 클래스로 이전한다.
|
|
119
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
import { ElementDefineLifeCycler } from '../types';
|
|
121
|
+
|
|
122
|
+
export class RouteSubscribeLifeCycler implements ElementDefineLifeCycler {
|
|
123
|
+
onConnected(_helperHostSet: HelperHostSet): void {
|
|
124
|
+
// 실제 구독은 SwcAppMixin._connected 에서 처리
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** SwcAppMixin 이 구독 대상 메타를 꺼낼 때 사용 */
|
|
128
|
+
getMetadata(helperHostSet: HelperHostSet): SwcAppRouteChangeSubscriberMetadata[] {
|
|
129
|
+
return getSubscribeSwcAppRouteChangeMetadata(helperHostSet.$this);
|
|
130
|
+
}
|
|
131
|
+
}
|