@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
|
@@ -55,7 +55,7 @@ export const resolvePropertyTargets = (inst: any, selector: PropertySelector, op
|
|
|
55
55
|
// Resolve selector if it's a function
|
|
56
56
|
let resolvedSelector: string | Node | Element | NodeList | Element[] | null = selector as any;
|
|
57
57
|
if (typeof selector === 'function') {
|
|
58
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
58
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
59
59
|
resolvedSelector = selector(inst, hostSet);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -182,9 +182,7 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
182
182
|
if (resolvedValue === undefined) return resolvedValue;
|
|
183
183
|
|
|
184
184
|
const targetEls = resolvePropertyTargets(inst, selector, finalOptions);
|
|
185
|
-
const
|
|
186
|
-
const currentWin = conf.window;
|
|
187
|
-
const hostSet = SwcUtils.getHelperAndHostSet(currentWin, inst);
|
|
185
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
188
186
|
|
|
189
187
|
targetEls.forEach(targetEl => {
|
|
190
188
|
// Apply filter if provided
|
|
@@ -219,7 +217,7 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
219
217
|
* use that value. Otherwise use the entire return value.
|
|
220
218
|
*
|
|
221
219
|
* Example:
|
|
222
|
-
* @
|
|
220
|
+
* @property('selector')
|
|
223
221
|
* myMethod() {
|
|
224
222
|
* return {
|
|
225
223
|
* [PROPERTY_METADATA_KEY]: 'property-value',
|
|
@@ -251,7 +249,13 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
251
249
|
return;
|
|
252
250
|
}
|
|
253
251
|
|
|
254
|
-
// Property decorator: define getter/setter to proxy to element properties
|
|
252
|
+
// Property decorator: define getter/setter to proxy to element properties.
|
|
253
|
+
// selector가 '$this' + 타겟 키가 자기 필드면 getter/setter 만들지 않고 리턴.
|
|
254
|
+
// (메타데이터는 위에서 이미 등록됨 → 하이드레이션 수집은 됨.)
|
|
255
|
+
// 순수 필드로 두는 게 업그레이드 때 값 유지에 안전 (setter 경로 타면 생성자·폴리필에 날아감).
|
|
256
|
+
if (selector === '$this' && targetPropertyKey === propertyKey) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
255
259
|
Object.defineProperty(target, propertyKey, {
|
|
256
260
|
configurable: true,
|
|
257
261
|
enumerable: true,
|
|
@@ -259,9 +263,7 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
259
263
|
ensureInit(this);
|
|
260
264
|
// Read from matched elements
|
|
261
265
|
const targetEls = resolvePropertyTargets(this, selector, finalOptions);
|
|
262
|
-
const
|
|
263
|
-
const currentWin = conf.window;
|
|
264
|
-
const hostSet = SwcUtils.getHelperAndHostSet(currentWin, this);
|
|
266
|
+
const hostSet = SwcUtils.getHelperAndHostSet(this);
|
|
265
267
|
|
|
266
268
|
// Apply filter
|
|
267
269
|
const filtered = targetEls.filter(el => {
|
|
@@ -292,76 +294,42 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
292
294
|
// ============================================
|
|
293
295
|
|
|
294
296
|
/**
|
|
295
|
-
* @property -
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* - @property
|
|
301
|
-
* - @property(
|
|
302
|
-
* - @property
|
|
303
|
-
*
|
|
304
|
-
*
|
|
297
|
+
* @property - 필드/메서드 공용. 붙은 곳에 따라 동작이 갈린다.
|
|
298
|
+
* - 필드: 대상 엘리먼트 프로퍼티를 읽고 쓰는 getter/setter ($this + 같은 이름이면 순수 필드로 두고 하이드레이션만 등록)
|
|
299
|
+
* - 메서드: 리턴값을 대상 엘리먼트 프로퍼티에 대입 (undefined 면 무시)
|
|
300
|
+
*
|
|
301
|
+
* 첫 문자열은 항상 셀렉터. 자기 자신의 프로퍼티는 필드 그 자체라 이름만 받는 형태는 의미가 없다.
|
|
302
|
+
* - @property - $this, 이름은 필드/메서드 이름
|
|
303
|
+
* - @property('selector') / @property('selector', options) - 대상의 같은 이름 프로퍼티
|
|
304
|
+
* - @property('selector', 'propertyName', options?) - 대상의 propertyName
|
|
305
|
+
* - @property((this, helper) => el, 'propertyName') - 함수 셀렉터
|
|
306
|
+
* - @property(options) - $this, 이름은 options.name 또는 필드/메서드 이름
|
|
307
|
+
*
|
|
308
|
+
* Example:
|
|
309
|
+
* @property('#chart', 'data') chartData: number[];
|
|
310
|
+
*
|
|
311
|
+
* @property('button', 'disabled')
|
|
312
|
+
* lock() { return true; }
|
|
305
313
|
*/
|
|
306
|
-
export function property(selector: string, targetPropertyKey: string | symbol, options?: PropertyQueryOptions): PropertyDecorator;
|
|
307
|
-
export function property(selector: PropertyFnSelector, targetPropertyKey: string | symbol, options?: PropertyNonQueryOptions): PropertyDecorator;
|
|
308
|
-
export function property(selector: string, options?: PropertyQueryOptions): PropertyDecorator;
|
|
309
|
-
export function property(selector: PropertyFnSelector, options?: PropertyNonQueryOptions): PropertyDecorator;
|
|
314
|
+
export function property(selector: string, targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator & PropertyDecorator;
|
|
315
|
+
export function property(selector: PropertyFnSelector, targetPropertyKey: string | symbol, options?: PropertyNonQueryOptions): MethodDecorator & PropertyDecorator;
|
|
316
|
+
export function property(selector: string, options?: PropertyQueryOptions): MethodDecorator & PropertyDecorator;
|
|
317
|
+
export function property(selector: PropertyFnSelector, options?: PropertyNonQueryOptions): MethodDecorator & PropertyDecorator;
|
|
318
|
+
export function property(options: PropertyOptions): MethodDecorator & PropertyDecorator;
|
|
319
|
+
// bare(@property) 시그니처는 맨 뒤 — 앞에 있으면 문자열 두 개짜리 호출이 (target, key) 로 먼저 잡혀 TS1240
|
|
310
320
|
export function property(target: Object, propertyKey: string | symbol): void;
|
|
311
321
|
export function property(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
312
|
-
export function property(selectorOrTarget?: PropertySelector | Object, targetPropertyKeyOrOptions?: any, optionsOrDescriptor?: any): any {
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
&& typeof selectorOrTarget !== 'string' && typeof selectorOrTarget !== 'function'
|
|
317
|
-
&& (typeof targetPropertyKeyOrOptions === 'string' || typeof targetPropertyKeyOrOptions === 'symbol')) {
|
|
318
|
-
return applyProperty('$this', undefined as any, {})(selectorOrTarget as Object, targetPropertyKeyOrOptions, optionsOrDescriptor as PropertyDescriptor);
|
|
319
|
-
}
|
|
320
|
-
// With selector
|
|
321
|
-
if (typeof selectorOrTarget === 'string') {
|
|
322
|
-
return applyProperty(selectorOrTarget, targetPropertyKeyOrOptions as any, optionsOrDescriptor as PropertyOptions);
|
|
323
|
-
}
|
|
324
|
-
// Function selector
|
|
325
|
-
if (typeof selectorOrTarget === 'function') {
|
|
326
|
-
return applyProperty(selectorOrTarget as PropertyFnSelector, targetPropertyKeyOrOptions as any, optionsOrDescriptor as PropertyOptions);
|
|
327
|
-
}
|
|
328
|
-
// Without selector (defaults to $this)
|
|
329
|
-
return applyProperty('$this', undefined as any, selectorOrTarget as PropertyOptions);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* @setProperty - Method decorator for setting element properties from method return value
|
|
334
|
-
*
|
|
335
|
-
* Usage:
|
|
336
|
-
* - @setProperty('selector', 'propertyName') - Set specific property
|
|
337
|
-
* - @setProperty('selector') - Use method name as property name
|
|
338
|
-
* - @setProperty('selector', options) - Set with options
|
|
339
|
-
* - @setProperty((this, helper) => 'selector', 'propertyName') - Function-based selector
|
|
340
|
-
* - @setProperty - Bare decorator (sets on $this with method name)
|
|
341
|
-
*
|
|
342
|
-
* Note: This decorator is for METHODS ONLY. For fields, use @property.
|
|
343
|
-
*
|
|
344
|
-
* Example:
|
|
345
|
-
* @setProperty('selector', 'disabled')
|
|
346
|
-
* disableElement() {
|
|
347
|
-
* return true;
|
|
348
|
-
* }
|
|
349
|
-
*/
|
|
350
|
-
export function setProperty(selector: string, targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
351
|
-
export function setProperty(selector: PropertyFnSelector, targetPropertyKey: string | symbol, options?: PropertyNonQueryOptions): MethodDecorator;
|
|
352
|
-
export function setProperty(selector: string, options?: PropertyQueryOptions): MethodDecorator;
|
|
353
|
-
export function setProperty(selector: PropertyFnSelector, options?: PropertyNonQueryOptions): MethodDecorator;
|
|
354
|
-
export function setProperty(selectorOrOptions?: PropertySelector | PropertyOptions, targetPropertyKeyOrOptions?: any, optionsOrUndefined?: PropertyOptions): MethodDecorator {
|
|
355
|
-
// With selector
|
|
356
|
-
if (typeof selectorOrOptions === 'string') {
|
|
357
|
-
return applyProperty(selectorOrOptions, targetPropertyKeyOrOptions as any, optionsOrUndefined as PropertyOptions) as MethodDecorator;
|
|
322
|
+
export function property(selectorOrTarget?: PropertySelector | PropertyOptions | Object, targetPropertyKeyOrOptions?: any, optionsOrDescriptor?: any): any {
|
|
323
|
+
// 셀렉터 (문자열 / 함수)
|
|
324
|
+
if (typeof selectorOrTarget === 'string' || typeof selectorOrTarget === 'function') {
|
|
325
|
+
return applyProperty(selectorOrTarget as PropertySelector, targetPropertyKeyOrOptions as any, optionsOrDescriptor as PropertyOptions);
|
|
358
326
|
}
|
|
359
|
-
//
|
|
360
|
-
if (typeof
|
|
361
|
-
return applyProperty(
|
|
327
|
+
// bare: @property — 데코레이터로 바로 호출됨 (target=prototype, key[, descriptor])
|
|
328
|
+
if (selectorOrTarget && (typeof targetPropertyKeyOrOptions === 'string' || typeof targetPropertyKeyOrOptions === 'symbol')) {
|
|
329
|
+
return applyProperty('$this', {})(selectorOrTarget as Object, targetPropertyKeyOrOptions, optionsOrDescriptor as PropertyDescriptor);
|
|
362
330
|
}
|
|
363
|
-
//
|
|
364
|
-
return applyProperty('$this',
|
|
331
|
+
// 옵션만: $this
|
|
332
|
+
return applyProperty('$this', selectorOrTarget as PropertyOptions);
|
|
365
333
|
}
|
|
366
334
|
|
|
367
335
|
/**
|
|
@@ -415,7 +383,7 @@ export function callPropertyAll(selector: string, targetMethodKey?: string | sym
|
|
|
415
383
|
// ============================================
|
|
416
384
|
|
|
417
385
|
export const findAllPropertyMetadata = (target: any): PropertyMetadata[] => {
|
|
418
|
-
const actualTarget = target
|
|
386
|
+
const actualTarget = typeof target === 'function' ? target : target.constructor;
|
|
419
387
|
return ReflectUtils.findAllMetadata<PropertyMetadata[]>(PROPERTY_METADATA_KEY, actualTarget).flat();
|
|
420
388
|
};
|
|
421
389
|
|
|
@@ -429,7 +397,7 @@ export const findAllPropertyApplyMetadata = (target: any): Map<string | symbol,
|
|
|
429
397
|
};
|
|
430
398
|
|
|
431
399
|
// ─── Aliases ───
|
|
432
|
-
//
|
|
400
|
+
// applyProperty 단축명
|
|
433
401
|
export const prop = applyProperty;
|
|
434
402
|
|
|
435
403
|
// ─── 편의 헬퍼 (selector/root 생략) ───
|
|
@@ -446,22 +414,17 @@ const dispatchPropConvenience = (
|
|
|
446
414
|
return applyProperty(selector, targetPropertyKeyOrTarget as any, optionsOrPropertyKey as PropertyQueryOptions);
|
|
447
415
|
};
|
|
448
416
|
|
|
449
|
-
export function
|
|
450
|
-
export function propThis(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
451
|
-
export function propThis(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
452
|
-
return dispatchPropConvenience('$this', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
453
|
-
}
|
|
454
|
-
export function propAppHost(targetPropertyKey?: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
417
|
+
export function propAppHost(targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
455
418
|
export function propAppHost(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
456
419
|
export function propAppHost(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
457
420
|
return dispatchPropConvenience('$appHost', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
458
421
|
}
|
|
459
|
-
export function propWindow(targetPropertyKey
|
|
422
|
+
export function propWindow(targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
460
423
|
export function propWindow(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
461
424
|
export function propWindow(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
462
425
|
return dispatchPropConvenience('$window', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
463
426
|
}
|
|
464
|
-
export function propDocument(targetPropertyKey
|
|
427
|
+
export function propDocument(targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
465
428
|
export function propDocument(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
466
429
|
export function propDocument(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
467
430
|
return dispatchPropConvenience('$document', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
@@ -476,25 +439,14 @@ export function propAll(selector: string, targetPropertyKey?: string | symbol, o
|
|
|
476
439
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'all'});
|
|
477
440
|
}
|
|
478
441
|
|
|
479
|
-
// ───
|
|
480
|
-
export function
|
|
481
|
-
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'light'});
|
|
482
|
-
}
|
|
483
|
-
export function setPropertyShadow(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator {
|
|
484
|
-
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'shadow'});
|
|
485
|
-
}
|
|
486
|
-
export function setPropertyAll(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator {
|
|
487
|
-
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'all'});
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// ─── property root 별칭 (필드 프록시) ───
|
|
491
|
-
export function propertyLight(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): PropertyDecorator {
|
|
442
|
+
// ─── property root 별칭 ───
|
|
443
|
+
export function propertyLight(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator & PropertyDecorator {
|
|
492
444
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'light'});
|
|
493
445
|
}
|
|
494
|
-
export function propertyShadow(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): PropertyDecorator {
|
|
446
|
+
export function propertyShadow(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator & PropertyDecorator {
|
|
495
447
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'shadow'});
|
|
496
448
|
}
|
|
497
|
-
export function propertyAll(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): PropertyDecorator {
|
|
449
|
+
export function propertyAll(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator & PropertyDecorator {
|
|
498
450
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'all'});
|
|
499
451
|
}
|
|
500
452
|
|
|
@@ -114,7 +114,7 @@ export function applySlot(targetId: string, opt: OptionalType<ApplySlotOptions,
|
|
|
114
114
|
const currentWin = conf.window;
|
|
115
115
|
const swcId = this._swcId;
|
|
116
116
|
const currentDoc = currentWin.document;
|
|
117
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
117
|
+
const hostSet = SwcUtils.getHelperAndHostSet(this);
|
|
118
118
|
const pos = options.position;
|
|
119
119
|
|
|
120
120
|
const resToNodes = (res: string | Node) => {
|
|
@@ -216,6 +216,6 @@ export function replaceChildrenTextSlot(targetId: string): MethodDecorator {
|
|
|
216
216
|
|
|
217
217
|
// export const findAllApplySlotMetadata = (target: any): Map<string | symbol, ApplySlotMetadata> => {
|
|
218
218
|
export const findAllApplySlotMetadata = (target: any): ApplySlotMetadata[] => {
|
|
219
|
-
const constructor = target
|
|
220
|
-
return ReflectUtils.
|
|
219
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
220
|
+
return ReflectUtils.findAllMetadata<any[]>(APPLY_SLOT_METADATA_KEY, constructor).flat();
|
|
221
221
|
};
|
|
@@ -39,7 +39,7 @@ export const STYLE_METADATA_KEY = Symbol.for('simple-web-component:style');
|
|
|
39
39
|
*/
|
|
40
40
|
const resolveSelector = (selector: StyleSelector, inst: any, win: Window): string | HTMLElement[] => {
|
|
41
41
|
if (typeof selector === 'function') {
|
|
42
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
42
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
43
43
|
const result = selector(inst, hostSet);
|
|
44
44
|
|
|
45
45
|
if (result instanceof win.Element) {
|
|
@@ -75,7 +75,7 @@ const getTargetElements = (selector: StyleSelector, inst: any, win: Window, root
|
|
|
75
75
|
const stringSelector = resolved as string;
|
|
76
76
|
const targetEls: HTMLElement[] = [];
|
|
77
77
|
const host = inst as unknown as HTMLElement;
|
|
78
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
78
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
79
79
|
|
|
80
80
|
const applyRoot = (t: any) => {
|
|
81
81
|
if (!t || !(t instanceof win.HTMLElement)) return;
|
|
@@ -139,7 +139,7 @@ function createStyleDecorator(action: StyleAction) {
|
|
|
139
139
|
if (resolvedValue !== undefined) {
|
|
140
140
|
const win = (this as any).ownerDocument?.defaultView || window;
|
|
141
141
|
const host = this as unknown as HTMLElement;
|
|
142
|
-
const hostSet = { ...SwcUtils.getHelperAndHostSet(
|
|
142
|
+
const hostSet = { ...SwcUtils.getHelperAndHostSet(host), $this: this };
|
|
143
143
|
const r = options.root || 'auto';
|
|
144
144
|
const targetEls = getTargetElements(selector, this, win, r);
|
|
145
145
|
|
|
@@ -183,9 +183,11 @@ function createStyleDecorator(action: StyleAction) {
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
if (res instanceof Promise) {
|
|
186
|
-
|
|
186
|
+
// 자기 몫(valueKey)만 적용하고 원래 반환값은 그대로 위로 넘긴다 — 위에 쌓인 다른 아웃풋도 자기 키를 찾게
|
|
187
|
+
return res.then((v: any) => { handleResult(extractValue(v)); return v; });
|
|
187
188
|
} else {
|
|
188
|
-
|
|
189
|
+
handleResult(extractValue(res));
|
|
190
|
+
return res;
|
|
189
191
|
}
|
|
190
192
|
};
|
|
191
193
|
return descriptor;
|
|
@@ -266,15 +268,7 @@ export const style = applyStyle;
|
|
|
266
268
|
|
|
267
269
|
// ─── 편의 헬퍼 (selector/root 생략) ───
|
|
268
270
|
|
|
269
|
-
export function
|
|
270
|
-
export function styleThis(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
271
|
-
export function styleThis(actionOrTarget?: StyleAction | Object, optionsOrPropertyKey?: StyleQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
272
|
-
if ((typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') && descriptor !== undefined) {
|
|
273
|
-
return (applyStyle('$this', undefined as any, {}) as any)(actionOrTarget, optionsOrPropertyKey, descriptor);
|
|
274
|
-
}
|
|
275
|
-
return applyStyle('$this', actionOrTarget as any, optionsOrPropertyKey as StyleQueryOptions);
|
|
276
|
-
}
|
|
277
|
-
export function styleAppHost(action?: StyleAction, options?: StyleQueryOptions): MethodDecorator;
|
|
271
|
+
export function styleAppHost(action: StyleAction, options?: StyleQueryOptions): MethodDecorator;
|
|
278
272
|
export function styleAppHost(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
279
273
|
export function styleAppHost(actionOrTarget?: StyleAction | Object, optionsOrPropertyKey?: StyleQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
280
274
|
if ((typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') && descriptor !== undefined) {
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { HelperHostSet } from '../types';
|
|
5
|
+
|
|
6
|
+
export const AROUND_METADATA_KEY = Symbol.for('simple-web-component:around');
|
|
7
|
+
|
|
8
|
+
export interface AroundMetadata {
|
|
9
|
+
propertyKey: string | symbol;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AroundPropertyOptions<Value = any> {
|
|
13
|
+
/**
|
|
14
|
+
* get할 때 (helperHostSet, 저장된 값)을 받아 실제로 읽는 쪽에 돌려줄 값을 리턴한다.
|
|
15
|
+
* undefined 리턴이면 저장값 그대로 통과. 안 주면 그대로 통과.
|
|
16
|
+
*/
|
|
17
|
+
get?: (helperHostSet: HelperHostSet, stored: Value) => Value | void;
|
|
18
|
+
/**
|
|
19
|
+
* set할 때 (helperHostSet, 대입되는 값)을 받아 실제로 저장할 값을 리턴한다.
|
|
20
|
+
* 안 주면 그대로 통과.
|
|
21
|
+
*/
|
|
22
|
+
set?: (helperHostSet: HelperHostSet, incoming: Value) => Value | void;
|
|
23
|
+
}
|
|
24
|
+
export interface AroundOptions<Args extends any[] = any[], Return = any> {
|
|
25
|
+
/**
|
|
26
|
+
* 원본 실행 전에, (helperHostSet, 호출자가 넘긴 인자 배열)을 받아
|
|
27
|
+
* 실제로 원본에 넘길 인자 배열을 리턴한다. undefined 리턴이면 인자 그대로 통과.
|
|
28
|
+
* 원본이 Promise를 리턴하면 before도 Promise를 리턴할 수 있다 —
|
|
29
|
+
* resolve된 값으로 원본이 호출된다 (이 경우 래퍼도 Promise 반환).
|
|
30
|
+
* 원본이 sync면 before도 sync여야 한다 (async를 달면 호출 측이 모르는 Promise가 새어나감).
|
|
31
|
+
* 안 주면 그대로 통과.
|
|
32
|
+
*
|
|
33
|
+
* 제네릭 생략 시(Return=any) 양쪽 다 허용이라 느슨하게 동작하고,
|
|
34
|
+
* `@around<[number], number>`처럼 명시하면 위 규칙이 검사된다.
|
|
35
|
+
*/
|
|
36
|
+
before?: (helperHostSet: HelperHostSet, args: Args) => Return extends Promise<any> ? Args | void | Promise<Args | void> : Args | void;
|
|
37
|
+
/**
|
|
38
|
+
* 원본 실행 후에, (helperHostSet, 리턴값)을 받아 실제로 호출자에게
|
|
39
|
+
* 돌려줄 값을 리턴한다.
|
|
40
|
+
* 원본이 Promise를 리턴하면 resolve된 값(Awaited<Return>)을 넘겨받고,
|
|
41
|
+
* after의 리턴값이 그대로 최종 Promise의 resolve 값이 된다.
|
|
42
|
+
* after가 Promise를 리턴해도 된다 — resolve된 값이 최종값이 된다.
|
|
43
|
+
* 안 주면 그대로 통과.
|
|
44
|
+
*
|
|
45
|
+
* 제네릭을 명시하면 훅 입출력이 검사된다:
|
|
46
|
+
* `@around<[string], Promise<string>>({ after: (h, r) => ... })`
|
|
47
|
+
* (r은 string으로 추론). 생략하면 any로 느슨하게 동작한다.
|
|
48
|
+
* 단, legacy 데코레이터 한계상 래핑된 메서드의 겉보기 시그니처는 안 바뀌므로
|
|
49
|
+
* async 훅을 단 sync 메서드는 호출 측에서 Promise로 취급해야 한다.
|
|
50
|
+
*/
|
|
51
|
+
after?: (helperHostSet: HelperHostSet, result: Awaited<Return>) => Return | Promise<Awaited<Return>>;
|
|
52
|
+
/**
|
|
53
|
+
* before/원본/after 실행이 끝나면 성공·실패와 무관하게 항상 실행되는 정리 훅 (try/finally의 finally).
|
|
54
|
+
* 두 번째 인자로 컨텍스트를 받는다:
|
|
55
|
+
* - args: 호출자가 넘긴 원본 인자 배열(before 가공 전)
|
|
56
|
+
* - result: 성공 시 최종 리턴값(after 적용 후). 실패 시 없음.
|
|
57
|
+
* - error: throw/reject 시 그 에러. 성공 시 없음.
|
|
58
|
+
* 에러를 삼키지 않는다 — finally를 먼저 돌린 뒤 원래 에러/리턴값이 그대로 전파된다.
|
|
59
|
+
* sync 원본 + sync finally면 sync로 유지되지만, finally가 Promise를 리턴하면
|
|
60
|
+
* 래퍼는 Promise를 반환한다(async before/after와 동일 규칙).
|
|
61
|
+
*/
|
|
62
|
+
finally?: (helperHostSet: HelperHostSet, ctx: { args: Args; result?: Awaited<Return>; error?: any }) => void | Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 인스턴스에서 HelperHostSet을 해석한다 — 실패(SSR/미초기화)하면 최소 폴백을 돌려준다. */
|
|
66
|
+
const resolveHelperHostSet = (inst: any): HelperHostSet => {
|
|
67
|
+
try {
|
|
68
|
+
ensureInit(inst);
|
|
69
|
+
return SwcUtils.getHelperAndHostSet(inst);
|
|
70
|
+
} catch {
|
|
71
|
+
return { $this: inst } as HelperHostSet;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 메서드에 걸면 호출을 감싸서 호출 전(인자 가공)/후(리턴값 가공) 훅을 건다 - AOP의 "around advice".
|
|
77
|
+
*
|
|
78
|
+
* @around({
|
|
79
|
+
* before: (h, args: [number, string]) => [args[0], args[1].toUpperCase()],
|
|
80
|
+
* after: (h, r: string) => r + '!'
|
|
81
|
+
* })
|
|
82
|
+
* greet(a: number, b: string) { return `${a}:${b}`; }
|
|
83
|
+
*
|
|
84
|
+
* 프로퍼티(필드)에 걸면 get/set을 가로챈다 - get은 get, set은 set이 호출된다.
|
|
85
|
+
* (프로퍼티 경로에서는 값이 배열이 아니라 단일값으로 넘어간다)
|
|
86
|
+
* 필드 이니셜라이저(`wow = 'x'`)가 있어도 된다 — ensureInit이 state처럼
|
|
87
|
+
* 초기값을 살려 setter 경유로 정리하므로 초기값에도 set()이 적용된다.
|
|
88
|
+
* (단, custom element가 아닌 일반 클래스에서는 ensureInit이 안 돌므로
|
|
89
|
+
* `declare` + 생성자 대입으로 쓸 것)
|
|
90
|
+
*
|
|
91
|
+
* @around({
|
|
92
|
+
* get: (h, stored: string) => stored?.toUpperCase(),
|
|
93
|
+
* set: (h, incoming: string) => incoming?.trim()
|
|
94
|
+
* })
|
|
95
|
+
* wow: string = ' hi ';
|
|
96
|
+
*/
|
|
97
|
+
export function around<Args extends any[] = any[], Return = any>(options: AroundOptions<NoInfer<Args>, NoInfer<Return>>): MethodDecorator;
|
|
98
|
+
export function around<Value = any>(options: AroundPropertyOptions<NoInfer<Value>>): PropertyDecorator;
|
|
99
|
+
export function around(options: any = {}): MethodDecorator & PropertyDecorator {
|
|
100
|
+
return ((target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
101
|
+
// 메서드: descriptor.value가 실제 함수일 때만 (이미 정의된 접근자를 감싸는 건 지원 범위 밖)
|
|
102
|
+
if (descriptor && typeof descriptor.value === 'function') {
|
|
103
|
+
const original = descriptor.value;
|
|
104
|
+
const runAfter = function (this: any, hhs: HelperHostSet, res: any) {
|
|
105
|
+
if (!options.after) return res;
|
|
106
|
+
const run = (v: any) => (options.after as any).call(this, hhs, v);
|
|
107
|
+
// after가 Promise를 리턴하면 resolve된 값이 최종값이 된다 (then 플래트닝).
|
|
108
|
+
return res && typeof res.then === 'function' ? res.then(run) : run(res);
|
|
109
|
+
};
|
|
110
|
+
descriptor.value = function (this: any, ...args: any[]) {
|
|
111
|
+
const hhs = resolveHelperHostSet(this);
|
|
112
|
+
const self = this;
|
|
113
|
+
const callOriginal = (finalArgs: any[]) => runAfter.call(self, hhs, original.apply(self, finalArgs));
|
|
114
|
+
const runBody = () => {
|
|
115
|
+
if (options.before) {
|
|
116
|
+
const mapped = (options.before as any).call(self, hhs, args);
|
|
117
|
+
if (mapped && typeof mapped.then === 'function') {
|
|
118
|
+
// async before — resolve된 인자로 원본 호출, 래퍼는 Promise 반환.
|
|
119
|
+
// (sync 원본 + sync 훅일 때만 sync 반환이 유지된다)
|
|
120
|
+
return mapped.then((m: any) => {
|
|
121
|
+
if (Array.isArray(m)) return callOriginal(m);
|
|
122
|
+
if (m !== undefined) return callOriginal([m]);
|
|
123
|
+
return callOriginal(args);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(mapped)) return callOriginal(mapped);
|
|
127
|
+
if (mapped !== undefined) return callOriginal([mapped]);
|
|
128
|
+
}
|
|
129
|
+
return callOriginal(args);
|
|
130
|
+
};
|
|
131
|
+
if (!options.finally) return runBody();
|
|
132
|
+
// sync/async 투명성을 지키는 try/finally — 에러는 삼키지 않고 finally 후 전파.
|
|
133
|
+
const afterFin = (settle: () => any, ctx: { args: any[]; result?: any; error?: any }) => {
|
|
134
|
+
const f = (options.finally as any).call(self, hhs, ctx);
|
|
135
|
+
return f && typeof f.then === 'function' ? f.then(settle) : settle();
|
|
136
|
+
};
|
|
137
|
+
let out: any;
|
|
138
|
+
try {
|
|
139
|
+
out = runBody();
|
|
140
|
+
} catch (e) {
|
|
141
|
+
return afterFin(() => { throw e; }, { args, error: e });
|
|
142
|
+
}
|
|
143
|
+
if (out && typeof out.then === 'function') {
|
|
144
|
+
return out.then(
|
|
145
|
+
(v: any) => afterFin(() => v, { args, result: v }),
|
|
146
|
+
(e: any) => afterFin(() => { throw e; }, { args, error: e })
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return afterFin(() => out, { args, result: out });
|
|
150
|
+
};
|
|
151
|
+
return descriptor;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 프로퍼티: ensureInit에서 own property를 정리할 수 있도록 메타데이터 등록 (state.ts와 동일 패턴).
|
|
155
|
+
// 필드 이니셜라이저가 있으면 own property가 accessor를 가리므로 getter가 영원히 안 불림 —
|
|
156
|
+
// getter 안의 자체이주는 마지막 안전망일 뿐, 실질적인 살림은 ensureInit이 한다.
|
|
157
|
+
const constructor = (target as any).constructor;
|
|
158
|
+
let aroundList = ReflectUtils.getOwnMetadata(AROUND_METADATA_KEY, constructor) as AroundMetadata[] | undefined;
|
|
159
|
+
if (!aroundList) {
|
|
160
|
+
aroundList = [];
|
|
161
|
+
ReflectUtils.defineMetadata(AROUND_METADATA_KEY, aroundList, constructor);
|
|
162
|
+
}
|
|
163
|
+
aroundList.push({ propertyKey });
|
|
164
|
+
|
|
165
|
+
// target은 prototype이라 여기 값을 직접 두면 모든 인스턴스가 공유해버린다.
|
|
166
|
+
// 인스턴스별로 따로 저장되도록 인스턴스 전용 Symbol 슬롯(this[storageKey])에 실제 값을 둔다.
|
|
167
|
+
const storageKey = Symbol(`around:${String(propertyKey)}`);
|
|
168
|
+
const getFn = (options as AroundPropertyOptions).get as unknown as ((hhs: HelperHostSet, value: any) => any) | undefined;
|
|
169
|
+
const setFn = (options as AroundPropertyOptions).set as unknown as ((hhs: HelperHostSet, value: any) => any) | undefined;
|
|
170
|
+
Object.defineProperty(target, propertyKey, {
|
|
171
|
+
get(this: any) {
|
|
172
|
+
// 안전망: 누군가 plain delete로 own을 걷어낸 뒤라면 여기서 슬롯으로 회수한다.
|
|
173
|
+
// (정상 경로에서는 ensureInit이 이미 setter 경유로 정리했으므로 own이 없음)
|
|
174
|
+
const own = Object.getOwnPropertyDescriptor(this, propertyKey);
|
|
175
|
+
if (own && 'value' in own) {
|
|
176
|
+
delete this[propertyKey];
|
|
177
|
+
this[storageKey] = setFn ? setFn.call(this, resolveHelperHostSet(this), own.value) : own.value;
|
|
178
|
+
}
|
|
179
|
+
const raw = this[storageKey];
|
|
180
|
+
if (!getFn) return raw;
|
|
181
|
+
const mapped = getFn.call(this, resolveHelperHostSet(this), raw);
|
|
182
|
+
return mapped === undefined ? raw : mapped;
|
|
183
|
+
},
|
|
184
|
+
set(this: any, value: any) {
|
|
185
|
+
this[storageKey] = setFn ? setFn.call(this, resolveHelperHostSet(this), value) : value;
|
|
186
|
+
},
|
|
187
|
+
enumerable: true,
|
|
188
|
+
configurable: true
|
|
189
|
+
});
|
|
190
|
+
}) as MethodDecorator & PropertyDecorator;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const findAllAroundMetadata = (target: any): AroundMetadata[] => {
|
|
194
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
195
|
+
return ReflectUtils.findAllMetadata<any[]>(AROUND_METADATA_KEY, constructor).flat();
|
|
196
|
+
};
|
|
@@ -5,6 +5,12 @@ export const ON_ATTRIBUTE_CHANGED_METADATA_KEY = Symbol.for('simple-web-componen
|
|
|
5
5
|
export interface ChangedAttributeThisOptions {
|
|
6
6
|
type?: typeof Number | typeof Boolean | typeof String;
|
|
7
7
|
while?: 'connected';
|
|
8
|
+
/** 속성 변경 시 핸들러 실행 여부 게이트. Promise<boolean>도 되어 async 판정 가능. false면 스킵. */
|
|
9
|
+
filter?: (value: any, meta: { currentThis: any; helper: HelperHostSet }) => boolean | Promise<boolean>;
|
|
10
|
+
/** filter 통과 후 핸들러 직전 훅. await되고, 리턴값은 @changedAttributeBeforeReturn 으로 핸들러에 주입된다. */
|
|
11
|
+
before?: (value: any, meta: { currentThis: any; helper: HelperHostSet }) => any | Promise<any>;
|
|
12
|
+
/** 핸들러가 성공/실패해도 항상 실행되는 정리 훅. ctx로 인자/결과/에러를 받는다. 에러는 로깅됨. */
|
|
13
|
+
finally?: (value: any, meta: { currentThis: any; helper: HelperHostSet }, ctx: { args: any[]; result?: any; error?: any }) => any | Promise<any>;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export interface ChangedAttributeThisMetadata {
|
|
@@ -13,13 +19,6 @@ export interface ChangedAttributeThisMetadata {
|
|
|
13
19
|
options: ChangedAttributeThisOptions;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
const convertValue = (val: any, type: any): any => {
|
|
17
|
-
if (val === null || val === undefined) return val;
|
|
18
|
-
if (type === Number) return Number(val);
|
|
19
|
-
if (type === Boolean) return val === 'false' || val === '0' ? false : true;
|
|
20
|
-
return val;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
22
|
const applyChangedAttribute = (attributeName: string | undefined, options: ChangedAttributeThisOptions, target: Object, propertyKey: string | symbol): void => {
|
|
24
23
|
const constructor = target.constructor;
|
|
25
24
|
let metaList = ReflectUtils.getOwnMetadata(ON_ATTRIBUTE_CHANGED_METADATA_KEY, constructor) as ChangedAttributeThisMetadata[];
|
|
@@ -36,7 +35,7 @@ const applyChangedAttribute = (attributeName: string | undefined, options: Chang
|
|
|
36
35
|
});
|
|
37
36
|
};
|
|
38
37
|
|
|
39
|
-
export function changedAttribute(attributeName
|
|
38
|
+
export function changedAttribute(attributeName: string, options?: ChangedAttributeThisOptions): MethodDecorator;
|
|
40
39
|
export function changedAttribute(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): void;
|
|
41
40
|
/**
|
|
42
41
|
* @changedAttributeThis decorator - fires when any attribute on $this changes.
|
|
@@ -48,7 +47,7 @@ export function changedAttribute(attributeNameOrTarget?: string | Object, option
|
|
|
48
47
|
applyChangedAttribute(undefined, {}, attributeNameOrTarget as Object, optionsOrPropertyKey);
|
|
49
48
|
return;
|
|
50
49
|
}
|
|
51
|
-
// 옵션과 함께: @changedAttribute(
|
|
50
|
+
// 옵션과 함께: @changedAttribute('name', options?)
|
|
52
51
|
const attributeName = attributeNameOrTarget as string | undefined;
|
|
53
52
|
const options = (optionsOrPropertyKey as ChangedAttributeThisOptions) ?? {};
|
|
54
53
|
return (target: Object, propertyKey: string | symbol) => {
|
|
@@ -57,12 +56,12 @@ export function changedAttribute(attributeNameOrTarget?: string | Object, option
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
export const getChangedAttributeMetadata = (target: any): ChangedAttributeThisMetadata[] | undefined => {
|
|
60
|
-
const constructor = target
|
|
61
|
-
return ReflectUtils.
|
|
59
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
60
|
+
return ReflectUtils.findAllMetadata<any[]>(ON_ATTRIBUTE_CHANGED_METADATA_KEY, constructor).flat();
|
|
62
61
|
};
|
|
63
62
|
|
|
64
63
|
export const findAllAttributeChangedMetadata = (target: any): Map<string, ChangedAttributeThisMetadata[]> => {
|
|
65
|
-
const constructor = target
|
|
64
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
66
65
|
const metaList = ReflectUtils.findAllMetadata<ChangedAttributeThisMetadata[]>(ON_ATTRIBUTE_CHANGED_METADATA_KEY, constructor) || [];
|
|
67
66
|
|
|
68
67
|
const result = new Map<string, ChangedAttributeThisMetadata[]>();
|
|
@@ -85,7 +84,9 @@ export const convertAttributeValue = (val: any, type?: typeof Number | typeof Bo
|
|
|
85
84
|
// ChangedAttributeLifeCycler
|
|
86
85
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
86
|
import { ElementDefineLifeCycler, HelperHostSet } from '../types';
|
|
88
|
-
import { getAttributeValue } from './applyAttribute';
|
|
87
|
+
import { getAttributeValue, convertValue } from './applyAttribute';
|
|
88
|
+
import { SwcUtils } from '../utils/Utils';
|
|
89
|
+
import { buildSwcParameterArgs } from './parameter';
|
|
89
90
|
|
|
90
91
|
export class ChangedAttributeLifeCycler implements ElementDefineLifeCycler {
|
|
91
92
|
private attrChangeMap: Map<string, ChangedAttributeThisMetadata[]> | null = null;
|
|
@@ -109,7 +110,7 @@ export class ChangedAttributeLifeCycler implements ElementDefineLifeCycler {
|
|
|
109
110
|
for (const meta of metaList) {
|
|
110
111
|
if (meta.options.while === 'connected') {
|
|
111
112
|
const val = getAttributeValue(inst, name, { type: meta.options.type });
|
|
112
|
-
if (val !== null)
|
|
113
|
+
if (val !== null) this.runChanged(helperHostSet, meta, val, null, name);
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
}
|
|
@@ -122,7 +123,30 @@ export class ChangedAttributeLifeCycler implements ElementDefineLifeCycler {
|
|
|
122
123
|
if (!metaList) return;
|
|
123
124
|
for (const meta of metaList) {
|
|
124
125
|
if (meta.options.while === 'connected' && !inst.__swc_connected) continue;
|
|
125
|
-
|
|
126
|
+
this.runChanged(helperHostSet, meta, convertAttributeValue(newVal, meta.options.type), old, name);
|
|
126
127
|
}
|
|
127
128
|
}
|
|
129
|
+
|
|
130
|
+
// filter(async)/before/finally + @changedAttributeBeforeReturn 주입. fire-and-forget.
|
|
131
|
+
private runChanged(helperHostSet: HelperHostSet, meta: ChangedAttributeThisMetadata, value: any, old: string | null, name: string): void {
|
|
132
|
+
const inst = helperHostSet.$this;
|
|
133
|
+
const opts = meta.options;
|
|
134
|
+
void (async () => {
|
|
135
|
+
const helper = SwcUtils.getHelperAndHostSet(inst);
|
|
136
|
+
if (opts.filter && !(await opts.filter(value, { currentThis: inst, helper }))) return;
|
|
137
|
+
const hostSet = SwcUtils.getHostSet(inst);
|
|
138
|
+
const helperSet = SwcUtils.getHelperSet(helperHostSet.$w);
|
|
139
|
+
const legacyArgs = [value, old, name, helper];
|
|
140
|
+
const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, meta.propertyKey, {
|
|
141
|
+
hostSet, helperHostSet: helper, helperSet, changedAttributeBeforeReturn: beforeReturn
|
|
142
|
+
}, [...legacyArgs, beforeReturn]);
|
|
143
|
+
let args = buildArgs(undefined);
|
|
144
|
+
if (opts.before) { const br = await opts.before(value, { currentThis: inst, helper }); args = buildArgs(br); }
|
|
145
|
+
let result: any, error: any;
|
|
146
|
+
try { result = await inst[meta.propertyKey](...args); }
|
|
147
|
+
catch (e) { error = e; }
|
|
148
|
+
finally { if (opts.finally) await opts.finally(value, { currentThis: inst, helper }, { args, result, error }); }
|
|
149
|
+
if (error) throw error;
|
|
150
|
+
})().catch(e => console.error('[SWC] changedAttribute handler error:', e));
|
|
151
|
+
}
|
|
128
152
|
}
|