@dooboostore/simple-web-component 1.0.48 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -19
- package/dist/cjs/config/config.js +1 -1
- package/dist/cjs/config/config.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +2373 -30
- package/dist/cjs/decorators/addEventListener.js.map +3 -3
- package/dist/cjs/decorators/applyAttribute.js +31 -0
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +55 -18
- package/dist/cjs/decorators/applyClass.js.map +3 -3
- package/dist/cjs/decorators/applyNode.js +73 -1
- package/dist/cjs/decorators/applyNode.js.map +3 -3
- package/dist/cjs/decorators/applyProperty.js +107 -3
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applySlot.js +6 -5
- package/dist/cjs/decorators/applySlot.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +42 -5
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/changedAttribute.js +61 -13
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +254 -439
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +55 -0
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/eventMedia.js +84 -0
- package/dist/cjs/decorators/eventMedia.js.map +7 -0
- package/dist/cjs/decorators/index.js +6 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +200 -0
- package/dist/cjs/decorators/intersectionObserver.js.map +7 -0
- package/dist/cjs/decorators/mutationObserver.js +186 -55
- package/dist/cjs/decorators/mutationObserver.js.map +3 -3
- package/dist/cjs/decorators/parameter.js +73 -0
- package/dist/cjs/decorators/parameter.js.map +7 -0
- package/dist/cjs/decorators/query.js +117 -102
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/requestAnimationFrame.js +124 -0
- package/dist/cjs/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/cjs/decorators/resizeObserver.js +133 -55
- package/dist/cjs/decorators/resizeObserver.js.map +2 -2
- package/dist/cjs/decorators/setInterval.js +114 -0
- package/dist/cjs/decorators/setInterval.js.map +7 -0
- package/dist/cjs/decorators/setTimeout.js +117 -0
- package/dist/cjs/decorators/setTimeout.js.map +7 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js +9 -0
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js +9 -0
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +19 -3
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/window.d.js.map +1 -1
- package/dist/esm/config/config.js +1 -1
- package/dist/esm/config/config.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +2374 -30
- package/dist/esm/decorators/addEventListener.js.map +3 -3
- package/dist/esm/decorators/applyAttribute.js +31 -0
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +55 -18
- package/dist/esm/decorators/applyClass.js.map +3 -3
- package/dist/esm/decorators/applyNode.js +73 -1
- package/dist/esm/decorators/applyNode.js.map +3 -3
- package/dist/esm/decorators/applyProperty.js +107 -3
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applySlot.js +6 -5
- package/dist/esm/decorators/applySlot.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +42 -5
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/changedAttribute.js +62 -13
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +261 -446
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +55 -0
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/eventMedia.js +66 -0
- package/dist/esm/decorators/eventMedia.js.map +7 -0
- package/dist/esm/decorators/index.js +6 -1
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +182 -0
- package/dist/esm/decorators/intersectionObserver.js.map +7 -0
- package/dist/esm/decorators/mutationObserver.js +187 -55
- package/dist/esm/decorators/mutationObserver.js.map +3 -3
- package/dist/esm/decorators/parameter.js +54 -0
- package/dist/esm/decorators/parameter.js.map +7 -0
- package/dist/esm/decorators/query.js +117 -102
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/requestAnimationFrame.js +105 -0
- package/dist/esm/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/esm/decorators/resizeObserver.js +134 -55
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +95 -0
- package/dist/esm/decorators/setInterval.js.map +7 -0
- package/dist/esm/decorators/setTimeout.js +98 -0
- package/dist/esm/decorators/setTimeout.js.map +7 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +9 -0
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js +9 -0
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +20 -4
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +4961 -1628
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/config/config.d.ts.map +1 -1
- package/dist/types/decorators/addEventListener.d.ts +1046 -33
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +24 -34
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +36 -34
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +32 -20
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +61 -34
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applySlot.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +27 -12
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/changedAttribute.d.ts +12 -3
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts +0 -14
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +28 -4
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/eventMedia.d.ts +25 -0
- package/dist/types/decorators/eventMedia.d.ts.map +1 -0
- package/dist/types/decorators/index.d.ts +6 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/intersectionObserver.d.ts +58 -0
- package/dist/types/decorators/intersectionObserver.d.ts.map +1 -0
- package/dist/types/decorators/mutationObserver.d.ts +44 -25
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/parameter.d.ts +35 -0
- package/dist/types/decorators/parameter.d.ts.map +1 -0
- package/dist/types/decorators/query.d.ts +57 -14
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/requestAnimationFrame.d.ts +47 -0
- package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -0
- package/dist/types/decorators/resizeObserver.d.ts +43 -23
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts +41 -0
- package/dist/types/decorators/setInterval.d.ts.map +1 -0
- package/dist/types/decorators/setTimeout.d.ts +41 -0
- package/dist/types/decorators/setTimeout.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +7 -1
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +6 -0
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +104 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +4961 -1628
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +7 -5
- package/src/config/config.ts +1 -1
- package/src/decorators/addEventListener.ts +1446 -34
- package/src/decorators/applyAttribute.ts +60 -36
- package/src/decorators/applyClass.ts +86 -26
- package/src/decorators/applyNode.ts +124 -36
- package/src/decorators/applyProperty.ts +172 -40
- package/src/decorators/applySlot.ts +6 -5
- package/src/decorators/applyStyle.ts +74 -17
- package/src/decorators/changedAttribute.ts +76 -15
- package/src/decorators/elementDefine.ts +310 -582
- package/src/decorators/emitCustomEvent.ts +73 -5
- package/src/decorators/eventMedia.ts +78 -0
- package/src/decorators/index.ts +6 -1
- package/src/decorators/intersectionObserver.ts +275 -0
- package/src/decorators/mutationObserver.ts +282 -89
- package/src/decorators/parameter.ts +81 -0
- package/src/decorators/query.ts +168 -126
- package/src/decorators/requestAnimationFrame.ts +158 -0
- package/src/decorators/resizeObserver.ts +211 -89
- package/src/decorators/setInterval.ts +136 -0
- package/src/decorators/setTimeout.ts +139 -0
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +17 -0
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +19 -0
- package/src/elements/SwcAppMixin.ts +20 -5
- package/src/types/index.ts +141 -0
- package/src/types/window.d.ts +2 -18
- package/dist/cjs/decorators/queryAll.js +0 -181
- package/dist/cjs/decorators/queryAll.js.map +0 -7
- package/dist/cjs/examples/RxJSOperatorsExample.js +0 -285
- package/dist/cjs/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/esm/decorators/queryAll.js +0 -162
- package/dist/esm/decorators/queryAll.js.map +0 -7
- package/dist/esm/examples/RxJSOperatorsExample.js +0 -266
- package/dist/esm/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/types/decorators/queryAll.d.ts +0 -22
- package/dist/types/decorators/queryAll.d.ts.map +0 -1
- package/dist/types/examples/RxJSOperatorsExample.d.ts +0 -94
- package/dist/types/examples/RxJSOperatorsExample.d.ts.map +0 -1
- package/src/decorators/queryAll.ts +0 -186
- package/src/examples/RxJSOperatorsExample.ts +0 -279
package/src/decorators/query.ts
CHANGED
|
@@ -1,36 +1,110 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
2
|
import { getElementConfig, ensureInit } from './elementDefine';
|
|
3
3
|
import { SwcUtils } from '../utils/Utils';
|
|
4
|
-
import { SwcQueryOptions, HelperHostSet } from '../types';
|
|
4
|
+
import { SwcQueryOptions, SwcRootType, SwcFnSelector, SwcSelector, HelperHostSet } from '../types';
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
export type QueryPick = 'first' | 'last' | 'all' | 'even' | 'odd' | number;
|
|
7
|
+
|
|
8
|
+
export interface QueryBaseOptions {
|
|
9
|
+
// 어떤 요소를 고를지 — 기본 'first'(단일). 'all'/'even'/'odd'는 배열, number는 인덱스(단일)
|
|
10
|
+
pick?: QueryPick;
|
|
11
11
|
filter?: (target: HTMLElement, meta: { currentThis: any, helper: HelperHostSet }) => boolean;
|
|
12
12
|
}
|
|
13
|
+
// 문자열 셀렉터 전용 — root 허용
|
|
14
|
+
export type QueryOptions = QueryBaseOptions & SwcQueryOptions;
|
|
15
|
+
// 함수 셀렉터 전용 — root 금지
|
|
16
|
+
export type QueryNonQueryOptions = QueryBaseOptions;
|
|
17
|
+
|
|
18
|
+
export type QueryFnSelector = SwcFnSelector;
|
|
19
|
+
export type QuerySelector = SwcSelector;
|
|
20
|
+
|
|
21
|
+
// 셀렉터 종류에 따라 옵션 타입 분기
|
|
22
|
+
export type QueryOptionsOf<S extends QuerySelector> = S extends string ? QueryOptions : QueryNonQueryOptions;
|
|
13
23
|
|
|
14
24
|
export interface QueryMetadata {
|
|
15
25
|
propertyKey: string | symbol;
|
|
16
|
-
selector:
|
|
17
|
-
options: QueryOptions
|
|
26
|
+
selector: QuerySelector;
|
|
27
|
+
options: QueryOptions;
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
export const QUERY_METADATA_KEY = Symbol.for('simple-web-component:query');
|
|
21
31
|
|
|
32
|
+
/**
|
|
33
|
+
* 셀렉터를 해석해 매칭된 모든 요소를 배열로 반환한다 (query/queryAll 공용).
|
|
34
|
+
* $window/$document는 요소가 아니므로 여기서 처리하지 않는다.
|
|
35
|
+
*/
|
|
36
|
+
export const resolveQueryElements = (
|
|
37
|
+
inst: any,
|
|
38
|
+
selector: QuerySelector,
|
|
39
|
+
options: QueryOptions,
|
|
40
|
+
win: Window
|
|
41
|
+
): HTMLElement[] => {
|
|
42
|
+
const hostSet = SwcUtils.getHelperAndHostSet(win, inst);
|
|
43
|
+
let all: HTMLElement[] = [];
|
|
44
|
+
|
|
45
|
+
if (typeof selector === 'function') {
|
|
46
|
+
const result = (selector as any)(inst, hostSet);
|
|
47
|
+
if (result instanceof win.NodeList) all = Array.from(result as NodeList).filter(e => e instanceof win.HTMLElement) as HTMLElement[];
|
|
48
|
+
else if (Array.isArray(result)) all = (result as any[]).filter(e => e instanceof win.HTMLElement) as HTMLElement[];
|
|
49
|
+
else if (result instanceof win.HTMLElement) all = [result as HTMLElement];
|
|
50
|
+
} else if (typeof selector === 'string') {
|
|
51
|
+
const r = (options as any)?.root || 'auto';
|
|
52
|
+
if (selector === '$this') all = [inst as HTMLElement];
|
|
53
|
+
else if (selector === '$host') all = hostSet.$host ? [hostSet.$host] : [];
|
|
54
|
+
else if (selector === '$parentHost') all = hostSet.$parentHost ? [hostSet.$parentHost] : [];
|
|
55
|
+
else if (selector === '$appHost') all = hostSet.$appHost ? [hostSet.$appHost as any] : [];
|
|
56
|
+
else if (selector === '$firstHost') all = hostSet.$firstHost ? [hostSet.$firstHost] : [];
|
|
57
|
+
else if (selector === '$lastHost') all = hostSet.$lastHost ? [hostSet.$lastHost] : [];
|
|
58
|
+
else if (selector === '$firstAppHost') all = hostSet.$firstAppHost ? [hostSet.$firstAppHost as any] : [];
|
|
59
|
+
else if (selector === '$lastAppHost') all = hostSet.$lastAppHost ? [hostSet.$lastAppHost as any] : [];
|
|
60
|
+
else if (selector === '$hosts') all = hostSet.$hosts.filter(Boolean);
|
|
61
|
+
else if (selector === '$appHosts') all = hostSet.$appHosts.filter(Boolean) as any[];
|
|
62
|
+
else {
|
|
63
|
+
const scopes: any[] = [];
|
|
64
|
+
if (r === 'auto') scopes.push(inst.shadowRoot || inst);
|
|
65
|
+
else if (r === 'light') scopes.push(inst);
|
|
66
|
+
else if (r === 'shadow' && inst.shadowRoot) scopes.push(inst.shadowRoot);
|
|
67
|
+
else if (r === 'all') { scopes.push(inst); if (inst.shadowRoot) scopes.push(inst.shadowRoot); }
|
|
68
|
+
for (const scope of scopes) {
|
|
69
|
+
const found = scope?.querySelectorAll?.(selector) as NodeListOf<HTMLElement> | undefined;
|
|
70
|
+
if (found?.length) all.push(...Array.from(found).filter(e => e instanceof win.HTMLElement));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if ((options as any)?.filter) {
|
|
76
|
+
all = all.filter(el => (options as any).filter(el, { currentThis: inst, helper: hostSet }));
|
|
77
|
+
}
|
|
78
|
+
return all;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 매칭된 요소 배열에서 pick 옵션에 따라 요소를 고른다.
|
|
83
|
+
* - 'first'(기본) / 'last' / number(인덱스) → 단일 요소
|
|
84
|
+
* - 'all' / 'even'(0,2,4...) / 'odd'(1,3,5...) → 배열
|
|
85
|
+
*/
|
|
86
|
+
export const pickElements = (
|
|
87
|
+
all: HTMLElement[],
|
|
88
|
+
pick: QueryPick
|
|
89
|
+
): HTMLElement | HTMLElement[] | null => {
|
|
90
|
+
if (pick === 'all') return all;
|
|
91
|
+
if (pick === 'even') return all.filter((_, i) => i % 2 === 0);
|
|
92
|
+
if (pick === 'odd') return all.filter((_, i) => i % 2 === 1);
|
|
93
|
+
if (typeof pick === 'number') return all[pick] ?? null;
|
|
94
|
+
if (pick === 'last') return all.length ? all[all.length - 1] : null;
|
|
95
|
+
return all.length ? all[0] : null;
|
|
96
|
+
};
|
|
97
|
+
|
|
22
98
|
export function query(target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor): PropertyDescriptor | void;
|
|
23
|
-
export function query(selector: string
|
|
99
|
+
export function query(selector: string, options?: QueryOptions): PropertyDecorator;
|
|
100
|
+
export function query(selector: QueryFnSelector, options?: QueryNonQueryOptions): PropertyDecorator;
|
|
24
101
|
export function query(options?: QueryOptions): PropertyDecorator;
|
|
25
102
|
/**
|
|
26
|
-
* @query decorator
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* -
|
|
30
|
-
* -
|
|
31
|
-
*
|
|
32
|
-
* Supports filtering:
|
|
33
|
-
* - @query('#my-element', { filter: (target, meta) => target.hasAttribute('active') })
|
|
103
|
+
* @query decorator — 클래스 필드에 요소를 주입한다.
|
|
104
|
+
*
|
|
105
|
+
* - 문자열 셀렉터: CSS / 특수 셀렉터($this,$host,$window,...), root 옵션으로 탐색 영역 지정
|
|
106
|
+
* - 함수 셀렉터: (currentThis, helper) => Node | NodeList | Element | Element[] | null (root 금지)
|
|
107
|
+
* - options.pick: 'first'(기본) | 'last' | 'all' | 'even' | 'odd' | number
|
|
34
108
|
*/
|
|
35
109
|
export function query(selectorOrTarget?: string | ((currentThis: any, helper: HelperHostSet) => Node | NodeList | Element | Element[] | null) | Object, optionsOrPropertyKey?: QueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
36
110
|
// Bare decorator: @query
|
|
@@ -50,118 +124,32 @@ export function query(selectorOrTarget?: string | ((currentThis: any, helper: He
|
|
|
50
124
|
queries = [];
|
|
51
125
|
ReflectUtils.defineMetadata(QUERY_METADATA_KEY, queries, constructor);
|
|
52
126
|
}
|
|
53
|
-
queries.push({ propertyKey, selector: selectorOrTarget as any, options: optionsOrPropertyKey as QueryOptions || {} });
|
|
127
|
+
queries.push({ propertyKey, selector: selectorOrTarget as any, options: (optionsOrPropertyKey as QueryOptions) || {} });
|
|
54
128
|
|
|
55
129
|
Object.defineProperty(targetObj, propertyKey, {
|
|
56
130
|
get(this: HTMLElement) {
|
|
57
131
|
ensureInit(this);
|
|
58
132
|
const config = getElementConfig(this);
|
|
59
133
|
const win = config?.window || window;
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
// Resolve selector - can be string or function
|
|
63
|
-
let resolvedSelector: string;
|
|
64
|
-
let resolvedElement: HTMLElement | null = null;
|
|
65
|
-
|
|
66
|
-
if (typeof selectorOrTarget === 'function') {
|
|
67
|
-
const result = (selectorOrTarget as any)(this, hostSet);
|
|
68
|
-
// If function returns a Node/Element, return it directly
|
|
69
|
-
if (result instanceof win.Node || result instanceof win.Element) {
|
|
70
|
-
resolvedElement = result instanceof win.HTMLElement ? (result as HTMLElement) : null;
|
|
71
|
-
}
|
|
72
|
-
// If function returns NodeList or Element[], return first element
|
|
73
|
-
else if (result instanceof win.NodeList || Array.isArray(result)) {
|
|
74
|
-
resolvedElement = result.length > 0 && result[0] instanceof win.HTMLElement ? (result[0] as HTMLElement) : null;
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
resolvedSelector = selectorOrTarget;
|
|
78
|
-
|
|
79
|
-
const r = (optionsOrPropertyKey as QueryOptions)?.root || 'auto';
|
|
80
|
-
const applyRoot = (t: any) => {
|
|
81
|
-
if (!t || !(t instanceof HTMLElement)) return t;
|
|
82
|
-
if (r === 'auto') return t.shadowRoot || t;
|
|
83
|
-
if (r === 'shadow') return t.shadowRoot;
|
|
84
|
-
if (r === 'light') return t;
|
|
85
|
-
if (r === 'all') return t.shadowRoot || t;
|
|
86
|
-
return t;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// --- Special Selectors: HostSet ---
|
|
90
|
-
if (resolvedSelector === '$this') resolvedElement = applyRoot(this);
|
|
91
|
-
else if (resolvedSelector === '$host') resolvedElement = applyRoot(hostSet.$host);
|
|
92
|
-
else if (resolvedSelector === '$parentHost') resolvedElement = applyRoot(hostSet.$parentHost);
|
|
93
|
-
else if (resolvedSelector === '$appHost') resolvedElement = applyRoot(hostSet.$appHost);
|
|
94
|
-
else if (resolvedSelector === '$firstHost') resolvedElement = applyRoot(hostSet.$firstHost);
|
|
95
|
-
else if (resolvedSelector === '$lastHost') resolvedElement = applyRoot(hostSet.$lastHost);
|
|
96
|
-
else if (resolvedSelector === '$firstAppHost') resolvedElement = applyRoot(hostSet.$firstAppHost);
|
|
97
|
-
else if (resolvedSelector === '$lastAppHost') resolvedElement = applyRoot(hostSet.$lastAppHost);
|
|
98
|
-
// --- Special Selectors: Env ---
|
|
99
|
-
else if (resolvedSelector === '$window') return win;
|
|
100
|
-
else if (resolvedSelector === '$document') return win.document;
|
|
101
|
-
// --- Standard Selectors ---
|
|
102
|
-
else {
|
|
103
|
-
if (r === 'shadow') resolvedElement = this.shadowRoot ? (this.shadowRoot.querySelector(resolvedSelector) as HTMLElement | null) : null;
|
|
104
|
-
else if (r === 'light') resolvedElement = this.querySelector(resolvedSelector) as HTMLElement | null;
|
|
105
|
-
else if (r === 'all') {
|
|
106
|
-
const shadowMatch = this.shadowRoot ? (this.shadowRoot.querySelector(resolvedSelector) as HTMLElement | null) : null;
|
|
107
|
-
resolvedElement = shadowMatch || (this.querySelector(resolvedSelector) as HTMLElement | null);
|
|
108
|
-
}
|
|
109
|
-
else resolvedElement = ((this.shadowRoot || this).querySelector(resolvedSelector) as HTMLElement | null);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
134
|
+
const options = (optionsOrPropertyKey as QueryOptions) || {};
|
|
112
135
|
|
|
113
|
-
//
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
if (!shouldInclude) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
136
|
+
// $window/$document는 요소가 아니므로 항상 단일 반환
|
|
137
|
+
if (typeof selectorOrTarget === 'string' && selectorOrTarget === '$window') return win;
|
|
138
|
+
if (typeof selectorOrTarget === 'string' && selectorOrTarget === '$document') return win.document;
|
|
120
139
|
|
|
121
|
-
|
|
140
|
+
const all = resolveQueryElements(this, selectorOrTarget as QuerySelector, options, win);
|
|
141
|
+
return pickElements(all, options.pick ?? 'first');
|
|
122
142
|
},
|
|
123
143
|
set(this: HTMLElement, nv: any) {
|
|
124
144
|
ensureInit(this);
|
|
125
|
-
if (nv === null || nv === undefined) {
|
|
145
|
+
if (nv === null || nv === undefined || (Array.isArray(nv) && nv.length === 0)) {
|
|
126
146
|
const config = getElementConfig(this);
|
|
127
147
|
const win = config?.window || window;
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
const result = (selectorOrTarget as any)(this, hostSet);
|
|
134
|
-
if (result instanceof win.Element) {
|
|
135
|
-
(result as Element).remove();
|
|
136
|
-
} else if (result instanceof win.NodeList || Array.isArray(result)) {
|
|
137
|
-
const el = result.length > 0 ? result[0] : null;
|
|
138
|
-
if (el instanceof win.Element) {
|
|
139
|
-
(el as Element).remove();
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const resolvedSelector = selectorOrTarget;
|
|
146
|
-
|
|
147
|
-
if (resolvedSelector.startsWith('$') && resolvedSelector !== '$this') return;
|
|
148
|
-
|
|
149
|
-
const r = (optionsOrPropertyKey as QueryOptions)?.root || 'auto';
|
|
150
|
-
const targets: Element[] = [];
|
|
151
|
-
|
|
152
|
-
if (r === 'shadow') {
|
|
153
|
-
if (this.shadowRoot?.querySelector(resolvedSelector)) targets.push(this.shadowRoot.querySelector(resolvedSelector)!);
|
|
154
|
-
} else if (r === 'light') {
|
|
155
|
-
if (this.querySelector(resolvedSelector)) targets.push(this.querySelector(resolvedSelector)!);
|
|
156
|
-
} else if (r === 'all') {
|
|
157
|
-
if (this.shadowRoot?.querySelector(resolvedSelector)) targets.push(this.shadowRoot.querySelector(resolvedSelector)!);
|
|
158
|
-
if (this.querySelector(resolvedSelector)) targets.push(this.querySelector(resolvedSelector)!);
|
|
159
|
-
} else {
|
|
160
|
-
const el = (this.shadowRoot || this).querySelector(resolvedSelector);
|
|
161
|
-
if (el) targets.push(el);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
targets.forEach(t => t.remove());
|
|
148
|
+
const options = (optionsOrPropertyKey as QueryOptions) || {};
|
|
149
|
+
const all = resolveQueryElements(this, selectorOrTarget as QuerySelector, options, win);
|
|
150
|
+
const picked = pickElements(all, options.pick ?? 'first');
|
|
151
|
+
if (Array.isArray(picked)) picked.forEach(t => t.remove());
|
|
152
|
+
else if (picked) picked.remove();
|
|
165
153
|
}
|
|
166
154
|
},
|
|
167
155
|
enumerable: true,
|
|
@@ -170,7 +158,7 @@ export function query(selectorOrTarget?: string | ((currentThis: any, helper: He
|
|
|
170
158
|
};
|
|
171
159
|
}
|
|
172
160
|
// Without selector (defaults to $this)
|
|
173
|
-
return query('$this', selectorOrTarget as
|
|
161
|
+
return query('$this', selectorOrTarget as QueryOptions);
|
|
174
162
|
}
|
|
175
163
|
|
|
176
164
|
export const getQueryMetadata = (target: any): QueryMetadata[] => {
|
|
@@ -178,25 +166,79 @@ export const getQueryMetadata = (target: any): QueryMetadata[] => {
|
|
|
178
166
|
return ReflectUtils.getMetadata(QUERY_METADATA_KEY, constructor) ?? [];
|
|
179
167
|
};
|
|
180
168
|
|
|
169
|
+
// ─── queryAll (query의 pick:'all' 편의 래퍼 — 호환용) ───
|
|
170
|
+
export const QUERY_ALL_METADATA_KEY = QUERY_METADATA_KEY;
|
|
171
|
+
|
|
172
|
+
export function queryAll(target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor): PropertyDescriptor | void;
|
|
173
|
+
export function queryAll(selector: string, options?: Omit<QueryOptions, 'pick'>): PropertyDecorator;
|
|
174
|
+
export function queryAll(selector: QueryFnSelector, options?: Omit<QueryNonQueryOptions, 'pick'>): PropertyDecorator;
|
|
175
|
+
export function queryAll(options?: Omit<QueryOptions, 'pick'>): PropertyDecorator;
|
|
176
|
+
export function queryAll(selectorOrTarget?: any, optionsOrPropertyKey?: any, descriptor?: PropertyDescriptor): any {
|
|
177
|
+
// bare(필드 직접 적용)는 query로 위임
|
|
178
|
+
if (descriptor !== undefined && (typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol')) {
|
|
179
|
+
return query(selectorOrTarget, optionsOrPropertyKey, descriptor);
|
|
180
|
+
}
|
|
181
|
+
if (typeof selectorOrTarget === 'string' || typeof selectorOrTarget === 'function') {
|
|
182
|
+
return query(selectorOrTarget, {...(optionsOrPropertyKey ?? {}), pick: 'all'});
|
|
183
|
+
}
|
|
184
|
+
return query({...(selectorOrTarget ?? {}), pick: 'all'});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const getQueryAllMetadata = getQueryMetadata;
|
|
188
|
+
|
|
181
189
|
// ─── root별 편의 데코레이터 ───
|
|
182
190
|
|
|
183
191
|
/**
|
|
184
|
-
* @queryShadow(selector, options?) — shadow DOM에서
|
|
192
|
+
* @queryShadow(selector, options?) — shadow DOM에서 요소를 쿼리
|
|
193
|
+
*/
|
|
194
|
+
export function queryShadow(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator {
|
|
195
|
+
return query(selector as any, {...options ?? {}, root: 'shadow'});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @queryLight(selector, options?) — light DOM에서 요소를 쿼리
|
|
200
|
+
*/
|
|
201
|
+
export function queryLight(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator {
|
|
202
|
+
return query(selector as any, {...options ?? {}, root: 'light'});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @queryAllRoots(selector, options?) — shadow+light 모두에서 요소를 쿼리 (pick 기본 first)
|
|
207
|
+
*/
|
|
208
|
+
export function queryAllRoots(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator {
|
|
209
|
+
return query(selector as any, {...options ?? {}, root: 'all'});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @queryAllShadow(selector, options?) — shadow DOM에서 모든 매칭 요소를 배열로 쿼리
|
|
185
214
|
*/
|
|
186
|
-
export function
|
|
187
|
-
return query(selector, {...options ?? {}, root: 'shadow'});
|
|
215
|
+
export function queryAllShadow(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator {
|
|
216
|
+
return query(selector as any, {...options ?? {}, root: 'shadow', pick: 'all'});
|
|
188
217
|
}
|
|
189
218
|
|
|
190
219
|
/**
|
|
191
|
-
* @
|
|
220
|
+
* @queryAllLight(selector, options?) — light DOM에서 모든 매칭 요소를 배열로 쿼리
|
|
192
221
|
*/
|
|
193
|
-
export function
|
|
194
|
-
return query(selector, {...options ?? {}, root: 'light'});
|
|
222
|
+
export function queryAllLight(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator {
|
|
223
|
+
return query(selector as any, {...options ?? {}, root: 'light', pick: 'all'});
|
|
195
224
|
}
|
|
196
225
|
|
|
197
226
|
/**
|
|
198
|
-
* @
|
|
227
|
+
* @queryAllAll(selector, options?) — shadow+light 모두에서 모든 매칭 요소를 배열로 쿼리
|
|
199
228
|
*/
|
|
200
|
-
export function
|
|
201
|
-
return query(selector, {...options ?? {}, root: 'all'});
|
|
229
|
+
export function queryAllAll(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator {
|
|
230
|
+
return query(selector as any, {...options ?? {}, root: 'all', pick: 'all'});
|
|
202
231
|
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @queryIn(root, pick?) — root + pick 조합 팩토리.
|
|
235
|
+
* first/last/even/odd/all/number를 모두 지원하는 데코레이터를 생성한다.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* @queryIn('shadow', 'even')('.item') // shadow DOM 짝수 인덱스들
|
|
239
|
+
* @queryIn('light', 2)('.item') // light DOM 3번째 요소
|
|
240
|
+
* @queryIn('all')('.item') // root all, pick 기본 first
|
|
241
|
+
*/
|
|
242
|
+
export const queryIn = (root: SwcRootType, pick?: QueryPick) =>
|
|
243
|
+
(selector: string | QueryFnSelector, options?: QueryBaseOptions): PropertyDecorator =>
|
|
244
|
+
query(selector as any, {...options ?? {}, root, ...(pick !== undefined ? {pick} : {})});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { ensureInit, getElementConfig } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { ElementDefineLifeCycler, HelperHostSet } from '../types';
|
|
5
|
+
|
|
6
|
+
export const REQUEST_ANIMATION_FRAME_METADATA_KEY = Symbol.for('simple-web-component:request-animation-frame');
|
|
7
|
+
|
|
8
|
+
export type RequestAnimationFrameType = 'onConnected' | 'returnValue';
|
|
9
|
+
|
|
10
|
+
export interface RequestAnimationFrameOptions {
|
|
11
|
+
/**
|
|
12
|
+
* 'onConnected' - connect 시 자동으로 루프 시작 (매 프레임 데코레이트된 메서드를 직접 호출), disconnect 시 자동 정리.
|
|
13
|
+
* 'returnValue'(기본값) - 자동 시작 없음. 개발자가 이 메서드를 직접 호출해야 시작되고,
|
|
14
|
+
* 그 호출의 리턴값(또는 valueKey로 뽑은 값)이 실제로 매 프레임 호출될 함수가 된다.
|
|
15
|
+
*/
|
|
16
|
+
type?: RequestAnimationFrameType;
|
|
17
|
+
/** 루프 시작 직후 1회 호출 (디버깅/로깅용). id는 첫 requestAnimationFrame 반환값 - cleanup은 자동으로 처리되므로 직접 취소할 필요 없음. */
|
|
18
|
+
created?: (set: HelperHostSet, id: number) => void;
|
|
19
|
+
/**
|
|
20
|
+
* type:'returnValue' 전용 - 리턴값이 함수면 그 자체를, 객체면 이 키의 값이 함수일 때 그 값을
|
|
21
|
+
* "매 프레임 호출될 함수"로 사용한다. 안 주면 REQUEST_ANIMATION_FRAME_METADATA_KEY로 폴백한다 -
|
|
22
|
+
* applyAttribute.ts/applyNode.ts의 valueKey와 동일한 컨벤션.
|
|
23
|
+
*/
|
|
24
|
+
valueKey?: symbol | string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RequestAnimationFrameMetadata {
|
|
28
|
+
propertyKey: string | symbol;
|
|
29
|
+
options: RequestAnimationFrameOptions;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 프레임 콜백 시그니처.
|
|
34
|
+
* - timestamp: 이번 프레임의 시각 (requestAnimationFrame이 넘겨주는 값)
|
|
35
|
+
* - prevValue: 직전 호출이 리턴한 값 (첫 호출에서는 undefined)
|
|
36
|
+
* 리턴값이 undefined/null이면 다음 프레임을 예약하지 않고 루프가 멈춘다 (개발자가 종료를 제어).
|
|
37
|
+
* undefined/null이 아닌 값을 리턴하면 그 값 그대로 다음 호출의 prevValue로 전달된다.
|
|
38
|
+
*/
|
|
39
|
+
export type FrameCallback = (timestamp: number, prevValue?: any) => any;
|
|
40
|
+
|
|
41
|
+
const activeMap = new WeakMap<any, number[]>();
|
|
42
|
+
const getActiveEntries = (inst: any): number[] => {
|
|
43
|
+
let entries = activeMap.get(inst);
|
|
44
|
+
if (!entries) {
|
|
45
|
+
entries = [];
|
|
46
|
+
activeMap.set(inst, entries);
|
|
47
|
+
}
|
|
48
|
+
return entries;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const startFrameLoop = (win: Window, entries: number[], frameFn: FrameCallback): number => {
|
|
52
|
+
let currentId = 0;
|
|
53
|
+
const step = (timestamp: number, prevValue?: any) => {
|
|
54
|
+
const idx = entries.indexOf(currentId);
|
|
55
|
+
if (idx >= 0) entries.splice(idx, 1);
|
|
56
|
+
let result: any;
|
|
57
|
+
try {
|
|
58
|
+
result = frameFn(timestamp, prevValue);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.error('[SWC] requestAnimationFrame callback error:', e);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (result === undefined || result === null) return; // 개발자가 종료를 신호함 - 다음 프레임 예약 안 함
|
|
64
|
+
currentId = win.requestAnimationFrame(ts => step(ts, result));
|
|
65
|
+
entries.push(currentId);
|
|
66
|
+
};
|
|
67
|
+
currentId = win.requestAnimationFrame(ts => step(ts, undefined));
|
|
68
|
+
entries.push(currentId);
|
|
69
|
+
return currentId;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const applyRequestAnimationFrame = (options: RequestAnimationFrameOptions, target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor): PropertyDescriptor | void => {
|
|
73
|
+
const constructor = target.constructor;
|
|
74
|
+
const normalizedOptions: RequestAnimationFrameOptions = { ...options, type: options.type ?? 'returnValue' };
|
|
75
|
+
|
|
76
|
+
let metaList = ReflectUtils.getOwnMetadata(REQUEST_ANIMATION_FRAME_METADATA_KEY, constructor) as RequestAnimationFrameMetadata[];
|
|
77
|
+
if (!metaList) {
|
|
78
|
+
metaList = [];
|
|
79
|
+
ReflectUtils.defineMetadata(REQUEST_ANIMATION_FRAME_METADATA_KEY, metaList, constructor);
|
|
80
|
+
}
|
|
81
|
+
metaList.push({ propertyKey, options: normalizedOptions });
|
|
82
|
+
|
|
83
|
+
if (normalizedOptions.type === 'onConnected' || !descriptor) {
|
|
84
|
+
return; // LifeCycler가 connect/disconnect를 전담 - wrapping 불필요
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const original = descriptor.value;
|
|
88
|
+
descriptor.value = function (this: any, ...args: any[]) {
|
|
89
|
+
const res = original.apply(this, args);
|
|
90
|
+
// valueKey를 안 주면 이 데코레이터 전용 METADATA_KEY로 폴백 (applyAttribute.ts/applyNode.ts와 동일한 컨벤션)
|
|
91
|
+
const vk = normalizedOptions.valueKey ?? REQUEST_ANIMATION_FRAME_METADATA_KEY;
|
|
92
|
+
const frameFn: FrameCallback | undefined =
|
|
93
|
+
typeof res === 'function' ? res : res && typeof res === 'object' && typeof res[vk] === 'function' ? res[vk] : undefined;
|
|
94
|
+
|
|
95
|
+
if (frameFn) {
|
|
96
|
+
ensureInit(this);
|
|
97
|
+
const conf = getElementConfig(this);
|
|
98
|
+
const win = (this as any)._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as Window);
|
|
99
|
+
const id = startFrameLoop(win, getActiveEntries(this), frameFn);
|
|
100
|
+
normalizedOptions.created?.(SwcUtils.getHelperAndHostSet(win, this), id);
|
|
101
|
+
}
|
|
102
|
+
return res;
|
|
103
|
+
};
|
|
104
|
+
return descriptor;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* type:'onConnected' - connect 시 자동으로 매 프레임 루프를 시작하고, disconnect 시 자동으로 cancelAnimationFrame 한다.
|
|
109
|
+
* type:'returnValue'(기본값) - 메서드를 감싸지 않는 대신, 호출될 때마다(개발자가 직접 호출) 원본 로직을
|
|
110
|
+
* 그대로 실행하고 리턴값(또는 valueKey로 뽑은 값)이 함수면 그 함수를 매 프레임 호출되는 루프로 등록한다.
|
|
111
|
+
* 원본 리턴값은 그대로 통과시키므로 다른 데코레이터와 같은 메서드에 스택해도 순서에 상관없이 안전하다.
|
|
112
|
+
*
|
|
113
|
+
* 옵션 없이 `@requestAnimationFrame` 그대로 붙여도 되고, `@requestAnimationFrame({...})`처럼 옵션을 줄 수도 있다.
|
|
114
|
+
*/
|
|
115
|
+
export function requestAnimationFrame(options?: RequestAnimationFrameOptions): MethodDecorator;
|
|
116
|
+
export function requestAnimationFrame(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): void;
|
|
117
|
+
export function requestAnimationFrame(optionsOrTarget?: RequestAnimationFrameOptions | Object, propertyKey?: string | symbol, descriptor?: PropertyDescriptor): MethodDecorator | void {
|
|
118
|
+
if (propertyKey !== undefined) {
|
|
119
|
+
// 옵션 없이: @requestAnimationFrame
|
|
120
|
+
applyRequestAnimationFrame({}, optionsOrTarget as Object, propertyKey, descriptor);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// 옵션과 함께: @requestAnimationFrame() 또는 @requestAnimationFrame({...})
|
|
124
|
+
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
125
|
+
return applyRequestAnimationFrame((optionsOrTarget as RequestAnimationFrameOptions) ?? {}, target, propertyKey, descriptor);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const findAllRequestAnimationFrameMetadata = (target: any): RequestAnimationFrameMetadata[] => {
|
|
130
|
+
const constructor = target instanceof Function ? target : target.constructor;
|
|
131
|
+
return (ReflectUtils.findAllMetadata<RequestAnimationFrameMetadata[]>(REQUEST_ANIMATION_FRAME_METADATA_KEY, constructor) || []).flat();
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export class RequestAnimationFrameLifeCycler implements ElementDefineLifeCycler {
|
|
135
|
+
onConnected(helperHostSet: HelperHostSet): void {
|
|
136
|
+
const inst = helperHostSet.$this;
|
|
137
|
+
activeMap.delete(inst); // 재연결 대비 리셋
|
|
138
|
+
const entries = getActiveEntries(inst);
|
|
139
|
+
for (const meta of findAllRequestAnimationFrameMetadata(inst)) {
|
|
140
|
+
if (meta.options.type !== 'onConnected') continue; // returnValue 타입은 수동 호출을 기다림
|
|
141
|
+
const frameFn: FrameCallback = (timestamp, prevValue) => inst[meta.propertyKey](timestamp, prevValue);
|
|
142
|
+
const id = startFrameLoop(helperHostSet.$w, entries, frameFn);
|
|
143
|
+
meta.options.created?.(helperHostSet, id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
onDisconnected(helperHostSet: HelperHostSet): void {
|
|
148
|
+
const inst = helperHostSet.$this;
|
|
149
|
+
for (const id of activeMap.get(inst) ?? []) {
|
|
150
|
+
try {
|
|
151
|
+
helperHostSet.$w.cancelAnimationFrame(id);
|
|
152
|
+
} catch (e) {
|
|
153
|
+
console.error('[SWC] cancelAnimationFrame error:', e);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
activeMap.delete(inst);
|
|
157
|
+
}
|
|
158
|
+
}
|