@dooboostore/simple-web-component 1.0.32 → 1.0.34

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.
Files changed (143) hide show
  1. package/README.md +280 -43
  2. package/dist/cjs/decorators/addEventListener.js.map +1 -1
  3. package/dist/cjs/decorators/applyAttribute.js +67 -57
  4. package/dist/cjs/decorators/applyAttribute.js.map +2 -2
  5. package/dist/cjs/decorators/applyClass.js +80 -70
  6. package/dist/cjs/decorators/applyClass.js.map +2 -2
  7. package/dist/cjs/decorators/applyNode.js +1 -8
  8. package/dist/cjs/decorators/applyNode.js.map +2 -2
  9. package/dist/cjs/decorators/applyStyle.js +66 -56
  10. package/dist/cjs/decorators/applyStyle.js.map +2 -2
  11. package/dist/cjs/decorators/attribute.js +35 -29
  12. package/dist/cjs/decorators/attribute.js.map +2 -2
  13. package/dist/cjs/decorators/changedAttributeHost.js +32 -12
  14. package/dist/cjs/decorators/changedAttributeHost.js.map +2 -2
  15. package/dist/cjs/decorators/elementDefine.js +29 -16
  16. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  17. package/dist/cjs/decorators/emitCustomEvent.js +57 -44
  18. package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
  19. package/dist/cjs/decorators/index.js +3 -1
  20. package/dist/cjs/decorators/index.js.map +2 -2
  21. package/dist/cjs/decorators/publishSwcAppMessage.js +86 -0
  22. package/dist/cjs/decorators/publishSwcAppMessage.js.map +7 -0
  23. package/dist/cjs/decorators/setProperty.js +50 -39
  24. package/dist/cjs/decorators/setProperty.js.map +2 -2
  25. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js +62 -0
  26. package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +7 -0
  27. package/dist/cjs/decorators/{subscribeSwcAppRouteChange.js → subscribeSwcAppRouteChangeWhileConnected.js} +16 -16
  28. package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +7 -0
  29. package/dist/cjs/elements/SwcAppEngine.js +7 -7
  30. package/dist/cjs/elements/SwcAppEngine.js.map +3 -3
  31. package/dist/cjs/elements/SwcAppMixin.js +90 -43
  32. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  33. package/dist/cjs/elements/SwcChoose.js +19 -15
  34. package/dist/cjs/elements/SwcChoose.js.map +2 -2
  35. package/dist/cjs/elements/register.js +21 -16
  36. package/dist/cjs/elements/register.js.map +2 -2
  37. package/dist/cjs/types.js.map +1 -1
  38. package/dist/cjs/utils/Utils.js +7 -3
  39. package/dist/cjs/utils/Utils.js.map +2 -2
  40. package/dist/esm/decorators/addEventListener.js.map +1 -1
  41. package/dist/esm/decorators/applyAttribute.js +67 -57
  42. package/dist/esm/decorators/applyAttribute.js.map +2 -2
  43. package/dist/esm/decorators/applyClass.js +80 -70
  44. package/dist/esm/decorators/applyClass.js.map +2 -2
  45. package/dist/esm/decorators/applyNode.js +1 -8
  46. package/dist/esm/decorators/applyNode.js.map +2 -2
  47. package/dist/esm/decorators/applyStyle.js +66 -56
  48. package/dist/esm/decorators/applyStyle.js.map +2 -2
  49. package/dist/esm/decorators/attribute.js +35 -29
  50. package/dist/esm/decorators/attribute.js.map +2 -2
  51. package/dist/esm/decorators/changedAttributeHost.js +32 -12
  52. package/dist/esm/decorators/changedAttributeHost.js.map +2 -2
  53. package/dist/esm/decorators/elementDefine.js +30 -17
  54. package/dist/esm/decorators/elementDefine.js.map +2 -2
  55. package/dist/esm/decorators/emitCustomEvent.js +57 -44
  56. package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
  57. package/dist/esm/decorators/index.js +3 -1
  58. package/dist/esm/decorators/index.js.map +2 -2
  59. package/dist/esm/decorators/publishSwcAppMessage.js +67 -0
  60. package/dist/esm/decorators/publishSwcAppMessage.js.map +7 -0
  61. package/dist/esm/decorators/setProperty.js +50 -39
  62. package/dist/esm/decorators/setProperty.js.map +2 -2
  63. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +43 -0
  64. package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +7 -0
  65. package/dist/esm/decorators/{subscribeSwcAppRouteChange.js → subscribeSwcAppRouteChangeWhileConnected.js} +13 -13
  66. package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +7 -0
  67. package/dist/esm/elements/SwcAppEngine.js +6 -6
  68. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  69. package/dist/esm/elements/SwcAppMixin.js +90 -43
  70. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  71. package/dist/esm/elements/SwcChoose.js +20 -16
  72. package/dist/esm/elements/SwcChoose.js.map +2 -2
  73. package/dist/esm/elements/register.js +22 -17
  74. package/dist/esm/elements/register.js.map +2 -2
  75. package/dist/esm/types.js.map +1 -1
  76. package/dist/esm/utils/Utils.js +7 -3
  77. package/dist/esm/utils/Utils.js.map +2 -2
  78. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +543 -348
  79. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  80. package/dist/types/decorators/addEventListener.d.ts +4 -1
  81. package/dist/types/decorators/addEventListener.d.ts.map +1 -1
  82. package/dist/types/decorators/applyAttribute.d.ts +5 -1
  83. package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
  84. package/dist/types/decorators/applyClass.d.ts +4 -0
  85. package/dist/types/decorators/applyClass.d.ts.map +1 -1
  86. package/dist/types/decorators/applyNode.d.ts +4 -1
  87. package/dist/types/decorators/applyNode.d.ts.map +1 -1
  88. package/dist/types/decorators/applyStyle.d.ts +5 -1
  89. package/dist/types/decorators/applyStyle.d.ts.map +1 -1
  90. package/dist/types/decorators/attribute.d.ts +5 -0
  91. package/dist/types/decorators/attribute.d.ts.map +1 -1
  92. package/dist/types/decorators/changedAttributeHost.d.ts +13 -3
  93. package/dist/types/decorators/changedAttributeHost.d.ts.map +1 -1
  94. package/dist/types/decorators/elementDefine.d.ts +1 -0
  95. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  96. package/dist/types/decorators/emitCustomEvent.d.ts +5 -1
  97. package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
  98. package/dist/types/decorators/index.d.ts +3 -1
  99. package/dist/types/decorators/index.d.ts.map +1 -1
  100. package/dist/types/decorators/publishSwcAppMessage.d.ts +13 -0
  101. package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -0
  102. package/dist/types/decorators/setProperty.d.ts +5 -1
  103. package/dist/types/decorators/setProperty.d.ts.map +1 -1
  104. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +16 -0
  105. package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +1 -0
  106. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +18 -0
  107. package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +1 -0
  108. package/dist/types/elements/SwcAppEngine.d.ts +6 -3
  109. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  110. package/dist/types/elements/SwcAppMixin.d.ts +8 -3
  111. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  112. package/dist/types/elements/register.d.ts.map +1 -1
  113. package/dist/types/types.d.ts +6 -0
  114. package/dist/types/types.d.ts.map +1 -1
  115. package/dist/types/utils/Utils.d.ts.map +1 -1
  116. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +543 -348
  117. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  118. package/package.json +4 -4
  119. package/src/decorators/addEventListener.ts +1 -1
  120. package/src/decorators/applyAttribute.ts +74 -61
  121. package/src/decorators/applyClass.ts +91 -77
  122. package/src/decorators/applyNode.ts +3 -12
  123. package/src/decorators/applyStyle.ts +75 -61
  124. package/src/decorators/attribute.ts +55 -43
  125. package/src/decorators/changedAttributeHost.ts +47 -14
  126. package/src/decorators/elementDefine.ts +38 -19
  127. package/src/decorators/emitCustomEvent.ts +65 -49
  128. package/src/decorators/index.ts +3 -1
  129. package/src/decorators/publishSwcAppMessage.ts +100 -0
  130. package/src/decorators/setProperty.ts +58 -44
  131. package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +73 -0
  132. package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +71 -0
  133. package/src/elements/SwcAppEngine.ts +11 -12
  134. package/src/elements/SwcAppMixin.ts +115 -44
  135. package/src/elements/SwcChoose.ts +28 -7
  136. package/src/elements/register.ts +28 -7
  137. package/src/types.ts +8 -0
  138. package/src/utils/Utils.ts +15 -5
  139. package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +0 -7
  140. package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +0 -7
  141. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +0 -18
  142. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +0 -1
  143. package/src/decorators/subscribeSwcAppRouteChange.ts +0 -71
@@ -1,10 +1,11 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
2
  import { ensureInit } from './elementDefine';
3
3
  import { SwcUtils } from '../utils/Utils';
4
- import { SwcQueryOptions } from '../types';
4
+ import { HelperHostSet, SwcQueryOptions } from '../types';
5
5
 
6
6
  export interface PropertySetOptions extends SwcQueryOptions {
7
7
  name?: string;
8
+ filter?: (target: HTMLElement, value: any, meta: {currentThis: any, helper: HelperHostSet}) => boolean;
8
9
  }
9
10
 
10
11
  export interface PropertySetMetadata {
@@ -38,55 +39,68 @@ export const setProperty = (selector: string, propertyNameOrOptions?: string | P
38
39
  });
39
40
 
40
41
  const original = descriptor.value;
41
- descriptor.value = async function (...args: any[]) {
42
+ descriptor.value = function (...args: any[]) {
42
43
  ensureInit(this);
43
- const res = await (original as any).apply(this, args);
44
- if (res !== undefined) {
45
- const win = (this as any).ownerDocument?.defaultView || window;
46
- const host = this as unknown as HTMLElement;
47
- const hostSet = { ...SwcUtils.getHelperAndHostSet(win, host), $this: this };
48
- const r = finalOptions.root || 'auto';
49
- const targetEls: HTMLElement[] = [];
44
+ const res = (original as any).apply(this, args);
50
45
 
51
- const applyRoot = (t: any) => {
52
- if (!t || !(t instanceof win.HTMLElement)) return;
53
- targetEls.push(t);
54
- };
46
+ const handleResult = (resolvedValue: any) => {
47
+ if (resolvedValue !== undefined) {
48
+ const win = (this as any).ownerDocument?.defaultView || window;
49
+ const host = this as unknown as HTMLElement;
50
+ const hostSet = { ...SwcUtils.getHelperAndHostSet(win, host), $this: this };
51
+ const r = finalOptions.root || 'auto';
52
+ const targetEls: HTMLElement[] = [];
55
53
 
56
- if (selector === ':parentHost') applyRoot(hostSet.$parentHost);
57
- else if (selector === ':appHost') applyRoot(hostSet.$appHost);
58
- else if (selector === ':firstHost') applyRoot(hostSet.$firstHost);
59
- else if (selector === ':lastHost') applyRoot(hostSet.$lastHost);
60
- else if (selector === ':firstAppHost') applyRoot(hostSet.$firstAppHost);
61
- else if (selector === ':lastAppHost') applyRoot(hostSet.$lastAppHost);
62
- else if (selector === ':hosts') hostSet.$hosts.forEach(applyRoot);
63
- else if (selector === ':appHosts') hostSet.$appHosts.forEach(applyRoot);
64
- else if (selector === ':host' || !selector) applyRoot(host);
65
- else {
66
- const targetRoot = r === 'auto' ? (host.shadowRoot || host) : (r === 'shadow' ? host.shadowRoot : host);
67
- if (targetRoot) {
68
- const found = targetRoot.querySelectorAll(selector);
69
- if (found) targetEls.push(...(Array.from(found) as HTMLElement[]));
54
+ const applyRoot = (t: any) => {
55
+ if (!t || !(t instanceof win.HTMLElement)) return;
56
+ targetEls.push(t);
57
+ };
58
+
59
+ if (selector === ':parentHost') applyRoot(hostSet.$parentHost);
60
+ else if (selector === ':appHost') applyRoot(hostSet.$appHost);
61
+ else if (selector === ':firstHost') applyRoot(hostSet.$firstHost);
62
+ else if (selector === ':lastHost') applyRoot(hostSet.$lastHost);
63
+ else if (selector === ':firstAppHost') applyRoot(hostSet.$firstAppHost);
64
+ else if (selector === ':lastAppHost') applyRoot(hostSet.$lastAppHost);
65
+ else if (selector === ':hosts') hostSet.$hosts.forEach(applyRoot);
66
+ else if (selector === ':appHosts') hostSet.$appHosts.forEach(applyRoot);
67
+ else if (selector === ':host' || !selector) applyRoot(host);
68
+ else {
69
+ const targetRoot = r === 'auto' ? (host.shadowRoot || host) : (r === 'shadow' ? host.shadowRoot : host);
70
+ if (targetRoot) {
71
+ const found = targetRoot.querySelectorAll(selector);
72
+ if (found) targetEls.push(...(Array.from(found) as HTMLElement[]));
73
+ }
70
74
  }
71
- }
72
75
 
73
- targetEls.forEach(targetEl => {
74
- const resolvedRes = typeof res === 'function' ? (res as any)(targetEl, hostSet) : res;
76
+ targetEls.forEach(targetEl => {
77
+ if (finalOptions.filter && !finalOptions.filter(targetEl, resolvedValue, {currentThis: this, helper: hostSet})) {
78
+ return;
79
+ }
80
+ const resolvedRes = typeof resolvedValue === 'function' ? (resolvedValue as any)(targetEl, hostSet) : resolvedValue;
75
81
 
76
- if (propertyName) {
77
- // Case: @setProperty(selector, 'propertyName')
78
- (targetEl as any)[propertyName] = resolvedRes;
79
- } else if (typeof resolvedRes === 'object' && resolvedRes !== null && !Array.isArray(resolvedRes)) {
80
- // Case: @setProperty(selector) with Object return - assign multiple properties
81
- Object.entries(resolvedRes as any).forEach(([prop, val]) => {
82
- const finalVal = typeof val === 'function' ? val(targetEl, hostSet) : val;
83
- (targetEl as any)[prop] = finalVal;
84
- });
85
- } else {
86
- // Simple value - assign to 'value' property by default
87
- (targetEl as any).value = resolvedRes;
88
- }
89
- });
82
+ if (propertyName) {
83
+ // Case: @setProperty(selector, 'propertyName')
84
+ (targetEl as any)[propertyName] = resolvedRes;
85
+ } else if (typeof resolvedRes === 'object' && resolvedRes !== null && !Array.isArray(resolvedRes)) {
86
+ // Case: @setProperty(selector) with Object return - assign multiple properties
87
+ Object.entries(resolvedRes as any).forEach(([prop, val]) => {
88
+ const finalVal = typeof val === 'function' ? val(targetEl, hostSet) : val;
89
+ (targetEl as any)[prop] = finalVal;
90
+ });
91
+ } else {
92
+ // Simple value - assign to 'value' property by default
93
+ (targetEl as any).value = resolvedRes;
94
+ }
95
+ });
96
+ }
97
+ return resolvedValue;
98
+ };
99
+
100
+ if (res instanceof Promise) {
101
+ return res.then(handleResult);
102
+ } else {
103
+ return handleResult(res);
90
104
  }
91
105
  };
92
106
  };
@@ -0,0 +1,73 @@
1
+ import { ReflectUtils } from '@dooboostore/core';
2
+ import { SwcAppMessage } from '../types';
3
+
4
+ export const SUBSCRIBE_SWC_APP_MESSAGE_WHILE_CONNECTED_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-message-while-connected');
5
+
6
+ export interface SwcAppMessageSubscriberMetadata {
7
+ propertyKey: string | symbol;
8
+ messageType?: string;
9
+ filter?: (message: SwcAppMessage, currentThis: any) => boolean;
10
+ }
11
+
12
+ export interface SwcAppMessageOptions {
13
+ filter?: (message: SwcAppMessage, currentThis: any) => boolean;
14
+ }
15
+
16
+ function createSubscribeSwcAppMessageWhileConnected(options?: SwcAppMessageOptions, messageType?: string): MethodDecorator {
17
+ return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
18
+ const filter = options?.filter;
19
+
20
+ const constructor = target.constructor;
21
+ let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_MESSAGE_WHILE_CONNECTED_METADATA_KEY, constructor) as SwcAppMessageSubscriberMetadata[];
22
+ if (!list) {
23
+ list = [];
24
+ ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_MESSAGE_WHILE_CONNECTED_METADATA_KEY, list, constructor);
25
+ }
26
+ list.push({ propertyKey, messageType, filter });
27
+
28
+ return descriptor;
29
+ };
30
+ }
31
+
32
+ // 오버로드 시그니처 - 직접 사용 (괄호 없음)
33
+ export function subscribeSwcAppMessageWhileConnected(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
34
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음) - 메시지 타입만
35
+ export function subscribeSwcAppMessageWhileConnected(messageType: string): MethodDecorator;
36
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음) - 옵션만
37
+ export function subscribeSwcAppMessageWhileConnected(options: SwcAppMessageOptions): MethodDecorator;
38
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음) - 메시지 타입 + 옵션
39
+ export function subscribeSwcAppMessageWhileConnected(messageType: string, options: SwcAppMessageOptions): MethodDecorator;
40
+
41
+ export function subscribeSwcAppMessageWhileConnected(targetOrMessageTypeOrOptions?: any, optionsOrPropertyKey?: any, descriptor?: PropertyDescriptor): any {
42
+ // 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppMessageWhileConnected
43
+ if (targetOrMessageTypeOrOptions && typeof targetOrMessageTypeOrOptions === 'object' &&
44
+ optionsOrPropertyKey && (typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') &&
45
+ descriptor) {
46
+ const target = targetOrMessageTypeOrOptions;
47
+ const propertyKey = optionsOrPropertyKey;
48
+ return createSubscribeSwcAppMessageWhileConnected({})(target, propertyKey, descriptor);
49
+ }
50
+
51
+ // 함수로 호출된 경우 (괄호 있음)
52
+ let messageType: string | undefined;
53
+ let options: SwcAppMessageOptions = {};
54
+
55
+ if (typeof targetOrMessageTypeOrOptions === 'string') {
56
+ // 메시지 타입 전달: @subscribeSwcAppMessageWhileConnected('messageType') 또는 @subscribeSwcAppMessageWhileConnected('messageType', options)
57
+ messageType = targetOrMessageTypeOrOptions;
58
+ if (optionsOrPropertyKey && typeof optionsOrPropertyKey === 'object') {
59
+ options = optionsOrPropertyKey;
60
+ }
61
+ } else if (targetOrMessageTypeOrOptions && typeof targetOrMessageTypeOrOptions === 'object') {
62
+ // 옵션만 전달: @subscribeSwcAppMessageWhileConnected({ filter: ... })
63
+ options = targetOrMessageTypeOrOptions;
64
+ }
65
+
66
+ return createSubscribeSwcAppMessageWhileConnected(options, messageType);
67
+ }
68
+
69
+ // Helper function to retrieve message subscribers metadata
70
+ export const getSubscribeSwcAppMessageWhileConnectedMetadata = (target: any): SwcAppMessageSubscriberMetadata[] | undefined => {
71
+ const constructor = target instanceof Function ? target : target.constructor;
72
+ return ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_MESSAGE_WHILE_CONNECTED_METADATA_KEY, constructor);
73
+ };
@@ -0,0 +1,71 @@
1
+ import { ReflectUtils } from '@dooboostore/core';
2
+ import { Router } from '@dooboostore/core-web';
3
+
4
+ export const SUBSCRIBE_SWC_APP_ROUTE_CHANGE_WHILE_CONNECTED_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-route-change-while-connected');
5
+
6
+ export interface SwcAppRouteChangeSubscriberMetadata {
7
+ propertyKey: string | symbol;
8
+ pathPattern?: string | string[];
9
+ filter?: (router: Router, currentThis: any) => boolean;
10
+ }
11
+
12
+ export interface SwcAppRouteChangeOptions {
13
+ path?: string | string[];
14
+ filter?: (router: Router, currentThis: any) => boolean;
15
+ }
16
+
17
+ function createSubscribeSwcAppRouteChangeWhileConnected(options?: SwcAppRouteChangeOptions): MethodDecorator {
18
+ return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
19
+ const pathPattern = options?.path;
20
+ const filter = options?.filter;
21
+
22
+ const constructor = target.constructor;
23
+ let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_WHILE_CONNECTED_METADATA_KEY, constructor) as SwcAppRouteChangeSubscriberMetadata[];
24
+ if (!list) {
25
+ list = [];
26
+ ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_WHILE_CONNECTED_METADATA_KEY, list, constructor);
27
+ }
28
+ list.push({ propertyKey, pathPattern, filter });
29
+
30
+ return descriptor;
31
+ };
32
+ }
33
+
34
+ // 오버로드 시그니처 - 직접 사용 (괄호 없음)
35
+ export function subscribeSwcAppRouteChangeWhileConnected(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
36
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음)
37
+ export function subscribeSwcAppRouteChangeWhileConnected(): MethodDecorator;
38
+ export function subscribeSwcAppRouteChangeWhileConnected(pathPattern: string): MethodDecorator;
39
+ export function subscribeSwcAppRouteChangeWhileConnected(pathPatterns: string[]): MethodDecorator;
40
+ export function subscribeSwcAppRouteChangeWhileConnected(options: SwcAppRouteChangeOptions): MethodDecorator;
41
+
42
+ // 실제 구현 (이중 모드)
43
+ export function subscribeSwcAppRouteChangeWhileConnected(targetOrOptions?: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor): any {
44
+ // 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppRouteChangeWhileConnected
45
+ // propertyKey가 string | symbol이고 descriptor가 있으면 직접 사용
46
+ if (propertyKey && (typeof propertyKey === 'string' || typeof propertyKey === 'symbol')) {
47
+ return createSubscribeSwcAppRouteChangeWhileConnected({})(targetOrOptions, propertyKey, descriptor);
48
+ }
49
+
50
+ // 함수로 호출된 경우 (괄호 있음): @subscribeSwcAppRouteChangeWhileConnected() / @subscribeSwcAppRouteChangeWhileConnected('/path')
51
+ let options: SwcAppRouteChangeOptions = {};
52
+
53
+ if (Array.isArray(targetOrOptions)) {
54
+ // 배열: @subscribeSwcAppRouteChangeWhileConnected(['/path1', '/path2'])
55
+ options.path = targetOrOptions;
56
+ } else if (typeof targetOrOptions === 'string') {
57
+ // 문자열: @subscribeSwcAppRouteChangeWhileConnected('/path')
58
+ options.path = targetOrOptions;
59
+ } else if (targetOrOptions && typeof targetOrOptions === 'object') {
60
+ // 객체: @subscribeSwcAppRouteChangeWhileConnected({ path: '/path', filter: ... })
61
+ options = targetOrOptions;
62
+ }
63
+
64
+ return createSubscribeSwcAppRouteChangeWhileConnected(options);
65
+ }
66
+
67
+ // Helper function to retrieve route change subscribers metadata
68
+ export const getSubscribeSwcAppRouteChangeWhileConnectedMetadata = (target: any): SwcAppRouteChangeSubscriberMetadata[] | undefined => {
69
+ const constructor = target instanceof Function ? target : target.constructor;
70
+ return ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_WHILE_CONNECTED_METADATA_KEY, constructor);
71
+ };
@@ -1,26 +1,23 @@
1
1
  import { SimpleApplication } from '@dooboostore/simple-boot/SimpleApplication';
2
- import { PathRouter } from '@dooboostore/core-web';
3
- import { HashRouter } from '@dooboostore/core-web';
2
+ import { HashRouter, PathRouter, Router, RouterEventType } from '@dooboostore/core-web';
4
3
  import { ElementRouter } from '../router/ElementRouter';
5
4
  import { SimOption } from '@dooboostore/simple-boot/SimOption';
6
- import { Router, RouterEventType } from '@dooboostore/core-web';
7
- import { Subscription } from '@dooboostore/core';
8
- import { ConstructorType, OptionalType } from '@dooboostore/core';
9
- import { FunctionUtils } from '@dooboostore/core';
10
- import { SwcUtils } from '../utils/Utils';
5
+ import { ConstructorType, OptionalType, Subscription } from '@dooboostore/core';
11
6
 
12
7
  import { SwcAppInterface } from '../types';
13
- import {Intent} from "@dooboostore/simple-boot";
8
+ import { SimConfig } from '@dooboostore/simple-boot';
14
9
 
15
10
  export type SwcConfigType = {
16
11
  routeType: 'path' | 'hash' | 'element';
17
- connectMode?: 'direct' | 'swap';
12
+ connectMode?: 'direct' | 'swap' | 'clear-swap';
18
13
  container?: symbol;
14
+ otherInstanceSim?: Map<ConstructorType<any> | Function | SimConfig | symbol, any>;
19
15
  path?: string;
20
16
  window: Window;
21
17
  onEngineStarted?: (sp: SimpleApplication, app: SwcAppInterface) => void;
22
18
  onConnected?: (app: SwcAppInterface) => void;
23
19
  onDisconnected?: (app: SwcAppInterface) => void;
20
+ onRouteChanged?: (event: RouterEventType, app: SwcAppInterface) => void;
24
21
  };
25
22
 
26
23
  export type SwcAttributeConfigType = OptionalType<SwcConfigType, 'routeType' | 'window'>;
@@ -52,15 +49,17 @@ export class SwcAppEngine {
52
49
  // console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv', this.router)
53
50
 
54
51
  // App start
55
- const containerSymbol = this._config.container || (this.host as any).getContainerSymbol?.();
56
52
  this.simpleApplication = new SimpleApplication(
57
53
  new SimOption({
58
54
  excludeProxys: [Node],
59
- container: containerSymbol
55
+ container: this._config.container
60
56
  })
61
57
  );
62
58
 
63
59
  const otherInstanceSim = new Map<any, any>();
60
+ this.config.otherInstanceSim?.forEach((v, k) => {
61
+ otherInstanceSim.set(k, v);
62
+ });
64
63
  if (this.router) otherInstanceSim.set(Router, this.router);
65
64
  this.simpleApplication.run(otherInstanceSim);
66
65
 
@@ -68,7 +67,7 @@ export class SwcAppEngine {
68
67
  this._config.onEngineStarted(this.simpleApplication, this.host as unknown as SwcAppInterface);
69
68
  }
70
69
  } catch (e) {
71
- console.error('[SWC-APP-ENGINE] Init failed:', e);
70
+ console.error('[SWC-APP-ENGINE] Init failed:', e, this._config);
72
71
  }
73
72
  }
74
73
 
@@ -1,16 +1,19 @@
1
1
  import { SwcAppEngine, SwcAttributeConfigType, SwcConfigType } from './SwcAppEngine';
2
- import { SwcAppInterface } from '../types';
2
+ import { SwcAppInterface, SwcAppMessage } from '../types';
3
3
  import { SwcUtils } from '../utils/Utils';
4
4
  import { FunctionUtils, Subscription } from '@dooboostore/core';
5
5
  import { Router, RouterEventType } from '@dooboostore/core-web';
6
6
  import { ON_CONNECTED_SWC_APP_METADATA_KEY, findAllLifecycleMetadata } from '../decorators/lifecycles';
7
- import { getSubscribeSwcAppRouteChangeMetadata } from '../decorators/subscribeSwcAppRouteChange';
7
+ import { getSubscribeSwcAppRouteChangeWhileConnectedMetadata } from '../decorators/subscribeSwcAppRouteChangeWhileConnected';
8
+ import { getSubscribeSwcAppMessageWhileConnectedMetadata } from '../decorators/subscribeSwcAppMessageWhileConnected';
8
9
 
9
10
  export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Base: T) {
10
11
  //@ts-ignore
11
12
  return class extends Base implements SwcAppInterface {
12
13
  __swc_engine = new SwcAppEngine(this as any);
13
14
  _swc_connected_instance = new Set<any>();
15
+ _swc_started_instances = new WeakSet<any>();
16
+ _swc_notified_routes = new WeakMap<any, RouterEventType>();
14
17
  _routerSubscription?: Subscription;
15
18
  _lastRouterEvent?: RouterEventType;
16
19
 
@@ -28,11 +31,15 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
28
31
  return this.__swc_engine.router;
29
32
  }
30
33
 
31
- _invokeRouteChangeSubscribers(instance: any, re: RouterEventType) {
32
- const routeChangeSubscribers = getSubscribeSwcAppRouteChangeMetadata(instance);
34
+ async _invokeRouteChangeSubscribers(instance: any, re: RouterEventType) {
35
+ // 이미 이 라우트 이벤트에 대해 처리된 인스턴스라면 스킵 (동시성/중복 방지)
36
+ if (this._swc_notified_routes.get(instance) === re) return;
37
+ this._swc_notified_routes.set(instance, re);
38
+
39
+ const routeChangeSubscribers = getSubscribeSwcAppRouteChangeWhileConnectedMetadata(instance);
33
40
  if (routeChangeSubscribers && Array.isArray(routeChangeSubscribers)) {
34
- routeChangeSubscribers.forEach((metadata: any) => {
35
- const methodName = metadata.propertyKey || metadata;
41
+ const ps = routeChangeSubscribers.map(async metadata => {
42
+ const methodName = metadata.propertyKey;
36
43
  const pathPattern = metadata.pathPattern;
37
44
  const filter = metadata.filter;
38
45
 
@@ -61,13 +68,14 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
61
68
  }
62
69
 
63
70
  // Filter validation
64
- const filterPassed = !filter || filter(this.router!);
71
+ const filterPassed = !filter || filter(this.router!, instance);
65
72
 
66
73
  // Invoke method if conditions pass
67
- if (pathMatched && filterPassed) {
68
- instance[methodName]?.({...re, pathData: pathData});
74
+ if (pathMatched && filterPassed && instance[methodName]) {
75
+ await instance[methodName]({ ...re, pathData: pathData });
69
76
  }
70
77
  });
78
+ return await Promise.allSettled(ps);
71
79
  }
72
80
  }
73
81
 
@@ -88,19 +96,20 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
88
96
  _disconnected(instance: any) {
89
97
  if (instance) {
90
98
  this._swc_connected_instance.delete(instance);
99
+ this._swc_started_instances.delete(instance);
100
+ this._swc_notified_routes.delete(instance);
91
101
  }
92
102
  }
93
103
 
94
- _handleRouteChange(route: RouterEventType) {
104
+ async _handleRouteChange(route: RouterEventType) {
105
+ // Save last router event
106
+ this._lastRouterEvent = route;
95
107
  if (route.triggerPoint === 'end') {
96
108
  // Iterate through all connected component instances
97
- this._swc_connected_instance.forEach((instance: any) => {
98
- this._invokeRouteChangeSubscribers(instance, route);
99
- });
109
+ const swcConnectedInstance = Array.from(this._swc_connected_instance);
110
+ const ps = swcConnectedInstance.map((instance: any) => this._invokeRouteChangeSubscribers(instance, route));
111
+ await Promise.allSettled(ps);
100
112
  }
101
-
102
- // Save last router event
103
- this._lastRouterEvent = route;
104
113
  }
105
114
 
106
115
  async connect(config?: SwcAttributeConfigType) {
@@ -112,29 +121,44 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
112
121
 
113
122
  const mode = config?.connectMode || 'direct';
114
123
 
124
+ // console.log('-------mode', mode);
115
125
  if (mode === 'direct') {
116
126
  await this.__swc_engine.connect(swcConfig);
117
127
  this.setAttribute('swc-connect-mode', 'direct');
118
- } else if (mode === 'swap' && !this.parentNode) {
119
- await this.__swc_engine.connect(swcConfig);
120
- this.setAttribute('swc-connect-mode', 'direct');
121
-
122
- const clonedApp = this.cloneNode(true) as any;
123
- clonedApp.style.display = 'none';
124
- this.parentNode.insertBefore(clonedApp, this.nextSibling);
128
+ } else if (mode === 'swap' || mode === 'clear-swap') {
129
+ if (!this.parentNode) {
130
+ // 부모가 없으면 swap을 할 수 없으므로 direct로 폴백합니다.
131
+ await this.__swc_engine.connect(swcConfig);
132
+ this.setAttribute('swc-connect-mode', 'direct');
133
+ } else {
134
+ // 1. 현재 SSR 상태(화면)를 그대로 유지한 채로, 백그라운드용 클론을 만듭니다.
135
+ const clonedApp = this.cloneNode(mode === 'swap') as any; // clear-swap 모드면 자식 노드를 클론하지 않음(false)
136
+ clonedApp.style.display = 'none';
137
+
138
+ // 2. DOM에 클론을 몰래 붙입니다.
139
+ this.parentNode.insertBefore(clonedApp, this.nextSibling);
125
140
 
126
- try {
127
- await clonedApp.connect({ ...(config || {}), connectMode: 'direct' });
128
- if (this.parentNode) {
129
- this.parentNode.removeChild(this);
130
- }
131
- clonedApp.setAttribute('swc-connect-mode', 'swap');
132
- clonedApp.style.display = '';
133
- } catch (e) {
134
- if (clonedApp.parentNode) {
135
- clonedApp.parentNode.removeChild(clonedApp);
141
+ try {
142
+ // 3. 클론을 direct 모드로 연결(초기화/렌더링) 시킵니다.
143
+ await clonedApp.connect({ ...(config || {}), connectMode: 'direct' });
144
+
145
+ // 4. 클론의 렌더링이 완전히 끝나면, 기존 SSR 화면(this)을 지우고 클론을 보여줍니다.
146
+ if (this.parentNode) {
147
+ this.parentNode.removeChild(this);
148
+ }
149
+ clonedApp.style.display = '';
150
+ clonedApp.setAttribute('swc-connect-mode', mode);
151
+
152
+ // ★ 매우 중요: 교체가 완료되었으므로 기존 앱(this)은 여기서 즉시 종료하여 불필요한 라우터/이벤트 구독을 막아야 합니다!
153
+ return;
154
+ } catch (e) {
155
+ // 실패 시 클론을 지우고 원본을 direct로 강제 연결합니다.
156
+ if (clonedApp.parentNode) {
157
+ clonedApp.parentNode.removeChild(clonedApp);
158
+ }
159
+ await this.__swc_engine.connect(swcConfig);
160
+ this.setAttribute('swc-connect-mode', 'direct');
136
161
  }
137
- throw e;
138
162
  }
139
163
  }
140
164
 
@@ -144,32 +168,41 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
144
168
  });
145
169
  // Invoke @onConnectedSwcApp lifecycle methods on all connected component instances
146
170
  // const hostSet = SwcUtils.getHostSet(this as any);
147
- this._swc_connected_instance.forEach((instance: any) => {
148
- this._invokeOnConnectedSwcAppStarted(instance);
149
- });
150
-
171
+ const scis = Array.from(this._swc_connected_instance);
172
+ await Promise.allSettled(scis.map(instance => this._invokeOnConnectedSwcAppStarted(instance)));
173
+ // this._swc_connected_instance.forEach((instance: any) => {
174
+ // this._invokeOnConnectedSwcAppStarted(instance);
175
+ // });
151
176
 
152
177
  // Subscribe to router changes after connect
153
178
  if (!this._routerSubscription && this.router) {
179
+ const config = this.__swc_engine.config;
154
180
  this._routerSubscription = this.router.observable.subscribe((route: RouterEventType) => {
155
181
  //@ts-ignore
156
182
  // console.log('Route changed:', route, this._swcId);
157
- this._handleRouteChange(route);
183
+ this._handleRouteChange(route).then(it => {
184
+ config?.onRouteChanged?.(route, this);
185
+ });
158
186
  });
159
187
  // Navigate to initial path if specified
160
- const config = this.__swc_engine.config;
161
188
  // if (config?.path) {
162
189
  // this.router.go(config.path);
163
190
  // }
164
191
  }
165
192
  }
166
193
 
167
- _invokeOnConnectedSwcAppStarted(instance: any) {
194
+ async _invokeOnConnectedSwcAppStarted(instance: any) {
195
+ // 이미 시작된 인스턴스라면 스킵 (비동기 병렬 중복 호출 방지)
196
+ if (this._swc_started_instances.has(instance)) return;
197
+ this._swc_started_instances.add(instance);
198
+
168
199
  const startedMethods = findAllLifecycleMetadata(instance, ON_CONNECTED_SWC_APP_METADATA_KEY);
169
200
  if (startedMethods) {
170
- for (const m of startedMethods) {
171
- instance._invokeLifecycleMethod(m);
172
- }
201
+ const ps = startedMethods.map(m => instance._invokeLifecycleMethod(m));
202
+ return await Promise.allSettled(ps)
203
+ // for (const m of startedMethods) {
204
+ // instance._invokeLifecycleMethod(m);
205
+ // }
173
206
  }
174
207
  }
175
208
 
@@ -221,5 +254,43 @@ export function SwcAppMixin<T extends { new (...args: any[]): HTMLElement }>(Bas
221
254
  reload(): void {
222
255
  this.router?.reload();
223
256
  }
257
+
258
+ publishMessage(message: SwcAppMessage): void {
259
+ // 메시지를 모든 connected component instances로 분배
260
+ this._swc_connected_instance.forEach((instance: any) => {
261
+ this._invokeMessageSubscribers(instance, message);
262
+ });
263
+ }
264
+
265
+ _invokeMessageSubscribers(instance: any, message: SwcAppMessage) {
266
+ const messageSubscribers = getSubscribeSwcAppMessageWhileConnectedMetadata(instance);
267
+ if (messageSubscribers && Array.isArray(messageSubscribers)) {
268
+ messageSubscribers.forEach(metadata => {
269
+ const methodName = metadata.propertyKey;
270
+ const messageType = metadata.messageType;
271
+ const filter = metadata.filter;
272
+
273
+ // 메시지 타입 확인
274
+ let typeMatched = false;
275
+ if (!messageType) {
276
+ // messageType이 없으면 모든 메시지 매칭 (와일드카드)
277
+ typeMatched = true;
278
+ } else if (message.type === messageType) {
279
+ typeMatched = true;
280
+ }
281
+
282
+ // 필터 적용
283
+ let filterMatched = true;
284
+ if (filter && typeMatched) {
285
+ filterMatched = filter(message, instance);
286
+ }
287
+
288
+ // 조건을 만족하면 메서드 호출
289
+ if (typeMatched && filterMatched && typeof instance[methodName] === 'function') {
290
+ instance[methodName](message);
291
+ }
292
+ });
293
+ }
294
+ }
224
295
  };
225
296
  }
@@ -3,10 +3,12 @@ import { SwcUtils } from '../utils/Utils';
3
3
  import { FunctionUtils, ActionExpression } from '@dooboostore/core';
4
4
  import { ConvertUtils } from '@dooboostore/core-web';
5
5
  import { SwcChooseInterface } from '../types';
6
- import { attributeHost } from '../decorators';
6
+ import { attributeHost, getAttributeValue, getElementConfig } from '../decorators';
7
7
 
8
8
  class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
9
- @attributeHost('value')
9
+ // @attributeHost('value')
10
+ // fetchValue: any;
11
+
10
12
  _value: any;
11
13
 
12
14
  @attributeHost('replace-wrap')
@@ -20,12 +22,29 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
20
22
  }
21
23
 
22
24
  set value(nv: any) {
23
- this.refresh(nv);
25
+ this._value = nv;
26
+ this.refresh(this._value);
27
+ }
28
+
29
+ constructor() {
30
+ super();
31
+ // console.log('swc chooooooooooooooooooooooooose constructor called!');
32
+ // console.log('swc cccccccss', this.getAttribute('value'));
33
+ }
34
+
35
+ // static get observedAttributes() {
36
+ // return ['value'];
37
+ // }
38
+
39
+ attributeChangedCallback(na, o, n) {
40
+ // console.log('-----------------choooooooose attr origin', na, o, n);
24
41
  }
25
42
 
26
43
  @changedAttributeHost('value')
27
- private setValue() {
28
- this.refresh();
44
+ private setValue(value: any, o: string, n: string) {
45
+ // console.log('-----------------choooooooose attr setValue', value, o, n);
46
+ this._value = value;
47
+ this.refresh(this._value);
29
48
  }
30
49
 
31
50
  @changedAttributeHost('replace-wrap')
@@ -55,11 +74,13 @@ class SwcChoose extends HTMLTemplateElement implements SwcChooseInterface {
55
74
  return ae.getUnprocessedTemplate();
56
75
  }
57
76
 
58
- refresh(value = this._value) {
77
+ refresh(value?: any) {
78
+ value = getAttributeValue(this, 'value') ?? this._value;
79
+ // console.log('chooooooooo refresh->', value);
59
80
  const portal = this.parentElement;
60
81
  if (!portal) return;
61
82
 
62
- const win = this.ownerDocument?.defaultView || window;
83
+ const win = (getElementConfig(this).window ?? this.ownerDocument?.defaultView) || window;
63
84
  const helperSet = SwcUtils.getHelperAndHostSet(win, this);
64
85
  const groupArgs = { ...helperSet, $value: value };
65
86